趣味と実益と現実逃避で更新されるblogです.
役に立つことから無意味なことまでさまざま書いていきます.
|
February 15th, 2006 |
PHPのmail()のできの悪さ(Read: 540491) |
参照: [cvs] Log of /php/src/ext/standard/mail.c |
大分読めるようになったので改めてメモ. |
どうにも頭の悪い実装をしている. ;
bugs.php.netに書くのも面倒くさい… SKIP_LONG_HEADER_SEPはこれ. ようするに「\r\n[ \t\+]」な文字列を飛ばします. #define SKIP_LONG_HEADER_SEP(str, pos) \ if (str[pos] == '\r' && str[pos + 1] == '\n' && (str[pos + 2] == ' ' || str[pos + 2] == '\t')) { \ pos += 3; \ while (str[pos] == ' ' || str[pos] == '\t') { \ pos++; \ } \ continue; \ } \ ようするに不当な制御コードをここで落とします.ただし上記マクロを使って「改行」+「行頭の空白」は例外とします. if (to_len > 0) { to_r = estrndup(to, to_len); for (; to_len; to_len--) { if (!isspace((unsigned char) to_r[to_len - 1])) { break; } to_r[to_len - 1] = '\0'; } for (i = 0; to_r[i]; i++) { if (iscntrl((unsigned char) to_r[i])) { /* According to RFC 822, section 3.1.1 long headers may be separated into * parts using CRLF followed at least one linear-white-space character ('\t' or ' '). * To prevent these separators from being replaced with a space, we use the * SKIP_LONG_HEADER_SEP to skip over them. */ SKIP_LONG_HEADER_SEP(to_r, i); to_r[i] = ' '; } } } else { to_r = to; } この関数は改行コードを2種類使い分けなければいけなくて, ・第3引数のメッセージ本文の改行はLF(\n)で行うこと ・第4引数の$additional_headersの改行はCR+LF(\r\n)で行うこと となっています.頭悪いですね.一応$toもメール的にはヘッダーなので $additional_headersと同等に「\r\n」で改行を表現します. 日本語メールとしてはmb_encode_mimeheader()を改行コードをいじらずに使用すればその状態になります. ちなみに第2引数の$subjectも同様のフィルタリングが行われます. で,Windows以外はphp_mail()という内部関数が実行されます. ここのコードを読んでみましょう. fprintf(sendmail, "To: %s\n", to); fprintf(sendmail, "Subject: %s\n", subject); if (headers != NULL) { fprintf(sendmail, "%s\n", headers); } fprintf(sendmail, "\n%s\n", message); ret = pclose(sendmail); そうです改行コードが複数混在です. バカですね. ちなみにWindowsは独自にSMTP処理が組み込まれた関数がwin32/sendmail.cに実装されています. 説明は面倒なので省くと(苦笑 こちらの方がとてもまともな実装をしています. ・内部関数php_win32_mail_trim_header()にて$additional_headersのフィルタリング - 改行コード修正(LF/CRのCR+LF化) - ダブル改行の除去 ・内部関数PostHeader()にてDateフィールドが存在しない場合,自前で生成して付加 Content-Typeとかも吐いてくれてもいいじゃないかとか思うけど余計なおせっかいかもしれない. ただしUNIX系でsendmailコマンドで処理されるメールより適当なテキストでもそこそこまともなメールを生成します. メール送信はmail()と,とあるひとつの理由だけで使い勝手のあるmb_send_mail()とを常用してたけどなんかmail()も嫌になってきた. ちなみにmail()がどんなメールテキストを生成してるかはこういう手順を踏むと簡単です. PEAR::MailやphpMailerもi18n的には問題あるしどうしたものか. $ echo "#!/bin/sh" > mail.sh $ echo "cat - > mail.txt" >> mail.sh $ php -d "sendmail_path=./mail.sh" -r 'mail( "foo@example.com", "foo\r\n foo\n foo\nfoo", "body", "From: foo@example.com\r\nX-Foo: foo\nX-Bar: bar");' $ od -tx1 mail.txt 0000000 54 6f 3a 20 66 6f 6f 40 65 78 61 6d 70 6c 65 2e 0000020 63 6f 6d 0a 53 75 62 6a 65 63 74 3a 20 66 6f 6f 0000040 0d 0a 20 66 6f 6f 20 20 66 6f 6f 20 66 6f 6f 0a 0000060 46 72 6f 6d 3a 20 66 6f 6f 40 65 78 61 6d 70 6c 0000100 65 2e 63 6f 6d 0d 0a 58 2d 46 6f 6f 3a 20 66 6f 0000120 6f 0a 58 2d 42 61 72 3a 20 62 61 72 0a 0a 62 6f 0000140 64 79 0a |
ELF Feb 15th, 2006 18:07 / [ 編集 ]
[ コメントする ] [ トラックバック(1) ] [ ] |
[ POST ] [ AddLink ] [ CtlPanel ]
<< | 9月 | >> | ||||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | * | * | * | * | * |