Ruby の SMTP ライブラリ net-smtp の 0.4.0 をリリースしたので変更内容を書いておく。
認証用のクラスを分離
今まで Net::SMTP クラス中に全部の認証用のメソッドが用意されていたんだけど、新しい認証方式に対応するたびに Net::SMTP 本体に手を入れるのは大変なので、認証用のクラスを作った。
新しい認証方式に対応するには Net::SMTP::Authenticator クラスを継承したクラスを作って、auth メソッドを書けば良い。
たとえば PLAIN 認証をサポートする net/smtp/auth_plain.rb
はこんな内容になってる:
class AuthPlain < Net::SMTP::Authenticator auth_type :plain def auth(user, secret) finish('AUTH PLAIN ' + base64_encode("\0#{user}\0#{secret}")) end end
finish
メソッドで SMTP サーバーに命令を送って、それが成功すれば認証が成功したとみなす。
認証が成功するまでに何回かやりとりする必要がある認証方式もある。 LOGIN 認証は、次のような流れ。
Client> AUTH LOGIN Server> 334 プロンプト用文字列(base64) Client> ユーザー名(base64) Server> 334 プロンプト用文字列(base64) Client> パスワード(base64) Server> 235 2.7.0 Authentication successful
net/smtp/auth_login.rb
はこんな感じ:
class AuthLogin < Net::SMTP::Authenticator auth_type :login def auth(user, secret) continue('AUTH LOGIN') continue(base64_encode(user)) finish(base64_encode(secret)) end end
continue
メソッドは finish
と同じく SMTP サーバーにデータを送るけど、まだ続きがあること(3xx 応答)を期待する。
PLAIN や LOGIN は平文認証だけど、チャレンジレスポンス方式で認証情報を暗号化する認証方式もある。
サーバーからのレスポンスデータを使うには continue
メソッドの戻り値を使えばいい。
たとえば CRAM-MD5 用の net/smtp/auth_cram_md5.rb
はこんな感じ:
class AuthCramMD5 < Net::SMTP::Authenticator auth_type :cram_md5 def auth(user, secret) challenge = continue('AUTH CRAM-MD5') crammed = cram_md5_response(secret, challenge.unpack1('m')) finish(base64_encode("#{user} #{crammed}")) end ...以下略...
SMTPUTF8 対応
サーバーが SMTPUTF8 拡張に対応している場合(EHLO で SMTPUTF8 を返す場合)、メールアドレスに 8bit 文字が含まれていたら MAIL FROM 時に SMTPUTF8 オプションを使うようになった。
例外が発生するのにメールが送られてることがあったのを修正
RCPT TO が 53x エラーを返した場合、例外が発生するけどそれ以外の宛先にはメールが送られることがあったのを直した。
53x 以外のエラーはそのまま例外になって終わるんで、53x エラーだけ特別扱いしてるのが謎だったので 53x エラーも特別扱いしないようにした。
そもそも 53x エラーは認証エラーなので、普通は RCPT TO で 53x エラーが返ることなんてないんでホントに謎だった。
Subversion 用のキーワードを消した
未だに Subversion のキーワード $Id$
, $Revision$
が含まれてたので消した。
以上のような感じなので、まあ普通に使う分には変わりはないはず。