24 April 2009

(PHP) Net_SMTPおよびNet_Mailでのメール送信

PHPでの日本語メールの送信方法メモ。

RFC2822 (Internet Message Format)で、行の長さの制限 この仕様が 1 行の文字数に課す制限は二つある。CRLF を除いて、各行は 998 文字を超えてはならならず(MUST)、78 文字を超えるべきではない(SHOULD)。 となっているため、長い文章の自動改行処理を行わないといけない。

また、日本語は全てJIS('ISO-2022-JP)に変換するとともに、題名(Subject)はMIMEエンコードしなければならない。
さらに、2バイト文字コードのうち、2バイト目が”5C”の場合に、自動改行処理等で用いている mb_XXXX系のPHP関数がエラーを起こすので、スクリプトはUTF-8で保存する。

※ Net_SMTP版とNet_mime版の差異は、ヘッダ部分の『Content-Type』属性が、Net_mime版ではクオーテーションマークで属性が囲まれる。たとえば、『charset="ISO-2022-JP"』のようになる。この表記で読み取り川でエラーが発生する可能性もある。(例:PearのNet_IMAPではエラーになる)

PearのNet_SMTPパッケージのみを利用する場合

<?php // PEARの Net_SMTPコンポーネントを用いる require_once("Net/SMTP.php"); // 言語と文字コードの設定 mb_language('Japanese'); mb_internal_encoding('ISO-2022-JP'); mb_http_output('UTF-8'); /* SMTPサーバ名、送受信者アドレスの指定 */ $host = 'mail.example.com'; $from = 'sample@example.com'; $rcpt = 'user@gmail.com'; $smtp_user = 'user'; $smtp_password = 'password'; /* 新しい Net_SMTP オブジェクトを作成します */ if (! ($smtp = new Net_SMTP($host))) { die("ネットワーク異常(SMTPオブジェクト作成不能)"); } /* デバッグ出力を、stdoutに送る */ // $smtp->setDebug(false); /* SMTP サーバに接続します */ if (PEAR::isError($e = $smtp->connect())) { die("ネットワーク異常(SMTPでコネクション不能)"); } /* ユーザ名、パスワードを用いてSMTPログオンします */ if (PEAR::isError($e = $smtp->auth($smtp_user, $smtp_password, 'LOGIN'))) { $smtp->disconnect(); die("SMTPサーバの認証に失敗"); } /* SMTP コマンド 'MAIL FROM:' を送信します */ if (PEAR::isError($smtp->mailFrom($from))) { $smtp->disconnect(); die("送信者アドレスがSMTPサーバで許可されませんでした"); } /* 受信者のアドレスを指定します */ if (PEAR::isError($res = $smtp->rcptTo($rcpt))) { $smtp->disconnect(); die("受信者アドレスがSMTPサーバで許可されませんでした"); } /* 本文テキスト部分を 75 桁で折り返し処理 */ // 文字列を一旦JISコードに変換する(全角=2バイト、半角=1バイトで扱うため) $strMessage = mb_convert_encoding($_POST['message'], 'ISO-2022-JP', 'UTF-8'); $strMessageWrap = ""; $nCountCol = 0; for($i=0; $i<mb_strlen($strMessage); $i++) { $substr= mb_substr($strMessage, $i, 1, 'ISO-2022-JP'); $strMessageWrap .= $substr; if($substr == "\n") $nCountCol = 0; // 改行コードを検知した場合は、桁数をリセット $nCountCol += strlen($substr)==1?1:2; // 切り出された文字のバイト数 if($nCountCol >= 76) { // 76桁目で改行コードを挿入 $nCountCol=0; $strMessageWrap .= "\n"; } } /* メールの本文を設定します */ $strBody = "User-Agent: PHP Pear Net_SMTP\n"; $strBody .= "To: user <user@gmail.com>\n"; $strBody .= "From: sample <sample@example.com>\n"; // 題名はMIMEエンコードする $strBody .= 'Subject: ' . mb_encode_mimeheader(mb_convert_encoding($_POST['subject'], 'ISO-2022-JP', 'UTF-8'), 'ISO-2022-JP', 'Q') . "\r\n"; $strBody .= "Content-Type: text/plain; charset=ISO-2022-JP\n"; $strBody .= "Content-Transfer-Encoding: 7bit\n"; $strBody .= "\n\n"; // 本文の前に改行コードを送る // 本文はJISエンコード(桁数揃えのところで既に変換済みのため、念のため) $strBody .= mb_convert_encoding($strMessageWrap, 'ISO-2022-JP', 'auto'); if (PEAR::isError($smtp->data($strBody))) { $smtp->disconnect(); die("本文の送信に失敗"); } /* SMTP サーバとの接続を切断します */ $smtp->disconnect();

次に、Net_Mail と Net_mime コンポーネントを用いる場合

<?php // PEARの Net_SMTPコンポーネントを用いる require_once("Net/SMTP.php"); // 言語と文字コードの設定 mb_language('Japanese'); mb_internal_encoding('ISO-2022-JP'); mb_http_output('UTF-8'); /* SMTPサーバ名、送受信者アドレスの指定 */ $host = 'mail.example.com'; $from = 'sample@example.com'; $rcpt = 'user@gmail.com'; $smtp_user = 'user'; $smtp_password = 'password'; /* 新しい Mail_Mime オブジェクトを作成します */ $mime = new Mail_mime("\n"); /* 本文テキスト部分を 75 桁で折り返し処理 */ // 文字列をJISコードに変換する(全角=2バイト、半角=1バイトで扱うため) $strMessage = mb_convert_encoding($_POST['message'], 'ISO-2022-JP', 'UTF-8'); $strMessageWrap = ""; $nCountCol = 0; for($i=0; $i<mb_strlen($strMessage); $i++) { $substr= mb_substr($strMessage, $i, 1, 'ISO-2022-JP'); $strMessageWrap .= $substr; if($substr == "\n") $nCountCol = 0; // 改行コードを検知した場合は、桁数をリセット $nCountCol += strlen($substr)==1?1:2; // 切り出された文字のバイト数 if($nCountCol >= 76) { // 76桁目で改行コードを挿入 $nCountCol=0; $strMessageWrap .= "\n"; } } /* 本文を$mimeにセット */ $strMessageWrap = "\n\n" . $strMessageWrap; // データ開始前に改行 $mime->setTxtBody(mb_convert_encoding($strMessageWrap, 'ISO-2022-JP', 'auto')); /* ヘッダ情報を$mimeにセット*/ $headers = array( "User-Agent" => 'PHP Pear Net_mime', "To" => mb_encode_mimeheader(mb_convert_encoding($rcptname, 'ISO-2022-JP', 'UTF-8'))."<".$rcpt.">", "From" => mb_encode_mimeheader(mb_convert_encoding($fromname, 'ISO-2022-JP', 'UTF-8'))."<".$from.">", "Subject" => mb_encode_mimeheader(mb_convert_encoding($_POST['subject'], 'ISO-2022-JP', 'UTF-8'), 'ISO-2022-JP', 'Q') ); /* 文字エンコード */ $param = array( 'head_charset' => 'ISO-2022-JP' 'text_charset' => 'ISO-2022-JP', 'text_encoding' => '7bit', ); /* Mail_Mime オブジェクトを構築 */ $body = $mime->get($param); $headers = $mime->headers($headers); $smtp = array( 'host' => $host, 'port' => 25, 'auth' => TRUE, 'username' => $smtp_user, 'password' => $smtp_password, 'debug' => FALSE, ); /* 新しいMailオブジェクトを作成 */ $mail =& Mail::factory('smtp', $smtp); /* メールの送信 */ if (PEAR::isError($mail->send($rcpt, $headers, $body))) { die("送信エラー発生\n"); }