#!/usr/bin/perl -T

use strict;

use lib '/usr/local/share/perl/5.10.1';  # substituted at 'make' time

use File::Spec;

# Do 'use vars' instead of my() since CmdLearn looks for these and my()
# makes them non-exportable.  Doh.
use vars qw/ $PREFIX $SPAMD_PREFIX $DEF_RULES_DIR $LOCAL_RULES_DIR $PRODUCT_ROOT_D $QMAIL_DIR $ADMIN_DIR $SPAM_HOOK_BIN $SPAM_HOOK_PRIORITY %STD_LIBDIRS %PSA_LIBDIRS /;

BEGIN {
	$PREFIX = '/usr';  # substituted at 'make' time
	$SPAMD_PREFIX = '/sbin'; # substitiuted at 'make' time
	$PRODUCT_ROOT_D = '/opt/psa';  # substituted at 'make' time
	$DEF_RULES_DIR = '/usr/share/spamassassin';  # substituted at 'make' time
	$LOCAL_RULES_DIR = '/etc/mail/spamassassin';  # substituted at 'make' time
	$QMAIL_DIR = '/var/qmail';  # substituted at 'make' time
	$ADMIN_DIR = '/opt/psa' . '/admin/bin'; # substituted at 'make' time
	$SPAM_HOOK_BIN= '/opt/psa/handlers/hooks/' . 'spam';
	$SPAM_HOOK_PRIORITY= '10';

#TODO	$PID_DIR = '@@SPAMD_PIDFILES_DIR@@' # substituted at 'make' time

  # Locate locally installed SA libraries *without* using FindBin, which generates
  # warnings and causes more trouble than its worth.  We don't need to be too
  # smart about this BTW.
  my @bin = File::Spec->splitpath($0);
  my $bin = ($bin[0] ? File::Spec->catpath(@bin[0..1]) : $bin[1])  # /home/jm/foo -> /home/jm
            || File::Spec->curdir;                                 # foo          -> .

  # check to make sure it wasn't just installed in the normal way.
  # note that ./lib/Mail/SpamAssassin.pm takes precedence, for
  # building SpamAssassin on a machine where an old version is installed.
  if (-e $bin.'/lib/Mail/SpamAssassin.pm'
        || !-e '/usr/local/share/perl/5.10.1/Mail/SpamAssassin.pm')
  {
    # These are common paths where the SA libs might be found.
    foreach (qw(lib ../lib/site_perl
                ../lib/spamassassin ../share/spamassassin/lib))
    {
      my $dir = File::Spec->catdir($bin, split('/', $_));
      if(-f File::Spec->catfile($dir, "Mail", "SpamAssassin.pm")) {
        unshift(@INC, $dir); last;
      }
    }
  }

	use Config;
	my $new_prefix = $PRODUCT_ROOT_D;
	$new_prefix =~ s,([^/])$,$1/,;
	my $sys_prefix = (($^O eq 'freebsd') && ($] < 5.006)) ? "/usr/local" : $Config{'prefix'};
	my @lib_dir_list = ('INSTALLSITELIB', 'INSTALLSITEARCH', 'INSTALLARCHLIB', 'INSTALLPRIVLIB');

	foreach my $dir_name (@lib_dir_list) {
		$_ = $Config{lc($dir_name)};
		### define standard libdir
		$STD_LIBDIRS{$dir_name} = $_;
		### define psa libdir
		s,^$sys_prefix/?,$new_prefix,;
		$PSA_LIBDIRS{$dir_name} = $_;
	}
}

use lib $PSA_LIBDIRS{'INSTALLSITELIB'};
use lib $PSA_LIBDIRS{'INSTALLSITEARCH'};
use lib $PSA_LIBDIRS{'INSTALLARCHLIB'};
use lib $PSA_LIBDIRS{'INSTALLPRIVLIB'};

use lib $STD_LIBDIRS{'INSTALLSITELIB'};
use lib $STD_LIBDIRS{'INSTALLSITEARCH'};
use lib $STD_LIBDIRS{'INSTALLARCHLIB'};
use lib $STD_LIBDIRS{'INSTALLPRIVLIB'};


use Mail::SpamAssassin;
my $sa_version = Mail::SpamAssassin::Version;

my $CmdManage;
if ($sa_version =~ /^3\./) {
	$CmdManage = 'Mail::SpamAssassin::CmdManageSA3';
} else {
	print "Unsupported SpamAssassin version: $sa_version\n1\n";
	exit(1);
}

eval "require $CmdManage";
print STDERR "$@\n" if $@;
my $result = eval("$CmdManage->cmdline_run()");
print STDERR "$@\n" if $@;
if ($result == eval("$CmdManage->EX_STOPPED()")) { ## For "clever" PSA
	print STDERR "$@\n" if $@;
	print "is stopped\n0\n";
	exit(0);
} else {
	print STDERR "$@\n" if $@;
	exit($result);
}

# ---------------------------------------------------------------------------

=head1 NAME

spammng - manage SpamAssassin's daemon and configuration files.
Also, train Bayesian classifier base.

=head1 SYNOPSIS

B<spammng> [argument] [options]

=head1 OPTIONS

=over 4

=item B<-c|--enable-server-configs>

Enable server (or user) configs in filtering process
(only useful with B<-e|--enable-mailname> option or B<start> argument)

=item B<-C|--enable-user-configs>

Enable user configs in filtering process (only useful with B<start> argument)

=item B<-s|--sensitivity=I<number>>

Set the of hits required before a mail is considered as spam

=item B<-m|--modify-header=I<string>>

The subject lines of suspected spam will be tagged by this string

=item B<-M|--not-modify-header>

Do not tagging subject lines

=item B<-a|--add-to-whitelist=I<email_address_pattern>[, I<email_address_pattern> ...]>

Add an email pattern(s) to a whitelist

=item B<-A|--add-to-blacklist=I<email_address_pattern>[, I<email_address_pattern> ...]>

Add an email pattern(s) to a blacklist

=item B<-d|--del-from-whitelist=I<email_address_pattern>[, I<email_address_pattern> ...]>

Delete an email pattern(s) from a whitelist

=item B<-D|--del-from-blacklist=I<email_address_pattern>[, I<email_address_pattern> ...]>

Delete an email pattern(s) from a blacklist

=item B<-u|--add-to-unwhitelist=I<email_address_pattern>[, I<email_address_pattern> ...]>

Add an email pattern(s) to an unwhitelist

=item B<-U|--add-to-unblacklist=I<email_address_pattern>[, I<email_address_pattern> ...]>

Add an email pattern(s) to an unblacklist

=item B<-l|--del-from-unwhitelist=I<email_address_pattern>[, I<email_address_pattern> ...]>

Delete an email pattern(s) from an unwhitelist

=item B<-L|--del-from-unblacklist=I<email_address_pattern>[, I<email_address_pattern> ...]>

Delete an email pattern(s) from an unblacklist

=item B<-b|--bayes>

Train SpamAssassin's Bayesian classifier

=item B<-p|--spam=I<filename>[, I<filename> ...]>

Learn message(s) as spam (only useful with B<-b|--bayes> option)

=item B<-h|--ham=I<filename>[, I<filename> ...]>

Learn message(s) as ham (non-spam, only useful with B<-b|--bayes> option)

=item B<-f|--forget=I<filename>[, I<filename> ...]>

Forget message(s) (only useful with B<-b|--bayes> option)

=item B<-r|--clear>

Clear Bayesian database (only useful with B<-b|--bayes> option)

=item B<--enable-checking>

Enable spam filtering for specified mailname

=item B<--disable-checking>

Disable spam filtering for specified mailname

=item B<-e|--enable-mailname>

Add spam filtering for specified mailname

=item B<-E|--disable-mailname>

Delete spam filtering for specified mailname

=item B<-z|--action=I<save|delete|move>>

What to do if the mail is recognized as spam. "save" (by default) places marked 
spam mail in mailbox, "delete" removes spam mail without placing it in mailbox
and "move" mode places marked spam mail into "Spam" mailbox folder.

=item B<-n|--mailname=I<string>>

Use a specified mailname instead of server config files

=item B<-x|--list-mailbox>

Show list of incoming message(s) for specified mailname.
This is useful for Bayesian classifer training. List is in tab-delimited format
of three fields in each line: C<filename>, C<subject> and C<from>

=item B<-o|--output-charset=I<string>>

Specifies output charset for emails listing. Default is UTF8

=item B<-?|--help>

Show usage information

=back

=head1 ARGUMENTS

=over 4

=item B<start>

Start the SpamAssassin's daemon

=item B<stop>

Stop the SpamAssassin's daemon

=item B<restart>

Restart the SpamAssassin's daemon by sending to it an C<SIGHUP>.
It will cause to restart daemon and reread it's configuration files

=item B<status>

Check for the SpamAssassin's daemon running

=back

=head1 DESCRIPTION

The B<spammng> utility is supposed to manage the SpamAssassin's daemon and it's
configuration files. This utility can also be used to train the Bayesian classifier.
You can find additional information located on corresponding manual pages for 
utilities listed in L<SEE ALSO> tab.

=head1 SETTINGS' DETAILS AND USAGE POLICY

The B<-e|--enable-mailname> and B<-E|--disable-mailname> options will cause to
rewrite the mailname's virtual user C<.qmail> config file for enable or disable
the spam filtering. Otherwise, starting the SpamAssassin's daemon without
the B<-c|--enable-server-configs> option will going by another way - it will
start the F<spamd> daemon with C<-C> switch that will targets to a
I<empty> server-wide config patch. B<Note>: then unusing B<-c|--enable-server-configs>
be carefully - it will erases server-wide config file before starting the SpamAssassin's daemon.
Also note what the B<-e|--enable-mailname> must be used every time you creating
the mailbox in purpose of the server-wide configuration must affects on I<any>
of existing mailboxes.

=head1 EXIT CODES

Then the utility operation will be completed the notification will be shown and
program exits with one of following exit codes.

=over 4

=item B<EX_OK>           0

normal exit

=item B<EX_STOPPED>      1

daemon is not running (only used with B<status> argument)

=item B<EX_USAGE>       64

command line usage error

=item B<EX_DATAERR>     65

data format error       

=item B<EX_NOINPUT>     66

cannot open input

=item B<EX_NOUSER>      67

address unknown

=item B<EX_NOHOST>      68

host name unknown

=item B<EX_UNAVAILABLE> 69

service unavailable

=item B<EX_SOFTWARE>    70

internal software error

=item B<EX_OSERR>       71

system error (e.g., can't fork)

=item B<EX_OSFILE>      72

critical OS file missing

=item B<EX_CANTCREAT>   73

can't create (user) output file

=item B<EX_IOERR>       74

input/output error

=item B<EX_TEMPFAIL>    75

temp failure; user is invited to retry

=item B<EX_PROTOCOL>    76

remote error in protocol

=item B<EX_NOPERM>      77

permission denied

=item B<EX_CONFIG>      78

configuration error

=back

=head1 INSTALLATION

The B<spammng> command is part of the L<Mail::SpamAssassin> Perl module.
Install this as a normal Perl module, using C<perl -MCPAN -e shell>,
or by hand.

=head1 ENVIRONMENT

No environment variables, aside from those used by perl, are required to
be set.

=head1 SEE ALSO

=over 4

=item Perl module documentation

L<Mail::SpamAssassin>

=item Unix manpages

L<spamassassin(1)>
L<sa-learn(1)>
L<spamc(1)>
L<spamd(1)>

=item Online documentation

L<http://www.paulgraham.com/>, Paul Graham's "A Plan For Spam" paper

L<http://radio.weblogs.com/0101454/stories/2002/09/16/spamDetection.html>, Gary
Robinson's f(x) and combining algorithms, as used in SpamAssassin

L<http://www.bgl.nu/~glouis/bogofilter/test6000.html>, discussion of various
Bayes training regimes, including 'train on error' and unsupervised training

=back

=head1 AUTHOR

Parallels L<http://www.parallels.com>

=cut

