]> git.proxmox.com Git - proxmox-spamassassin.git/blame - upstream/spamassassin.raw
buildsys: track debug package
[proxmox-spamassassin.git] / upstream / spamassassin.raw
CommitLineData
37ef5775
SI
1#!/usr/bin/perl -w
2
3# <@LICENSE>
4# Licensed to the Apache Software Foundation (ASF) under one or more
5# contributor license agreements. See the NOTICE file distributed with
6# this work for additional information regarding copyright ownership.
7# The ASF licenses this file to you under the Apache License, Version 2.0
8# (the "License"); you may not use this file except in compliance with
9# the License. You may obtain a copy of the License at:
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18# </@LICENSE>
19
20use strict;
21use warnings;
22use re 'taint';
23
24use File::Spec;
25
26my $PREFIX = '@@PREFIX@@'; # substituted at 'make' time
27my $DEF_RULES_DIR = '@@DEF_RULES_DIR@@'; # substituted at 'make' time
28my $LOCAL_RULES_DIR = '@@LOCAL_RULES_DIR@@'; # substituted at 'make' time
29my $LOCAL_STATE_DIR = '@@LOCAL_STATE_DIR@@'; # substituted at 'make' time
30
31use lib '@@INSTALLSITELIB@@'; # substituted at 'make' time
32
33BEGIN {
34 # Locate locally installed SA libraries *without* using FindBin, which
35 # generates warnings and causes more trouble than its worth. We don't
36 # need to be too smart about this BTW.
37 my @bin = File::Spec->splitpath($0);
38 my $bin = (
39 $bin[0]
40 ? File::Spec->catpath( @bin[ 0 .. 1 ], '' )
41 : $bin[1]
42 ) # /home/jm/foo -> /home/jm
43 || File::Spec->curdir; # foo -> .
44
45 # check to make sure it wasn't just installed in the normal way.
46 # note that ./lib/Mail/SpamAssassin.pm takes precedence, for
47 # building SpamAssassin on a machine where an old version is installed.
48 if (-e $bin.'/lib/Mail/SpamAssassin.pm'
49 || !-e '@@INSTALLSITELIB@@/Mail/SpamAssassin.pm' )
50 {
51 my $searchrelative;
52 $searchrelative = 1; # disabled during "make install": REMOVEFORINST
53
54 # Firstly, are we running "make test" in the "t" dir? the test files
55 # *need* to use 'blib', so that 'use bytes' is removed for pre-5.6 perls
56 # beforehand by the preproc. However, ./spamassassin does not, as the
57 # preproc will have stripped out the "use rule files from cwd" code from
58 # Mail::SpamAssassin. So we want to use blib just for the t scripts.
59 # This is disabled during the "make install" process.
60 if ($searchrelative && $bin eq '../' && -e '../blib/lib/Mail/SpamAssassin.pm')
61 {
62 unshift ( @INC, '../blib/lib' );
63 } else {
64 # These are common paths where the SA libs might be found.
65 foreach ( qw(lib ../lib/site_perl
66 ../lib/spamassassin ../share/spamassassin/lib))
67 {
68 my $dir = File::Spec->catdir( $bin, split ( '/', $_ ) );
69 if ( -f File::Spec->catfile( $dir, "Mail", "SpamAssassin.pm" ) )
70 { unshift ( @INC, $dir ); last; }
71 }
72 }
73 }
74}
75
76use Getopt::Long;
77use Pod::Usage;
78use POSIX qw(locale_h setsid sigprocmask _exit);
79use Mail::SpamAssassin;
80use Mail::SpamAssassin::ArchiveIterator;
81use Mail::SpamAssassin::Util::Progress;
82use Mail::SpamAssassin::Logger qw(log_message);
83
84BEGIN {
85 # redirect __WARN__, but NOT until after the
86 # Mail::SpamAssassin::Logger class has been parsed.
87 # do not trap warnings here based on eval scope; evals are very
88 # common throughout. die()s can be trapped in future though.
89 $SIG{__WARN__} = sub {
90 log_message("warn", $_[0]);
91 };
92};
93
94POSIX::setlocale(LC_TIME,'C');
95
96my %resphash = (
97 EX_OK => 0, # no problems
98 EX_USAGE => 64, # command line usage error
99 EX_DATAERR => 65, # data format error
100 EX_NOINPUT => 66, # cannot open input
101 EX_NOUSER => 67, # addressee unknown
102 EX_NOHOST => 68, # host name unknown
103 EX_UNAVAILABLE => 69, # service unavailable
104 EX_SOFTWARE => 70, # internal software error
105 EX_OSERR => 71, # system error (e.g., can't fork)
106 EX_OSFILE => 72, # critical OS file missing
107 EX_CANTCREAT => 73, # can't create (user) output file
108 EX_IOERR => 74, # input/output error
109 EX_TEMPFAIL => 75, # temp failure; user is invited to retry
110 EX_PROTOCOL => 76, # remote error in protocol
111 EX_NOPERM => 77, # permission denied
112 EX_CONFIG => 78, # configuration error
113);
114
115
116sub print_version {
117 printf("SpamAssassin version %s\n running on Perl version %s\n",
118 Mail::SpamAssassin::Version(),
119 join(".", map( 0+($_||0), ($] =~ /(\d)\.(\d{3})(\d{3})?/ ))))
120 or die "error writing: $!";
121}
122
123sub print_usage_and_exit {
124 my ( $message, $respnam ) = @_;
125 $respnam ||= 'EX_USAGE';
126
127 if ($respnam eq 'EX_OK' ) {
128 print_version();
129 print("\n") or die "error writing: $!";
130 }
131 pod2usage(
132 -verbose => 0,
133 -message => $message,
134 -exitval => $resphash{$respnam},
135 -input => "spamassassin-run.pod",
136 -pathlist => \@INC,
137 );
138}
139
140
141
142sub usage {
143 my ( $verbose, $message ) = @_;
144 my $ver = Mail::SpamAssassin::Version();
145
146 print "SpamAssassin version $ver\n" or die "error writing: $!";
147 pod2usage( -verbose => $verbose, -message => $message, -exitval => 64, -input => "spamassassin-run.pod", -pathlist => \@INC );
148
149}
150
151# Check to make sure the script version and the module version matches.
152# If not, die here! Also, deal with unchanged VERSION macro.
153if ($Mail::SpamAssassin::VERSION ne '@@VERSION@@' && '@@VERSION@@' ne "\@\@VERSION\@\@") {
154 die 'spamassassin: spamassassin script is v@@VERSION@@, but using modules v'.$Mail::SpamAssassin::VERSION."\n";
155}
156
157# by default:
158# - create user preference files
159# - have ArchiveIterator detect the input message format (file vs dir)
160#
161my %opt = ( 'create-prefs' => 1, 'format' => 'detect', cf => [] );
162
163my $doing_whitelist_operation = 0;
164my $count = 0;
165my @targets = ();
166my $exitvalue;
167
168my $init_results = 0;
169my $progress;
170my $total_messages = 0;
171
172# gnu_getopt is not available in Getopt::Long 2.24, see bug 732
173# gnu_compat neither.
174Getopt::Long::Configure(
175 qw(bundling no_getopt_compat no_auto_abbrev no_ignore_case));
176GetOptions(
177 'add-addr-to-blacklist=s' => \$opt{'add-addr-to-blacklist'},
178 'add-addr-to-whitelist=s' => \$opt{'add-addr-to-whitelist'},
179 'add-to-blacklist' => \$opt{'add-to-blacklist'},
180 'add-to-whitelist|W' => \$opt{'add-to-whitelist'},
181 'configpath|config-file|config-dir|c|C=s' => \$opt{'configpath'},
182 'create-prefs!' => \$opt{'create-prefs'},
183 'cf=s' => \@{$opt{'cf'}},
184 'debug|D:s' => \$opt{'debug'},
185 'error-code|exit-code|e:i' => \$opt{'error-code'},
186 'help|h|?' => \$opt{'help'},
187 '4|ipv4only|ipv4-only|ipv4' => sub { $opt{'force_ipv4'} = 1;
188 $opt{'force_ipv6'} = 0; },
189 '6' => sub { $opt{'force_ipv6'} = 1;
190 $opt{'force_ipv4'} = 0; },
191 'lint' => \$opt{'lint'},
192 'local-only|local|L' => \$opt{'local'},
193 'mbox' => sub { $opt{'format'} = 'mbox'; },
194 'mbx' => sub { $opt{'format'} = 'mbx'; },
195 'prefspath|prefs-file|p=s' => \$opt{'prefspath'},
196 'remove-addr-from-whitelist=s' => \$opt{'remove-addr-from-whitelist'},
197 'remove-from-whitelist|R' => \$opt{'remove-from-whitelist'},
198 'remove-markup|despamassassinify|d' => \$opt{'remove-markup'},
199 'report|r' => \$opt{'report'},
200 'revoke|k' => \$opt{'revoke'},
201 'siteconfigpath=s' => \$opt{'siteconfigpath'},
202 'test-mode|test|t' => \$opt{'test-mode'},
203 'progress' => \$opt{'progress'},
204 'version|V' => \$opt{'version'},
205 'x' => sub { $opt{'create-prefs'} = 0 },
206
207 #
208 # NOTE: These are old options. We should ignore (but warn about)
209 # the ones that are now defaults. Everything else gets a die (see note2)
210 # so the user doesn't get us doing something they didn't expect.
211 #
212 # NOTE2: 'die' doesn't actually stop the process, GetOptions() catches
213 # it, then passes the error on, so we'll end up doing a Usage statement.
214 # You can avoid that by doing an explicit exit in the sub.
215 #
216
217 # last in 2.3
218 'pipe|P' => sub { warn "The -P option is deprecated as 'pipe mode' is now the default behavior, ignoring.\n" },
219 'F:i' => sub { warn "The -F option has been removed from spamassassin, please remove from your commandline and re-run.\n"; exit 2; },
220 'add-from!' => sub { warn "The --add-from option has been removed from spamassassin, please remove from your commandline and re-run.\n"; exit 2; },
221
222 # last in 2.4
223 'stop-at-threshold|S' => sub { warn "The -S option has been deprecated and is no longer supported, ignoring.\n" },
224
225 # last in 2.6
226 'log-to-mbox|l:s' => sub { warn "The -l option has been deprecated and is no longer supported, ignoring.\n" },
227 'warning-from|w:s' => sub { warn "The -w option has been removed from spamassassin, please remove from your commandline and re-run.\n"; exit 2; },
228 'whitelist-factory|M:s' => sub { warn "The -M option has been removed from spamassassin, please remove from your commandline and re-run.\n"; exit 2; },
229
230) or print_usage_and_exit();
231
232if ( defined $opt{'help'} ) {
233 print_usage_and_exit("For more information read the spamassassin man page.\n", 'EX_OK');
234}
235if ( defined $opt{'version'} ) {
236 print_version();
237 exit($resphash{'EX_OK'});
238}
239
240# set debug areas, if any specified (only useful for command-line tools)
241if (defined $opt{'debug'}) {
242 $opt{'debug'} ||= 'all';
243}
244
245if (Mail::SpamAssassin::Util::am_running_on_windows()) {
246 binmode(STDIN) or die "cannot set binmode on STDIN: $!"; # bug 4363
247 binmode(STDOUT) or die "cannot set binmode on STDOUT: $!";
248}
249
250# bug 5048: --lint should not cause network accesses
251if ($opt{'lint'}) { $opt{'local'} = 1; }
252
253# create the tester factory
254my $spamtest = new Mail::SpamAssassin(
255 {
256 rules_filename => $opt{'configpath'},
257 site_rules_filename => $opt{'siteconfigpath'},
258 userprefs_filename => $opt{'prefspath'},
259 force_ipv4 => $opt{'force_ipv4'},
260 force_ipv6 => $opt{'force_ipv6'},
261 local_tests_only => $opt{'local'},
262 debug => $opt{'debug'},
263 dont_copy_prefs => ( $opt{'create-prefs'} ? 0 : 1 ),
264 post_config_text => join("\n", @{$opt{'cf'}})."\n",
265 require_rules => 1,
266 PREFIX => $PREFIX,
267 DEF_RULES_DIR => $DEF_RULES_DIR,
268 LOCAL_RULES_DIR => $LOCAL_RULES_DIR,
269 LOCAL_STATE_DIR => $LOCAL_STATE_DIR,
270 }
271);
272
273if ($opt{'lint'}) {
274 $spamtest->debug_diagnostics();
275 my $res = $spamtest->lint_rules();
276 warn "lint: $res issues detected, please rerun with debug enabled for more information\n" if ($res and !$opt{'debug'});
277 # make sure we notice any write errors while flushing output buffer
278 close STDOUT or die "error closing STDOUT: $!";
279 close STDIN or die "error closing STDIN: $!";
280 exit $res ? 1 : 0;
281}
282
283if ($opt{'remove-addr-from-whitelist'} ||
284 $opt{'add-addr-to-whitelist'} ||
285 $opt{'add-addr-to-blacklist'})
286{
287 $spamtest->init(1);
288
289 if ( $opt{'add-addr-to-whitelist'} ) {
290 $spamtest->add_address_to_whitelist($opt{'add-addr-to-whitelist'}, 1);
291 }
292 elsif ( $opt{'remove-addr-from-whitelist'} ) {
293 $spamtest->remove_address_from_whitelist($opt{'remove-addr-from-whitelist'}, 1);
294 }
295 elsif ( $opt{'add-addr-to-blacklist'} ) {
296 $spamtest->add_address_to_blacklist($opt{'add-addr-to-blacklist'}, 1);
297 }
298 else {
299 die "spamassassin: oops! unhandled whitelist operation";
300 }
301
302 $spamtest->finish();
303 # make sure we notice any write errors while flushing output buffer
304 close STDOUT or die "error closing STDOUT: $!";
305 close STDIN or die "error closing STDIN: $!";
306 exit(0);
307}
308
309# if we're going to do white/black-listing, let's prep now...
310if ( $opt{'remove-from-whitelist'}
311 or $opt{'add-to-whitelist'}
312 or $opt{'add-to-blacklist'} )
313{
314 $doing_whitelist_operation = 1;
315 $spamtest->init(1);
316}
317
318# if we're doing things in test mode, force disable long-term memory
319# functions like autowhitelist and bayes autolearn.
320# XXX - feels like we need a plugin hook here so plugins can be made
321# aware and take appropriate action.
322if ($opt{'test-mode'}) {
323 $spamtest->{'conf'}->{'use_auto_whitelist'} = 0;
324 $spamtest->{'conf'}->{'bayes_auto_learn'} = 0;
325}
326
327###########################################################################
328# Deal with the target listing, and STDIN -> tempfile
329
330my $tempfile; # will be defined if stdin -> tempfile
331push(@targets, @ARGV);
332@targets = ('-') unless @targets;
333
334for(my $elem = 0; $elem <= $#targets; $elem++) {
335 # ArchiveIterator doesn't really like STDIN, so if "-" is specified
336 # as a target, make it a temp file instead.
337 if ( $targets[$elem] =~ /(?:^|:)-$/ ) {
338 if (defined $tempfile) {
339 # uh-oh, stdin specified multiple times?
340 warn "skipping extra stdin target (".$targets[$elem].")\n";
341 splice @targets, $elem, 1;
342 $elem--; # go back to this element again
343 next;
344 }
345 else {
346 my $handle;
347 ( $tempfile, $handle ) = Mail::SpamAssassin::Util::secure_tmpfile();
348 binmode $handle or die "cannot set binmode on file $tempfile: $!";
349
350 # avoid slurping the whole file into memory, copy chunk by chunk
351 my($inbuf,$nread,$nwrites);
352 while ( $nread = sysread(STDIN, $inbuf, 32*1024) ) {
353 for (my $ofs = 0; $ofs < length($inbuf); $ofs += $nwrites) {
354 $nwrites = $handle->syswrite($inbuf, length($inbuf)-$ofs, $ofs);
355 defined $nwrites or die "error writing to $tempfile: $!";
356 }
357 }
358 undef $inbuf; # release storage
359 defined $nread or die "error reading from STDIN: $!";
360 close $handle or die "cannot close $tempfile: $!";
361
362 # re-aim the targets at the tempfile instead of STDIN
363 $targets[$elem] =~ s/-$/$tempfile/;
364 }
365 }
366
367 # make sure the target list is in the normal AI format
368 if ($targets[$elem] !~ /^[^:]*:[a-z]+:/) {
369 my $format = $opt{'format'} || 'detect';
370 $targets[$elem] = join ( ":", '', $format, $targets[$elem] );
371 }
372}
373
374###########################################################################
375
376setup_sig_handlers();
377
378# Everything below here needs ArchiveIterator ...
379my $iter = new Mail::SpamAssassin::ArchiveIterator(
380 {
381 'opt_max_size' => 0, # no limit
382 'opt_want_date' => 0
383 }
384);
385
386$iter->set_functions( \&wanted, \&result );
387
388# Go run the messages!
389# bug 4930: use a temp variable since "||=" decides whether or not to set the
390# value before the RHS is actually run, so if the RHS separately sets the LHS
391# variable, things don't work right. Stupid global variables. ;)
392my $eval_stat;
393eval {
394 my $runreturn = !$iter->run(@targets); $exitvalue ||= $runreturn; 1;
395} or do {
396 $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
397};
398
399$progress->final() if ($opt{progress} && $progress);
400
401# If we needed to make a tempfile, go delete it now.
402if (defined $tempfile) {
403 unlink $tempfile or die "cannot unlink temporary file $tempfile: $!";
404 undef $tempfile;
405}
406
407# Let folks know how many messages were handled, as long as the handling
408# didn't produce output (ala: check, test, or remove_markup ...)
409if ( $opt{'report'} || $opt{'revoke'} || $doing_whitelist_operation ) {
410 print "$count message(s) examined.\n" or die "error writing: $!";
411}
412
413# if the eval died from something, report it here and return an error.
414if (defined $eval_stat) { die $eval_stat; }
415
416$spamtest->finish() if $spamtest;
417
418# make sure we notice any write errors while flushing output buffer
419close STDOUT or die "error closing STDOUT: $!";
420close STDIN or die "error closing STDIN: $!";
421# Ok, exit!
422exit( $exitvalue || 0 );
423
424###########################################################################
425
426sub init_results {
427 $init_results = 1;
428
429 return unless $opt{'progress'};
430
431 $total_messages = $Mail::SpamAssassin::ArchiveIterator::MESSAGES;
432
433 $progress = Mail::SpamAssassin::Util::Progress->new({total => $total_messages,});
434}
435
436###########################################################################
437
438sub result {
439 my ($class, $result, $time) = @_;
440
441 # don't open results files until we get here to avoid overwriting files
442 &init_results if !$init_results;
443
444 $progress->update($count) if ($opt{progress} && $progress);
445}
446
447###########################################################################
448
449my $mail; # global, so signal handler can clean it up; bug 5626
450
451# make sure it only returns false values so that result_sub() isn't called...
452sub wanted {
453 $spamtest->timer_reset; # reset timers for each AI message
454 my $dataref = $_[3];
455 $mail = $spamtest->parse($dataref);
456 $count++;
457
458 # This is a short cut -- doing white/black-list? Do it and return quickly.
459 if ($doing_whitelist_operation) {
460 if ( $opt{'add-to-whitelist'} ) {
461 $spamtest->add_all_addresses_to_whitelist($mail, 1);
462 }
463 elsif ( $opt{'remove-from-whitelist'} ) {
464 $spamtest->remove_all_addresses_from_whitelist($mail, 1);
465 }
466 elsif ( $opt{'add-to-blacklist'} ) {
467 $spamtest->add_all_addresses_to_blacklist($mail, 1);
468 }
469 else {
470 warn "spamassassin: oops! unhandled whitelist operation";
471 }
472
473 $mail->finish();
474 $mail = undef;
475 return 1;
476 }
477
478 # handle removing reports
479 if ( $opt{'remove-markup'} ) {
480
481 # If we're not going to retest, just remove the markup and print it out
482 if ( !$opt{'test-mode'} ) {
483 print $spamtest->remove_spamassassin_markup ($mail);
484 $mail->finish();
485 $mail = undef;
486 return 1;
487 }
488 else {
489
490 # remove the markup and retest it... a little more tricky ...
491 # go ahead and remove the markup, then fake that the clean version
492 # was what was sent in
493 #
494 my $new_mail =
495 $spamtest->parse( $spamtest->remove_spamassassin_markup($mail) );
496
497 $mail->finish();
498 $mail = $new_mail;
499 }
500 }
501
502 # handle reporting and revoking
503 if ( $opt{'report'} || $opt{'revoke'} ) {
504
505 # Make sure the message is clean first ...
506 my $new_mail =
507 $spamtest->parse( $spamtest->remove_spamassassin_markup($mail) );
508 $mail->finish();
509 $mail = $new_mail;
510
511 my $failed;
512 if ( $opt{'report'} && !$spamtest->report_as_spam($mail) ) {
513 $failed = 'report';
514 }
515
516 if ( $opt{'revoke'} && !$spamtest->revoke_as_spam($mail) ) {
517 $failed = 'revoke';
518 }
519
520 if ($failed) {
521 warn "spamassassin: warning, unable to $failed message\n";
522 warn "spamassassin: for more information, re-run with -D option to see debug output\n";
523 }
524
525 $mail->finish();
526 $mail = undef;
527 return 1;
528 }
529
530 # OK, do checks and put out the message.
531 my $status = $spamtest->check($mail);
532 print $status->rewrite_mail() or die "error writing: $!";
533
534 if ( $opt{'test-mode'} ) {
535 print $status->get_report() or die "error writing: $!";
536 }
537
538 # if this message was spam, set the exit value appropriately.
539 if ( defined $opt{'error-code'} && $status->is_spam() && !defined $exitvalue )
540 {
541 $exitvalue = $opt{'error-code'} || 5;
542 }
543
544 # clean up after ourselves
545 $mail->finish();
546 $mail = undef;
547
548 $status->finish();
549
550 return 1;
551}
552
553###########################################################################
554
555sub setup_sig_handlers {
556 $SIG{HUP} = \&kill_handler;
557 $SIG{INT} = \&kill_handler;
558 $SIG{TERM} = \&kill_handler;
559# $SIG{PIPE} = \&kill_handler;
560 $SIG{PIPE} = 'IGNORE';
561}
562
563sub kill_handler {
564 my ($sig) = @_;
565 warn "spamassassin: killed by SIG$sig\n";
566 if ($mail) {
567 $mail->finish(); # bug 5626: remove temp files etc.
568 $mail = undef;
569 }
570 if (defined $tempfile) { # bug 5557: additional paranoia about tmpfiles
571 unlink $tempfile or warn "cannot unlink temporary file $tempfile: $!";
572 undef $tempfile;
573 }
574 close STDOUT; close STDIN; # ignoring status
575 exit 15;
576}
577
578__END__
579
580# ---------------------------------------------------------------------------
581
582=head1 NAME
583
584spamassassin - extensible email filter used to identify spam
585
586=head1 DESCRIPTION
587
588SpamAssassin is an intelligent email filter which uses a diverse range of
589tests to identify unsolicited bulk email, more commonly known as "spam".
590These tests are applied to email headers and content to classify email
591using advanced statistical methods. In addition, SpamAssassin has a
592modular architecture that allows other technologies to be quickly wielded
593against spam and is designed for easy integration into virtually any email
594system.
595
596=head1 SYNOPSIS
597
598For ease of access, the SpamAssassin manual has been split up into
599several sections. If you're intending to read these straight through
600for the first time, the suggested order will tend to reduce the number
601of forward references.
602
603Extensive additional documentation for SpamAssassin is available,
604primarily on the SpamAssassin web site and wiki.
605
606You should be able to view SpamAssassin's documentation with your man(1)
607program or perldoc(1).
608
609=head2 OVERVIEW
610
611 spamassassin SpamAssassin overview (this section)
612
613=head2 CONFIGURATION
614
615 Mail::SpamAssassin::Conf SpamAssassin configuration files
616
617=head2 USAGE
618
619 spamassassin-run "spamassassin" front-end filtering script
620 sa-learn train SpamAssassin's Bayesian classifier
621 spamc client for spamd (faster than spamassassin)
622 spamd spamassassin server (needed by spamc)
623
624=head2 DEFAULT PLUGINS
625
626@@PLUGIN_POD@@
627
628=head1 WEB SITES
629
630 SpamAssassin web site: http://spamassassin.apache.org/
631 Wiki-based documentation: http://wiki.apache.org/spamassassin/
632
633=head1 USER MAILING LIST
634
635A users mailing list exists where other experienced users are often able
636to help and provide tips and advice. Subscription instructions are
637located on the SpamAssassin web site.
638
639=head1 CONFIGURATION FILES
640
641The SpamAssassin rule base, text templates, and rule description text
642are loaded from configuration files.
643
644Default configuration data is loaded from the first existing directory
645in:
646
647=over 4
648
649=item @@LOCAL_STATE_DIR@@/@@VERSION@@
650
651=item @@DEF_RULES_DIR@@
652
653=item @@PREFIX@@/share/spamassassin
654
655=item /usr/local/share/spamassassin
656
657=item /usr/share/spamassassin
658
659=back
660
661Site-specific configuration data is used to override any values which had
662already been set. This is loaded from the first existing directory in:
663
664=over 4
665
666=item @@LOCAL_RULES_DIR@@
667
668=item @@PREFIX@@/etc/mail/spamassassin
669
670=item @@PREFIX@@/etc/spamassassin
671
672=item /usr/local/etc/spamassassin
673
674=item /usr/pkg/etc/spamassassin
675
676=item /usr/etc/spamassassin
677
678=item /etc/mail/spamassassin
679
680=item /etc/spamassassin
681
682=back
683
684From those directories, SpamAssassin will first read files ending in
685".pre" in lexical order and then it will read files ending in ".cf" in
686lexical order (most files begin with two numbers to make the sorting
687order obvious).
688
689In other words, it will read F<init.pre> first, then F<10_default_prefs.cf> before
690F<50_scores.cf> and F<20_body_tests.cf> before F<20_head_tests.cf>.
691Options in later files will override earlier files.
692
693Individual user preferences are loaded from the location specified on
694the C<spamassassin>, C<sa-learn>, or C<spamd> command line (see respective
695manual page for details). If the location is not specified,
696F<~/.spamassassin/user_prefs> is used if it exists. SpamAssassin will
697create that file if it does not already exist, using
698F<user_prefs.template> as a template. That file will be looked for in:
699
700=over 4
701
702=item @@LOCAL_RULES_DIR@@
703
704=item @@PREFIX@@/etc/mail/spamassassin
705
706=item @@PREFIX@@/share/spamassassin
707
708=item /etc/spamassassin
709
710=item /etc/mail/spamassassin
711
712=item /usr/local/share/spamassassin
713
714=item /usr/share/spamassassin
715
716=back
717
718=head1 TAGGING
719
720The following two sections detail the default tagging and markup that
721takes place for messages when running C<spamassassin> or C<spamc> with
722C<spamd> in the default configuration.
723
724Note: before header modification and addition, all headers beginning
725with C<X-Spam-> are removed to prevent spammer mischief and also to
726avoid potential problems caused by prior invocations of SpamAssassin.
727
728=head2 TAGGING FOR SPAM MAILS
729
730By default, all messages with a calculated score of 5.0 or higher are
731tagged as spam.
732
733If an incoming message is tagged as spam, instead of modifying the
734original message, SpamAssassin will create a new report message and
735attach the original message as a message/rfc822 MIME part (ensuring the
736original message is completely preserved and easier to recover).
737
738The new report message inherits the following headers (if they are
739present) from the original spam message:
740
741=over 4
742
743=item From: header
744
745=item To: header
746
747=item Cc: header
748
749=item Subject: header
750
751=item Date: header
752
753=item Message-ID: header
754
755=back
756
757The above headers can be modified if the relevant C<rewrite_header>
758option is given (see C<Mail::SpamAssassin::Conf> for more information).
759
760By default these message headers are added to spam:
761
762=over 4
763
764=item X-Spam-Flag: header
765
766Set to C<YES>.
767
768=back
769
770The headers that added are fully configurable via the C<add_header>
771option (see C<Mail::SpamAssassin::Conf> for more information).
772
773=over 4
774
775=item spam mail body text
776
777The SpamAssassin report is added to top of the mail message body,
778if the message is marked as spam.
779
780=back
781
782=head2 DEFAULT TAGGING FOR ALL MAILS
783
784These headers are added to all messages, both spam and ham (non-spam).
785
786=over 4
787
788=item X-Spam-Checker-Version: header
789
790The version and subversion of SpamAssassin and the host where
791SpamAssassin was run.
792
793=item X-Spam-Level: header
794
795A series of "*" characters where each one represents a full score point.
796
797=item X-Spam-Status: header
798
799A string, C<(Yes|No), score=nn required=nn tests=xxx,xxx
800autolearn=(ham|spam|no|unavailable|failed)> is set in this header to
801reflect the filter status. For the first word, "Yes" means spam and
802"No" means ham (non-spam).
803
804=back
805
806The headers that added are fully configurable via the C<add_header>
807option (see C<Mail::SpamAssassin::Conf> for more information).
808
809=head1 INSTALLATION
810
811The B<spamassassin> command is part of the B<Mail::SpamAssassin> Perl module.
812Install this as a normal Perl module, using C<perl -MCPAN -e shell>, or by
813hand.
814
815Note that it is not possible to use the C<PERL5LIB> environment variable
816to affect where SpamAssassin finds its perl modules, due to limitations
817imposed by perl's "taint" security checks.
818
819For further details on how to install, please read the C<INSTALL> file
820from the SpamAssassin distribution.
821
822=head1 DEVELOPER DOCUMENTATION
823
824 Mail::SpamAssassin
825 Spam detector and markup engine
826
827 Mail::SpamAssassin::ArchiveIterator
828 find and process messages one at a time
829
830 Mail::SpamAssassin::AutoWhitelist
831 auto-whitelist handler for SpamAssassin
832
833 Mail::SpamAssassin::Bayes
834 determine spammishness using a Bayesian classifier
835
836 Mail::SpamAssassin::BayesStore
837 Bayesian Storage Module
838
839 Mail::SpamAssassin::BayesStore::SQL
840 SQL Bayesian Storage Module Implementation
841
842 Mail::SpamAssassin::Conf::LDAP
843 load SpamAssassin scores from LDAP database
844
845 Mail::SpamAssassin::Conf::Parser
846 parse SpamAssassin configuration
847
848 Mail::SpamAssassin::Conf::SQL
849 load SpamAssassin scores from SQL database
850
851 Mail::SpamAssassin::Message
852 decode, render, and hold an RFC-2822 message
853
854 Mail::SpamAssassin::Message::Metadata
855 extract metadata from a message
856
857 Mail::SpamAssassin::Message::Node
858 decode, render, and make available MIME message parts
859
860 Mail::SpamAssassin::PerMsgLearner
861 per-message status (spam or not-spam)
862
863 Mail::SpamAssassin::PerMsgStatus
864 per-message status (spam or not-spam)
865
866 Mail::SpamAssassin::PersistentAddrList
867 persistent address list base class
868
869 Mail::SpamAssassin::Plugin
870 SpamAssassin plugin base class
871
872 Mail::SpamAssassin::Plugin::Hashcash
873 perform hashcash verification tests
874
875 Mail::SpamAssassin::Plugin::RelayCountry
876 add message metadata indicating the country code of each relay
877
878 Mail::SpamAssassin::Plugin::SPF
879 perform SPF verification tests
880
881 Mail::SpamAssassin::Plugin::URIDNSBL
882 look up URLs against DNS blocklists
883
884 Mail::SpamAssassin::SQLBasedAddrList
885 SpamAssassin SQL Based Auto Whitelist
886
887=head1 BUGS
888
889See <http://issues.apache.org/SpamAssassin/>
890
891=head1 AUTHORS
892
893The SpamAssassin(tm) Project <http://spamassassin.apache.org/>
894
895=head1 COPYRIGHT AND LICENSE
896
897SpamAssassin is distributed under the Apache License, Version 2.0, as
898described in the file C<LICENSE> included with the distribution.
899
900Copyright (C) 2015 The Apache Software Foundation
901