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

kameoyaji kenjik @ trialgoods.com
2008年 7月 11日 (金) 09:41:04 JST


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

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

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

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

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

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


流れとしては、

・送信するメールの本文を作成する。
・送信先のメールサーバを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 @ trialgoods.com>



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