CakePHP1.2beta PEAR::Mail_mimeDecodeを使ったメール分解コンポーネント
2009-09-09 追記
結構、検索されてるようなので書いときます。
現在、筆者はPEAR依存が面倒くさくなったので、こちらを使ってます
QdmailReceiverとは - QdmailReceiver Multibyte mail decoder & POP Client
自重気味にα版ってことで、よろしくお願いします。
なんかヤバイとこがあれば、コメントいただけるとうれしいです。
- スパムっぽいものを検出できるようにしてみた。
- 添付ファイルにも対応(イメージファイルのみ)
- エラーメール時のアドレス取得ができるようにしてみた
使い方
Controller
<?php var $components = array('MimeDecode'); function decode() { // $msg にメールを取り込み後 $this->MimeDecode->decode($msg); $mail = $this->MimeDecode->getDecoded(); } ?>
準備
vendors フォルダにPEAR::Mail_mimeDecodeを配置
/vendors ├ pear_ini.php └ /Pear └ /Mail └ mimeDecode.php
<?php define('PEAR_PATH', dirname(__FILE__) . DS . 'Pear'); set_include_path(PEAR_PATH . PATH_SEPARATOR . get_include_path()); ?>
MimeDecodeComponent
<?php vendor('pear_ini'); vendor('Pear/Mail/mimeDecode'); class MimeDecodeComponent extends Object { /** * Mail_mimeDecode::decodeへ渡すパラメータ * * @var array */ var $params = array( 'include_bodies' => true, 'decode_bodies' => true, 'decode_headers' => true, ); /** * デフォルトのcharset * 日本語メールならISO-2022-JPかな * * @var string */ var $default_charset = 'ISO-2022-JP'; /** * charsetの順番 * * @var string */ var $charset_order = 'ISO-2022-JP, SJIS, EUC-JP, UTF-8'; /** * スパム判定メッセージ * * @var array */ var $spam = array(); /** * エラーでもどってきたであろう、メールアドレス * * @var array */ var $return_address = array(); /** * 送信者メールアドレス * * @var string */ var $from = ''; /** * subject * * @var string */ var $subject = ''; /** * headers * * @var array */ var $headers = array(); /** * 本文 * * @var array array('text'=>{text}, 'html'=>{html}) */ var $body = array('text'=>'', 'html'=>''); /** * 添付ファイル * * @var array array[] = array('mime_type'=>{mime_type}, 'filename'=>{filename}, 'binary'=>{binary}) */ var $attachments = array(); /** * 初期化 * */ function reset() { $this->spam = array(); $this->return_address = array(); $this->from = ''; $this->subject = ''; $this->headers = ''; $this->body = array('text'=>'', 'html'=>''); $this->attachments = array(); } /** * decode * * @param string $msg */ function decode($msg) { // http://www.tt.rim.or.jp/~canada/comp/cgi/tech/mailaddrmatch/ 参考にした // 2007-03-01 修正 ↓のように変更 // DoCoMo・Ezweb はメールアドレスの前後に<>がつかない // $email_regix = "/<([\S]+@[0-9a-zA-Z_\.\-]+\.[a-zA-Z]+)>/"; // 誤↑・正↓ $email_regix = "/([_\w\.\-\"]+@[_0-9a-zA-Z\.\-]+\.[a-zA-Z]+)/"; // \nをつけてるのは、そのままだとSoftbankの空メールが取得できなかったから $Decoder = new Mail_mimeDecode($msg."\n"); $obj = $Decoder->decode($this->params); // 送信者のメールアドレスをチェックします if (!empty($obj->headers['from'])) { $charset = $this->__setCharset($obj->headers['from']); $from_text = trim(mb_convert_encoding($obj->headers['from'], mb_internal_encoding(), $charset)); preg_match($email_regix, $from_text, $match); if (!empty($match[1])) { $this->from = $match[1]; } } // subject if (!empty($obj->headers['subject'])) { $charset = $this->__setCharset($obj->headers['subject']); $this->subject = trim(mb_convert_encoding($obj->headers['subject'], mb_internal_encoding(), $charset)); } // subjectのスパムチェック if (stristr($this->subject, 'spam')) { $this->spam[] = 'subject has spam !'; } // headers $this->headers = $obj->headers; // body分割 $this->__parts($obj); // エラーメール時のアドレス抽出(テキスト本文中のメールアドレスをとりあえず総ざらい) // fromにDAEMONがあるか判別いれたほうがいいかも preg_match_all($email_regix, $this->body['text'], $match); if (!empty($match[1])) { $this->return_address = array_unique($match[1]); } } /** * オブジェクトを配列にして返す * * @return array */ function getDecoded() { $a = array(); $a['spam'] = $this->spam; $a['return_address'] = $this->return_address; $a['headers'] = $this->headers; $a['from'] = $this->from; $a['subject'] = $this->subject; $a['body'] = $this->body; $a['attachments'] = $this->attachments; return $a; } /** * エンコードチェック * ヘッダでのSJIS使用は、スパムっぽいのでチェック * * @param string $string * @return string charset */ function __setCharset($string) { mb_detect_order($this->charset_order); $charset = mb_detect_encoding($string); if ($this->default_charset === $charset) { return $this->default_charset; } else { $this->spam[] = 'different charset at header! '.$charset; return 'auto'; } } /** * 分解されたbodyを再帰処理で再構築 * * @param object $obj */ function __parts($obj) { if (!empty($obj->parts)) { foreach ($obj->parts as $o) { $this->__parts($o); } } else { $charset = 'auto'; if (!empty($obj->ctype_parameters['charset'])) { $charset = strtoupper($obj->ctype_parameters['charset']); mb_detect_order($charset.', '.$this->charset_order); // 宣言されてるcharsetと実際がちがうとか、スパムっぽい if ($charset !== mb_detect_encoding($obj->body)) { $charset = 'auto'; $this->spam[] = 'different charset at body ! '.mb_detect_encoding($obj->body); } } if (!empty($obj->body)) { // text なら if ('text' === $obj->ctype_primary) { if ('plain' === $obj->ctype_secondary) { $this->body['text'] .= trim(mb_convert_encoding($obj->body, mb_internal_encoding(), $charset))."\n"; } if ('html' === $obj->ctype_secondary) { $this->body['html'] .= trim(mb_convert_encoding($obj->body, mb_internal_encoding(), $charset))."\n"; } } // image なら if ('image' === $obj->ctype_primary) { $a = array(); $a['mime_type'] = $obj->ctype_primary.'/'.$obj->ctype_secondary; $a['filename'] = $obj->ctype_parameters['name']; $a['binary'] = $obj->body; $this->attachments[] = $a; } // 他のタイプはいまのとこ無視 } } } } ?>