[PHP-users 16627]PEARのmimeDecodeで日本語ファイル名を化けずに受け取るには

藤本悟司 satoshi @ try-square.co.jp
2003年 7月 10日 (木) 20:28:57 JST


はじめて投稿します。藤本と申します。

PHP-4.2.2と、PEARのMail_Mime-1.2.1を利用しています。
おそらく正式にはサポートされていない、日本語のファイル名を
正しく受け取る方法について、ご相談なのですが。

例えば「技術メモ.txt」というファイルを添付すると、
MIMEヘッダの中で
Content-Disposition: attachment;
	filename="=?ISO-2022-JP?B?GyRCNTs9USVhJWIbKEIudHh0?="
        ↑"技術メモ.txt"をmimeheaderエンコードしたもの
のような形で書かれると理解しています。

次のようなソースでそのままこのファイル名を取得、表示しようと
すると、化けてしまいます。

    // $mailtextにメール本文を取得
  (省略)

    // デコード方法の指定
    $params['include_bodies'] = TRUE;
    $params['decode_bodies']  = TRUE;
    $params['decode_headers'] = TRUE;

    // デコード処理
    $decoder = new Mail_mimeDecode( $mailtext );

    // メールの構造を取得
    $mail = $decoder->decode( $params );

    // 複数パート?(添付ファイルがある?)
    if ( count($mail->parts) > 1 ) {
        // 添付ファイルがある場合、multipartからbodyおよびfileを取得
        // (一つ目は本文、二つ目は添付ファイルと仮定。三つ目以降は無視)
        $body = $mail->parts[0]->body;
        $file = $mail->parts[1]->body;
        $filename = $mail->parts[1]->d_parameters['filename'];
        if ( $filename == "" ) {
            // オプションフィールド名がnameだった場合
            $filename = $mail->parts[1]->d_parameters['name'];
        }
    } else {
        // 添付ファイルが無い場合、そのままbodyを取得
        $body = $mail->body;
    }

    $header = $mail->headers;

    // デバッグ表示
    printf("本文:%s\n", $body );
    printf("差出人:%s\n", $header['from'] );
    printf("件名:%s\n", $header['subject'] );
    printf("添付ファイル:'%s'\n", $filename);

デバッグ表示される結果は、本文、差出人、件名は正常でしたが
添付ファイル名だけが化けていました。
(mb_detect_encoding()で調べたところ、いずれもJIS)

Mail_Mimeに含まれる、mimeDecode.phpのソースを追ってみたのですが、
原因は、
    function _parseHeaderValue()
が呼ばれる順序にあるようです。すなわち・・・

 1) メールヘッダをparseして各フィールドに切り分ける
 2) 各フィールドをデコードする(decode_headersがTRUEの場合)
 3) 各フィールドをparseしてオプションに切り分ける
  (function _parseHeaderValue())

という順となっているため、上記関数の中では、既にJIS(?)に
デコードされてしまった文字列をpreg_splitしているようです。

filename="技術メモ.txt"
  をurlencodeしてみると、次のようになります。
filename%3D%22%1B%24B5%3B%3DQ%25a%25b%1B%28B.txt%22
     ↑       ↑
     "="      "="?

「技術メモ」の途中でpreg_splitでちぎられているのではないか?と
思ったら、ビンゴでした。

そこで対策なのですが、以下のようなコードを加えました。

423行目:
    function _parseHeaderValue($input)
    {
// added
$input = mb_convert_encoding( $input, "EUC-JP", "JIS" );

442行目:
        $param_value = substr($parameters[$i], $pos + 1);
        if ($param_value[0] == '"') {
            $param_value = substr($param_value, 1, -1);
        }
// added
$param_value = mb_convert_encoding( $param_value, "JIS", "EUC-JP" );

        $return['other'][$param_name] = $param_value;
        $return['other'][strtolower($param_name)] = $param_value;

関数の入り口でEUC-JPに変換してやって、値を切り出したあとJISに
戻してやる、というものです。
おそらく、EUC-JPならば、イコールやダブルクオートなどと
まぎらわしいコードは、含まれないと思ったからです。

このような方法で、どこかに問題ありませんでしょうか?
識者の方の、ご意見をお願いいたします。

長文で恐縮ですが、よろしくお願いします。

----
藤本 悟司


PHP-users メーリングリストの案内