merged latest changes from trunk using: 'svn merge -r 602889:613114 https://svn.apache.org/repos/asf/spamassassin/trunk'
git-svn-id: https://svn.apache.org/repos/asf/spamassassin/branches/bug-5293-pluginized-bayes@613117 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP
index 4d6974f..f3f6e97 100644
--- a/MANIFEST.SKIP
+++ b/MANIFEST.SKIP
@@ -103,3 +103,4 @@
^masses/rule-dev/seek-phrases-in-corpus$
^build/announcements/.*.txt$
t/mass_check.t
+^build/backup
diff --git a/Makefile.PL b/Makefile.PL
index f752d03..e47a76d 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -5,7 +5,18 @@
use warnings;
use Config;
-use ExtUtils::MakeMaker 5.45;
+use ExtUtils::MakeMaker;
+
+# Store the version for later use
+my $mm_version = $ExtUtils::MakeMaker::VERSION;
+
+# avoid stupid 'Argument "6.30_01" isn't numeric in numeric ge (>=)' warnings;
+# strip off the beta subversion noise that causes the trouble.
+$mm_version =~ s/_\S+$//; # "6.30_01" => "6.30"
+
+if ($mm_version+0 < 5.45) {
+ die "SpamAssassin Makefile.PL requires at least ExtUtils::MakeMaker v5.45";
+}
use constant RUNNING_ON_WINDOWS => ($^O =~ /^(mswin|dos|os2)/oi);
use constant HAS_DBI => eval { require DBI; };
@@ -120,16 +131,12 @@
# Gather some information about what EU::MM offers and/or needs
my(
- $mm_version,
$mm_knows_destdir,
$mm_has_destdir,
$mm_has_good_destdir,
$mm_needs_destdir,
);
-# Store the version for later use
-$mm_version = $ExtUtils::MakeMaker::VERSION;
-
# MakeMaker prior to 6.11 doesn't support DESTDIR which is needed for
# packaging with builddir!=destdir. See bug 2388.
$mm_knows_destdir = $ExtUtils::MakeMaker::Recognized_Att_Keys{DESTDIR};
diff --git a/build/README b/build/README
index b3fb6f1..85d0084 100644
--- a/build/README
+++ b/build/README
@@ -123,7 +123,7 @@
svn commit -m "preparing to release X.Y.Z"
(If you are privately preparing a security release, and don't wish to
- perform commits to public SVN repos, you can omit this step until
+ perform commits to public SVN repos, you can defer this step until
later.)
- SVN tag the release files. This is done using "svn copy".
@@ -156,7 +156,7 @@
base of the tag release.
(If you are privately preparing a security release, and don't wish to
- perform commits to public SVN repos, you can omit this step until
+ perform commits to public SVN repos, you can defer this step until
later.)
@@ -183,24 +183,12 @@
- take a copy of the MD5sum line output.
-- by default, they're written to ~/site/released/ .
- Copy them to wherever you want, yourself:
-
- mkdir -p ~/public_html/devel
- mv ~/site/released/Mail-SpamAssassin-3.2.0-rc2.* ~/public_html/devel
-
- (For me, "~/public_html/devel" is http://people.apache.org/~jm/devel/ .)
-
- test the tar.gz and zip files! redo until they work!! ;)
- Write the release announcement mail! This is a simple matter of copying
the previous release's announcement, updating the version numbers and
links, fixing the MD5 and SHA1 checksums in this mail, and summarising
- the important changes from the Changes file.
-
- Warning: you must send this using a "From:" address @apache.org,
- otherwise it will be silently discarded at the ASF instead of being
- delivered to the announce list.
+ the important changes from the Changes file.
cp build/announcements/3.1.7.txt build/announcements/3.1.8.txt
svn add !$
@@ -211,7 +199,14 @@
of the website, but included in the vote mail) and request a vote on the
development mailing list to make the release. Post the URL,
md5sums/sha1sums, and proposed release announcement mail to the dev
- list.
+ list. The default location -- ~/public_html/devel/ , or
+ http://people.apache.org/~jm/devel/ , qualifies as "discreet".
+
+ While doing this, also upload a copy of the proposed release
+ announcement to the website:
+
+ version=3.2.4
+ cp build/announcements/$version.txt ~/public_html/devel/PROPOSED-$version.txt
Pre-releases and RCs require just lazy consensus -- ie. no objections.
@@ -274,12 +269,18 @@
make it clear that this is an unofficial "test build" by placing it
in your public_html dir:
- http://people.apache.org/~jm/devel/
+ http://people.apache.org/~jm/rc/
+
+ this command will do it:
+
+ version=X.Y.Z
+ cp -p ~/public_html/devel/Mail-SpamAssassin-$version.* \
+ ~/public_html/rc/
(for full release builds) copy the tarballs to www.apache.org/dist:
version=X.Y.Z
- cp -p ~/site/released/Mail-SpamAssassin-$version.* \
+ cp -p ~/public_html/devel/Mail-SpamAssassin-$version.* \
/www/www.apache.org/dist/spamassassin/source
cd /www/www.apache.org/dist/spamassassin
@@ -295,7 +296,7 @@
cd /www/www.apache.org/dist/spamassassin
prev=X.Y.notZ
- rm -f source/Mail-SpamAssassin-$prev.*
+ rm -f source/Mail-SpamAssassin-$prev.* Mail-SpamAssassin-$prev.*
rm -f binaries/*/Mail-SpamAssassin-$prev.*
(Archive copies are automatically kept on archive.apache.org/dist/ .)
@@ -312,6 +313,7 @@
- rebuild the SpamAssassin website with webmake:
+ PATH=$HOME/sabuildtools/perl584/bin:$HOME/sabuildtools/bin:$PATH
cd /www/spamassassin.apache.org
webmake -F
@@ -341,10 +343,8 @@
- Release a new rules update matching the released code:
ssh spamassassin.zones.apache.org
- sudo -H -u updatesd \
- /home/updatesd/svn/spamassassin/build/mkupdates/run_nightly
- sudo -H -u updatesd \
- /home/updatesd/svn/spamassassin/build/mkupdates/run_part2
+ cd /home/updatesd/svn/spamassassin/build/mkupdates
+ sudo -H -u updatesd ./update-rules 3.2
- update the tag used to point to "current release":
@@ -395,4 +395,5 @@
http://issues.apache.org/SpamAssassin/editversions.cgi?product=Spamassassin&action=add
+
// vim:tw=74:
diff --git a/build/automc/etc-init.d-freqsd b/build/automc/etc-init.d-freqsd
index 8c2f1ca..db66b23 100755
--- a/build/automc/etc-init.d-freqsd
+++ b/build/automc/etc-init.d-freqsd
@@ -1,22 +1,23 @@
#!/bin/sh
+#
+# symlinked from
+# lrwxrwxrwx 1 root root 60 Dec 2 10:03 /etc/init.d/freqsd -> /home/automc/svn/spamassassin/build/automc/etc-init.d-freqsd*
. /etc/profile
prog=freqsd
-HOME=/home/automc
+HOME=/export/home/automc
export HOME
start() {
echo "Starting $prog: "
- cd /export/home/automc/svn/spamassassin
- rm /export/home/automc/freqsd/log
+ cd $HOME/svn/spamassassin
+ rm $HOME/freqsd/log.1
+ mv $HOME/freqsd/log $HOME/freqsd/log.1
- HOME=/export/home/automc
- export HOME
-
- su automc -c "./build/automc/freqsd -pidfile /export/home/automc/freqsd/pid" \
- > /export/home/automc/freqsd/log 2>&1 \
+ su automc -c "./build/automc/freqsd -pidfile $HOME/freqsd/pid" \
+ > $HOME/freqsd/log 2>&1 \
< /dev/null &
echo
@@ -24,11 +25,9 @@
}
stop() {
- cd /export/home/automc/svn/spamassassin
- su automc -c "./build/automc/freqsd -pidfile /export/home/automc/freqsd/pid -kill"
+ cd $HOME/svn/spamassassin
+ su automc -c "./build/automc/freqsd -pidfile $HOME/freqsd/pid -kill"
- # sleep 1
- # pkill -15 -f automc/freqsd # just in case
sleep 1
pkill -15 -u automc
}
diff --git a/build/automc/freqsd b/build/automc/freqsd
index 72bb248..adbfd0e 100755
--- a/build/automc/freqsd
+++ b/build/automc/freqsd
@@ -196,7 +196,7 @@
system ($cmd);
if (!$ignoreexit) {
- die "command '$cmd' failed with status $?" if (($? >> 8) != 0);
+ warn "command '$cmd' failed with status $?" if (($? >> 8) != 0);
}
}
diff --git a/build/automc/freqsd-infrequent b/build/automc/freqsd-infrequent
index ae6ce98..eb4bdc3 100755
--- a/build/automc/freqsd-infrequent
+++ b/build/automc/freqsd-infrequent
@@ -1,14 +1,7 @@
#!/bin/sh
-BBMHOME=/export/home/bbmass
-
-# clean up our temporary dir
-cd $BBMHOME/tmp
-find . -type d -mtime +7 -print | xargs rm -rf
-
# clean out old copies of mass-check logs
# 180 days = ~6 months
cd /home/automc/corpus/html
-find . -mtime +180 -name '*.log.gz' > $BBMHOME/tmp/o
-xargs rm -f < $BBMHOME/tmp/o
+find . -mtime +180 -name '*.log.gz' | xargs rm -f
diff --git a/build/automc/gzip_old_ruleqa_data b/build/automc/gzip_old_ruleqa_data
index fc2b845..aba68cf 100755
--- a/build/automc/gzip_old_ruleqa_data
+++ b/build/automc/gzip_old_ruleqa_data
@@ -1,7 +1,13 @@
#!/bin/sh
+# gzip old files
cd /export/home/automc/corpus/html
-exec find . -mtime +200 -name '*.???' -print | \
+find . -mtime +200 -name '*.???' -print | \
egrep '(age|all|new)$' | \
xargs gzip -9
+# clean up a temporary dir; these dirs are used for interchange
+# of log results between preflight mass-check mc-fast and freqsd
+cd /home/bbmass/tmp
+find . -type d -mtime +7 -print | xargs rm -rf
+
diff --git a/build/nightlymc/corpus.doc b/build/nightlymc/corpus.doc
index 361b32f..c9bf47c 100644
--- a/build/nightlymc/corpus.doc
+++ b/build/nightlymc/corpus.doc
@@ -1,11 +1,11 @@
-opts_weekly="--net --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 --after="15552000" --tail=15000 --net -j 8 -f /home/bbmass/mc-nightly/targets.doc"
-opts_nightly=" --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 --after="15552000" --tail=15000 -f /home/bbmass/mc-nightly/targets.doc"
+opts_weekly="--net -j 8 --reuse --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/export/home/bbmass/rawcor/doc/ham/* --after="15552000" --tail=25000 spam:detect:/export/home/bbmass/rawcor/doc/spam/*"
+opts_nightly=" --reuse --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/export/home/bbmass/rawcor/doc/ham/* --after="15552000" --tail=25000 spam:detect:/export/home/bbmass/rawcor/doc/spam/*"
tmp=$HOME/tmp
tree=$HOME/svn
prefs_weekly=$HOME/user_prefs.weekly
prefs_nightly=$HOME/user_prefs.nightly
username=bb-doc
password=__RSYNC_PASSWORD__
-serverhost=spamassassin.zones.apache.org:38892
+serverhost=spamassassin.zones.apache.org.:38892
clienthosts=__CLIENTHOSTS__
clienttree=nightlymc_doc
diff --git a/build/nightlymc/corpus.fredt b/build/nightlymc/corpus.fredt
index b7d6c6b..76fb725 100644
--- a/build/nightlymc/corpus.fredt
+++ b/build/nightlymc/corpus.fredt
@@ -1,11 +1,11 @@
-opts_weekly="--net --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 --after="15552000" --tail=15000 --net -j 8 -f /home/bbmass/mc-nightly/targets.fredt"
-opts_nightly="--cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 --after="15552000" --tail=15000 -f /home/bbmass/mc-nightly/targets.fredt"
+opts_weekly="--net -j 8 --reuse --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/export/home/bbmass/rawcor/fredt/ham/* --after="15552000" --tail=25000 spam:detect:/export/home/bbmass/rawcor/fredt/spam/*"
+opts_nightly=" --reuse --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/export/home/bbmass/rawcor/fredt/ham/* --after="15552000" --tail=25000 spam:detect:/export/home/bbmass/rawcor/fredt/spam/*"
tmp=$HOME/tmp
tree=$HOME/svn
prefs_weekly=$HOME/user_prefs.weekly
prefs_nightly=$HOME/user_prefs.nightly
username=bb-fredt
password=__RSYNC_PASSWORD__
-serverhost=spamassassin.zones.apache.org:38893
+serverhost=spamassassin.zones.apache.org.:38893
clienthosts=__CLIENTHOSTS__
clienttree=nightlymc_fredt
diff --git a/build/nightlymc/corpus.jm b/build/nightlymc/corpus.jm
index d1b894b..56aecbd 100644
--- a/build/nightlymc/corpus.jm
+++ b/build/nightlymc/corpus.jm
@@ -1,11 +1,11 @@
-opts_weekly="--net --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/home/bbmass/rawcor/jm/ham/* --after="15552000" --tail=25000 --scanprob=0.3 spam:detect:/home/bbmass/rawcor/jm/spam/*"
-opts_nightly="--cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/home/bbmass/rawcor/jm/ham/* --after="15552000" --tail=25000 --scanprob=0.3 spam:detect:/home/bbmass/rawcor/jm/spam/*"
+opts_weekly="--net -j 8 --reuse --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/home/bbmass/rawcor/jm/ham/* --after="15552000" --tail=40000 --scanprob=0.3 spam:detect:/home/bbmass/rawcor/jm/spam/*"
+opts_nightly="--reuse --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/home/bbmass/rawcor/jm/ham/* --after="15552000" --tail=40000 --scanprob=0.3 spam:detect:/home/bbmass/rawcor/jm/spam/*"
tmp=$HOME/tmp
tree=$HOME/svn
prefs_weekly=$HOME/user_prefs.weekly
prefs_nightly=$HOME/user_prefs.nightly
username=bb-jm
password=__RSYNC_PASSWORD__
-serverhost=spamassassin.zones.apache.org:38891
+serverhost=spamassassin.zones.apache.org.:38891
clienthosts=__CLIENTHOSTS__
clienttree=nightlymc_jm
diff --git a/build/nightlymc/corpus.zmi b/build/nightlymc/corpus.zmi
index 45e237e..2299717 100644
--- a/build/nightlymc/corpus.zmi
+++ b/build/nightlymc/corpus.zmi
@@ -1,11 +1,11 @@
-opts_weekly="--net --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 --after="15552000" --tail=15000 --net -j 8 -f /home/bbmass/mc-nightly/targets.zmi"
-opts_nightly="--cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 --after="15552000" --tail=15000 -f /home/bbmass/mc-nightly/targets.zmi"
+opts_weekly="--net -j 8 --reuse --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/export/home/bbmass/rawcor/zmi/ham/* --after="15552000" --tail=25000 spam:detect:/export/home/bbmass/rawcor/zmi/spam/*"
+opts_nightly=" --reuse --cache --cachedir=/tmpfs/aicache_nightly --cs_schedule_cache --cs_cachedir=/export/home/bbmass/cache --restart=500 ham:detect:/export/home/bbmass/rawcor/zmi/ham/* --after="15552000" --tail=25000 spam:detect:/export/home/bbmass/rawcor/zmi/spam/*"
tmp=$HOME/tmp
tree=$HOME/svn
prefs_weekly=$HOME/user_prefs.weekly
prefs_nightly=$HOME/user_prefs.nightly
username=bb-zmi
password=__RSYNC_PASSWORD__
-serverhost=spamassassin.zones.apache.org:38894
+serverhost=spamassassin.zones.apache.org.:38894
clienthosts=__CLIENTHOSTS__
clienttree=nightlymc_zmi
diff --git a/build/update_devel b/build/update_devel
index 150a43d..1974ed1 100755
--- a/build/update_devel
+++ b/build/update_devel
@@ -4,17 +4,15 @@
umask 002
-WEBDIR=$SA_WEBDIR
-test -n "$WEBDIR" || WEBDIR=$HOME/site
+WEBDIR=$HOME/public_html
if [ ! -d $WEBDIR ] ; then
echo "WARNING: $WEBDIR does not exist" 1>&2
echo 1>&2
fi
-DEVDIR=$WEBDIR/devel
-RELDIR=$WEBDIR/released
-mkdir -p $DEVDIR $RELDIR
+RELDIR=$WEBDIR/devel
+mkdir -p $RELDIR
DISTNAME=Mail-SpamAssassin
@@ -32,9 +30,6 @@
DISTDNAME=$DISTNAME-$DIST_VERSION
DISTVNAME=$DISTNAME-$VERSION
-# delete old CVS files
-find . -name '.#*' -print | xargs rm -f
-
make || exit $?
make text_html_doc
@@ -52,15 +47,15 @@
zip -rv - $DISTDNAME > $DISTVNAME.zip || exit $?
for ext in tar.bz2 tar.gz zip ; do
- cp $DISTVNAME.$ext $DEVDIR
+ cp $DISTVNAME.$ext $RELDIR
- perl build/md5sum.pl $DISTVNAME.$ext > $DEVDIR/$DISTVNAME.$ext.md5 || exit $?
- perl build/sha1sum.pl $DISTVNAME.$ext > $DEVDIR/$DISTVNAME.$ext.sha1 || exit $?
+ perl build/md5sum.pl $DISTVNAME.$ext > $RELDIR/$DISTVNAME.$ext.md5 || exit $?
+ perl build/sha1sum.pl $DISTVNAME.$ext > $RELDIR/$DISTVNAME.$ext.sha1 || exit $?
rm -f $DISTVNAME.$ext.asc*
if [ -d $HOME/sabuildtools/sasigningkey ]; then
$HOME/sabuildtools/bin/gpg --homedir $HOME/sabuildtools/sasigningkey -bsa $DISTVNAME.$ext || exit $?
- mv $DISTVNAME.$ext.asc $DEVDIR/$DISTVNAME.$ext.asc || exit $?
+ mv $DISTVNAME.$ext.asc $RELDIR/$DISTVNAME.$ext.asc || exit $?
fi
rm -f $DISTVNAME.$ext
@@ -69,5 +64,5 @@
test -f Makefile && make distclean
rm -f $DISTVNAME.*
-chgrp -R spamassassin $DEVDIR $RELDIR
-ls -l $DEVDIR
+chgrp -R spamassassin $RELDIR
+ls -l $RELDIR
diff --git a/build/update_stable b/build/update_stable
index e3f98af..ce9cc79 100755
--- a/build/update_stable
+++ b/build/update_stable
@@ -9,15 +9,6 @@
rm -rf $WEBDIR/doc
tar cf - --exclude=CVS --exclude='.#*' doc | ( cd $WEBDIR ; tar xf - )
-for ext in tar.bz2 tar.gz zip ; do
- for fxt in '' .asc .md5 .sha1 ; do
- rm -f $RELDIR/$DISTVNAME.$ext$fxt
- cp -p $DEVDIR/$DISTVNAME.$ext$fxt $RELDIR || exit $?
- rm -f $DEVDIR/$DISTVNAME.$ext$fxt
- done
-done
-
-
set +x
echo
diff --git a/lib/Mail/SpamAssassin/AsyncLoop.pm b/lib/Mail/SpamAssassin/AsyncLoop.pm
index 7eadce8..0912889 100644
--- a/lib/Mail/SpamAssassin/AsyncLoop.pm
+++ b/lib/Mail/SpamAssassin/AsyncLoop.pm
@@ -48,8 +48,10 @@
use vars qw($timer_resolution);
eval {
require Time::HiRes or die "Error loading Time::HiRes: $@, $!";
- Time::HiRes->import( qw(time CLOCK_REALTIME) );
- $timer_resolution = Time::HiRes::clock_getres(CLOCK_REALTIME());
+ Time::HiRes->import( qw(time) );
+ $timer_resolution = Time::HiRes->can('clock_getres')
+ ? Time::HiRes::clock_getres(Time::HiRes::CLOCK_REALTIME())
+ : 0.001; # wild guess, assume resolution is better than 1s
1;
} or do {
$timer_resolution = 1; # Perl's builtin timer ticks at one second
diff --git a/lib/Mail/SpamAssassin/BayesStore/DBM.pm b/lib/Mail/SpamAssassin/BayesStore/DBM.pm
index a3b144b..334ebfb 100644
--- a/lib/Mail/SpamAssassin/BayesStore/DBM.pm
+++ b/lib/Mail/SpamAssassin/BayesStore/DBM.pm
@@ -27,6 +27,7 @@
use Mail::SpamAssassin::Util qw(untaint_var);
use Mail::SpamAssassin::BayesStore;
use Mail::SpamAssassin::Logger;
+use Mail::SpamAssassin::Timeout;
use Digest::SHA1 qw(sha1);
use File::Basename;
use File::Spec;
@@ -169,23 +170,41 @@
my $db_var = 'db_'.$dbname;
dbg("bayes: tie-ing to DB file R/O $name");
- # untie %{$self->{$db_var}} if (tied %{$self->{$db_var}});
- if (!tie %{$self->{$db_var}},$self->DBM_MODULE, $name, O_RDONLY,
- (oct($main->{conf}->{bayes_file_mode}) & 0666))
- {
- # bug 2975: it's acceptable for the db_seen to not be present,
- # to allow it to be recycled. if that's the case, just create
- # a new, empty one. we don't need to lock it, since we won't
- # be writing to it; let the R/W api deal with that case.
+ # bug 5731: something in DB_File appears to hang on tie() on gutsy
+ my $err;
+ dbg("starting tie timeout at ".(scalar localtime time));
+ my $timer = Mail::SpamAssassin::Timeout->new({ secs => 30 });
+ $timer->run_and_catch(sub {
- if ($dbname eq 'seen') {
- tie %{$self->{$db_var}},$self->DBM_MODULE, $name, O_RDWR|O_CREAT,
- (oct($main->{conf}->{bayes_file_mode}) & 0666)
- or goto failed_to_tie;
+ # untie %{$self->{$db_var}} if (tied %{$self->{$db_var}});
+ if (!tie %{$self->{$db_var}},$self->DBM_MODULE, $name, O_RDONLY,
+ (oct($main->{conf}->{bayes_file_mode}) & 0666))
+ {
+ # bug 2975: it's acceptable for the db_seen to not be present,
+ # to allow it to be recycled. if that's the case, just create
+ # a new, empty one. we don't need to lock it, since we won't
+ # be writing to it; let the R/W api deal with that case.
+
+ if ($dbname eq 'seen') {
+ tie %{$self->{$db_var}},$self->DBM_MODULE, $name, O_RDWR|O_CREAT,
+ (oct($main->{conf}->{bayes_file_mode}) & 0666)
+ or $err++;
+ }
+ else {
+ $err++;
+ }
}
- else {
- goto failed_to_tie;
- }
+
+ dbg("tie returned: ".(scalar localtime time));
+ });
+ dbg("timer returned: ".(scalar localtime time));
+
+ if ($timer->timed_out()) {
+ warn "bayes: DB_File tie() call timed out after 30 seconds";
+ goto failed_to_tie;
+ }
+ if ($err) {
+ goto failed_to_tie;
}
}
@@ -281,12 +300,30 @@
my $db_var = 'db_'.$dbname;
dbg("bayes: tie-ing to DB file R/W $name");
- ($self->DBM_MODULE eq 'DB_File') and
- Mail::SpamAssassin::Util::avoid_db_file_locking_bug ($name);
+ # bug 5731: something in DB_File appears to hang on tie() on gutsy
+ my $err;
+ dbg("starting tie timeout at ".(scalar localtime time));
+ my $timer = Mail::SpamAssassin::Timeout->new({ secs => 30 });
+ $timer->run_and_catch(sub {
- tie %{$self->{$db_var}},$self->DBM_MODULE, $name, O_RDWR|O_CREAT,
- (oct($main->{conf}->{bayes_file_mode}) & 0666)
- or goto failed_to_tie;
+ ($self->DBM_MODULE eq 'DB_File') and
+ Mail::SpamAssassin::Util::avoid_db_file_locking_bug ($name);
+
+ tie %{$self->{$db_var}},$self->DBM_MODULE, $name, O_RDWR|O_CREAT,
+ (oct($main->{conf}->{bayes_file_mode}) & 0666)
+ or $err++;
+
+ dbg("tie returned: ".(scalar localtime time));
+ });
+ dbg("timer returned: ".(scalar localtime time));
+
+ if ($timer->timed_out()) {
+ warn "bayes: DB_File tie() call timed out after 30 seconds";
+ goto failed_to_tie;
+ }
+ if ($err) {
+ goto failed_to_tie;
+ }
}
umask $umask;
diff --git a/lib/Mail/SpamAssassin/Conf/Parser.pm b/lib/Mail/SpamAssassin/Conf/Parser.pm
index 3abd784..fd650f7 100644
--- a/lib/Mail/SpamAssassin/Conf/Parser.pm
+++ b/lib/Mail/SpamAssassin/Conf/Parser.pm
@@ -546,7 +546,8 @@
while ( my($sk) = each %{$conf->{scores}} ) {
if (!exists $conf->{tests}->{$sk}) {
- $self->lint_warn("config: warning: score set for non-existent rule $sk\n", $sk);
+ # bug 5514: not a lint warning any more
+ dbg("config: warning: score set for non-existent rule $sk");
}
}
}
diff --git a/lib/Mail/SpamAssassin/DBBasedAddrList.pm b/lib/Mail/SpamAssassin/DBBasedAddrList.pm
index 20eb8b2..e7da285 100644
--- a/lib/Mail/SpamAssassin/DBBasedAddrList.pm
+++ b/lib/Mail/SpamAssassin/DBBasedAddrList.pm
@@ -26,12 +26,9 @@
use Mail::SpamAssassin::PersistentAddrList;
use Mail::SpamAssassin::Util;
use Mail::SpamAssassin::Logger;
+use Mail::SpamAssassin::Timeout;
-use vars qw{
- @ISA
-};
-
-@ISA = qw(Mail::SpamAssassin::PersistentAddrList);
+our @ISA = qw(Mail::SpamAssassin::PersistentAddrList);
###########################################################################
@@ -83,13 +80,30 @@
dbg("auto-whitelist: tie-ing to DB file of type $dbm_module $mod1 in $path");
- ($self->{is_locked} && $dbm_module eq 'DB_File') and
- Mail::SpamAssassin::Util::avoid_db_file_locking_bug ($path);
+ # bug 5731: something in DB_File appears to hang on tie() on gutsy
+ my $err;
+ my $tied;
+ dbg("starting tie timeout at ".(scalar localtime time));
+ my $timer = Mail::SpamAssassin::Timeout->new({ secs => 30 });
+ $timer->run_and_catch(sub {
- if (! tie %{ $self->{accum} }, $dbm_module, $path, $mod2,
- oct($main->{conf}->{auto_whitelist_file_mode}) )
- {
- my $err = $!; # might get overwritten later
+ ($self->{is_locked} && $dbm_module eq 'DB_File') and
+ Mail::SpamAssassin::Util::avoid_db_file_locking_bug ($path);
+
+ $tied = tie %{ $self->{accum} }, $dbm_module, $path, $mod2,
+ oct($main->{conf}->{auto_whitelist_file_mode});
+ $err = $!;
+
+ dbg("tie returned: ".(scalar localtime time));
+ });
+ dbg("timer returned: ".(scalar localtime time));
+
+ if ($timer->timed_out() || !$tied) {
+ if ($timer->timed_out()) {
+ warn "auto-whitelist: DB_File tie() call timed out after 30 seconds";
+ $err = "timed out";
+ }
+
if ($self->{is_locked}) {
$self->{main}->{locker}->safe_unlock($self->{locked_file});
$self->{is_locked} = 0;
diff --git a/lib/Mail/SpamAssassin/Dns.pm b/lib/Mail/SpamAssassin/Dns.pm
index 85a35af..865fb7d 100644
--- a/lib/Mail/SpamAssassin/Dns.pm
+++ b/lib/Mail/SpamAssassin/Dns.pm
@@ -632,12 +632,6 @@
# DNS if we're only supposed to be looking at local tests.
goto done if ($self->{main}->{local_tests_only});
- if ($dnsopt eq "yes") {
- $IS_DNS_AVAILABLE = 1;
- dbg("dns: dns_available set to yes in config file, skipping test");
- return $IS_DNS_AVAILABLE;
- }
-
# Check version numbers - runtime check only
if (defined $Net::DNS::VERSION) {
if (Mail::SpamAssassin::Util::am_running_on_windows()) {
@@ -657,6 +651,22 @@
$self->clear_resolver();
goto done unless $self->load_resolver();
+ my @nameservers = $self->{resolver}->nameservers();
+
+ # optionally shuffle the list of nameservers to distribute the load
+ if ($self->{conf}->{dns_options}->{rotate}) {
+ Mail::SpamAssassin::Util::fisher_yates_shuffle(\@nameservers);
+ dbg("dns: shuffled NS list: ".join(", ", @nameservers));
+ $self->{resolver}->nameservers(@nameservers);
+ $self->{resolver}->connect_sock();
+ }
+
+ if ($dnsopt eq "yes") {
+ $IS_DNS_AVAILABLE = 1;
+ dbg("dns: dns_available set to yes in config file, skipping test");
+ return $IS_DNS_AVAILABLE;
+ }
+
if ($dnsopt =~ /test:\s+(.+)$/) {
my $servers=$1;
dbg("dns: servers: $servers");
@@ -667,15 +677,10 @@
@domains = @EXISTING_DOMAINS;
}
- # TODO: retry every now and again if we get this far, but the
- # next test fails? could be because the ethernet cable has
- # simply fallen out ;)
-
# Net::DNS::Resolver scans a list of nameservers when it does a foreground
# query but only uses the first in a background query like we use.
# Try the different nameservers here in case the first one is not working
-
- my @nameservers = $self->{resolver}->nameservers();
+
my @good_nameservers = ();
dbg("dns: testing resolver nameservers: " . join(", ", @nameservers));
my $ns;
@@ -710,9 +715,6 @@
if ($IS_DNS_AVAILABLE == 1)
{
- if ($self->{conf}->{dns_options}->{rotate}) {
- Mail::SpamAssassin::Util::fisher_yates_shuffle(\@good_nameservers);
- }
dbg("dns: NS list: ".join(", ", @good_nameservers));
$self->{resolver}->nameservers(@good_nameservers);
$self->{resolver}->connect_sock();
diff --git a/lib/Mail/SpamAssassin/HTML.pm b/lib/Mail/SpamAssassin/HTML.pm
index ea52b93..dd31096 100644
--- a/lib/Mail/SpamAssassin/HTML.pm
+++ b/lib/Mail/SpamAssassin/HTML.pm
@@ -668,17 +668,21 @@
if ($display{whitespace}) {
# trim trailing whitespace from previous element if it was not whitespace
+ # and it was not invisible
if (@{ $self->{text} } &&
(!defined $self->{text_whitespace} ||
- !vec($self->{text_whitespace}, $#{$self->{text}}, 1)))
+ !vec($self->{text_whitespace}, $#{$self->{text}}, 1)) &&
+ (!defined $self->{text_invisible} ||
+ !vec($self->{text_invisible}, $#{$self->{text}}, 1)))
{
$self->{text}->[-1] =~ s/ $//;
}
}
else {
$text =~ s/[ \t\n\r\f\x0b\xa0]+/ /g;
- # trim leading whitespace if previous element was whitespace
- if (@{ $self->{text} } &&
+ # trim leading whitespace if previous element was whitespace
+ # and current element is not invisible
+ if (@{ $self->{text} } && !$display{invisible} &&
defined $self->{text_whitespace} &&
vec($self->{text_whitespace}, $#{$self->{text}}, 1))
{
diff --git a/lib/Mail/SpamAssassin/Message/Metadata/Received.pm b/lib/Mail/SpamAssassin/Message/Metadata/Received.pm
index 69b2b89..fc84494 100644
--- a/lib/Mail/SpamAssassin/Message/Metadata/Received.pm
+++ b/lib/Mail/SpamAssassin/Message/Metadata/Received.pm
@@ -373,8 +373,12 @@
$id = $1;
}
- if (/\bhelo=([-A-Za-z0-9\.]+)(?:[^-A-Za-z0-9\.]|$)/) { $helo = $1; }
- elsif (/\b(?:HELO|EHLO) ([-A-Za-z0-9\.]+)(?:[^-A-Za-z0-9\.]|$)/) { $helo = $1; }
+ if (/\bhelo=([-A-Za-z0-9\.\^+_&:=?!@%*\$\\\/]+)(?:[^-A-Za-z0-9\.\^+_&:=?!@%*\$\\\/]|$)/) {
+ $helo = $1;
+ }
+ elsif (/\b(?:HELO|EHLO) ([-A-Za-z0-9\.\^+_&:=?!@%*\$\\\/]+)(?:[^-A-Za-z0-9\.\^+_&:=?!@%*\$\\\/]|$)/) {
+ $helo = $1;
+ }
if (/ by (\S+)(?:[^-A-Za-z0-9\;\.]|$)/) { $by = $1; }
# ---------------------------------------------------------------------------
diff --git a/lib/Mail/SpamAssassin/Message/Node.pm b/lib/Mail/SpamAssassin/Message/Node.pm
index 48716d5..9b10cc8 100644
--- a/lib/Mail/SpamAssassin/Message/Node.pm
+++ b/lib/Mail/SpamAssassin/Message/Node.pm
@@ -306,6 +306,7 @@
seek $fd, 0, 0;
local $/ = undef;
$raw = <$fd>;
+ $raw = '' if !defined $raw;
}
else {
# create a new scalar from the raw array in memory
diff --git a/lib/Mail/SpamAssassin/Plugin/BodyRuleBaseExtractor.pm b/lib/Mail/SpamAssassin/Plugin/BodyRuleBaseExtractor.pm
index ae40c34..34728ed 100644
--- a/lib/Mail/SpamAssassin/Plugin/BodyRuleBaseExtractor.pm
+++ b/lib/Mail/SpamAssassin/Plugin/BodyRuleBaseExtractor.pm
@@ -610,12 +610,13 @@
# we can do both, since we canonicalize to lc.
if (!$spcs && $item =~ /^EXACT/ && $args =~ /<(.*)>/)
{
- $buf .= $1;
+ my $str = $1;
+ $buf .= $str;
if ($buf =~ s/\\x\{[0-9a-fA-F]{4,}\}.*$//) {
# a high Unicode codepoint, interpreted by perl 5.8.x. cut and stop
$add_candidate->();
}
- if ($1 && length $1 >= 55 && $buf =~ s/\.\.\.$//) {
+ if (length $str >= 55 && $buf =~ s/\.\.\.$//) {
# perl 5.8.x truncates with a "..." here! cut and stop
$add_candidate->();
}
diff --git a/lib/Mail/SpamAssassin/Plugin/DCC.pm b/lib/Mail/SpamAssassin/Plugin/DCC.pm
index 42e38da..e0aa4bf 100644
--- a/lib/Mail/SpamAssassin/Plugin/DCC.pm
+++ b/lib/Mail/SpamAssassin/Plugin/DCC.pm
@@ -414,7 +414,8 @@
my $right;
my $timeout = $self->{main}->{conf}->{dcc_timeout};
my $sockpath = $self->{main}->{conf}->{dcc_dccifd_path};
- my @opts = split(' ',$self->{main}->{conf}->{dcc_options});
+ my $opts = $self->{main}->{conf}->{dcc_options};
+ my @opts = !defined $opts ? () : split(' ',$opts);
$count{body} = 0;
$count{fuz1} = 0;
@@ -481,6 +482,7 @@
dbg("dcc: dccifd check failed - no X-DCC returned: $response");
return 0;
}
+ $response =~ s/[ \t]\z//; # strip trailing whitespace
if ($response =~ /^X-DCC-(.*)-Metrics: (.*)$/) {
$permsgstatus->{tag_data}->{DCCB} = $1;
@@ -538,7 +540,8 @@
# note: not really tainted, this came from system configuration file
my $path = Mail::SpamAssassin::Util::untaint_file_path($self->{main}->{conf}->{dcc_path});
- my @opts = split(' ',$self->{main}->{conf}->{dcc_options}||'');
+ my $opts = $self->{main}->{conf}->{dcc_options};
+ my @opts = !defined $opts ? () : split(' ',$opts);
untaint_var(\@opts);
unshift(@opts, "-a",
@@ -693,7 +696,8 @@
my $timeout = $self->{main}->{conf}->{dcc_timeout};
my $sockpath = $self->{main}->{conf}->{dcc_dccifd_path};
# instead of header use whatever the report option is
- my @opts = split(' ',$self->{main}->{conf}->{dcc_options});
+ my $opts = $self->{main}->{conf}->{dcc_options};
+ my @opts = !defined $opts ? () : split(' ',$opts);
$options->{report}->enter_helper_run_mode();
my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
@@ -749,7 +753,8 @@
# note: not really tainted, this came from system configuration file
my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{dcc_path});
- my @opts = split(' ',$self->{main}->{conf}->{dcc_options});
+ my $opts = $self->{main}->{conf}->{dcc_options};
+ my @opts = !defined $opts ? () : split(' ',$opts);
untaint_var(\@opts);
# get the metadata from the message so we can pass the external relay info
diff --git a/lib/Mail/SpamAssassin/Plugin/DKIM.pm b/lib/Mail/SpamAssassin/Plugin/DKIM.pm
index 92dc24d..d78934f 100644
--- a/lib/Mail/SpamAssassin/Plugin/DKIM.pm
+++ b/lib/Mail/SpamAssassin/Plugin/DKIM.pm
@@ -23,7 +23,10 @@
loadplugin Mail::SpamAssassin::Plugin::DKIM [/path/to/DKIM.pm]
- full DOMAINKEY_DOMAIN eval:check_dkim_verified()
+ full DKIM_VALID eval:check_dkim_valid()
+ full DKIM_VALID_AU eval:check_dkim_valid_author_sig()
+
+(for compatibility, a check_dkim_verified is a synonym for check_dkim_valid)
=head1 DESCRIPTION
@@ -38,7 +41,7 @@
=head1 TAGS
The following tags are added to the set, available for use in reports,
-headers, other plugins, etc.:
+header fields, other plugins, etc.:
_DKIMIDENTITY_ signing identities (the 'i' tag) from valid signatures;
_DKIMDOMAIN_ signing domains (the 'd' tag) from valid signatures;
@@ -87,7 +90,9 @@
bless ($self, $class);
$self->register_eval_rule ("check_dkim_signed");
- $self->register_eval_rule ("check_dkim_verified");
+ $self->register_eval_rule ("check_dkim_verified"); # old synonym for _valid
+ $self->register_eval_rule ("check_dkim_valid");
+ $self->register_eval_rule ("check_dkim_valid_author_sig");
$self->register_eval_rule ("check_dkim_signsome");
$self->register_eval_rule ("check_dkim_testing");
$self->register_eval_rule ("check_dkim_signall");
@@ -109,27 +114,27 @@
=over 4
-=item whitelist_from_dkim originator@example.com [signing-identity]
+=item whitelist_from_dkim author@example.com [signing-identity]
-Use this to supplement the whitelist_from addresses with a check to make sure
-the message with a given From: author's address (originator address) carries
-a valid Domain Keys Identified Mail (DKIM) signature by a verifier-acceptable
+Use this to supplement the whitelist_from addresses with a check to make
+sure the message with a given From address (the author's address) carries a
+valid Domain Keys Identified Mail (DKIM) signature by a verifier-acceptable
signing-identity (the i= tag).
Only one whitelist entry is allowed per line, as in C<whitelist_from_rcvd>.
-Multiple C<whitelist_from_dkim> lines are allowed. File-glob style
-meta characters are allowed for the From: address (the first parameter),
-just like with C<whitelist_from_rcvd>.
+Multiple C<whitelist_from_dkim> lines are allowed. File-glob style characters
+are allowed for the From address (the first parameter), just like with
+C<whitelist_from_rcvd>. The second parameter does not accept wildcards.
If no signing identity parameter is specified, the only acceptable signature
-will be an originator signature (not a third-party signature). An originator
-signature is a signature where the signing identity of a signature matches
-the originator address (i.e. the address in a From header field).
+will be a first-party signature, i.e. the so called author signature, which
+is a signature where the signing identity of a signature matches the author
+address (i.e. the address in a From header field).
Since this whitelist requires a DKIM check to be made, network tests must
be enabled.
-Examples of whitelisting based on an originator signature:
+Examples of whitelisting based on an author signature (first-party):
whitelist_from_dkim joe@example.com
whitelist_from_dkim *@corp.example.com
@@ -143,7 +148,7 @@
whitelist_from_dkim *@info.example.com example.com
whitelist_from_dkim *@* remailer.example.com
-=item def_whitelist_from_dkim originator@example.com [signing-identity]
+=item def_whitelist_from_dkim author@example.com [signing-identity]
Same as C<whitelist_from_dkim>, but used for the default whitelist entries
in the SpamAssassin distribution. The whitelist score is lower, because
@@ -163,7 +168,7 @@
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
}
my $address = $1;
- my $identity = defined $2 ? $2 : ''; # empty implies originator signature
+ my $identity = defined $2 ? $2 : ''; # empty implies author signature
$self->{parser}->add_to_addrlist_rcvd('whitelist_from_dkim',
$address, $identity);
}
@@ -181,7 +186,7 @@
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
}
my $address = $1;
- my $identity = defined $2 ? $2 : ''; # empty implies originator signature
+ my $identity = defined $2 ? $2 : ''; # empty implies author signature
$self->{parser}->add_to_addrlist_rcvd('def_whitelist_from_dkim',
$address, $identity);
}
@@ -218,17 +223,33 @@
return $scan->{dkim_signed};
}
-# mosnomer, should be check_dkim_valid, keep for compatibility
+
+sub check_dkim_valid_author_sig {
+ my ($self, $scan) = @_;
+ $self->_check_dkim_signature($scan) unless $scan->{dkim_checked_signature};
+ return $scan->{dkim_valid_author_sig};
+}
+
+sub check_dkim_valid {
+ my ($self, $scan) = @_;
+ $self->_check_dkim_signature($scan) unless $scan->{dkim_checked_signature};
+ return $scan->{dkim_valid};
+}
+
+# mosnomer, old synonym for check_dkim_valid, kept for compatibility
sub check_dkim_verified {
my ($self, $scan) = @_;
$self->_check_dkim_signature($scan) unless $scan->{dkim_checked_signature};
return $scan->{dkim_valid};
}
+# useless, semantically always true according to the current SSP draft
sub check_dkim_signsome {
my ($self, $scan) = @_;
- $self->_check_dkim_policy($scan) unless $scan->{dkim_checked_policy};
- return $scan->{dkim_signsome};
+# $self->_check_dkim_policy($scan) unless $scan->{dkim_checked_policy};
+# return $scan->{dkim_signsome};
+ # just return false to avoid rule DKIM_POLICY_SIGNSOME always firing
+ return 0;
}
sub check_dkim_signall {
@@ -254,15 +275,15 @@
sub check_for_dkim_whitelist_from {
my ($self, $scan) = @_;
$self->_check_dkim_whitelist($scan) unless $scan->{whitelist_checked};
- return $scan->{match_in_whitelist_from_dkim} ||
- $scan->{match_in_whitelist_auth};
+ return $scan->{dkim_match_in_whitelist_from_dkim} ||
+ $scan->{dkim_match_in_whitelist_auth};
}
sub check_for_def_dkim_whitelist_from {
my ($self, $scan) = @_;
$self->_check_dkim_whitelist($scan) unless $scan->{whitelist_checked};
- return $scan->{match_in_def_whitelist_from_dkim} ||
- $scan->{match_in_def_whitelist_auth};
+ return $scan->{dkim_match_in_def_whitelist_from_dkim} ||
+ $scan->{dkim_match_in_def_whitelist_auth};
}
# ---------------------------------------------------------------------------
@@ -273,26 +294,30 @@
$scan->{dkim_checked_signature} = 1;
$scan->{dkim_signed} = 0;
$scan->{dkim_valid} = 0;
+ $scan->{dkim_valid_author_sig} = 0;
$scan->{dkim_key_testing} = 0;
+ $scan->{dkim_author_address} =
+ $scan->get('from:addr') if !defined $scan->{dkim_author_address};
my $timemethod = $self->{main}->time_method("check_dkim_signature");
- my $message = Mail::DKIM::Verifier->new_object();
- if (!$message) {
+# my $verifier = Mail::DKIM::Verifier->new(); # per new docs
+ my $verifier = Mail::DKIM::Verifier->new_object(); # old style???
+ if (!$verifier) {
dbg("dkim: cannot create Mail::DKIM::Verifier");
return;
}
- $scan->{dkim_object} = $message;
+ $scan->{dkim_object} = $verifier;
# feed content of message into verifier, using \r\n endings,
# required by Mail::DKIM API (see bug 5300)
# note: bug 5179 comment 28: perl does silly things on non-Unix platforms
# unless we use \015\012 instead of \r\n
eval {
- foreach my $line (split(/\n/s, $scan->{msg}->get_pristine)) {
- $line =~ s/\r?$/\015\012/s; # ensure \015\012 ending
- $message->PRINT($line);
- }
+ my $str = $scan->{msg}->get_pristine;
+ $str =~ s/\r?\n/\015\012/sg; # ensure \015\012 ending
+ # feeding large chunks to Mail::DKIM is much faster than line-by-line feed
+ $verifier->PRINT($str);
1;
} or do { # intercept die() exceptions and render safe
my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
@@ -306,20 +331,28 @@
my $err = $timer->run_and_catch(sub {
dbg("dkim: performing public key lookup and signature verification");
- $message->CLOSE(); # the action happens here
+ $verifier->CLOSE(); # the action happens here
- $scan->{dkim_address} = !$message->message_originator ? ''
- : $message->message_originator->address();
- dbg("dkim: originator: ".
- ($scan->{dkim_address} ? $scan->{dkim_address} : 'none'));
+ my $author = $verifier->message_originator;
+ $author = $author->address() if $author;
+ $author = '' if !defined $author; # when a From header field is missing
+ # Mail::DKIM sometimes leaves leading or trailing whitespace in address
+ $author =~ s/^[ \t]+//s; $author =~ s/[ \t]+\z//s; # trim
+ if ($author ne $scan->{dkim_author_address}) {
+ dbg("dkim: author parsing inconsistency, SA: <%s>, DKIM: <%s>",
+ $author, $scan->{dkim_author_address});
+ # currently SpamAssassin's parsing is better than Mail::Address parsing
+ # $scan->{dkim_author_address} = $author;
+ }
$scan->{dkim_signatures} = [];
# versions before 0.29 only provided a public interface to fetch one
# signature, new versions allow access to all signatures of a message
- my @signatures = Mail::DKIM->VERSION >= 0.29 ? $message->signatures
- : $message->signature;
+ my @signatures = Mail::DKIM->VERSION >= 0.29 ? $verifier->signatures
+ : $verifier->signature;
@signatures = grep { defined } @signatures; # just in case
+ my $has_author_sig = 0;
foreach my $signature (@signatures) {
# i= Identity of the user or agent (e.g., a mailing list manager) on
# behalf of which this message is signed (dkim-quoted-printable;
@@ -336,45 +369,58 @@
$identity = '@' . $identity;
$signature->identity($identity);
}
+ if ($signature->result eq 'pass') {
+ local ($1); # check if we have a valid first-party signature
+ if ($identity =~ /.\@[^@]*\z/s) { # identity has a localpart
+ $has_author_sig = 1 if lc($author) eq lc($identity);
+ } elsif ($author =~ /^.*?(\@[^\@]*)?\z/s && lc($1) eq lc($identity)) {
+ # ignoring localpart if identity doesn't have a localpart
+ $has_author_sig = 1;
+ }
+ }
}
$scan->{dkim_signatures} = \@signatures;
{ my (%seen1,%seen2);
- my @v_sign = grep { $_->result eq 'pass' } @signatures;
+ my @valid_s = grep { $_->result eq 'pass' } @signatures;
$scan->set_tag('DKIMIDENTITY',
- join(" ", grep { !$seen1{$_}++ } map { $_->identity } @v_sign));
+ join(" ", grep { !$seen1{$_}++ } map { $_->identity } @valid_s));
$scan->set_tag('DKIMDOMAIN',
- join(" ", grep { !$seen2{$_}++ } map { $_->domain } @v_sign));
+ join(" ", grep { !$seen2{$_}++ } map { $_->domain } @valid_s));
}
- my $result = $message->result();
- my $detail = $message->result_detail();
+ # corresponds to 'best' result in case of multiple signatures
+ my $result = $verifier->result();
+ my $detail = $verifier->result_detail();
# let the result stand out more clearly in the log, use uppercase
dbg("dkim: signature verification result: ".
($detail eq 'none' ? $detail : uc $detail));
- # extract the actual lookup results
+ # check and remember verification results
if ($result eq 'pass') {
$scan->{dkim_signed} = 1;
$scan->{dkim_valid} = 1;
+ $scan->{dkim_valid_author_sig} = $has_author_sig;
}
elsif ($result eq 'fail') {
$scan->{dkim_signed} = 1;
- }
- elsif ($result eq 'none') {
- # no-op, this is the default state
+ # Returned if a valid DKIM-Signature header was found, but the
+ # signature does not contain a correct value for the message.
}
elsif ($result eq 'invalid') {
+ $scan->{dkim_signed} = 1;
# Returned if no valid DKIM-Signature headers were found,
# but there is at least one invalid DKIM-Signature header.
- dbg("dkim: invalid DKIM-Signature: $detail");
+ }
+ elsif ($result eq 'none') {
+ # no signatures, this is a default state
}
});
if ($timer->timed_out()) {
- dbg("dkim: public key lookup timed out after $timeout seconds");
+ dbg("dkim: public key lookup or verification timed out after $timeout s");
} elsif ($err) {
chomp $err;
- dbg("dkim: public key lookup failed: $err");
+ dbg("dkim: public key lookup or verification failed: $err");
}
}
@@ -385,24 +431,26 @@
$scan->{dkim_signsome} = 0;
$scan->{dkim_signall} = 0;
$scan->{dkim_policy_testing} = 0;
+ $scan->{dkim_author_address} =
+ $scan->get('from:addr') if !defined $scan->{dkim_author_address};
# must check the message first to obtain signer, domain, and verif. status
$self->_check_dkim_signature($scan) unless $scan->{dkim_checked_signature};
- my $message = $scan->{dkim_object};
+ my $verifier = $scan->{dkim_object};
my $timemethod = $self->{main}->time_method("check_dkim_policy");
- if (!$message) {
+ if (!$verifier) {
dbg("dkim: policy: dkim object not available (programming error?)");
} elsif (!$scan->is_dns_available()) {
dbg("dkim: policy: not retrieved, no DNS resolving available");
- } elsif ($scan->{dkim_valid}) { # no need to fetch policy when valid
- # draft-allman-dkim-ssp-02: If the message contains a valid Originator
+ } elsif ($scan->{dkim_valid_author_sig}) { # don't fetch policy when valid
+ # draft-allman-dkim-ssp: If the message contains a valid Author
# Signature, no Sender Signing Practices check need be performed:
# the Verifier SHOULD NOT look up the Sender Signing Practices
# and the message SHOULD be considered non-Suspicious.
- dbg("dkim: policy: not retrieved, signature is valid");
+ dbg("dkim: policy: not retrieved, author signature is valid");
} else {
my $timeout = $scan->{conf}->{dkim_timeout};
@@ -413,7 +461,7 @@
my $policy;
eval {
- $policy = $message->fetch_author_policy; 1;
+ $policy = $verifier->fetch_author_policy; 1;
} or do {
# fetching or parsing a policy may throw an error, ignore such policy
my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
@@ -423,7 +471,7 @@
if (!$policy) {
dbg("dkim: policy: none");
} else {
- my $policy_result = $policy->apply($message);
+ my $policy_result = $policy->apply($verifier);
dbg("dkim: policy result $policy_result: ".$policy->as_string());
# extract the flags we expose, from the policy
@@ -455,28 +503,36 @@
$scan->{whitelist_checked} = 1;
return unless $scan->is_dns_available();
- # if the message doesn't pass DKIM validation, it can't pass DKIM whitelist
-
- # trigger a DKIM check so we can get address/identity info
- # continue if verification succeeded or we want the debug info
- return unless $self->check_dkim_verified($scan) || would_log("dbg","dkim");
-
- my $originator = $scan->{dkim_address};
- unless ($originator) {
- dbg("dkim: check_dkim_whitelist: could not find originator address");
+ my $author = $scan->{dkim_author_address};
+ if (!defined $author) {
+ $scan->{dkim_author_address} = $author = $scan->get('from:addr');
+ }
+ if (!defined $author || $author eq '') {
+ dbg("dkim: check_dkim_whitelist: could not find author address");
return;
}
+ # collect whitelist entries matching the author from all lists
my @acceptable_identity_tuples;
- # collect whitelist entries matching the originator from all lists
$self->_wlcheck_acceptable_signature($scan, \@acceptable_identity_tuples,
'def_whitelist_from_dkim');
- $self->_wlcheck_originator_signature($scan, \@acceptable_identity_tuples,
+ $self->_wlcheck_author_signature($scan, \@acceptable_identity_tuples,
'def_whitelist_auth');
$self->_wlcheck_acceptable_signature($scan, \@acceptable_identity_tuples,
'whitelist_from_dkim');
- $self->_wlcheck_originator_signature($scan, \@acceptable_identity_tuples,
+ $self->_wlcheck_author_signature($scan, \@acceptable_identity_tuples,
'whitelist_auth');
+ if (!@acceptable_identity_tuples) {
+ dbg("dkim: no wl entries match author $author, no need to verify sigs");
+ return;
+ }
+
+ # if the message doesn't pass DKIM validation, it can't pass DKIM whitelist
+
+ # trigger a DKIM check so we can get address/identity info;
+ # continue if one or more signatures are valid or we want the debug info
+ return unless $self->check_dkim_valid($scan) || would_log("dbg","dkim");
+
# now do all the matching in one go, against all signatures in a message
my($any_match_at_all, $any_match_by_wl_ref) =
_wlcheck_list($self, $scan, \@acceptable_identity_tuples);
@@ -485,30 +541,28 @@
foreach my $wl (keys %$any_match_by_wl_ref) {
my $match = $any_match_by_wl_ref->{$wl};
if (defined $match) {
- $scan->{"match_in_$wl"} = 1 if $match;
+ $scan->{"dkim_match_in_$wl"} = 1 if $match;
if ($match) { push(@valid,$wl) } else { push(@fail,$wl) }
}
}
if (@valid) {
- dbg("dkim: originator %s, WHITELISTED by %s",
- $originator, join(", ",@valid));
+ dbg("dkim: author %s, WHITELISTED by %s", $author, join(", ",@valid));
} elsif (@fail) {
- dbg("dkim: originator %s, found in %s BUT IGNORED",
- $originator, join(", ",@fail));
+ dbg("dkim: author %s, found in %s BUT IGNORED", $author, join(", ",@fail));
} else {
- dbg("dkim: originator %s, not in any dkim whitelist", $originator);
+ dbg("dkim: author %s, not in any dkim whitelist", $author);
}
}
# check for verifier-acceptable signatures; an empty (or undefined) signing
-# identity in a whitelist implies checking for an originator signature
+# identity in a whitelist implies checking for an author signature
#
sub _wlcheck_acceptable_signature {
my ($self, $scan, $acceptable_identity_tuples_ref, $wl) = @_;
- my $originator = $scan->{dkim_address};
+ my $author = $scan->{dkim_author_address};
foreach my $white_addr (keys %{$scan->{conf}->{$wl}}) {
my $re = qr/$scan->{conf}->{$wl}->{$white_addr}{re}/i;
- if ($originator =~ $re) {
+ if ($author =~ $re) {
foreach my $acc_id (@{$scan->{conf}->{$wl}->{$white_addr}{domain}}) {
push(@$acceptable_identity_tuples_ref, [$acc_id,$wl,$re] );
}
@@ -517,15 +571,15 @@
}
# use a traditional whitelist_from -style addrlist, the only acceptable DKIM
-# signature is an Originator Signature. Note: don't pre-parse and store the
+# signature is an Author Signature. Note: don't pre-parse and store the
# domains; that's inefficient memory-wise and only saves one m//
#
-sub _wlcheck_originator_signature {
+sub _wlcheck_author_signature {
my ($self, $scan, $acceptable_identity_tuples_ref, $wl) = @_;
- my $originator = $scan->{dkim_address};
+ my $author = $scan->{dkim_author_address};
foreach my $white_addr (keys %{$scan->{conf}->{$wl}}) {
my $re = $scan->{conf}->{$wl}->{$white_addr};
- if ($originator =~ $re) {
+ if ($author =~ $re) {
push(@$acceptable_identity_tuples_ref, [undef,$wl,$re] );
}
}
@@ -537,7 +591,7 @@
my %any_match_by_wl;
my $any_match_at_all = 0;
my $expiration_supported = Mail::DKIM->VERSION >= 0.29 ? 1 : 0;
- my $originator = $scan->{dkim_address}; # address in a 'From' header field
+ my $author = $scan->{dkim_author_address}; # address in a From header field
# walk through all signatures present in a message
foreach my $signature (@{$scan->{dkim_signatures}}) {
@@ -555,15 +609,15 @@
$identity =~ /^ (.*?) \@ ([^\@]*) $/xs;
my($identity_mbx, $identity_dom) = ($1,$2);
- my $originator_matching_part = $originator;
+ my $author_matching_part = $author;
if ($identity =~ /^\@/) { # empty localpart in signing identity
- $originator_matching_part =~ s/^.*?(\@[^\@]*)?$/$1/s; # strip localpart
+ $author_matching_part =~ s/^.*?(\@[^\@]*)?$/$1/s; # strip localpart
}
my $info = ''; # summary info string to be used for logging
$info .= ($valid ? 'VALID' : 'FAILED') . ($expired ? ' EXPIRED' : '');
- $info .= lc $identity eq lc $originator_matching_part ? ' originator'
- : ' third-party';
+ $info .= lc $identity eq lc $author_matching_part ? ' author'
+ : ' third-party';
$info .= " signature by id " . $identity;
foreach my $entry (@$acceptable_identity_tuples_ref) {
@@ -571,18 +625,19 @@
# $re and $wl are here for logging purposes only, $re already checked.
# The $acceptable_identity is a verifier-acceptable signing identity.
# When $acceptable_identity is undef or an empty string it implies an
- # originator signature check.
+ # author signature check.
my $matches = 0;
if (!defined $acceptable_identity || $acceptable_identity eq '') {
- # An "Originator Signature" is any Valid Signature where the signing
- # identity matches the Originator Address. If the signing identity
- # does not include a localpart, then only the domains must match;
- # otherwise, the two addresses must be identical.
+ # An "Author Signature" (also called a first-party signature) is
+ # any Valid Signature where the signing identity matches the Author
+ # Address. If the signing identity does not include a localpart,
+ # then only the domains must match; otherwise, the two addresses
+ # must be identical.
- # checking for originator signature
- $matches = 1 if lc $identity eq lc $originator_matching_part;
+ # checking for author signature
+ $matches = 1 if lc $identity eq lc $author_matching_part;
}
else { # checking for verifier-acceptable signature
if ($acceptable_identity !~ /\@/) {
@@ -601,7 +656,7 @@
}
}
if ($matches) {
- dbg("dkim: $info, originator $originator, MATCHES $wl $re");
+ dbg("dkim: $info, author $author, MATCHES $wl $re");
# a defined value indicates at least a match, not necessarily valid
$any_match_by_wl{$wl} = 0 if !exists $any_match_by_wl{$wl};
}
@@ -610,8 +665,7 @@
$any_match_by_wl{$wl} = $any_match_at_all = 1 if $matches;
}
- dbg("dkim: $info, originator $originator, no valid matches")
- if !$any_match_at_all;
+ dbg("dkim: $info, author $author, no valid matches") if !$any_match_at_all;
}
return ($any_match_at_all, \%any_match_by_wl);
}
diff --git a/lib/Mail/SpamAssassin/Plugin/HeaderEval.pm b/lib/Mail/SpamAssassin/Plugin/HeaderEval.pm
index 8d27d92..035a7ab 100644
--- a/lib/Mail/SpamAssassin/Plugin/HeaderEval.pm
+++ b/lib/Mail/SpamAssassin/Plugin/HeaderEval.pm
@@ -67,6 +67,7 @@
$self->register_eval_rule("check_unresolved_template");
$self->register_eval_rule("check_ratware_name_id");
$self->register_eval_rule("check_ratware_envelope_from");
+ $self->register_eval_rule("gated_through_received_hdr_remover");
return $self;
}
diff --git a/masses/hit-frequencies b/masses/hit-frequencies
index 5788888..07a0c03 100755
--- a/masses/hit-frequencies
+++ b/masses/hit-frequencies
@@ -192,6 +192,7 @@
my $num_ham = 0;
my %ranking = ();
my $ok_lang = '';
+my %meta_subrule_pairs = ();
my %rule_times = ();
@@ -200,8 +201,8 @@
$ok_lang = lc ($opt_l || $opt_L || '');
if ($ok_lang eq 'all') { $ok_lang = '.'; }
-if ($opt_t && $rules_pl_unparseable) {
- die "-t requires rules.pl to be parseable";
+if (($opt_t || $opt_o) && $rules_pl_unparseable) {
+ die "-t/-o require rules.pl to be parseable";
}
foreach my $key (keys %rules) {
@@ -843,6 +844,9 @@
my $reverse_ratio = $1 || 0;
next unless defined $r2;
+ my $is_subrule = ($meta_subrule_pairs{"$r1.$r2"}
+ || $meta_subrule_pairs{"$r2.$r1"});
+
if ($opt_d) {
print qq{
<overlappair>
@@ -852,9 +856,10 @@
};
} else {
- printf " overlap %4s: %3d%% of %s hits also hit %s; %3d%% of %s hits also hit %s\n",
- $type, $ratio, $r2, $r1,
- $reverse_ratio, $r1, $r2;
+ printf " overlap %4s: %3d%% of %s hits also hit %s; %3d%% of %s hits also hit %s%s\n",
+ $type, $ratio, $r1, $r2,
+ $reverse_ratio, $r2, $r1,
+ ($is_subrule ? ' (meta rule and subrule)' : '');
}
}
}
@@ -865,8 +870,7 @@
}
sub _prettify_overlap_rules {
- my $rule = shift;
- my $str = shift;
+ my ($rule, $str) = @_;
my @rules = sort split(' ', $str);
if ($rules{$rule} && $rules{$rule}->{type} eq 'meta') {
@@ -879,12 +883,15 @@
@rules = grep {
my $tmp = $_;
$tmp =~ s/\[.*\]$//;
- $code !~ /\b\Q${tmp}\E\b/;
+ if ($code =~ /\b\Q${tmp}\E\b/) {
+ $meta_subrule_pairs{"$rule.$tmp"} = 1;
+ 0;
+ } else {
+ 1;
+ }
} @rules;
}
-
- my $s = join (' ', @rules);
- return $s;
+ return join (' ', @rules);
}
sub _hmap_to_overlap_ratio {
diff --git a/masses/mass-check b/masses/mass-check
index 4d0d509..7e18b8c 100755
--- a/masses/mass-check
+++ b/masses/mass-check
@@ -127,6 +127,7 @@
options used during score generation process
--learn=N learn N% of messages as spam or ham
--reuse reuse network checks if X-Spam-Status: is present in messages
+ (note: both clients and servers in c/s mode need this)
non-option arguments are used as target names (mail files and folders),
the target format is: <class>:<format>:<location>
@@ -285,40 +286,23 @@
exit;
}
-my $user_prefs = "$opt_p/user_prefs";
-
# either --net or --reuse means we should use set1/set3
my $use_net_rules = $opt_net || $opt_reuse;
# --lint
-# In theory we could probably use the same spamtest object as below,
-# but since it's probably not expecting that, and we don't want
-# strange things happening, create a local object.
if ($opt_lint) {
- my $spamlint = new Mail::SpamAssassin ({
- 'debug' => $opt_debug,
- 'rules_filename' => $opt_c,
- 'userprefs_filename' => $user_prefs,
- 'site_rules_filename' => "$opt_p/local.cf",
- 'userstate_dir' => "$opt_p",
- 'save_pattern_hits' => $opt_loghits,
- 'dont_copy_prefs' => 1,
- 'local_tests_only' => $use_net_rules ? 0 : 1,
- 'only_these_rules' => $opt_rules,
- 'ignore_safety_expire_timeout' => 1,
- 'post_config_text' => join("\n", @{$opt_cf})."\n",
- PREFIX => '',
- DEF_RULES_DIR => $opt_c,
- # TODO: it would be nicer for mass-check to not have to specify
- # this, or to use the same compiler as spamassassin, sa-update etc.
- LOCAL_RULES_DIR => '',
- });
-
+ # In theory we could probably use the same spamtest object as below,
+ # but since it's probably not expecting that, and we don't want
+ # strange things happening, create a local object.
+ my $spamlint = create_spamtest();
$spamlint->debug_diagnostics();
my $res = $spamlint->lint_rules();
$spamlint->finish();
- warn "lint: $res issues detected, please rerun with debug enabled for more information\n" if ($res);
- exit 1 if $res;
+ if ($res) {
+ warn "lint: $res issues detected, ".
+ "please rerun with debug enabled for more information\n";
+ exit 1;
+ }
}
# test messages for the mass-check
@@ -356,25 +340,31 @@
}
}
-my $spamtest = new Mail::SpamAssassin ({
- 'debug' => $opt_debug,
- 'rules_filename' => $opt_c,
- 'userprefs_filename' => $user_prefs,
- 'site_rules_filename' => "$opt_p/local.cf",
- 'userstate_dir' => "$opt_p",
- 'save_pattern_hits' => $opt_loghits,
- 'dont_copy_prefs' => 1,
- 'local_tests_only' => $use_net_rules ? 0 : 1,
- 'only_these_rules' => $opt_rules,
- 'ignore_safety_expire_timeout' => 1,
- 'post_config_text' => join("\n", @{$opt_cf})."\n",
- PREFIX => '',
- DEF_RULES_DIR => $opt_c,
- LOCAL_RULES_DIR => '',
-});
+my $user_prefs = "$opt_p/user_prefs";
+sub create_spamtest {
+ return new Mail::SpamAssassin ({
+ 'debug' => $opt_debug,
+ 'rules_filename' => $opt_c,
+ 'site_rules_filename' => "$opt_p/local.cf",
+ 'userprefs_filename' => $user_prefs,
+ 'userstate_dir' => $opt_p,
+ 'save_pattern_hits' => $opt_loghits,
+ 'dont_copy_prefs' => 1,
+ 'local_tests_only' => $use_net_rules ? 0 : 1,
+ 'only_these_rules' => $opt_rules,
+ 'ignore_safety_expire_timeout' => 1,
+ 'post_config_text' => join("\n", @{$opt_cf})."\n",
+ PREFIX => '',
+ DEF_RULES_DIR => $opt_c,
+ LOCAL_RULES_DIR => '',
+ });
+}
+
+my $spamtest = create_spamtest();
$spamtest->compile_now(0); # 0 since we will be reading more configs
$spamtest->read_scoreonly_config("$FindBin::Bin/mass-check.cf");
+$spamtest->read_scoreonly_config($user_prefs);
# generated user_prefs
if ($opt_reuse) {
diff --git a/masses/rule-qa/automc/ruleqa.cgi b/masses/rule-qa/automc/ruleqa.cgi
index a4b811d..300cadd 100755
--- a/masses/rule-qa/automc/ruleqa.cgi
+++ b/masses/rule-qa/automc/ruleqa.cgi
@@ -1253,20 +1253,22 @@
}
my %uniq=();
+ my $max_x = 0;
my $max_y = 0;
for my $i (keys %{$chart{'spam'}}, keys %{$chart{'ham'}}) {
next if exists $uniq{$i}; undef $uniq{$i};
if (($chart{'spam'}{$i}||0) > $max_y) { $max_y = $chart{'spam'}{$i}; }
if (($chart{'ham'}{$i}||0) > $max_y) { $max_y = $chart{'ham'}{$i}; }
+ if ($i > $max_x) { $max_x = $i; }
}
$max_y ||= 0.001;
+ # ensure 0 .. $max_x are always set
+ foreach my $i (0 .. $max_x) { $uniq{$i} = undef; }
+
my @idxes = sort { $a <=> $b } keys %uniq;
- my $max_x;
if (!scalar @idxes) {
$max_x = 1; @idxes = ( 0 );
- } else {
- $max_x = $idxes[scalar(@idxes) - 1];
}
my $min_x = $idxes[0];
@@ -1275,10 +1277,8 @@
my @ycoords_h = map { sprintf "%.2f", (100/$max_y) * ($chart{'ham'}{$_}||0) } @idxes;
my @xcoords = map { sprintf "%.2f", (100/$max_x) * $_ } @idxes;
- my $thresh_x;
- foreach my $i (@xcoords) {
- if ($i >= 5) { $thresh_x = $i; last; }
- }
+ my $xgrid = (100/$max_x) * 5;
+ my $ygrid = (100/$max_y) * 10;
# http://code.google.com/apis/chart/ , woo
my $chartsetup =
@@ -1289,13 +1289,14 @@
."\&chts=ff0000,18"
."\&chdl=Ham|Spam"
."\&chco=ff0000,0000ff,00ff00"
- ."\&chls=3,1,0"
- ."\&chm=V,00ff00,0,$thresh_x,1"
+ ."\&chg=$xgrid,$ygrid"
."\&chxl=0:|$min_x+points|$max_x+points|1:|0\%|$max_y\%"
."\&chxt=x,y";
- $$outref .= "<p><img src='http://chart.apis.google.com/chart?$chartsetup'
- width='400' height='200' align='right' /></p>\n";
+ $$outref .= "<div style='scoremap_chart'>
+ <img src='http://chart.apis.google.com/chart?$chartsetup'
+ style='scoremap_chart' width='400' height='200' align='right'
+ /></div>\n";
}
sub format_overlap {
@@ -1306,7 +1307,8 @@
my $out_subrules = '';
foreach my $line (split(/^/m, $ovl)) {
- my $issubrule = ($line =~ /\d+\%\s+of __/);
+ my $issubrule = ($line =~ /\d+\%\s+of __/
+ || $line =~ /\(meta rule and subrule\)/);
$line =~ s{^(\s+overlap\s+(?:ham|spam):\s+\d+% )(\S.+?)$}{
my $str = "$1";
@@ -1472,7 +1474,7 @@
$self->{cgi_params}{$k} = "$k=$v";
push (@{$self->{cgi_param_order}}, $k);
}
- $self->{q}->param(-name=>$k, -value=>$v);
+ $self->{q}->param(-name=>$k, -value=>uri_unescape($v));
}
sub get_params_except {
diff --git a/masses/rule-qa/automc/ruleqa.css b/masses/rule-qa/automc/ruleqa.css
index 217c3d7..fb42056 100644
--- a/masses/rule-qa/automc/ruleqa.css
+++ b/masses/rule-qa/automc/ruleqa.css
@@ -1,147 +1,155 @@
+/* in SVN at
- body {
- padding: 1em 1em 1em 1em;
- }
- pre.freqs {
- font-family: monospace;
- font-size: 14px;
- border: 1px dashed #ddb;
- margin: 0em -0.5em 0em -0.5em;
- padding: 10px 20px 10px 20px;
- }
- div.updateform {
- border: 3px solid #aaa;
- background: #eec;
- margin: 0em 0em 1em 0em;
- padding: 0em 1em 0em 2em;
- }
+https://svn.apache.org/repos/asf/spamassassin/trunk/masses/rule-qa/automc/ruleqa.css
- p.showfreqslink {
- color: #999;
- font-size: 50%;
- text-align: right;
- margin: 0px 0px 0px 0px;
- border: 0px 0px 0px 0px;
- }
- p.showfreqslink a { color: #999; }
+*/
- div.headdiv {
- border: 1px solid;
- background: #f0f8c0;
- margin: 0px 0px 0px 20px;
- }
- p.headclosep {
- margin: 0px 0px 0px 0px;
- border: 0px 0px 0px 0px;
- }
- pre.head {
- margin-left: 10px;
- }
-
- table.freqs {
- border: 1px dashed #ddb;
- background: #fff;
- padding: 10px 5px 10px 5px;
- }
+body {
+ padding: 1em 1em 1em 1em;
+}
+pre.freqs {
+ font-family: monospace;
+ font-size: 14px;
+ border: 1px dashed #ddb;
+ margin: 0em -0.5em 0em -0.5em;
+ padding: 10px 20px 10px 20px;
+}
+div.updateform {
+ border: 3px solid #aaa;
+ background: #eec;
+ margin: 0em 0em 1em 0em;
+ padding: 0em 1em 0em 2em;
+}
- tr.freqsline_promo1 td {
- text-align: right;
- padding: 0.1em 0.2em 0.1em 0.2em;
- }
- tr.freqsline_promo0 td {
- text-align: right;
- padding: 0.1em 0.2em 0.1em 0.2em;
- color: #999;
- }
- tr.freqsline_promo0 td a { color: #999; }
+p.showfreqslink {
+ color: #999;
+ font-size: 50%;
+ text-align: right;
+ margin: 0px 0px 0px 0px;
+ border: 0px 0px 0px 0px;
+}
+p.showfreqslink a { color: #999; }
- a.mcloghref {
- color: #999;
- font-size: 50%;
- }
+div.headdiv {
+ border: 1px solid;
+ background: #f0f8c0;
+ margin: 0px 0px 0px 20px;
+}
+p.headclosep {
+ margin: 0px 0px 0px 0px;
+ border: 0px 0px 0px 0px;
+}
+pre.head {
+ margin-left: 10px;
+}
- h3 {
- border: 1px solid;
- padding: 10px 20px 10px 20px;
- margin: 20px -20px -10px -20px;
- background: #fe8;
- }
+table.freqs {
+ border: 1px dashed #ddb;
+ background: #fff;
+ padding: 10px 5px 10px 5px;
+}
- td.daterevtd {
- font-size: 75%;
- padding: 1px 3px 1px 5px;
- }
+tr.freqsline_promo1 td {
+ text-align: right;
+ padding: 0.1em 0.2em 0.1em 0.2em;
+}
+tr.freqsline_promo0 td {
+ text-align: right;
+ padding: 0.1em 0.2em 0.1em 0.2em;
+ color: #999;
+}
+tr.freqsline_promo0 td a { color: #999; }
- td.daterevcommittd {
- font-size: 75%;
- padding: 1px 3px 1px 5px;
- background: #ffc;
- }
+tr.freqsline_promo0:hover { background: #e0e6f6; }
+tr.freqsline_promo1:hover { background: #e0e6f6; }
- td.mcviewing {
- background: #7f9;
- }
+a.mcloghref {
+ color: #999;
+ font-size: 50%;
+}
- div.commitmsgdiv {
- font-size: 75%;
- overflow: auto;
- }
+h3 {
+ border: 1px solid;
+ padding: 10px 20px 10px 20px;
+ margin: 20px -20px -10px -20px;
+ background: #fe8;
+}
- td.daterevtdempty {
- background: #eec;
- }
+td.daterevtd {
+ font-size: 75%;
+ padding: 1px 3px 1px 5px;
+}
- tr.daterevtr {
- background: #fff;
- }
+td.daterevcommittd {
+ font-size: 75%;
+ padding: 1px 3px 1px 5px;
+ background: #ffc;
+}
- tr.daterevdesc {
- background: #fea;
- }
+td.mcviewing {
+ background: #7f9;
+}
- div.ui_label {
- font-size: 75%;
- color: #676;
- }
- label.ui_label {
- font-size: 75%;
- color: #676;
- }
+div.commitmsgdiv {
+ font-size: 75%;
+ overflow: auto;
+}
+
+td.daterevtdempty {
+ background: #eec;
+}
+
+tr.daterevtr {
+ background: #fff;
+}
+
+tr.daterevdesc {
+ background: #fea;
+}
+
+div.ui_label {
+ font-size: 75%;
+ color: #676;
+}
+label.ui_label {
+ font-size: 75%;
+ color: #676;
+}
- /* Sortable tables, see http://www.kryogenix.org/code/browser/sorttable/ */
- table.sortable a.sortheader {
- background: #ddd;
- color:#666;
- font-weight: bold;
- text-decoration: none;
- display: block;
- }
- tr.freqsheader {
- background: #ddd;
- }
- table.sortable span.sortarrow {
- color: black;
- text-decoration: none;
- }
+/* Sortable tables, see http://www.kryogenix.org/code/browser/sorttable/ */
+table.sortable a.sortheader {
+ background: #ddd;
+ color:#666;
+ font-weight: bold;
+ text-decoration: none;
+ display: block;
+}
+tr.freqsheader {
+ background: #ddd;
+}
+table.sortable span.sortarrow {
+ color: black;
+ text-decoration: none;
+}
- /* mouseover data for the freqs spam% and ham% figures using CSS2.
- * see: http://www.meyerweb.com/eric/css/edge/popups/demo.html
- */
- table tr td a.ftd {
- position: relative;
- /* relative positioning so that the span will be
- * "absolute" positioned relative to this block */
- }
- table tr td a.ftd span {
- display: none;
- }
- table tr td a.ftd:hover span {
- display: block;
- position: absolute; top: 1em; left: 0.5em;
- padding: 5px 20px 5px 20px; margin: 10px; z-index: 100;
- border: 1px dashed;
- background: #ffc;
- }
+/* mouseover data for the freqs spam% and ham% figures using CSS2.
+ * see: http://www.meyerweb.com/eric/css/edge/popups/demo.html
+ */
+table tr td a.ftd {
+ position: relative;
+ /* relative positioning so that the span will be
+ * "absolute" positioned relative to this block */
+}
+table tr td a.ftd span {
+ display: none;
+}
+table tr td a.ftd:hover span {
+ display: block;
+ position: absolute; top: 1em; left: 0.5em;
+ padding: 5px 20px 5px 20px; margin: 10px; z-index: 100;
+ border: 1px dashed;
+ background: #ffc;
+}
diff --git a/masses/rule-qa/corpus-hourly b/masses/rule-qa/corpus-hourly
index a267b18..5de53b7 100755
--- a/masses/rule-qa/corpus-hourly
+++ b/masses/rule-qa/corpus-hourly
@@ -22,7 +22,7 @@
use File::Path;
use File::Copy;
use Time::ParseDate;
-use Cwd 'abs_path';
+use Cwd qw(abs_path);
use POSIX qw(nice strftime);
use constant WEEK => 7*60*60*24;
@@ -422,7 +422,6 @@
return;
}
- chdir "$opt{tree}/masses" or die "cannot chdir $opt{tree}/masses";
for my $user (sort keys %spam) {
next unless $ham{$user};
@@ -431,12 +430,13 @@
time_filter_fileset([ "$corpusdir/$spam{$user}" ],
"$opt{tmp}/spam.log.$$", $OLDEST_SPAM_WEEKS, undef);
- open(IN, "./hit-frequencies -TxpaP $flags $opt{tmp}/spam.log.$$ $opt{tmp}/ham.log.$$ |");
- while(<IN>) {
+ start_freqs($rev, "$flags $opt{tmp}/spam.log.$$ $opt{tmp}/ham.log.$$");
+
+ while(<FREQS>) {
chomp;
push @output, "$_:$user\n";
}
- close(IN);
+ close(FREQS);
system("cat $opt{tmp}/ham.log.$$ >> $opt{tmp}/hamall.log.$$");
system("cat $opt{tmp}/spam.log.$$ >> $opt{tmp}/spamall.log.$$");
@@ -447,12 +447,13 @@
return; # we'll try again later
}
- open(IN, "./hit-frequencies -TxpaP $flags $opt{tmp}/spamall.log.$$ $opt{tmp}/hamall.log.$$ |");
- while(<IN>) {
+ start_freqs($rev, "$flags $opt{tmp}/spamall.log.$$ $opt{tmp}/hamall.log.$$");
+
+ while(<FREQS>) {
/\s0\s+0\s+0.500\s+0.00\s+0.00\s+\(all messages\)/ and $no_msgs = 1;
push @output, $_;
}
- close(IN);
+ close(FREQS);
for (sort sort_all @output) { print OUT; }
}
@@ -467,13 +468,13 @@
time_filter_fileset(\@spam, "$opt{tmp}/spam.log.$$", $after, $before);
# print out by age
- chdir "$opt{tree}/masses" or die "cannot chdir $opt{tree}/masses";
- open(IN, "./hit-frequencies -TxpaP $flags $opt{tmp}/spam.log.$$ $opt{tmp}/ham.log.$$ |");
- while(<IN>) {
+ start_freqs($rev, "$flags $opt{tmp}/spam.log.$$ $opt{tmp}/ham.log.$$");
+
+ while(<FREQS>) {
chomp;
push @output, "$_:$which\n";
}
- close(IN);
+ close(FREQS);
}
for (sort sort_all @output) { print OUT; }
}
@@ -487,13 +488,13 @@
return; # we'll try again later
}
- chdir "$opt{tree}/masses" or die "cannot chdir $opt{tree}/masses";
- open(IN, "./hit-frequencies -TxpaP $flags $opt{tmp}/spam.log.$$ $opt{tmp}/ham.log.$$ |");
- while(<IN>) {
+ start_freqs($rev, "$flags $opt{tmp}/spam.log.$$ $opt{tmp}/ham.log.$$");
+
+ while(<FREQS>) {
/\s0\s+0\s+0.500\s+0.00\s+0.00\s+\(all messages\)/ and $no_msgs = 1;
print(OUT);
}
- close(IN);
+ close(FREQS);
}
$bytes = (-s OUT);
@@ -597,3 +598,32 @@
warn "'$cmd' failed";
}
}
+
+sub start_freqs {
+ my ($rev, $args) = @_;
+
+ $rev ||= 'HEAD';
+ my $hfdir = "$opt{tmp}/hfdir/r$rev";
+
+ print "setting up hit-frequencies for r$rev in $hfdir\n";
+
+ (-d "$opt{tmp}/hfdir") or system("mkdir $opt{tmp}/hfdir");
+ if (!-d $hfdir) {
+ system ("cp -pr $opt{tree} $hfdir");
+ ($?>>8 != 0) and die "cp $opt{tree}/masses $hfdir failed";
+ }
+
+ chdir "$hfdir" or die "cannot chdir $hfdir";
+ system("svn up -r$rev; svn up -r$rev rulesrc");
+
+ # ensure these are rebuilt
+ system("rm -f rules/70_sandbox.cf rules/72_active.cf");
+
+ # do this twice in case Makefile.PL is rebuilt
+ system ("( make build_rules; make build_rules ) < /dev/null");
+
+ chdir "$hfdir/masses" or die "cannot chdir $hfdir/masses";
+ open (FREQS, "./hit-frequencies -TxpaP $args |")
+ or die "cannot run ./hit-frequencies $args |";
+}
+
diff --git a/masses/rule-qa/corpus-nightly b/masses/rule-qa/corpus-nightly
index 16393de..c5f9e5b 100755
--- a/masses/rule-qa/corpus-nightly
+++ b/masses/rule-qa/corpus-nightly
@@ -2,7 +2,8 @@
# settings are located in $HOME/.corpus
-echo "Using corpus-nightly settings from $HOME/.corpus"
+echo "Using corpus-nightly settings from $HOME/.corpus at"
+date
. $HOME/.corpus
# use $PERL from env if unset
diff --git a/masses/rule-qa/corpus-nightly.post-svn b/masses/rule-qa/corpus-nightly.post-svn
index daecd0e..7c6dd64 100755
--- a/masses/rule-qa/corpus-nightly.post-svn
+++ b/masses/rule-qa/corpus-nightly.post-svn
@@ -77,6 +77,9 @@
# will exit when the server mass-check does (they "phone home"
# to the specified server host/port and exit if that disappears
# for a long time)
+
+ # just to be sure, though: run a 'stop' script in case that fails
+ ./rule-qa/nightly-slaves-stop $clienthosts
fi
date | tee test.end
diff --git a/masses/rule-qa/nightly-slave-start b/masses/rule-qa/nightly-slave-start
index 6d1e335..6ca0787 100755
--- a/masses/rule-qa/nightly-slave-start
+++ b/masses/rule-qa/nightly-slave-start
@@ -3,8 +3,6 @@
echo "Using ssh-nightly-slave settings from $HOME/.corpus"
. $HOME/.corpus
-host="$1"
-
host=`echo "$1" | perl -ne 's/:-j(\S+)$//;print'`
opts=`echo "$1" | perl -ne 's/:-j(\S+)$// and print "-j $1"'`
@@ -56,6 +54,6 @@
. /etc/profile \; \
TZ=UTC perl mass-check \
--cs_ssl --cs_verbose --cs_schedule_cache --cs_cachedir=../../cs_cache \
- $opts \
+ --reuse $opts \
--client $serverhost &
diff --git a/masses/rule-qa/nightly-slave-stop b/masses/rule-qa/nightly-slave-stop
new file mode 100755
index 0000000..21b9bf6
--- /dev/null
+++ b/masses/rule-qa/nightly-slave-stop
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+echo "Using ssh-nightly-slave settings from $HOME/.corpus"
+. $HOME/.corpus
+
+host=`echo "$1" | perl -ne 's/:-j(\S+)$//;print'`
+
+set -x
+
+# a rather rough-and-ready way to ensure any hangover mass-checks
+# from the previous set are killed, before we start these ones;
+ssh $host \
+ killall perl
+
diff --git a/masses/rule-qa/nightly-slaves-stop b/masses/rule-qa/nightly-slaves-stop
new file mode 100755
index 0000000..4d49f7a
--- /dev/null
+++ b/masses/rule-qa/nightly-slaves-stop
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+for host in $* ; do
+ rule-qa/nightly-slave-stop $host
+done
+
diff --git a/rules/20_ratware.cf b/rules/20_ratware.cf
index 37d006c..cf9c344 100644
--- a/rules/20_ratware.cf
+++ b/rules/20_ratware.cf
@@ -62,7 +62,8 @@
header __OE_MSGID_1 MESSAGEID =~ /^<[A-Za-z0-9-]{7}[A-Za-z0-9]{20}\@hotmail\.com>$/m
header __OE_MSGID_2 MESSAGEID =~ /^<(?:[0-9a-f]{8}|[0-9a-f]{12})\$[0-9a-f]{8}\$[0-9a-f]{8}\@\S+>$/m
header __OE_MSGID_3 MESSAGEID =~ /^<BAY\d+-DAV\d+[A-Z0-9]{25}\@phx\.gbl>$/m
-meta __FORGED_OE (__OE_MUA && !__OE_MSGID_1 && !__OE_MSGID_2 && !__OE_MSGID_3 && !__UNUSABLE_MSGID)
+header __OE_MSGID_4 MESSAGEID =~ /^<BAYC\d+-PASMTP\d+[A-Z0-9]{25}\@CEZ\.ICE>$/m
+meta __FORGED_OE (__OE_MUA && !__OE_MSGID_1 && !__OE_MSGID_2 && !__OE_MSGID_3 && !__OE_MSGID_4 && !__UNUSABLE_MSGID)
# Outlook versions that usually use "dollar signs"
header __OUTLOOK_DOLLARS_MUA X-Mailer =~ /^Microsoft Outlook(?: 8| CWS, Build 9|, Build 10)\./
diff --git a/rules/50_scores.cf b/rules/50_scores.cf
index 8faf212..dbaf63a 100644
--- a/rules/50_scores.cf
+++ b/rules/50_scores.cf
@@ -193,7 +193,6 @@
score FH_RE_NEW_DDD 2.251 1.209 1.526 2.687 # n=2
score FH_XMAIL_REPLACE 1.254 2.142 1.662 1.065 # n=2
score FIN_FREE 2.599 2.599 2.599 2.596
-score FM_DDDD_TIMES_2 0
score FM_DOESNT_SAY_STOCK 4.299 4.295 3.812 3.995 # n=2
score FM_FAKE_53COM_SPOOF 3.099 3.096 3.099 3.096 # n=2
score FM_FAKE_HELO_HOTMAIL 0 # n=1 n=3
@@ -214,7 +213,6 @@
score FM_SCHOOL_DIPLOMA 2.719 0.776 1.634 0.001 # n=2
score FM_SCHOOL_TYPES 3.099 3.096 3.100 3.096 # n=2
score FM_SEX_HELODDDD 0.001 2.332 2.689 1.851 # n=2
-score FM_SEX_HOSTDDDD 0
score FM_SUBJ_APPROVE 0 # n=1 n=3
score FM_TRUE_LOV_ALL_N 0 # n=1 n=2 n=3
score FM_VEGAS_CASINO 0 # n=1 n=3
diff --git a/rules/active.list b/rules/active.list
index 2b28a46..a96cf61 100644
--- a/rules/active.list
+++ b/rules/active.list
@@ -1,5 +1,5 @@
# active ruleset list, automatically generated from http://ruleqa.spamassassin.org/
-# with results from: day 1: bb-fredt cthielen dos; day 2: bb-fredt cthielen dos; day 3: bb-fredt cthielen dos
+# with results from: day 1: bb-jm bb-zmi dos jm theo zmi; day 2: bb-jm bb-zmi cthielen dos jm theo zmi; day 3: bb-jm bb-zmi cthielen dos jm theo zmi
# tflags userconf
ALL_TRUSTED
@@ -17,8 +17,14 @@
AXB_XMID_1212
# good enough
+AXB_XMID_DIGDOTSHRT
+
+# good enough
BASE64_LENGTH_78_79
+# good enough
+BASE64_LENGTH_79_INF
+
# tflags learn
BAYES_00
@@ -55,9 +61,6 @@
# tflags userconf
CORRUPT_FROM_LINE_IN_HDRS
-# good enough
-DEAR_WINNER
-
# tflags net
DIGEST_MULTIPLE
@@ -76,6 +79,12 @@
# tflags net
DNS_FROM_RFC_DSN
+# tflags publish
+DOS_ANAL_SPAM_MAILER
+
+# good enough
+DOS_FORGED_RCVD_QUADS
+
# good enough
DOS_OE_TO_MX
@@ -89,10 +98,10 @@
DOS_OUTLOOK_TO_MX_IMAGE
# good enough
-DRUGS_STOCK_MIMEOLE
+DOS_REMOVE_DOMAIN_DOT
# good enough
-DVLABS_GOZI_PDF
+DRUGS_STOCK_MIMEOLE
# good enough
DYN_RDNS_AND_INLINE_IMAGE
@@ -125,7 +134,19 @@
FAKE_HELO_YAHOO_CA
# good enough
-FB_ALMOST_SEX
+FAKE_REPLY_C
+
+# good enough
+FB_ACHIEVE_BACH
+
+# good enough
+FB_ADD_INCHES
+
+# good enough
+FB_B0NUS
+
+# good enough
+FB_CASINO
# good enough
FB_CIALIS_LEO3
@@ -137,9 +158,24 @@
FB_GVR
# good enough
+FB_LOWER_PAYM
+
+# good enough
+FB_MED1CAT
+
+# good enough
+FB_NO_SCRIP_NEEDED
+
+# good enough
+FB_P1LL
+
+# good enough
FB_QUALITY_REPLICA
# good enough
+FB_REPLICA_ROLEX
+
+# good enough
FB_REPLIC_CAP
# good enough
@@ -149,6 +185,9 @@
FB_SOFTTABS
# good enough
+FB_YOUR_REFI
+
+# good enough
FB_YOUR_WOMAN
# good enough
@@ -161,7 +200,13 @@
FH_DATE_PAST_20XX
# good enough
-FH_FROM_CASH
+FH_FAKE_RCVD_LINE
+
+# good enough
+FH_FROMEML_NOTLD
+
+# good enough
+FH_HAS_XID
# good enough
FH_HELO_ALMOST_IP
@@ -194,43 +239,61 @@
FH_MSGID_XXX
# good enough
-FM_FRM_RN_L_BRACK
+FM_DOESNT_SAY_STOCK
# good enough
-FM_LOTTO_YOU_WON
+FM_FAKE_HELO_HOTMAIL
# good enough
-FM_SCHOOLING
+FM_FAKE_HELO_VERIZON
+
+# good enough
+FM_MORTGAGE3PLUS
+
+# good enough
+FM_MULTI_LUX_GIFTS
# good enough
FM_SEX_HELODDDD
# good enough
+FM_VEGAS_CASINO
+
+# good enough
FM_XMAIL_F_OUT
# tflags userconf
FRAGMENTED_MESSAGE
# good enough
-FRT_LEVITRA
+FRT_BIGGERMEM1
+
+# good enough
+FRT_DISCOUNT
# good enough
FRT_PENIS1
# good enough
-FRT_PRICE
+FRT_REFINANCE1
+
+# good enough
+FRT_ROLEX
# good enough
FRT_SEXUAL
# good enough
+FRT_VALIUM1
+
+# good enough
FR_ALMOST_VIAG2
# good enough
-FR_WATCHES_HTTP
+FS_ABIGGER
# good enough
-FS_ABIGGER
+FS_DOLLAR_BONUS
# good enough
FS_EJACULA
@@ -242,9 +305,15 @@
FS_GET_LAID
# good enough
+FS_LOWER_YOUR
+
+# good enough
FS_MORE_CONFIDENT
# good enough
+FS_NO_SCRIP
+
+# good enough
FS_OBFU_PRMCY
# good enough
@@ -254,6 +323,9 @@
FS_REPLICAWATCH
# good enough
+FS_START_DOYOU2
+
+# good enough
FS_WILL_HELP
# good enough
@@ -299,7 +371,7 @@
HEAD_LONG
# good enough
-HELO_LH_LD
+HELO_LH_HOME
# good enough
HELO_LOCALHOST
@@ -311,12 +383,18 @@
HIGH_CODEPAGE_URI
# good enough
-HS_INDEX_PARAM
+HS_FORGED_OE_FW
# tflags userconf
HTML_CHARSET_FARAWAY
# good enough
+IMG_ALT_HSPACE_CID_ALIGN
+
+# good enough
+IMG_CID_PART1
+
+# good enough
JM_0800_GMT
# good enough
@@ -325,6 +403,9 @@
# good enough
JM_GMT_RCVD
+# tflags publish
+JM_I_FEEL_LUCKY
+
# good enough
JM_NICE_GIRL
@@ -338,6 +419,9 @@
JM_SOUGHT_3
# good enough
+JM_STORM_ECARD
+
+# good enough
JM_TORA_XM
# good enough
@@ -361,6 +445,9 @@
# good enough
LOTTERY_PH_004470
+# good enough
+MID_DEGREES
+
# tflags userconf
MIME_CHARSET_FARAWAY
@@ -380,8 +467,17 @@
NO_RELAYS
# good enough
+NULL_IN_BODY
+
+# good enough
OUTLOOK_3416
+# good enough
+PART_CID_STOCK
+
+# good enough
+PART_CID_STOCK_LESS
+
# tflags net
PYZOR_CHECK
@@ -577,6 +673,9 @@
# tflags net
RCVD_IN_XBL
+# good enough
+RCVD_MAIL_COM
+
# tflags net
ROUND_THE_WORLD
@@ -622,6 +721,9 @@
# good enough
STOCK_IMG_HTML
+# good enough
+STOX_REPLY_TYPE_WITHOUT_QUOTES
+
# tflags userconf
SUBJECT_IN_BLACKLIST
@@ -629,26 +731,38 @@
SUBJECT_IN_WHITELIST
# good enough
+SUBJ_RE_NUM
+
+# good enough
TT_OBSCURED_VIAGRA
# good enough
-TVD_PP_PHISH
+TVD_FINGER_02
+
+# good enough
+TVD_FW_GRAPHIC_ID1
+
+# good enough
+TVD_GOOG_LUCKY
# good enough
TVD_QUAL_MEDS
# good enough
+TVD_RCVD_IP
+
+# good enough
+TVD_RCVD_IP4
+
+# good enough
+TVD_RCVD_SINGLE
+
+# good enough
+TVD_STOCK1
+
+# good enough
TVD_VISIT_PHARMA
-# good enough
-FB_ACHIEVE_BACH
-
-# good enough
-FRT_BELOW2
-
-# good enough
-FRT_PHARMAC
-
# tflags net
RCVD_IN_DSBL
@@ -661,8 +775,8 @@
# tflags net
RCVD_IN_XBL
-# tflags net
-WHOIS_MONIKER_ROLE
+# good enough
+YAHOO_RDS_REDIR
# tflags userconf
UNPARSEABLE_RELAY
@@ -785,6 +899,9 @@
WHOIS_MONIKER_PRIV
# tflags net
+WHOIS_MONIKER_ROLE
+
+# tflags net
WHOIS_MYPRIVREG
# tflags net
@@ -854,6 +971,15 @@
XMAILER_MIMEOLE_OL_07794
# good enough
+XMAILER_MIMEOLE_OL_09BB4
+
+# good enough
+XMAILER_MIMEOLE_OL_1ECD5
+
+# good enough
+XMAILER_MIMEOLE_OL_20C99
+
+# good enough
XMAILER_MIMEOLE_OL_22B61
# good enough
@@ -866,6 +992,12 @@
XMAILER_MIMEOLE_OL_3AC1D
# good enough
+XMAILER_MIMEOLE_OL_3D61D
+
+# good enough
+XMAILER_MIMEOLE_OL_465CD
+
+# good enough
XMAILER_MIMEOLE_OL_4B815
# good enough
@@ -893,6 +1025,12 @@
XMAILER_MIMEOLE_OL_7533E
# good enough
+XMAILER_MIMEOLE_OL_812FF
+
+# good enough
+XMAILER_MIMEOLE_OL_83BF7
+
+# good enough
XMAILER_MIMEOLE_OL_8627E
# good enough
@@ -902,6 +1040,9 @@
XMAILER_MIMEOLE_OL_9B90B
# good enough
+XMAILER_MIMEOLE_OL_A50F8
+
+# good enough
XMAILER_MIMEOLE_OL_ADFF7
# good enough
@@ -911,12 +1052,18 @@
XMAILER_MIMEOLE_OL_B4B40
# good enough
+XMAILER_MIMEOLE_OL_B9B11
+
+# good enough
XMAILER_MIMEOLE_OL_BC7E6
# good enough
XMAILER_MIMEOLE_OL_C65FA
# good enough
+XMAILER_MIMEOLE_OL_CAC8F
+
+# good enough
XMAILER_MIMEOLE_OL_CF0C0
# good enough
@@ -931,6 +1078,9 @@
# good enough
XMAILER_MIMEOLE_OL_F6D01
+# good enough
+XMAILER_MIMEOLE_OL_FF5C8
+
# tflags net
__DNS_FROM_RFC_ABUSE
diff --git a/rules/sa-update-pubkey.txt b/rules/sa-update-pubkey.txt
index 6323ef0..b0190f3 100644
--- a/rules/sa-update-pubkey.txt
+++ b/rules/sa-update-pubkey.txt
@@ -52,8 +52,29 @@
VNqX9f7jlybpLZW/n8cQ2r1ax19v7FleO/xSGvkYm7B1+4BW0mjy6A5dta5+e5WG
D5R06Uya3/xRAPGdmV6t4Mw8fFsuyCvs+vC73PR3+eS1UvCYsDpcQD8KpVBnsHaA
duWRKKhjuFL0vdOWAr25tFOTKAj5Ywas47PBukO0isov2WBCA1rVqOr6FUvdP76y
-mqHv/0E6/vnTLxFoNsu4Ce42nAQ/A/jR
-=SY0J
+mqHv/0E6/vnTLxFoNsu4Ce42nAQ/A/jRiQQ+BBgBAgAJAhsCBQJHhbheAinBXSAE
+GQECAAYFAkOnbIoACgkQbFU5eCT0NM68MQ/8DvYqxRm3vP0Gwnr+63kzET8S+6vf
+gxOghnU+eMlqUeUu/ajqnVDMzoAIRDw9QgQc9ZZoklOSJQwOuloAbdpL4TwQ2XfJ
+MLU60JkZWnEOXJwClb0qG1GqtcBPbMEUPfZcQfphdRL3jpWZlaexFiJRSD+A0riw
+7q3NZKPDt4FrF7F3GY9krFy+P0nRt5f462DeDhCYZgguBQH+oGtjc5Hx+kOVWDsS
+txo5xkt4/0DG50ZklPkTlCohmJwRLACy+NswdQ9q83eWAhzKOPgkal7xF6a+LyE+
+ytVYy2EgEU74r2gVw5iizy92FDj//Z2QAUyf/c4BMuAhvfwVIHd8n2DPHvpMP15L
+6fwoymh0OjzmhwK94Z2u1YqNC1CK27/hfB6okQ/Tct7/Ik61dBjtiYdUC9tTA5Ze
+W8X5ouSmttS1QFixx+Z4hiXV7Qj12lgVKuJohjrVshfcbVzTHljjAo3YkOZIHIoA
+IJTUMRNzTIx9k4hrPVbxbVQhKjKTwFNtBuxvmptGTcLEIv9THpqlq8jkcStJ2Zrd
+hhofPCWRT/Kzo+WE+Kgefv88T5Li7Ku12U/UpiK85+6nRspXj3rnkfDOUbLZjGM+
+1NET0xQTPuyxN6CXF7MMxfGCpszCudYxMANDQqNXu9brcPN/+EIxGRjqin4E7q+h
+kYUaY7Ki8mXtJ8cJEEBWphpSROxFktcQALWQv996bFq1iFcGuQ0ITxNDlOWCsses
+bgEM5zR10DH+6s2bXEO8xyDHQJtrvdCPetRDosnuOToBMnGMXTYVytnWzwwAzwq1
+YM+bGAeTHaIX+2UmxwFyX4GMOdqsNB+xDZ8pmRKjamJSgUQt6e18YpZlg1Y4QkxS
+Vptq7OZBjiKeLUhLhGJ6GWgEIedLcoCtFzKCfz3zwn0Oxl+1EnVu8yqN+quWTf8P
+7EZn+0ztqZY059BrcK2jmOyXvtOZBcAHXCUknh/uPHwAJV2WFWSNid2kNiLOrV+J
+3eLTs5sF9wNhxWRhl6/10cwTzjy0Onv5cJh2tjdwksigMRMwz4c839zXORni/tnY
++IY22kNTKu84gB8rBuqUq8MQXNdS3bbROwwNUzpC0D1C1z1fBvyXDL1EwJdz70Wc
+2m/Sw6tIid5g98+XMW+Ibt43Jk2XbK71JLhbVbePbAcHVh/UXEtnjhRfX7oyWlwS
+a+lkKMiJd/6CQ6bvYsgklE7uEzTpRskpkkOcCk1O+8jfl+DsDwKrvVaNu8tpx45k
+TtV4JDA6iEHKakD/zZdVTR79W2CFqBvRfRikc5INOl1OfMQ4ODmjkMl3yI9wrHwS
+SQQxdq2XsS7xbU9HDFBEguQDu0rfzILZ9DuKIVHyr/CsRoJ5joj+JvKaUQC81ywQ
+aB8EKy5bg4U6
+=IbYW
-----END PGP PUBLIC KEY BLOCK-----
-
-
diff --git a/sa-compile.raw b/sa-compile.raw
index f5048ec..0975893 100755
--- a/sa-compile.raw
+++ b/sa-compile.raw
@@ -327,7 +327,7 @@
my $force = 1;
my $FILE = shift;
- open(my $fh, "sort $FILE |") || die "open($FILE): $!";
+ open(my $fh, $FILE) || die "open($FILE): $!";
# read ruleset name from the first line in the file
my $ruleset_name;
$_ = <$fh>;
@@ -714,8 +714,12 @@
=head1 DESCRIPTION
-sa-compile uses C<re2c> to compile the SpamAssassin ruleset. This is then used
-by the C<Mail::SpamAssassin::Plugin::Rule2XSBody> plugin to speed up
+sa-compile uses C<re2c> to compile the site-wide parts of the SpamAssassin
+ruleset. No part of user_prefs or any files included from user_prefs can be
+built into the compiled set.
+
+This compiled set is then used by the
+C<Mail::SpamAssassin::Plugin::Rule2XSBody> plugin to speed up
SpamAssassin's operation, where possible, and when that plugin is loaded.
C<re2c> can match strings much faster than perl code, by constructing a DFA to
diff --git a/sa-learn.raw b/sa-learn.raw
index 3a1ff92..21ea032 100755
--- a/sa-learn.raw
+++ b/sa-learn.raw
@@ -1091,7 +1091,7 @@
This is a database file, using C<DB_File>. The database 'version
number' is 0 for databases from 2.5x, 1 for databases from certain 2.6x
-development releases, and 2 for all more recent databases.
+development releases, 2 for 2.6x, and 3 for 3.0 and later releases.
=item bayes_seen
diff --git a/sa-update.raw b/sa-update.raw
index c11c2a5..262c5b5 100755
--- a/sa-update.raw
+++ b/sa-update.raw
@@ -415,7 +415,7 @@
my $DNSQ = "$RevSAVersion.$channel";
my $newV;
- my $dnsV = do_txt_query($DNSQ);
+ my $dnsV = join(' ', do_txt_query($DNSQ));
if (defined $dnsV && $dnsV =~ /^(\d+)/) {
$newV = $1 if (!defined $newV || $1 > $newV);
dbg("dns: $DNSQ => $dnsV, parsed as $1");
@@ -477,12 +477,20 @@
else {
# We don't currently have the list of mirrors, so go grab it.
dbg("channel: no MIRRORED.BY file available");
- my $mirror = do_txt_query("mirrors.$channel");
- unless ($mirror) {
+ my @mirrors = do_txt_query("mirrors.$channel");
+ unless (@mirrors) {
warn "error: no mirror data available for channel $channel\n";
channel_failed("channel: MIRRORED.BY file location was not in DNS");
+ next;
}
- $mirby = http_get($mirror);
+ foreach my $mirror (@mirrors) {
+ $mirby = http_get($mirror);
+ unless ($mirby) {
+ dbg("channel: no mirror data available for channel $channel from $mirror");
+ next;
+ }
+ last;
+ }
unless ($mirby) {
warn "error: no mirror data available for channel $channel\n";
channel_failed("channel: MIRRORED.BY contents were missing");
@@ -1012,25 +1020,20 @@
my($query) = shift;
my $RR = $res->query($query, 'TXT');
- my $result = '';
+ my @result;
if ($RR) {
foreach my $rr ($RR->answer) {
my $text = $rr->rdatastr;
$text =~ /^"(.*)"$/;
- if (length $result) {
- $result .= " $1";
- }
- else {
- $result = $1;
- }
+ push @result, $1;
}
}
else {
dbg("dns: query failed: $query => " . $res->errorstring);
}
- return $result;
+ return @result;
}
##############################################################################
diff --git a/t/basic_meta.t b/t/basic_meta.t
index cb4c52e..b245d30 100755
--- a/t/basic_meta.t
+++ b/t/basic_meta.t
@@ -20,7 +20,7 @@
use Test;
use Mail::SpamAssassin;
-use vars qw( %rules %scores );
+use vars qw( %rules %scores $perl_path);
# "parse-rules-for-masses" requires Data::Dumper
use constant HAS_DATADUMPER => eval 'use Data::Dumper; 1;';
@@ -39,7 +39,7 @@
unlink $output || die;
%rules = ();
%scores = ();
- if (system("$prefix/build/parse-rules-for-masses -o $output -d \"$prefix/rules\" -s $scoreset -x")) {
+ if (system("$perl_path $prefix/build/parse-rules-for-masses -o $output -d \"$prefix/rules\" -s $scoreset -x")) {
warn "parse-rules-for-masses failed!";
}
eval {
diff --git a/t/data/spam/gtubedcc.eml b/t/data/spam/gtubedcc.eml
index d2c246c..b3145cc 100644
--- a/t/data/spam/gtubedcc.eml
+++ b/t/data/spam/gtubedcc.eml
@@ -1,4 +1,4 @@
-Received: from [255.255.255.255]
+Received: from [192.168.1.1]
by mail.example.com with SMTP id gQQvHEt9CmmU
for <recipient@example.com>; Mon, 07 Oct 2002 09:00:01 +0000
Message-ID: <GTUBE1.1010101@example.com>
diff --git a/t/dkim.t b/t/dkim.t
index 6916473..56a7b08 100755
--- a/t/dkim.t
+++ b/t/dkim.t
@@ -13,6 +13,12 @@
# use constant AM_ROOT => $< == 0;
# Since the plugin is disabled by default, so are the tests
+
+# Dec 28 2007 jm: as Daryl notes at
+# http://issues.apache.org/SpamAssassin/show_bug.cgi?id=5662#c25 , this test is
+# broken, since dkim.org have removed the "testing1234" selector from their
+# DNS. TODO: we need working DKIM tests :(
+
use constant DO_RUN => TEST_ENABLED && HAS_MODULES && 0;
BEGIN {
diff --git a/t/razor2.t b/t/razor2.t
index e89343b..870bf6a 100755
--- a/t/razor2.t
+++ b/t/razor2.t
@@ -22,7 +22,7 @@
my $ident = $ENV{'HOME'}.'/.razor/identity';
if (! -r $ident) {
- $razor_not_available = "razor-register has not been run, or $ident is unreadable.";
+ $razor_not_available = "razor-register / razor-admin -register has not been run, or $ident is unreadable.";
}
%patterns = (