PHPでメール受信してヘッダー情報をゴリゴリ成型したい
上の続編
メールの生データを扱うことにした
mimeDecode.phpとかphp-mime-mail-parserとかMailparseとかQdmailとかPHPで扱えるメールデータ解析用のライブラリは沢山あるけど正直、使い方が解らんです。
プログラミングに精通した人なら使い方とか解るのでしょうけど、初学者にとってはライブラリの利用はハードルが高いです。感覚的にですが、オブジェクト指向が理解出来ていないとライブラリの使い方の理解が出来ないんじゃないかと。ある程度、プログラミングが出来る人向けなのかなぁってのが正直な感想です。Qdmailは【「初心者には簡単に、上級者には詳細に」のポリシー】を掲げていますが初心者である私には解りません。
PHPのバージョンが違う、環境が追い付いてない
まず自分の開発環境や実装するサーバの環境が追い付いてないのが理解できない、使い方が解らないの原因の1つだと思われます。実装するサーバがマネージドサーバなのでroot権限がないのです。その為、Composerが使えないとかyumコマンドが使えないとかNode.jsが使えないとかFFmpegが使えないだとか物凄い制約がある。正直、環境変えたい、引っ越ししたい。でも会社のサーバなのでそう簡単に移設出来ない。歯痒い感じではある。
メールデータってテキストデータでしょ?
ならば、自分で成型してしまえばいいじゃないか。ということで行き着いたのがライブラリやモジュールに頼らず、自分で取りたいデータを生データから抽出すりゃええやないかと思ったわけです。MIMEデータとにらめっこすることにしたのです。お陰でメールの内容については粗方理解できました。とりあえず、自分が欲しい情報がどういった格納のされ方をしているのか、メールってこんな風にデータを処理してるのねってことが解りました。MIMEヘッダ情報に格納されているデータについてはほぼ一意なデータなので簡単に抜き出すことができました。以下にコードと説明。
参考サイト:文字列内から指定の開始文字から終了文字までの間の文字を取得するPHP
上記参考サイトの関数を使用しています。
<?php
// 参考サイトの文字列取得関数
function html_cut_syutoku($html_buf, $start_buf, $end_buf, $int_positon_cnt){
if(strstr($html_buf, $start_buf)){
$srt_position = strpos($html_buf, $start_buf, $int_positon_cnt);
$srt_position = $srt_position + strlen($start_buf);
$end_position = strpos($html_buf, $end_buf, $srt_position);
$result_buf = substr($html_buf, $srt_position, $end_position-$srt_position);
}else{
$result_buf = "";
}
return $result_buf;
}
/* メールの生データ(iPhone標準メーラーからWEB Gmail宛へ送った実際のデータ1行目と2行目)
* Delivered-To: hogehoge@gmail.com
* Received: by 10.200.34.219 with SMTP id g27csp(*******)qta;
* (*******)は 6桁~7桁 の数字
* "php://stdin"で受信したメールのデータも取得可能
* .forwardや.procmailrcの設定は割愛します
*/
$html_buf= file_get_contents("original_msg.txt");
// 終端文字列 CRLFの改行文字
$end_buf="\r\n";
// 開始文字列(取得したいフィールドの名称なので可変 "From: " とか)
$start_buf="Delivered-To: ";
// 開始文字列から終端文字列までの間の文字列を取得
$toAdd = html_cut_syutoku($html_buf,$start_buf,$end_buf,0);
echo '$toAdd:';var_dump($toAdd);
$toAdd:(string) hogehoge@gmail.jp
要するに**$start_bufと$end_buf**の中身を変えてやれば文字列の中の一意な文字列から次の改行(CRLF)までの間の文字列を取得できるわけです。この関数を使えばMIMEヘッダにある情報は簡単に取得出来ます。
件名(Subjectフィールド)
日本語がエンコードされているSubjectでも簡単に取得出来ます。尚且つ、デコードしてくれる関数もあるのでメールデータの件名の取得なんて数行で済みます。
MIMEヘッダについては下記サイトが初学者でも解りやすいです。
参考サイト:MIME(Multipurpose Internet Mail Extensions)~後編
/* 実際のメールデータに記述してある件名の行
* Subject: =?iso-2022-jp?B?5pel5pys6Kqe44K/44Kk44OI44Or?=
* や
* Subject: =?utf-8?B?5pel5pys6Kqe44K/44Kk44OI44Or?=
*/
$html_buf= file_get_contents("original_msg.txt");
$start_buf="Subject: ";
$end_buf="\r\n";
$subject = html_cut_syutoku($html_buf,$start_buf,$end_buf,0);
echo '$subject(デコード前):';var_dump($subject);
$subject = mb_decode_mimeheader($subject);
echo '$subject(デコード後):';var_dump($subject);
$subject(デコード前):(string) =?utf-8?B?5pel5pys6Kqe44K/44Kk44OI44Or?=
$subject(デコード後):(string) 日本語タイトル
件名(Subjectフィールド)にあるデータを抜き出してmb_decode_mimeheader($str)するだけでデコードして日本語に直してくれます。
送信者(Fromフィールド)に日本語の名前が書いてあった場合でも同様にすれば送信者が設定している日本語名が取得出来ますし、送信者アドレスはほぼ <> で囲まれていますので取得も簡単です。メーラーによって違いがあるのでマイナーなメーラーを対象にする場合は自力で調べて下さい。
日付(Dateフィールド)
日付の取得も簡単です。
/* 実際のメールデータ
* Date: Tue, 24 Oct 2017 21:00:48 +0900
*/
$html_buf= file_get_contents("original_msg.txt");
$start_buf="Date: ";
$end_buf="\r\n";
$Date = html_cut_syutoku($html_buf,$start_buf,$end_buf,0);
echo '$Date(成型前):';var_dump($Date);
$Date = mb_strcut($Date, 0, 25); // Tue, 24 Oct 2017 21:00:48 までを指定
echo '$Date(成型後):'.date("Y-m-d H:i:s",strtotime($Date));
$Date(成型前):(string) Tue, 24 Oct 2017 21:00:48 +0900
$Date(成型後):2017-10-24 21:00:48
mb_strcut(対象文字列(str), 開始位置(int), 取得文字の長さ(int))で欲しい部分の日付データを切り出します。strtotimeでタイムスタンプに変換してやった日付データをdateでフォーマットを指定してやります。
MIMEヘッダに関してはこんなやり方で色々取得出来るみたいです。
最後に
ただ、脆弱性があるかもしれません。MIMEヘッダを偽装することも可能だと思います。そのへんは考慮してないというか、思い付かない初学者の力業ですので、ご指摘頂ければ幸いです。なのでこのやり方を推奨しているわけではないことをご理解下さい。ライブラリやモジュールが扱える方であればそちらを優先した方がいいと思います。
あとはシングルパート、マルチパートの処理、添付ファイルやインライン画像のデータ取得及びデコード、ファイルの保存等が出来ればやりたいことにグッと近付くのですがそれはまた今度のお話。
自分の記事リンク:PHPでメール受信してヘッダー情報をゴリゴリ成型したい