| Tripletail documentation | Contained in the Tripletail distribution. |
Tripletail::Sendmail::MailQueue - 独自のメールキューを使用するメール送信
my $smail = $TL->newSendmail('SendMailQueue');
$smail->send(...);
$smail->send(...);
$smail->send(...);
$smail->process;
送信要求されたメールを、 TripletaiL のメールキューに保存する.
キュー内に保存されたメールは、"process" 呼び出し時に一括して配送される。
何もしない。
Tripletail::Sendmail::Smtp 参照。配信時のタイムアウト時間を設定する。
$smail->process
実際の配信処理を行う。一般にこのメソッドは処理に時間が掛かる為、 CGIのプロセスから直接呼ぶべきではない。
queuedir = /home/www/mqueue/
メールキューディレクトリ。
erroraddr = null@example.org%localhost
配信エラー時にエラーメールを送るなら、その送信先を指定。
null@example.org%localhost のように使用する smtp サーバーを指定する。
% 以降は省略可能で、省略された場合は localhost となる。
errorlog = 1
配信エラー時にエラーログを記録するかどうか。 0の場合、保存しない。 1の場合、保存する。
メールキューのディレクトリには、queue,incoming,outgoing の3つのディレクトリが 作成済みで、同一のパーティションになければならない。
メールは、1行目にエンベロープFrom、2行目以降にエンベロープToが1行1アドレスで
あり、空行を挟み、その後に本文データが続く。改行コードは \r\n とする。
新規にキューにメールを入れる場合は、incoming ディレクトリ内に作成してから queue へ rename(2) する。ファイル名は、時刻、プロセスID等を使い、 ユニークになるようなものとする。
メールを処理するときは,outgoing に rename してから処理を行い、終わったら
rm する。rename(2) する際、ファイル名の末尾に「.」とプロセスIDを記述する。
メールを定期的に調査し、設定されたMTAへSMTPで送信を行う。failure の場合は 指定アドレスにメールを送るか、ログに書き込む(設定で変更可能)。deferral の場合は queue ディレクトリに rename(2) で戻す。末尾の .$pid は削除する。
プロセスが存在しないのに、outgoing にファイルがある場合は、deferral として queue ディレクトリにrename(2) で戻す。末尾の .$pid は削除する。
プロセスが存在しないのにincomingにファイルがある場合は、それを削除する。
Copyright 2006 YMIRLINK Inc.
This framework is free software; you can redistribute it and/or modify it under the same terms as Perl itself
このフレームワークはフリーソフトウェアです。あなたは Perl と同じライセンスの 元で再配布及び変更を行うことが出来ます。
Address bug reports and comments to: tl@tripletail.jp
HP : http://tripletail.jp/
| Tripletail documentation | Contained in the Tripletail distribution. |
# ----------------------------------------------------------------------------- # Tripletail::Sendmail::MailQueue - ç¬èªã®ã¡ã¼ã«ãã¥ã¼ã使ç¨ããã¡ã¼ã«éä¿¡ # ----------------------------------------------------------------------------- package Tripletail::Sendmail::MailQueue; use strict; use warnings; use Tripletail; require Tripletail::Sendmail; require Tripletail::Sendmail::Smtp; our @ISA = qw(Tripletail::Sendmail); our $QUEUE_ID_COUNT = 0; 1; sub _new { my $class = shift; my $group = shift; my $this = bless {} => $class; local($_); if (my $queuedir = $TL->INI->get($group => 'queuedir')) { $queuedir =~ s!/+$!!; # æ«å°¾ã® / ãæ¶ã $this->{queuedir} = $queuedir; } else { die __PACKAGE__."#new: queuedir is not defined for the INI group [$group]. (queuedirãæå®ããã¦ãã¾ãã)\n"; } $this->{group} = $group; $this->{smtp} = Tripletail::Sendmail::Smtp->_new($group); $this->{erroraddr} = $TL->INI->get($group => 'erroraddr'); $this->{errorlog} = $TL->INI->get($group => 'errorlog'); $this->{host} = $TL->INI->get($group => 'host'); if(defined($_ = $TL->INI->get($group => 'timeout'))) { $this->{smtp}->setTimeout($_); } $this; } sub setTimeout { my $this = shift; $this->{smtp}->setTimeout(@_); } sub send { my $this = shift; my $data = $this->_getoptSend(@_); my $fname = sprintf 'TL-%d-%d-%d', time, $$, $QUEUE_ID_COUNT++; my $infile = "$this->{queuedir}/incoming/$fname"; my $queuefile = "$this->{queuedir}/queue/$fname"; open my $fh, '>', $infile or die __PACKAGE__."#send: failed to write file [$infile] (ãã¡ã¤ã«ã«æ¸ãè¾¼ãã¾ãã)\n"; print $fh "$data->{from}\r\n"; foreach my $rcpt (@{$data->{rcpt}}) { print $fh "$rcpt\r\n"; } print $fh "\r\n"; $data->{data} =~ s/\r?\n|\r/\r\n/g; print $fh $data->{data}; close $fh; rename $infile => $queuefile or die __PACKAGE__."#send: failed to rename [$infile] => [$queuefile] (ãªãã¼ã ã§ãã¾ãã)\n"; $this; } sub process { my $this = shift; local($_); # æåã«recoverå®è¡ $this->_recover; my $queue = "$this->{queuedir}/queue"; opendir my $dh, $queue or die __PACKAGE__."#process: failed to opendir [$queue]. (ãã£ã¬ã¯ããªãéãã¾ãã)\n"; while(defined($_ = readdir $dh)) { my $fname = $_; my $queuefile = "$queue/$fname"; -f $queuefile or next; my $outfile = "$this->{queuedir}/outgoing/$fname.$$"; rename $queuefile => $outfile or die __PACKAGE__."#process: failed to rename [$queuefile] => [$outfile] (ãªãã¼ã ã§ãã¾ãã)\n"; eval { if($this->_tryToSend($outfile)) { # æå unlink $outfile or die __PACKAGE__."#process: failed to unlink [$outfile] (ãã¡ã¤ã«ãåé¤ã§ãã¾ãã)\n"; } else { # 䏿ç失æ my $deferral = "$this->{queuedir}/queue/$fname"; rename $outfile => $deferral or die __PACKAGE__."#process: failed to rename [$outfile] => [$deferral] (ãªãã¼ã ã§ãã¾ãã)\n"; } }; if(my $error = $@) { # æ°¸ç¶ç失æ my $data = do { local $/ = undef; open my $fh, '<', $outfile or die __PACKAGE__."#process: failed to read [$outfile] (ãã¡ã¤ã«ãèªãã¾ãã)\n"; <$fh>; }; $data = Unicode::Japanese->new($data, 'auto')->get; $data =~ s/\r?\n|\r/\n/g; unlink $outfile or die __PACKAGE__."#process: failed to unlink [$outfile] (ãã¡ã¤ã«ãåé¤ã§ãã¾ãã)\n"; if($this->{errorlog}) { $TL->log( __PACKAGE__, "Failed to send the following message permanently:\n". "$data\n\n". "Error:\n$error" ); } if($this->{erroraddr}) { my $addr = $this->{erroraddr}; my $host = 'localhost'; if($addr =~ s/%(.+)$//) { $host = $1; } my $mail = $TL->newMail; my $from = 'null@'.$mail->_getHostname.''; $mail->setHeader( From => "Tripletail::Sendmail::MailQueue <$from>", To => $addr, Subject => "Tripletail::Sendmail::MailQueue é é失æ", ); $mail->setBody( "以ä¸ã®ã¡ã¼ã«ã®é éã«å¤±æãã¾ãã:\n\n". "$data\n\n". "ã¨ã©ã¼:\n$error" ); my $smtp = Tripletail::Sendmail::Smtp->_new($this->{group}); $smtp->connect($host); $smtp->send( rcpt => $addr, from => $addr, data => $mail->toStr, ); $smtp->disconnect; } } } closedir $dh; } sub _tryToSend { # æ°¸ç¶ç失æãèµ·ãã£ãå ´åã¯dieããã my $this = shift; my $fname = shift; my $data = do { local $/ = undef; open my $fh, '<', $fname or die __PACKAGE__."#process: failed to read file [$fname] (ãã¡ã¤ã«ãèªãã¾ãã)"; <$fh>; }; # ã¨ã³ããã¼ãFROM, ã¨ã³ããã¼ãTOãèªã¿åºã $data =~ s/^(.+?)\r\n// or die; my $from = $1; my $rcpt = []; while($data =~ s/^(.*?)\r\n//) { if(length $1) { push @$rcpt, $1; } else { last; } } eval { $this->{smtp}->connect($this->{host}); }; if($@) { # ç¹ãããªã => 䏿失æ $TL->log(__PACKAGE__, $@); return undef; } eval { $this->{smtp}->send( rcpt => $rcpt, from => $from, data => $data, ); }; if($@) { if($this->{smtp}->_getResultCode =~ m/^4/) { # 䏿失æ $TL->log(__PACKAGE__, $@); return undef; } else { # æ°¸ç¶ç失æ die $@; } } else { $TL->log(__PACKAGE__, "sent [$fname] successfully"); } eval { $this->{smtp}->disconnect; }; if($@) { $TL->log(__PACKAGE__, $@); } # æå return 1; } sub _recover { my $this = shift; $this->_recover_incoming; $this->_recover_outgoing; } sub _recover_incoming { my $this = shift; local($_); my $incoming = "$this->{queuedir}/incoming"; opendir my $dh, $incoming or die __PACKAGE__."#process: failed to opendir [$incoming] (ãã£ã¬ã¯ããªãéãã¾ãã)\n"; while(defined($_ = readdir $dh)) { my $fname = $_; my $fpath = "$incoming/$fname"; if(-f $fpath && $fname =~ m/^TL-\d+-(\d+)-/) { my $pid = $1; # ãã®ããã»ã¹ãçãã¦ãããã©ãããkill 0ã§èª¿ã¹ãã if(kill 0, $pid) { # çãã¦ããã®ã§å¼ããªãã next; } else { # æ»ãã§ããã®ã§æ¶ã $TL->log( __PACKAGE__, "Incoming mail [$fpath] seems to be an orphan. Deleting..." ); unlink $fpath or die __PACKAGE__."#process: failed to unlink [$fpath] (ãã¡ã¤ã«ãåé¤ã§ãã¾ãã)"; } } } closedir $dh; } sub _recover_outgoing { my $this = shift; local($_); my $outgoing = "$this->{queuedir}/outgoing"; my $queue = "$this->{queuedir}/queue"; opendir my $dh, $outgoing or die __PACKAGE__."#process: failed to opendir [$outgoing] (ãã£ã¬ã¯ããªãéãã¾ãã)\n"; while(defined($_ = readdir $dh)) { my $fname = $_; my $fpath = "$outgoing/$fname"; if(-f $fpath and $fpath =~ m/\.(\d+)$/) { my $pid = $1; # ãã®ããã»ã¹ãçãã¦ãããã©ãããkill 0ã§èª¿ã¹ãã if(kill 0, $pid) { # çãã¦ããã®ã§å¼ããªãã next; } else { # æ»ãã§ããã®ã§queueã«æ»ãã $TL->log( __PACKAGE__, "Outgoing mail [$fpath] seems to be an orphan. Requeueing..." ); my $requeue = "$queue/$fname"; $requeue =~ s/\.\d+$//; # pid ãæ¶ã rename $fpath => $requeue or die __PACKAGE__."#process: failed to rename [$fpath] => [$requeue] (ãªãã¼ã ã§ãã¾ãã)\n"; } } } closedir $dh; } __END__