ようこそゲストさん

QdmailReceiver Multibyte mail decoder & POP Client

POP特有の処理(OOPタイプ)

初期設定は必ず行ってください。

OOPでいける方はそれなりにPHPに習熟した方と思いますので、例題の解説として進めます。
$receiver には、初期化されたQdmailReceiverオブジェクトが代入されているものとします。
ご自分の(安全な)受信サーバー(POPサーバー)を初期設定し以下を実行してみてください。
当然ですが、POPサーバーにメールが残っていなければなりません。テストする場合はあまりたくさんのメールが残っていると時間がかかりますので、数通にしておきましょう。
echo  "<pre>";
for($i = 1 ; $i <= $receiver->count() ; $i++){

    echo "Mail number: ".$receiver->pointer()."\r\n";
    echo htmlspecialchars(
        print_r(
           $receiver->header( array('subject','name') , 'none' )
        ,true)
    ,ENT_NOQUOTES);

    echo "\r\n";
    $receiver->next();
}

echo "</pre>";
メール件名一覧が表示されましたか?
順番に解説していきます。
$receiver->count()
で、POPサーバーに残っているメールの件数を取得します。
未読・既読の管理はPOPの機能ではできません(そもそもPOPプロトコルは未読・既読を行いません)
$receiver->pointer()
現在、どの順番のメールを読んでいるかを取得します。
この順番(pointer)はセッション毎(プロセス毎)に変わります。これをプロセスを超えて共有して使用すると意図しない結果となる場合がありますので、注意してください。
プロセスを超えて、メールを特定したい場合は、POP-UID機能を使ってください。
$receiver->header( array('subject','name') , 'none' )
いよいよ本題です。これで、件名を取得しています。
一般的には
$receiver->header( 取得するヘッダー名 , 存在しない場合の返り値(省略可) )
と書きます。
どのような配列で内部格納されているか知りたい方は、上記のコードを以下に修正して、表示させてみてください。
     $receiver->header( 'subject' , 'none' )
これで、subjectに関するデコードデータを配列の形で入手することができます。

同様にして、
     $receiver->header( 'from' , 'none' )
     $receiver->header( array('from',0,'name') , 'none' )
なども試してみましょう。
注意!
ヘッダー名は、すべて小文字で指定してください。
ループの最後はカレントポインタの移動です。
 $receiver->next();
これで、次のメールへと処理をうつします。現在のメール情報は消去されます。(QdmaiReceiverの内部的に消去されるだけであって、POPサーバーからは削除されません)

# [質問]non-objectエラー 2011年01月31日(月) 午後3時19分

基本的な質問で失礼します。
上記参考にPHPスクリプト作ってみましたが、以下エラーとなってしまいます。
Fatal error: Call to a member function count() on a non-object

QdmailReceiver単体以外のライブラリ等が必要なのでしょうか?
PHPソースは以下の通りです。
----------------------
<?php

require_once('qdmail_receiver.php');
$popserver = array(
‘protocol’=>’pop3′,
‘host’=>’pop.mail.yahoo.co.jp’,
‘user’=>’xxxxxxx’,
‘pass’=>’xxxxxxx’,
);

$receiver = QdmailReceiver::start(‘pop’, $popserver,'UTF-8');

echo "<pre>";
for($i = 1 ; $i <= $receiver->count() ; $i++){

echo "Mail number: ".$receiver->pointer()."\r\n";
echo htmlspecialchars(
print_r(
$receiver->header( array('subject','name') , 'none' )
,true)
,ENT_NOQUOTES);

echo "\r\n";
$receiver->next();
}

echo "</pre>";
?>


#  非公開コメント   
  • TB-URL  http://hal456.net/qdmail_rec/027/tb/

POP特有の処理(一般関数タイプ)

POP初期化の処理を前提とします。初期化(サーバー情報の設定)を行わないと、POPでメールを受信サーバーから取得することはできません。
今後、受信サーバーのことをPOPサーバーと記載します(この場合は同じものを示しています)。

メール件数の取得

echo qd_receive_mail( 'count' );
これで、POPサーバーに溜まっているメールの件数を取得します。

次のメールを処理する

何も指定しなければ、受信サーバーに溜まった最初のメールだけを処理します。
それ以降のメールの処理をするには、次のようにします。
qd_receive_mail( 'next' );

#  非公開コメント   
  • TB-URL  http://hal456.net/qdmail_rec/026/tb/

Directモード

Directモードは、引数にメール全文を与えることで、そのメールを解析するモードです。
一番ベーシックな働きをします。
POPモードやSTDINモードも、このDirectモードを呼び出して使っています。

使い方(一般関数タイプ)

qd_receive_mail( 'direct' , 'メールデータ全文' );
メールデータ全文は、本文だけなく、ヘッダーも含めた全文である必要があります。
SMTPに準拠した形式を受け付けます。

使い方(OOPタイプ)

QdmailReceiver::start( 'direct' , 'メールデータ全文' );
あとは共通の使い方と同じです。

#  非公開コメント   
  • TB-URL  http://hal456.net/qdmail_rec/025/tb/

モード共通の使用方法

QdmailReceiverは、2通りの使用方法があります。

一般関数タイプの初期設定

qd_receive_mail( モード , パラメータ , 文字コード );
stdinモードの例
qd_receive_mail( 'stdin' );

OOPタイプの初期設定

$receiver = QdmailReceiver::start( モード , パラメータ , 文字コード );
stdinモードの例
$receiver = QdmailReceiver::start( 'stdin' );

記事リスト


#  非公開コメント   
  • TB-URL  http://hal456.net/qdmail_rec/024/tb/

STDINモード

STDINモードとは

メールの受信サーバーから、パイプライン処理等で、PHPプログラムを直接起動し、標準入力(STDIN)経由でメールデータを受け渡しするモードです。
受信サーバーの種類によって設定が異なります。

使い方(一般関数タイプ)

標準入力から、メールの全文を入手するタイプですので、他のプログラムの標準出力をパイプにて受け渡しする必要があります。
また、受け取った側のPHPプログラムは、多くの場合、1行目に、動作元となるPHP本体プログラムを指定する必要があるでしょう。
指定は、ファイルの最初に #! と綴り、そのすぐ後からPHP本体へのパスを記述します。
下記の例は、さくらインターネットの場合です。-qは、quietモードであり、httpヘッダーを出力しない設定です。
#!/usr/local/php-5.2.6/bin/php-cgi -q
<?php

include_once('qdmail_receiver.php');

qd_receive_mail( 'stdin' );

?>
サーバーによって
#!/usr/local/php-5.2.6/bin/php-cgi -q
の部分は変更する必要があるでしょう。

よくわからない場合は、
#!/usr/bin/php
で、うまくいくことが多いとは思います*1
qd_receive_mail( 'stdin' );
と宣言すれば、後は他のモードの使い方と同じです。

使い方(OOPタイプ)

qd_receive_mail( 'stdin' );
の代わりに
QdmailReceiver::start( 'stdin' );
として下さい。

文字コードについて

QdmailReceiverは、すべての出力を、内部エンコーディングに変換して返します。
特定の文字コードにしたい場合は、以下のようにしてください。
utf-8で出力する場合の例)
qd_receive_mail( 'stdin' , 'utf-8' );
または
QdmailReceiver::start( 'stdin' , 'utf-8' );

*1 : 保証の限りでない

応用

Qdmailはメール受信サーバーを操作することができますが、次のようなことはどうでしょうか。
メールが到着したら、リアルタイムで処理する
メール到着をきっかけに、何らかのプログラムを走らせるわけですね。
これはPOPサーバーに定期的にアクセスするプログラムでは実現できません。
せいぜい、cron*2で、数分おきに実行させるのがやっとでしょう。

しかし、sendmail,qmail,PostFixなどのメールサーバーとQdmailReceiverを組み合わせることで実現することができます。
これが実現できれば、以下のようなサービスが簡単に提供できるでしょう。
  • 空メールによる簡単会員登録
  • ブログへのメール投稿
  • メール転送プログラム
  • 簡易メーリングリスト
  • リアルタイムSpamフィルター
  • WEBメール

*2 : unix系の定期実行デーモン

# SAMURAI 2008年11月08日(土) 午前6時01分

この度はお世話になります。
STDINモードで
メールの本文がとりだせないのですが・・
お助け頂けますと幸いです。

#!/usr/bin/php -q

<?php

require_once('qdmail_receiver.php');
$receiver = QdmailReceiver::start( 'stdin' );

$text = $receiver->bodyAutoSelect();
?>
↑上記ではかならず エラーになるようです。



$text = $receiver->bodyAutoSelect();

# spok 2008年11月09日(日) 午後8時03分


どんなエラーですか?

# SAM 2008年11月09日(日) 午後8時35分


お世話になります。
以下のようなかたちで、sendmailのaliasとして仕込んでいます。
(※function_sendMail.phpはメール送信部の自作関数です。)

$body = $receiver->bodyAutoSelect();
でメール本文を取り出そうとすると、

"554 5.3.0 unknown mailer error 255"

が戻って来ます。
そこで
$body = $receiver->bodyAutoSelect();
を注釈にしたり、
$body = "テスト用ダミーの本文です";
を活かすと問題なく返信が返って来ました。

すみませんがご教授頂けますと幸いです。

======================================
#!/usr/bin/php -q
<?php
//標準入力から読み込み
require_once('qdmail_receiver.php');
$receiver = QdmailReceiver::start( 'stdin' );

$from = $receiver->header( array('from','mail') , 'none' );
$subject = $receiver->header( array('subject','name') , "無題" );
$header = serialize ( $receiver->header( 'ALL' ) );
$now = strtotime( $receiver->header( 'date') );
$body = $receiver->bodyAutoSelect();

//$body = "テスト用ダミーの本文です";

$subject = "TEST";
$to = $from ;

//メール返信
require_once("function_sendMail.php");
sendAttachMail( "support@test.com", $to , $subject, $body , $img_files );
?>
======================================

# spok 2008年11月09日(日) 午後10時21分


stdin関係のスクリプトは原因が特定しにくいのですが、まずは以下のサイトを参考に、パーミッションを再確認してみてください。それでもダメなら再度ご連絡下さい。

http://q.hatena.ne.jp/1167279986
http://d.hatena.ne.jp/makotoworld/mobile?date=20071105
http://www.cpa-lab.com/tech/0143

# SAMURAI 2008年11月09日(日) 深夜0時59分

ご返信ありがとうございました。

パーミッションを確認しましたが、問題なさそうでした。
再度

//$body = $receiver->bodyAutoSelect();
$body = "テスト用ダミーの本文です";

でテストを行ったところ、やはり正しく返信がきました。
そこで両方の注釈をとり、
$body = $receiver->bodyAutoSelect();
$body = "テスト用ダミーの本文です";

で行いますと、これは
"554 5.3.0 unknown mailer error 255"

が戻って来ます。

$body = $receiver->text();
も試してみてはいたのですが、、、これも同様でした。

そこで

$body = $receiver->header( array('subject','name') , "none" );

で、件名を返信の本文として返させたところ、こちらは無事かえってきましたので、
本文の取り出しのみがうまくいっていないようでした。
すみませんがよろしくお願い致します。

# spok 2008年11月10日(月) 午前6時25分

>"554 5.3.0 unknown mailer error 255"
>が戻って来ます。

というのは、どう「戻って」来ますでしょうか

$bodyの中にそのメッセージが入っていますか?

# spok 2008年11月10日(月) 午前7時50分

なぜこれをお伺いするかというと、本来
>"554 5.3.0 unknown mailer error 255"
はSMTPサーバーが出すエラーであり、QdmailReceiverは直接関係のないエラーメッセージだからです。

バウンスのエラーメッセージを解析している可能性はないですか?

また、$body本文にそのエラーメッセージが入っているとして、他のメッセージは一切入っていないですか?

# spok 2008年11月10日(月) 午前8時00分

$all = $receiver->all();

とすると、$allには、ヘッダーも含めたメール全文が代入されます。
この$allのtext(orHTML)部分と$bodyが異なっていれば、QdmailRecieverに原因があり、そうでなければ、それ以前の標準入出力部分で何らかのトラブルがあることが考えられます。

# SAMU 2008年11月10日(月) 午後1時46分


お世話になります。
すみません、何度もお手数をおかけ致します。

$body = $receiver->all();

とし、メール”本文にこれはメール本文です(を5つ)”をいう内容
のメールをautoreplay@test.com に送信しました。

autoreplay@test.comにはメールのaliasで
①の”/var/www/html/autoreplay.php”が受信するように仕掛けておりまして、
②の内容の本文のメールが無事送信元に届きました。
これを拝見すると、$receiver->all()では送信した本文の内容、
(これはメール本文です が5つ)

こちらから何かヒントになる箇所はございますでしょうか?
念のため、下3行の
//メール返信
require_once("function_sendMail.php");
sendAttachMail( "support@test.com", $to , $subject, $body , $img_files );

を削除しても③のエラー返信がありますので、こちらの関数は直接関係は無いものと思われました。
お手数ですがよろしくお願い致します。



①”/var/www/html/autoreplay.php”(↓OKのパターン)
======================================
#!/usr/bin/php -q
<?php
//標準入力から読み込み
require_once('qdmail_receiver.php');
$receiver = QdmailReceiver::start( 'stdin' );

$from = $receiver->header( array('from','mail') , 'none' );
$body = $receiver->all();
//$body = $receiver->bodyAutoSelect();


$subject = "自動返信";
$to = $from ;

//メール返信
require_once("function_sendMail.php");
sendAttachMail( "support@test.com", $to , $subject, $body , $img_files );
?>
======================================

② ①で返信されたメール本文
===================================================================
From testuser@test.com Mon Nov 10 13:21:07 2008
Received: from TESTPC (xxx.ne.jp [xxx.xxx.xxx.xxx])
by www.test.com (8.13.1/8.13.1) with ESMTP id mAA4L7Oq006763
for <autoreplay@test.com>; Mon, 10 Nov 2008 13:21:07 +0900
From: "TEST USER" <testuser@test.com>
To: <autoreplay@test.com>
Subject: This is TEST mail !!!
Date: Mon, 10 Nov 2008 13:19:06 +0900
Message-ID: <003f01c942eb$7889bbd0$0301a8c0@TESTPC0>
MIME-Version: 1.0
Content-Type: text/plain;
charset="iso-2022-jp"
Content-Transfer-Encoding: 7bit
X-Mailer: Microsoft Office Outlook 11
X-MIMEOLE: Produced By Microsoft MimeOLE V6.00.2900.3350
Thread-Index: AclCg1MX1suE9IjiQRmtloTrbNPK8g==



これはメール本文です
これはメール本文です
これはメール本文です
これはメール本文です
これはメール本文です
===================================================================


③ ①で$body = $receiver->bodyAutoSelect();
を活かした際に送信元に返信されるエラーメール
======================================
The original message was received at Sun, 9 Nov 2008 20:25:56 +0900 from [xxx.xxx.xxx.xxx]

----- The following addresses had permanent fatal errors ----- "|/usr/bin/php -q /var/www/html/autoreplay.php"
(reason: 255)
(expanded from: <autoreplay@test.com>)

----- Transcript of session follows -----

554 5.3.0 unknown mailer error 255
======================================

# spok 2008年11月10日(月) 深夜0時13分


ようやくなさっていることがわかりました。

おそらく、本文解析の部分でPHPがエラーを出しているため、このバウンスメールが返ってきているのでしょう。

お使いのサーバーに合わせて

http://www.cpa-lab.com/tech/0143#k143p5

を参考にしていただき、デバッグしてみていただけると
PHPが出しているエラーメッセージがわかるかと思います。

お手数ですが、当該エラーメッセージをお知らせ下さい。

# SAMU 2008年11月10日(月) 深夜2時49分


お世話になります、
何度もすみませんでした。

頂きましたサイトを参考に cat でエラーを
はき出させたところ、下記がでまして、原因はmb_check_encoding()が使えないことだと判りました・・・
そこで、PHPのバージョンを調べたところ4.3.9 でした。
mb_check_encoding()は4.4.3からということで・・・大変お手数おかけしました。
今は②のように回避し、文字化け対策は別途メール送信の関数の方で、
$body = mb_convert_encoding($body, "ISO-2022-JP" , "sjis");
などとしています。

今の環境のPHPは5へUP予定なので、その後は問題もないと思います。
ご対応頂きまして大変ありがとうございました。




①エラー
=====================================
PHP Fatal error: Call to undefined function: mb_check_encoding() in /var/www/html/qdmail_receiver.php on line 605
Fatal error: Call to undefined function: mb_check_encoding() in <b>/var/www/html/qdmail_receiver.php
on line 605
=====================================


②qdmail_receiver.php 605行目あたりと、640行目付近
=====================================

if (function_exists('mb_check_encoding')) {
if( mb_check_encoding( $body , $charset ) ){
$body = mb_convert_encoding( $body , $this->target_charset , $charset );
}
}

if (function_exists('mb_check_encoding')) {
if( mb_check_encoding( $_filename , $org_charset ) ){
$_filename = mb_convert_encoding(
$_filename ,
$this->target_charset,
$org_charset
);
}
}

# spok 2008年11月11日(火) 午前7時54分

ご連絡ありがとうございました。
今後の更新時には、このエラー回避を組み込みたいと思います。

# SAMU 2008年11月11日(火) 午後2時07分

こちらこそありがとうございました。
またVerUPの際には差し替えて活用させて頂きたいとおもいますので、
どうぞよろしくお願い致します。

# SAMU 2009年02月06日(金) 深夜1時20分

前回はお世話になりました。SAMUと申します。

QdmailReceiverを利用させて頂き、gmailからのメールの処理をPHPで行おうとしたとろ、なぜか添付ファイルがうまく処理されないようでした・・

そこで、
$attach_files = $receiver->attach();
echo "--->attach_num:".count($attach)."\n";

などで添付ファイル数を表示させますと、”0”が表示され、
どうも添付ファイル自体を認識していないようでした。

もし、これを回避するためのアイディアやお気づきの点がありましたら教えて頂けますと幸いです

どうぞよろしくお願い致します。

# SAMU 2009年02月08日(日) 午後8時58分

お世話になります。自己レスです。

※前回の確認用スクリプトは一部記述が間違っておりました。
(正)
$attach_files = $receiver->attach();
echo "--->attach_num:".count($attach_files);


なぜ$attach_files のカウントが添付ファイル付きでも”0”になるのか?をちょっと調べておりましたが、原因はGmailのboundary= が、
ダブルクォーテーションで囲まれておらず、
Content-Type: multipart/mixed; boundary=00c09f90f7297b2103046242157f

になっているためと考えられました。
そこで、

qdmail_receiver.phpの461行目あたりを

preg_match( '/boundary\s*=\s*"*([^"]+)"*/is' , $this->header['content-type'] , $matches );

と変更し、ダブルクォーテーションで囲まれていない場合も認識するように改変を加えたところ無事にGmailからの添付ファイルも処理できました。

Gmailの仕様がなぜダブルクォーテーション無しなのかと、
RFC的(?)にはどうなのか?
って言う点がまだちょっと調べ切れてはおりませんが、これはまたちょっとおいおい調査してみます。



# Vert 2009年03月27日(金) 午後1時28分

こう書きましたが、本文はうまく取得できるのですが、タイトル部分がうまく取得できません。環境はcakePHP1.2、consoleから起動しております。

$DECODE = QdmailReceiver::start('stdin', 'utf-8' );
$from = $DECODE -> header(array('from','mail'));
$data['Diary']['title']=$DECODE->header(array('subject','name'));
$message=$DECODE->bodyAutoSelect();

# wataru 2010年12月15日(水) 午後5時43分

STDINモードで、添付画像が抽出できないものがあったので・・・
バージョンは、0.1.4aです

GMailの場合、boundaryが「"」で囲まれていなくて、preg_matchでマッチしていませんでした。

正規表現を
「'/boundary\s*=\s*"([^"]+)"/is'」→「'/boundary\s*=\s*"?([^"]+)"?/is'」
と変更してうまく取得できました。

一応報告まで・・・

ところで、アップデートされないんですかねぇ・・・

# wataru 2010年12月15日(水) 午後5時45分

あっ、既に報告がありました<(_ _)>

# mikan 2012年03月23日(金) 午前8時35分

wataruさん助かりました。
Gmailの本文も取得が出来なかったのですが、
上記対応で取得出来るようになりました。ありがとうございます。


#  非公開コメント   
  • TB-URL  http://hal456.net/qdmail_rec/023/tb/