spacer
spacer
開発者の談話室

System.Net.MailでTo,Fromフィールドをiso-2022-jp Bエンコード

|

先日、RFC違反のメールアドレスに関する記事を書きましたが、ASP.NETのライブラリはGlobal基準(またはMicrosoft基準)のため、ガラパゴス進化をしている日本のネット環境と乖離していることがあります。

System.Net.Mailはその中でも際立っていて、海外ではUTF-8 & Qエンコードが主流なため、国内のShift-JIS & Bエンコードという日本のネット環境に適合していません。

悩まれている方も多く、あちらこちらで記事があがっています。

System.Net.Mail問題その3(中の技術日誌)
せっかくsubjectとbodyにはエンコード方式の指定があるにもかかわらず、From, To, Cc, Bcc, ReplyToなどのこのMailAddressクラスにはエンコード方式を指定する方法がありません

結局、MailAddressクラスのコンストラクタが「送り先氏名 <user@address.com>」という文字列を解釈して処理してくれるにもかかわらず、Qエンコーディングなため、自前でBエンコード処理をして2つ変数をとるコンストラクタ MailAddress( address, displayname) を呼ぶ必要があります。

string from_name = MyBEncode( "株式会社 アジルテック", enc );
string from_address = "user@address.com"; 
msg.To.Add( new MailAddress( to_address, to_name ) );

また、Subject, Bodyのエンコードを指定できますが、iso-2022-jpに指定しても、

Subject: =?iso-2022-jp?Q?=1B$B%5%V%8%'%/%H=1B(B1?=
Content-Type: text/plain; charset=iso-2022-jp
Content-Transfer-Encoding: 8bit

出力はSubjectはQエンコードされ、BodyのContent-Transfer-Encodingは8bitエンコーディングです。

結局、せっかく自動処理してくれるSubjectもMailMessage.SubjectEncodingは未指定にし、自前でBエンコーディングをする必要があります。

Bodyを7bitにするには、Content-Transfer-Encodingについては、msg.HeadersにAddしても追加されず、AlternateViewを使って7bitエンコーディングを実現する必要があります。

追加されない:
msg.Headers.Add( "Content-Transfer-Encoding", "7bit" );
AlternateViewを利用する:
byte[] buf = enc.GetBytes( body );
MemoryStream mem = new MemoryStream( buf );
AlternateView alt_msg = new AlternateView( mem, new ContentType("text/plain; charset=" + enc.BodyName ) ); 
alt_msg.TransferEncoding  = TransferEncoding.SevenBit;

これまでは上記の方法でも、「content-transfer-encoding: 7bit」と出力するところが、「content-transfer-encoding: sevenbit」となっていたため、正しく送信できなかったのですが、.Net Framework 2.0 SP1で、「[FIX] System.Net.Mime 名前空間または System.Net.Mail 名前空間を使用する Visual Studio 2005 プログラムを実行すると、TransferEncoding プロパティの値が正しくないことがある」修正が含まれたので、SP1以降は送信するこができます。

 参考:
 [.NET 2.0]日本語(ISO-2022-JP) を Content-Transfer-Encoding: 7bit で送信する方法

上記の方法で大部分は問題なく動作するが、まだ問題が残っています。RFC 2047に記述のあるエンコード行の76文字改行規制です。

encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
複数の行からなるヘッダフィールドの長さに制限はないが、1つ以上の
  'encoded-word' を含むそれぞれの行は、76文字に制限される。

改行いれた文字列を渡すと、MailMessageのSubjectプロパティもMailAddressクラスコンストラクタもエラーを発生します。

2バイト文字だと40文字越えるとこの制限に抵触します。

=?iso-2022-jp?B?GyRCJTUlViU4JSclLyVIRDkkJCU1JVYlOCUnJS8lSCRAJEgkSSQmJEokayRzJEckNyRnJCYkKyRKIzEjMiMzIzQjNSM2IzcjOCM5IzEjMiMzIzQjNSM2IzcjOCM5GyhCQUJDREVGRxskQiQiJCQkJiQoJCokKyQtJC8kMSQzJDUkNyQ5JDskPSQ/JEEkRCRGJEghIktcRnwkT0AyRTckSiRqGyhC?=

しかし、上記のような長いエンコード文字列を渡しても大丈夫なSMTPサーバ、メールクライアントも多いようです。Au, Softbankは76文字で区切らなくても表示できる端末があるので、少なくとも両社のSMTPサーバは受け取ります。(ただし、端末によっては表示されない可能性あり)

まとめ:

  1. ヘッダ中(To, Subject)のBエンコードは自分で行う。文字長は気にしなくてもよい
  2. 本文のエンコード指定はSP1を適用し、AlternateViewを用いる

RFCの作法は守りたいという方は、Socketから直接SMTPサーバと通信してください。

[追記 2008/11/20 ] 弊社より、サブジェクト文字化けとRFC非準拠アドレスに対応したSMTPClientを公開しました。現在、β公開につき無料で利用できます。ぜひお試しください。

RFC非準拠アドレスに対応メールライブラリ

サンプルコード

 

private void SendMail(){ string subject = &quot;サブジェクト&quot;; string body = &quot;メール本文&quot;; // エンコード取得 Encoding enc = Encoding.GetEncoding( &quot;ISO-2022-JP&quot; ); MailMessage msg = new MailMessage(); string to_name = MyBEncode( &quot;お客様のお名前&quot;, enc ); string to_address = &quot;customer@address.com&quot;; string from_name = MyBEncode( &quot;株式会社 アジルテック&quot;, enc ); string from_address = &quot;developper@agile-tech.com&quot;; // SubjectとBodyのエンコードを指定してはいけない // msg.BodyEncoding = enc; // msg.SubjectEncoding = enc; msg.To.Add( new MailAddress( to_address, to_name ) ); msg.From = new MailAddress( from_address, from_name ); msg.Subject = MyBEncode( subject, enc ); // エンコード文字列を渡す msg.Body = string.Empty; // 空に指定 // 本文をバイト列に返還 byte[] buf = enc.GetBytes( body ); // AlternateViewのためにメモリストリームを作成 MemoryStream mem = new MemoryStream( buf ); AlternateView alt_msg = new AlternateView( mem, new ContentType(&quot;text/plain; charset=&quot; + enc.BodyName ) ); alt_msg.TransferEncoding = TransferEncoding.SevenBit; msg.AlternateViews.Add( alt_msg ); msg.IsBodyHtml = false; SmtpClient smtp = new SmtpClient( &quot;smtp.agile-tech.com&quot; ); // SMTPサーバを指定 smtp.Send( msg ); } private string MyBEncode( string str, Encoding encode ) { return &quot;=?iso-2022-jp?B?&quot; + Convert.ToBase64String( encode.GetBytes( str ) ) + &quot;?=&quot;; }

 

メッセージ送信

この記事に対してのご意見をお聞かせ下さい。
頂いたメッセージは訪問者様に読みやすいよう整形したのち公開させて頂く場合がございます。
 



 
spacer
spacer