File: //usr/share/perl5/vendor_perl/Amavis/Out/SQL/Quarantine.pm
package Amavis::Out::SQL::Quarantine;
use strict;
use re 'taint';
use warnings;
use warnings FATAL => qw(utf8 void);
no warnings 'uninitialized';
# use warnings 'extra'; no warnings 'experimental::re_strict'; use re 'strict';
BEGIN {
require Exporter;
use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
$VERSION = '2.412';
@ISA = qw(Exporter);
@EXPORT = qw(&mail_via_sql);
import Amavis::Out::SQL::Connection ();
}
use subs @EXPORT;
use DBI qw(:sql_types);
use Amavis::Conf qw(:platform c cr ca $sql_quarantine_chunksize_max);
use Amavis::rfc2821_2822_Tools qw(qquote_rfc2821_local);
use Amavis::Timing qw(section_time);
use Amavis::Util qw(ll do_log snmp_count collect_equal_delivery_recips);
use Amavis::IO::SQL;
sub mail_via_sql {
my($conn_h,
$msginfo, $initial_submission, $dsn_per_recip_capable, $filter) = @_;
my(@snmp_vars) = !$initial_submission ?
('', 'Relay', 'ProtoSQL', 'ProtoSQLRelay')
: ('', 'Submit', 'ProtoSQL', 'ProtoSQLSubmit',
'Submit'.$initial_submission);
snmp_count('OutMsgs'.$_) for @snmp_vars;
my $logmsg =
sprintf("%s via SQL (%s): %s", ($initial_submission?'SEND':'FWD'),
$conn_h->dsn_current, $msginfo->sender_smtp);
my($per_recip_data_ref, $proto_sockname) =
collect_equal_delivery_recips($msginfo, $filter, qr/^sql:/i);
if (!$per_recip_data_ref || !@$per_recip_data_ref) {
do_log(5, "%s, nothing to do", $logmsg); return 1;
}
my $mail_id = $msginfo->mail_id;
defined $mail_id or die "mail_via_sql: mail_id still undefined";
$proto_sockname = $proto_sockname->[0] if ref $proto_sockname;
ll(1) && do_log(1, "delivering to %s, %s -> %s, mail_id %s",
$proto_sockname, $logmsg,
join(',', qquote_rfc2821_local(
map($_->recip_final_addr, @$per_recip_data_ref)) ),
$mail_id);
my $msg = $msginfo->mail_text; # a scalar reference, or a file handle
my $msg_str_ref = $msginfo->mail_text_str; # have an in-memory copy?
$msg = $msg_str_ref if ref $msg_str_ref;
my($err,$smtp_response);
eval {
my $sql_cl_r = cr('sql_clause');
$conn_h->begin_work; # SQL transaction starts
eval {
my $mp = Amavis::IO::SQL->new;
$mp->open($conn_h, $sql_cl_r->{'ins_quar'}, $msginfo->mail_id, 'w',
$msginfo->partition_tag, $sql_quarantine_chunksize_max,
$msginfo->rx_time)
or die "Can't open Amavis::IO::SQL object: $!";
my $hdr_edits = $msginfo->header_edits;
$hdr_edits = Amavis::Out::EditHeader->new if !$hdr_edits;
my($received_cnt,$file_position) =
$hdr_edits->write_header($msginfo,$mp,!$initial_submission);
if ($received_cnt > 100) { # loop detection required by RFC 5321 sect 6.2
die "Too many hops: $received_cnt 'Received:' header fields";
} elsif (!defined $msg) {
# empty mail
} elsif (ref $msg eq 'SCALAR') {
$mp->print(substr($$msg,$file_position))
or die "Can't write to SQL storage: $!";
} elsif ($msg->isa('MIME::Entity')) {
$msg->print_body($mp);
} else {
my($nbytes,$buff);
while (($nbytes = $msg->read($buff,32768)) > 0) {
$mp->print($buff) or die "Can't write to SQL storage: $!";
}
defined $nbytes or die "Error reading: $!";
}
$mp->close or die "Error closing Amavis::IO::SQL object: $!";
$conn_h->commit; 1;
} or do {
my $err = $@ ne '' ? $@ : "errno=$!"; chomp $err; my $msg = $err;
$msg = "writing mail text to SQL failed: $msg"; do_log(0,"%s",$msg);
if ($conn_h->in_transaction) {
eval {
$conn_h->rollback;
do_log(1,"mail_via_sql: rollback done"); 1;
} or do {
$@ = "errno=$!" if $@ eq ''; chomp $@;
do_log(1,"mail_via_sql: rollback %s", $@);
die $@ if $@ =~ /^timed out\b/; # resignal timeout
};
}
die $err if $err =~ /^timed out\b/; # resignal timeout
die $msg;
};
1;
} or do { $err = $@ ne '' ? $@ : "errno=$!" };
if ($err eq '') {
$smtp_response = "250 2.6.0 Ok, Stored to sql db as mail_id $mail_id";
snmp_count('OutMsgsDelivers');
my $size = $msginfo->msg_size;
snmp_count( ['OutMsgsSize'.$_, $size, 'C64'] ) for @snmp_vars;
} else {
chomp $err;
if ($err =~ /too many hops\b/i) {
$smtp_response = "554 5.4.6 Reject: $err";
snmp_count('OutMsgsRejects');
} else {
$smtp_response =
"451 4.5.0 Storing to sql db as mail_id $mail_id failed: $err";
snmp_count('OutMsgsAttemptFails');
}
die $err if $err =~ /^timed out\b/; # resignal timeout
}
$smtp_response .= ", id=" . $msginfo->log_id;
for my $r (@$per_recip_data_ref) {
next if $r->recip_done;
$r->recip_smtp_response($smtp_response); $r->recip_done(2);
if ($smtp_response =~ /^2/) {
my $mbxname = $mail_id;
my $p_tag = $msginfo->partition_tag;
$mbxname .= '[' . $p_tag . ']'
if defined($p_tag) && $p_tag ne '' && $p_tag ne '0';
$r->recip_mbxname($mbxname);
}
}
section_time('fwd-sql');
1;
}
1;