Rubyで日本語のメールを送る。
なるべく少ないライブラリで正しい日本語のメールを送る方法について。
結論
先に結論から。
require "nkf" require "net/smtp" def sendmail(from, to, subject, body, host = "localhost", port = 25) body = <<EOT From: #{from} To: #{to.to_a.join(",\n ")} Subject: #{NKF.nkf("-WMm0", subject)} Date: #{Time::now.strftime("%a, %d %b %Y %X %z")} Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-2022-JP Content-Transfer-Encoding: 7bit #{NKF.nkf("-Wjm0", body)} EOT Net::SMTP.start(host, port) do |smtp| smtp.send_mail body, from, to end end
- http://gist.github.com/86778 (上記スクリプトの最新版)
ポイント
- nkf
- 最低限3つのMIMEヘッダをつける。
- Mime-Version
- Content-Type
- Content-Transfer-Encoding
- 改行をCR+LFに揃える処理は不要。net/smtpのsend_mailのコメントに変換すると書いてある。
- Single CR ("\r") and LF ("\n") found in the +msgstr+, are converted into the CR LF pair.
- RailsではActionMailer+GetTextを使うが、ちょっとメールを送るだけの用途には大げさだと思う。
- gem版のTMailを使う場合はTMail.KCODE="EUC"と設定した上で、日本語をEUCで渡す必要がある。
前提
素のメールの仕様はRFC 5322。US-ASCIIしか使えない。
素メールを拡張するのがMIME(Multipurpose Internet Mail Extension)。RFC 2045〜2049。
- RFC 2045 - Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
- RFC 2046 - Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types
- RFC 2047 - MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text
- RFC 2048 - Multipurpose Internet Mail Extensions (MIME) Part Four: Registration Procedures
- RFC 2049 - Multipurpose Internet Mail Extensions (MIME) Part Five: Conformance Criteria and Examples
MIMEについての記事。
- Multipurpose Internet Mail Extensions - Wikipedia Wikipedia。わかりやすい。
- メールにかけられた呪文「MIME〜前編」 @IT。わかりやすい。
- メールにかけられた呪文「MIME〜後編」
ヘッダで非ASCII文字を使うにはRFC 2047。=?ISO-2022-JP?B?xxxxx?=みたいなやつ。「MIMEエンコード」「MIME Bエンコード」などと呼ばれる。
- RFC 2047 - MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text
nkfはこいつを自動的にデコードしてくれる(してしまう)。デコードを抑制するため-m0をつけるよう心がける。
$ echo -n 'abc' | nkf -MB YWJj $ echo -n '=?ISO-2022-JP?B?YWJj?=' | nkf abc $ echo -n '=?ISO-2022-JP?B?YWJj?=' | nkf -m0 =?ISO-2022-JP?B?YWJj?=
一般に、まともに外に出せるメールをつくるなら
1) To:、Subject:、Date:、Mime-Version:、Content-Type: をつける
2) ヘッダにある mail safe でない部分を B エンコード
3) 必要なら本体を Base64 エンコード
4) できればヘッダの文法チェックは最低でも行う必要があります。
[ruby-list:22157] Re: net/smtp
Rubyでメールを送る。
Rubyで日本語のメールを送るにはどうすればいいのか。US-ASCIIだけでいいなら簡単だ。
require "net/smtp" from = "from@example.com" to = "to@example.com" body = <<EOT From: #{from} To: #{to} Subject: test Date: #{Time::now.strftime("%a, %d %b %Y %X %z")} Hi, This is US-ASCII mail test. EOT Net::SMTP.start("localhost", 25) {|smtp| smtp.send_mail body, from, to }
これにMIMEヘッダと日本語の処理を追加すると、
TMailの古いバージョンを使ってみる。
TMailにはMIME Bエンコーダ・デコーダが含まれている。
古いTMailは$KCODE='e'などとしてEUCを設定すると件名をMIME Bエンコードできる。以下から0.10.8をダウンロード。
#! /usr/bin/env ruby $KCODE = 'e' $:.unshift("./tmail-0.10.8/lib") # require "rubygems" require "tmail" mail = TMail::Mail.new mail.subject = "テスト" puts mail.encoded # -> Subject: =?iso-2022-jp?B?GyRCJUYlOSVIGyhC?=
TMailの最近のバージョンを使う。
TMailは2007年からメンテナが変わっている。
gemでインストールした新しいTMailで同じ事をしてもうまくいかない。
$ sudo gem install tmail
もうちょっとパラメータを足してみるが、やはりエンコードされない。
#! /usr/bin/env ruby require "rubygems" require "tmail" mail = TMail::Mail.new mail.date = Time.now mail.mime_version = "1.0" mail.set_content_type "text", "plain", {"charset"=>"iso-2022-jp"} mail.transfer_encoding = "7bit" mail.message_id = TMail.new_message_id("example.com") mail.to = "test@example.com" mail.from = "from@example.com" mail.subject = "例" mail.body = "テストメールです。" puts mail.encoded
うーん……と悩んでソースを見ると、TMail.KCODEを設定すればいいことがわかった。
#! /usr/bin/env ruby require "rubygems" require "tmail" TMail.KCODE='EUC' mail = TMail::Mail.new mail.subject = NKF.nkf("-We", "例") puts mail.encoded mail.subject = NKF.nkf("-We", "ASCII 日本語 ASCIIと日本語 ASCII ASCII あいうえおあいうえおあいうえお\n") puts mail.encoded
出力はこんな感じ。ちゃんと分割されているみたい。
Subject: =?iso-2022-jp?B?GyRCTmMbKEI=?= Subject: ASCII =?iso-2022-jp?B?GyRCRnxLXDhsGyhCIEFTQ0lJGyRCJEhGfEtcOGwbKEI=?= ASCII ASCII =?iso-2022-jp?B?GyRCJCIkJCQmJCgkKiQiJCQkJiQoJCokIiQkJCYbKEI=?= =?iso-2022-jp?B?GyRCJCgkKhsoQg==?=
nkfでデコードできるか確認。
$ ruby ./encode_test.rb | nkf Subject: 例 Subject: ASCII 日本語 ASCIIと日本語 ASCII ASCII あいうえおあいうえおあいうえお
うまくいった。うまくいったけど、、。うーん。。EUCに変換する意味は全くないし、件名のエンコードだけならわざわざTMailを使う必要もないような……。スッキリしない。
nkf -M を使う。
puts NKF.nkf("-M", "ASCII 日本語 ASCIIと日本語 ASCII ASCII あいうえおあいうえおあいうえお") ASCII =?ISO-2022-JP?B?GyRCRnxLXDhsGyhCIEFTQ0lJGyRCJEhGfEtcOGwbKEI=?= ASCII ASCII =?ISO-2022-JP?B?GyRCJCIkJCQmJCgkKiQiJCQkJiQoJCokIiQkJCYkKCQqGyhC?=
こうかー!-MはASCIIだけ入力して、何も起こらないなーと思ってた。頭悪い。
GmailのSMTPサーバを使う。
を参考に。
Ruby 1.8のNet::SMTPはSTARTTLSに対応していないが、gemで解決する。素晴らしい。
sudo gem install tlsmail
で、
require "rubygems" require "tlsmail"
とした上で、
Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE) Net::SMTP.start("smtp.gmail.com", 587, "localhost.localdomain", gmail_user, gmail_pass, "plain") do |smtp| smtp.send_mail body, from, to end
で送信できた。
の sendgmail.rb を参照。
まだ追記するかも。