携帯サイトで画像投稿に ランダム文字列@domain.com みたいなアドレスをユーザーに発行して、そこに添付メールを送信してもらい、PHPで受け取ってパースしたかった。
PHPとsendmailで任意のメールアドレスに来たメールを処理する方法
こちらのブログで紹介されている通りなんだけど、/etc/mail/sendmail.cfを修正してもエラーになる。
表記が難解過ぎて自分で調べる気にもならない・・・。
ふとコメントを見ると「virtusertable」で設定するのが今風らしい。 で調べて半日以上ウダウダ格闘した結果。
/etc/aliases に追記
hoge: "| cake shell_name function -app /cake_path/app" |
hoge に実体(linuxユーザー)はない。
php, cake, cake.phpはsendmailが呼べるように/etc/smrsh/にシンボリックリンクを貼っておく。
$ln -s /usr/bin/php php $ln -s /cake_path/cake/console/cake cake $ln -s /cake_path/cake/console/cake.php cake.php |
ハッシュリスト再構築
newaliases |
/etc/mail/virtusertable
@mail.hoge.com hoge |
mail.hoge.comに来たメールは全てaliasesのhogeに投げるという設定。
本当は @hoge.com に全て飛ばしたかったのだが、ユーザーの有無に関わらず全て転送されてしまうため、サブドメインを用意した。
他に方法はあるんだろうか。
再構築。
/usr/sbin/makemap hash /etc/mail/virtusertable.db > /etc/mail/virtusertable |
そんでもって更にサブドメインにしてしまったのでlocal-host-namesを修正。
/etc/mail/local-host-names
mail.hoge.com |
たぶんsendmailの再起動、stop → startが必要。
ここからやっと本題?のCakePHPでの処理。
メールのパースにはPEARのMail_mimeDecodeを利用する。
/cake_path/vendors/pear_init.php
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . dirname(__FILE__)); |
PEAR環境は整っているものとする。
Mail_Mime
/cake_path/vendors/Mail/mime.php
Mail_mimeDecode
/cake_path/vendors/Mail/mimeDecode.php
Mail_mimePart
/cake_path/vendors/Mail/mimePart.php
をダウンロード、設置。
/cake_path/app/vendors/shells/mailer.php とか
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | App::import('Vendor', array('pear_init')); App::import('Vendor', 'Mail_mimeDecode', array('file' => 'Mail/mimeDecode.php')); App::import('Component', 'Email'); class MailerShell extends Shell { function startup(){ $this->Controller =& new Controller(); $this->Email =& new EmailComponent(null); $this->Email->initialize($this->controller); } function receive() { // 標準出力から読み取り $stdin = $this->Dispatch->stdin; while(!feof($stdin)) { $email .= fgets($stdin, 4096); } $decoder = new Mail_mimeDecode($email); $params['include_bodies'] = true; //ボディを解析する $params['decode_bodies'] = true; //ボディをコード変換する $params['decode_headers'] = true; //ヘッダをコード変換する $structure = $decoder->decode($params); // たとえばtoをパース preg_match("/([a-zA-Z0-9"._-]+@[a-zA-Z0-9._-]+.+[a-zA-Z0-9]+)/" , $structure->headers['to'] , $mailto); // 以後適当な処理 // たとえば宛先不明なら $this->_user_unknown($mailto[0], $email); } function _user_unknown($to, $body) { $this->Email->smtpOptions = array( 'port'=>'25', 'timeout'=>'30', 'host' => 'localhost', ); $this->Email->delivery = 'smtp'; $this->Email->from = "Mail Delivery Subsystem <mailer-daemon@mail.hoge.com>"; $this->Email->to = "<".$to.">"; $this->Email->subject = "Delivery Status Notification (Failure)"; $this->Email->sendAs = "text"; $this->Email->template = "annon_user"; $this->Email->send($body); } |
toからDBに紐づいたユーザーを判別して云々とか。
設定したドメイン宛のメールが全てここに来ることになるので、User unknownとかも自分で送信しないといけないっぽい。
後は$structureに全部デコード状態で入っているので$this->log()とかして中身を見れば後はわかるかと。
文字列はエンコードタイプを見て、必要に応じて変換(mb_convert_encoding)する必要あり。
添付のバイナリデータはそのままファイルに出力すればOKだった。