diff options
| author | fukachan <fukachan> | 2006-03-25 03:50:07 +0000 |
|---|---|---|
| committer | fukachan <fukachan> | 2006-03-25 03:50:07 +0000 |
| commit | a54a4d0893c4f64fab7c38747b2237ed722c9779 (patch) | |
| tree | ea5034e17007d0f547e57f1b9006f3dcbf38ddb4 /fml/lib/Mail/Delivery | |
| parent | b09b126042d8e6ac17bbda3debb7ba056f2a442a (diff) | |
| download | fml8-a54a4d0893c4f64fab7c38747b2237ed722c9779.tar.gz fml8-a54a4d0893c4f64fab7c38747b2237ed722c9779.tar.bz2 fml8-a54a4d0893c4f64fab7c38747b2237ed722c9779.zip | |
overhaul.
more access method based.
Diffstat (limited to 'fml/lib/Mail/Delivery')
| -rw-r--r-- | fml/lib/Mail/Delivery/Queue.pm | 814 | ||||
| -rw-r--r-- | fml/lib/Mail/Delivery/SMTP.pm | 340 |
2 files changed, 599 insertions, 555 deletions
diff --git a/fml/lib/Mail/Delivery/Queue.pm b/fml/lib/Mail/Delivery/Queue.pm index 1c70fd89..18ad6d07 100644 --- a/fml/lib/Mail/Delivery/Queue.pm +++ b/fml/lib/Mail/Delivery/Queue.pm @@ -4,15 +4,17 @@ # All rights reserved. This program is free software; you can # redistribute it and/or modify it under the same terms as Perl itself. # -# $FML: Queue.pm,v 1.57 2006/02/15 13:52:49 fukachan Exp $ +# $FML: Queue.pm,v 1.58 2006/03/21 12:58:13 fukachan Exp $ # package Mail::Delivery::Queue; use strict; use Carp; -use vars qw($Counter @class_list @local_class_list $counter); +use vars qw(@ISA $Counter @class_list @local_class_list $counter); use File::Spec; -use Mail::Delivery::ErrorStatus qw(error_set error error_clear); + +use Mail::Delivery::Base; +@ISA = qw(Mail::Delivery::Base); =head1 NAME @@ -79,8 +81,15 @@ my $default_policy = "oldest"; my $dir_mode = 0755; @class_list = qw(lock - new deferred active incoming info sender recipients - transport strategy + new + deferred + active + incoming + info + sender + recipients + transport + strategy ); # Descriptions: constructor. @@ -118,10 +127,7 @@ sub new # hold information for delivery my $qf_new = $me->new_file_path($id); - my $files = []; - push(@$files, $qf_new); - $me->{ _cleanup_files } = $files; - + $me->{ _cleanup_files } = [ $qf_new ]; return bless $me, $type; } @@ -170,6 +176,23 @@ sub _new_queue_id } +# Descriptions: clear this queue file. +# Arguments: OBJ($self) +# Side Effects: unlink this queue +# Return Value: NUM +sub DESTROY +{ + my ($self) = @_; + my $files = $self->{ _cleanup_files } || []; + + for my $file (@$files) { + unlink $file if -f $file; + } +} + + +=head1 INFORMATION + =head2 id() return the queue id assigned to this object C<$self>. @@ -188,6 +211,71 @@ sub id } +=head2 getidinfo($id) + +return information related with the queue id C<$id>. +The returned information is + + id => $id, + path => "$dir/active/$id", + sender => $sender, + recipients => \@recipients, + +=cut + + +# Descriptions: get information of queue for this object. +# Arguments: OBJ($self) STR($id) +# Side Effects: none +# Return Value: HASH_REF +sub getidinfo +{ + my ($self, $id) = @_; + + $id ||= $self->id(); + + my $sender = $self->get_sender($id); + my $rcpts = $self->get_recipient_as_array_ref($id); + return { + id => $id, + path => $self->active_file_path($id), + sender => $sender || '', + recipients => $rcpts || [], + }; +} + + +# Descriptions: when last modified. +# Arguments: OBJ($self) STR($id) +# Side Effects: none +# Return Value: NUM(oldest unix time) +sub last_modified_time +{ + my ($self, $id) = @_; + my $min_mtime = time; + + # queue id. + $id ||= $self->id(); + + use File::stat; + for my $class (@class_list, @local_class_list) { + my $fp = sprintf("%s_file_path", $class); + my $file = $self->can($fp) ? $self->$fp($id) : + $self->local_file_path($class, $id); + + if (-f $file) { + my $st = stat($file); + my $mtime = $st->mtime(); + + # find oldest file info. + $min_mtime = $min_mtime < $mtime ? $min_mtime : $mtime; + } + } + + return $min_mtime; +} + + =head2 list( [ $class ] ) return queue list as ARRAY REFERENCE. @@ -251,11 +339,12 @@ sub list_all push(@r, @$ra); } - for my $q (sort @r) { + # generate unique array by removing duplication. + for my $q (@r) { $r{ $q } = 1; } - @r = keys %r; + return \@r; } @@ -390,6 +479,11 @@ sub _queue_streategy_newest } +=head1 SCHEDULE MANAGEMENT + +=cut + + # Descriptions: update queue info for queue management policy. # Arguments: OBJ($self) HASH_REF($policy_args) # Side Effects: update $self. @@ -401,7 +495,7 @@ sub update_schedule my $qf_deferred = $self->deferred_file_path($id); # get hints. - my $hints = $self->_update_schedule_info($id); + my $hints = $self->_update_schedule_strategy($id); my $sleep = $hints->{ sleep } || 300; my $time = time + $sleep; @@ -414,7 +508,7 @@ sub update_schedule # Arguments: OBJ($self) STR($id) # Side Effects: none # Return Value: none -sub _update_schedule_info +sub _update_schedule_strategy { my ($self, $id) = @_; my $info = {}; @@ -486,14 +580,14 @@ sub _change_queue_mode rename($qf_deferred, $qf_active); $self->touch($qf_active); if (-f $qf_active) { - $self->log("qid=$id activated."); + $self->_log("qid=$id activated."); } else { - $self->log("error: qid=$id operation failed."); + $self->_log("error: qid=$id operation failed."); } } else { - $self->log("no such deferred queue qid=$id"); + $self->_log("no such deferred queue qid=$id"); } } elsif ($to_mode eq 'deferred' || $to_mode eq 'defer') { @@ -503,24 +597,24 @@ sub _change_queue_mode $self->update_schedule($qstr_args); if (-f $qf_deferred) { - $self->log("qid=$id deferred"); + $self->_log("qid=$id deferred"); } else { - $self->log("error: qid=$id operation failed."); + $self->_log("error: qid=$id operation failed."); } } else { - $self->log("no such active queue qid=$id"); + $self->_log("no such active queue qid=$id"); } } else { - $self->log("invalid mode"); + $self->_log("invalid mode"); } $self->unlock(); } else { - $self->log("qid=$id lock failed."); + $self->_log("qid=$id lock failed."); } } @@ -553,105 +647,15 @@ sub reschedule } if ($count) { - $self->log("activate $count queue(s)"); - $self->log("$early queue(s) sleeping") if $early; + $self->_log("activate $count queue(s)"); + $self->_log("$early queue(s) sleeping") if $early; } else { - $self->log("$early queue(s) sleeping"); + $self->_log("$early queue(s) sleeping"); } } -=head1 METHODS TO MANIPULATE INFORMATION - -=head2 getidinfo($id) - -return information related with the queue id C<$id>. -The returned information is - - id => $id, - path => "$dir/active/$id", - sender => $sender, - recipients => \@recipients, - -=cut - - -# Descriptions: get information of queue for this object. -# Arguments: OBJ($self) STR($id) -# Side Effects: none -# Return Value: HASH_REF -sub getidinfo -{ - my ($self, $id) = @_; - my $dir = $self->{ _directory }; - my ($fh, $sender, @recipients); - - # validate if the queue id is given - $id ||= $self->id(); - - # sender - use FileHandle; - $fh = new FileHandle $self->sender_file_path($id); - if (defined $fh) { - $sender = $fh->getline; - $sender =~ s/[\n\s]*$//o; - $fh->close; - } - - # recipient array - $fh = new FileHandle $self->recipients_file_path($id); - if (defined $fh) { - my $buf; - - ENTRY: - while (defined($buf = $fh->getline)) { - $buf =~ s/[\n\s]*$//o; - push(@recipients, $buf); - } - $fh->close; - } - - return { - id => $id, - path => $self->active_file_path($id), - sender => $sender || '', - recipients => \@recipients || [], - }; -} - - -# Descriptions: when last modified. -# Arguments: OBJ($self) STR($id) -# Side Effects: none -# Return Value: NUM(oldest unix time) -sub last_modified_time -{ - my ($self, $id) = @_; - my $min_mtime = time; - - # queue id. - $id ||= $self->id(); - - use File::stat; - for my $class (@class_list, @local_class_list) { - my $fp = sprintf("%s_file_path", $class); - my $file = $self->can($fp) ? $self->$fp($id) : - $self->local_file_path($class, $id); - - if (-f $file) { - my $st = stat($file); - my $mtime = $st->mtime(); - - # find oldest file info. - $min_mtime = $min_mtime < $mtime ? $min_mtime : $mtime; - } - } - - return $min_mtime; -} - - =head1 LOCK =head2 lock() @@ -708,6 +712,67 @@ sub unlock } +=head1 IO + +=head2 open($class, $args) + +open incoming queue of this queue id with mode $mode and return the +file handle. + +=head2 close($class) + +close. + +=cut + + +# Descriptions: open incoming queue of this object with mode $mode +# and return the file handle. +# Arguments: OBJ($self) STR($class) HASH_REF($op_args) +# Side Effects: file handle opened. +# Return Value: HANDLE +sub open +{ + my ($self, $class, $op_args) = @_; + my $id = $self->id(); + my $fp = sprintf("%s_file_path", $class); + my $qf = $self->can($fp) ? $self->$fp($id) : + $self->local_file_path($class, $id); + + if (defined $op_args->{ in_channel }) { + my $channel = $op_args->{ in_channel }; + open($channel, $qf); + } + else { + use FileHandle; + my $mode = $op_args->{ mode } || "r"; + my $fh = new FileHandle $qf, $mode; + if (defined $fh) { + $self->{ "_${class}_channel" } = $fh; + return $fh; + } + else { + return undef; + } + } +} + + +# Descriptions: close the incoming channel of this object. +# Arguments: OBJ($self) STR($class) +# Side Effects: file handle closed. +# Return Value: none +sub close +{ + my ($self, $class) = @_; + my $channel = $self->{ "_${class}_channel" } || undef; + + if (defined $channel) { + close($channel); + } +} + + =head2 in($msg) C<in()> creates a queue file in C<new/> directory @@ -747,12 +812,13 @@ sub in $fh->clearerr(); $msg->print($fh); if ($fh->error()) { - $self->error_set("write error"); + $self->set_error("write error"); } $fh->close; if ($msg->can('write_count')) { - my $write_count = $self->{ _write_count } = $msg->write_count(); + my $write_count = $msg->write_count(); + $self->set_write_count($write_count); use File::stat; my $try_count = 3; @@ -768,7 +834,7 @@ sub in } unless ($ok) { - $self->error_set("write error: size mismatch"); + $self->set_error("write error: size mismatch"); } } } @@ -778,108 +844,117 @@ sub in } -# Descriptions: return num of bytes written successfully. -# Arguments: OBJ($self) +# Descriptions: create a new queue file. +# Arguments: OBJ($self) OBJ($msg) # Side Effects: none -# Return Value: NUM -sub write_count +# Return Value: 1 or 0 +sub add { - my ($self) = @_; - - return( $self->{ _write_count } || 0 ); + my ($self, $msg) = @_; + $self->in($msg); } -=head2 set($key, $args) +=head2 add ($msg) - $queue->set('sender', $sender); - $queue->set('recipients', [ $recipient0, $recipient1 ] ); +same as in(). -It sets up delivery information in C<info/sender/> and -C<info/recipients/> directories. +=head2 delete() + +remove all queue assigned to this object C<$self>. + +=head2 remove() + +remove all queue assigned to this object C<$self>. + +=head2 valid() + +check if the queue file is broken or not. +return 1 (valid) or 0 (broken). =cut -# Descriptions: set value for key. -# Arguments: OBJ($self) STR($key) STR($value) -# Side Effects: none -# Return Value: same as close() -sub set +# Descriptions: remove queue files for this object (queue). +# Arguments: OBJ($self) +# Side Effects: remove queue file(s) +# Return Value: none +sub delete { - my ($self, $key, $value) = @_; - my $id = $self->id(); - my $qf_sender = $self->sender_file_path($id); - my $qf_recipients = $self->recipients_file_path($id); - my $qf_transport = $self->transport_file_path($id); + my ($self) = @_; + $self->remove(); +} - use FileHandle; - if ($key eq 'sender') { - my $fh = new FileHandle "> $qf_sender"; - if (defined $fh) { - $fh->clearerr(); - print $fh $value, "\n"; - if ($fh->error()) { - $self->error_set("write error"); - } - $fh->close; - } - } - elsif ($key eq 'recipients') { - my $fh = new FileHandle ">> $qf_recipients"; - if (defined $fh) { - $fh->clearerr(); - if (ref($value) eq 'ARRAY') { - for my $rcpt (@$value) { print $fh $rcpt, "\n";} - } - if ($fh->error()) { - $self->error_set("write error"); - } - $fh->close; +# Descriptions: remove queue files for this object (queue). +# Arguments: OBJ($self) +# Side Effects: remove queue file(s) +# Return Value: none +sub remove +{ + my ($self) = @_; + my $id = $self->id(); + + my $count = 0; + my $removed = 0; + for my $class (@class_list, @local_class_list) { + my $fp = sprintf("%s_file_path", $class); + my $f = $self->can($fp) ? $self->$fp($id) : + $self->local_file_path($class, $id); + + if (-f $f) { + $count++; + unlink $f; + $removed++ unless -f $f; } } - elsif ($key eq 'recipient_maps') { - my $fh = new FileHandle ">> $qf_recipients"; - if (defined $fh) { - $fh->clearerr(); - - if (ref($value) eq 'ARRAY') { - for my $map (@$value) { - use IO::Adapter; - my $obj = new IO::Adapter $map; - if (defined $obj) { - $obj->open(); - - my $buf; - while ($buf = $obj->get_next_key()) { - print $fh $buf, "\n"; - } - $obj->close(); - } - } - } - if ($fh->error()) { - $self->error_set("write error"); - } - $fh->close; + if ($count > 0) { + if ($count == $removed) { + $self->_log("qid=$id removed"); } - } - elsif ($key eq 'transport') { - my $fh = new FileHandle "> $qf_transport"; - if (defined $fh) { - $fh->clearerr(); - print $fh $value, "\n"; - if ($fh->error()) { - $self->error_set("write error"); - } - $fh->close; + else { + $self->_log("qid=$id remove failed"); } } } +# Descriptions: return num of bytes written successfully. +# Arguments: OBJ($self) +# Side Effects: none +# Return Value: NUM +sub write_count +{ + my ($self) = @_; + $self->get_write_count(); +} + + +# Descriptions: return num of bytes written successfully. +# Arguments: OBJ($self) +# Side Effects: none +# Return Value: NUM +sub get_write_count +{ + my ($self) = @_; + + return( $self->{ _write_count } || 0 ); +} + + +# Descriptions: save num of bytes written successfully. +# Arguments: OBJ($self) NUM($count) +# Side Effects: none +# Return Value: NUM +sub set_write_count +{ + my ($self, $count) = @_; + + $self->{ _write_count } = $count || 0; +} + + =head2 setrunnable() set the status of the queue assigned to this object C<$self> @@ -906,8 +981,8 @@ sub setrunnable my $qf_recipients = $self->recipients_file_path($id); # something error. - if ($self->error()) { - warn( $self->error() ); + if ($self->get_error()) { + warn( $self->get_error() ); return 0; } @@ -955,67 +1030,204 @@ sub touch } -=head2 delete() +=head1 ACCESS METHOD -remove all queue assigned to this object C<$self>. - -=head2 remove() +=head2 set($key, $args) -remove all queue assigned to this object C<$self>. +defined for compatibility. -=head2 valid() + $queue->set('sender', $sender); + $queue->set('recipients', [ $recipient0, $recipient1 ] ); -check if the queue file is broken or not. -return 1 (valid) or 0 (broken). +It sets up delivery information in C<info/sender/> and +C<info/recipients/> directories. =cut -# Descriptions: remove queue files for this object (queue). -# Arguments: OBJ($self) -# Side Effects: remove queue file(s) -# Return Value: none -sub delete +# Descriptions: set value for key. +# Arguments: OBJ($self) STR($key) STR($value) +# Side Effects: none +# Return Value: same as close() +sub set { - my ($self) = @_; - $self->remove(); + my ($self, $key, $value) = @_; + my $id = $self->id(); + + if ($key eq 'sender') { + $self->set_sender($id, $value); + } + elsif ($key eq 'recipients') { + $self->set_recipient_as_array_ref($id, $value); + } + elsif ($key eq 'recipient_maps') { + $self->set_recipient_maps($id, $value); + } + elsif ($key eq 'transport') { + $self->set_transport($id, $value); + } } -# Descriptions: remove queue files for this object (queue). -# Arguments: OBJ($self) -# Side Effects: remove queue file(s) +# Descriptions: set sender for queue $id. +# Arguments: OBJ($self) STR($id) STR($value) +# Side Effects: create queue file. # Return Value: none -sub remove +sub set_sender { - my ($self) = @_; - my $id = $self->id(); + my ($self, $id, $value) = @_; + my $qf_sender = $self->sender_file_path($id); - my $count = 0; - my $removed = 0; - for my $class (@class_list, @local_class_list) { - my $fp = sprintf("%s_file_path", $class); - my $f = $self->can($fp) ? $self->$fp($id) : - $self->local_file_path($class, $id); + use FileHandle; + my $fh = new FileHandle "> $qf_sender"; + if (defined $fh) { + $fh->clearerr(); + print $fh $value, "\n"; + if ($fh->error()) { + $self->set_error("write error"); + } + $fh->close; + } + else { + $self->set_error("cannot open $qf_sender"); + } +} - if (-f $f) { - $count++; - unlink $f; - $removed++ unless -f $f; + +# Descriptions: get sender for queue $id. +# Arguments: OBJ($self) STR($id) +# Side Effects: create queue file. +# Return Value: STR +sub get_sender +{ + my ($self, $id) = @_; + my $qf_sender = $self->sender_file_path($id); + my $sender = ''; + + use FileHandle; + my $fh = new FileHandle $qf_sender; + if (defined $fh) { + $sender = $fh->getline; + $sender =~ s/[\n\s]*$//o; + $fh->close; + } + else { + $self->set_error("cannot open $qf_sender"); + } + + return $sender; +} + + +sub set_recipient_as_array_ref +{ + my ($self, $id, $value) = @_; + my $qf_recipients = $self->recipients_file_path($id); + + use FileHandle; + my $fh = new FileHandle ">> $qf_recipients"; + if (defined $fh) { + $fh->clearerr(); + if (ref($value) eq 'ARRAY') { + for my $rcpt (@$value) { print $fh $rcpt, "\n";} } + if ($fh->error()) { + $self->set_error("write error"); + } + $fh->close; + } + else { + $self->set_error("cannot open $qf_recipients"); } +} - if ($count > 0) { - if ($count == $removed) { - $self->log("qid=$id removed"); + +sub get_recipient_as_array_ref +{ + my ($self, $id) = @_; + my (@recipients) = (); + my $qf_recipients = $self->recipients_file_path($id); + + use FileHandle; + my $fh = new FileHandle $qf_recipients ; + if (defined $fh) { + my $buf; + + ENTRY: + while (defined($buf = $fh->getline)) { + $buf =~ s/[\n\s]*$//o; + push(@recipients, $buf); } - else { - $self->log("qid=$id remove failed"); + $fh->close; + } + else { + $self->set_error("cannot open $qf_recipients "); + } + + return \@recipients; +} + + +sub set_recipient_maps +{ + my ($self, $id, $value) = @_; + my $qf_recipients = $self->recipients_file_path($id); + + use FileHandle; + my $fh = new FileHandle ">> $qf_recipients"; + if (defined $fh) { + $fh->clearerr(); + + if (ref($value) eq 'ARRAY') { + for my $map (@$value) { + use IO::Adapter; + my $io = new IO::Adapter $map; + if (defined $io) { + $io->open(); + + my $buf; + while ($buf = $io->get_next_key()) { + print $fh $buf, "\n"; + } + $io->close(); + } + } + } + + if ($fh->error()) { + $self->set_error("write error"); } + $fh->close; } } +sub set_transport +{ + my ($self, $id, $value) = @_; + my $qf_transport = $self->transport_file_path($id); + + use FileHandle; + my $fh = new FileHandle "> $qf_transport"; + if (defined $fh) { + $fh->clearerr(); + print $fh $value, "\n"; + if ($fh->error()) { + $self->set_error("write error"); + } + $fh->close; + } + else { + $self->set_error("cannot open $qf_transport"); + } +} + + +=head1 UTILITIES + +=cut + + # Descriptions: this object (queue) is sane as active queue? # Arguments: OBJ($self) # Side Effects: none @@ -1072,24 +1284,7 @@ sub is_valid_queue } -# Descriptions: clear this queue file. -# Arguments: OBJ($self) -# Side Effects: unlink this queue -# Return Value: NUM -sub DESTROY -{ - my ($self) = @_; - my $files = $self->{ _cleanup_files } || []; - - for my $file (@$files) { - unlink $file if -f $file; - } -} - - -=head1 UTILITIES - -=head2 dup_content($class) +=head2 dup_content($old_class, $new_class) duplicate content at a class $class other than incoming. @@ -1113,67 +1308,6 @@ sub dup_content } -=head1 IO Interface - -=head2 open($class, $args) - -open incoming queue of this queue id with mode $mode and return the -file handle. - -=head2 close($class) - -close. - -=cut - - -# Descriptions: open incoming queue of this object with mode $mode -# and return the file handle. -# Arguments: OBJ($self) STR($class) HASH_REF($op_args) -# Side Effects: file handle opened. -# Return Value: HANDLE -sub open -{ - my ($self, $class, $op_args) = @_; - my $id = $self->id(); - my $fp = sprintf("%s_file_path", $class); - my $qf = $self->can($fp) ? $self->$fp($id) : - $self->local_file_path($class, $id); - - if (defined $op_args->{ in_channel }) { - my $channel = $op_args->{ in_channel }; - open($channel, $qf); - } - else { - use FileHandle; - my $mode = $op_args->{ mode } || "r"; - my $fh = new FileHandle $qf, $mode; - if (defined $fh) { - $self->{ "_${class}_channel" } = $fh; - return $fh; - } - else { - return undef; - } - } -} - - -# Descriptions: close the incoming channel of this object. -# Arguments: OBJ($self) STR($class) -# Side Effects: file handle closed. -# Return Value: none -sub close -{ - my ($self, $class) = @_; - my $channel = $self->{ "_${class}_channel" } || undef; - - if (defined $channel) { - close($channel); - } -} - - =head1 DIR/FILE UTILITIES =cut @@ -1476,57 +1610,19 @@ sub local_file_path } -=head1 LOG - -=head2 log() - -=head2 get_log_function() - -=head2 set_log_function($fp) +=head1 LOGGING INTERFACE =cut # Descriptions: log interface. -# Arguments: OBJ($self) STR($s) +# Arguments: OBJ($self) STR($buf) # Side Effects: none # Return Value: none -sub log -{ - my ($self, $s) = @_; - my $fp = $self->get_log_function(); - - my $buf = "qmgr: $s"; - if (defined $fp) { - eval q{ &$fp($buf);}; - if ($@) { - carp($@); - } - } -} - - -# Descriptions: return log function pointer. -# Arguments: OBJ($self) -# Side Effects: none -# Return Value: CODE -sub get_log_function +sub _log { - my ($self) = @_; - - return( $self->{ _log_function } || undef ); -} - - -# Descriptions: return log function pointer. -# Arguments: OBJ($self) CODE($fp) -# Side Effects: update $self. -# Return Value: CODE -sub set_log_function -{ - my ($self, $fp) = @_; - - $self->{ _log_function } = $fp || undef; + my ($self, $buf) = @_; + $self->log("qmgr: $buf"); } @@ -1551,7 +1647,7 @@ sub cleanup use DirHandle; use File::stat; - my $incoming_queue_dir = File::Spec->catfile($dir, "incoming"); + my $incoming_queue_dir = $self->incoming_dir_path(); my $dh = new DirHandle $incoming_queue_dir; if (defined $dh) { my ($file, $entry, $stat); @@ -1561,10 +1657,10 @@ sub cleanup while ($entry = $dh->read()) { next ENTRY if $entry =~ /^\./o; - $file = File::Spec->catfile($dir, "incoming", $entry); + $file = $self->incoming_file_path($entry); $stat = stat($file); if ($stat->mtime < $day_limit) { - $self->log("remove too old incoming queue: qid=$entry"); + $self->_log("remove too old incoming queue: qid=$entry"); unlink $file; } } diff --git a/fml/lib/Mail/Delivery/SMTP.pm b/fml/lib/Mail/Delivery/SMTP.pm index 44c260e7..26ffc877 100644 --- a/fml/lib/Mail/Delivery/SMTP.pm +++ b/fml/lib/Mail/Delivery/SMTP.pm @@ -1,10 +1,10 @@ #-*- perl -*- # -# Copyright (C) 2000,2001,2002,2003,2004 Ken'ichi Fukamachi +# Copyright (C) 2000,2001,2002,2003,2004,2006 Ken'ichi Fukamachi # All rights reserved. This program is free software; you can # redistribute it and/or modify it under the same terms as Perl itself. # -# $FML: SMTP.pm,v 1.36 2006/03/21 13:01:43 fukachan Exp $ +# $FML: SMTP.pm,v 1.37 2006/03/23 15:33:09 fukachan Exp $ # @@ -13,10 +13,10 @@ use strict; use vars qw(@ISA @EXPORT @EXPORT_OK $fp_address_validate); use Carp; use IO::Socket; -use Mail::Delivery::Utils; -use Mail::Delivery::Net::INET4; -use Mail::Delivery::Net::INET6; +# Mail::Delivery::SMTP IS-A Mail::Delivery::Protocol. +use Mail::Delivery::Protocol; +@ISA = qw(Mail::Delivery::Protocol); BEGIN {} END {} @@ -33,6 +33,7 @@ my $MTA_ERR_TIMEOUT = 'TIMEOUT'; # SMTP STATUS my $SMTP_OK = 'OK'; my $SMTP_ERR_RETRY = 'SMTP RETRY'; +my $SMTP_ERR_FATAL = 'SMTP FATAL ERROR'; =head1 NAME @@ -48,14 +49,14 @@ To initialize, ... make $message (Mail::Message object) ... use Mail::Delivery::SMTP; - my $fp = sub { Log(@_);}; # pointer to the log function + my $fp = sub { $curproc->log(@_);}; # pointer to the log function my $sfp = sub { my ($s) = @_; print $s; print "\n" if $s !~ /\n$/o;}; my $service = new Mail::Delivery::SMTP { log_function => $fp, smtp_log_function => $sfp, default_io_timeout => 10, }; - if ($service->error) { Log($service->error); return;} + if ($service->error) { $curproc->logerror($service->error); return;} To start delivery, use deliver() method in this way. @@ -134,25 +135,23 @@ sub new my ($type) = ref($self) || $self; my $me = {}; # malloc new SMTP session struct - # _recipient_limit: maximum recipients in one smtp session. - # _default_io_timeout: basic timeout parameter for smtp session - # _log_function: pointer to the log() function - $me->{ _recipient_limit } = $args->{recipient_limit} || 1000; - $me->{ _default_io_timeout } = $args->{default_io_timeout} || 10; - $me->{ _log_function } = $args->{log_function} || undef; - $me->{ _smtp_log_function } = $args->{smtp_log_function} || undef; - $me->{ _smtp_log_handle } = $args->{smtp_log_handle} || undef; - $me->{ _num_recipients } = 0; + $me->{ _num_recipients } = 0; # define package global pointer to the log() function - $LogFunctionPointer = $args->{log_function} || undef; - $SmtpLogFunctionPointer = $args->{smtp_log_function} || undef; - $fp_address_validate = $args->{address_validate_function} || undef; + $fp_address_validate = $args->{address_validate_function} || undef; bless $me, $type; + # smtp_recipient_limit: maximum recipients in one smtp session. + # default_timeout: basic timeout parameter for smtp session + $me->set_smtp_recipient_limit($args->{ recipient_limit } || 1000); + $me->set_smtp_default_timeout($args->{ default_io_timeout } || 10); + _initialize_delivery_session($me, $args); + # new style (2006/03) + $me->_initialize_logging($args); + return bless $me, $type; } @@ -165,7 +164,7 @@ sub new sub _send_command { my ($self, $command) = @_; - my $socket = $self->{'_socket'} || undef; + my $socket = $self->get_socket() || undef; $self->set_last_command($command); $self->set_send_command_status(''); @@ -175,7 +174,7 @@ sub _send_command $socket->print($command, "\r\n"); } else { - Log("Error: _send_command: undefined socket"); + $self->logerror("_send_command: undefined socket"); } } @@ -187,7 +186,7 @@ sub _send_command sub _read_reply { my ($self) = @_; - my $socket = $self->{'_socket'}; + my $socket = $self->get_socket() || undef; # unique identifier to clarify the trapped error message my $id = $$; @@ -204,7 +203,7 @@ sub _read_reply # See books on Perl for more details on my() and local() difference. eval { local($SIG{ALRM}) = sub { croak("$id socket timeout");}; - alarm( $self->{_default_io_timeout} ); + alarm( $self->get_smtp_default_timeout() ); my $buf = ''; croak("socket is not connected") unless @@ -233,8 +232,10 @@ sub _read_reply # check status code if ($buf =~ /^[45]\d{2}\s/) { - Log($buf); - die("$id retry"); + my $command = $self->get_last_command(); + $self->logerror(sprintf("%s ... %s", $command, $buf)); + die("$id temprary failure") if $buf =~ /^4/; + die("$id fatal") if $buf =~ /^5/; } # end of reply e.g. "250 ..." @@ -242,16 +243,22 @@ sub _read_reply } }; - if ($@ =~ /$id retry/) { + if ($@ =~ /$id temprary failure/) { $self->set_send_command_status($SMTP_ERR_RETRY); - Log("need smtp retry"); - $self->error_set("need smtp retry"); + $self->logerror("temporary failure, retry"); + $self->set_error("temporary failure, retry"); + } + + if ($@ =~ /$id fatal/) { + $self->set_send_command_status($SMTP_ERR_FATAL); + $self->logerror("fatal error"); + $self->set_error("fatal error"); } if ($@ =~ /$id socket timeout/) { - my $x = $self->get_last_command(); - Log("Error: smtp reply for \"$x\" is timeout"); - $self->error_set("Error: smtp reply for \"$x\" is timeout"); + my $command = $self->get_last_command(); + $self->logerror("smtp reply for \"$command\" is timeout"); + $self->set_error("smtp reply for \"$command\" is timeout"); } # reset latest alarm() setting @@ -259,54 +266,6 @@ sub _read_reply } -# Descriptions: save last command info. -# Arguments: OBJ($self) STR($command) -# Side Effects: update $self -# Return Value: none -sub set_last_command -{ - my ($self, $command) = @_; - - $self->{ _last_command } = $command; -} - - -# Descriptions: get last command info. -# Arguments: OBJ($self) -# Side Effects: update $self -# Return Value: none -sub get_last_command -{ - my ($self) = @_; - - return( $self->{ _last_command } || '' ); -} - - -# Descriptions: save send command info. -# Arguments: OBJ($self) STR($command) -# Side Effects: update $self -# Return Value: none -sub set_send_command_status -{ - my ($self, $command) = @_; - - $self->{ _send_command_status } = $command; -} - - -# Descriptions: get send command info. -# Arguments: OBJ($self) -# Side Effects: update $self -# Return Value: none -sub get_send_command_status -{ - my ($self) = @_; - - return( $self->{ _send_command_status } || '' ); -} - - # Descriptions: connect(2) # 1. try connect(2) by IPv6 if we can use Socket6.pm # 2. try connect(2) by IPv4 @@ -317,85 +276,45 @@ sub get_send_command_status sub _connect { my ($self, $args) = @_; - my $mta = $args->{'_mta'} || '127.0.0.1:25'; - my $socket; - $self->error_clear; + $self->clear_error; - # 1. try to connect(2) $args->{ _mta } by IPv6 if we can use Socket6. + # 1. try to connect(2) $args->{ mta } by IPv6 if we can use Socket6. if ($self->is_ipv6_ready($args)) { - Log("debug: try mta=$args->{_mta} by IPv6"); + $self->logdebug("try mta=$args->{ mta } by IPv6"); $self->connect6($args); - my $socket = $self->{_socket}; - return $socket if defined $socket; + my $socket = $self->get_socket() || undef; + if (defined $socket) { + return $socket; + } + else { + $self->logerror("cannot connect $args->{ mta }"); + } } else { - Log("IPv6 is not ready"); + $self->logdebug("IPv6 is not ready"); } - $self->error_clear; + $self->clear_error; - # 2. try to connect(2) $args->{ _mta } by IPv4. - # XXX check the _mta syntax. - # XXX if $args->{ _mta } looks [$ipv6_addr]:$port style, + # 2. try to connect(2) $args->{ mta } by IPv4. + # XXX check the mta syntax. + # XXX if $args->{ mta } looks [$ipv6_addr]:$port style, # XXX we do not try to connect the host by IPv4. - if ( $self->is_ipv6_mta_syntax($mta) ) { - Log("debug: not try MTA $args->{_mta}"); + my $mta = $args->{ mta } || '127.0.0.1:25'; + if ($self->is_ipv6_mta_syntax($mta)) { + $self->logdebug("not try MTA $args->{ mta }"); return undef; } else { - Log("debug: try mta=$args->{_mta} by IPv4"); - return $self->connect4($args); - } -} - - -=head2 is_socket_connected($socket) - -$socket has peer or not by C<getpeername()>. - - XXX sub $socket->connected { getpeername($self);} - XXX IO::Socket of old perl have no such method. - -=cut - - -# Descriptions: this socket is connected or not. -# Arguments: OBJ($self) HANDLE($socket) -# Side Effects: none -# Return Value: 1 or 0 -sub is_socket_connected -{ - my ($self, $socket) = @_; - - if (defined $socket) { - return( getpeername($socket) ); - } - - return 0; -} - - -=head2 close() - -close BSD socket - -=cut - -# Descriptions: close BSD socket. -# Arguments: OBJ($self) -# Side Effects: none -# Return Value: same as close() -sub close -{ - my ($self) = @_; - my $socket = $self->{'_socket'}; - - if (defined $socket) { - $socket->close; - } - else { - Log("Error: try to close invalid socket"); + $self->logdebug("try mta=$args->{ mta } by IPv4"); + my $socket = $self->connect4($args); + if (defined $socket) { + return $socket; + } + else { + $self->logerror("cannot connect $args->{ mta }"); + } } } @@ -472,12 +391,10 @@ sub deliver { my ($self, $args) = @_; - # recipient limit - $self->{_recipient_limit} = $args->{recipient_limit} || 1000; - - if ($self->{_recipient_limit} != 1000) { - Log("debug: recipient_limit = $self->{_recipient_limit}"); - } + # smtp_recipient_limit: maximum recipients in one smtp session. + # default_timeout: basic timeout parameter for smtp session + $self->set_smtp_recipient_limit($args->{ recipient_limit } || 1000); + $self->set_smtp_default_timeout($args->{ default_io_timeout } || 10); # temporary hash to check whether the map/mta is used already. my %used_mta = (); @@ -512,7 +429,7 @@ sub deliver }; if ($@) { $self->set_map_status($map, $MAP_ERR_OPEN); - Log("Error: cannot open and ignore $map"); + $self->logerror("cannot open and ignore $map"); next MAP; } @@ -532,8 +449,8 @@ sub deliver # check infinite loop if ($loop_count++ > $max_loop_count) { - my $r = "too many smtp retry, give up map=$map"; - $self->error_set($r); + $self->logdebug("too many smtp retry, give up map=$map"); + $self->set_error("too many smtp retry, give up"); last MTA_RETRY_LOOP; } @@ -551,12 +468,12 @@ sub deliver $n_mta++; # o.k. try to deliver mail by using $mta. - Log("debug: use $mta for map=$map"); - $args->{ _mta } = $mta; + $self->logdebug("use $mta for map=$map"); + $args->{ mta } = $mta; $self->_deliver($args); # remove error messages for the next _deliver() session. - $self->error_clear; + $self->clear_error; # we read the whole $map now. if ($self->get_map_status($map) eq $MAP_DONE) { @@ -572,9 +489,9 @@ sub deliver # NO effective mta in this inter loop. It impiles that # we used all MTA candidates. We reuse @mta again. if ($n_mta == 0) { - Log("debug: we used all MTA candidates. reuse \$mta"); + $self->logdebug("we used all MTA candidates. reuse \$mta"); my (@c) = keys %used_mta; - Log("debug: candidates = (@c)"); + $self->logdebug("candidates = (@c)"); undef %used_mta; next MTA_RETRY_LOOP; } @@ -594,11 +511,11 @@ sub deliver # clean up recipient_map information after "all delivery". # CAUTION: this mapinfo tracks the delivery status. - $self->reset_mapinfo; + $self->clear_mapinfo; if ( $self->{ _num_recipients } ) { my $n = $self->{ _num_recipients }; - Log("sent total=$n"); + $self->log("sent total=$n"); } } @@ -614,7 +531,7 @@ sub _fallback_into_queue # log current status. my $pos = $self->get_map_position($map) || 0; - Log("map=$map pos=$pos status=\"$status\""); + $self->logdebug("map=$map pos=$pos status=\"$status\""); # dump into queue. if (defined $args->{ use_queue_dir } && $args->{ use_queue_dir }) { @@ -629,8 +546,8 @@ sub _fallback_into_queue my $obj = new IO::Adapter $map, $args->{ map_params }; if (defined $obj) { $obj->open || do { - Log("cannot open $map"); - Log("fatal: delivery fallback failed."); + $self->logerror("cannot open $map"); + $self->logerror("fatal: delivery fallback failed."); return; }; @@ -665,9 +582,9 @@ sub _fallback_into_queue $queue->in( $msg ) || croak("fail to queue in"); { my $error; - if ($error = $queue->error()) { Log("fallback: $error");} + if ($error = $queue->error()) { $self->logerror("fallback: $error");} my $n = $queue->write_count(); - Log("queue: size=$n written"); + $self->logdebug("queue: size=$n written"); } $msg->set_print_mode($cur_print_mode); @@ -681,11 +598,11 @@ sub _fallback_into_queue $queue->sleep_queue(); }; unless ($@) { - Log("fallback: total=$num_rcpt qid=$qid"); + $self->logdebug("fallback: total=$num_rcpt qid=$qid"); } else { - Log("fallback error: $@"); - Log("fatal: delivery fallback failed."); + $self->logerror("fallback error: $@"); + $self->logerror("fatal: delivery fallback failed."); } } } @@ -725,13 +642,13 @@ sub _deliver my $socket = $self->_connect($args); my $is_connected = $self->is_socket_connected($socket); unless (defined($socket) && $is_connected) { - my $mta = $args->{_mta} || 'unknown'; - Log("cannot connected to $mta"); + my $mta = $args->{mta} || 'unknown'; + $self->logdebug("cannot connect to $mta"); return undef; } else { - my $mta = $args->{_mta} || 'unknown'; - Log("connected to $mta"); + my $mta = $args->{mta} || 'unknown'; + $self->logdebug("connected to $mta"); } # 1. receive the first "220 .." message @@ -739,7 +656,7 @@ sub _deliver # since smtp connection has not established yet. # IF_ERROR_FOUND: do nothing and return as soon as possible $self->_read_reply; - if ($self->error) { + if ($self->get_error) { $self->_set_mta_as_ignored($args); return; } @@ -764,12 +681,12 @@ sub _deliver # 3. MAIL FROM; # IF_ERROR_FOUND: do nothing and return as soon as possible $self->_send_mail_from($args); - if ($self->error) { $self->_reset_smtp_transaction($args); return;} + if ($self->get_error) { $self->_reset_smtp_transaction($args); return;} # 4. RCPT TO; ... send list of recipients # IF_ERROR_FOUND: roll back the process to the state before this $self->_send_recipient_list($args); - if ($self->error) { + if ($self->get_error) { $self->rollback_map_position; $self->_reset_smtp_transaction($args); return; @@ -779,7 +696,7 @@ sub _deliver # IF_ERROR_FOUND: handled in _send_data_to_mta(), so # return as soon as possible from here. $self->_send_data_to_mta($args); - if ($self->error) { + if ($self->get_error) { $self->rollback_map_position; $self->_reset_smtp_transaction($args); return; @@ -789,13 +706,13 @@ sub _deliver # IF_ERROR_FOUND: do nothing ? $self->_send_command("QUIT"); $self->_read_reply; - if ($self->error) { $self->_reset_smtp_transaction; return;} + if ($self->get_error) { $self->_reset_smtp_transaction; return;} # o.k. succeded to deliver. my $n = $self->{ _num_recipients_in_this_transaction } || 0; if ($n) { - my $mta = $args->{ _mta } || 'unknown'; - Log("sent num=$n mta=$mta"); + my $mta = $args->{ mta } || 'unknown'; + $self->log("sent num=$n mta=$mta"); $self->{ _num_recipients } += $n; } } @@ -815,6 +732,35 @@ sub _initialize_delivery_session } +# Descriptions: initialize logging interface. +# Arguments: OBJ($self) HASH_REF($args) +# Side Effects: none +# Return Value: none +sub _initialize_logging +{ + my ($self, $args) = @_; + + # default logging interface (infomational). + my ($fp_log_info) = $args->{ log_info_function } || undef; + if (defined $fp_log_info) { $self->set_log_info_function($fp_log_info);} + + # error level log. + my ($fp_log_error) = $args->{ log_error_function } || undef; + if (defined $fp_log_error) { $self->set_log_error_function($fp_log_error);} + + # debug level log. + my ($fp_log_debug) = $args->{ log_debug_function } || undef; + if (defined $fp_log_debug) { $self->set_log_debug_function($fp_log_debug);} + + # smtp transaction logging interface. + my ($fp_smtp_log) = $args->{ smtp_log_function } || undef; + if (defined $fp_smtp_log) { $self->set_smtp_log_function($fp_smtp_log);} + + my ($handle) = $args->{smtp_log_handle} || undef; + if (defined $handle) { $self->set_smtp_log_handle($handle);} +} + + ############################################################ ##### ##### MAIL FROM: @@ -866,18 +812,18 @@ sub _send_recipient_list_by_recipient_map # $map syntax is "type:parameter", e.g., # file:$filename mysql:$schema_name use IO::Adapter; - my $obj = new IO::Adapter $map, $args->{ map_params }; + my $obj = new IO::Adapter $map, $args->{ map_params }; unless (defined $obj) { - Log("Error: fail to create map=$map object."); + $self->logerror("fail to create map=$map object."); } else { # $obj is good. my $rcpt; my $num_recipients = 0; - my $recipient_limit = $self->{_recipient_limit}; + my $recipient_limit = $self->get_smtp_recipient_limit(); $obj->open || do { - $self->error_set( $obj->error ); + $self->set_error( $obj->error ); return undef; }; @@ -896,12 +842,12 @@ sub _send_recipient_list_by_recipient_map unless ($@) { unless ($r) { $self->smtplog("===> ignore <$rcpt>"); - Log("ignore <$rcpt>"); + $self->logdebug("ignore <$rcpt>"); next RCPT_INPUT; } } else { - Log("\$fp_address_validate failes"); + $self->logerror("cannot call address validate function"); } } @@ -934,7 +880,7 @@ sub _send_recipient_list_by_recipient_map $self->{ _num_recipients_in_this_transaction } = $num_recipients; unless ($num_recipients) { - Log("Error: no recipients for $map"); + $self->logerror("no recipient for $map"); $self->_send_command("RSET"); $self->_read_reply; } @@ -988,12 +934,14 @@ sub _send_body_to_mta my ($self, $socket, $msg) = @_; # XXX $msg is Mail::Message object. - $msg->set_log_function($SmtpLogFunctionPointer, $SmtpLogFunctionPointer); + my $fp = $self->get_smtp_log_function(); + $msg->set_log_function($fp, $fp); $msg->set_print_mode('smtp'); $msg->print($socket); - if (defined $self->{ _smtp_log_handle }) { - $msg->print( $self->{ _smtp_log_handle }); + my $wh = $self->get_smtp_log_handle(); + if (defined $wh) { + $msg->print( $wh ); } $msg->unset_log_function(); @@ -1011,15 +959,15 @@ sub _send_data_to_mta # prepare smtp information my $header = $args->{ message }->whole_message_header; my $body = $args->{ message }->whole_message_body; - my $socket = $self->{'_socket'}; + my $socket = $self->get_socket() || undef; if (defined $body) { $self->_send_command("DATA"); $self->_read_reply; # XXX if "DATA" transaction cannot start, retry ? - if ($self->get_status_code != '354' || $self->error) { - Log($self->error); + if ($self->get_status_code != '354' || $self->get_error) { + $self->logerror($self->get_error); return undef; } @@ -1060,7 +1008,7 @@ sub _reset_smtp_transaction # reset SMTP transaction. $self->_send_command("RSET"); $self->_read_reply; - Log("reset smtp transcation"); + $self->log("reset smtp transcation"); } @@ -1073,7 +1021,7 @@ sub _set_mta_as_ignored my ($self, $args) = @_; # mark this mta is invalid. - my $mta = $args->{ _mta } || ''; + my $mta = $args->{ mta } || ''; if ($mta) { $self->set_mta_status($mta, $MTA_ERR_TIMEOUT); } @@ -1102,7 +1050,7 @@ Ken'ichi Fukamachi =head1 COPYRIGHT -Copyright (C) 2000,2001,2002,2003,2004 Ken'ichi Fukamachi +Copyright (C) 2000,2001,2002,2003,2004,2006 Ken'ichi Fukamachi All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. |
