

先日、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サーバは受け取ります。(ただし、端末によっては表示されない可能性あり)
まとめ:
RFCの作法は守りたいという方は、Socketから直接SMTPサーバと通信してください。
[追記 2008/11/20 ] 弊社より、サブジェクト文字化けとRFC非準拠アドレスに対応したSMTPClientを公開しました。現在、β公開につき無料で利用できます。ぜひお試しください。
ASP.NETはweb.config中のglobalizationを指定するだけで、文字コードを簡単に変更することができます。
<globalization requestEncoding="UTF-8" responseEncoding="UTF-8"/>
PC向けのサイトではなにも考えずにUTF-8でサイトを構築してもいいのですが、携帯向けサイトですと、SHIF-JISでエンコードする必要があります。
<globalization requestEncoding="Shift-JIS" responseEncoding="Shift-JIS"/>
この場合、ASP.NETのサーバオブジェクトは、クライアントからのポストデータをShift-JISエンコードとして自動処理します。
単一のページで、PCと携帯の両対応を目論む方の中には、「PC向けにはUTF-8で、携帯向けにはShift-JISで表示したい」と考えるかもしれません。(迷信ですが、SEO的にUTF-8のほうが良いと言われているため)
この場合、globalizationではUTF-8を指定しておき、UserAgentから携帯電話端末を識別して、Responseオブジェクトに1行追加するだけでShift-JISにすることができます。
Response.ContentEncoding = System.Text.Encoding.GetEncoding( "Shift-JIS" );
ただし、これを行うと、Softbank&Vodafone端末で文字化けがおこります!
理由は、.NET FrameworkはDoCoMoとKDDIとJ-PHONEはご存知なのですが、「Vodafone?Softbank?だれそれ?」状態です。ですので、browser情報を追加する必要があります。
ASP.NETフォルダの追加からApp_Browsersフォルダを追加して、その中にVodafoneとSoftbankのための情報を追加してください。特に重要なのは下記の1行です。
<capability name="preferredResponseEncoding" value="shift_jis" />
アプリケーション単体ではなく、サーバ自体に設定を追加する場合は、こちらの記事を参考にしてください。
以上、ASP.NET 2.0の話です。ASP.NET 1.1の環境ではBrowser Capsで対応してください。
その他資料:
BrwoserCapsファイルダウンロードするプログラムを作成する際にはHTTPヘッダに下記の表記を追加します。
Content-Disposition: attachement; filename=<ファイル名>
しかし、ダウンロードファイルに日本語や記号を含む場合、なにもしないと文字化けが発生します。
これは、各社のブラウザがContent-Dispositionのフォーマットに関する実装をRFC通りに実装していなため、英語圏以外で問題が発生しています。
ファイルをダウンロードする ASP.NET Web ページで日本語ファイル名が文字化けする
原因
Internet Explorerでは、Content-Disposition ヘッダが送信された場合、送られてきたコンテンツをそのままブラウザで開かずにファイルダウンロードダイアログを表示するようになっています。その際にこのヘッダの filename パラメータを利用している場合、このパラメータで渡されたファイル名をファイルの保存時の既定のファイル名にします。ただし、クライアントコンピュータのロケールを日本語にしている場合、Internet Explorer ではこのパラメータで渡された文字列 (ファイル名) を Shift-JIS でエンコードされているものとして処理します。
また、ASP.NET および .NET Framework では、構成ファイル globalization エントリ responseEncoding 属性の設定に関わらず、HTTP ヘッダを UTF-8 でエンコードして送信します。Internet Explorer では、UTF-8 でエンコードされたファイル名を Shift-JIS でエンコードされているものとして処理するため、ファイル名を正しく表示できません。
Content-Disposition ヘッダのパラメータの文字コードのエンコード方式に関しては、RFC2231 に基づくべきですが、現行の Internet Explorer はこのエンコード方式をサポートしていません。 RFC2231 につきましては、以下の URL をご参照ください。
Microsoftではファイル名をURLエンコードして回避するようにと記載していますが、これですとFireFoxなどほかのブラウザで文字化けをしてしまします。
ブラウザ毎にContent-Dispositionの記載を変更できるのであれば、IE向けにはURLエンコードをし、FireFoxにはなにもしないという対処もあります。
ASP.NET環境であれば下記の対応をするのがもっともシンプルかとおもいます。
Response.HeaderEncoding = System.Text.Encoding.GetEncoding( "SHIFT-JIS" );
IE用に強引にSHIFT-JIS送信してあげればいいわけです。
ただし、これでは一部文字がまだ文字化けします。化けるのは2バイトコードで考えた場合、下位バイトが「5C」、「7C」の場合です。
「予 (0x975c)」→「誉 (0x975f)」 「表 (0x955c)」→「廟 (0x955f)」
これを避けるためには、該当する文字をURLエンコーディングしてあげます。(すべてURLエンコーディングしないのは、一定以上の文字列長になった場合にファイル名を省略するのを避けるためです)
そのほか参考情報

