--- amavisd.conf Fri Apr 2 21:33:43 2004 +++ amavisd.conf-qmqpqq.patch Tue May 4 11:10:34 2004 @@ -51,7 +51,7 @@ # $MYHOME serves as a quick default for some other configuration settings. # More refined control is available with each individual setting further down. # $MYHOME is not used directly by the program. No trailing slash! -#$MYHOME = '/var/lib/amavis'; # (default is '/var/amavis') +$MYHOME = '/var/amavis'; # (default is '/var/amavis') # $mydomain serves as a quick default for some other configuration settings. # More refined control is available with each individual setting further down. @@ -63,7 +63,7 @@ # Set the user and group to which the daemon will change if started as root # (otherwise just keeps the UID unchanged, and these settings have no effect): $daemon_user = 'vscan'; # (no default; customary: vscan or amavis) -$daemon_group = 'sweep'; # (no default; customary: vscan or amavis) +$daemon_group = 'vscan'; # (no default; customary: vscan or amavis) # Runtime working directory (cwd), and a place where # temporary directories for unpacking mail are created. @@ -216,12 +216,25 @@ #$gets_addr_in_quoted_form = 0; # Bob "Funny" Dude@example.com - # SMTP SERVER (INPUT) PROTOCOL SETTINGS (e.g. with Postfix, Exim v4, ...) -# (used when MTA is configured to pass mail to amavisd via SMTP or LMTP) -$inet_socket_port = 10024; # accept SMTP on this local TCP port +# (used when MTA is configured to pass mail to amavisd via SMTP, LMTP or QMQPqq) +$inet_socket_port = 10024; # accept on this local TCP port # (default is undef, i.e. disabled) # multiple ports may be provided: $inet_socket_port = [10024, 10026, 10028]; +# multiple protocols may be provided with multiple ports +# (according to the protocol port settings below): +# $inet_socket_port = [10024, 10628]; + +# SMTP or LMTP port +$inet_smtp_port = 10024; # accept on this local TCP port + # (default is undef, i.e. disabled) +# multiple ports may be provided: $inet_smtp_port = [10024, 10025]; + +# QMQPqq port (can be used only with qmail) +$inet_qmqpqq_port = 10628; # accept on this local TCP port + # (default is undef, i.e. disabled) +# multiple ports may be provided: $inet_qmqpqq_port = [10628, 10629]; + # SMTP SERVER (INPUT) access control # - do not allow free access to the amavisd SMTP port !!! @@ -276,7 +289,7 @@ # 3: server, client # 4: decompose parts # 5: more debug details -$log_level = 2; # (defaults to 0) +$log_level = 0; # (defaults to 0) # Customizable template for the most interesting log file entry (e.g. with # $log_level=0) (take care to properly quote Perl special characters like '\') --- amavisd Fri Apr 2 21:33:50 2004 +++ amavisd-new-qmqpqq.patch Tue May 4 11:10:14 2004 @@ -83,6 +83,7 @@ # Amavis::Lookup::LDAP # Amavis::In::AMCL # Amavis::In::SMTP +# Amavis::In::QMQPqq # Amavis::AV # Amavis::SpamControl #------------------------------------------------------------------------------ @@ -158,6 +159,7 @@ $warnvirusrecip $warnbannedrecip $log_templ $unix_socketname $inet_socket_port $inet_socket_bind @inet_acl + $inet_smtp_port $inet_qmqpqq_port $myhostname $localhost_name $insert_received_line $mta_in_type $gets_addr_in_quoted_form @@ -800,7 +802,8 @@ @EXPORT_OK = qw(&safe_encode &am_id &do_log &debug_oneshot &retcode &prolong_timer &sanitize_str &min &max &strip_tempdir &rmdir_recursively &rmdir_flat - &read_text &read_l10n_templates &read_hash &run_command); + &read_text &read_l10n_templates &read_hash &run_command + &if_exists_in_array); } use subs @EXPORT_OK; use POSIX qw(WEXITSTATUS WIFEXITED WTERMSIG WIFSIGNALED); @@ -1139,6 +1142,19 @@ $proc_fh; # return subprocess file handle } +# returns undef if value does not exist in array or position of the first occurence in the array +sub if_exists_in_array($$) { +my($value,$ref) = @_; +(ref $ref eq 'ARRAY') || return(undef); + +for(my $i=0;$i<@{$ref};$i++) { + if($value eq $ref->[$i]) { + return($i); + } + } +return(undef); +} + 1; # @@ -1218,19 +1234,31 @@ sub received_line($$$$) { my($conn, $msginfo, $id, $folded) = @_; - my($smtp_proto,$recips) = ($conn->smtp_proto, $msginfo->recips); + my($proto,$helo); + + if(defined $conn->smtp_proto) { + $proto = $conn->smtp_proto; + $helo = $conn->smtp_helo; + } + elsif(defined $conn->qmqp_proto) { + $proto = $conn->qmqp_proto; + $helo = $conn->qmqp_helo; + } + my($client_ip) = $conn->client_ip; if ($client_ip =~ /:/ && $client_ip !~ /^IPv6:/i) { $client_ip = 'IPv6:' . $client_ip; } + + my($recips) = $msginfo->recips; my($s) = sprintf("from %s%s\n by %s%s (amavisd-new, %s)", - ($conn->smtp_helo eq '' ? 'unknown' : $conn->smtp_helo), + ($helo eq '' ? 'unknown' : $helo), ($client_ip eq '' ? '' : " ([$client_ip])"), $localhost_name, ($conn->socket_ip eq '' ? '' : sprintf(" (%s [%s])", $myhostname, $conn->socket_ip)), ($conn->socket_port eq '' ? 'unix socket' : "port ".$conn->socket_port)); - $s .= "\n with $smtp_proto" if $smtp_proto =~ /^(ES|S|L)MTP$/i; + $s .= "\n with $proto" if defined($proto); $s .= "\n id $id" if $id ne ''; # do not disclose if many $s .= "\n for " . qquote_rfc2821_local(@$recips) if @$recips==1; @@ -2172,6 +2200,10 @@ { my($self)=shift; !@_ ? $self->{smtp_proto}: ($self->{smtp_proto}=shift) } sub smtp_helo # (E)SMTP HELO/EHLO parameter { my($self)=shift; !@_ ? $self->{smtp_helo} : ($self->{smtp_helo}=shift) } +sub qmqp_proto # QMQP/QMQPqq + { my($self)=shift; !@_ ? $self->{qmqp_proto}: ($self->{qmqp_proto}=shift) } +sub qmqp_helo # for future use maybe + { my($self)=shift; !@_ ? $self->{qmqp_helo} : ($self->{qmqp_helo}=shift) } 1; @@ -4886,7 +4918,7 @@ BEGIN { import Amavis::Conf qw(:platform :confvars :notifyconf :sa); import Amavis::Util qw(do_log debug_oneshot am_id prolong_timer - min max); + min max if_exists_in_array); import Amavis::Timing qw(section_time); import Amavis::Log; import Amavis::Lookup qw(lookup lookup_ip_acl); @@ -4914,6 +4946,7 @@ use vars qw($extra_code_sql $extra_code_ldap $extra_code_in_amcl $extra_code_in_smtp + $extra_code_in_qmqpqq $extra_code_antivirus $extra_code_antispam); use vars qw($spam_level $spam_status $spam_report); @@ -4950,7 +4983,11 @@ use vars qw($av_output @virusname @detecting_scanners @banned_filename @bad_headers); -use vars qw($amcl_in_obj $smtp_in_obj); # Amavis::In::AMCL and In::SMTP objects +use vars qw($amcl_in_obj); # Amavis::In::AMCL object +use vars qw($smtp_in_obj); # Amavis::In::SMTP object +use vars qw($qmqpqq_in_obj); # Amavis::In::QMQPqq object +use vars qw($inet_smtp_port $inet_qmqpqq_port); + use vars qw($sql_policy $sql_wblist); # Amavis::Lookup::SQL objects ### Net::Server hook @@ -5126,17 +5163,24 @@ $conn->socket_ip($prop->{sockaddr}); $conn->socket_port($prop->{sockport}); $conn->client_ip($prop->{peeraddr}); - if (!$extra_code_in_smtp) { + + if (!$extra_code_in_smtp && !$extra_code_in_qmqpqq) { die ("incomming TCP connection, but dynamic code ". - "to handle SMTP or LMTP not loaded"); - } else { + "to handle SMTP, LMTP or QMQPqq not loaded"); + } elsif(defined if_exists_in_array($prop->{sockport},$inet_smtp_port)) { my($lmtp); # false by default, start as a SMTP server # $lmtp = $prop->{sockport} != 25 && # $prop->{sockport} != $inet_socket_port; $smtp_in_obj = Amavis::In::SMTP->new if !$smtp_in_obj; - $smtp_in_obj->process_smtp_request( - $sock, $lmtp, $conn, \&check_mail); + $smtp_in_obj->process_smtp_request($sock, $lmtp, $conn, \&check_mail); + } elsif(defined if_exists_in_array($prop->{sockport},$inet_qmqpqq_port)) { + $qmqpqq_in_obj = Amavis::In::QMQPqq->new if !$qmqpqq_in_obj; + $qmqpqq_in_obj->process_qmqpqq_request($sock,$conn,\&check_mail); + } else { # better to be safe than sorry + die ("dynamic code to handle incomming TCP connection on port '" . + "$prop->{sockport}' is not loaded or does not exist (yet)"); } + } else { die ("unsupported protocol: " . $sock->NS_proto); } @@ -5186,13 +5230,15 @@ my($self) = shift; local $SIG{CHLD} = 'DEFAULT'; # do_log(0, "Amavis::In::SMTP::DESTROY will be called from 'child_finish_hook'"); - $smtp_in_obj = undef; # calls Amavis::In::SMTP::DESTROY - $amcl_in_obj = undef; # (currently does nothing for Amavis::In::AMCL) + $smtp_in_obj = undef; # calls Amavis::In::SMTP::DESTROY + $qmqpqq_in_obj = undef; # calls Amavis::In::QMQPqq::DESTROY + $amcl_in_obj = undef; # (currently does nothing for Amavis::In::AMCL) } sub END { # runs before exiting the module # do_log(0, "Amavis::In::SMTP::DESTROY will be called from 'END'"); $smtp_in_obj = undef; # calls Amavis::In::SMTP::DESTROY + $qmqpqq_in_obj = undef; # calls Amavis::In::QMQPqq::DESTROY $amcl_in_obj = undef; # (currently does nothing for Amavis::In::AMCL) } @@ -6434,7 +6480,7 @@ do{ local($/) = "__DATA__\n"; # set line terminator to this string map { chomp($_ = ) } ($extra_code_sql, $extra_code_ldap, - $extra_code_in_amcl, $extra_code_in_smtp, + $extra_code_in_amcl, $extra_code_in_smtp, $extra_code_in_qmqpqq, $extra_code_antivirus, $extra_code_antispam, $log_templ, $notify_sender_templ, @@ -6461,7 +6507,7 @@ my($amavisd_path) = find_program_path($0, [split(/:/, $path, -1)], 0); $amavisd_path = $1 if $amavisd_path=~m{^([A-Za-z0-9/._=+-]+)$(?!\n)}; # untaint -my($config_file) = '/etc/amavisd.conf'; # default location of config file +my($config_file) = '/usr/local/etc/amavisd.conf'; # default location of config file if (@ARGV >= 2 && $ARGV[0] eq '-c') { # override by command line option -c shift @ARGV; $config_file = shift @ARGV; $config_file = $1 if $config_file=~m{^([A-Za-z0-9/._=+-]+)$(?!\n)};# untaint @@ -6488,12 +6534,90 @@ eval $extra_code_in_amcl or die "Problem in the In::AMCL code: $@"; $extra_code_in_amcl = 1; # release memory occupied by the source code } -if ($inet_socket_port eq '' || ref $inet_socket_port && !@$inet_socket_port) { - $extra_code_in_smtp = undef; -} else { - eval $extra_code_in_smtp or die "Problem in the In::SMTP code: $@"; - $extra_code_in_smtp = 1; # release memory occupied by the source code -} + +# make it a reference +$inet_socket_port = ($inet_socket_port eq ''? + undef : (ref $inet_socket_port? + (@$inet_socket_port ? + $inet_socket_port : undef) : [$inet_socket_port])); + +if(!ref $inet_socket_port) { + $extra_code_in_smtp = undef; + $extra_code_in_qmqpqq = undef; + } +else { + # make it a reference + $inet_smtp_port = ($inet_smtp_port eq ''? + undef : (ref $inet_smtp_port? + (@$inet_smtp_port ? + [@$inet_smtp_port] : undef) : [$inet_smtp_port])); + # make it a reference + $inet_qmqpqq_port = ($inet_qmqpqq_port eq ''? + undef : (ref $inet_qmqpqq_port ? + (@$inet_qmqpqq_port ? + [@$inet_qmqpqq_port] : undef) : [$inet_qmqpqq_port])); + + # check whether these two does not overlay + if(ref $inet_smtp_port && ref $inet_qmqpqq_port) { + foreach my $port (@$inet_smtp_port) { + (!defined if_exists_in_array($port,$inet_qmqpqq_port)) || + die("QMQPqq port '$port' is already assigned to SMTP/LMTP"); + } + } + + # check whether every $inet_socket_port is assigned some service + foreach my $port (@$inet_socket_port) { + if(ref $inet_smtp_port && defined if_exists_in_array($port,$inet_smtp_port)) { + next; + } + if(ref $inet_qmqpqq_port && defined if_exists_in_array($port,$inet_qmqpqq_port)) { + next; + } + die("Port '$port' is not assigned any service"); + } + + if(ref $inet_smtp_port) { + # check whether $inet_smtp_port is in $inet_socket_port + my $if_any = undef; + foreach my $port (@$inet_smtp_port) { + if(defined if_exists_in_array($port,$inet_socket_port)) { + $if_any = 1; + last; + } + } + if(!$if_any) { + $extra_code_in_smtp = undef; # feature not a bug + } + else { + eval $extra_code_in_smtp || die("Problem in the In::SMTP code: $@"); + $extra_code_in_smtp = 1; # release memory occupied by the source code + } + } + else { + $extra_code_in_smtp = undef; + } + + if(ref $inet_qmqpqq_port) { + # check whether $inet_qmqpqq_port is in $inet_socket_port + my $if_any = undef; + foreach my $port (@$inet_qmqpqq_port) { + if(defined if_exists_in_array($port,$inet_socket_port)) { + $if_any = 1; + last; + } + } + if(!$if_any) { + $extra_code_in_qmqpqq = undef; # feature not a bug + } + else { + eval $extra_code_in_qmqpqq || die("Problem in the In::QMQPqq code: $@"); + $extra_code_in_qmqpqq = 1; # release memory occupied by the source code + } + } + else { + $extra_code_in_qmqpqq = undef; + } + } if (!@av_scanners && !@av_scanners_backup) { $extra_code_antivirus = undef; @@ -6584,6 +6708,7 @@ do_log(1, "Lookup::LDAP code ".($extra_code_ldap ?'':" NOT")." loaded"); do_log(1, "AMCL-in protocol code ".($extra_code_in_amcl?'':" NOT")." loaded"); do_log(1, "SMTP-in protocol code ".($extra_code_in_smtp?'':" NOT")." loaded"); +do_log(1, "QMQPqq-in protocol code ".($extra_code_in_qmqpqq?'':" NOT")." loaded"); do_log(1, "ANTI-VIRUS code ".($extra_code_antivirus?'':" NOT")." loaded"); do_log(1, "ANTI-SPAM code ".($extra_code_antispam?'':" NOT")." loaded"); @@ -7891,6 +8016,331 @@ print map($_."\015\012", @{$self->{smtp_outbuf}}); @{$self->{smtp_outbuf}} = (); } +} + +1; + +__DATA__ +# +package Amavis::In::QMQPqq; +use strict; + +BEGIN { + use Exporter (); + use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); + $VERSION = '1.15'; + @ISA = qw(Exporter); +} +use POSIX qw(strftime); +use Errno qw(ENOENT); +use Time::HiRes qw(time); + +BEGIN { + import Amavis::Conf qw(:platform :confvars); + import Amavis::Util qw(do_log am_id prolong_timer debug_oneshot + sanitize_str strip_tempdir rmdir_recursively); + import Amavis::Lookup qw(lookup); + import Amavis::Timing qw(section_time); + import Amavis::rfc2821_2822_Tools; + import Amavis::In::Message; + import Amavis::In::Connection; +} + +sub new($) { + my($class) = @_; + my($self) = bless {}, $class; + $self->{fh_pers} = undef; # persistent file handle for email.txt + $self->{tempdir_pers} = undef; # temporary directory for check_mail + $self->{preserve} = undef; # don't delete tempdir on exit + $self->{tempdir_empty} = 1; # anything of interest in tempdir? + $self->{bytesleft} = undef; # bytes left for whole package + $self->{len} = undef; # set by getlen() method + $self->{sock} = undef; # connected socket + $self->{proto} = undef; # protocol + $self->{session_closed_normally} = undef; # closed properly? (waited for K/Z/D) + $self; +} + +sub preserve_evidence # try to preserve temporary files etc in case of trouble + { my($self)=shift; !@_ ? $self->{preserve} : ($self->{preserve}=shift) } + +sub DESTROY { + my($self) = shift; +# do_log(0, "Amavis::In::QMQPqq::DESTROY called"); + $self->{fh_pers}->close + or die "Can't close temp file: $!" if $self->{fh_pers}; + my($errn) = $self->{tempdir_pers} eq '' ? ENOENT + : (stat($self->{tempdir_pers}) ? 0 : 0+$!); + if (defined $self->{tempdir_pers} && $errn != ENOENT) { + # this will not be included in the TIMING report, + # but it only occurs infrequently and doesn't take that long + if ($self->preserve_evidence && !$self->{tempdir_empty}) { + do_log(0, "tempdir is to be PRESERVED: ".$self->{tempdir_pers}); + } else { + do_log(2, "tempdir being removed: ".$self->{tempdir_pers}); + rmdir_recursively($self->{tempdir_pers}); + } + } + if (! $self->{session_closed_normally}) { + $self->qmqpqq_resp("Z","Service shutting down, closing channel"); + } +} + +sub prepare_tempdir($) { + my($self) = @_; + if (! defined $self->{tempdir_pers} ) { + # invent a name for a temporary directory for this child, and create it + my($now_iso8601) = strftime("%Y%m%dT%H%M%S", localtime); + $self->{tempdir_pers} = sprintf("%s/amavis-%s-%05d", + $TEMPBASE, $now_iso8601, $$); + } + my($errn) = stat($self->{tempdir_pers}) ? 0 : 0+$!; + if ($errn == ENOENT || ! -d _) { + mkdir($self->{tempdir_pers}, 0750) + or die "Can't create directory $self->{tempdir_pers}: $!"; + $self->{tempdir_empty} = 1; + section_time('mkdir tempdir'); + } + # prepare temporary file for writing (and reading later) + my($fname) = $self->{tempdir_pers} . "/email.txt"; + my($errn) = stat($fname) ? 0 : 0+$!; + if ($self->{fh_pers} && !$errn && -f _) { + $self->{fh_pers}->seek(0,0) or die "Can't rewind mail file: $!"; + $self->{fh_pers}->truncate(0) or die "Can't truncate mail file: $!"; + } else { + $self->{fh_pers} = IO::File->new($fname, 'w+', 0640) + or die "Can't create file $fname: $!"; + section_time('create email.txt'); + } +} + + +# get byte, die if no bytes left +sub getbyte($) { +my($self) = shift; +if(!$self->{bytesleft}--) { + die("No bytes left"); + } +if(defined($_ = $self->{sock}->getc)) { + return($_); + } +die("EOF on socket"); +} + +sub getlen($) { +my($self) = shift; +my($ch,$len); + +for(;;) { + $ch = $self->getbyte; + if($ch eq ':') { + return($self->{len} = $len); + } + if($ch !~ /^\d$/) { + die("Char '$ch' is not a number while determining length"); + } + $len .= $ch; + } +} + +sub getcomma($) { +my($self) = shift; +if($self->getbyte ne ',') { + die("Comma expected, found '$_'"); + } +} + +sub getnetstring($$) { +my($self) = shift; +($self->{sock}->read($_[0],$self->getlen) == $self->{len}) || + die("EOF on socket"); +$self->{bytesleft} -= $self->{len}; +$self->getcomma; +} + + +# Accept a QMQPqq connect +# and call content checking for the message received +# +sub process_qmqpqq_request($$$$) { +my($self,$sock,$conn,$check_mail) = @_; +# $sock: connected socket from Net::Server +# $conn: information about client connection +# $check_mail: subroutine ref to be called with file handle + +$self->{proto} = "QMQPqq"; +$self->{sock} = $sock; # store $sock info for getbyte() method +$self->{bytesleft} = 20; # initial bytesleft value, there should + # NEVER EVER be longer email than 10^20 (approximately) + # bytes but increase if needed ;) +$self->{len} = undef; + +my($msginfo); + +my($sender,@recips); + +my($len); + +$conn->qmqp_proto("QMQPqq"); +$conn->qmqp_helo("QMQPqq client"); # for future possible use +eval { + # get length of whole package + $self->{bytesleft} = $self->getlen; + + # get length of 'email' + $len = $self->getlen; + section_time('initial length determination'); + + am_id(sprintf("%05d-%02d",$$,$Amavis::child_invocation_count)); + + # prepare tempdir + $self->prepare_tempdir; + $msginfo = Amavis::In::Message->new; + $msginfo->rx_time(time); + + # get 'email' + $self->{tempdir_empty} = 0; + my $size = 16384; + while(($len > 0) && ($sock->read($_,($len >= $size ? $size : $size = $len)) == $size)) { + (print {$self->{fh_pers}} $_) || + die("Can't write to mail file: $!"); + $len -= $size; + } + if($len > 0) { + die("EOF on socket"); + } + $self->{fh_pers}->flush || die("Can't flush mail file: $!"); + $self->{fh_pers}->seek(0,1) || die("Can't seek on file: $!"); + $self->{bytesleft} -= $self->{len}; + section_time('email receiving'); + # comma has to follow + $self->getcomma; + + # get sender + $self->getnetstring($sender); + section_time('sender receiving'); + + # get recips + my $i = 0; + while($self->{bytesleft}) { + $self->getnetstring($recips[$i++]); + } + section_time('recips receiving'); + + # final comma has to follow + $self->{bytesleft} = 1; + $self->getcomma; + + $msginfo->sender($sender); + $msginfo->recips(\@recips); + + do_log(1, sprintf("%s:%s:%s %s: <%s> -> %s Received: %s", + $self->{proto},$conn->socket_ip eq $inet_socket_bind ? + '' : '['.$conn->socket_ip.']', + $conn->socket_port, $self->{tempdir_pers}, + $sender, join(',', map{"<$_>"}@recips), + join(' ', + ($msginfo->msg_size eq '' ? () + : 'SIZE='.$msginfo->msg_size), + ($msginfo->body_type eq '' ? () + : 'BODY='.$msginfo->body_type), + received_line($conn,$msginfo,am_id(),0) ) + )); + + $msginfo->mail_text($self->{fh_pers}); + my($smtp_resp,$exit_code,$preserve_evidence) = + &$check_mail($conn,$msginfo,0,$self->{tempdir_pers}); + + if ($preserve_evidence) { + $self->preserve_evidence(1); + } + if ($smtp_resp !~ /^4/ && + grep { !$_->recip_done } @{$msginfo->per_recip_data}) { + die("TROUBLE/MISCONFIG: not all recipients done, ". + "\$forward_method is \"$forward_method\""); + } + + # all ok + if($smtp_resp =~ /^2/) { + $self->qmqpqq_resp("K",$smtp_resp); + } + # permanent reject + elsif($smtp_resp =~ /^5/) { + $self->qmqpqq_resp("D",$smtp_resp); + } + # temporary reject (or other error if !~ /^4/) + else { + $self->qmqpqq_resp("Z",$smtp_resp); + } + }; + +alarm(0); do_log(5,"timer stopped after QMQPqq eval"); + +if($@ ne '') { + chomp($@); + + do_log(0,"QMQPqq: NOTICE: $@"); + $self->qmqpqq_resp("Z","Service shutting down, $@"); + } + +if ($self->preserve_evidence && !$self->{tempdir_empty}) { + # keep evidence in case of trouble + do_log(0,"PRESERVING EVIDENCE in ".$self->{tempdir_pers}); + $self->{fh_pers}->close or die "Can't close mail file: $!"; + $self->{fh_pers} = undef; $self->{tempdir_pers} = undef; + $self->{tempdir_empty} = 1; + } + +# cleanup, but leave directory (and file handle +# if possible) for reuse +if ($self->{fh_pers} && !$can_truncate) { + # truncate is not standard across all Unix variants, + # it is not Posix, but is XPG4-UNIX. + # So if we can't truncate a file and leave it open, + # we have to create it anew later, at some cost. + # + $self->{fh_pers}->close or die "Can't close mail file: $!"; + $self->{fh_pers} = undef; + unlink($self->{tempdir_pers}."/email.txt") + or die "Can't delete file ". + $self->{tempdir_pers}."/email.txt: $!"; + section_time('delete email.txt'); + } + +if (defined $self->{tempdir_pers}) { # prepare for the next one + strip_tempdir($self->{tempdir_pers}); + $self->{tempdir_empty} = 1; + } + +$self->preserve_evidence(0); # reset +# report elapsed times by section for each transaction +do_log(2, Amavis::Timing::report()); + +$self->{session_closed_normally} = 1; +# closes connection after child_finish_hook +} + +# sends a QMQPqq response consisting of K/D/Z code and an optional message; +# slow down evil clients by delaying response on permanent errors +sub qmqpqq_resp($$$;$$) { +my($self,$code,$resp,$penalize,$line) = @_; +if($code !~ /^(K|Z|D)$/) { + die("Internal error(2): bad QMQPqq response code: '$code'"); + } +if($penalize) { + do_log(0,"QMQPqq: $resp; PENALIZE: $line"); + sleep 5; + section_time('QMQPqq penalty wait'); + } +my($taint) = substr($resp,0,0); +$resp = sanitize_str($resp,1); +do_log(4,"QMQPqq> $resp"); +print($self->netstring($code . $resp)); +} + +sub netstring($$) { +my($self,$string) = @_; +return(sprintf("%d:%s,",length($string),$string)); } 1;