【PC】Android で Socketを使ったメール送信(1)
Android でメールを送信というと、ユーザーは様々なメールソフトを使えばOK。アプリを作る方は多くの場合は Intent を使って(Intent って非常に便利ですねぇ)メール画面を出してあげればOK。ただし、この手法は「メールを送信する一歩手前の画面」を用意できるだけなので、メールを送るところは操作しないといけない。
自動でメールを送信したい場合や、メールアプリを作る場合などには、これだとちょっと困る。そこでよく出てくるのが JavaMail API を使う方法だが、筆者の場合、いままでずっと Visual Studio でメール送信処理は作ってきたので( Windows だけど ) メール送信は Socket で出来るだろうと思い込んでいたりする。
長い間、放置していた古い Visual Studio のプログラムソースを開いて、やっぱり Socket だけでやってます、ということで Android でも Socket だけでメール送信は書けるはず、と挑んでみた。
ほとんど単なる移植だけでも・・・。
メール送信の基本は、インターネット上にある特定のメール送信サーバー(SMTPサーバー)に、お願いしてメールを送ってもらえばよい。メールは、「自分のメールアドレス」「相手先のメールアドレス」「件名」「本文」があれば送れるはずで、SMTPサーバーさんとお話しながら、この4項目を渡してあげればよい。はず。
それでも手順は少し面倒くさいのだが、それだけでよい。はず。
しかし実際には、ここ数年のインターネットの普及と、そのダークサイド=セキュリティのお話、なりすまし、ありとあらゆる悪意によって、ものすごくややこしい手順を踏まねばならなくなってしまった。まず、メールの受信をする。受信なのでもちろん認証あり、だ。そうすることによって、はじめてメール送信サーバーにアクセスできる。次に、メール送信サーバーにアクセスする。最近は多くの場合、認証付きだ。2回も認証をして、やっと差出人と宛先を送ると、次は、件名。この件名に日本語が使えないので、BASE64というエンコードをしてあげる。で、ようやく本文。むかしは受信がとてもややこしくて送信は送るだけ、だったのだが、昨今はこれ全部を自前で処理をしないと、メール送信アプリは作れない。たいへん面倒くさい。
ともかく膨大な量のプロシージャーというかクラスが必要になるのだが、ちょっとづつ、小出しに(笑)書き殴っていこうと思う。
1回目は最も単純なメール送信プログラムのサンプル。
実用性ゼロ(笑)
package com.example.blog; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import java.net.UnknownHostException; import android.os.Bundle; import android.app.Activity; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if( sendMail("smtp.server.xxx.zzz", 587, "送信者の名前", "soushinsya@mail.xx.zz", "atesaki@xx.zz", "件名", "本文") == true ){ // // メール送信成功 // }else{ // // メール送信失敗( orz ) // } } //============================== // // Mail送信 // public Boolean sendMail( String smtphost, int smtpport, String fromName, String fromMailAddress, String toMailAddress, String subject, String bodymsg ){ Boolean ret = true; Socket connection = null; BufferedReader reader = null; BufferedWriter writer = null; try { // サーバーに接続 connection = new Socket( smtphost,smtpport ); reader = new BufferedReader( new InputStreamReader( connection.getInputStream() ) ); writer = new BufferedWriter( new OutputStreamWriter( connection.getOutputStream(),"ISO2022JP" ) ); String message = reader.readLine(); if ( !message.startsWith("220") ) return false; // HELOの送信 sendData( writer, "EHLO " + smtphost + "\r\n" ); message = reader.readLine(); if ( !message.startsWith("250") ) return false; // MAILFROMの送信 sendData( writer, "MAIL FROM: <" + fromMailAddress + ">\r\n" ); message = reader.readLine(); if ( !message.startsWith("250") ) return false; // MAILTOの送信 sendData( writer, "RCPT TO: <" + toMailAddress + ">\r\n" ); message = reader.readLine(); if ( !(message.startsWith("250")) && !(message.startsWith("251")) ) return false; // DATA(メッセージ本体) の送信 sendData( writer, "DATA\r\n" ); message = reader.readLine(); if ( !(message.startsWith("250")) && !(message.startsWith("354")) ) return false; String header = "MIME-Version: 1.0\r\n" + "Content-type: text/plain;"+ "charset=\"iso-2022-jp\"\r\n" + "Content-Transfer-Encoding: 7bit\r\n" + "To: " + toMailAddress + "\r\n" + "From: =?ISO-2022-JP?B?" + base64Encode( fromName ) + "?=" + "<"+ fromMailAddress +">\r\n"; String sndMsg = "Subject: =?ISO-2022-JP?B?" + base64Encode( subject ) +"?=\r\n" + header + "\r\n\r\n" + bodymsg + "\r\n" + "." + "\r\n"; sendData( writer, sndMsg ); message = reader.readLine(); if ( !(message.startsWith("250")) && !(message.startsWith("354")) ) return false; // QUITの送信 sendData( writer, "QUIT\r\n" ); message = reader.readLine(); if ( !message.startsWith("221") ) return false; } catch (UnknownHostException e) { // // サーバー 不明エラー // ret = false; } catch (IOException e) { // // 接続エラー // ret = false; } catch (Exception e) { // // その他のエラー // ret = false; } finally { // // 接続終了処理 // try { reader.close(); writer.close(); connection.close(); } catch (IOException e2) { ret = false; } catch (Exception e2) { ret = false; } } return ret; } private Boolean sendData( BufferedWriter writer, String msg ) throws IOException{ // // 送信する // Boolean ret = true; writer.write( msg ); writer.flush(); return ret; } private String base64Encode( String s ){ // // BASE64エンコードを行う予定 // return s; } }
次回はまずこの基本を解説することにしようかな、と考え中。