メールをPHPで受信→外部SMTPで転送 - PHPプロ!Q&A掲示板

4869

  • 募集中!! 0P

メールをPHPで受信→外部SMTPで転送

質問日時 / 2017年10月4日 11:23    回答数 / 6件

Questioner:  uskdin  このエントリーをはてなブックマークに追加 

キーワード / PHP    PEAR    SMTP   

PostfixによりメールをPHPで受信→複数アドレスに転送を実現しようと試行錯誤した結果
sendmailやmailでは「なりすまし」として迷惑メール扱いになってしまったので

受信したメールを分解して外部SMTPで転送しようとしています。

下記のように書いたのですが、転送はされるのですが

①Content-Typeの設定がおかしいのか、本文がhtmlコードで転送される。
・転送されたheaderをみると「Content-Type:multipart/mixed;」となっている

②添付ファイルは転送されていない

本文(添付ファイル含む)はそのまま転送したいのですが、どのように取り出せば良いでしょうか

  1. <?php
  2. require_once(dirname(__FILE__).'/../PEAR/Mail/Mail.php');
  3. require_once(dirname(__FILE__).'/../PEAR/Mail/mimeDecode.php');
  4.  
  5. mb_language("japanese");
  6. mb_internal_encoding("UTF-8");
  7.  
  8. // メールの文字セット
  9. define( "MAIL_CHARSET""ISO-2022-JP" );
  10.  
  11.   $params['include_bodies'] = true
  12.   $params['decode_bodies']  = true
  13.   $params['decode_headers'] = true
  14.   $params['input'] = file_get_contents("php://stdin")
  15.   $params['crlf'] = "\r\n";
  16.   $structure = Mail_mimeDecode::decode($params)
  17.  
  18.  
  19.     //送信者のメールアドレスを抽出 
  20.     $from = $structure->headers['from']
  21.     $from = addslashes($from)
  22.     $from = str_replace('"','',$from);
  23.     //署名付きの場合の処理を追加 
  24.     preg_match("/<.*>/",$from,$str)
  25.     if($str[0]!=""){ 
  26.        $str=substr($str[0],1,strlen($str[0])-2)
  27.        $from = $str
  28.       }
  29.  
  30.     //toを取得
  31.     $mail_to=$structure->headers['to'];
  32.     $mail_to = addslashes($mail_to)
  33.     $mail_to = str_replace('"','',$mail_to);
  34.     //署名付きの場合の処理を追加 
  35.     preg_match("/<.*>/",$mail_to,$str)
  36.     if($str[0]!=""){ 
  37.       $str=substr($str[0],1,strlen($str[0])-2)
  38.        $mail_to = $str
  39.       }
  40.  
  41.     // 件名を取得 
  42.     $subject = $structure->headers['subject']
  43.     $subject = mb_convert_encoding($subject,"UTF-8","JIS");
  44.  
  45. //本文取得
  46.   switch(strtolower($structure->ctype_primary)){ 
  47.     case "text"// シングルパート(テキストのみ) 
  48.       $diary_body = $structure->body;
  49.           $ctype_primary="text/plain";
  50.       break
  51.     case "multipart":  // マルチパート(画像付き) 
  52.     $ctype_primary="multipart/mixed";
  53.       foreach($structure->parts as $part){ 
  54.         switch(strtolower($part->ctype_primary)){ 
  55.           case "text"// テキスト 
  56.             $diary_body = $part->body
  57.             break
  58.           case "image"// 画像 
  59.             //画像の拡張子を取得する(小文字に変換 
  60.             $type = strtolower($part->ctype_secondary)
  61.             //JPEGチェック(GIFやPNG形式の画像チェックなども可 
  62.             if($type != "jpeg" and $type != "jpg"){ 
  63.               continue
  64.             } 
  65.             //添付内容をファイルに保存 
  66.             $fp = fopen("/tmp/picture.jpg" . $type,"w" )
  67.             $length = strlen( $part->body )
  68.             fwrite( $fp$part->body$length )
  69.             fclose( $fp )
  70.             break
  71.         } 
  72.       } 
  73.     break
  74.     default
  75.     $diary_body = ""
  76.   } 
  77.  
  78.  
  79. // smtpサーバ接続情報
  80. $param = array(
  81.     "host" => "server"
  82.     , "port" => 587
  83.     , "auth" => true
  84.     , "username" => "user"
  85.     , "password" => "password"
  86.     , "timeout" => 20
  87. );
  88.  
  89. // PEAR::Mailオブジェクト取得
  90. $obj =& Mail::factory( "smtp"$param );
  91.  
  92. // 送信先情報
  93. $to="mail-a,mail-c,mail-c";
  94.  
  95. // 送信元&件名&本文を用意し、エンコード
  96. $from = mb_encode_mimeheader( $fromMAIL_CHARSET );
  97. $subject = mb_encode_mimeheader( $subjectMAIL_CHARSET );
  98. $body = mb_convert_encoding( $diary_bodyMAIL_CHARSET"UTF-8" );
  99.  
  100. // メールヘッダ
  101. $header = array(
  102.     "From" => $from
  103.     , "To" => $to
  104.     , "Return-Path" => $from
  105.     , "Subject" => $subject
  106.     , "Content-Type" => "{$ctype_primary}; charset=" . MAIL_CHARSET
  107. );
  108.  
  109.  
  110. // メール送信
  111. $ret = $obj->send( $to$header$body );
  112. if ( PEAR::isError( $ret ) ) {        
  113.    echo "Code[" . $ret->getCode() . "], Msg[" . $ret->getMessage() . "]\n";
  114. } else {
  115.   echo "メールを送信しました。\n";
  116. }
  117.  
  118. ?>


ツリー一覧

┗A01shimix添付ファイルがあるということですから、元々のメール
 ┗A01-1uskdinshimix 様 こちらでもありがとうございます。
  ┗A01-1-1shimixcontent-typeがmultipart/mixedなのにbodyがマルチパ
   ┣A01-1-1-1uskdinshimix さま ありがとうございます。 >という
   ┣A01-1-1-2uskdinこの意見は質問者によって削除されました。
   ┣A01-1-1-3uskdin>というか「ヘッダ以外は何も変更しない」なら、ヘッ
   ┗A01-1-1-4uskdinshimix様 色々試した結果、どれもうまくいかずに

回答一覧

並び替え:

A01
answerershimix [10月4日 13:24]

添付ファイルがあるということですから、元々のメールは(メール本文がプレーンテキストであっても)multipart/mixedです(htmlメールは本来はmultipart/alternativeであるべき)。

その中からテキストパートだけを取得して(添付ファイルの拡張子がjpegもしくはjpg以外は読み飛ばしていますしipegであっても"/tmp/picture.jpg"に保存したファイルは利用していません)、content-typeは元のメールのものをそのまま使って送信しています。


>「Content-Type:multipart/mixed;」となっている
>添付ファイルは転送されていない

そういう現象になるべくしてなっています。

添付ファイルが必要なら保存しておいて、そのデータを含めたメールbodyを生成しないといけません。

この意見に回答する

ツリーへ TOPへ

A01-1
replyeruskdin [10月4日 14:17]

shimix 様

こちらでもありがとうございます。

>content-typeは元のメールのものをそのまま使って送信しています。
>「Content-Type:multipart/mixed;」となっている
>添付ファイルは転送されていない
>そういう現象になるべくしてなっています。

そうなんです。
説明下手ですいません(;^_^A
受け取ったContent-Typeのまま転送していますので、当然転送したメールのヘッダーは「Content-Type:multipart/mixed;」となっているのですが、本文が

例えば転送前は

こんにちは

と表示されていた本文が
転送したのものはheader情報は「Content-Type:multipart/mixed;」となっているのに

<html><meta>.....省略
こんにちは
</html>

といった感じでHTMLのコードがそのまま本文に表示されているのです。

この意見に回答する

ツリーへ TOPへ

A01-1-1 参考になった
replyershimix [10月4日 15:27]

content-typeがmultipart/mixedなのにbodyがマルチパートになっていない(text/plainなデータだけ)なので、メーラがどう処理するかは不定です。
マルチパートのパート部のヘッダ(text/plain)すらなくなっていますから、データをまったく扱えない状態(本文が真っ白)でもおかしくはないと思います。

そのあたりは(添付ファイルの有無でそれも転送するなら)キチンとmultipart/mixedをメールを組み立て直さないといけません。


というか「ヘッダ以外は何も変更しない」なら、ヘッダのcontent-typeとbody部の中身は「そのまま」でいいのでは?わざわざ分解する必要もないように思えますけど・・・

この意見に回答する

ツリーへ TOPへ

A01-1-1-1
replyeruskdin [10月4日 23:12] (最終編集:10月5日 00:06)

shimix さま

ありがとうございます。

>というか「ヘッダ以外は何も変更しない」なら、ヘッダのcontent-typeとbody部の中身は「そのまま」でいいのでは?わざわざ分解する必要もないように思えますけど・・・

そうなんです。
本システムの目的が、メール送信によるブログ投稿が可能な複数のサイトに一括で投稿できるシステムを構築したいと思い考えました。
もともと、下記コードのように受け取ったもののheaderだけ置き換えてsendmailに直で
送っていました。

が、なりすまし、迷惑メール扱いされうまく送信できなかったので
外部SMTPでの送信を検討しました。

上記のようにSMTPで送信する方法で
header情報だけ置き換えて送信する方法ってありますでしょうか

  1. // 送信先情報
  2. $to="mail-a";
  3.  
  4. $input = file_get_contents("php://stdin"); // 標準入力
  5. $decoder = new Mail_mimeDecode($input);
  6. $mail_data = $decoder->decode($params);
  7.  
  8.  
  9. $mail_to=$mail_data->headers['to'];
  10. $mail_to = addslashes($mail_to); 
  11. $mail_to = str_replace('"','',$mail_to);
  12. //署名付きの場合の処理を追加 
  13. preg_match("/<.*>/",$mail_to,$str); 
  14. if($str[0]!=""){ 
  15.    $str=substr($str[0],1,strlen($str[0])-2); 
  16.    $mail_to = $str; 
  17.   }
  18.  
  19.  
  20. $sendmail = popen("/usr/sbin/sendmail ".$to, "w");
  21. $input2=str_replace($mail_to,$to,$input);
  22. fwrite($sendmail, $input2);
  23. fclose($sendmail);

そういえば、
http://www.phppro.jp/qa/4867
こちらで外部SMTPのヒントをくれたのもshimix様でしたね

結局上記でResent-fromとResent-toをメールヘッダに追加しても実現できなかったので、本件に至ります。

恥ずかしながらメール周りは知識が乏しく四苦八苦しております・・

この意見に回答する

ツリーへ TOPへ

A01-1-1-2
replyeruskdin [10月4日 23:19]

この意見は質問者によって削除されました。

ツリーへ TOPへ

A01-1-1-3
replyeruskdin [10月5日 00:10]

>というか「ヘッダ以外は何も変更しない」なら、ヘッダのcontent-typeとbody部の中身は「そのまま」でいいのでは?わざわざ分解する必要もないように思えますけど・・・

コードを眺めながら・・・

  1. $structure = Mail_mimeDecode::decode($params);

 $structure に入ったheaderの必要な情報だけ置き換えて

  1. $ret = $obj->send( $to, $structure->headers, $structure-> parts);

とすればいいんですかね!
一度やってみます

この意見に回答する

ツリーへ TOPへ

A01-1-1-4
replyeruskdin [10月6日 18:10]

shimix様

色々試した結果、どれもうまくいかずに
結局、http://www.phppro.jp/qa/4008
の添付して送る方法をやってみようと思い試していますが、
addAttachmentにmessage/rfc822形式で添付の情報も少なく
うまく添付されません

おかしな点などありましたらご指摘いただけないでしょうか

上記の回答の時はPHPmailerを利用されていましたが、
こちらでPEARで試験中です。

  1. <?php
  2. header('Content-Type: text/html; charset=UTF-8');
  3.  
  4. require_once(dirname(__FILE__)."/../model/interface_php.php");
  5. require_once(dirname(__FILE__).'/../PEAR/Mail/Mail.php');
  6. require_once(dirname(__FILE__).'/../PEAR/Mail/mime.php');
  7.  
  8. mb_language("japanese");
  9. mb_internal_encoding("UTF-8");
  10.  
  11. define( "MAIL_CHARSET""ISO-2022-JP" );
  12.  
  13. $input = file_get_contents("php://stdin");
  14.  
  15. $from='hoge@mail.com';
  16. $mailto='hoge@mail.net';
  17.  
  18. $subject='転送メール';
  19.  
  20. $body = "添付ファイルのテストです。";
  21.  
  22. $mimeObject = new Mail_Mime("¥n");
  23. $mimeObject -> setTxtBody($body);
  24. $mimeObject -> addAttachment($input"message/rfc822");
  25. $bodyParam = array(
  26.   "text_charset" => "UTF-8"
  27. );
  28. $body = $mimeObject -> get($bodyParam);
  29.  
  30. // smtpサーバ接続情報
  31. $param = array(
  32.   "host" => "host"
  33.   , "port" => 587
  34.   , "auth" => true
  35.   , "username" => "user"
  36.   , "password" => "pass"
  37.   , "timeout" => 20
  38. );
  39.  
  40.  
  41.   // PEAR::Mailオブジェクト取得
  42.   $obj =& Mail::factory( "smtp"$param );
  43.   // メールヘッダ
  44.   $header = array(
  45.     "From" => $from
  46.     , "To" => $mailto
  47.     , "Return-Path" => $from
  48.     , "Subject" => $subject
  49.   );
  50.  
  51.   // メール送信
  52.   $ret = $obj->send( $mailto$header$body );
  53.   if ( PEAR::isError( $ret ) ) {
  54.     $ms"Code[" . $ret->getCode() . "], Msg[" . $ret->getMessage() . "]\n";
  55.   } else {
  56.     $ms$to."にメールを送信しました。\n";
  57.   }
  58.  
  59. ?>

この意見に回答する

ツリーへ TOPへ

<<質問一覧へ



Pick Up Q&A

Q
ログファイルの中の空のデータ行を削除したい
 このエントリーをはてなブックマークに追加 
A
ログのデータ個数(列数)が固定で、空のログが"<><><>"だと既知であれば if ($line === "<><><>") { continue; } で読み飛ばしてもいいのでは? ...

>>続きを読む

まずは配列や文字列の扱いから、じっくり勉強して行きましょう。

▲解説者:岡本(アシアル株式会社 教育コーディネーター兼 システムエンジニア)