2006-04-16
■[RoR]ActionMailerでGMailのSMTPサーバを使いたい
ActionMailerのSMTPサーバにGMailを使いたい。
だって宅鯖なんだもの。
問題は、GMailがTLS経由でのSMTP接続を要求すること。
Rubyのメジャーバージョンが上がるとnet/popやnet/smtpでTLSが使えるようになるみたいだが、いつになるか分からない。
まだ作業の途中だがメモしておく。
参考:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/2789
http://bloghome.lovepeers.org/daymemo2/?date=20040802#p02
http://d.hatena.ne.jp/drawnboy/20051113/1131822348
接続のための設定
$ tail config/envirionment.rb ActionMailer::Base.delivery_method = :smtp ActionMailer::Base.server_settings = { :address => "smtp.gmail.com", :port => "587", :domain => "localhost.localdomain", :authentication => :plain, :user_name => "hogehoge", :password => "password" }
net/smtpの変更
Ruby On Railsのpluginの機能を使ってnet/smtpの挙動を変える。
今回は動くかどうか試すだけなので、決め打ちでTLSを使うようにした。
$ cat vendor/plugins/action_mailer_tls/init.rb require_dependency 'smtp_tls'
$ cat vendor/plugins/action_mailer_tls/lib/smtp_tls.rb require "openssl" require "net/smtp" Net::SMTP.class_eval do private def do_start(helodomain, user, secret, authtype) raise IOError, 'SMTP session already started' if @started check_auth_args user, secret, authtype if user or secret sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) } @socket = Net::InternetMessageIO.new(sock) @socket.read_timeout = 60 #@read_timeout @socket.debug_output = STDERR #@debug_output check_response(critical { recv_response() }) do_helo(helodomain) raise 'openssl library not installed' unless defined?(OpenSSL) starttls ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.sync_close = true ssl.connect @socket = Net::InternetMessageIO.new(ssl) @socket.read_timeout = 60 #@read_timeout @socket.debug_output = STDERR #@debug_output do_helo(helodomain) authenticate user, secret, authtype if user @started = true ensure unless @started # authentication failed, cancel connection. @socket.close if not @started and @socket and not @socket.closed? @socket = nil end end def do_helo(helodomain) begin if @esmtp ehlo helodomain else helo helodomain end rescue Net::ProtocolError if @esmtp @esmtp = false @error_occured = false retry end raise end end def starttls getok('STARTTLS') end end
実行結果
対話環境から実行してみた。
$ ruby script/console Loading development environment. >> ProductMailer.deliver_added -> "220 mx.gmail.com ESMTP 12sm68450nzn\r\n" <- "EHLO localhost.localdomain\r\n" -> "250-mx.gmail.com at your service\r\n" -> "250-SIZE 20971520\r\n" -> "250-8BITMIME\r\n" -> "250-STARTTLS\r\n" -> "250 ENHANCEDSTATUSCODES\r\n" <- "STARTTLS\r\n" -> "220 2.0.0 Ready to start TLS\r\n" <- "EHLO localhost.localdomain\r\n" -> "250-mx.gmail.com at your service\r\n" -> "250-SIZE 20971520\r\n" -> "250-8BITMIME\r\n" -> "250-AUTH LOGIN PLAIN\r\n" -> "250 ENHANCEDSTATUSCODES\r\n" <- "AUTH PLAIN xxxxxxxxxxxxxxxxxxxx\r\n" -> "235 2.7.0 Accepted\r\n" <- "MAIL FROM:<>\r\n" -> "250 2.1.0 OK\r\n" <- "RCPT TO:<hogehoge@example.com>\r\n" -> "250 2.1.5 OK\r\n" <- "DATA\r\n" -> "354 Go ahead\r\n" writing message from String wrote 276 bytes -> "250 2.0.0 OK 1145201897 12sm68450nzn\r\n" <- "QUIT\r\n" EOFError: end of file reached from /usr/lib/ruby/1.8/net/protocol.rb:133:in `sysread' from /usr/lib/ruby/1.8/net/protocol.rb:133:in `rbuf_fill' from /usr/lib/ruby/1.8/timeout.rb:56:in `timeout' from /usr/lib/ruby/1.8/timeout.rb:76:in `timeout' from /usr/lib/ruby/1.8/net/protocol.rb:132:in `rbuf_fill' from /usr/lib/ruby/1.8/net/protocol.rb:116:in `readuntil' from /usr/lib/ruby/1.8/net/protocol.rb:126:in `readline' from /usr/lib/ruby/1.8/net/smtp.rb:664:in `recv_response' from /usr/lib/ruby/1.8/net/smtp.rb:651:in `getok' from /usr/lib/ruby/1.8/net/smtp.rb:686:in `critical' from /usr/lib/ruby/1.8/net/smtp.rb:649:in `getok' from /usr/lib/ruby/1.8/net/smtp.rb:639:in `quit' from /usr/lib/ruby/1.8/net/smtp.rb:426:in `do_finish' from /usr/lib/ruby/1.8/net/smtp.rb:381:in `start' from /usr/lib/ruby/1.8/net/smtp.rb:316:in `start' from /usr/lib/ruby/gems/1.8/gems/actionmailer-1.2.1/lib/action_mailer/base.rb:447:in `perform_delivery_smtp' from /usr/lib/ruby/gems/1.8/gems/actionmailer-1.2.1/lib/action_mailer/base.rb:333:in `deliver!' from /usr/lib/ruby/gems/1.8/gems/actionmailer-1.2.1/lib/action_mailer/base.rb:227:in `method_missing' from (irb):1>>
メールは送信出来たが、最後にEOFErrorが発生した。
試しにsylpheedで同様にメール送信を試してみたが、QUITを送るとコネクションが切断されるらしく、sylpheedにプロトコルエラーと怒られた。
どうも、GMailはQUITを送ると応答を返さずにコネクションを切断するようだ。
どうするのが正しいのか分からんので、とりあえず保留。
追記
やっぱりGMailが悪いみたい。
http://www.puni.net/~mimori/rfc/rfc2821b.txt
RFC2821の4.1.1.10で、
The receiver MUST NOT intentionally close the transmission channel until it receives and replies to a QUIT command (even if there was an error).
と書いてある。
http://tmtm.org/cgi-bin/w3ml/sylpheed-jp/msg/2885
あと、2chのWanderlustスレでも似たような事で困ってる人がいた。


