メール受信からのシェル機能実行
空メール送信での会員登録や、メールに添付されたファイルをサーバに自動でアップロードなど、メール受信からのスクリプト実行というのは様々なシーンで用途があると思います。
今回はそれをCakePHP 1.2系のシェル機能を使って実現してみました。(1.2.0.6311-betaで確認)
PEARを使用できるようにセットアップ
まずapp/vendorsの下にPEARフォルダを作り、PEARのコアクラス(PEAR.php)とMail_Mimeパッケージのクラスを置く。
次に、以下のソースをapp/vendors/pear_ini.phpとして保存。
<?php define('PEAR_PATH', dirname(__FILE__) . DS . 'PEAR'); set_include_path(PEAR_PATH . PATH_SEPARATOR . get_include_path()); ?>
セットアップ後の配置は上のスクリーンショットの様になっていればOKです。
シェル機能の実装
Mail_mimeDecodeを使ったスクリプトは、2008-01-23に掲載されているスクリプトをベースにさせて頂きました。
以下のソースをapp/vendors/shells/receiver.phpとして保存。
<?php vendor('pear_ini'); require_once('Mail' . DS . 'mimeDecode.php'); class ReceiverShell extends Shell { function receiveMail() { $mail = ""; $image_filename = ""; $from = array(); $headers = array(); // 標準入力から受け取ったメールを取得 $stdin=$this->Dispatch->stdin; while( !feof($stdin) ){ $mail .= fgets($stdin,4096); } // デコード方法の指定 $params['include_bodies'] = true; $params['decode_bodies'] = true; $params['decode_headers'] = true; // デコード処理 $decoder = new Mail_mimeDecode($mail); $decoded = $decoder->decode($params); // 添付ファイルの有無で処理を分岐 if ( !empty($decoded->parts) ) { // bodyを取得 $body = $decoded->parts[0]->body; // 添付ファイルの数だけループ for($idx = 1; $idx < count($decoded->parts); $idx++) { $file = $decoded->parts[$idx]->body; $filetype = $decoded->parts[$idx]->ctype_secondary; /********************************************************** * ファイルタイプのチェックしたり、指定したディレクトリに * * 添付ファイルを保存したりの処理をここでする * **********************************************************/ } } else { // 添付ファイルが無い場合はbodyを取得するだけ $body = $decoded->body; } // ヘッダ部分を取得して漢字コードを変換する $headers = $decoded->headers; $from_text = mb_convert_encoding($headers['from'], mb_internal_encoding(),'ISO-2022-JP'); ereg("[0-9a-zA-Z_\.\-]+@[0-9a-zA-Z_\.\-]+",$from_text,$from); $from = $from[0]; $subject = mb_convert_encoding($headers['subject'], mb_internal_encoding(),'ISO-2022-JP'); $body = mb_convert_encoding($body, mb_internal_encoding(),'ISO-2022-JP'); /********************************************************** * DBに格納したり、メールを送り返したりの処理をここでする * **********************************************************/ } } ?>
シェルスクリプトファイル、.qmailファイルの作成
上記で実装したCakePHPのメール受信スクリプトを呼ぶシェルスクリプト(receiver.sh)は以下のように書きました。
#!/bin/sh cd /appフォルダまでのパス/app ../cake/console/cake receiver receiveMail
そして、メール受信時にそのシェルスクリプトを呼ぶように.qmailファイルは
|/シェルスクリプトまでのパス/receiver.sh
として、しかるべき場所に配置します。
ハマッた点
標準入力のオープン・クローズを以下のように自前でやると、標準入力からメールがうまく読み込めてなくてしばらくハマってしまいました。
$stdin=fopen("php://stdin",'r'); while( !feof($stdin) ){ $mailtext .= fgets($stdin,4096); } fclose($stdin);
ソースを追っていたら、ShellDispatcherクラスの__initEnvironmentメソッドで標準入力をオープンして$stdinにセット⇒ShellDispatcherのインスタンスがShellクラスの$Dispatchにセットされている、という事が分かったので標準入力は自前で開かずに$this->Dispatch->stdinを使ってみると、うまく読み込むことが出来ました。