system: Linux mars.sprixweb.com 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64
Direktori : /usr/bin/ |
|
Current File : //usr/bin/amavisd-submit |
#!/usr/bin/perl -T
#------------------------------------------------------------------------------
# This is amavisd-submit, a simple demonstrational program, taking an email
# message on stdin and submiting it to amavisd daemon. It is functionally
# much like the old amavis.c helper program, except that it talks a new
# AM.PDP protocol with the amavisd daemon. See README.protocol for the
# description of AM.PDP protocol.
#
# Author: Mark Martinec <Mark.Martinec@ijs.si>
#
# Copyright (c) 2004,2010-2014, Mark Martinec
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of the Jozef Stefan Institute.
# (the above license is the 2-clause BSD license, also known as
# a "Simplified BSD License", and pertains to this program only)
#
# Patches and problem reports are welcome.
# The latest version of this program is available at:
# http://www.ijs.si/software/amavisd/
#------------------------------------------------------------------------------
# Usage:
# amavisd-submit sender recip1 recip2 recip3 ... <email.msg
# (should run under the same GID as amavisd, to make files accessible to it)
#
# To be placed in amavisd.conf:
# $interface_policy{'SOCK'} = 'AM.PDP';
# $policy_bank{'AM.PDP'} = { protocol=>'AM.PDP' };
# $unix_socketname = '/run/amavisd/amavisd.sock';
use warnings;
use warnings FATAL => 'utf8';
no warnings 'uninitialized';
use strict;
use re 'taint';
use IO::Socket;
use IO::File;
use File::Temp ();
use Time::HiRes ();
BEGIN {
use vars qw($VERSION); $VERSION = 2.101;
use vars qw($log_level $socketname $tempbase $io_socket_module_name);
### USER CONFIGURABLE:
$log_level = 0;
$tempbase = '/var/amavis/tmp'; # where to create a temp directory with a msg
$socketname = '/run/amavisd/amavisd.sock';
# $socketname = '127.0.0.1:9998';
# $socketname = '[::1]:9998';
### END OF USER CONFIGURABLE
# load a suitable sockets module
if ($socketname =~ m{^/}) {
require IO::Socket::UNIX;
$io_socket_module_name = 'IO::Socket::UNIX';
} elsif (eval { require IO::Socket::IP }) {
# prefer using module IO::Socket::IP if available,
$io_socket_module_name = 'IO::Socket::IP';
} elsif (eval { require IO::Socket::INET6 }) {
# otherwise fall back to IO::Socket::INET6
$io_socket_module_name = 'IO::Socket::INET6';
} elsif (eval { require IO::Socket::INET }) {
$io_socket_module_name = 'IO::Socket::INET';
}
$io_socket_module_name or die "No suitable socket module available";
}
sub sanitize_str {
my($str, $keep_eol) = @_;
my(%map) = ("\r" => '\\r', "\n" => '\\n', "\f" => '\\f', "\t" => '\\t',
"\b" => '\\b', "\e" => '\\e', "\\" => '\\\\');
if ($keep_eol) {
$str =~ s/([^\012\040-\133\135-\176])/ # and \240-\376 ?
exists($map{$1}) ? $map{$1} :
sprintf(ord($1)>255 ? '\\x{%04x}' : '\\%03o', ord($1))/eg;
} else {
$str =~ s/([^\040-\133\135-\176])/ # and \240-\376 ?
exists($map{$1}) ? $map{$1} :
sprintf(ord($1)>255 ? '\\x{%04x}' : '\\%03o', ord($1))/eg;
}
$str;
}
sub ll($) {
my($level) = @_;
$level <= $log_level;
}
sub do_log($$;@) {
my($level, $errmsg, @args) = @_;
$errmsg = sprintf($errmsg,@args) if @args;
print STDERR sanitize_str($errmsg),"\n" if $level <= $log_level;
}
sub proto_decode($) {
my($str) = @_;
$str =~ s/%([0-9a-fA-F]{2})/pack("C",hex($1))/eg;
$str;
}
sub proto_encode($@) {
my($attribute_name,@strings) = @_; local($1);
$attribute_name =~ # encode all but alfanumerics, '_' and '-'
s/([^0-9a-zA-Z_-])/sprintf("%%%02x",ord($1))/eg;
for (@strings) { # encode % and nonprintables
s/([^\041-\044\046-\176])/sprintf("%%%02x",ord($1))/eg;
}
$attribute_name . '=' . join(' ',@strings);
}
sub ask_amavisd($$) {
my($sock,$query_ref) = @_;
my(@encoded_query) =
map { /^([^=]+)=(.*)\z/s; proto_encode($1,$2) } @$query_ref;
do_log(2, "> %s", $_) for @encoded_query;
$sock->print( map($_."\015\012", (@encoded_query,'')) )
or die "Can't write response to socket: $!";
$sock->flush or die "Can't flush on socket: $!";
my(%attr);
local($/) = "\015\012"; # set line terminator to CRLF
# must not use \r and \n, which may not be \015 and \012 on certain platforms
do_log(2, "waiting for response");
while(<$sock>) {
last if /^\015\012\z/; # end of response
if (/^ ([^=\000\012]*?) (=|:[ \t]*) ([^\012]*?) \015\012 \z/xsi) {
my $attr_name = proto_decode($1);
my $attr_val = proto_decode($3);
if (!exists $attr{$attr_name}) { $attr{$attr_name} = [] }
push(@{$attr{$attr_name}}, $attr_val);
}
}
if (!defined($_) && $! != 0) { die "read from socket failed: $!" }
\%attr;
}
sub usage(;$) {
my($msg) = @_;
print STDERR $msg,"\n\n" if $msg ne '';
my $prog = $0; $prog =~ s{^.*/(?=[^/]+\z)}{};
print STDERR "$prog version $VERSION\n";
die "Usage: \$ $prog sender recip1 recip2 ... < email.msg\n";
}
# Main program starts here
$SIG{INT} = sub { die "\n" }; # do the END code block when interrupted
$SIG{TERM} = sub { die "\n" }; # do the END code block when killed
umask(0027); # set our preferred umask
@ARGV >= 1 or usage("Not enough arguments");
my($sock, %sock_args);
if ($io_socket_module_name eq 'IO::Socket::UNIX') {
%sock_args = (Type => &SOCK_STREAM, Peer => $socketname);
} else {
%sock_args = (Type => &SOCK_STREAM, PeerAddr => $socketname);
}
do_log(2, "Connecting to %s using a module %s",
$socketname, $io_socket_module_name);
$sock = $io_socket_module_name->new(%sock_args)
or die "Can't connect to a $io_socket_module_name socket $socketname: $!\n";
my $tempdir = File::Temp::tempdir('amavis-XXXXXXXXXX', DIR => $tempbase);
defined $tempdir && $tempdir ne ''
or die "Can't create a temporary directory: $!";
chmod(0750, $tempdir)
or die "Can't change protection on directory $tempdir: $!";
my $fname = "$tempdir/email.txt";
# copy message from stdin to a file email.txt in the temporary directory
my $fh = IO::File->new;
$fh->open($fname, O_CREAT|O_EXCL|O_RDWR, 0640)
or die "Can't create file $fname: $!";
my($nbytes,$buff);
while (($nbytes=read(STDIN,$buff,32768)) > 0) {
$fh->print($buff) or die "Error writing to $fname: $!";
}
defined $nbytes or die "Error reading mail file: $!";
$fh->close or die "Error closing $fname: $!";
close(STDIN) or die "Error closing STDIN: $!";
my(@query) = (
'request=AM.PDP',
"mail_file=$fname",
"tempdir=$tempdir",
'tempdir_removed_by=server',
map("sender=<$_>", shift(@ARGV)),
map("recipient=<$_>", @ARGV),
# 'delivery_care_of=server',
# 'protocol_name=ESMTP',
# 'helo_name=b.example.com',
# 'client_address=::1',
);
my $attr_ref = ask_amavisd($sock,\@query);
if (ll(2)) {
for my $attr_name (keys %$attr_ref) {
for my $attr_val (@{$attr_ref->{$attr_name}}) {
do_log(2, "< %s=%s", $attr_name,$attr_val);
}
}
}
my($setreply,$exit_code);
$setreply = $attr_ref->{'setreply'}->[0] if $attr_ref->{'setreply'};
$exit_code = $attr_ref->{'exit_code'}->[0] if $attr_ref->{'exit_code'};
if (defined $setreply && $setreply =~ /^2\d\d/) { # all ok
do_log(1, "%s", $setreply);
} elsif (!defined($setreply)) {
do_log(0, "Error, missing 'setreply' attribute");
} else {
do_log(0, "%s", $setreply);
}
# may do another request here if needed ...
$sock->close or die "Error closing socket: $!";
$exit_code = 0 if $exit_code==99; # same thing in this case, both is ok
exit 0+$exit_code;
END {
# remove a temporary file and directory if necessary
if (defined $fname && -f $fname) {
unlink $fname or warn "Error deleting file $fname: $!";
}
if (defined $tempdir && -d $tempdir) {
rmdir $tempdir or warn "Error deleting temporary directory $tempdir: $!";
}
}