2008年7月11日

[PHP-users 33813] Re:既存のmail、mb_send_mailを使用しないSMTP送信について


SMPTPの送信の参考のプログラムが出て来たので、私も参考プログラムを・・。
(色々改修途中ではありますが)

PHPを勉強しながら作っていたものなので、全てのメールサーバが受信して
もらえるかどうかは、保証できないのと、ESAMPTでは無く、SMTPで処理を
しているものになります。(多分改造の余地と、ステップを減らすこと等
、突っ込みどころは色々あるとは思いますが)


sendmail_one_server_fで、1つのメールサーバにのみメールを送信できます。

送信時のcharsetは、引数にしてありますが、ISO-2022-JPで固定しています
(当初は、UTF-8で送りたかったのですが、携帯やメールソフトによっては
文字化けが発生したので、日本でスタンダードなISO-2022-JPに固定しています。)

送信は、text/plainしか対応していません。

送信先(to)は1メールアドレス固定です。
(to:に a@xxxxx,b@xxxxx の様に複数のアドレスは設定出来ません)


流れとしては、

・送信するメールの本文を作成する。
・送信先のメールサーバをMXレコードを元に一覧を入手する。
・重みを元に、メールサーバへの接続をトライして行く
 1つのドメインで、複数のIPがセットされていたら、それも総当りで接続して
いく。
・接続できたら、メールの送信を行う。


自分が運営しているサイトで、このプログラムは使用しているので、それなりに
は動くかと思います。
(クレームが来ていないと言うことは、問題が無いのかな・・・(笑))


追記:デバック時の表示がコメント状態で残っています。
追記2:関数としては、作成途中で機能としては不十分です。
追記3:テストするときには、サーバでテストしてください、
    サーバで動作させる前提で作っています。(25ポートを閉じられた環境
では、ソケットを接続することは不可能です)


作った経緯としては、メールの返信をする時に、返信メールをメールキューに残
さず、返信先のメールサーバに直接メールを預けてしまいたい。
(メールキューを使用してメールを送ると、送信できなかった時に管理者宛
にメールが返ってくるので嫌だったかから。)


<?
/*
* メールを1通のみ送信する。
* $charset: 送信時のエンコード形式
* $from: 送信元アドレス
* $namefrom: 送信元名称
* $to: 送信先
* $nameto: 送信先名称
* $subject: タイトル
* $message: 本文
* $atach array(): 添付ファイル、$atache[添付ファイル名] = 添付データ(バイナリ)
* $atach は初期段階では未対応
*/
function sendmail_one_server_f($charset, $from, $namefrom, $to, $nameto, $subject, $message, $atach =array()){
$newLine = "\r\n"; // 改行コード
$charset = "ISO-2022-JP";
// エンコードを実行して送信するデータを作成する。

ini_set("mbstring.internal_encoding","UTF-8"); //UTF-8で書かれていることを宣言。

// サブジェクトをエンコードする。
$esubject = mb_encode_mimeheader($subject,$charset);

//ヘッダー部分を作成する。
$header = "MIME-Version: 1.0".$newLine;
$header .= "Content-Type: text/plain; charset=ISO-2022-JP".$newLine;
$header .= "Content-Transfer-Encoding: 8bit".$newLine;
$header .= "Date: ".date("r").$newLine;
$header .= "From: ".mb_encode_mimeheader($namefrom,$charset)."<".$from.">".$newLine;
$header .= "To: ".mb_encode_mimeheader($nameto,$charset)."<".$to.">".$newLine;
$header .= "Subject: ".$esubject.$newLine;
$header .= $newLine; // ヘッダー終了

$data = $header.$message;
// messageを分解して、行頭に"."があったら、".."に変換する。
$mesary = preg_split("/\r\n|\r|\n/",$message);
$newmessage ="";
foreach( $mesary as $value ){
if ( substr($value,0,1) == "." ){
$value = ".".$value;
}
$newmessage .= $value.$newLine;
}
$newmessage = mb_convert_encoding($newmessage,"ISO-2022-JP");
$data = $header.$newmessage;

//echo "data:".htmlspecialchars($data)."<br>\n";
$ret = sendmail_one_server($from,$to,$data);
return $ret;
}
/*
* メールを1通のみ送信する。
* 送信先アドレスが複数設定されていても、最初の1つにしか送信しません。
* $from: 送信元アドレス "アドレス"形式、内部で"<アドレス>"形式に変換する。
* $to: 送信先アドレス "アドレス"形式、内部で"<アドレス>"形式に変換する。
* $data: 本文
*/
function sendmail_one_server($from, $to, $data){

//echo "from:$from|to:$to<br>\n";
// 各種設定
$port = 25; // SMTPポート番号
$timeout = 5; // 接続タイムアウト
$sendservername = MAIL_DOMAIN; // 自分のサーバ名称
$newLine = "\r\n"; // 改行コード

$result = -1; // 送信できなかったエラーを設定しておく。

// 送信先アドレスからホスト名を求める。
$hostname = get_host_name($to);
// ホスト名から接続先IPを求める。
$iptables = get_mail_hostsl($hostname);
// 接続先IPにソケットを接続する。
//print_r($iptables);
foreach ( $iptables as $ip => $weight ){
//echo "connect ip $ip<br>\n";
$smtpConnect = fsockopen($ip, $port, $errno, $errstr, $timeout);
//print_r($smtpConnec);
if(empty($smtpConnect))
{
// 接続できなかった。
$result = "メールサーバに接続出来ませんでした(".$ip.")";
continue;
}
else
{
// 接続出来た。
$smtpResponse = fgets($smtpConnect, 4096);
//echo "smtpResponse:$smtpResponse<br>\n";
$status = substr($smtpResponse,0,3);
if ($status <> "220"){
// 接続出来たが、メールの送信は出来ない。
fclose($smtpConnect); // ソケットをクローズ
$result = $smtpResponse;
break;
}
// 接続出来たら、HELOを送信
fputs($smtpConnect, "HELO $sendservername". $newLine);
$smtpResponse = fgets($smtpConnect, 4096);
//echo "HELO smtpResponse:$smtpResponse<br>\n";
$status = substr($smtpResponse,0,3);
if ($status <> "250"){
// HELOが正常に受け入れらなかった。
fclose($smtpConnect); // ソケットをクローズ
$result = $smtpResponse;
break;
}
// MAIL FROM:を送信
fputs($smtpConnect, "MAIL FROM:<$from>". $newLine);
$smtpResponse = fgets($smtpConnect, 4096);
//echo "MAIL_FROM smtpResponse:$smtpResponse<br>\n";
$status = substr($smtpResponse,0,3);
if ($status <> "250"){
// MAIL FROMが正常に受け入れらなかった。
fclose($smtpConnect); // ソケットをクローズ
$result = $smtpResponse;
break;
}
// RCPT TO:を送信
fputs($smtpConnect, "RCPT TO:<$to>". $newLine);
$smtpResponse = fgets($smtpConnect, 4096);
//echo "RCPT smtpResponse:$smtpResponse<br>\n";
$status = substr($smtpResponse,0,3);
if ($status <> "250"){
// RCPT TOが正常に受け入れらなかった。
fclose($smtpConnect); // ソケットをクローズ
$result = $smtpResponse;
break;
}
// DATAを送信
fputs($smtpConnect, "DATA". $newLine);
$smtpResponse = fgets($smtpConnect, 4096);
//echo "DATA smtpResponse:$smtpResponse<br>\n";
$status = substr($smtpResponse,0,3);
if ($status <> "354"){
// DATAが正常に受け入れらなかった。
fclose($smtpConnect); // ソケットをクローズ
$result = $smtpResponse;
break;
}
// 本文を送信
//echo "-----------------DATA-----------------------\n";
//print_r($data);
//echo "<br>-------------DATA END -------------------\n";
fputs($smtpConnect, $data. $newLine);
// 本文を終了
fputs($smtpConnect, ".". $newLine);
$smtpResponse = fgets($smtpConnect, 4096);
//echo "DATA END .LF smtpResponse:$smtpResponse<br>\n";
$status = substr($smtpResponse,0,3);
if ($status <> "250"){
// DATAが正常に受け入れらなかった。
fclose($smtpConnect); // ソケットをクローズ
$result = $smtpResponse;
break;
}
// QUITを送信
fputs($smtpConnect, "QUIT". $newLine);
$smtpResponse = fgets($smtpConnect, 4096);
$status = substr($smtpResponse,0,3);
if ($status <> "221"){
// QUITが正常に受け入れらなかった。
// QUIT以外が正常に終わっていれば、メールの送信は終了している。
$result = NULL;
}
// 正常に送信できたので、リターンステータスを正常送信にセットする。
$result = NULL;
fclose($smtpConnect); // ソケットをクローズ
break;
}
}
return $result;
}
/*
* メールアドレスから、ホスト名を分離して返す。
* 区切り文字は、@を区切り文字として使用し、@で区切られた最後の文字列を返す。
*/

function get_host_name($mailfromaddr){
$hostname = $mailfromaddr;
$matches = array();
$matches = explode('@', $mailfromaddr);
$hostname = $matches[count($matches) -1 ];
return $hostname;
}
/*
* ホスト名から、MXレコードを元にメール送信先のIPを求める。
*/
function get_mail_hostsl($mailhost){
$mshosts = array();
$weight = array();
$hosttable = array();
$iptables = array();

$ret = getmxrr ( $mailhost, $mxhosts , $weight );
if ( $ret ){
} else {
// MXレコードが無い
// 一番重く(0)して、ホストをセットする。
$mxhosts[0] = $mailhost;
$weight[0] = 0;
}

// ホスト毎に重み付けを行う。
foreach ( $mxhosts as $key => $value ){
$hosttable[$value] = $weight[$key];
}
asort( $hosttable,SORT_NUME);
// HostNameを元にIPアドレスのテーブルを作成する。
foreach ($hosttable as $host => $host_weight ){
if (($iparray = gethostbynamel($host)) === FALSE ){
error_disp("","メール返信先のサーバ[".$mailhost."]からIPが解決出来ませんでした");
}
foreach( $iparray as $ip ){
$iptables[$ip] = $host_weight;
}
}
return $iptables;
}

?>

--
かめおやじと申します、お見知りおきを
個人で運営中のサイト
http://trialgoods.com/emoji 絵文字を使いこなして見るページ
http://katte.mixsn.com 勝手にシリーズ(ことば遊びサイト)
http://mobile.mixed-in.com 携帯向けの絵文字の情報サイト
http://15qm.com 15分間のメルアドサービス
                  (アングラなサイトですが 笑)
kameoyaji <kenjik@xxxxx>

_______________________________________________
PHP-users mailing list PHP-users@xxxxx
http://ml.php.gr.jp/mailman/listinfo/php-users
PHP初心者のためのページ - 質問する前にはこちらをお読みください
http://oldwww.php.gr.jp/php/novice.php3


投稿者 xml-rpc : 2008年7月11日 09:41
役に立ちました?:
過去のフィードバック 平均:(0) 総合:(0) 投票回数:(0)
本記事へのTrackback: http://hoop.euqset.org/blog/mt-tb2006.cgi/75212
トラックバック
コメント
コメントする




画像の中に見える文字を入力してください。