mirror of
https://git.proxmox.com/git/proxmox-spamassassin
synced 2025-04-28 15:29:05 +00:00
update SpamAssassin to 4.0.1
generated by make update-upstream Signed-off-by: Stoiko Ivanov <s.ivanov@proxmox.com>
This commit is contained in:
parent
10a34462c9
commit
f887dfc0c7
@ -1,4 +1,4 @@
|
|||||||
Copyright (C) 2022 The Apache Software Foundation
|
Copyright (C) 2024 The Apache Software Foundation
|
||||||
|
|
||||||
Project Management Committee (PMC):
|
Project Management Committee (PMC):
|
||||||
|
|
||||||
@ -299,6 +299,8 @@ Patch submitters:
|
|||||||
|
|
||||||
- Tomasz Ostrowski, <tometzky(at)batory.org.pl>: perl 5.005 support.
|
- Tomasz Ostrowski, <tometzky(at)batory.org.pl>: perl 5.005 support.
|
||||||
|
|
||||||
|
- Kent Oyer, <kent(at)mxguardian.net>: multiple patches: Razor2 docs, encoding, and HTML parsing.
|
||||||
|
|
||||||
- Henning P. Schmiedehausen, <hps(at)intermeta.de> <henning(at)apache.org>:
|
- Henning P. Schmiedehausen, <hps(at)intermeta.de> <henning(at)apache.org>:
|
||||||
adding ? to shell globs.
|
adding ? to shell globs.
|
||||||
|
|
||||||
@ -355,8 +357,8 @@ Patch submitters:
|
|||||||
by Mike Quinn: source of the original SpamAssassin logo.
|
by Mike Quinn: source of the original SpamAssassin logo.
|
||||||
|
|
||||||
If your name is not here, and you've submitted a patch that was included,
|
If your name is not here, and you've submitted a patch that was included,
|
||||||
it's just an oversight. Please mail me at <jm /at/ jmason.org> and I'll add
|
it's just an oversight. Please mail the PMC at <private(at)spamassassin.apache.org>
|
||||||
you to the list.
|
and we'll add you to the list.
|
||||||
|
|
||||||
ASF Sponsorship:
|
ASF Sponsorship:
|
||||||
|
|
||||||
|
4803
upstream/Changes
4803
upstream/Changes
File diff suppressed because it is too large
Load Diff
@ -123,6 +123,11 @@ lib/Mail/SpamAssassin/Plugin/WLBLEval.pm
|
|||||||
lib/Mail/SpamAssassin/Plugin/WelcomeListSubject.pm
|
lib/Mail/SpamAssassin/Plugin/WelcomeListSubject.pm
|
||||||
lib/Mail/SpamAssassin/PluginHandler.pm
|
lib/Mail/SpamAssassin/PluginHandler.pm
|
||||||
lib/Mail/SpamAssassin/Plugin/URILocalBL.pm
|
lib/Mail/SpamAssassin/Plugin/URILocalBL.pm
|
||||||
|
lib/Mail/SpamAssassin/Pyzor/Client.pm
|
||||||
|
lib/Mail/SpamAssassin/Pyzor/Digest/Pieces.pm
|
||||||
|
lib/Mail/SpamAssassin/Pyzor/Digest/StripHtml.pm
|
||||||
|
lib/Mail/SpamAssassin/Pyzor/Digest.pm
|
||||||
|
lib/Mail/SpamAssassin/Pyzor.pm
|
||||||
lib/Mail/SpamAssassin/RegistryBoundaries.pm
|
lib/Mail/SpamAssassin/RegistryBoundaries.pm
|
||||||
lib/Mail/SpamAssassin/Reporter.pm
|
lib/Mail/SpamAssassin/Reporter.pm
|
||||||
lib/Mail/SpamAssassin/SQLBasedAddrList.pm
|
lib/Mail/SpamAssassin/SQLBasedAddrList.pm
|
||||||
@ -152,6 +157,7 @@ rules/v341.pre
|
|||||||
rules/v342.pre
|
rules/v342.pre
|
||||||
rules/v343.pre
|
rules/v343.pre
|
||||||
rules/v400.pre
|
rules/v400.pre
|
||||||
|
rules/v401.pre
|
||||||
rules/20_aux_tlds.cf
|
rules/20_aux_tlds.cf
|
||||||
rules-extras/README.txt
|
rules-extras/README.txt
|
||||||
rules-extras/10_uridnsbl_skip_financial.cf
|
rules-extras/10_uridnsbl_skip_financial.cf
|
||||||
@ -247,6 +253,7 @@ t/autolearn_force.t
|
|||||||
t/autolearn_force_fail.t
|
t/autolearn_force_fail.t
|
||||||
t/basic_lint.t
|
t/basic_lint.t
|
||||||
t/basic_lint_net.t
|
t/basic_lint_net.t
|
||||||
|
t/basic_lint_without_plugins.t
|
||||||
t/basic_lint_without_sandbox.t
|
t/basic_lint_without_sandbox.t
|
||||||
t/basic_meta.t
|
t/basic_meta.t
|
||||||
t/basic_meta2.t
|
t/basic_meta2.t
|
||||||
@ -382,6 +389,7 @@ t/data/nice/orig_ip_hdr.eml
|
|||||||
t/data/nice/spf1
|
t/data/nice/spf1
|
||||||
t/data/nice/spf2
|
t/data/nice/spf2
|
||||||
t/data/nice/spf3
|
t/data/nice/spf3
|
||||||
|
t/data/nice/spf4
|
||||||
t/data/nice/spf3-received-spf
|
t/data/nice/spf3-received-spf
|
||||||
t/data/nice/spf4-received-spf-nofold
|
t/data/nice/spf4-received-spf-nofold
|
||||||
t/data/nice/spf5-received-spf-crlf
|
t/data/nice/spf5-received-spf-crlf
|
||||||
@ -418,9 +426,12 @@ t/data/spam/badmime3.txt
|
|||||||
t/data/spam/base64.txt
|
t/data/spam/base64.txt
|
||||||
t/data/spam/bsmtp
|
t/data/spam/bsmtp
|
||||||
t/data/spam/bsmtpnull
|
t/data/spam/bsmtpnull
|
||||||
|
t/data/spam/decodeshorturl/anchor.eml
|
||||||
t/data/spam/decodeshorturl/base.eml
|
t/data/spam/decodeshorturl/base.eml
|
||||||
t/data/spam/decodeshorturl/base2.eml
|
t/data/spam/decodeshorturl/base2.eml
|
||||||
t/data/spam/decodeshorturl/chain.eml
|
t/data/spam/decodeshorturl/chain.eml
|
||||||
|
t/data/spam/decodeshorturl/doubleslash.eml
|
||||||
|
t/data/spam/decodeshorturl/params.eml
|
||||||
t/data/spam/dmarc/nodmarc.eml
|
t/data/spam/dmarc/nodmarc.eml
|
||||||
t/data/spam/dmarc/noneko.eml
|
t/data/spam/dmarc/noneko.eml
|
||||||
t/data/spam/dmarc/quarko.eml
|
t/data/spam/dmarc/quarko.eml
|
||||||
@ -457,11 +468,22 @@ t/data/spam/spf1
|
|||||||
t/data/spam/spf2
|
t/data/spam/spf2
|
||||||
t/data/spam/spf3
|
t/data/spam/spf3
|
||||||
t/data/spam/unicode1
|
t/data/spam/unicode1
|
||||||
|
t/data/spam/unicode2
|
||||||
t/data/spam/urilocalbl_net.eml
|
t/data/spam/urilocalbl_net.eml
|
||||||
t/data/spamc_blank.cf
|
t/data/spamc_blank.cf
|
||||||
t/data/taintcheckplugin.pm
|
t/data/taintcheckplugin.pm
|
||||||
t/data/testplugin.pm
|
t/data/testplugin.pm
|
||||||
t/data/testplugin2.pm
|
t/data/testplugin2.pm
|
||||||
|
t/data/txrep/0
|
||||||
|
t/data/txrep/1
|
||||||
|
t/data/txrep/2
|
||||||
|
t/data/txrep/3
|
||||||
|
t/data/txrep/4
|
||||||
|
t/data/txrep/5
|
||||||
|
t/data/txrep/6
|
||||||
|
t/data/txrep/7
|
||||||
|
t/data/txrep/8
|
||||||
|
t/data/txrep/9
|
||||||
t/data/validuserplugin.pm
|
t/data/validuserplugin.pm
|
||||||
t/data/welcomelists/action.eff.org
|
t/data/welcomelists/action.eff.org
|
||||||
t/data/welcomelists/amazon_co_uk_ship
|
t/data/welcomelists/amazon_co_uk_ship
|
||||||
@ -505,6 +527,7 @@ t/dkim.t
|
|||||||
t/dnsbl.t
|
t/dnsbl.t
|
||||||
t/dnsbl_sc_meta.t
|
t/dnsbl_sc_meta.t
|
||||||
t/dnsbl_subtests.t
|
t/dnsbl_subtests.t
|
||||||
|
t/dnsplatform.t
|
||||||
t/enable_compat.t
|
t/enable_compat.t
|
||||||
t/extracttext.t
|
t/extracttext.t
|
||||||
t/freemail.t
|
t/freemail.t
|
||||||
@ -519,6 +542,7 @@ t/hashbl.t
|
|||||||
t/html_colors.t
|
t/html_colors.t
|
||||||
t/html_obfu.t
|
t/html_obfu.t
|
||||||
t/html_utf8.t
|
t/html_utf8.t
|
||||||
|
t/html_visibility.t
|
||||||
t/idn_dots.t
|
t/idn_dots.t
|
||||||
t/if_can.t
|
t/if_can.t
|
||||||
t/if_else.t
|
t/if_else.t
|
||||||
@ -583,6 +607,9 @@ t/sa_awl.t
|
|||||||
t/sa_awl_welcome_block.t
|
t/sa_awl_welcome_block.t
|
||||||
t/sa_check_spamd.t
|
t/sa_check_spamd.t
|
||||||
t/sa_compile.t
|
t/sa_compile.t
|
||||||
|
t/sa_txrep.t
|
||||||
|
t/sa_txrep_sql.t
|
||||||
|
t/sa_txrep_welcomelist_out.t
|
||||||
t/sha1.t
|
t/sha1.t
|
||||||
t/shortcircuit.t
|
t/shortcircuit.t
|
||||||
t/shortcircuit_before_dns.t
|
t/shortcircuit_before_dns.t
|
||||||
@ -658,6 +685,7 @@ t/uribl.t
|
|||||||
t/uribl_all_types.t
|
t/uribl_all_types.t
|
||||||
t/uribl_domains_only.t
|
t/uribl_domains_only.t
|
||||||
t/uribl_ips_only.t
|
t/uribl_ips_only.t
|
||||||
|
t/uridetail.t
|
||||||
t/urilocalbl.t
|
t/urilocalbl.t
|
||||||
t/utf8.t
|
t/utf8.t
|
||||||
t/util_wrap.t
|
t/util_wrap.t
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
"dynamic_config" : 1,
|
"dynamic_config" : 1,
|
||||||
"generated_by" : "ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010",
|
"generated_by" : "ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010",
|
||||||
"license" : [
|
"license" : [
|
||||||
"unknown",
|
|
||||||
"apache_2_0"
|
"apache_2_0"
|
||||||
],
|
],
|
||||||
"meta-spec" : {
|
"meta-spec" : {
|
||||||
@ -48,7 +47,7 @@
|
|||||||
"IO::String" : "0",
|
"IO::String" : "0",
|
||||||
"IP::Country::DB_File" : "0",
|
"IP::Country::DB_File" : "0",
|
||||||
"IP::Country::Fast" : "0",
|
"IP::Country::Fast" : "0",
|
||||||
"LWP::UserAgent" : "0",
|
"LWP::Protocol::https" : "0",
|
||||||
"MIME::Base64" : "0",
|
"MIME::Base64" : "0",
|
||||||
"Mail::DKIM" : "0.37",
|
"Mail::DKIM" : "0.37",
|
||||||
"Mail::DMARC" : "0",
|
"Mail::DMARC" : "0",
|
||||||
@ -67,10 +66,11 @@
|
|||||||
"Errno" : "0",
|
"Errno" : "0",
|
||||||
"File::Copy" : "2.02",
|
"File::Copy" : "2.02",
|
||||||
"File::Spec" : "0.8",
|
"File::Spec" : "0.8",
|
||||||
|
"File::Temp" : "0",
|
||||||
"HTML::Parser" : "3.43",
|
"HTML::Parser" : "3.43",
|
||||||
"IO::Zlib" : "1.04",
|
"IO::Zlib" : "1.04",
|
||||||
"Mail::DKIM" : "0.31",
|
"Mail::DKIM" : "0.31",
|
||||||
"Net::DNS" : "0.69",
|
"Net::DNS" : "1.1",
|
||||||
"NetAddr::IP" : "4.01",
|
"NetAddr::IP" : "4.01",
|
||||||
"Pod::Usage" : "1.1",
|
"Pod::Usage" : "1.1",
|
||||||
"Sys::Hostname" : "0",
|
"Sys::Hostname" : "0",
|
||||||
@ -81,14 +81,14 @@
|
|||||||
},
|
},
|
||||||
"test" : {
|
"test" : {
|
||||||
"recommends" : {
|
"recommends" : {
|
||||||
"Net::DNS::Nameserver" : "0"
|
"Devel::Cycle" : "0",
|
||||||
|
"Net::DNS::Nameserver" : "0",
|
||||||
|
"Text::Diff" : "0"
|
||||||
},
|
},
|
||||||
"requires" : {
|
"requires" : {
|
||||||
"Devel::Cycle" : "0",
|
|
||||||
"Perl::Critic::Policy::Perlsecret" : "0",
|
"Perl::Critic::Policy::Perlsecret" : "0",
|
||||||
"Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict" : "0",
|
"Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict" : "0",
|
||||||
"Test::More" : "0",
|
"Test::Simple" : "1.302067"
|
||||||
"Text::Diff" : "0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -104,6 +104,6 @@
|
|||||||
},
|
},
|
||||||
"x_MailingList" : "http://wiki.apache.org/spamassassin/MailingLists"
|
"x_MailingList" : "http://wiki.apache.org/spamassassin/MailingLists"
|
||||||
},
|
},
|
||||||
"version" : "4.000000",
|
"version" : "4.000001",
|
||||||
"x_serialization_backend" : "JSON::PP version 4.06"
|
"x_serialization_backend" : "JSON::PP version 4.06"
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,15 @@ abstract: 'Apache SpamAssassin is an extensible email filter which is used to id
|
|||||||
author:
|
author:
|
||||||
- 'The Apache SpamAssassin Project <dev@spamassassin.apache.org>'
|
- 'The Apache SpamAssassin Project <dev@spamassassin.apache.org>'
|
||||||
build_requires:
|
build_requires:
|
||||||
Devel::Cycle: '0'
|
|
||||||
ExtUtils::MakeMaker: '6.64'
|
ExtUtils::MakeMaker: '6.64'
|
||||||
Perl::Critic::Policy::Perlsecret: '0'
|
Perl::Critic::Policy::Perlsecret: '0'
|
||||||
Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict: '0'
|
Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict: '0'
|
||||||
Test::More: '0'
|
Test::Simple: '1.302067'
|
||||||
Text::Diff: '0'
|
|
||||||
configure_requires:
|
configure_requires:
|
||||||
ExtUtils::MakeMaker: '6.64'
|
ExtUtils::MakeMaker: '6.64'
|
||||||
dynamic_config: 1
|
dynamic_config: 1
|
||||||
generated_by: 'ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010'
|
generated_by: 'ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010'
|
||||||
license: unknown
|
license: apache
|
||||||
meta-spec:
|
meta-spec:
|
||||||
url: http://module-build.sourceforge.net/META-spec-v1.4.html
|
url: http://module-build.sourceforge.net/META-spec-v1.4.html
|
||||||
version: '1.4'
|
version: '1.4'
|
||||||
@ -38,7 +36,7 @@ recommends:
|
|||||||
IO::String: '0'
|
IO::String: '0'
|
||||||
IP::Country::DB_File: '0'
|
IP::Country::DB_File: '0'
|
||||||
IP::Country::Fast: '0'
|
IP::Country::Fast: '0'
|
||||||
LWP::UserAgent: '0'
|
LWP::Protocol::https: '0'
|
||||||
MIME::Base64: '0'
|
MIME::Base64: '0'
|
||||||
Mail::DKIM: '0.37'
|
Mail::DKIM: '0.37'
|
||||||
Mail::DMARC: '0'
|
Mail::DMARC: '0'
|
||||||
@ -56,10 +54,11 @@ requires:
|
|||||||
Errno: '0'
|
Errno: '0'
|
||||||
File::Copy: '2.02'
|
File::Copy: '2.02'
|
||||||
File::Spec: '0.8'
|
File::Spec: '0.8'
|
||||||
|
File::Temp: '0'
|
||||||
HTML::Parser: '3.43'
|
HTML::Parser: '3.43'
|
||||||
IO::Zlib: '1.04'
|
IO::Zlib: '1.04'
|
||||||
Mail::DKIM: '0.31'
|
Mail::DKIM: '0.31'
|
||||||
Net::DNS: '0.69'
|
Net::DNS: '1.1'
|
||||||
NetAddr::IP: '4.01'
|
NetAddr::IP: '4.01'
|
||||||
Pod::Usage: '1.1'
|
Pod::Usage: '1.1'
|
||||||
Sys::Hostname: '0'
|
Sys::Hostname: '0'
|
||||||
@ -71,5 +70,5 @@ resources:
|
|||||||
homepage: https://spamassassin.apache.org/
|
homepage: https://spamassassin.apache.org/
|
||||||
license: http://www.apache.org/licenses/LICENSE-2.0.html
|
license: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
repository: http://svn.apache.org/repos/asf/spamassassin/
|
repository: http://svn.apache.org/repos/asf/spamassassin/
|
||||||
version: '4.000000'
|
version: '4.000001'
|
||||||
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
|
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
|
||||||
|
@ -175,12 +175,13 @@ my %makefile = (
|
|||||||
'PREREQ_PM' => {
|
'PREREQ_PM' => {
|
||||||
'File::Spec' => 0.8, # older versions lack some routines we need
|
'File::Spec' => 0.8, # older versions lack some routines we need
|
||||||
'File::Copy' => 2.02, # this version is shipped with 5.005_03, the oldest version known to work
|
'File::Copy' => 2.02, # this version is shipped with 5.005_03, the oldest version known to work
|
||||||
|
'File::Temp' => 0, # core module, dependency not needed, here for testing purposes, see bug 8089
|
||||||
'Pod::Usage' => 1.10, # all versions prior to this do seem to be buggy
|
'Pod::Usage' => 1.10, # all versions prior to this do seem to be buggy
|
||||||
'HTML::Parser' => 3.43, # the HTML code is based on this parser, older versions have utf-8 bugs
|
'HTML::Parser' => 3.43, # the HTML code is based on this parser, older versions have utf-8 bugs
|
||||||
'Archive::Tar' => 1.23, # for sa-update
|
'Archive::Tar' => 1.23, # for sa-update
|
||||||
'IO::Zlib' => 1.04, # for sa-update
|
'IO::Zlib' => 1.04, # for sa-update
|
||||||
'Mail::DKIM' => 0.31,
|
'Mail::DKIM' => 0.31,
|
||||||
'Net::DNS' => 0.69,
|
'Net::DNS' => 1.10,
|
||||||
'NetAddr::IP' => 4.010,
|
'NetAddr::IP' => 4.010,
|
||||||
'Sys::Hostname' => 0,
|
'Sys::Hostname' => 0,
|
||||||
'Time::HiRes' => 0,
|
'Time::HiRes' => 0,
|
||||||
@ -198,15 +199,15 @@ my %makefile = (
|
|||||||
'ExtUtils::MakeMaker' => MIN_MAKEMAKER_VERSION,
|
'ExtUtils::MakeMaker' => MIN_MAKEMAKER_VERSION,
|
||||||
},
|
},
|
||||||
|
|
||||||
# The modules that are not core that are used in default tests
|
# The modules that are not core or that require a minimum version that are used in default tests
|
||||||
'TEST_REQUIRES' => {
|
'TEST_REQUIRES' => {
|
||||||
'Devel::Cycle' => 0,
|
'Test::Simple' => 1.302067,
|
||||||
'Test::More' => 0,
|
|
||||||
'Text::Diff' => 0,
|
|
||||||
'Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict' => 0,
|
'Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict' => 0,
|
||||||
'Perl::Critic::Policy::Perlsecret' => 0,
|
'Perl::Critic::Policy::Perlsecret' => 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'LICENSE' => 'apache_2_0',
|
||||||
|
|
||||||
'dist' => {
|
'dist' => {
|
||||||
COMPRESS => 'gzip -9f',
|
COMPRESS => 'gzip -9f',
|
||||||
SUFFIX => '.gz',
|
SUFFIX => '.gz',
|
||||||
@ -349,11 +350,9 @@ $makefile{META_MERGE} = {
|
|||||||
|
|
||||||
'meta-spec' => {
|
'meta-spec' => {
|
||||||
version => '2',
|
version => '2',
|
||||||
url => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
|
url => 'https://metacpan.org/pod/CPAN::Meta::Spec',
|
||||||
},
|
},
|
||||||
|
|
||||||
license => 'apache_2_0',
|
|
||||||
|
|
||||||
resources => {
|
resources => {
|
||||||
license => 'http://www.apache.org/licenses/LICENSE-2.0.html',
|
license => 'http://www.apache.org/licenses/LICENSE-2.0.html',
|
||||||
homepage => 'https://spamassassin.apache.org/',
|
homepage => 'https://spamassassin.apache.org/',
|
||||||
@ -387,7 +386,7 @@ $makefile{META_MERGE} = {
|
|||||||
'Mail::DKIM' => 0.37,
|
'Mail::DKIM' => 0.37,
|
||||||
'DBI' => 0,
|
'DBI' => 0,
|
||||||
'DBD::SQLite' => 1.59_01,
|
'DBD::SQLite' => 1.59_01,
|
||||||
'LWP::UserAgent' => 0,
|
'LWP::Protocol::https' => 0,
|
||||||
'Encode::Detect::Detector' => 0,
|
'Encode::Detect::Detector' => 0,
|
||||||
'Net::Patricia' => 1.16,
|
'Net::Patricia' => 1.16,
|
||||||
'Net::CIDR::Lite' => 0,
|
'Net::CIDR::Lite' => 0,
|
||||||
@ -401,6 +400,8 @@ $makefile{META_MERGE} = {
|
|||||||
test => {
|
test => {
|
||||||
recommends => {
|
recommends => {
|
||||||
'Net::DNS::Nameserver' => 0,
|
'Net::DNS::Nameserver' => 0,
|
||||||
|
'Devel::Cycle' => 0,
|
||||||
|
'Text::Diff' => 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1161,6 +1162,7 @@ conf__install:
|
|||||||
$(PERL) -MFile::Copy -e "copy(q[rules/v342.pre], q[$(B_CONFDIR)/v342.pre]) unless -f q[$(B_CONFDIR)/v342.pre]"
|
$(PERL) -MFile::Copy -e "copy(q[rules/v342.pre], q[$(B_CONFDIR)/v342.pre]) unless -f q[$(B_CONFDIR)/v342.pre]"
|
||||||
$(PERL) -MFile::Copy -e "copy(q[rules/v343.pre], q[$(B_CONFDIR)/v343.pre]) unless -f q[$(B_CONFDIR)/v343.pre]"
|
$(PERL) -MFile::Copy -e "copy(q[rules/v343.pre], q[$(B_CONFDIR)/v343.pre]) unless -f q[$(B_CONFDIR)/v343.pre]"
|
||||||
$(PERL) -MFile::Copy -e "copy(q[rules/v400.pre], q[$(B_CONFDIR)/v400.pre]) unless -f q[$(B_CONFDIR)/v400.pre]"
|
$(PERL) -MFile::Copy -e "copy(q[rules/v400.pre], q[$(B_CONFDIR)/v400.pre]) unless -f q[$(B_CONFDIR)/v400.pre]"
|
||||||
|
$(PERL) -MFile::Copy -e "copy(q[rules/v401.pre], q[$(B_CONFDIR)/v401.pre]) unless -f q[$(B_CONFDIR)/v401.pre]"
|
||||||
|
|
||||||
data__install:
|
data__install:
|
||||||
-$(MKPATH) $(B_DATADIR)
|
-$(MKPATH) $(B_DATADIR)
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
Note for Users Upgrading to SpamAssassin 4.0.1
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
- Phishstats.info domain has expired;
|
||||||
|
"phishing_phishstats_feed" and "phishing_phishstats_minscore"
|
||||||
|
options have been removed from Mail::SpamAssassin::Plugin::Phishing
|
||||||
|
plugin.
|
||||||
|
|
||||||
Note for Users Upgrading to SpamAssassin 4.0.0
|
Note for Users Upgrading to SpamAssassin 4.0.0
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
@ -252,7 +260,7 @@ improved throughout.
|
|||||||
including internal_networks, trusted_networks, msa_networks, and
|
including internal_networks, trusted_networks, msa_networks, and
|
||||||
uri_local_cidr.
|
uri_local_cidr.
|
||||||
|
|
||||||
- The HashBL plugin in 342.pre is now enabled by default.
|
- The HashBL plugin in v342.pre is now enabled by default.
|
||||||
|
|
||||||
- HeaderEval check_for_unique_subject_id() function is deprecated.
|
- HeaderEval check_for_unique_subject_id() function is deprecated.
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ use Time::HiRes qw(time);
|
|||||||
use Cwd;
|
use Cwd;
|
||||||
use Config;
|
use Config;
|
||||||
|
|
||||||
our $VERSION = "4.000000"; # update after release (same format as perl $])
|
our $VERSION = "4.000001"; # update after release (same format as perl $])
|
||||||
#our $IS_DEVEL_BUILD = 1; # 1 for devel build
|
#our $IS_DEVEL_BUILD = 1; # 1 for devel build
|
||||||
our $IS_DEVEL_BUILD = 0; # 0 for release versions including rc & pre releases
|
our $IS_DEVEL_BUILD = 0; # 0 for release versions including rc & pre releases
|
||||||
|
|
||||||
@ -100,18 +100,18 @@ our @ISA = qw();
|
|||||||
|
|
||||||
# SUB_VERSION is now just <yyyy>-<mm>-<dd>
|
# SUB_VERSION is now just <yyyy>-<mm>-<dd>
|
||||||
our $SUB_VERSION = 'svnunknown';
|
our $SUB_VERSION = 'svnunknown';
|
||||||
if ('$LastChangedDate: 2022-12-14 02:29:30 +0000 (Wed, 14 Dec 2022) $' =~ ':') {
|
if ('$LastChangedDate: 2024-03-26 17:46:03 +1300 (Tue, 26 Mar 2024) $' =~ ':') {
|
||||||
# Subversion keyword "$LastChangedDate: 2022-12-14 02:29:30 +0000 (Wed, 14 Dec 2022) $" has been successfully expanded.
|
# Subversion keyword "$LastChangedDate: 2024-03-26 17:46:03 +1300 (Tue, 26 Mar 2024) $" has been successfully expanded.
|
||||||
# Doesn't happen with automated launchpad builds:
|
# Doesn't happen with automated launchpad builds:
|
||||||
# https://bugs.launchpad.net/launchpad/+bug/780916
|
# https://bugs.launchpad.net/launchpad/+bug/780916
|
||||||
$SUB_VERSION = (split(/\s+/,'$LastChangedDate: 2022-12-14 02:29:30 +0000 (Wed, 14 Dec 2022) $ updated by SVN'))[1];
|
$SUB_VERSION = (split(/\s+/,'$LastChangedDate: 2024-03-26 17:46:03 +1300 (Tue, 26 Mar 2024) $ updated by SVN'))[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (defined $IS_DEVEL_BUILD && $IS_DEVEL_BUILD) {
|
if (defined $IS_DEVEL_BUILD && $IS_DEVEL_BUILD) {
|
||||||
if ('$LastChangedRevision: 1905971 $' =~ ':') {
|
if ('$LastChangedRevision: 1916544 $' =~ ':') {
|
||||||
# Subversion keyword "$LastChangedRevision: 1905971 $" has been successfully expanded.
|
# Subversion keyword "$LastChangedRevision: 1916544 $" has been successfully expanded.
|
||||||
push(@EXTRA_VERSION, ('r' . qw{$LastChangedRevision: 1905971 $ updated by SVN}[1]));
|
push(@EXTRA_VERSION, ('r' . qw{$LastChangedRevision: 1916544 $ updated by SVN}[1]));
|
||||||
} else {
|
} else {
|
||||||
push(@EXTRA_VERSION, ('r' . 'svnunknown'));
|
push(@EXTRA_VERSION, ('r' . 'svnunknown'));
|
||||||
}
|
}
|
||||||
@ -173,7 +173,7 @@ our @default_userstate_dir = (
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $t = Mail::SpamAssassin->new( { opt => val, ... } )
|
=item $t = Mail::SpamAssassin-E<gt>new( { opt =E<gt> val, ... } )
|
||||||
|
|
||||||
Constructs a new C<Mail::SpamAssassin> object. You may pass a hash
|
Constructs a new C<Mail::SpamAssassin> object. You may pass a hash
|
||||||
reference to the constructor which may contain the following attribute-
|
reference to the constructor which may contain the following attribute-
|
||||||
@ -272,7 +272,7 @@ in advance that some information will not be needed by a caller.
|
|||||||
A value of the option can either be a string (a comma-delimited list of tag
|
A value of the option can either be a string (a comma-delimited list of tag
|
||||||
names), or a reference to a list of individual tag names. A caller may provide
|
names), or a reference to a list of individual tag names. A caller may provide
|
||||||
the list in advance, specifying his intention to later collect the information
|
the list in advance, specifying his intention to later collect the information
|
||||||
through $pms->get_tag() calls. If a name of a tag starts with a 'NO' (case
|
through $pms-E<gt>get_tag() calls. If a name of a tag starts with a 'NO' (case
|
||||||
insensitive), it shows that a caller will not be interested in such tag,
|
insensitive), it shows that a caller will not be interested in such tag,
|
||||||
although there is no guarantee it would save any resources, nor that a tag
|
although there is no guarantee it would save any resources, nor that a tag
|
||||||
value will be empty. Currently no built-in tags start with 'NO'. A later
|
value will be empty. Currently no built-in tags start with 'NO'. A later
|
||||||
@ -289,9 +289,9 @@ Not requesting it can save a millisecond or two - it mostly serves to
|
|||||||
illustrate the usage of need_tags.
|
illustrate the usage of need_tags.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
need_tags => 'TIMING,noLANGUAGES,RELAYCOUNTRY,ASN,noASNCIDR',
|
need_tags =E<gt> 'TIMING,noLANGUAGES,RELAYCOUNTRY,ASN,noASNCIDR',
|
||||||
or:
|
or:
|
||||||
need_tags => [qw(TIMING noLANGUAGES RELAYCOUNTRY ASN noASNCIDR)],
|
need_tags =E<gt> [qw(TIMING noLANGUAGES RELAYCOUNTRY ASN noASNCIDR)],
|
||||||
|
|
||||||
=item ignore_site_cf_files
|
=item ignore_site_cf_files
|
||||||
|
|
||||||
@ -561,7 +561,7 @@ sub parse {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status = $f->check ($mail)
|
=item $status = $f-E<gt>check ($mail)
|
||||||
|
|
||||||
Check a mail, encapsulated in a C<Mail::SpamAssassin::Message> object,
|
Check a mail, encapsulated in a C<Mail::SpamAssassin::Message> object,
|
||||||
to determine if it is spam or not.
|
to determine if it is spam or not.
|
||||||
@ -586,7 +586,7 @@ sub check {
|
|||||||
$pms;
|
$pms;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $status = $f->check_message_text ($mailtext)
|
=item $status = $f-E<gt>check_message_text ($mailtext)
|
||||||
|
|
||||||
Check a mail, encapsulated in a plain string C<$mailtext>, to determine if it
|
Check a mail, encapsulated in a plain string C<$mailtext>, to determine if it
|
||||||
is spam or not.
|
is spam or not.
|
||||||
@ -612,7 +612,7 @@ sub check_message_text {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status = $f->learn ($mail, $id, $isspam, $forget)
|
=item $status = $f-E<gt>learn ($mail, $id, $isspam, $forget)
|
||||||
|
|
||||||
Learn from a mail, encapsulated in a C<Mail::SpamAssassin::Message> object.
|
Learn from a mail, encapsulated in a C<Mail::SpamAssassin::Message> object.
|
||||||
|
|
||||||
@ -663,7 +663,7 @@ sub learn {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->init_learner ( [ { opt => val, ... } ] )
|
=item $f-E<gt>init_learner ( [ { opt =E<gt> val, ... } ] )
|
||||||
|
|
||||||
Initialise learning. You may pass the following attribute-value pairs to this
|
Initialise learning. You may pass the following attribute-value pairs to this
|
||||||
method.
|
method.
|
||||||
@ -735,7 +735,7 @@ sub init_learner {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->rebuild_learner_caches ({ opt => val })
|
=item $f-E<gt>rebuild_learner_caches ({ opt =E<gt> val })
|
||||||
|
|
||||||
Rebuild any cache databases; should be called after the learning process.
|
Rebuild any cache databases; should be called after the learning process.
|
||||||
Options include: C<verbose>, which will output diagnostics to C<stdout>
|
Options include: C<verbose>, which will output diagnostics to C<stdout>
|
||||||
@ -750,7 +750,7 @@ sub rebuild_learner_caches {
|
|||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $f->finish_learner ()
|
=item $f-E<gt>finish_learner ()
|
||||||
|
|
||||||
Finish learning.
|
Finish learning.
|
||||||
|
|
||||||
@ -763,7 +763,7 @@ sub finish_learner {
|
|||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $f->dump_bayes_db()
|
=item $f-E<gt>dump_bayes_db()
|
||||||
|
|
||||||
Dump the contents of the Bayes DB
|
Dump the contents of the Bayes DB
|
||||||
|
|
||||||
@ -774,7 +774,7 @@ sub dump_bayes_db {
|
|||||||
$self->{bayes_scanner}->dump_bayes_db(@opts) if $self->{bayes_scanner};
|
$self->{bayes_scanner}->dump_bayes_db(@opts) if $self->{bayes_scanner};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $f->signal_user_changed ( [ { opt => val, ... } ] )
|
=item $f-E<gt>signal_user_changed ( [ { opt =E<gt> val, ... } ] )
|
||||||
|
|
||||||
Signals that the current user has changed (possibly using C<setuid>), meaning
|
Signals that the current user has changed (possibly using C<setuid>), meaning
|
||||||
that SpamAssassin should close any per-user databases it has open, and re-open
|
that SpamAssassin should close any per-user databases it has open, and re-open
|
||||||
@ -853,13 +853,13 @@ sub signal_user_changed {
|
|||||||
userstate_dir => $self->{userstate_dir},
|
userstate_dir => $self->{userstate_dir},
|
||||||
user_dir => $self->{user_dir},
|
user_dir => $self->{user_dir},
|
||||||
});
|
});
|
||||||
|
undef $self->{cf_files_read};
|
||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->report_as_spam ($mail, $options)
|
=item $f-E<gt>report_as_spam ($mail, $options)
|
||||||
|
|
||||||
Report a mail, encapsulated in a C<Mail::SpamAssassin::Message> object, as
|
Report a mail, encapsulated in a C<Mail::SpamAssassin::Message> object, as
|
||||||
human-verified spam. This will submit the mail message to live,
|
human-verified spam. This will submit the mail message to live,
|
||||||
@ -912,7 +912,7 @@ sub report_as_spam {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->revoke_as_spam ($mail, $options)
|
=item $f-E<gt>revoke_as_spam ($mail, $options)
|
||||||
|
|
||||||
Revoke a mail, encapsulated in a C<Mail::SpamAssassin::Message> object, as
|
Revoke a mail, encapsulated in a C<Mail::SpamAssassin::Message> object, as
|
||||||
human-verified ham (non-spam). This will revoke the mail message from live,
|
human-verified ham (non-spam). This will revoke the mail message from live,
|
||||||
@ -952,7 +952,7 @@ sub revoke_as_spam {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->add_address_to_welcomelist ($addr, $cli_p)
|
=item $f-E<gt>add_address_to_welcomelist ($addr, $cli_p)
|
||||||
|
|
||||||
Previously add_address_to_whitelist which will work interchangeably until 4.1.
|
Previously add_address_to_whitelist which will work interchangeably until 4.1.
|
||||||
|
|
||||||
@ -973,7 +973,7 @@ sub add_address_to_welcomelist {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->add_all_addresses_to_welcomelist ($mail, $cli_p)
|
=item $f-E<gt>add_all_addresses_to_welcomelist ($mail, $cli_p)
|
||||||
|
|
||||||
Previously add_all_addresses_to_whitelist which will work interchangeably until 4.1.
|
Previously add_all_addresses_to_whitelist which will work interchangeably until 4.1.
|
||||||
|
|
||||||
@ -996,7 +996,7 @@ sub add_all_addresses_to_welcomelist {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->remove_address_from_welcomelist ($addr, $cli_p)
|
=item $f-E<gt>remove_address_from_welcomelist ($addr, $cli_p)
|
||||||
|
|
||||||
Previously remove_address_from_whitelist which will work interchangeably until 4.1.
|
Previously remove_address_from_whitelist which will work interchangeably until 4.1.
|
||||||
|
|
||||||
@ -1017,7 +1017,7 @@ sub remove_address_from_welcomelist {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->remove_all_addresses_from_welcomelist ($mail, $cli_p)
|
=item $f-E<gt>remove_all_addresses_from_welcomelist ($mail, $cli_p)
|
||||||
|
|
||||||
Previously remove_all_addresses_from_whitelist which will work interchangeably until 4.1.
|
Previously remove_all_addresses_from_whitelist which will work interchangeably until 4.1.
|
||||||
|
|
||||||
@ -1041,7 +1041,7 @@ sub remove_all_addresses_from_welcomelist {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->add_address_to_blocklist ($addr, $cli_p)
|
=item $f-E<gt>add_address_to_blocklist ($addr, $cli_p)
|
||||||
|
|
||||||
Previously add_address_to_blacklist which will work interchangeably until 4.1.
|
Previously add_address_to_blacklist which will work interchangeably until 4.1.
|
||||||
|
|
||||||
@ -1061,7 +1061,7 @@ sub add_address_to_blocklist {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->add_all_addresses_to_blocklist ($mail, $cli_p)
|
=item $f-E<gt>add_all_addresses_to_blocklist ($mail, $cli_p)
|
||||||
|
|
||||||
Previously add_all_addresses_to_blacklist which will work interchangeably until 4.1.
|
Previously add_all_addresses_to_blacklist which will work interchangeably until 4.1.
|
||||||
|
|
||||||
@ -1097,7 +1097,7 @@ sub add_all_addresses_to_blocklist {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $text = $f->remove_spamassassin_markup ($mail)
|
=item $text = $f-E<gt>remove_spamassassin_markup ($mail)
|
||||||
|
|
||||||
Returns the text of the message, with any SpamAssassin-added text (such
|
Returns the text of the message, with any SpamAssassin-added text (such
|
||||||
as the report, or X-Spam-Status headers) stripped.
|
as the report, or X-Spam-Status headers) stripped.
|
||||||
@ -1249,7 +1249,7 @@ sub remove_spamassassin_markup {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->read_scoreonly_config ($filename)
|
=item $f-E<gt>read_scoreonly_config ($filename)
|
||||||
|
|
||||||
Read a configuration file and parse user preferences from it.
|
Read a configuration file and parse user preferences from it.
|
||||||
|
|
||||||
@ -1292,7 +1292,7 @@ sub read_scoreonly_config {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->load_scoreonly_sql ($username)
|
=item $f-E<gt>load_scoreonly_sql ($username)
|
||||||
|
|
||||||
Read configuration parameters from SQL database and parse scores from it. This
|
Read configuration parameters from SQL database and parse scores from it. This
|
||||||
will only take effect if the perl C<DBI> module is installed, and the
|
will only take effect if the perl C<DBI> module is installed, and the
|
||||||
@ -1318,7 +1318,7 @@ sub load_scoreonly_sql {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->load_scoreonly_ldap ($username)
|
=item $f-E<gt>load_scoreonly_ldap ($username)
|
||||||
|
|
||||||
Read configuration parameters from an LDAP server and parse scores from it.
|
Read configuration parameters from an LDAP server and parse scores from it.
|
||||||
This will only take effect if the perl C<Net::LDAP> and C<URI> modules are
|
This will only take effect if the perl C<Net::LDAP> and C<URI> modules are
|
||||||
@ -1343,7 +1343,7 @@ sub load_scoreonly_ldap {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->set_persistent_address_list_factory ($factoryobj)
|
=item $f-E<gt>set_persistent_address_list_factory ($factoryobj)
|
||||||
|
|
||||||
Set the persistent address list factory, used to create objects for the
|
Set the persistent address list factory, used to create objects for the
|
||||||
automatic welcomelist algorithm's persistent-storage back-end. See
|
automatic welcomelist algorithm's persistent-storage back-end. See
|
||||||
@ -1359,7 +1359,7 @@ sub set_persistent_address_list_factory {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->compile_now ($use_user_prefs, $keep_userstate)
|
=item $f-E<gt>compile_now ($use_user_prefs, $keep_userstate)
|
||||||
|
|
||||||
Compile all patterns, load all configuration files, and load all
|
Compile all patterns, load all configuration files, and load all
|
||||||
possibly-required Perl modules.
|
possibly-required Perl modules.
|
||||||
@ -1471,7 +1471,7 @@ sub compile_now {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->debug_diagnostics ()
|
=item $f-E<gt>debug_diagnostics ()
|
||||||
|
|
||||||
Output some diagnostic information, useful for debugging SpamAssassin
|
Output some diagnostic information, useful for debugging SpamAssassin
|
||||||
problems.
|
problems.
|
||||||
@ -1491,7 +1491,7 @@ sub debug_diagnostics {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $failed = $f->lint_rules ()
|
=item $failed = $f-E<gt>lint_rules ()
|
||||||
|
|
||||||
Syntax-check the current set of rules. Returns the number of
|
Syntax-check the current set of rules. Returns the number of
|
||||||
syntax errors discovered, or 0 if the configuration is valid.
|
syntax errors discovered, or 0 if the configuration is valid.
|
||||||
@ -1536,7 +1536,7 @@ sub lint_rules {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->finish()
|
=item $f-E<gt>finish()
|
||||||
|
|
||||||
Destroy this object, so that it will be garbage-collected once it
|
Destroy this object, so that it will be garbage-collected once it
|
||||||
goes out of scope. The object will no longer be usable after this
|
goes out of scope. The object will no longer be usable after this
|
||||||
@ -2017,7 +2017,7 @@ sub test_global_state_dir {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $fullpath = $f->find_rule_support_file ($filename)
|
=item $fullpath = $f-E<gt>find_rule_support_file ($filename)
|
||||||
|
|
||||||
Find a rule-support file, such as C<languages> or C<triplets.txt>,
|
Find a rule-support file, such as C<languages> or C<triplets.txt>,
|
||||||
in the system-wide rules directory, and return its full path if
|
in the system-wide rules directory, and return its full path if
|
||||||
@ -2048,7 +2048,7 @@ sub find_rule_support_file {
|
|||||||
map { my $p = $_; $p =~ s{$}{/$filename}; $p } @paths );
|
map { my $p = $_; $p =~ s{$}{/$filename}; $p } @paths );
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $f->create_default_prefs ($filename, $username [ , $userdir ] )
|
=item $f-E<gt>create_default_prefs ($filename, $username [ , $userdir ] )
|
||||||
|
|
||||||
Copy default preferences file into home directory for later use and
|
Copy default preferences file into home directory for later use and
|
||||||
modification, if it does not already exist and C<dont_copy_prefs> is
|
modification, if it does not already exist and C<dont_copy_prefs> is
|
||||||
@ -2374,7 +2374,7 @@ sub sa_die {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->copy_config ( [ $source ], [ $dest ] )
|
=item $f-E<gt>copy_config ( [ $source ], [ $dest ] )
|
||||||
|
|
||||||
Used for daemons to keep a persistent Mail::SpamAssassin object's
|
Used for daemons to keep a persistent Mail::SpamAssassin object's
|
||||||
configuration correct if switching between users. Pass an associative
|
configuration correct if switching between users. Pass an associative
|
||||||
@ -2425,7 +2425,7 @@ sub copy_config {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item @plugins = $f->get_loaded_plugins_list ( )
|
=item @plugins = $f-E<gt>get_loaded_plugins_list ( )
|
||||||
|
|
||||||
Return the list of plugins currently loaded by this SpamAssassin object's
|
Return the list of plugins currently loaded by this SpamAssassin object's
|
||||||
configuration; each entry in the list is an object of type
|
configuration; each entry in the list is an object of type
|
||||||
|
@ -77,7 +77,7 @@ and C<result_sub> functions appropriately per message.
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $item = Mail::SpamAssassin::ArchiveIterator->new( [ { opt => val, ... } ] )
|
=item $item = Mail::SpamAssassin::ArchiveIterator-E<gt>new( [ { opt =E<gt> val, ... } ] )
|
||||||
|
|
||||||
Constructs a new C<Mail::SpamAssassin::ArchiveIterator> object. You may
|
Constructs a new C<Mail::SpamAssassin::ArchiveIterator> object. You may
|
||||||
pass the following attribute-value pairs to the constructor. The pairs are
|
pass the following attribute-value pairs to the constructor. The pairs are
|
||||||
|
@ -85,7 +85,7 @@ sub new {
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
=item $ent = $async->bgsend_and_start_lookup($name, $type, $class, $ent, $cb, %options)
|
=item $ent = $async-E<gt>bgsend_and_start_lookup($name, $type, $class, $ent, $cb, %options)
|
||||||
|
|
||||||
Launch async DNS lookups. This is the only official method supported for
|
Launch async DNS lookups. This is the only official method supported for
|
||||||
plugins since version 4.0.0. Do not use bgsend and start_lookup separately.
|
plugins since version 4.0.0. Do not use bgsend and start_lookup separately.
|
||||||
@ -111,26 +111,26 @@ Deprecated, ignored, set as undef.
|
|||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item $ent->{rulename} (required)
|
=item $ent-E<gt>{rulename} (required)
|
||||||
|
|
||||||
The rulename that started and/or depends on this query. Required for rule
|
The rulename that started and/or depends on this query. Required for rule
|
||||||
dependencies to work correctly. Can be a single rulename, or array of
|
dependencies to work correctly. Can be a single rulename, or array of
|
||||||
multiple rulenames.
|
multiple rulenames.
|
||||||
|
|
||||||
=item $ent->{type} (optional)
|
=item $ent-E<gt>{type} (optional)
|
||||||
|
|
||||||
A string, typically one word, used to describe the type of lookup in log
|
A string, typically one word, used to describe the type of lookup in log
|
||||||
messages, such as C<DNSBL>, C<URIBL-A>. If not defined, default is value of
|
messages, such as C<DNSBL>, C<URIBL-A>. If not defined, default is value of
|
||||||
$type.
|
$type.
|
||||||
|
|
||||||
=item $ent->{zone} (optional)
|
=item $ent-E<gt>{zone} (optional)
|
||||||
|
|
||||||
A zone specification (typically a DNS zone name - e.g. host, domain, or
|
A zone specification (typically a DNS zone name - e.g. host, domain, or
|
||||||
RBL) which may be used as a key to look up per-zone settings. No semantics
|
RBL) which may be used as a key to look up per-zone settings. No semantics
|
||||||
on this parameter is imposed by this module. Currently used to fetch
|
on this parameter is imposed by this module. Currently used to fetch
|
||||||
by-zone timeouts (from rbl_timeout setting). Defaults to $name.
|
by-zone timeouts (from rbl_timeout setting). Defaults to $name.
|
||||||
|
|
||||||
=item $ent->{timeout_initial} (optional)
|
=item $ent-E<gt>{timeout_initial} (optional)
|
||||||
|
|
||||||
An initial value of elapsed time for which we are willing to wait for a
|
An initial value of elapsed time for which we are willing to wait for a
|
||||||
response (time in seconds, floating point value is allowed). When elapsed
|
response (time in seconds, floating point value is allowed). When elapsed
|
||||||
@ -147,17 +147,17 @@ variable rbl_timeout.
|
|||||||
If a value of the timeout_initial parameter is below timeout_min, the initial
|
If a value of the timeout_initial parameter is below timeout_min, the initial
|
||||||
timeout is set to timeout_min.
|
timeout is set to timeout_min.
|
||||||
|
|
||||||
=item $ent->{timeout_min} (optional)
|
=item $ent-E<gt>{timeout_min} (optional)
|
||||||
|
|
||||||
A lower bound (in seconds) to which the actual timeout approaches as the
|
A lower bound (in seconds) to which the actual timeout approaches as the
|
||||||
number of queries completed approaches the number of all queries started.
|
number of queries completed approaches the number of all queries started.
|
||||||
Defaults to 0.2 * timeout_initial.
|
Defaults to 0.2 * timeout_initial.
|
||||||
|
|
||||||
=item $ent->{key}, $ent->{id} (deprecated)
|
=item $ent-E<gt>{key}, $ent-E<gt>{id} (deprecated)
|
||||||
|
|
||||||
Deprecated, ignored, automatically generated since 4.0.0.
|
Deprecated, ignored, automatically generated since 4.0.0.
|
||||||
|
|
||||||
=item $ent->{YOUR_OWN_ITEM}
|
=item $ent-E<gt>{YOUR_OWN_ITEM}
|
||||||
|
|
||||||
Any other custom values/objects that you want to pass on to the answer
|
Any other custom values/objects that you want to pass on to the answer
|
||||||
callback.
|
callback.
|
||||||
@ -166,10 +166,10 @@ callback.
|
|||||||
|
|
||||||
=item $cb (required)
|
=item $cb (required)
|
||||||
|
|
||||||
Callback function for answer, called as $cb->($ent, $pkt). C<$ent> is the
|
Callback function for answer, called as $cb-E<gt>($ent, $pkt). C<$ent> is the
|
||||||
same object that bgsend_and_start_lookup was called with. C<$pkt> is the
|
same object that bgsend_and_start_lookup was called with. C<$pkt> is the
|
||||||
packet object for the response, Net::DNS:RR objects can be found from
|
packet object for the response, Net::DNS:RR objects can be found from
|
||||||
$pkt->answer.
|
$pkt-E<gt>answer.
|
||||||
|
|
||||||
=item %options (required)
|
=item %options (required)
|
||||||
|
|
||||||
@ -386,7 +386,7 @@ sub bgsend_and_start_lookup {
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
=item $ent = $async->start_lookup($ent, $master_deadline)
|
=item $ent = $async-E<gt>start_lookup($ent, $master_deadline)
|
||||||
|
|
||||||
DIRECT USE DEPRECATED since 4.0.0, please use bgsend_and_start_lookup.
|
DIRECT USE DEPRECATED since 4.0.0, please use bgsend_and_start_lookup.
|
||||||
|
|
||||||
@ -483,7 +483,7 @@ sub _start_lookup {
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
=item $ent = $async->get_lookup($key)
|
=item $ent = $async-E<gt>get_lookup($key)
|
||||||
|
|
||||||
DEPRECATED since 4.0.0. Do not use.
|
DEPRECATED since 4.0.0. Do not use.
|
||||||
|
|
||||||
@ -497,7 +497,7 @@ sub get_lookup {
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
=item $async->log_lookups_timing()
|
=item $async-E<gt>log_lookups_timing()
|
||||||
|
|
||||||
Log sorted timing for all completed lookups.
|
Log sorted timing for all completed lookups.
|
||||||
|
|
||||||
@ -513,7 +513,7 @@ sub log_lookups_timing {
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
=item $alldone = $async->complete_lookups()
|
=item $alldone = $async-E<gt>complete_lookups()
|
||||||
|
|
||||||
Perform a poll of the pending lookups, to see if any are completed.
|
Perform a poll of the pending lookups, to see if any are completed.
|
||||||
Callbacks on completed queries will be called from poll_responses().
|
Callbacks on completed queries will be called from poll_responses().
|
||||||
@ -644,7 +644,7 @@ sub complete_lookups {
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
=item $async->abort_remaining_lookups()
|
=item $async-E<gt>abort_remaining_lookups()
|
||||||
|
|
||||||
Abort any remaining lookups.
|
Abort any remaining lookups.
|
||||||
|
|
||||||
@ -712,21 +712,21 @@ sub abort_remaining_lookups {
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
=item $async->set_response_packet($id, $pkt, $key, $timestamp)
|
=item $async-E<gt>set_response_packet($id, $pkt, $key, $timestamp)
|
||||||
|
|
||||||
For internal use, do not call from plugins.
|
For internal use, do not call from plugins.
|
||||||
|
|
||||||
Register a "response packet" for a given query. C<$id> is the ID for the
|
Register a "response packet" for a given query. C<$id> is the ID for the
|
||||||
query, and must match the C<id> supplied in C<start_lookup()>. C<$pkt> is the
|
query, and must match the C<id> supplied in C<start_lookup()>. C<$pkt> is the
|
||||||
packet object for the response. A parameter C<$key> identifies an entry in a
|
packet object for the response. A parameter C<$key> identifies an entry in a
|
||||||
hash %{$self->{pending_lookups}} where the object which spawned this query can
|
hash %{$self-E<gt>{pending_lookups}} where the object which spawned this query can
|
||||||
be found, and through which further information about the query is accessible.
|
be found, and through which further information about the query is accessible.
|
||||||
|
|
||||||
C<$pkt> may be undef, indicating that no response packet is available, but a
|
C<$pkt> may be undef, indicating that no response packet is available, but a
|
||||||
query has completed (e.g. was aborted or dismissed) and is no longer "pending".
|
query has completed (e.g. was aborted or dismissed) and is no longer "pending".
|
||||||
|
|
||||||
The DNS resolver's response packet C<$pkt> will be made available to a callback
|
The DNS resolver's response packet C<$pkt> will be made available to a callback
|
||||||
subroutine through its argument as well as in C<$ent-<gt>{response_packet}>.
|
subroutine through its argument as well as in C<$ent-E<gt>{response_packet}>.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
@ -765,11 +765,11 @@ sub set_response_packet {
|
|||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $async->report_id_complete($id,$key,$key,$timestamp)
|
=item $async-E<gt>report_id_complete($id,$key,$key,$timestamp)
|
||||||
|
|
||||||
DEPRECATED since 4.0.0. Do not use.
|
DEPRECATED since 4.0.0. Do not use.
|
||||||
|
|
||||||
Legacy. Equivalent to $self->set_response_packet($id,undef,$key,$timestamp),
|
Legacy. Equivalent to $self-E<gt>set_response_packet($id,undef,$key,$timestamp),
|
||||||
i.e. providing undef as a response packet. Register that a query has
|
i.e. providing undef as a response packet. Register that a query has
|
||||||
completed and is no longer "pending". C<$id> is the ID for the query,
|
completed and is no longer "pending". C<$id> is the ID for the query,
|
||||||
and must match the C<id> supplied in C<start_lookup()>.
|
and must match the C<id> supplied in C<start_lookup()>.
|
||||||
@ -786,7 +786,7 @@ sub report_id_complete {
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
=item $time = $async->last_poll_responses_time()
|
=item $time = $async-E<gt>last_poll_responses_time()
|
||||||
|
|
||||||
Get the time of the last call to C<poll_responses()> (which is called
|
Get the time of the last call to C<poll_responses()> (which is called
|
||||||
from C<complete_lookups()>. If C<poll_responses()> was never called or
|
from C<complete_lookups()>. If C<poll_responses()> was never called or
|
||||||
|
@ -105,7 +105,7 @@ sub new {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $meanscore = awl->check_address($addr, $originating_ip, $signedby);
|
=item $meanscore = awl-E<gt>check_address($addr, $originating_ip, $signedby);
|
||||||
|
|
||||||
This method will return the mean score of all messages associated with the
|
This method will return the mean score of all messages associated with the
|
||||||
given address, or undef if the address hasn't been seen before.
|
given address, or undef if the address hasn't been seen before.
|
||||||
@ -160,7 +160,7 @@ sub check_address {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item awl->count();
|
=item awl-E<gt>count();
|
||||||
|
|
||||||
This method will return the count of messages used in determining the
|
This method will return the count of messages used in determining the
|
||||||
welcomelist correction.
|
welcomelist correction.
|
||||||
@ -175,7 +175,7 @@ sub count {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item awl->add_score($score);
|
=item awl-E<gt>add_score($score);
|
||||||
|
|
||||||
This method will add half the score to the current entry. Half the
|
This method will add half the score to the current entry. Half the
|
||||||
score is used, so that repeated use of the same From and IP address
|
score is used, so that repeated use of the same From and IP address
|
||||||
@ -200,7 +200,7 @@ sub add_score {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item awl->add_known_good_address($addr);
|
=item awl-E<gt>add_known_good_address($addr);
|
||||||
|
|
||||||
This method will add a score of -100 to the given address -- effectively
|
This method will add a score of -100 to the given address -- effectively
|
||||||
"bootstrapping" the address as being one that should be welcomelisted.
|
"bootstrapping" the address as being one that should be welcomelisted.
|
||||||
@ -216,7 +216,7 @@ sub add_known_good_address {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item awl->add_known_bad_address($addr);
|
=item awl-E<gt>add_known_bad_address($addr);
|
||||||
|
|
||||||
This method will add a score of 100 to the given address -- effectively
|
This method will add a score of 100 to the given address -- effectively
|
||||||
"bootstrapping" the address as being one that should be blocklisted.
|
"bootstrapping" the address as being one that should be blocklisted.
|
||||||
|
@ -510,7 +510,7 @@ sub _create_connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unless ($remote) {
|
unless ($remote) {
|
||||||
print "Failed to create connection to spamd daemon: $!\n";
|
warn "Failed to create connection to spamd daemon: $!\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,7 +594,7 @@ sub _filter {
|
|||||||
if(defined $self->{max_size}) {
|
if(defined $self->{max_size}) {
|
||||||
$msg = substr($msg,0,$self->{max_size});
|
$msg = substr($msg,0,$self->{max_size});
|
||||||
}
|
}
|
||||||
$msgsize = length($msg.$EOL);
|
$msgsize = length($msg);
|
||||||
|
|
||||||
print $remote "$command $PROTOVERSION$EOL";
|
print $remote "$command $PROTOVERSION$EOL";
|
||||||
print $remote "Content-length: $msgsize$EOL";
|
print $remote "Content-length: $msgsize$EOL";
|
||||||
|
@ -1544,7 +1544,7 @@ Empty the list of msa networks.
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
=item originating_ip_headers header ... (default: X-Yahoo-Post-IP X-Originating-IP X-Apparently-From X-SenderIP)
|
=item originating_ip_headers header ... (default: none)
|
||||||
|
|
||||||
A list of header field names from which an originating IP address can
|
A list of header field names from which an originating IP address can
|
||||||
be obtained. For example, webmail servers may record a client IP address
|
be obtained. For example, webmail servers may record a client IP address
|
||||||
@ -1556,6 +1556,10 @@ are used in RBL checks where appropriate.
|
|||||||
Currently the IP addresses are not added into X-Spam-Relays-* header fields,
|
Currently the IP addresses are not added into X-Spam-Relays-* header fields,
|
||||||
but they may be in the future.
|
but they may be in the future.
|
||||||
|
|
||||||
|
A default list may be supplied via sa-update, use
|
||||||
|
C<clear_originating_ip_headers> to clear and override the settings if
|
||||||
|
needed.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
@ -1577,7 +1581,8 @@ but they may be in the future.
|
|||||||
|
|
||||||
=item clear_originating_ip_headers
|
=item clear_originating_ip_headers
|
||||||
|
|
||||||
Empty the list of 'originating IP address' header field names.
|
Empty the list of 'originating IP address' header field names. Useful if
|
||||||
|
you want to override the standard list supplied by sa-update.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
@ -2077,7 +2082,7 @@ home_dir_for_helpers/.spamassassin, $HOME/.spamassassin,
|
|||||||
my $rule = $1;
|
my $rule = $1;
|
||||||
foreach my $domain (split(/\s+/, lc($2))) {
|
foreach my $domain (split(/\s+/, lc($2))) {
|
||||||
$domain =~ s/^\.//; $domain =~ s/\.\z//; # strip dots
|
$domain =~ s/^\.//; $domain =~ s/\.\z//; # strip dots
|
||||||
if ($domain !~ /^[a-z0-9.-]+$/) {
|
if ($domain !~ /^[a-z0-9_.-]+$/) {
|
||||||
return $INVALID_VALUE;
|
return $INVALID_VALUE;
|
||||||
}
|
}
|
||||||
# will end up in filename, do not allow / etc in above regex!
|
# will end up in filename, do not allow / etc in above regex!
|
||||||
@ -3677,10 +3682,6 @@ normal rules to use. When rule depends on a tag that might be set at later
|
|||||||
stage by a plugin for example, it's priority should be set manually to a
|
stage by a plugin for example, it's priority should be set manually to a
|
||||||
higher value.
|
higher value.
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
=head1 ADMINISTRATOR SETTINGS
|
=head1 ADMINISTRATOR SETTINGS
|
||||||
|
|
||||||
These settings differ from the ones above, in that they are considered 'more
|
These settings differ from the ones above, in that they are considered 'more
|
||||||
|
@ -79,7 +79,7 @@ sub load_modules { # static
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->load ($username)
|
=item $f-E<gt>load ($username)
|
||||||
|
|
||||||
Read configuration parameters from LDAP server and parse scores from it.
|
Read configuration parameters from LDAP server and parse scores from it.
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ sub load_modules { # static
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $f->load ($username)
|
=item $f-E<gt>load ($username)
|
||||||
|
|
||||||
Read configuration parameters from SQL database and parse scores from it.
|
Read configuration parameters from SQL database and parse scores from it.
|
||||||
|
|
||||||
|
@ -65,29 +65,6 @@ our $IS_DNS_AVAILABLE = undef;
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
BEGIN {
|
|
||||||
# some trickery. Load these modules right here, if possible; that way, if
|
|
||||||
# the module exists, we'll get it loaded now. Very useful to avoid attempted
|
|
||||||
# loads later (which will happen). If we do a fork(), we could wind up
|
|
||||||
# attempting to load these modules in *every* subprocess.
|
|
||||||
#
|
|
||||||
# # We turn off strict and warnings, because Net::DNS and Razor both contain
|
|
||||||
# # crud that -w complains about (perl 5.6.0). Not that this seems to work,
|
|
||||||
# # mind ;)
|
|
||||||
# no strict;
|
|
||||||
# local ($^W) = 0;
|
|
||||||
|
|
||||||
no warnings;
|
|
||||||
eval {
|
|
||||||
require MIME::Base64;
|
|
||||||
};
|
|
||||||
eval {
|
|
||||||
require IO::Socket::UNIX;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
###########################################################################
|
|
||||||
|
|
||||||
sub do_rbl_lookup {
|
sub do_rbl_lookup {
|
||||||
my ($self, $rule, $set, $type, $host, $subtest) = @_;
|
my ($self, $rule, $set, $type, $host, $subtest) = @_;
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ sub new {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $res->load_resolver()
|
=item $res-E<gt>load_resolver()
|
||||||
|
|
||||||
Load the C<Net::DNS::Resolver> object. Returns 0 if Net::DNS cannot be used,
|
Load the C<Net::DNS::Resolver> object. Returns 0 if Net::DNS cannot be used,
|
||||||
1 if it is available.
|
1 if it is available.
|
||||||
@ -185,7 +185,7 @@ sub load_resolver {
|
|||||||
return defined $self->{res};
|
return defined $self->{res};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $resolver = $res->get_resolver()
|
=item $resolver = $res-E<gt>get_resolver()
|
||||||
|
|
||||||
Return the C<Net::DNS::Resolver> object.
|
Return the C<Net::DNS::Resolver> object.
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ sub get_resolver {
|
|||||||
return $self->{res};
|
return $self->{res};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $res->configured_nameservers()
|
=item $res-E<gt>configured_nameservers()
|
||||||
|
|
||||||
Get a list of nameservers as configured by dns_server directives
|
Get a list of nameservers as configured by dns_server directives
|
||||||
or as provided by Net::DNS, typically from /etc/resolv.conf
|
or as provided by Net::DNS, typically from /etc/resolv.conf
|
||||||
@ -221,7 +221,7 @@ sub configured_nameservers {
|
|||||||
return @ns_addr_port;
|
return @ns_addr_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $res->available_nameservers()
|
=item $res-E<gt>available_nameservers()
|
||||||
|
|
||||||
Get or set a list of currently available nameservers,
|
Get or set a list of currently available nameservers,
|
||||||
which is typically a known-to-be-good subset of configured nameservers
|
which is typically a known-to-be-good subset of configured nameservers
|
||||||
@ -346,7 +346,7 @@ sub pick_random_available_port {
|
|||||||
return $port_number;
|
return $port_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $res->connect_sock()
|
=item $res-E<gt>connect_sock()
|
||||||
|
|
||||||
Re-connect to the first nameserver listed in C</etc/resolv.conf> or similar
|
Re-connect to the first nameserver listed in C</etc/resolv.conf> or similar
|
||||||
platform-dependent source, as provided by C<Net::DNS>.
|
platform-dependent source, as provided by C<Net::DNS>.
|
||||||
@ -477,7 +477,7 @@ sub connect_sock_if_reqd {
|
|||||||
$self->connect_sock() if !$self->{sock};
|
$self->connect_sock() if !$self->{sock};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $res->get_sock()
|
=item $res-E<gt>get_sock()
|
||||||
|
|
||||||
Return the C<IO::Socket::INET> object used to communicate with
|
Return the C<IO::Socket::INET> object used to communicate with
|
||||||
the nameserver.
|
the nameserver.
|
||||||
@ -599,7 +599,11 @@ sub new_dns_packet {
|
|||||||
my $udp_payload_size = $self->{conf}->{dns_options}->{edns};
|
my $udp_payload_size = $self->{conf}->{dns_options}->{edns};
|
||||||
if ($udp_payload_size && $udp_payload_size > 512) {
|
if ($udp_payload_size && $udp_payload_size > 512) {
|
||||||
# dbg("dns: adding EDNS ext, UDP payload size %d", $udp_payload_size);
|
# dbg("dns: adding EDNS ext, UDP payload size %d", $udp_payload_size);
|
||||||
$packet->edns->size($udp_payload_size);
|
if ($packet->edns->can('udpsize')) { # since Net::DNS 1.38
|
||||||
|
$packet->edns->udpsize($udp_payload_size);
|
||||||
|
} else {
|
||||||
|
$packet->edns->size($udp_payload_size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,7 +655,7 @@ sub _packet_id {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $id = $res->bgsend($domain, $type, $class, $cb)
|
=item $id = $res-E<gt>bgsend($domain, $type, $class, $cb)
|
||||||
|
|
||||||
DIRECT USE DISCOURAGED, please use bgsend_and_start_lookup in plugins.
|
DIRECT USE DISCOURAGED, please use bgsend_and_start_lookup in plugins.
|
||||||
|
|
||||||
@ -734,7 +738,7 @@ sub bgsend {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $id = $res->bgread()
|
=item $id = $res-E<gt>bgread()
|
||||||
|
|
||||||
Similar to C<Net::DNS::Resolver::bgread>. Reads a DNS packet from
|
Similar to C<Net::DNS::Resolver::bgread>. Reads a DNS packet from
|
||||||
a supplied socket, decodes it, and returns a Net::DNS::Packet object
|
a supplied socket, decodes it, and returns a Net::DNS::Packet object
|
||||||
@ -765,7 +769,7 @@ sub bgread {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $nfound = $res->poll_responses()
|
=item $nfound = $res-E<gt>poll_responses()
|
||||||
|
|
||||||
See if there are any C<bgsend> reply packets ready, and return
|
See if there are any C<bgsend> reply packets ready, and return
|
||||||
the number of such packets delivered to their callbacks.
|
the number of such packets delivered to their callbacks.
|
||||||
@ -833,6 +837,30 @@ sub poll_responses {
|
|||||||
info("dns: bad dns reply: %s", $eval_stat);
|
info("dns: bad dns reply: %s", $eval_stat);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# bug 8225 - Do TCP fallback when UDP reply packet is too long, by retrying using Net::DNS::Resolver bgsend and bgread
|
||||||
|
my ($id, $packet_id);
|
||||||
|
if ($packet && $packet->header) {
|
||||||
|
my $header = $packet->header;
|
||||||
|
$packet_id = $header->id; # set these here in case we need to retry for TCP fallback
|
||||||
|
$id = $self->_packet_id($packet); # which will change $packet to a different class object
|
||||||
|
if ($header->rcode eq 'NOERROR' && $header->tc) {
|
||||||
|
# Use original Resolver which can handle TCP fallback, but keep id from the custom packet
|
||||||
|
my (undef, $qclass, $qtype, $qname) = split('/', $id);
|
||||||
|
dbg("dns: TCP fallback retry with %s, %s, %s", $qname, $qtype, $qclass);
|
||||||
|
my $orig_resolver = $self->{main}->{resolver}->get_resolver();
|
||||||
|
eval {
|
||||||
|
my $handle = $orig_resolver->bgsend($qname, $qtype, $qclass);
|
||||||
|
$packet = $orig_resolver->bgread($handle);
|
||||||
|
} or do {
|
||||||
|
undef $packet;
|
||||||
|
my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
|
||||||
|
# resignal if alarm went off
|
||||||
|
die $eval_stat if $eval_stat =~ /__alarm__ignore__\(.*\)/s;
|
||||||
|
info("dns: bad dns tcp fallback reply: %s", $eval_stat);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!$packet) {
|
if (!$packet) {
|
||||||
# error already reported above
|
# error already reported above
|
||||||
# my $dns_err = $self->{res}->errorstring;
|
# my $dns_err = $self->{res}->errorstring;
|
||||||
@ -844,9 +872,6 @@ sub poll_responses {
|
|||||||
info("dns: dns reply is missing a header section");
|
info("dns: dns reply is missing a header section");
|
||||||
} else {
|
} else {
|
||||||
my $rcode = $header->rcode;
|
my $rcode = $header->rcode;
|
||||||
my $packet_id = $header->id;
|
|
||||||
my $id = $self->_packet_id($packet);
|
|
||||||
|
|
||||||
if ($rcode eq 'NOERROR') { # success
|
if ($rcode eq 'NOERROR') { # success
|
||||||
# NOERROR, may or may not have answer records
|
# NOERROR, may or may not have answer records
|
||||||
dbg("dns: dns reply %s is OK, %d answer records",
|
dbg("dns: dns reply %s is OK, %d answer records",
|
||||||
@ -930,7 +955,7 @@ sub flush_responses {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $res->bgabort()
|
=item $res-E<gt>bgabort()
|
||||||
|
|
||||||
Call this to release pending requests from memory, when aborting backgrounded
|
Call this to release pending requests from memory, when aborting backgrounded
|
||||||
requests, or when the scan is complete.
|
requests, or when the scan is complete.
|
||||||
@ -945,7 +970,7 @@ sub bgabort {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $packet = $res->send($name, $type, $class)
|
=item $packet = $res-E<gt>send($name, $type, $class)
|
||||||
|
|
||||||
Emulates C<Net::DNS::Resolver::send()>.
|
Emulates C<Net::DNS::Resolver::send()>.
|
||||||
|
|
||||||
@ -1002,12 +1027,12 @@ sub send {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $res->errorstring()
|
=item $res-E<gt>errorstring()
|
||||||
|
|
||||||
Little more than a stub for callers expecting this from C<Net::DNS::Resolver>.
|
Little more than a stub for callers expecting this from C<Net::DNS::Resolver>.
|
||||||
|
|
||||||
If called immediately after a call to $res->send this will return
|
If called immediately after a call to $res-E<gt>send this will return
|
||||||
C<query timed out> if the $res->send DNS query timed out. Otherwise
|
C<query timed out> if the $res-E<gt>send DNS query timed out. Otherwise
|
||||||
C<unknown error or no error> will be returned.
|
C<unknown error or no error> will be returned.
|
||||||
|
|
||||||
No other errors are reported.
|
No other errors are reported.
|
||||||
@ -1022,7 +1047,7 @@ sub errorstring {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $res->finish_socket()
|
=item $res-E<gt>finish_socket()
|
||||||
|
|
||||||
Reset socket when done with it.
|
Reset socket when done with it.
|
||||||
|
|
||||||
@ -1039,7 +1064,7 @@ sub finish_socket {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $res->finish()
|
=item $res-E<gt>finish()
|
||||||
|
|
||||||
Clean up for destruction.
|
Clean up for destruction.
|
||||||
|
|
||||||
|
@ -24,8 +24,8 @@ Plugins need to signal SA main package the modules they want loaded
|
|||||||
package Mail::SpamAssassin::Plugin::MyPlugin;
|
package Mail::SpamAssassin::Plugin::MyPlugin;
|
||||||
sub new {
|
sub new {
|
||||||
...
|
...
|
||||||
$self->{main}->{geodb_wanted}->{country} = 1;
|
$self-E<gt>{main}-E<gt>{geodb_wanted}-E<gt>{country} = 1;
|
||||||
$self->{main}->{geodb_wanted}->{isp} = 1;
|
$self-E<gt>{main}-E<gt>{geodb_wanted}-E<gt>{isp} = 1;
|
||||||
)
|
)
|
||||||
|
|
||||||
(internal stuff still subject to change)
|
(internal stuff still subject to change)
|
||||||
|
@ -349,12 +349,17 @@ sub push_uri {
|
|||||||
my ($self, $type, $uri) = @_;
|
my ($self, $type, $uri) = @_;
|
||||||
|
|
||||||
$uri = $self->canon_uri($uri);
|
$uri = $self->canon_uri($uri);
|
||||||
|
return if $uri eq '';
|
||||||
utf8::encode($uri) if $self->{SA_encode_results};
|
utf8::encode($uri) if $self->{SA_encode_results};
|
||||||
|
|
||||||
my $target = target_uri($self->{base_href} || "", $uri);
|
if ($uri =~ /^(?:data|mailto|file|cid|tel):/i) {
|
||||||
|
# No target handling required
|
||||||
# skip things like <iframe src="" ...>
|
$self->{uri}->{$uri}->{types}->{$type} = 1;
|
||||||
$self->{uri}->{$uri}->{types}->{$type} = 1 if $uri ne '';
|
} else {
|
||||||
|
my $target = target_uri($self->{base_href} || "", $uri);
|
||||||
|
# skip things like <iframe src="" ...>
|
||||||
|
$self->{uri}->{$target}->{types}->{$type} = 1 if $target ne '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub canon_uri {
|
sub canon_uri {
|
||||||
@ -383,7 +388,20 @@ sub html_uri {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($tag =~ /^(?:a|area|link)$/) {
|
elsif ($tag =~ /^(?:a|area|link)$/) {
|
||||||
|
while ( my ( $k, $v ) = each %$attr ) {
|
||||||
|
# read uris from bad formatted html as well
|
||||||
|
if($k =~ /\w{1,8}\/href/) {
|
||||||
|
delete($attr->{$k});
|
||||||
|
$attr->{href} = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (defined $attr->{href}) {
|
if (defined $attr->{href}) {
|
||||||
|
# Remove the Unicode "replacement character" from the url
|
||||||
|
if (utf8::is_utf8($attr->{href})) {
|
||||||
|
$attr->{href} =~ s/\x{FEFF}//g;
|
||||||
|
} else {
|
||||||
|
$attr->{href} =~ s/\x{EF}\x{BB}\x{BF}//g;
|
||||||
|
}
|
||||||
$self->push_uri($tag, $attr->{href});
|
$self->push_uri($tag, $attr->{href});
|
||||||
}
|
}
|
||||||
if (defined $attr->{'data-saferedirecturl'}) {
|
if (defined $attr->{'data-saferedirecturl'}) {
|
||||||
@ -391,6 +409,13 @@ sub html_uri {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($tag =~ /^(?:img|frame|iframe|embed|script|bgsound)$/) {
|
elsif ($tag =~ /^(?:img|frame|iframe|embed|script|bgsound)$/) {
|
||||||
|
while ( my ( $k, $v ) = each %$attr ) {
|
||||||
|
# read uris from bad formatted html as well
|
||||||
|
if($k =~ /\w{1,8}\/src/) {
|
||||||
|
delete($attr->{$k});
|
||||||
|
$attr->{src} = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (defined $attr->{src}) {
|
if (defined $attr->{src}) {
|
||||||
$self->push_uri($tag, $attr->{src});
|
$self->push_uri($tag, $attr->{src});
|
||||||
}
|
}
|
||||||
@ -535,6 +560,9 @@ sub text_style {
|
|||||||
if (/^\s*(background-)?color:\s*(.+?)\s*$/i) {
|
if (/^\s*(background-)?color:\s*(.+?)\s*$/i) {
|
||||||
my $whcolor = $1 ? 'bgcolor' : 'fgcolor';
|
my $whcolor = $1 ? 'bgcolor' : 'fgcolor';
|
||||||
my $value = lc $2;
|
my $value = lc $2;
|
||||||
|
# prevent parsing of the valid CSS3 property value as
|
||||||
|
# 'invalid color' (Bug 7892)
|
||||||
|
$value =~ s/\s*!\s*important$//;
|
||||||
|
|
||||||
if (index($value, 'rgb') >= 0) {
|
if (index($value, 'rgb') >= 0) {
|
||||||
$value =~ tr/0-9,//cd;
|
$value =~ tr/0-9,//cd;
|
||||||
@ -547,19 +575,41 @@ sub text_style {
|
|||||||
# do nothing, just prevent parsing of the valid
|
# do nothing, just prevent parsing of the valid
|
||||||
# CSS3 property value as 'invalid color' (Bug 7778)
|
# CSS3 property value as 'invalid color' (Bug 7778)
|
||||||
}
|
}
|
||||||
elsif ($value eq '!important') {
|
elsif ($value eq 'transparent') {
|
||||||
# do nothing, just prevent parsing of the valid
|
# keep for now, handle outside the loop (Bug 8205)
|
||||||
# CSS3 property value as 'invalid color' (Bug 7892)
|
$new{$whcolor} = $value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$new{$whcolor} = name_to_rgb($value);
|
$new{$whcolor} = name_to_rgb($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elsif (/^\s*background:\s*(.+?)\s*$/) {
|
||||||
|
# parse CSS background property (Bug 8210)
|
||||||
|
my $layers = parse_css_background(lc($1));
|
||||||
|
# loop through values in the bottom layer and look for valid colors
|
||||||
|
for my $value (@{$layers->[-1]}) {
|
||||||
|
my $color = name_to_rgb($value);
|
||||||
|
if ($color ne 'invalid') {
|
||||||
|
$new{bgcolor} = $color;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
elsif (/^\s*([a-z_-]+)\s*:\s*(\S.*?)\s*$/i) {
|
elsif (/^\s*([a-z_-]+)\s*:\s*(\S.*?)\s*$/i) {
|
||||||
# "display: none", "visibility: hidden", etc.
|
# "display: none", "visibility: hidden", etc.
|
||||||
$new{'style_'.$1} = $2;
|
$new{'style_'.$1} = $2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
# Handle transparent colors (Bug 8205)
|
||||||
|
if ($new{bgcolor} eq 'transparent') {
|
||||||
|
# replace with parent's bgcolor
|
||||||
|
$new{bgcolor} = $self->{text_style}[-1]->{bgcolor};
|
||||||
|
}
|
||||||
|
if ($new{fgcolor} eq 'transparent') {
|
||||||
|
# replace with current bgcolor
|
||||||
|
$new{fgcolor} = $new{bgcolor};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elsif ($name eq "bgcolor") {
|
elsif ($name eq "bgcolor") {
|
||||||
# overwrite with hex value, $new{bgcolor} is set below
|
# overwrite with hex value, $new{bgcolor} is set below
|
||||||
@ -588,6 +638,92 @@ sub text_style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Parses a CSS background property value.
|
||||||
|
# Returns an arrayref of layers, each layer is an arrayref of values such as
|
||||||
|
# [
|
||||||
|
# 'rgb(255, 192, 0)',
|
||||||
|
# '35%',
|
||||||
|
# 'url("../../media/examples/lizard.png")'
|
||||||
|
# ]
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/CSS/background
|
||||||
|
sub parse_css_background {
|
||||||
|
my ($background) = @_;
|
||||||
|
|
||||||
|
my @layers;
|
||||||
|
my @tokens;
|
||||||
|
my @stack;
|
||||||
|
my ($state,$token) = (0,'');
|
||||||
|
for (my $i=0;$i < length($background);$i++) {
|
||||||
|
my $ch = substr($background, $i, 1);
|
||||||
|
if ($state == 0) {
|
||||||
|
if ($ch eq ' ') {
|
||||||
|
push @tokens, $token if $token ne '';
|
||||||
|
$token = '';
|
||||||
|
} elsif ($ch eq '(') {
|
||||||
|
$token .= $ch;
|
||||||
|
push @stack, $state;
|
||||||
|
$state = 1;
|
||||||
|
} elsif ($ch eq '"') {
|
||||||
|
$token .= $ch;
|
||||||
|
push @stack, $state;
|
||||||
|
$state = 2;
|
||||||
|
} elsif ($ch eq q(')) {
|
||||||
|
$token .= $ch;
|
||||||
|
push @stack, $state;
|
||||||
|
$state = 3;
|
||||||
|
} elsif ($ch eq ',') {
|
||||||
|
push @tokens, $token if $token ne '';
|
||||||
|
$token = '';
|
||||||
|
push @layers, [ @tokens ];
|
||||||
|
@tokens = ();
|
||||||
|
} else {
|
||||||
|
$token .= $ch;
|
||||||
|
}
|
||||||
|
} elsif ($state == 1) {
|
||||||
|
if ($ch eq ')') {
|
||||||
|
$token .= $ch;
|
||||||
|
push @tokens, $token;
|
||||||
|
$token = '';
|
||||||
|
$state = pop @stack;
|
||||||
|
} elsif ($ch eq '"') {
|
||||||
|
$token .= $ch;
|
||||||
|
push(@stack, $state);
|
||||||
|
$state = 2;
|
||||||
|
} elsif ($ch eq q(')) {
|
||||||
|
$token .= $ch;
|
||||||
|
push(@stack, $state);
|
||||||
|
$state = 3;
|
||||||
|
} else {
|
||||||
|
$token .= $ch;
|
||||||
|
}
|
||||||
|
} elsif ($state == 2) {
|
||||||
|
if ($ch eq '"') {
|
||||||
|
$token .= $ch;
|
||||||
|
$state = pop @stack;
|
||||||
|
} else {
|
||||||
|
$token .= $ch;
|
||||||
|
}
|
||||||
|
} elsif ($state == 3) {
|
||||||
|
if ($ch eq q(')) {
|
||||||
|
$token .= $ch;
|
||||||
|
$state = pop @stack;
|
||||||
|
} else {
|
||||||
|
$token .= $ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($token ne '') {
|
||||||
|
push @tokens, $token;
|
||||||
|
}
|
||||||
|
if ( scalar @tokens > 0 ) {
|
||||||
|
push @layers, [ @tokens ];
|
||||||
|
}
|
||||||
|
|
||||||
|
return \@layers;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
sub html_font_invisible {
|
sub html_font_invisible {
|
||||||
my ($self, $text) = @_;
|
my ($self, $text) = @_;
|
||||||
|
|
||||||
@ -1255,7 +1391,7 @@ sub target_uri {
|
|||||||
$result .= "ftp:";
|
$result .= "ftp:";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($t{authority}) {
|
if (defined $t{authority}) {
|
||||||
$result .= "//" . $t{authority};
|
$result .= "//" . $t{authority};
|
||||||
}
|
}
|
||||||
$result .= $t{path};
|
$result .= $t{path};
|
||||||
|
@ -293,6 +293,7 @@ sub _log {
|
|||||||
my ($level, $message, @args) = @_;
|
my ($level, $message, @args) = @_;
|
||||||
|
|
||||||
utf8::encode($message) if utf8::is_utf8($message); # handle as octets
|
utf8::encode($message) if utf8::is_utf8($message); # handle as octets
|
||||||
|
foreach (@args) { utf8::encode($_) if utf8::is_utf8($_); } # Bug 8138
|
||||||
|
|
||||||
$message =~ s/^(?:[a-z0-9_-]*):\s*//i;
|
$message =~ s/^(?:[a-z0-9_-]*):\s*//i;
|
||||||
|
|
||||||
@ -304,7 +305,7 @@ sub _log {
|
|||||||
log_message(($level == INFO ? "info" : "dbg"), $message);
|
log_message(($level == INFO ? "info" : "dbg"), $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
=item add(method => 'syslog', socket => $socket, facility => $facility, escape => $escape)
|
=item add(method =E<gt> 'syslog', socket =E<gt> $socket, facility =E<gt> $facility, escape =E<gt> $escape)
|
||||||
|
|
||||||
C<socket> is the type the syslog ("unix" or "inet"). C<facility> is the
|
C<socket> is the type the syslog ("unix" or "inet"). C<facility> is the
|
||||||
syslog facility (typically "mail").
|
syslog facility (typically "mail").
|
||||||
@ -317,12 +318,12 @@ output: backslashes change to \\ and non-ascii chars to \x{XX} or \x{XXXX}
|
|||||||
Escape value can be overridden with environment variable
|
Escape value can be overridden with environment variable
|
||||||
C<SA_LOGGER_ESCAPE>.
|
C<SA_LOGGER_ESCAPE>.
|
||||||
|
|
||||||
=item add(method => 'file', filename => $file, escape => $escape)
|
=item add(method =E<gt> 'file', filename =E<gt> $file, escape =E<gt> $escape)
|
||||||
|
|
||||||
C<filename> is the name of the log file. C<escape> works as described
|
C<filename> is the name of the log file. C<escape> works as described
|
||||||
above.
|
above.
|
||||||
|
|
||||||
=item add(method => 'stderr', escape => $escape)
|
=item add(method =E<gt> 'stderr', escape =E<gt> $escape)
|
||||||
|
|
||||||
No options are needed for stderr logging, just don't close stderr first.
|
No options are needed for stderr logging, just don't close stderr first.
|
||||||
C<escape> works as described above.
|
C<escape> works as described above.
|
||||||
|
@ -572,8 +572,8 @@ sub get_pristine_body_digest {
|
|||||||
|
|
||||||
=item get_msgid()
|
=item get_msgid()
|
||||||
|
|
||||||
Returns Message-ID header for the message, with <> and surrounding
|
Returns Message-ID header for the message, with E<lt>E<gt> and surrounding
|
||||||
whitespace removed. Returns undef, if nothing found between <>.
|
whitespace removed. Returns undef, if nothing found between E<lt>E<gt>.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
@ -759,6 +759,7 @@ sub finish {
|
|||||||
delete $part->{'invisible_rendered'};
|
delete $part->{'invisible_rendered'};
|
||||||
delete $part->{'type'};
|
delete $part->{'type'};
|
||||||
delete $part->{'rendered_type'};
|
delete $part->{'rendered_type'};
|
||||||
|
delete $part->{'effective_type'};
|
||||||
|
|
||||||
# if there are children nodes, add them to the queue of nodes to clean up
|
# if there are children nodes, add them to the queue of nodes to clean up
|
||||||
if (exists $part->{'body_parts'}) {
|
if (exists $part->{'body_parts'}) {
|
||||||
@ -1261,10 +1262,11 @@ sub get_body_text_array_common {
|
|||||||
# already been done.
|
# already been done.
|
||||||
my $html_needs_setting = !exists $self->{metadata}->{html};
|
my $html_needs_setting = !exists $self->{metadata}->{html};
|
||||||
|
|
||||||
my $text = $method_name eq 'invisible_rendered' ? ''
|
my $subject = $method_name eq 'invisible_rendered' ? ''
|
||||||
: ($self->get_header('subject') || "\n");
|
: ($self->get_header('subject') || "\n");
|
||||||
|
|
||||||
# Go through each part
|
# Go through each part
|
||||||
|
my $text = '';
|
||||||
for (my $pt = 0 ; $pt <= $#parts ; $pt++ ) {
|
for (my $pt = 0 ; $pt <= $#parts ; $pt++ ) {
|
||||||
my $p = $parts[$pt];
|
my $p = $parts[$pt];
|
||||||
|
|
||||||
@ -1316,7 +1318,8 @@ sub get_body_text_array_common {
|
|||||||
$text =~ tr/\x00/\n/; # null => newline
|
$text =~ tr/\x00/\n/; # null => newline
|
||||||
|
|
||||||
utf8::encode($text) if utf8::is_utf8($text);
|
utf8::encode($text) if utf8::is_utf8($text);
|
||||||
my @textary = split_into_array_of_short_lines($text);
|
utf8::encode($subject) if utf8::is_utf8($subject);
|
||||||
|
my @textary = split_into_array_of_short_lines($subject.$text);
|
||||||
$self->{$key} = \@textary;
|
$self->{$key} = \@textary;
|
||||||
|
|
||||||
return $self->{$key};
|
return $self->{$key};
|
||||||
@ -1350,11 +1353,13 @@ sub get_decoded_body_text_array {
|
|||||||
my $scansize = $self->{rawbody_part_scan_size};
|
my $scansize = $self->{rawbody_part_scan_size};
|
||||||
|
|
||||||
# Find all parts which are leaves
|
# Find all parts which are leaves
|
||||||
my @parts = $self->find_parts(qr/^(?:text|message)\b/,1);
|
my @parts = $self->find_parts(qr/./,1);
|
||||||
return $self->{text_decoded} unless @parts;
|
return $self->{text_decoded} unless @parts;
|
||||||
|
|
||||||
# Go through each part
|
# Go through each part
|
||||||
for(my $pt = 0 ; $pt <= $#parts ; $pt++ ) {
|
for(my $pt = 0 ; $pt <= $#parts ; $pt++ ) {
|
||||||
|
# skip non-text parts (Bug 6439)
|
||||||
|
next unless $parts[$pt]->effective_type() =~ /^(?:text|message)\b/;
|
||||||
# bug 4843: skip text/calendar parts since they're usually an attachment
|
# bug 4843: skip text/calendar parts since they're usually an attachment
|
||||||
# and not displayed
|
# and not displayed
|
||||||
next if ($parts[$pt]->{'type'} eq 'text/calendar');
|
next if ($parts[$pt]->{'type'} eq 'text/calendar');
|
||||||
|
@ -28,12 +28,12 @@ supplemental data inferred from the message, like the examples below.
|
|||||||
It is held in two forms:
|
It is held in two forms:
|
||||||
|
|
||||||
1. as name-value pairs of strings, presented in mail header format. For
|
1. as name-value pairs of strings, presented in mail header format. For
|
||||||
example, "X-Languages" => "en". This is the general form for simple
|
example, "X-Languages" =E<gt> "en". This is the general form for simple
|
||||||
metadata that's useful as Bayes tokens, can be added to marked-up
|
metadata that's useful as Bayes tokens, can be added to marked-up
|
||||||
messages using "add_header", etc., such as the trusted-relay inference
|
messages using "add_header", etc., such as the trusted-relay inference
|
||||||
and language detection.
|
and language detection.
|
||||||
|
|
||||||
2. as more complex data structures on the $msg->{metadata} object. This
|
2. as more complex data structures on the $msg-E<gt>{metadata} object. This
|
||||||
is the form used for metadata like the HTML parse data, which is stored
|
is the form used for metadata like the HTML parse data, which is stored
|
||||||
there for access by eval rule code. Because it's not simple strings,
|
there for access by eval rule code. Because it's not simple strings,
|
||||||
it's not added as a Bayes token by default (Bayes needs simple strings).
|
it's not added as a Bayes token by default (Bayes needs simple strings).
|
||||||
|
@ -686,6 +686,19 @@ sub _normalize {
|
|||||||
return $rv;
|
return $rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Parse effective content type (Bug 6260, 6439)
|
||||||
|
sub effective_type {
|
||||||
|
my ($self) = @_;
|
||||||
|
if (!exists $self->{'effective_type'}) {
|
||||||
|
if (($self->{'name'}||'') =~ /\.s?html?$/i) {
|
||||||
|
$self->{'effective_type'} = 'text/html';
|
||||||
|
} else {
|
||||||
|
$self->{'effective_type'} = $self->{'type'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $self->{'effective_type'};
|
||||||
|
}
|
||||||
|
|
||||||
=item rendered()
|
=item rendered()
|
||||||
|
|
||||||
rendered() takes the given text/* type MIME part, and attempts to
|
rendered() takes the given text/* type MIME part, and attempts to
|
||||||
@ -708,7 +721,7 @@ sub rendered {
|
|||||||
# Note: for bug 4843, make sure to skip text/calendar parts
|
# Note: for bug 4843, make sure to skip text/calendar parts
|
||||||
# we also want to skip things like text/x-vcard
|
# we also want to skip things like text/x-vcard
|
||||||
# text/x-aol is ignored here, but looks like text/html ...
|
# text/x-aol is ignored here, but looks like text/html ...
|
||||||
my $type = lc $self->{'type'};
|
my $type = $self->effective_type();
|
||||||
unless ($type eq 'text/plain' || $type eq 'text/html') {
|
unless ($type eq 'text/plain' || $type eq 'text/html') {
|
||||||
return (undef,undef);
|
return (undef,undef);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ sub forget {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $didlearn = $status->did_learn()
|
=item $didlearn = $status-E<gt>did_learn()
|
||||||
|
|
||||||
Returns C<1> if the message was learned from or forgotten successfully.
|
Returns C<1> if the message was learned from or forgotten successfully.
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ sub did_learn {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->finish()
|
=item $status-E<gt>finish()
|
||||||
|
|
||||||
Finish with the object.
|
Finish with the object.
|
||||||
|
|
||||||
|
@ -332,8 +332,11 @@ sub new {
|
|||||||
# in some circumstances
|
# in some circumstances
|
||||||
my $tag_data_ref = $self->{tag_data};
|
my $tag_data_ref = $self->{tag_data};
|
||||||
foreach (qw(SUMMARY REPORT SUBJPREFIX RBL)) { $tag_data_ref->{$_} = '' }
|
foreach (qw(SUMMARY REPORT SUBJPREFIX RBL)) { $tag_data_ref->{$_} = '' }
|
||||||
foreach (qw(AWL AWLMEAN AWLCOUNT AWLPRESCORE
|
foreach (qw(ASN ASNCIDR AWL AWLMEAN AWLCOUNT AWLPRESCORE
|
||||||
DCCB DCCR DCCREP PYZOR DKIMIDENTITY DKIMDOMAIN DKIMSELECTOR
|
DCCB DCCR EXTRACTTEXTCHARS EXTRACTTEXTWORDS
|
||||||
|
EXTRACTTEXTTOOLS EXTRACTTEXTTYPES EXTRACTTEXTEXTENSIONS
|
||||||
|
EXTRACTTEXTFLAGS EXTRACTTEXTURIS DCCREP
|
||||||
|
PYZOR DKIMIDENTITY DKIMDOMAIN DKIMSELECTOR
|
||||||
BAYESTC BAYESTCLEARNED BAYESTCSPAMMY BAYESTCHAMMY
|
BAYESTC BAYESTCLEARNED BAYESTCSPAMMY BAYESTCHAMMY
|
||||||
HAMMYTOKENS SPAMMYTOKENS TOKENSUMMARY)) {
|
HAMMYTOKENS SPAMMYTOKENS TOKENSUMMARY)) {
|
||||||
$tag_data_ref->{$_} = undef; # exist, but undefined
|
$tag_data_ref->{$_} = undef; # exist, but undefined
|
||||||
@ -357,7 +360,7 @@ sub DESTROY {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->check ()
|
=item $status-E<gt>check ()
|
||||||
|
|
||||||
Runs the SpamAssassin rules against the message pointed to by the object.
|
Runs the SpamAssassin rules against the message pointed to by the object.
|
||||||
|
|
||||||
@ -526,7 +529,7 @@ sub check_cleanup {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->learn()
|
=item $status-E<gt>learn()
|
||||||
|
|
||||||
After a mail message has been checked, this method can be called. If the score
|
After a mail message has been checked, this method can be called. If the score
|
||||||
is outside a certain range around the threshold, ie. if the message is judged
|
is outside a certain range around the threshold, ie. if the message is judged
|
||||||
@ -624,7 +627,7 @@ sub learn_timed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $score = $status->get_autolearn_points()
|
=item $score = $status-E<gt>get_autolearn_points()
|
||||||
|
|
||||||
Return the message's score as computed for auto-learning. Certain tests are
|
Return the message's score as computed for auto-learning. Certain tests are
|
||||||
ignored:
|
ignored:
|
||||||
@ -647,7 +650,7 @@ sub get_autolearn_points {
|
|||||||
return $self->{autolearn_points};
|
return $self->{autolearn_points};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $score = $status->get_head_only_points()
|
=item $score = $status-E<gt>get_head_only_points()
|
||||||
|
|
||||||
Return the message's score as computed for auto-learning, ignoring
|
Return the message's score as computed for auto-learning, ignoring
|
||||||
all rules except for header-based ones.
|
all rules except for header-based ones.
|
||||||
@ -660,7 +663,7 @@ sub get_head_only_points {
|
|||||||
return $self->{head_only_points};
|
return $self->{head_only_points};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $score = $status->get_learned_points()
|
=item $score = $status-E<gt>get_learned_points()
|
||||||
|
|
||||||
Return the message's score as computed for auto-learning, ignoring
|
Return the message's score as computed for auto-learning, ignoring
|
||||||
all rules except for learning-based ones.
|
all rules except for learning-based ones.
|
||||||
@ -673,7 +676,7 @@ sub get_learned_points {
|
|||||||
return $self->{learned_points};
|
return $self->{learned_points};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $score = $status->get_body_only_points()
|
=item $score = $status-E<gt>get_body_only_points()
|
||||||
|
|
||||||
Return the message's score as computed for auto-learning, ignoring
|
Return the message's score as computed for auto-learning, ignoring
|
||||||
all rules except for body-based ones.
|
all rules except for body-based ones.
|
||||||
@ -686,7 +689,7 @@ sub get_body_only_points {
|
|||||||
return $self->{body_only_points};
|
return $self->{body_only_points};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $score = $status->get_autolearn_force_status()
|
=item $score = $status-E<gt>get_autolearn_force_status()
|
||||||
|
|
||||||
Return whether a message's score included any rules that are flagged as
|
Return whether a message's score included any rules that are flagged as
|
||||||
autolearn_force.
|
autolearn_force.
|
||||||
@ -699,7 +702,7 @@ sub get_autolearn_force_status {
|
|||||||
return $self->{autolearn_force};
|
return $self->{autolearn_force};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $rule_names = $status->get_autolearn_force_names()
|
=item $rule_names = $status-E<gt>get_autolearn_force_names()
|
||||||
|
|
||||||
Return a list of comma separated list of rule names if a message's
|
Return a list of comma separated list of rule names if a message's
|
||||||
score included any rules that are flagged as autolearn_force.
|
score included any rules that are flagged as autolearn_force.
|
||||||
@ -856,7 +859,7 @@ sub _get_autolearn_points {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $isspam = $status->is_spam ()
|
=item $isspam = $status-E<gt>is_spam ()
|
||||||
|
|
||||||
After a mail message has been checked, this method can be called. It will
|
After a mail message has been checked, this method can be called. It will
|
||||||
return 1 for mail determined likely to be spam, 0 if it does not seem
|
return 1 for mail determined likely to be spam, 0 if it does not seem
|
||||||
@ -872,7 +875,7 @@ sub is_spam {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $list = $status->get_names_of_tests_hit ()
|
=item $list = $status-E<gt>get_names_of_tests_hit ()
|
||||||
|
|
||||||
After a mail message has been checked, this method can be called. It will
|
After a mail message has been checked, this method can be called. It will
|
||||||
return a comma-separated string, listing all the symbolic test names
|
return a comma-separated string, listing all the symbolic test names
|
||||||
@ -886,7 +889,7 @@ sub get_names_of_tests_hit {
|
|||||||
return join(',', sort @{$self->{test_names_hit}});
|
return join(',', sort @{$self->{test_names_hit}});
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $list = $status->get_names_of_tests_hit_with_scores_hash ()
|
=item $list = $status-E<gt>get_names_of_tests_hit_with_scores_hash ()
|
||||||
|
|
||||||
After a mail message has been checked, this method can be called. It will
|
After a mail message has been checked, this method can be called. It will
|
||||||
return a pointer to a hash for rule & score pairs for all the symbolic
|
return a pointer to a hash for rule & score pairs for all the symbolic
|
||||||
@ -904,7 +907,7 @@ sub get_names_of_tests_hit_with_scores_hash {
|
|||||||
return \%testsscores;
|
return \%testsscores;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $list = $status->get_names_of_tests_hit_with_scores ()
|
=item $list = $status-E<gt>get_names_of_tests_hit_with_scores ()
|
||||||
|
|
||||||
After a mail message has been checked, this method can be called. It will
|
After a mail message has been checked, this method can be called. It will
|
||||||
return a comma-separated string of rule=score pairs for all the symbolic
|
return a comma-separated string of rule=score pairs for all the symbolic
|
||||||
@ -924,7 +927,7 @@ sub get_names_of_tests_hit_with_scores {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $list = $status->get_names_of_subtests_hit ()
|
=item $list = $status-E<gt>get_names_of_subtests_hit ()
|
||||||
|
|
||||||
After a mail message has been checked, this method can be called. It will
|
After a mail message has been checked, this method can be called. It will
|
||||||
return a comma-separated string, listing all the symbolic test names of the
|
return a comma-separated string, listing all the symbolic test names of the
|
||||||
@ -978,7 +981,7 @@ sub get_names_of_subtests_hit {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $num = $status->get_score ()
|
=item $num = $status-E<gt>get_score ()
|
||||||
|
|
||||||
After a mail message has been checked, this method can be called. It will
|
After a mail message has been checked, this method can be called. It will
|
||||||
return the message's score.
|
return the message's score.
|
||||||
@ -998,7 +1001,7 @@ sub get_hits {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $num = $status->get_required_score ()
|
=item $num = $status-E<gt>get_required_score ()
|
||||||
|
|
||||||
After a mail message has been checked, this method can be called. It will
|
After a mail message has been checked, this method can be called. It will
|
||||||
return the score required for a mail to be considered spam.
|
return the score required for a mail to be considered spam.
|
||||||
@ -1018,7 +1021,7 @@ sub get_required_hits {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $num = $status->get_autolearn_status ()
|
=item $num = $status-E<gt>get_autolearn_status ()
|
||||||
|
|
||||||
After a mail message has been checked, this method can be called. It will
|
After a mail message has been checked, this method can be called. It will
|
||||||
return one of the following strings depending on whether the mail was
|
return one of the following strings depending on whether the mail was
|
||||||
@ -1042,7 +1045,7 @@ sub get_autolearn_status {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $report = $status->get_report ()
|
=item $report = $status-E<gt>get_report ()
|
||||||
|
|
||||||
Deliver a "spam report" on the checked mail message. This contains details of
|
Deliver a "spam report" on the checked mail message. This contains details of
|
||||||
how many spam detection rules it triggered.
|
how many spam detection rules it triggered.
|
||||||
@ -1073,7 +1076,7 @@ sub get_report {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $preview = $status->get_content_preview ()
|
=item $preview = $status-E<gt>get_content_preview ()
|
||||||
|
|
||||||
Give a "preview" of the content.
|
Give a "preview" of the content.
|
||||||
|
|
||||||
@ -1118,7 +1121,7 @@ sub get_content_preview {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $msg = $status->get_message()
|
=item $msg = $status-E<gt>get_message()
|
||||||
|
|
||||||
Return the object representing the message being scanned.
|
Return the object representing the message being scanned.
|
||||||
|
|
||||||
@ -1131,7 +1134,7 @@ sub get_message {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->rewrite_mail ()
|
=item $status-E<gt>rewrite_mail ()
|
||||||
|
|
||||||
Rewrite the mail message. This will at minimum add headers, and at
|
Rewrite the mail message. This will at minimum add headers, and at
|
||||||
maximum MIME-encapsulate the message text, to reflect its spam or not-spam
|
maximum MIME-encapsulate the message text, to reflect its spam or not-spam
|
||||||
@ -1622,7 +1625,7 @@ sub _replace_tags {
|
|||||||
|
|
||||||
# public API for plugins
|
# public API for plugins
|
||||||
|
|
||||||
=item $status->action_depends_on_tags($tags, $code, @args)
|
=item $status-E<gt>action_depends_on_tags($tags, $code, @args)
|
||||||
|
|
||||||
Enqueue the supplied subroutine reference C<$code>, to become runnable when
|
Enqueue the supplied subroutine reference C<$code>, to become runnable when
|
||||||
all the specified tags become available. The C<$tags> may be a simple
|
all the specified tags become available. The C<$tags> may be a simple
|
||||||
@ -1736,7 +1739,7 @@ sub report_unsatisfied_actions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $status->set_tag($tagname, $value)
|
=item $status-E<gt>set_tag($tagname, $value)
|
||||||
|
|
||||||
Set a template tag, as used in C<add_header>, report templates, etc. This
|
Set a template tag, as used in C<add_header>, report templates, etc. This
|
||||||
API is intended for use by plugins. Tag names will be converted to an
|
API is intended for use by plugins. Tag names will be converted to an
|
||||||
@ -1778,7 +1781,7 @@ sub set_tag {
|
|||||||
|
|
||||||
# public API for plugins
|
# public API for plugins
|
||||||
|
|
||||||
=item $string = $status->get_tag($tagname)
|
=item $string = $status-E<gt>get_tag($tagname)
|
||||||
|
|
||||||
Get the current value of a template tag, as used in C<add_header>, report
|
Get the current value of a template tag, as used in C<add_header>, report
|
||||||
templates, etc. This API is intended for use by plugins. Tag names will be
|
templates, etc. This API is intended for use by plugins. Tag names will be
|
||||||
@ -1821,7 +1824,7 @@ sub get_tag {
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $string = $status->get_tag_raw($tagname, @args)
|
=item $string = $status-E<gt>get_tag_raw($tagname, @args)
|
||||||
|
|
||||||
Similar to C<get_tag>, but keeps a tag name unchanged (does not uppercase it),
|
Similar to C<get_tag>, but keeps a tag name unchanged (does not uppercase it),
|
||||||
and does not convert arrayref tag values into a single string.
|
and does not convert arrayref tag values into a single string.
|
||||||
@ -1858,7 +1861,7 @@ sub get_tag_raw {
|
|||||||
|
|
||||||
# public API for plugins
|
# public API for plugins
|
||||||
|
|
||||||
=item $status->set_spamd_result_item($subref)
|
=item $status-E<gt>set_spamd_result_item($subref)
|
||||||
|
|
||||||
Set an entry for the spamd result log line. C<$subref> should be a code
|
Set an entry for the spamd result log line. C<$subref> should be a code
|
||||||
reference for a subroutine which will return a string in C<'name=VALUE'>
|
reference for a subroutine which will return a string in C<'name=VALUE'>
|
||||||
@ -1931,7 +1934,7 @@ sub _get_tag_value_for_required_score {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->finish ()
|
=item $status-E<gt>finish ()
|
||||||
|
|
||||||
Indicate that this C<$status> object is finished with, and can be destroyed.
|
Indicate that this C<$status> object is finished with, and can be destroyed.
|
||||||
|
|
||||||
@ -1970,7 +1973,7 @@ sub finish_tests {}
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $name = $status->get_current_eval_rule_name()
|
=item $name = $status-E<gt>get_current_eval_rule_name()
|
||||||
|
|
||||||
Return the name of the currently-running eval rule. C<undef> is
|
Return the name of the currently-running eval rule. C<undef> is
|
||||||
returned if no eval rule is currently being run. Useful for plugins
|
returned if no eval rule is currently being run. Useful for plugins
|
||||||
@ -2051,7 +2054,7 @@ sub extract_message_metadata {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->get_decoded_body_text_array ()
|
=item $status-E<gt>get_decoded_body_text_array ()
|
||||||
|
|
||||||
Returns the message body, with B<base64> or B<quoted-printable> encodings
|
Returns the message body, with B<base64> or B<quoted-printable> encodings
|
||||||
decoded, and non-text parts or non-inline attachments stripped.
|
decoded, and non-text parts or non-inline attachments stripped.
|
||||||
@ -2067,7 +2070,7 @@ sub get_decoded_body_text_array {
|
|||||||
return $_[0]->{msg}->get_decoded_body_text_array();
|
return $_[0]->{msg}->get_decoded_body_text_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $status->get_decoded_stripped_body_text_array ()
|
=item $status-E<gt>get_decoded_stripped_body_text_array ()
|
||||||
|
|
||||||
Returns the message body, decoded (as described in
|
Returns the message body, decoded (as described in
|
||||||
get_decoded_body_text_array()), with HTML rendered, and with whitespace
|
get_decoded_body_text_array()), with HTML rendered, and with whitespace
|
||||||
@ -2089,14 +2092,18 @@ sub get_decoded_stripped_body_text_array {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->get (header_name [, default_value])
|
=item $status-E<gt>get (header_name [, default_value])
|
||||||
|
|
||||||
Returns a message header, pseudo-header or a real name, email-address or
|
Returns message headers, pseudo-headers, names, email-addresses or some
|
||||||
some other parsed value set by modifiers. C<header_name> is the name of a
|
other parsed values set by modifiers. C<header_name> is the name of a mail
|
||||||
mail header, such as 'Subject', 'To', etc.
|
header such as 'Subject', 'To' etc, or a pseudo/metadata-header like 'ALL',
|
||||||
|
'X-Spam-Relays-Untrusted' etc.
|
||||||
|
|
||||||
Should be called in list context since 4.0. Will return list of headers
|
Should be called in list context since SpamAssassin 4.0. This supports
|
||||||
content, or other values when modifiers used.
|
returning multiple values for all header and modifier types.
|
||||||
|
|
||||||
|
If called in scalar context (pre-4.0 style), only first value is returned
|
||||||
|
for modifiers like C<:addr> or C<:name>.
|
||||||
|
|
||||||
If C<default_value> is given, it will be used if the requested
|
If C<default_value> is given, it will be used if the requested
|
||||||
C<header_name> does not exist. This is mainly useful when called in scalar
|
C<header_name> does not exist. This is mainly useful when called in scalar
|
||||||
@ -2116,17 +2123,17 @@ result in "example@foo" (and "example@bar"):
|
|||||||
|
|
||||||
=item example@foo
|
=item example@foo
|
||||||
|
|
||||||
=item example@foo (Foo Blah), <example@bar>
|
=item example@foo (Foo Blah), E<lt>example@barE<gt>
|
||||||
|
|
||||||
=item example@foo, example@bar
|
=item example@foo, example@bar
|
||||||
|
|
||||||
=item display: example@foo (Foo Blah), example@bar ;
|
=item display: example@foo (Foo Blah), example@bar ;
|
||||||
|
|
||||||
=item Foo Blah <example@foo>
|
=item Foo Blah E<lt>example@fooE<gt>
|
||||||
|
|
||||||
=item "Foo Blah" <example@foo>
|
=item "Foo Blah" E<lt>example@fooE<gt>
|
||||||
|
|
||||||
=item "'Foo Blah'" <example@foo>
|
=item "'Foo Blah'" E<lt>example@fooE<gt>
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
@ -2141,15 +2148,15 @@ stripped too, as it is often seen.
|
|||||||
|
|
||||||
=item example@foo (Foo Blah)
|
=item example@foo (Foo Blah)
|
||||||
|
|
||||||
=item example@foo (Foo Blah), "Bar Baz" <example@bar>
|
=item example@foo (Foo Blah), "Bar Baz" E<lt>example@barI<gt>
|
||||||
|
|
||||||
=item display: example@foo (Foo Blah), example@bar ;
|
=item display: example@foo (Foo Blah), example@bar ;
|
||||||
|
|
||||||
=item Foo Blah <example@foo>
|
=item Foo Blah E<lt>example@fooE<gt>
|
||||||
|
|
||||||
=item "Foo Blah" <example@foo>
|
=item "Foo Blah" E<lt>example@fooE<gt>
|
||||||
|
|
||||||
=item "'Foo Blah'" <example@foo>
|
=item "'Foo Blah'" E<lt>example@fooE<gt>
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
@ -2204,10 +2211,11 @@ headers.
|
|||||||
transaction that delivered this message, if this data has been made available
|
transaction that delivered this message, if this data has been made available
|
||||||
by the SMTP server.
|
by the SMTP server.
|
||||||
|
|
||||||
=item C<MESSAGEID> is a symbol meaning all Message-Id's found in the message;
|
=item C<MESSAGEID> is a symbol meaning all Message-Id's found in the
|
||||||
some mailing list software moves the real 'Message-Id' to 'Resent-Message-Id'
|
message; some mailing list software moves the real 'Message-Id' to
|
||||||
or 'X-Message-Id', then uses its own one in the 'Message-Id' header. The value
|
'Resent-Message-Id' or 'X-Message-Id' or 'X-Original-Message-ID', then uses
|
||||||
returned for this symbol is the text from all 3 headers, separated by newlines.
|
its own one in the 'Message-Id' header. The value returned for this symbol
|
||||||
|
is the text from all 4 headers.
|
||||||
|
|
||||||
=item C<X-Spam-Relays-Untrusted> is the generated metadata of untrusted relays
|
=item C<X-Spam-Relays-Untrusted> is the generated metadata of untrusted relays
|
||||||
the message has passed through
|
the message has passed through
|
||||||
@ -2490,7 +2498,7 @@ sub get {
|
|||||||
if ($_[1] =~ /:(?:addr|name|host|domain|ip|revip)\b/ ||
|
if ($_[1] =~ /:(?:addr|name|host|domain|ip|revip)\b/ ||
|
||||||
$_[1] eq 'EnvelopeFrom') {
|
$_[1] eq 'EnvelopeFrom') {
|
||||||
my $res = $found->[0];
|
my $res = $found->[0];
|
||||||
$res =~ s/\n\z$//;
|
$res =~ s/\n\z//;
|
||||||
return $res;
|
return $res;
|
||||||
} else {
|
} else {
|
||||||
return join('', @$found);
|
return join('', @$found);
|
||||||
@ -2569,12 +2577,12 @@ sub _tbirdurire {
|
|||||||
return $self->{tbirdurire};
|
return $self->{tbirdurire};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $status->get_uri_list ()
|
=item $status-E<gt>get_uri_list ()
|
||||||
|
|
||||||
Returns an array of all unique URIs found in the message. It takes
|
Returns an array of all unique URIs found in the message. It takes
|
||||||
a combination of the URIs found in the rendered (decoded and HTML
|
a combination of the URIs found in the rendered (decoded and HTML
|
||||||
stripped) body and the URIs found when parsing the HTML in the message.
|
stripped) body and the URIs found when parsing the HTML in the message.
|
||||||
Will also set $status->{uri_list} (the array as returned by this function).
|
Will also set $status-E<gt>{uri_list} (the array as returned by this function).
|
||||||
|
|
||||||
The returned array will include the "raw" URI as well as
|
The returned array will include the "raw" URI as well as
|
||||||
"slightly cooked" versions. For example, the single URI
|
"slightly cooked" versions. For example, the single URI
|
||||||
@ -2614,13 +2622,13 @@ sub get_uri_list {
|
|||||||
return @{$self->{uri_list}};
|
return @{$self->{uri_list}};
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $status->get_uri_detail_list ()
|
=item $status-E<gt>get_uri_detail_list ()
|
||||||
|
|
||||||
Returns a hash reference of all unique URIs found in the message and
|
Returns a hash reference of all unique URIs found in the message and
|
||||||
various data about where the URIs were found in the message. It takes a
|
various data about where the URIs were found in the message. It takes a
|
||||||
combination of the URIs found in the rendered (decoded and HTML stripped)
|
combination of the URIs found in the rendered (decoded and HTML stripped)
|
||||||
body and the URIs found when parsing the HTML in the message. Will also
|
body and the URIs found when parsing the HTML in the message. Will also
|
||||||
set $status->{uri_detail_list} (the hash reference as returned by this
|
set $status-E<gt>{uri_detail_list} (the hash reference as returned by this
|
||||||
function).
|
function).
|
||||||
|
|
||||||
The hash format looks something like this:
|
The hash format looks something like this:
|
||||||
@ -2649,8 +2657,8 @@ linkifying (i.e. email address found in body without mailto:).
|
|||||||
C<cleaned> is an array of the raw and canonicalized version of the raw_uri
|
C<cleaned> is an array of the raw and canonicalized version of the raw_uri
|
||||||
(http://spamassassin.apache%2Eorg/, https://spamassassin.apache.org/).
|
(http://spamassassin.apache%2Eorg/, https://spamassassin.apache.org/).
|
||||||
|
|
||||||
C<anchor_text> is an array of the anchor text (text between <a> and
|
C<anchor_text> is an array of the anchor text (text between E<lt>aE<gt> and
|
||||||
</a>), if any, which linked to the URI.
|
E<lt>/aE<gt>), if any, which linked to the URI.
|
||||||
|
|
||||||
C<domains> is a hash of the domains found in the canonicalized URIs.
|
C<domains> is a hash of the domains found in the canonicalized URIs.
|
||||||
|
|
||||||
@ -2835,7 +2843,7 @@ sub _process_dkim_uri_list {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $status->add_uri_detail_list ($raw_uri, $types, $source, $valid_domain)
|
=item $status-E<gt>add_uri_detail_list ($raw_uri, $types, $source, $valid_domain)
|
||||||
|
|
||||||
Adds values to internal uri_detail_list. When used from Plugins, recommended
|
Adds values to internal uri_detail_list. When used from Plugins, recommended
|
||||||
to call from parsed_metadata (along with register_method_priority, -10) so
|
to call from parsed_metadata (along with register_method_priority, -10) so
|
||||||
@ -2844,10 +2852,10 @@ other Plugins calling get_uri_detail_list() will see it.
|
|||||||
C<raw_uri> is the URI to be added. The only required parameter.
|
C<raw_uri> is the URI to be added. The only required parameter.
|
||||||
|
|
||||||
C<types> is an optional hash reference, contents are added to
|
C<types> is an optional hash reference, contents are added to
|
||||||
uri_detail_list->{types} (see get_uri_detail_list for known keys).
|
uri_detail_list-E<gt>{types} (see get_uri_detail_list for known keys).
|
||||||
I<parsed> is default is no hash given. I<nocanon> does not run
|
I<parsed> is default is no hash given. I<nocanon> does not run
|
||||||
uri_list_canonicalize (no redirector, uri fixing). I<noclean> skips adding
|
uri_list_canonicalize (no redirector, uri fixing). I<noclean> skips adding
|
||||||
uri_detail_list->{cleaned}, so it would not be used in "uri" rule checks,
|
uri_detail_list-E<gt>{cleaned}, so it would not be used in "uri" rule checks,
|
||||||
but domain/hosts would still be used for URIBL/RBL purposes.
|
but domain/hosts would still be used for URIBL/RBL purposes.
|
||||||
|
|
||||||
C<source> is an optional simple string, only used for debug logging purposes
|
C<source> is an optional simple string, only used for debug logging purposes
|
||||||
@ -2979,7 +2987,7 @@ ENDOFEVAL
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->clear_test_state()
|
=item $status-E<gt>clear_test_state()
|
||||||
|
|
||||||
DEPRECATED, UNNEEDED SINCE 4.0
|
DEPRECATED, UNNEEDED SINCE 4.0
|
||||||
|
|
||||||
@ -3037,7 +3045,7 @@ sub _wrap_desc {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->got_hit ($rulename, $desc_prepend [, name => value, ...])
|
=item $status-E<gt>got_hit ($rulename, $desc_prepend [, name =E<gt> value, ...])
|
||||||
|
|
||||||
Register a hit against a rule in the ruleset.
|
Register a hit against a rule in the ruleset.
|
||||||
|
|
||||||
@ -3050,37 +3058,37 @@ data:
|
|||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item score => $num
|
=item score =E<gt> $num
|
||||||
|
|
||||||
Optional: the score to use for the rule hit. If unspecified,
|
Optional: the score to use for the rule hit. If unspecified,
|
||||||
the value from the C<Mail::SpamAssassin::Conf> object's C<{scores}>
|
the value from the C<Mail::SpamAssassin::Conf> object's C<{scores}>
|
||||||
hash will be used (a configured score), and in its absence the
|
hash will be used (a configured score), and in its absence the
|
||||||
C<defscore> option value.
|
C<defscore> option value.
|
||||||
|
|
||||||
=item defscore => $num
|
=item defscore =E<gt> $num
|
||||||
|
|
||||||
Optional: the score to use for the rule hit if neither the
|
Optional: the score to use for the rule hit if neither the
|
||||||
option C<score> is provided, nor a configured score value is provided.
|
option C<score> is provided, nor a configured score value is provided.
|
||||||
|
|
||||||
=item value => $num
|
=item value =E<gt> $num
|
||||||
|
|
||||||
Optional: the value to assign to the rule; the default value is C<1>.
|
Optional: the value to assign to the rule; the default value is C<1>.
|
||||||
I<tflags multiple> rules use values of greater than 1 to indicate
|
I<tflags multiple> rules use values of greater than 1 to indicate
|
||||||
multiple hits. This value is accessible to meta rules.
|
multiple hits. This value is accessible to meta rules.
|
||||||
|
|
||||||
=item ruletype => $type
|
=item ruletype =E<gt> $type
|
||||||
|
|
||||||
Optional, but recommended: the rule type string. This is used in the
|
Optional, but recommended: the rule type string. This is used in the
|
||||||
C<hit_rule> plugin call, called by this method. If unset, I<'unknown'> is
|
C<hit_rule> plugin call, called by this method. If unset, I<'unknown'> is
|
||||||
used.
|
used.
|
||||||
|
|
||||||
=item tflags => $string
|
=item tflags =E<gt> $string
|
||||||
|
|
||||||
Optional: a string, i.e. a space-separated list of additional tflags
|
Optional: a string, i.e. a space-separated list of additional tflags
|
||||||
to be appended to an existing list of flags in $self->{conf}->{tflags},
|
to be appended to an existing list of flags in $self-E<gt>{conf}-E<gt>{tflags},
|
||||||
such as: "nice noautolearn multiple". No syntax checks are performed.
|
such as: "nice noautolearn multiple". No syntax checks are performed.
|
||||||
|
|
||||||
=item description => $string
|
=item description =E<gt> $string
|
||||||
|
|
||||||
Optional: a custom rule description string. This is used in the
|
Optional: a custom rule description string. This is used in the
|
||||||
C<hit_rule> plugin call, called by this method. If unset, the static
|
C<hit_rule> plugin call, called by this method. If unset, the static
|
||||||
@ -3089,7 +3097,7 @@ description is used.
|
|||||||
=back
|
=back
|
||||||
|
|
||||||
Backward compatibility: the two mandatory arguments have been part of this API
|
Backward compatibility: the two mandatory arguments have been part of this API
|
||||||
since SpamAssassin 2.x. The optional I<name=<gt>value> pairs, however, are a
|
since SpamAssassin 2.x. The optional C<name=E<gt>value> pairs, however, are a
|
||||||
new addition in SpamAssassin 3.2.0.
|
new addition in SpamAssassin 3.2.0.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
@ -3173,13 +3181,13 @@ sub got_hit {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $status->rule_ready ($rulename [, $no_async])
|
=item $status-E<gt>rule_ready ($rulename [, $no_async])
|
||||||
|
|
||||||
Mark an asynchronous rule ready, so it can be considered for meta rule
|
Mark an asynchronous rule ready, so it can be considered for meta rule
|
||||||
evaluation. Asynchronous rule is a rule whose eval-function returns undef,
|
evaluation. Asynchronous rule is a rule whose eval-function returns undef,
|
||||||
marking that it's not ready yet, expecting results later.
|
marking that it's not ready yet, expecting results later.
|
||||||
$status->rule_ready() must be called later to mark it ready, alternatively
|
$status-E<gt>rule_ready() must be called later to mark it ready, alternatively
|
||||||
$status->got_hit() also does this. If neither is called, then any meta rule
|
$status-E<gt>got_hit() also does this. If neither is called, then any meta rule
|
||||||
that depends on this rule might not evaluate.
|
that depends on this rule might not evaluate.
|
||||||
|
|
||||||
Optional boolean $no_async skips checking if there are pending async DNS
|
Optional boolean $no_async skips checking if there are pending async DNS
|
||||||
@ -3209,12 +3217,12 @@ sub rule_ready {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->test_log ($text [, $rulename])
|
=item $status-E<gt>test_log ($text [, $rulename])
|
||||||
|
|
||||||
Add $text log entry for a hit rule in final message REPORT/SUMMARY.
|
Add $text log entry for a hit rule in final message REPORT/SUMMARY.
|
||||||
|
|
||||||
Usually called just before got_hit(), to describe for example what URI the
|
Usually called just before got_hit(), to describe for example what URI the
|
||||||
rule matched on. Optional <$rulename> argument is recommended to make sure
|
rule matched on. Optional C<$rulename> argument is recommended to make sure
|
||||||
log is written to correct rule. If rulename is not provided,
|
log is written to correct rule. If rulename is not provided,
|
||||||
get_current_eval_rule_name() is used as fallback.
|
get_current_eval_rule_name() is used as fallback.
|
||||||
|
|
||||||
@ -3431,7 +3439,7 @@ sub sa_die { Mail::SpamAssassin::sa_die(@_); }
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $status->create_fulltext_tmpfile (fulltext_ref)
|
=item $status-E<gt>create_fulltext_tmpfile (fulltext_ref)
|
||||||
|
|
||||||
This function creates a temporary file containing the passed scalar
|
This function creates a temporary file containing the passed scalar
|
||||||
reference data. If no scalar is passed, full/pristine message text is
|
reference data. If no scalar is passed, full/pristine message text is
|
||||||
@ -3480,9 +3488,9 @@ sub create_fulltext_tmpfile {
|
|||||||
return $tmpf;
|
return $tmpf;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $status->delete_fulltext_tmpfile (tmpfile)
|
=item $status-E<gt>delete_fulltext_tmpfile (tmpfile)
|
||||||
|
|
||||||
Will cleanup after a $status->create_fulltext_tmpfile() call. Deletes the
|
Will cleanup after a $status-E<gt>create_fulltext_tmpfile() call. Deletes the
|
||||||
temporary file and uncaches the filename. Generally there no need to call
|
temporary file and uncaches the filename. Generally there no need to call
|
||||||
this, PerMsgStatus destructor cleans up all tmpfiles.
|
this, PerMsgStatus destructor cleans up all tmpfiles.
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ our @ISA = qw();
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $factory = PersistentAddrListSubclass->new();
|
=item $factory = PersistentAddrListSubclass-E<gt>new();
|
||||||
|
|
||||||
This creates a factory object, which SpamAssassin will call to create
|
This creates a factory object, which SpamAssassin will call to create
|
||||||
a new checker object for the persistent address list.
|
a new checker object for the persistent address list.
|
||||||
@ -72,7 +72,7 @@ sub new {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item my $addrlist = $factory->new_checker();
|
=item my $addrlist = $factory-E<gt>new_checker();
|
||||||
|
|
||||||
Create a new address-list checker object from the factory. Called by the
|
Create a new address-list checker object from the factory. Called by the
|
||||||
SpamAssassin classes.
|
SpamAssassin classes.
|
||||||
@ -86,7 +86,7 @@ sub new_checker {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $entry = $addrlist->get_addr_entry ($addr);
|
=item $entry = $addrlist-E<gt>get_addr_entry ($addr);
|
||||||
|
|
||||||
Given an email address C<$addr>, return an entry object with the details of
|
Given an email address C<$addr>, return an entry object with the details of
|
||||||
that address.
|
that address.
|
||||||
@ -115,7 +115,7 @@ sub get_addr_entry {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $entry = $addrlist->add_score($entry, $score);
|
=item $entry = $addrlist-E<gt>add_score($entry, $score);
|
||||||
|
|
||||||
This method should add the given score to the welcomelist database for the
|
This method should add the given score to the welcomelist database for the
|
||||||
given entry, and then return the new entry.
|
given entry, and then return the new entry.
|
||||||
@ -129,7 +129,7 @@ sub add_score {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $entry = $addrlist->remove_entry ($entry);
|
=item $entry = $addrlist-E<gt>remove_entry ($entry);
|
||||||
|
|
||||||
This method should remove the given entry from the welcomelist database.
|
This method should remove the given entry from the welcomelist database.
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ sub remove_entry {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $entry = $addrlist->finish ();
|
=item $entry = $addrlist-E<gt>finish ();
|
||||||
|
|
||||||
Clean up, if necessary. Called by SpamAssassin when it has finished
|
Clean up, if necessary. Called by SpamAssassin when it has finished
|
||||||
checking, or adding to, the auto-welcomelist database.
|
checking, or adding to, the auto-welcomelist database.
|
||||||
|
@ -108,7 +108,7 @@ our @ISA = qw();
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $plugin = MyPluginClass->new ($mailsaobject)
|
=item $plugin = MyPluginClass-E<gt>new ($mailsaobject)
|
||||||
|
|
||||||
Constructor. Plugins that need to register themselves will need to
|
Constructor. Plugins that need to register themselves will need to
|
||||||
define their own; the default super-class constructor will work fine
|
define their own; the default super-class constructor will work fine
|
||||||
@ -149,7 +149,7 @@ sub new {
|
|||||||
# the object and determine if it's capable of calling the method anyway.
|
# the object and determine if it's capable of calling the method anyway.
|
||||||
# Nifty!
|
# Nifty!
|
||||||
|
|
||||||
=item $plugin->parse_config ( { options ... } )
|
=item $plugin-E<gt>parse_config ( { options ... } )
|
||||||
|
|
||||||
Parse a configuration line that hasn't already been handled. C<options>
|
Parse a configuration line that hasn't already been handled. C<options>
|
||||||
is a reference to a hash containing these options:
|
is a reference to a hash containing these options:
|
||||||
@ -195,7 +195,7 @@ That can be found as C<$plugin-E<gt>{main}-E<gt>{conf}>, or as "conf" in the
|
|||||||
C<$options> hash reference above. By storing it on C<conf>, this allows
|
C<$options> hash reference above. By storing it on C<conf>, this allows
|
||||||
per-user and system-wide configuration precedence to be dealt with correctly.
|
per-user and system-wide configuration precedence to be dealt with correctly.
|
||||||
|
|
||||||
=item $plugin->finish_parsing_start ( { options ... } )
|
=item $plugin-E<gt>finish_parsing_start ( { options ... } )
|
||||||
|
|
||||||
Signals that the system-wide configuration has been completely read,
|
Signals that the system-wide configuration has been completely read,
|
||||||
but internal data structures are not yet created. It is possible to
|
but internal data structures are not yet created. It is possible to
|
||||||
@ -219,7 +219,7 @@ this plugin hook, if you modify the rules data structures in a
|
|||||||
third-party plugin, all bets are off until such time that an API is
|
third-party plugin, all bets are off until such time that an API is
|
||||||
present for modifying that configuration data.
|
present for modifying that configuration data.
|
||||||
|
|
||||||
=item $plugin->finish_parsing_end ( { options ... } )
|
=item $plugin-E<gt>finish_parsing_end ( { options ... } )
|
||||||
|
|
||||||
Signals that the system-wide configuration parsing has just finished, and
|
Signals that the system-wide configuration parsing has just finished, and
|
||||||
SpamAssassin is nearly ready to check messages.
|
SpamAssassin is nearly ready to check messages.
|
||||||
@ -241,7 +241,7 @@ this plugin hook, if you modify the rules data structures in a
|
|||||||
third-party plugin, all bets are off until such time that an API is
|
third-party plugin, all bets are off until such time that an API is
|
||||||
present for modifying that configuration data.
|
present for modifying that configuration data.
|
||||||
|
|
||||||
=item $plugin->user_conf_parsing_start ( { options ... } )
|
=item $plugin-E<gt>user_conf_parsing_start ( { options ... } )
|
||||||
|
|
||||||
Signals that the per-user configuration has been completely read, but
|
Signals that the per-user configuration has been completely read, but
|
||||||
not converted to internal data structures. It is possible to use this
|
not converted to internal data structures. It is possible to use this
|
||||||
@ -267,7 +267,7 @@ this plugin hook, if you modify the rules data structures in a
|
|||||||
third-party plugin, all bets are off until such time that an API is
|
third-party plugin, all bets are off until such time that an API is
|
||||||
present for modifying that configuration data.
|
present for modifying that configuration data.
|
||||||
|
|
||||||
=item $plugin->user_conf_parsing_end ( { options ... } )
|
=item $plugin-E<gt>user_conf_parsing_end ( { options ... } )
|
||||||
|
|
||||||
Signals that the per-user configuration parsing has just finished, and
|
Signals that the per-user configuration parsing has just finished, and
|
||||||
SpamAssassin is nearly ready to check messages. If C<allow_user_rules> is
|
SpamAssassin is nearly ready to check messages. If C<allow_user_rules> is
|
||||||
@ -291,7 +291,7 @@ this plugin hook, if you modify the rules data structures in a
|
|||||||
third-party plugin, all bets are off until such time that an API is
|
third-party plugin, all bets are off until such time that an API is
|
||||||
present for modifying that configuration data.
|
present for modifying that configuration data.
|
||||||
|
|
||||||
=item $plugin->signal_user_changed ( { options ... } )
|
=item $plugin-E<gt>signal_user_changed ( { options ... } )
|
||||||
|
|
||||||
Signals that the current user has changed for a new one.
|
Signals that the current user has changed for a new one.
|
||||||
|
|
||||||
@ -311,7 +311,7 @@ The new user's storage directory. (equivalent to C<~/.spamassassin>.)
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->services_authorized_for_username ( { options ... } )
|
=item $plugin-E<gt>services_authorized_for_username ( { options ... } )
|
||||||
|
|
||||||
Validates that a given username is authorized to use certain services.
|
Validates that a given username is authorized to use certain services.
|
||||||
|
|
||||||
@ -344,7 +344,7 @@ data should be stored.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->compile_now_start ( { options ... } )
|
=item $plugin-E<gt>compile_now_start ( { options ... } )
|
||||||
|
|
||||||
This is called at the beginning of Mail::SpamAssassin::compile_now() so
|
This is called at the beginning of Mail::SpamAssassin::compile_now() so
|
||||||
plugins can do any necessary initialization for multi-process
|
plugins can do any necessary initialization for multi-process
|
||||||
@ -362,7 +362,7 @@ The value of $keep_userstate option in compile_now().
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->compile_now_finish ( { options ... } )
|
=item $plugin-E<gt>compile_now_finish ( { options ... } )
|
||||||
|
|
||||||
This is called at the end of Mail::SpamAssassin::compile_now() so
|
This is called at the end of Mail::SpamAssassin::compile_now() so
|
||||||
plugins can do any necessary initialization for multi-process
|
plugins can do any necessary initialization for multi-process
|
||||||
@ -380,7 +380,7 @@ The value of $keep_userstate option in compile_now().
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->check_start ( { options ... } )
|
=item $plugin-E<gt>check_start ( { options ... } )
|
||||||
|
|
||||||
Signals that a message check operation is starting.
|
Signals that a message check operation is starting.
|
||||||
|
|
||||||
@ -400,7 +400,7 @@ APIs on that object, too. See C<Mail::SpamAssassin::PerMsgStatus> perldoc.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->check_main ( { options ... } )
|
=item $plugin-E<gt>check_main ( { options ... } )
|
||||||
|
|
||||||
Signals that a message should be checked. Note that implementations of
|
Signals that a message should be checked. Note that implementations of
|
||||||
this hook should return C<1>.
|
this hook should return C<1>.
|
||||||
@ -413,7 +413,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->check_tick ( { options ... } )
|
=item $plugin-E<gt>check_tick ( { options ... } )
|
||||||
|
|
||||||
Called periodically during a message check operation. A callback set for
|
Called periodically during a message check operation. A callback set for
|
||||||
this method is a good place to run through an event loop dealing with
|
this method is a good place to run through an event loop dealing with
|
||||||
@ -427,7 +427,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->check_dnsbl ( { options ... } )
|
=item $plugin-E<gt>check_dnsbl ( { options ... } )
|
||||||
|
|
||||||
Called when DNSBL or other network lookups are being launched, implying
|
Called when DNSBL or other network lookups are being launched, implying
|
||||||
current running priority of -100. This is the place to start your own
|
current running priority of -100. This is the place to start your own
|
||||||
@ -441,7 +441,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->check_post_dnsbl ( { options ... } )
|
=item $plugin-E<gt>check_post_dnsbl ( { options ... } )
|
||||||
|
|
||||||
Called after the DNSBL results have been harvested. This is a good
|
Called after the DNSBL results have been harvested. This is a good
|
||||||
place to harvest your own asynchronously-started network lookups.
|
place to harvest your own asynchronously-started network lookups.
|
||||||
@ -454,7 +454,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->check_cleanup ( { options ... } )
|
=item $plugin-E<gt>check_cleanup ( { options ... } )
|
||||||
|
|
||||||
Called just before message check is finishing and before possible
|
Called just before message check is finishing and before possible
|
||||||
auto-learning. This is guaranteed to be always called, unlike check_tick
|
auto-learning. This is guaranteed to be always called, unlike check_tick
|
||||||
@ -469,7 +469,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->check_post_learn ( { options ... } )
|
=item $plugin-E<gt>check_post_learn ( { options ... } )
|
||||||
|
|
||||||
Called after auto-learning may (or may not) have taken place. If you
|
Called after auto-learning may (or may not) have taken place. If you
|
||||||
wish to perform additional learning, whether or not auto-learning
|
wish to perform additional learning, whether or not auto-learning
|
||||||
@ -483,7 +483,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->check_end ( { options ... } )
|
=item $plugin-E<gt>check_end ( { options ... } )
|
||||||
|
|
||||||
Signals that a message check operation has just finished, and the
|
Signals that a message check operation has just finished, and the
|
||||||
results are about to be returned to the caller.
|
results are about to be returned to the caller.
|
||||||
@ -498,7 +498,7 @@ using the public APIs on this object.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->finish_tests ( { options ... } )
|
=item $plugin-E<gt>finish_tests ( { options ... } )
|
||||||
|
|
||||||
Called via C<Mail::SpamAssassin::finish>. This should clear up any tests that
|
Called via C<Mail::SpamAssassin::finish>. This should clear up any tests that
|
||||||
a plugin has added to the namespace.
|
a plugin has added to the namespace.
|
||||||
@ -521,7 +521,7 @@ data should be stored.
|
|||||||
|
|
||||||
See also the C<register_generated_rule_method> helper API, below.
|
See also the C<register_generated_rule_method> helper API, below.
|
||||||
|
|
||||||
=item $plugin->extract_metadata ( { options ... } )
|
=item $plugin-E<gt>extract_metadata ( { options ... } )
|
||||||
|
|
||||||
Signals that a message is being mined for metadata. Some plugins may wish
|
Signals that a message is being mined for metadata. Some plugins may wish
|
||||||
to add their own metadata as well.
|
to add their own metadata as well.
|
||||||
@ -538,7 +538,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->parsed_metadata ( { options ... } )
|
=item $plugin-E<gt>parsed_metadata ( { options ... } )
|
||||||
|
|
||||||
Signals that a message's metadata has been parsed, and can now be
|
Signals that a message's metadata has been parsed, and can now be
|
||||||
accessed by the plugin.
|
accessed by the plugin.
|
||||||
@ -551,7 +551,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->start_rules ( { options ... } )
|
=item $plugin-E<gt>start_rules ( { options ... } )
|
||||||
|
|
||||||
Called before testing a set of rules of a given type and priority.
|
Called before testing a set of rules of a given type and priority.
|
||||||
|
|
||||||
@ -571,7 +571,7 @@ The priority level of the rules about to be performed.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->hit_rule ( { options ... } )
|
=item $plugin-E<gt>hit_rule ( { options ... } )
|
||||||
|
|
||||||
Called when a rule fires.
|
Called when a rule fires.
|
||||||
|
|
||||||
@ -595,7 +595,7 @@ The rule's score in the active scoreset.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->ran_rule ( { options ... } )
|
=item $plugin-E<gt>ran_rule ( { options ... } )
|
||||||
|
|
||||||
Called after a rule has been tested, whether or not it fired. When the
|
Called after a rule has been tested, whether or not it fired. When the
|
||||||
rule fires, the hit_rule callback is always called before this.
|
rule fires, the hit_rule callback is always called before this.
|
||||||
@ -616,7 +616,7 @@ The name of the rule that was tested.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->autolearn_discriminator ( { options ... } )
|
=item $plugin-E<gt>autolearn_discriminator ( { options ... } )
|
||||||
|
|
||||||
Control whether a just-scanned message should be learned as either
|
Control whether a just-scanned message should be learned as either
|
||||||
spam or ham. This method should return one of C<1> to learn
|
spam or ham. This method should return one of C<1> to learn
|
||||||
@ -631,7 +631,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->autolearn ( { options ... } )
|
=item $plugin-E<gt>autolearn ( { options ... } )
|
||||||
|
|
||||||
Signals that a message is about to be auto-learned as either ham or spam.
|
Signals that a message is about to be auto-learned as either ham or spam.
|
||||||
|
|
||||||
@ -647,7 +647,7 @@ C<1> if the message is spam, C<0> if ham.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->per_msg_finish ( { options ... } )
|
=item $plugin-E<gt>per_msg_finish ( { options ... } )
|
||||||
|
|
||||||
Signals that a C<Mail::SpamAssassin::PerMsgStatus> object is being
|
Signals that a C<Mail::SpamAssassin::PerMsgStatus> object is being
|
||||||
destroyed, and any per-scan context held on that object by this
|
destroyed, and any per-scan context held on that object by this
|
||||||
@ -667,7 +667,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
=back
|
=back
|
||||||
|
|
||||||
|
|
||||||
=item $plugin->have_shortcircuited ( { options ... } )
|
=item $plugin-E<gt>have_shortcircuited ( { options ... } )
|
||||||
|
|
||||||
Has the current scan operation 'short-circuited'? In other words, can
|
Has the current scan operation 'short-circuited'? In other words, can
|
||||||
further scanning be skipped, since the message is already definitively
|
further scanning be skipped, since the message is already definitively
|
||||||
@ -684,7 +684,7 @@ The C<Mail::SpamAssassin::PerMsgStatus> context object for this scan.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->bayes_learn ( { options ... } )
|
=item $plugin-E<gt>bayes_learn ( { options ... } )
|
||||||
|
|
||||||
Called at the end of a bayes learn operation.
|
Called at the end of a bayes learn operation.
|
||||||
|
|
||||||
@ -709,7 +709,7 @@ are a single string containing the raw token value. You can test for
|
|||||||
backward compatibility by checking to see if the value for a key is a
|
backward compatibility by checking to see if the value for a key is a
|
||||||
reference to a perl HASH, for instance:
|
reference to a perl HASH, for instance:
|
||||||
|
|
||||||
if (ref($toksref->{$sometokenkey}) eq 'HASH') {...
|
if (ref($toksref-E<gt>{$sometokenkey}) eq 'HASH') {...
|
||||||
|
|
||||||
If it is, then you are using the old interface, otherwise you are using
|
If it is, then you are using the old interface, otherwise you are using
|
||||||
the current interface.
|
the current interface.
|
||||||
@ -732,7 +732,7 @@ could not be determined. In addition, if the receive date is more than
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->bayes_forget ( { options ... } )
|
=item $plugin-E<gt>bayes_forget ( { options ... } )
|
||||||
|
|
||||||
Called at the end of a bayes forget operation.
|
Called at the end of a bayes forget operation.
|
||||||
|
|
||||||
@ -755,7 +755,7 @@ Generated message id of the message just forgotten.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->bayes_scan ( { options ... } )
|
=item $plugin-E<gt>bayes_scan ( { options ... } )
|
||||||
|
|
||||||
Called at the end of a bayes scan operation. NOTE: Will not be
|
Called at the end of a bayes scan operation. NOTE: Will not be
|
||||||
called in case of error or if the message is otherwise skipped.
|
called in case of error or if the message is otherwise skipped.
|
||||||
@ -797,7 +797,7 @@ this message.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->plugin_report ( { options ... } )
|
=item $plugin-E<gt>plugin_report ( { options ... } )
|
||||||
|
|
||||||
Called if the message is to be reported as spam. If the reporting system is
|
Called if the message is to be reported as spam. If the reporting system is
|
||||||
available, the variable C<$options-E<gt>{report}-E<gt>report_available}> should
|
available, the variable C<$options-E<gt>{report}-E<gt>report_available}> should
|
||||||
@ -821,7 +821,7 @@ Reference to the original message object.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->plugin_revoke ( { options ... } )
|
=item $plugin-E<gt>plugin_revoke ( { options ... } )
|
||||||
|
|
||||||
Called if the message is to be reported as ham (revokes a spam report). If the
|
Called if the message is to be reported as ham (revokes a spam report). If the
|
||||||
reporting system is available, the variable
|
reporting system is available, the variable
|
||||||
@ -846,7 +846,7 @@ Reference to the original message object.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->welcomelist_address( { options ... } )
|
=item $plugin-E<gt>welcomelist_address( { options ... } )
|
||||||
|
|
||||||
Previously whitelist_address which will work interchangeably until 4.1.
|
Previously whitelist_address which will work interchangeably until 4.1.
|
||||||
|
|
||||||
@ -865,7 +865,7 @@ Indicate if the call is being made from a command line interface.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->blocklist_address( { options ... } )
|
=item $plugin-E<gt>blocklist_address( { options ... } )
|
||||||
|
|
||||||
Previously blacklist_address which will work interchangeably until 4.1.
|
Previously blacklist_address which will work interchangeably until 4.1.
|
||||||
|
|
||||||
@ -884,7 +884,7 @@ Indicate if the call is being made from a command line interface.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->remove_address( { options ... } )
|
=item $plugin-E<gt>remove_address( { options ... } )
|
||||||
|
|
||||||
Called when a request is made to remove an address to a
|
Called when a request is made to remove an address to a
|
||||||
persistent address list.
|
persistent address list.
|
||||||
@ -901,11 +901,11 @@ Indicate if the call is being made from a command line interface.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->spamd_child_init ()
|
=item $plugin-E<gt>spamd_child_init ()
|
||||||
|
|
||||||
Called in each new child process when it starts up under spamd.
|
Called in each new child process when it starts up under spamd.
|
||||||
|
|
||||||
=item $plugin->log_scan_result ( { options ... } )
|
=item $plugin-E<gt>log_scan_result ( { options ... } )
|
||||||
|
|
||||||
Called when spamd has completed scanning a message. Currently,
|
Called when spamd has completed scanning a message. Currently,
|
||||||
only spamd calls this API.
|
only spamd calls this API.
|
||||||
@ -919,14 +919,14 @@ at B<https://wiki.apache.org/spamassassin/SpamdSyslogFormat>.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->spamd_child_post_connection_close ()
|
=item $plugin-E<gt>spamd_child_post_connection_close ()
|
||||||
|
|
||||||
Called when child returns from handling a connection.
|
Called when child returns from handling a connection.
|
||||||
|
|
||||||
If there was an accept failure, the child will die and this code will
|
If there was an accept failure, the child will die and this code will
|
||||||
not be called.
|
not be called.
|
||||||
|
|
||||||
=item $plugin->finish ()
|
=item $plugin-E<gt>finish ()
|
||||||
|
|
||||||
Called when the C<Mail::SpamAssassin> object is destroyed.
|
Called when the C<Mail::SpamAssassin> object is destroyed.
|
||||||
|
|
||||||
@ -937,13 +937,13 @@ sub finish {
|
|||||||
%{$self} = ();
|
%{$self} = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $plugin->learner_new ()
|
=item $plugin-E<gt>learner_new ()
|
||||||
|
|
||||||
Used to support human-trained probabilistic classifiers like the BAYES_* ruleset.
|
Used to support human-trained probabilistic classifiers like the BAYES_* ruleset.
|
||||||
Called when a new C<Mail::SpamAssassin::Bayes> object has been created; typically
|
Called when a new C<Mail::SpamAssassin::Bayes> object has been created; typically
|
||||||
when a new user's scan is about to start.
|
when a new user's scan is about to start.
|
||||||
|
|
||||||
=item $plugin->learn_message ()
|
=item $plugin-E<gt>learn_message ()
|
||||||
|
|
||||||
Train the classifier with a training message.
|
Train the classifier with a training message.
|
||||||
|
|
||||||
@ -964,7 +964,7 @@ If it is C<undef>, one will be generated. It should be unique to that message.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->forget_message ()
|
=item $plugin-E<gt>forget_message ()
|
||||||
|
|
||||||
Tell the classifier to 'forget' its training about a specific message.
|
Tell the classifier to 'forget' its training about a specific message.
|
||||||
|
|
||||||
@ -981,7 +981,7 @@ If it is C<undef>, one will be generated. It should be unique to that message.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->learner_sync ()
|
=item $plugin-E<gt>learner_sync ()
|
||||||
|
|
||||||
Tell the classifier to 'sync' any pending changes against the current
|
Tell the classifier to 'sync' any pending changes against the current
|
||||||
user's training database. This is called by C<sa-learn --sync>.
|
user's training database. This is called by C<sa-learn --sync>.
|
||||||
@ -989,7 +989,7 @@ user's training database. This is called by C<sa-learn --sync>.
|
|||||||
If you do not need to implement these for your classifier, create an
|
If you do not need to implement these for your classifier, create an
|
||||||
implementation that just contains C<return 1>.
|
implementation that just contains C<return 1>.
|
||||||
|
|
||||||
=item $plugin->learner_expire_old_training ()
|
=item $plugin-E<gt>learner_expire_old_training ()
|
||||||
|
|
||||||
Tell the classifier to perform infrequent, time-consuming cleanup of
|
Tell the classifier to perform infrequent, time-consuming cleanup of
|
||||||
the current user's training database. This is called by C<sa-learn
|
the current user's training database. This is called by C<sa-learn
|
||||||
@ -998,12 +998,12 @@ the current user's training database. This is called by C<sa-learn
|
|||||||
If you do not need to implement these for your classifier, create an
|
If you do not need to implement these for your classifier, create an
|
||||||
implementation that just contains C<return 1>.
|
implementation that just contains C<return 1>.
|
||||||
|
|
||||||
=item $plugin->learner_is_scan_available ()
|
=item $plugin-E<gt>learner_is_scan_available ()
|
||||||
|
|
||||||
Should return 1 if it is possible to use the current user's training data for
|
Should return 1 if it is possible to use the current user's training data for
|
||||||
a message-scan operation, or 0 otherwise.
|
a message-scan operation, or 0 otherwise.
|
||||||
|
|
||||||
=item $plugin->learner_dump_database ()
|
=item $plugin-E<gt>learner_dump_database ()
|
||||||
|
|
||||||
Dump information about the current user's training data to C<stdout>.
|
Dump information about the current user's training data to C<stdout>.
|
||||||
This is called by C<sa-learn --dump>.
|
This is called by C<sa-learn --dump>.
|
||||||
@ -1025,7 +1025,7 @@ subset of the tokens to dump.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=item $plugin->learner_close ()
|
=item $plugin-E<gt>learner_close ()
|
||||||
|
|
||||||
Close any open databases.
|
Close any open databases.
|
||||||
|
|
||||||
@ -1046,7 +1046,7 @@ to receive specific events, or control the callback chain behaviour.
|
|||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item $plugin->register_eval_rule ($nameofevalsub, $ruletype)
|
=item $plugin-E<gt>register_eval_rule ($nameofevalsub, $ruletype)
|
||||||
|
|
||||||
Plugins that implement an eval test will need to call this, so that
|
Plugins that implement an eval test will need to call this, so that
|
||||||
SpamAssassin calls into the object when that eval test is encountered.
|
SpamAssassin calls into the object when that eval test is encountered.
|
||||||
@ -1069,7 +1069,7 @@ sub register_eval_rule {
|
|||||||
$self->{main}->{conf}->register_eval_rule ($self, $nameofsub, $ruletype);
|
$self->{main}->{conf}->register_eval_rule ($self, $nameofsub, $ruletype);
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $plugin->register_generated_rule_method ($nameofsub)
|
=item $plugin-E<gt>register_generated_rule_method ($nameofsub)
|
||||||
|
|
||||||
In certain circumstances, plugins may find it useful to compile
|
In certain circumstances, plugins may find it useful to compile
|
||||||
perl functions from the ruleset, on the fly. It is important to
|
perl functions from the ruleset, on the fly. It is important to
|
||||||
@ -1093,7 +1093,7 @@ sub register_generated_rule_method {
|
|||||||
$nameofsub;
|
$nameofsub;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $plugin->register_method_priority($methodname, $priority)
|
=item $plugin-E<gt>register_method_priority($methodname, $priority)
|
||||||
|
|
||||||
Indicate that the method named C<$methodname> on the current object
|
Indicate that the method named C<$methodname> on the current object
|
||||||
has a callback priority of C<$priority>.
|
has a callback priority of C<$priority>.
|
||||||
@ -1117,7 +1117,7 @@ sub register_method_priority {
|
|||||||
$self->{method_priority}->{$methname} = $pri;
|
$self->{method_priority}->{$methname} = $pri;
|
||||||
}
|
}
|
||||||
|
|
||||||
=item $plugin->inhibit_further_callbacks()
|
=item $plugin-E<gt>inhibit_further_callbacks()
|
||||||
|
|
||||||
Tells the plugin handler to inhibit calling into other plugins in the plugin
|
Tells the plugin handler to inhibit calling into other plugins in the plugin
|
||||||
chain for the current callback. Frequently used when parsing configuration
|
chain for the current callback. Frequently used when parsing configuration
|
||||||
@ -1145,7 +1145,7 @@ Output a debugging message C<$message>, if the SpamAssassin object is running
|
|||||||
with debugging turned on.
|
with debugging turned on.
|
||||||
|
|
||||||
I<NOTE:> This function is not available in the package namespace
|
I<NOTE:> This function is not available in the package namespace
|
||||||
of general plugins and can't be called via $self->dbg(). If a
|
of general plugins and can't be called via $self-E<gt>dbg(). If a
|
||||||
plugin wishes to output debug information, it should call
|
plugin wishes to output debug information, it should call
|
||||||
C<Mail::SpamAssassin::Plugin::dbg($msg)>.
|
C<Mail::SpamAssassin::Plugin::dbg($msg)>.
|
||||||
|
|
||||||
@ -1155,7 +1155,7 @@ Output an informational message C<$message>, if the SpamAssassin object
|
|||||||
is running with informational messages turned on.
|
is running with informational messages turned on.
|
||||||
|
|
||||||
I<NOTE:> This function is not available in the package namespace
|
I<NOTE:> This function is not available in the package namespace
|
||||||
of general plugins and can't be called via $self->info(). If a
|
of general plugins and can't be called via $self-E<gt>info(). If a
|
||||||
plugin wishes to output debug information, it should call
|
plugin wishes to output debug information, it should call
|
||||||
C<Mail::SpamAssassin::Plugin::info($msg)>.
|
C<Mail::SpamAssassin::Plugin::info($msg)>.
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ Autonomous System Number (ASN) of the connecting IP address.
|
|||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
This plugin uses DNS lookups to the services of an external DNS zone such
|
This plugin uses DNS lookups to the services of an external DNS zone such
|
||||||
as at C<http://www.routeviews.org/> to do the actual work. Please make
|
as at C<https://www.routeviews.org/> to do the actual work. Please make
|
||||||
sure that your use of the plugin does not overload their infrastructure -
|
sure that your use of the plugin does not overload their infrastructure -
|
||||||
this generally means that B<you should not use this plugin in a
|
this generally means that B<you should not use this plugin in a
|
||||||
high-volume environment> or that you should use a local mirror of the
|
high-volume environment> or that you should use a local mirror of the
|
||||||
@ -111,6 +111,90 @@ through the I<asn_prefix> directive and may be set to an empty string.
|
|||||||
|
|
||||||
C<_ASNCIDR_> is not available with local GeoDB ASN lookups.
|
C<_ASNCIDR_> is not available with local GeoDB ASN lookups.
|
||||||
|
|
||||||
|
=head1 USER SETTINGS
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item clear_asn_lookups
|
||||||
|
|
||||||
|
Removes all previously declared I<asn_lookup> or I<asn_lookup_ipv6> entries
|
||||||
|
from the list of queries.
|
||||||
|
|
||||||
|
=item asn_prefix 'prefix_string' (default: 'AS')
|
||||||
|
|
||||||
|
The string specified in the argument is prepended to each ASN when storing
|
||||||
|
it as a tag. This prefix is rather redundant, but its default value 'AS'
|
||||||
|
is kept for backward compatibility with versions of SpamAssassin earlier
|
||||||
|
than 3.4.0. A sensible setting is an empty string. The argument may be (but
|
||||||
|
need not be) enclosed in single or double quotes for clarity.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 RULE DEFINITIONS AND PRIVILEGED SETTINGS
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item asn_lookup asn-zone.example.com [ _ASNTAG_ _ASNCIDRTAG_ ]
|
||||||
|
|
||||||
|
Use this to lookup the ASN info in the specified zone for the first external
|
||||||
|
IPv4 address and add the AS number to the first specified tag and routing info
|
||||||
|
to the second specified tag.
|
||||||
|
|
||||||
|
If no tags are specified the AS number will be added to the _ASN_ tag and the
|
||||||
|
routing info will be added to the _ASNCIDR_ tag. You must specify either none
|
||||||
|
or both of the tag names. Tag names must start and end with an underscore.
|
||||||
|
|
||||||
|
If two or more I<asn_lookup>s use the same set of template tags, the results of
|
||||||
|
their lookups will be appended to each other in the template tag values in no
|
||||||
|
particular order. Duplicate results will be omitted when combining results.
|
||||||
|
In a similar fashion, you can also use the same template tag for both the AS
|
||||||
|
number tag and the routing info tag.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
asn_lookup asn.routeviews.org
|
||||||
|
|
||||||
|
asn_lookup asn.routeviews.org _ASN_ _ASNCIDR_
|
||||||
|
asn_lookup myview.example.com _MYASN_ _MYASNCIDR_
|
||||||
|
|
||||||
|
asn_lookup asn.routeviews.org _COMBINEDASN_ _COMBINEDASNCIDR_
|
||||||
|
asn_lookup myview.example.com _COMBINEDASN_ _COMBINEDASNCIDR_
|
||||||
|
|
||||||
|
asn_lookup in1tag.example.net _ASNDATA_ _ASNDATA_
|
||||||
|
|
||||||
|
=item asn_lookup_ipv6 asn-zone6.example.com [_ASN_ _ASNCIDR_]
|
||||||
|
|
||||||
|
Use specified zone for lookups of IPv6 addresses. If zone supports both
|
||||||
|
IPv4 and IPv6 queries, use both asn_lookup and asn_lookup_ipv6 for the same
|
||||||
|
zone.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 ADMINISTRATOR SETTINGS
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item asn_use_geodb ( 0 / 1 ) (default: 1)
|
||||||
|
|
||||||
|
Use Mail::SpamAssassin::GeoDB module to lookup ASN numbers. You need
|
||||||
|
suitable supported module like GeoIP2 or GeoIP with ISP or ASN database
|
||||||
|
installed (for example, add EditionIDs GeoLite2-ASN in GeoIP.conf for
|
||||||
|
geoipupdate program).
|
||||||
|
|
||||||
|
GeoDB can only set _ASN_ tag, it has no data for _ASNCIDR_. If you need
|
||||||
|
both, then set asn_prefer_geodb 0 so DNS rules are tried.
|
||||||
|
|
||||||
|
=item asn_prefer_geodb ( 0 / 1 ) (default: 1)
|
||||||
|
|
||||||
|
If set, DNS lookups (asn_lookup rules) will not be run if GeoDB successfully
|
||||||
|
finds ASN. Set this to 0 to get _ASNCIDR_ even if GeoDB finds _ASN_.
|
||||||
|
|
||||||
|
=item asn_use_dns ( 0 / 1 ) (default: 1)
|
||||||
|
|
||||||
|
Set to 0 to never allow DNS queries.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
=head1 BAYES
|
=head1 BAYES
|
||||||
|
|
||||||
The bayes tokenizer will use ASN data for bayes calculations, and thus
|
The bayes tokenizer will use ASN data for bayes calculations, and thus
|
||||||
@ -120,7 +204,7 @@ been performed.
|
|||||||
|
|
||||||
=head1 SEE ALSO
|
=head1 SEE ALSO
|
||||||
|
|
||||||
http://www.routeviews.org/ - all data regarding routing, ASNs, etc....
|
https://www.routeviews.org/ - all data regarding routing, ASNs, etc....
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
@ -159,82 +243,9 @@ sub set_config {
|
|||||||
my ($self, $conf) = @_;
|
my ($self, $conf) = @_;
|
||||||
my @cmds;
|
my @cmds;
|
||||||
|
|
||||||
=head1 ADMINISTRATOR SETTINGS
|
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=item asn_lookup asn-zone.example.com [ _ASNTAG_ _ASNCIDRTAG_ ]
|
|
||||||
|
|
||||||
Use this to lookup the ASN info in the specified zone for the first external
|
|
||||||
IPv4 address and add the AS number to the first specified tag and routing info
|
|
||||||
to the second specified tag.
|
|
||||||
|
|
||||||
If no tags are specified the AS number will be added to the _ASN_ tag and the
|
|
||||||
routing info will be added to the _ASNCIDR_ tag. You must specify either none
|
|
||||||
or both of the tag names. Tag names must start and end with an underscore.
|
|
||||||
|
|
||||||
If two or more I<asn_lookup>s use the same set of template tags, the results of
|
|
||||||
their lookups will be appended to each other in the template tag values in no
|
|
||||||
particular order. Duplicate results will be omitted when combining results.
|
|
||||||
In a similar fashion, you can also use the same template tag for both the AS
|
|
||||||
number tag and the routing info tag.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
asn_lookup asn.routeviews.org
|
|
||||||
|
|
||||||
asn_lookup asn.routeviews.org _ASN_ _ASNCIDR_
|
|
||||||
asn_lookup myview.example.com _MYASN_ _MYASNCIDR_
|
|
||||||
|
|
||||||
asn_lookup asn.routeviews.org _COMBINEDASN_ _COMBINEDASNCIDR_
|
|
||||||
asn_lookup myview.example.com _COMBINEDASN_ _COMBINEDASNCIDR_
|
|
||||||
|
|
||||||
asn_lookup in1tag.example.net _ASNDATA_ _ASNDATA_
|
|
||||||
|
|
||||||
=item asn_lookup_ipv6 asn-zone6.example.com [_ASN_ _ASNCIDR_]
|
|
||||||
|
|
||||||
Use specified zone for lookups of IPv6 addresses. If zone supports both
|
|
||||||
IPv4 and IPv6 queries, use both asn_lookup and asn_lookup_ipv6 for the same
|
|
||||||
zone.
|
|
||||||
|
|
||||||
=item clear_asn_lookups
|
|
||||||
|
|
||||||
Removes any previously declared I<asn_lookup> entries from a list of queries.
|
|
||||||
|
|
||||||
=item asn_prefix 'prefix_string' (default: 'AS')
|
|
||||||
|
|
||||||
The string specified in the argument is prepended to each ASN when storing
|
|
||||||
it as a tag. This prefix is rather redundant, but its default value 'AS'
|
|
||||||
is kept for backward compatibility with versions of SpamAssassin earlier
|
|
||||||
than 3.4.0. A sensible setting is an empty string. The argument may be (but
|
|
||||||
need not be) enclosed in single or double quotes for clarity.
|
|
||||||
|
|
||||||
=item asn_use_geodb ( 0 / 1 ) (default: 1)
|
|
||||||
|
|
||||||
Use Mail::SpamAssassin::GeoDB module to lookup ASN numbers. You need
|
|
||||||
suitable supported module like GeoIP2 or GeoIP with ISP or ASN database
|
|
||||||
installed (for example, add EditionIDs GeoLite2-ASN in GeoIP.conf for
|
|
||||||
geoipupdate program).
|
|
||||||
|
|
||||||
GeoDB can only set _ASN_ tag, it has no data for _ASNCIDR_. If you need
|
|
||||||
both, then set asn_prefer_geodb 0 so DNS rules are tried.
|
|
||||||
|
|
||||||
=item asn_prefer_geodb ( 0 / 1 ) (default: 1)
|
|
||||||
|
|
||||||
If set, DNS lookups (asn_lookup rules) will not be run if GeoDB successfully
|
|
||||||
finds ASN. Set this to 0 to get _ASNCIDR_ even if GeoDB finds _ASN_.
|
|
||||||
|
|
||||||
=item asn_use_dns ( 0 / 1 ) (default: 1)
|
|
||||||
|
|
||||||
Set to 0 to never allow DNS queries.
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'asn_lookup',
|
setting => 'asn_lookup',
|
||||||
is_admin => 1,
|
is_priv => 1,
|
||||||
code => sub {
|
code => sub {
|
||||||
my ($conf, $key, $value, $line) = @_;
|
my ($conf, $key, $value, $line) = @_;
|
||||||
unless (defined $value && $value !~ /^$/) {
|
unless (defined $value && $value !~ /^$/) {
|
||||||
@ -254,7 +265,7 @@ Set to 0 to never allow DNS queries.
|
|||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'asn_lookup_ipv6',
|
setting => 'asn_lookup_ipv6',
|
||||||
is_admin => 1,
|
is_priv => 1,
|
||||||
code => sub {
|
code => sub {
|
||||||
my ($conf, $key, $value, $line) = @_;
|
my ($conf, $key, $value, $line) = @_;
|
||||||
unless (defined $value && $value !~ /^$/) {
|
unless (defined $value && $value !~ /^$/) {
|
||||||
@ -274,7 +285,6 @@ Set to 0 to never allow DNS queries.
|
|||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'clear_asn_lookups',
|
setting => 'clear_asn_lookups',
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NOARGS,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NOARGS,
|
||||||
code => sub {
|
code => sub {
|
||||||
my ($conf, $key, $value, $line) = @_;
|
my ($conf, $key, $value, $line) = @_;
|
||||||
|
@ -33,21 +33,7 @@ responses trickle in, filters them according to the requested DNS resource
|
|||||||
record type and optional subrule filtering expression, yielding a rule hit
|
record type and optional subrule filtering expression, yielding a rule hit
|
||||||
if a response meets filtering conditions.
|
if a response meets filtering conditions.
|
||||||
|
|
||||||
=head1 USER SETTINGS
|
=head1 RULE DEFINITIONS AND PRIVILEGED SETTINGS
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=item rbl_timeout t [t_min] [zone] (default: 15 3)
|
|
||||||
|
|
||||||
The rbl_timeout setting is common to all DNS querying rules (as implemented
|
|
||||||
by other plugins). It can specify a DNS query timeout globally, or individually
|
|
||||||
for each zone. When the zone parameter is specified, the settings affects DNS
|
|
||||||
queries when their query domain equals the specified zone, or is its subdomain.
|
|
||||||
See the C<Mail::SpamAssassin::Conf> POD for details on C<rbl_timeout>.
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
=head1 RULE DEFINITIONS
|
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
@ -173,7 +159,7 @@ delimited by a '-' specifies an IPv4 address range, and a pair of values
|
|||||||
delimited by a '/' specifies an IPv4 address followed by a bitmask. Again,
|
delimited by a '/' specifies an IPv4 address followed by a bitmask. Again,
|
||||||
this type of filtering expression is primarily intended with RR type-A
|
this type of filtering expression is primarily intended with RR type-A
|
||||||
DNS queries. The rule hits if the RR type matches, and the returned IP
|
DNS queries. The rule hits if the RR type matches, and the returned IP
|
||||||
address falls within the specified range: (r >= n1 && r <= n2), or
|
address falls within the specified range: (r E<gt>= n1 && r E<lt>= n2), or
|
||||||
masked with a bitmask matches the specified value: (r & m) == (n & m) .
|
masked with a bitmask matches the specified value: (r & m) == (n & m) .
|
||||||
|
|
||||||
As a shorthand notation, a single quad-dotted value is equivalent to
|
As a shorthand notation, a single quad-dotted value is equivalent to
|
||||||
@ -193,6 +179,11 @@ rcode indicates an error. Example: [NXDOMAIN], or [FormErr,ServFail,4,5] .
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
|
=head1 NOTES
|
||||||
|
|
||||||
|
DNS timeout can be set with C<rbl_timeout> option. See the
|
||||||
|
C<Mail::SpamAssassin::Conf> POD for details on C<rbl_timeout>.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
package Mail::SpamAssassin::Plugin::AskDNS;
|
package Mail::SpamAssassin::Plugin::AskDNS;
|
||||||
@ -309,7 +300,7 @@ sub set_config {
|
|||||||
|
|
||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'askdns',
|
setting => 'askdns',
|
||||||
is_admin => 1,
|
is_priv => 1,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
|
||||||
code => sub {
|
code => sub {
|
||||||
my($self, $key, $value, $line) = @_;
|
my($self, $key, $value, $line) = @_;
|
||||||
|
@ -51,12 +51,15 @@ our @ISA = qw(Mail::SpamAssassin::Plugin);
|
|||||||
# https://www.iana.org/assignments/email-auth/email-auth.xhtml
|
# https://www.iana.org/assignments/email-auth/email-auth.xhtml
|
||||||
# some others not in that list:
|
# some others not in that list:
|
||||||
# dkim-atps=neutral
|
# dkim-atps=neutral
|
||||||
|
# dmarc=bestguesspass (some microsoft stuff)
|
||||||
my %method_result = (
|
my %method_result = (
|
||||||
|
'arc' => {'fail'=>1,'none'=>1,'pass'=>1},
|
||||||
'auth' => {'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1},
|
'auth' => {'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1},
|
||||||
'dkim' => {'fail'=>1,'neutral'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'policy'=>1,'temperror'=>1},
|
'dkim' => {'fail'=>1,'neutral'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'policy'=>1,'temperror'=>1},
|
||||||
'dkim-adsp' => {'discard'=>1,'fail'=>1,'none'=>1,'nxdomain'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1,'unknown'=>1},
|
'dkim-adsp' => {'discard'=>1,'fail'=>1,'none'=>1,'nxdomain'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1,'unknown'=>1},
|
||||||
'dkim-atps' => {'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1,'neutral'=>1},
|
'dkim-atps' => {'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1,'neutral'=>1},
|
||||||
'dmarc' => {'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1},
|
'dmarc' => {'bestguesspass'=>1,'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1},
|
||||||
|
'dnswl' => {'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1},
|
||||||
'domainkeys' => {'fail'=>1,'neutral'=>1,'none'=>1,'permerror'=>1,'policy'=>1,'pass'=>1,'temperror'=>1},
|
'domainkeys' => {'fail'=>1,'neutral'=>1,'none'=>1,'permerror'=>1,'policy'=>1,'pass'=>1,'temperror'=>1},
|
||||||
'iprev' => {'fail'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1},
|
'iprev' => {'fail'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1},
|
||||||
'rrvs' => {'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1,'unknown'=>1},
|
'rrvs' => {'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1,'unknown'=>1},
|
||||||
@ -66,17 +69,19 @@ my %method_result = (
|
|||||||
'vbr' => {'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1},
|
'vbr' => {'fail'=>1,'none'=>1,'pass'=>1,'permerror'=>1,'temperror'=>1},
|
||||||
);
|
);
|
||||||
my %method_ptype_prop = (
|
my %method_ptype_prop = (
|
||||||
|
'arc' => {'smtp' => {'remote-ip'=>1}, 'header' => {'oldest-pass'=>1}, 'arc' => {'chain'=>1}},
|
||||||
'auth' => {'smtp' => {'auth'=>1,'mailfrom'=>1}},
|
'auth' => {'smtp' => {'auth'=>1,'mailfrom'=>1}},
|
||||||
'dkim' => {'header' => {'d'=>1,'i'=>1,'b'=>1}},
|
'dkim' => {'header' => {'d'=>1,'i'=>1,'b'=>1,'a'=>1,'s'=>1}},
|
||||||
'dkim-adsp' => {'header' => {'from'=>1}},
|
'dkim-adsp' => {'header' => {'from'=>1}},
|
||||||
'dkim-atps' => {'header' => {'from'=>1}},
|
'dkim-atps' => {'header' => {'from'=>1}},
|
||||||
'dmarc' => {'header' => {'from'=>1}},
|
'dmarc' => {'header' => {'from'=>1}, 'policy' => {'dmarc'=>1}},
|
||||||
|
'dnswl' => {'dns' => {'zone'=>1,'sec'=>1}, 'policy' => {'ip'=>1,'txt'=>1}},
|
||||||
'domainkeys' => {'header' => {'d'=>1,'from'=>1,'sender'=>1}},
|
'domainkeys' => {'header' => {'d'=>1,'from'=>1,'sender'=>1}},
|
||||||
'iprev' => {'policy' => {'iprev'=>1}},
|
'iprev' => {'policy' => {'iprev'=>1}},
|
||||||
'rrvs' => {'smtp' => {'rcptto'=>1}},
|
'rrvs' => {'smtp' => {'rcptto'=>1}},
|
||||||
'sender-id' => {'header' => {'*'=>1}},
|
'sender-id' => {'header' => {'*'=>1}},
|
||||||
'smime' => {'body' => {'smime-part'=>1,'smime-identifer'=>1,'smime-serial'=>1,'smime-issuer'=>1}},
|
'smime' => {'body' => {'smime-part'=>1,'smime-identifer'=>1,'smime-serial'=>1,'smime-issuer'=>1}},
|
||||||
'spf' => {'smtp' => {'mailfrom'=>1,'helo'=>1}},
|
'spf' => {'smtp' => {'mailfrom'=>1,'mfrom'=>1,'helo'=>1,'rcpttodomain'=>1}},
|
||||||
'vbr' => {'header' => {'md'=>1,'mv'=>1}},
|
'vbr' => {'header' => {'md'=>1,'mv'=>1}},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -107,7 +112,7 @@ sub set_config {
|
|||||||
my ($self, $conf) = @_;
|
my ($self, $conf) = @_;
|
||||||
my @cmds;
|
my @cmds;
|
||||||
|
|
||||||
=head1 ADMINISTRATOR OPTIONS
|
=head1 ADMINISTRATOR SETTINGS
|
||||||
|
|
||||||
=over
|
=over
|
||||||
|
|
||||||
@ -272,6 +277,12 @@ For checking result of methods, $pms-E<gt>{authres_result} is available:
|
|||||||
|
|
||||||
Can be used to check results.
|
Can be used to check results.
|
||||||
|
|
||||||
|
Reference of valid result methods and values:
|
||||||
|
C<https://www.iana.org/assignments/email-auth/email-auth.xhtml>
|
||||||
|
|
||||||
|
Additionally the result value of 'missing' can be used to check if there is
|
||||||
|
no result at all.
|
||||||
|
|
||||||
ifplugin Mail::SpamAssassin::Plugin::AuthRes
|
ifplugin Mail::SpamAssassin::Plugin::AuthRes
|
||||||
ifplugin !(Mail::SpamAssassin::Plugin::SPF)
|
ifplugin !(Mail::SpamAssassin::Plugin::SPF)
|
||||||
header SPF_PASS eval:check_authres_result('spf', 'pass')
|
header SPF_PASS eval:check_authres_result('spf', 'pass')
|
||||||
@ -299,7 +310,8 @@ sub check_authres_result {
|
|||||||
return !defined($result) ? 1 : 0;
|
return !defined($result) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($wanted_result eq $result);
|
return 0 unless defined $result;
|
||||||
|
return ($wanted_result eq $result) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub parsed_metadata {
|
sub parsed_metadata {
|
||||||
@ -319,8 +331,8 @@ sub parsed_metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach my $hdr (split(/^/m, $pms->get($nethdr))) {
|
foreach my $hdr (split(/^/m, $pms->get($nethdr))) {
|
||||||
if ($hdr =~ /^(?:Arc\-)?Authentication-Results:\s*(.+)/i) {
|
if ($hdr =~ /^((?:Arc\-)?Authentication-Results):\s*(.+)/i) {
|
||||||
push @authres, $1;
|
push @authres, [$1,$2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +344,7 @@ sub parsed_metadata {
|
|||||||
|
|
||||||
foreach (@authres) {
|
foreach (@authres) {
|
||||||
eval {
|
eval {
|
||||||
$self->parse_authres($pms, $_);
|
$self->parse_authres($pms, $_->[0], $_->[1]);
|
||||||
} or do {
|
} or do {
|
||||||
dbg("authres: skipping header, $@");
|
dbg("authres: skipping header, $@");
|
||||||
}
|
}
|
||||||
@ -363,22 +375,51 @@ sub parsed_metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub parse_authres {
|
sub parse_authres {
|
||||||
my ($self, $pms, $hdr) = @_;
|
my ($self, $pms, $hdrname, $hdr) = @_;
|
||||||
|
|
||||||
dbg("authres: parsing Authentication-Results: $hdr");
|
dbg("authres: parsing $hdrname: $hdr");
|
||||||
|
|
||||||
my $authserv;
|
my $authserv;
|
||||||
my $version = 1;
|
my $version = 1;
|
||||||
my @methods;
|
my @methods;
|
||||||
|
my $arc_index;
|
||||||
|
|
||||||
local $_ = $hdr;
|
local $_ = $hdr;
|
||||||
|
|
||||||
|
if ($hdrname =~ /^ARC-/i) {
|
||||||
|
if (!/\Gi\b/gcs) {
|
||||||
|
die("missing arc index: $hdr");
|
||||||
|
}
|
||||||
|
skip_cfws();
|
||||||
|
if (!/\G=/gcs) {
|
||||||
|
die("invalid arc index: ".substr($_, pos())."\n");
|
||||||
|
}
|
||||||
|
skip_cfws();
|
||||||
|
if (!/\G(\d+)/gcs) {
|
||||||
|
die("invalid arc index: ".substr($_, pos())."\n");
|
||||||
|
}
|
||||||
|
$arc_index = $1;
|
||||||
|
if ($arc_index < 1 || $arc_index > 50) {
|
||||||
|
die("invalid arc index: $arc_index\n");
|
||||||
|
}
|
||||||
|
skip_cfws();
|
||||||
|
if (!/\G;/gcs) {
|
||||||
|
die("missing delimiter: ".substr($_, pos())."\n");
|
||||||
|
}
|
||||||
|
skip_cfws();
|
||||||
|
}
|
||||||
|
|
||||||
# authserv-id
|
# authserv-id
|
||||||
if (!/\G($TOKEN)/gcs) {
|
if (!/\G($TOKEN)/gcs) {
|
||||||
die("invalid authserv\n");
|
die("invalid authserv: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
$authserv = lc($1);
|
$authserv = lc($1);
|
||||||
|
|
||||||
|
# some invalid headers start with spf=foo etc, missing authserv-id
|
||||||
|
if (/\G=/gcs) {
|
||||||
|
die("missing authserv: $hdr\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (%{$pms->{conf}->{authres_trusted_authserv}}) {
|
if (%{$pms->{conf}->{authres_trusted_authserv}}) {
|
||||||
if (!$pms->{conf}->{authres_trusted_authserv}->{$authserv}) {
|
if (!$pms->{conf}->{authres_trusted_authserv}->{$authserv}) {
|
||||||
die("authserv not trusted: $authserv\n");
|
die("authserv not trusted: $authserv\n");
|
||||||
@ -388,12 +429,14 @@ sub parse_authres {
|
|||||||
die("ignored authserv: $authserv\n");
|
die("ignored authserv: $authserv\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# skip authserv version
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
if (/\G\d+/gcs) { # skip authserv version
|
if (/\G\d+/gcs) {
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!/\G;/gcs) {
|
if (!/\G;/gcs) {
|
||||||
die("missing delimiter\n");
|
die("missing delimiter: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
|
|
||||||
@ -402,6 +445,11 @@ sub parse_authres {
|
|||||||
my $reason = '';
|
my $reason = '';
|
||||||
my $props = {};
|
my $props = {};
|
||||||
|
|
||||||
|
# some silly generators add duplicate authserv-id; here
|
||||||
|
if (/\G\Q${authserv}\E\s*;/gcs) {
|
||||||
|
skip_cfws();
|
||||||
|
}
|
||||||
|
|
||||||
# skip none method
|
# skip none method
|
||||||
if (/\Gnone\b/igcs) {
|
if (/\Gnone\b/igcs) {
|
||||||
die("method none\n");
|
die("method none\n");
|
||||||
@ -409,17 +457,17 @@ sub parse_authres {
|
|||||||
|
|
||||||
# method / version = result
|
# method / version = result
|
||||||
if (!/\G([\w-]+)/gcs) {
|
if (!/\G([\w-]+)/gcs) {
|
||||||
die("invalid method\n");
|
die("invalid method: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
$method = lc($1);
|
$method = lc($1);
|
||||||
if (!exists $method_result{$method}) {
|
if (!exists $method_result{$method}) {
|
||||||
die("unknown method: $method\n");
|
die("unknown method: $method: $hdr\n");
|
||||||
}
|
}
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
if (/\G\//gcs) {
|
if (/\G\//gcs) {
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
if (!/\G\d+/gcs) {
|
if (!/\G\d+/gcs) {
|
||||||
die("invalid $method version\n");
|
die("invalid $method version: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
$version = $1;
|
$version = $1;
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
@ -429,7 +477,7 @@ sub parse_authres {
|
|||||||
}
|
}
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
if (!/\G(\w+)/gcs) {
|
if (!/\G(\w+)/gcs) {
|
||||||
die("invalid result for $method\n");
|
die("invalid result for $method: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
$result = $1;
|
$result = $1;
|
||||||
if (!exists $method_result{$method}{$result}) {
|
if (!exists $method_result{$method}{$result}) {
|
||||||
@ -441,50 +489,69 @@ sub parse_authres {
|
|||||||
if (/\Greason\b/igcs) {
|
if (/\Greason\b/igcs) {
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
if (!/\G=/gcs) {
|
if (!/\G=/gcs) {
|
||||||
die("invalid reason\n");
|
die("invalid reason: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
if (!/\G$QUOTED_STRING|($TOKEN)/gcs) {
|
if (!/\G$QUOTED_STRING|($TOKEN)/gcs) {
|
||||||
die("invalid reason\n");
|
die("invalid reason: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
$reason = defined $1 ? $1 : $2;
|
$reason = defined $1 ? $1 : $2;
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# action = value (some microsoft ARC stuff?)
|
||||||
|
if (/\Gaction\b/igcs) {
|
||||||
|
skip_cfws();
|
||||||
|
if (!/\G=/gcs) {
|
||||||
|
die("invalid action: ".substr($_, pos())."\n");
|
||||||
|
}
|
||||||
|
skip_cfws();
|
||||||
|
if (!/\G$QUOTED_STRING|$TOKEN/gcs) {
|
||||||
|
die("invalid action: ".substr($_, pos())."\n");
|
||||||
|
}
|
||||||
|
skip_cfws();
|
||||||
|
}
|
||||||
|
|
||||||
# ptype.property = value
|
# ptype.property = value
|
||||||
while (pos() < length()) {
|
while (pos() < length()) {
|
||||||
my ($ptype, $property, $value);
|
my ($ptype, $property, $value);
|
||||||
|
|
||||||
|
# no props?
|
||||||
|
if (/\G(?:;|$)/gcs) {
|
||||||
|
skip_cfws();
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
|
||||||
# ptype
|
# ptype
|
||||||
if (!/\G(\w+)/gcs) {
|
if (!/\G([\w-]+)/gcs) {
|
||||||
die("invalid ptype: ".substr($_,pos())."\n");
|
die("invalid ptype: ".substr($_,pos())."\n");
|
||||||
}
|
}
|
||||||
$ptype = lc($1);
|
$ptype = lc($1);
|
||||||
if (!exists $method_ptype_prop{$method}{$ptype}) {
|
if (!exists $method_ptype_prop{$method}{$ptype}) {
|
||||||
die("unknown ptype: $ptype\n");
|
die("unknown ptype: $method/$ptype\n");
|
||||||
}
|
}
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
|
|
||||||
# dot
|
# dot
|
||||||
if (!/\G\./gcs) {
|
if (!/\G\./gcs) {
|
||||||
die("missing property\n");
|
die("missing property: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
|
|
||||||
# property
|
# property
|
||||||
if (!/\G(\w+)/gcs) {
|
if (!/\G([\w-]+)/gcs) {
|
||||||
die("invalid property\n");
|
die("invalid property: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
$property = lc($1);
|
$property = lc($1);
|
||||||
if (!exists $method_ptype_prop{$method}{$ptype}{$property} &&
|
if (!exists $method_ptype_prop{$method}{$ptype}{$property} &&
|
||||||
!exists $method_ptype_prop{$method}{$ptype}{'*'}) {
|
!exists $method_ptype_prop{$method}{$ptype}{'*'}) {
|
||||||
die("unknown property for $ptype: $property\n");
|
die("unknown property for $method/$ptype: $property\n");
|
||||||
}
|
}
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
|
|
||||||
# =
|
# =
|
||||||
if (!/\G=/gcs) {
|
if (!/\G=/gcs) {
|
||||||
die("missing property value\n");
|
die("missing property value: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
|
|
||||||
@ -493,7 +560,7 @@ sub parse_authres {
|
|||||||
# where value := token / quoted-string
|
# where value := token / quoted-string
|
||||||
# and local-part := dot-atom / quoted-string / obs-local-part
|
# and local-part := dot-atom / quoted-string / obs-local-part
|
||||||
if (!/\G$QUOTED_STRING|($ATOM(?:\.$ATOM)*|$TOKEN)(?=(?:[\s;]|$))/gcs) {
|
if (!/\G$QUOTED_STRING|($ATOM(?:\.$ATOM)*|$TOKEN)(?=(?:[\s;]|$))/gcs) {
|
||||||
die("invalid $ptype.$property value\n");
|
die("invalid $method/$ptype.$property value: ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
$value = defined $1 ? $1 : $2;
|
$value = defined $1 ? $1 : $2;
|
||||||
skip_cfws();
|
skip_cfws();
|
||||||
@ -512,12 +579,13 @@ sub parse_authres {
|
|||||||
'result' => $result,
|
'result' => $result,
|
||||||
'reason' => $reason,
|
'reason' => $reason,
|
||||||
'properties' => $props,
|
'properties' => $props,
|
||||||
|
'arc_index' => $arc_index,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
# paranoid check..
|
# paranoid check..
|
||||||
if (pos() < length()) {
|
if (pos() < length()) {
|
||||||
die("parse ended prematurely?\n");
|
die("parse ended prematurely? ".substr($_, pos())."\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
# Pushed to pms only if header parsed completely
|
# Pushed to pms only if header parsed completely
|
||||||
|
@ -77,7 +77,7 @@ sub set_config {
|
|||||||
my($self, $conf) = @_;
|
my($self, $conf) = @_;
|
||||||
my @cmds;
|
my @cmds;
|
||||||
|
|
||||||
=head1 USER OPTIONS
|
=head1 USER SETTINGS
|
||||||
|
|
||||||
The following configuration settings are used to control auto-learning:
|
The following configuration settings are used to control auto-learning:
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ And the chi-square probability combiner as described here:
|
|||||||
|
|
||||||
The results are incorporated into SpamAssassin as the BAYES_* rules.
|
The results are incorporated into SpamAssassin as the BAYES_* rules.
|
||||||
|
|
||||||
=head1 ADMINISTRATOR SETTINGS
|
=head1 USER SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
@ -288,14 +288,12 @@ sub set_config {
|
|||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'bayes_max_token_length',
|
setting => 'bayes_max_token_length',
|
||||||
default => MAX_TOKEN_LENGTH,
|
default => MAX_TOKEN_LENGTH,
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
||||||
});
|
});
|
||||||
|
|
||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'bayes_stopword_languages',
|
setting => 'bayes_stopword_languages',
|
||||||
default => ['en'],
|
default => ['en'],
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRINGLIST,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRINGLIST,
|
||||||
code => sub {
|
code => sub {
|
||||||
my ($self, $key, $value, $line) = @_;
|
my ($self, $key, $value, $line) = @_;
|
||||||
@ -324,9 +322,6 @@ sub set_config {
|
|||||||
sub parse_config {
|
sub parse_config {
|
||||||
my ($self, $opts) = @_;
|
my ($self, $opts) = @_;
|
||||||
|
|
||||||
# Ignore users's configuration lines
|
|
||||||
return 0 if $opts->{user_config};
|
|
||||||
|
|
||||||
if ($opts->{key} =~ /^bayes_stopword_([a-z]{2})$/i) {
|
if ($opts->{key} =~ /^bayes_stopword_([a-z]{2})$/i) {
|
||||||
$self->inhibit_further_callbacks();
|
$self->inhibit_further_callbacks();
|
||||||
my $lang = lc($1);
|
my $lang = lc($1);
|
||||||
@ -1790,7 +1785,10 @@ sub learner_new {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbg("bayes: learner_new self=%s, bayes_store_module=%s", $self,$module);
|
dbg("bayes: learner_new self=%s, bayes_store_module=%s", $self,$module);
|
||||||
undef $self->{store}; # DESTROYs previous object, if any
|
if ($self->{store}) {
|
||||||
|
$self->{store}->untie_db();
|
||||||
|
undef $self->{store}; # DESTROYs previous object, if any
|
||||||
|
}
|
||||||
eval '
|
eval '
|
||||||
require '.$module.';
|
require '.$module.';
|
||||||
$store = '.$module.'->new($self);
|
$store = '.$module.'->new($self);
|
||||||
|
@ -666,11 +666,13 @@ sub do_head_tests {
|
|||||||
# setup the function to run the rules
|
# setup the function to run the rules
|
||||||
while(my($k,$v) = each %ordered) {
|
while(my($k,$v) = each %ordered) {
|
||||||
my($hdrname, $def) = split(/\t/, $k, 2);
|
my($hdrname, $def) = split(/\t/, $k, 2);
|
||||||
|
# get() might already include newlines, join accordingly (Bug 8121)
|
||||||
$self->push_evalstr_prefix($pms, '
|
$self->push_evalstr_prefix($pms, '
|
||||||
@harr = $self->get(q{'.$hdrname.'});
|
if (scalar(@harr = $self->get(q{'.$hdrname.'}))) {
|
||||||
$hval = scalar(@harr) ? join("\n", @harr) : ' .
|
$hval = join($harr[0] =~ /\n\z/ ? "" : "\n", @harr);
|
||||||
(!defined($def) ? 'undef' :
|
} else {
|
||||||
'$self->{conf}->{test_opt_unset}->{q{'.$def.'}}') . ';
|
$hval = '.(!defined($def) ? 'undef' :'$self->{conf}->{test_opt_unset}->{q{'.$def.'}}').'
|
||||||
|
}
|
||||||
');
|
');
|
||||||
foreach my $rulename (@{$v}) {
|
foreach my $rulename (@{$v}) {
|
||||||
my $tc_ref = $testcode{$rulename};
|
my $tc_ref = $testcode{$rulename};
|
||||||
|
@ -128,7 +128,7 @@ sub set_config {
|
|||||||
my($self, $conf) = @_;
|
my($self, $conf) = @_;
|
||||||
my @cmds;
|
my @cmds;
|
||||||
|
|
||||||
=head1 USER OPTIONS
|
=head1 USER SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
@ -141,7 +141,6 @@ Whether to use DCC, if it is available.
|
|||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'use_dcc',
|
setting => 'use_dcc',
|
||||||
default => 1,
|
default => 1,
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -156,7 +155,6 @@ where it's automatically used.
|
|||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'use_dcc_rep',
|
setting => 'use_dcc_rep',
|
||||||
default => 1,
|
default => 1,
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -189,32 +187,28 @@ default is C<90>.
|
|||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'dcc_body_max',
|
setting => 'dcc_body_max',
|
||||||
is_admin => 1,
|
|
||||||
default => 999999,
|
default => 999999,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
setting => 'dcc_fuz1_max',
|
setting => 'dcc_fuz1_max',
|
||||||
is_admin => 1,
|
|
||||||
default => 999999,
|
default => 999999,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
setting => 'dcc_fuz2_max',
|
setting => 'dcc_fuz2_max',
|
||||||
is_admin => 1,
|
|
||||||
default => 999999,
|
default => 999999,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
setting => 'dcc_rep_percent',
|
setting => 'dcc_rep_percent',
|
||||||
is_admin => 1,
|
|
||||||
default => 90,
|
default => 90,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
});
|
});
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 ADMINISTRATOR OPTIONS
|
=head1 ADMINISTRATOR SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
@ -799,7 +793,7 @@ sub _launch_dcc {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($pms->get('ALL-TRUSTED') =~ /^(X-DCC-[^:]*?-Metrics: .*)$/m) {
|
#if ($pms->get('ALL-TRUSTED') =~ /^(X-DCC-[^:]*?-Metrics: .*)$/m) {
|
||||||
# short-circuit if there is already a X-DCC header with value of
|
# short-circuit if there is already a X-DCC header with value of
|
||||||
# "bulk" from an upstream DCC check
|
# "bulk" from an upstream DCC check
|
||||||
# require "bulk" because then at least one body checksum will be "many"
|
# require "bulk" because then at least one body checksum will be "many"
|
||||||
@ -807,7 +801,7 @@ sub _launch_dcc {
|
|||||||
#if ($1 =~ / bulk /) {
|
#if ($1 =~ / bulk /) {
|
||||||
# return $self->check_dcc_result($pms, $1);
|
# return $self->check_dcc_result($pms, $1);
|
||||||
#}
|
#}
|
||||||
}
|
#}
|
||||||
|
|
||||||
my $envelope = $pms->{relays_external}->[0];
|
my $envelope = $pms->{relays_external}->[0];
|
||||||
|
|
||||||
|
@ -134,6 +134,7 @@ use Mail::SpamAssassin::Plugin;
|
|||||||
use Mail::SpamAssassin::Logger;
|
use Mail::SpamAssassin::Logger;
|
||||||
use Mail::SpamAssassin::Timeout;
|
use Mail::SpamAssassin::Timeout;
|
||||||
use Mail::SpamAssassin::Util qw(idn_to_ascii);
|
use Mail::SpamAssassin::Util qw(idn_to_ascii);
|
||||||
|
use version;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
@ -730,7 +731,6 @@ sub _dkim_load_modules {
|
|||||||
dbg("dkim: cannot load Mail::DKIM module, DKIM checks disabled: %s",
|
dbg("dkim: cannot load Mail::DKIM module, DKIM checks disabled: %s",
|
||||||
$eval_stat);
|
$eval_stat);
|
||||||
} else {
|
} else {
|
||||||
use version 0.77;
|
|
||||||
my $version = Mail::DKIM::Verifier->VERSION;
|
my $version = Mail::DKIM::Verifier->VERSION;
|
||||||
if (version->parse($version) >= version->parse(0.31)) {
|
if (version->parse($version) >= version->parse(0.31)) {
|
||||||
dbg("dkim: using Mail::DKIM version $version");
|
dbg("dkim: using Mail::DKIM version $version");
|
||||||
@ -807,7 +807,7 @@ sub _check_dkim_signed_by {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub _get_authors {
|
sub _get_authors {
|
||||||
my ($self, $pms) = @_;
|
my ($self, $pms, $sig_type) = @_;
|
||||||
|
|
||||||
# Note that RFC 5322 permits multiple addresses in the From header field,
|
# Note that RFC 5322 permits multiple addresses in the From header field,
|
||||||
# and according to RFC 5617 such message has multiple authors and hence
|
# and according to RFC 5617 such message has multiple authors and hence
|
||||||
@ -820,8 +820,8 @@ sub _get_authors {
|
|||||||
# be tolerant, ignore trailing WSP after a domain name
|
# be tolerant, ignore trailing WSP after a domain name
|
||||||
$author_domains{lc $1} = 1 if /\@([^\@]+?)[ \t]*\z/s;
|
$author_domains{lc $1} = 1 if /\@([^\@]+?)[ \t]*\z/s;
|
||||||
}
|
}
|
||||||
$pms->{dkim_author_addresses} = \@authors; # list of full addresses
|
$pms->{"${sig_type}_author_addresses"} = \@authors; # list of full addresses
|
||||||
$pms->{dkim_author_domains} = \%author_domains; # hash of their domains
|
$pms->{"${sig_type}_author_domains"} = \%author_domains; # hash of their domains
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _check_dkim_signature {
|
sub _check_dkim_signature {
|
||||||
@ -883,7 +883,7 @@ sub _check_dkim_signature {
|
|||||||
} else {
|
} else {
|
||||||
# signature objects not provided by the caller, must verify for ourselves
|
# signature objects not provided by the caller, must verify for ourselves
|
||||||
my $timemethod = $self->{main}->time_method("check_dkim_signature");
|
my $timemethod = $self->{main}->time_method("check_dkim_signature");
|
||||||
if (Mail::DKIM::Verifier->VERSION >= 0.40) {
|
if (version->parse(Mail::DKIM::Verifier->VERSION) >= version->parse(0.40)) {
|
||||||
my $edns = $conf->{dns_options}->{edns};
|
my $edns = $conf->{dns_options}->{edns};
|
||||||
if ($edns && $edns >= 1024) {
|
if ($edns && $edns >= 1024) {
|
||||||
# Let Mail::DKIM use our interface to Net::DNS::Resolver.
|
# Let Mail::DKIM use our interface to Net::DNS::Resolver.
|
||||||
@ -908,7 +908,7 @@ sub _check_signature {
|
|||||||
my($self, $pms, $verifier, $type, $signatures) = @_;
|
my($self, $pms, $verifier, $type, $signatures) = @_;
|
||||||
|
|
||||||
my $sig_type = lc $type;
|
my $sig_type = lc $type;
|
||||||
$self->_get_authors($pms) if !$pms->{"${sig_type}_author_addresses"};
|
$self->_get_authors($pms, $sig_type) if !$pms->{"${sig_type}_author_addresses"};
|
||||||
|
|
||||||
my(@valid_signatures);
|
my(@valid_signatures);
|
||||||
my $conf = $pms->{conf};
|
my $conf = $pms->{conf};
|
||||||
@ -1003,7 +1003,7 @@ sub _check_valid_signature {
|
|||||||
my($self, $pms, $verifier, $type, $signatures) = @_;
|
my($self, $pms, $verifier, $type, $signatures) = @_;
|
||||||
|
|
||||||
my $sig_type = lc $type;
|
my $sig_type = lc $type;
|
||||||
$self->_get_authors($pms) if !$pms->{"${sig_type}_author_addresses"};
|
$self->_get_authors($pms, $sig_type) if !$pms->{"${sig_type}_author_addresses"};
|
||||||
|
|
||||||
my(@valid_signatures);
|
my(@valid_signatures);
|
||||||
my $conf = $pms->{conf};
|
my $conf = $pms->{conf};
|
||||||
@ -1143,7 +1143,7 @@ sub _check_dkim_adsp {
|
|||||||
$pms->{dkim_adsp} = {}; # a hash: author_domain => adsp
|
$pms->{dkim_adsp} = {}; # a hash: author_domain => adsp
|
||||||
my $practices_as_string = '';
|
my $practices_as_string = '';
|
||||||
|
|
||||||
$self->_get_authors($pms) if !$pms->{dkim_author_addresses};
|
$self->_get_authors($pms, 'dkim') if !$pms->{dkim_author_addresses};
|
||||||
|
|
||||||
# collect only fully qualified domain names, allow '-', think of IDN
|
# collect only fully qualified domain names, allow '-', think of IDN
|
||||||
my @author_domains = grep { /.\.[a-z-]{2,}\z/si }
|
my @author_domains = grep { /.\.[a-z-]{2,}\z/si }
|
||||||
@ -1296,7 +1296,7 @@ sub _check_dkim_welcomelist {
|
|||||||
|
|
||||||
$pms->{welcomelist_checked} = 1;
|
$pms->{welcomelist_checked} = 1;
|
||||||
|
|
||||||
$self->_get_authors($pms) if !$pms->{dkim_author_addresses};
|
$self->_get_authors($pms, 'dkim') if !$pms->{dkim_author_addresses};
|
||||||
|
|
||||||
my $authors_str = join(", ", @{$pms->{dkim_author_addresses}});
|
my $authors_str = join(", ", @{$pms->{dkim_author_addresses}});
|
||||||
if ($authors_str eq '') {
|
if ($authors_str eq '') {
|
||||||
|
@ -108,6 +108,7 @@ Store DMARC reports using Mail::DMARC::Store, mail-dmarc.ini must be configured
|
|||||||
|
|
||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'dmarc_save_reports',
|
setting => 'dmarc_save_reports',
|
||||||
|
is_admin => 1,
|
||||||
default => 0,
|
default => 0,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
|
||||||
});
|
});
|
||||||
@ -326,6 +327,80 @@ sub _check_dmarc {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $dmarc_arc_verified = 0;
|
||||||
|
if (($result->result ne 'pass') and (ref($pms->{arc_verifier}) and ($pms->{arc_verifier}->result))) {
|
||||||
|
undef $result;
|
||||||
|
$dmarc_arc_verified = 1;
|
||||||
|
# if DMARC fails retry by reading data from AAR headers
|
||||||
|
# use Mail::SpamAssassin::Plugin::AuthRes if available to read ARC signature details
|
||||||
|
my @spf_parsed = sort { ( $a->{authres_parsed}{spf}{arc_index} // 0 ) <=> ( $b->{authres_parsed}{spf}{arc_index} // 0 ) } @{$pms->{authres_parsed}{spf}};
|
||||||
|
my $old_arc_index = 0;
|
||||||
|
foreach my $spf_parse ( @spf_parsed ) {
|
||||||
|
last if not defined $spf_parse->{arc_index};
|
||||||
|
last if $old_arc_index > $spf_parse->{arc_index};
|
||||||
|
dbg("Evaluate DMARC using AAR spf information for index $spf_parse->{arc_index}");
|
||||||
|
if(exists $spf_parse->{properties}{smtp}{mailfrom}) {
|
||||||
|
my $mfrom_dom = $spf_parse->{properties}{smtp}{mailfrom};
|
||||||
|
if($mfrom_dom =~ /\@(.*)/) {
|
||||||
|
$mfrom_dom = $1;
|
||||||
|
}
|
||||||
|
$dmarc->spf([
|
||||||
|
{
|
||||||
|
scope => 'mfrom',
|
||||||
|
domain => $mfrom_dom,
|
||||||
|
result => $spf_parse->{result},
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if(exists $spf_parse->{properties}{smtp}{helo}) {
|
||||||
|
$dmarc->spf([
|
||||||
|
{
|
||||||
|
scope => 'helo',
|
||||||
|
domain => $spf_parse->{properties}{smtp}{helo},
|
||||||
|
result => $spf_parse->{result},
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$old_arc_index = $spf_parse->{arc_index};
|
||||||
|
}
|
||||||
|
|
||||||
|
my @tmp_arc_seals;
|
||||||
|
my @arc_seals;
|
||||||
|
if(defined $pms->{arc_verifier}{seals}) {
|
||||||
|
@tmp_arc_seals = @{$pms->{arc_verifier}{seals}};
|
||||||
|
@arc_seals = sort { ( $a->{arc_verifier}{seals}{tags_by_name}{i}{value} // 0 ) <=> ( $b->{arc_verifier}{seals}{tags_by_name}{i}{value} // 0 ) } @tmp_arc_seals;
|
||||||
|
foreach my $seals ( @arc_seals ) {
|
||||||
|
if(exists($seals->{tags_by_name}{d}) and exists($pms->{arc_author_domains}->{$mfrom_domain})) {
|
||||||
|
dbg("Evaluate DMARC using AAR dkim information for index $seals->{tags_by_name}{i}{value} on domain $mfrom_domain and selector $seals->{tags_by_name}{s}{value}. Result is $seals->{verify_result}");
|
||||||
|
my $arc_result = $seals->{verify_result};
|
||||||
|
if($seals->{verify_result} eq 'invalid') {
|
||||||
|
$arc_result = 'permerror';
|
||||||
|
}
|
||||||
|
$dmarc->dkim(domain => $mfrom_domain, selector => $seals->{tags_by_name}{s}{value}, result => $arc_result);
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eval { $result = $dmarc->validate(); };
|
||||||
|
}
|
||||||
|
|
||||||
|
# Report that DMARC failed but it has been overridden because of AAR headers
|
||||||
|
if(ref($pms->{arc_verifier}) and ($pms->{arc_verifier}->result) and ($dmarc_arc_verified)) {
|
||||||
|
$result->reason->[0]{type} = 'local_policy';
|
||||||
|
$result->reason->[0]{comment} = "arc=" . $pms->{arc_verifier}->result;
|
||||||
|
my $cnt = 1;
|
||||||
|
foreach my $seals ( @{$pms->{arc_verifier}{seals}} ) {
|
||||||
|
if(exists($seals->{tags_by_name}{d}) and exists($seals->{tags_by_name}{s})) {
|
||||||
|
$result->reason->[0]{comment} .= " as[$cnt].d=$seals->{tags_by_name}{d}{value} as[$cnt].s=$seals->{tags_by_name}{s}{value}";
|
||||||
|
$cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($cnt gt 1) {
|
||||||
|
$result->reason->[0]{comment} .= " remote-ip[1]=$lasthop->{ip}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (defined($pms->{dmarc_result} = $result->result)) {
|
if (defined($pms->{dmarc_result} = $result->result)) {
|
||||||
if ($pms->{conf}->{dmarc_save_reports}) {
|
if ($pms->{conf}->{dmarc_save_reports}) {
|
||||||
my $rua = eval { $result->published()->rua(); };
|
my $rua = eval { $result->published()->rua(); };
|
||||||
|
@ -141,6 +141,7 @@ sub set_config {
|
|||||||
my @cmds;
|
my @cmds;
|
||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'rbl_headers',
|
setting => 'rbl_headers',
|
||||||
|
is_priv => 1,
|
||||||
default => 'EnvelopeFrom,Reply-To,Disposition-Notification-To,X-WebmailclientIP,X-Source-IP',
|
default => 'EnvelopeFrom,Reply-To,Disposition-Notification-To,X-WebmailclientIP,X-Source-IP',
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
||||||
}
|
}
|
||||||
@ -516,6 +517,7 @@ sub check_rbl_from_domain {
|
|||||||
return $self->_check_rbl_addresses($pms, $rule, $set, $rbl_server,
|
return $self->_check_rbl_addresses($pms, $rule, $set, $rbl_server,
|
||||||
$subtest, $pms->all_from_addrs_domains());
|
$subtest, $pms->all_from_addrs_domains());
|
||||||
}
|
}
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item check_rbl_ns_from
|
=item check_rbl_ns_from
|
||||||
|
@ -91,7 +91,7 @@ use vars qw(@ISA);
|
|||||||
|
|
||||||
my $VERSION = 4.00;
|
my $VERSION = 4.00;
|
||||||
|
|
||||||
use constant HAS_LWP_USERAGENT => eval { require LWP::UserAgent; };
|
use constant HAS_LWP_USERAGENT => eval { require LWP::UserAgent; require LWP::Protocol::https; };
|
||||||
|
|
||||||
sub dbg { my $msg = shift; return Mail::SpamAssassin::Logger::dbg("DecodeShortURLs: $msg", @_); }
|
sub dbg { my $msg = shift; return Mail::SpamAssassin::Logger::dbg("DecodeShortURLs: $msg", @_); }
|
||||||
sub info { my $msg = shift; return Mail::SpamAssassin::Logger::info("DecodeShortURLs: $msg", @_); }
|
sub info { my $msg = shift; return Mail::SpamAssassin::Logger::info("DecodeShortURLs: $msg", @_); }
|
||||||
@ -128,7 +128,7 @@ sub new {
|
|||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
=head1 PRIVILEGED SETTINGS
|
=head1 USER SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
@ -218,6 +218,8 @@ then only those are removed from list.
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
=head1 PRIVILEGED SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item url_shortener_cache_type (default: none)
|
=item url_shortener_cache_type (default: none)
|
||||||
@ -329,6 +331,8 @@ See C<url_shortener_cache_autoclean> for database cleaning.
|
|||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
});
|
});
|
||||||
|
|
||||||
|
=head1 ADMINISTRATOR SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item url_shortener_cache_autoclean (default: 1000)
|
=item url_shortener_cache_autoclean (default: 1000)
|
||||||
@ -593,7 +597,7 @@ sub initialise_url_shortener_cache {
|
|||||||
");
|
");
|
||||||
$self->{sth_delete} = $self->{dbh}->prepare("
|
$self->{sth_delete} = $self->{dbh}->prepare("
|
||||||
DELETE FROM short_url_cache
|
DELETE FROM short_url_cache
|
||||||
WHERE short_url ? = AND created < CAST(EXTRACT(epoch FROM NOW()) AS INT) - $conf->{url_shortener_cache_ttl}
|
WHERE short_url = ? AND created < CAST(EXTRACT(epoch FROM NOW()) AS INT) - $conf->{url_shortener_cache_ttl}
|
||||||
");
|
");
|
||||||
$self->{sth_clean} = $self->{dbh}->prepare("
|
$self->{sth_clean} = $self->{dbh}->prepare("
|
||||||
DELETE FROM short_url_cache
|
DELETE FROM short_url_cache
|
||||||
@ -707,6 +711,7 @@ sub _check_shortener_uri {
|
|||||||
my $levels = $host =~ tr/.//;
|
my $levels = $host =~ tr/.//;
|
||||||
# No point looking at single level "xxx.yy" without a path
|
# No point looking at single level "xxx.yy" without a path
|
||||||
return if $levels == 1 && !$has_path;
|
return if $levels == 1 && !$has_path;
|
||||||
|
|
||||||
if (exists $conf->{url_shortener}->{$host}) {
|
if (exists $conf->{url_shortener}->{$host}) {
|
||||||
return {
|
return {
|
||||||
'uri' => $uri,
|
'uri' => $uri,
|
||||||
@ -714,6 +719,18 @@ sub _check_shortener_uri {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
# if domain is a 3rd level domain check if there is a url shortener
|
# if domain is a 3rd level domain check if there is a url shortener
|
||||||
|
# on the www domain
|
||||||
|
elsif($levels == 2 && $host =~ /^www\.([^.]+\.[^.]+)$/i) {
|
||||||
|
my $domain = $1;
|
||||||
|
if(($host eq "www.$domain") and exists $conf->{url_shortener}->{$domain}) {
|
||||||
|
dbg("Found internal www redirection for domain $domain");
|
||||||
|
return {
|
||||||
|
'uri' => $uri,
|
||||||
|
'method' => $conf->{url_shortener}->{$domain} == 1 ? 'head' : 'get',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# if domain is a 3rd level domain check if there is a url shortener
|
||||||
# on the 2nd level tld
|
# on the 2nd level tld
|
||||||
elsif ($levels == 2 && $host =~ /^(?!www)[^.]+(\.[^.]+\.[^.]+)$/i &&
|
elsif ($levels == 2 && $host =~ /^(?!www)[^.]+(\.[^.]+\.[^.]+)$/i &&
|
||||||
exists $conf->{url_shortener}->{$1}) {
|
exists $conf->{url_shortener}->{$1}) {
|
||||||
@ -742,6 +759,8 @@ sub _check_short {
|
|||||||
my $uris = $pms->get_uri_detail_list();
|
my $uris = $pms->get_uri_detail_list();
|
||||||
while (my($uri, $info) = each %{$uris}) {
|
while (my($uri, $info) = each %{$uris}) {
|
||||||
next unless $info->{domains} && $info->{cleaned};
|
next unless $info->{domains} && $info->{cleaned};
|
||||||
|
# Remove anchors and parameters from shortened uris
|
||||||
|
$uri =~ s/(?:\#|\?).*//g;
|
||||||
if (my $short_url_info = _check_shortener_uri($uri, $conf)) {
|
if (my $short_url_info = _check_shortener_uri($uri, $conf)) {
|
||||||
$short_urls{$uri} = $short_url_info;
|
$short_urls{$uri} = $short_url_info;
|
||||||
last if scalar keys %short_urls >= $conf->{max_short_urls};
|
last if scalar keys %short_urls >= $conf->{max_short_urls};
|
||||||
@ -827,7 +846,7 @@ sub recursive_lookup {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$location = $response->headers->{location};
|
$location = $response->headers->{location};
|
||||||
if ($self->{url_shortener_loginfo}) {
|
if ($conf->{url_shortener_loginfo}) {
|
||||||
info("found $short_url => $location");
|
info("found $short_url => $location");
|
||||||
} else {
|
} else {
|
||||||
dbg("found $short_url => $location");
|
dbg("found $short_url => $location");
|
||||||
@ -850,21 +869,19 @@ sub recursive_lookup {
|
|||||||
# redirect back to the same host as chaining incorrectly.
|
# redirect back to the same host as chaining incorrectly.
|
||||||
$pms->{short_url_chained} = 1 if $count;
|
$pms->{short_url_chained} = 1 if $count;
|
||||||
|
|
||||||
# Check if we are being redirected to a local page
|
# Check if it is a redirection to a relative URI
|
||||||
# Don't recurse in this case...
|
# Make it an absolute URI and chain to it in that case
|
||||||
if ($location !~ m{^[a-z]+://}i) {
|
if ($location !~ m{^[a-z]+://}i) {
|
||||||
my $orig_location = $location;
|
my $orig_location = $location;
|
||||||
my $orig_short_url = $short_url;
|
my $orig_short_url = $short_url;
|
||||||
# Strip to..
|
# Strip to..
|
||||||
if (index($location, '/') == 0) {
|
if (index($location, '/') == 0) {
|
||||||
$short_url =~ s{^([a-z]+://.*?)[/?#].*}{$1}; # ..absolute path
|
$short_url =~ s{^([a-z]+://.*?)[/?#].*}{$1}; # ..absolute path base is http://example.com
|
||||||
} else {
|
} else {
|
||||||
$short_url =~ s{^([a-z]+://.*)/}{$1}; # ..relative path
|
$short_url =~ s{^([a-z]+://.*/)}{$1}; # ..relative path base is http://example.com/a/b/
|
||||||
}
|
}
|
||||||
$location = "$short_url/$location";
|
$location = "$short_url$location";
|
||||||
dbg("looks like a local redirection: $orig_short_url => $location ($orig_location)");
|
dbg("looks like a redirection to a relative URI: $orig_short_url => $location ($orig_location)");
|
||||||
$pms->add_uri_detail_list($location) if !$pms->{uri_detail_list}->{$location};
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exists $been_here{$location}) {
|
if (exists $been_here{$location}) {
|
||||||
|
@ -47,6 +47,10 @@ ifplugin Mail::SpamAssassin::Plugin::ExtractText
|
|||||||
extracttext_external tesseract {OMP_THREAD_LIMIT=1} /usr/bin/tesseract -c page_separator= {} -
|
extracttext_external tesseract {OMP_THREAD_LIMIT=1} /usr/bin/tesseract -c page_separator= {} -
|
||||||
extracttext_use tesseract .jpg .png .bmp .tif .tiff image/(?:jpeg|png|x-ms-bmp|tiff)
|
extracttext_use tesseract .jpg .png .bmp .tif .tiff image/(?:jpeg|png|x-ms-bmp|tiff)
|
||||||
|
|
||||||
|
# QR-code decoder
|
||||||
|
extracttext_external zbar /usr/bin/zbarimg -q -D {}
|
||||||
|
extracttext_use zbar .jpg .png .pdf image/(?:jpeg|png) application/pdf
|
||||||
|
|
||||||
add_header all ExtractText-Flags _EXTRACTTEXTFLAGS_
|
add_header all ExtractText-Flags _EXTRACTTEXTFLAGS_
|
||||||
header PDF_NO_TEXT X-ExtractText-Flags =~ /\bpdftotext_NoText\b/
|
header PDF_NO_TEXT X-ExtractText-Flags =~ /\bpdftotext_NoText\b/
|
||||||
describe PDF_NO_TEXT PDF without text
|
describe PDF_NO_TEXT PDF without text
|
||||||
@ -243,6 +247,14 @@ Contains notes from the plugin.
|
|||||||
|
|
||||||
X-ExtractText-Flags: openxml_NoText
|
X-ExtractText-Flags: openxml_NoText
|
||||||
|
|
||||||
|
=item X-ExtractText-Uris
|
||||||
|
|
||||||
|
Tag: _EXTRACTTEXTURIS_
|
||||||
|
|
||||||
|
Contains uris extracted from the plugin.
|
||||||
|
|
||||||
|
X-ExtractText-Uris: https://spamassassin.apache.org
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head3 Rules
|
=head3 Rules
|
||||||
@ -292,12 +304,14 @@ sub set_config {
|
|||||||
|
|
||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'extracttext_maxparts',
|
setting => 'extracttext_maxparts',
|
||||||
|
is_admin => 1,
|
||||||
default => 10,
|
default => 10,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
||||||
});
|
});
|
||||||
|
|
||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'extracttext_timeout',
|
setting => 'extracttext_timeout',
|
||||||
|
is_admin => 1,
|
||||||
default => 5,
|
default => 5,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
||||||
code => sub {
|
code => sub {
|
||||||
@ -448,7 +462,7 @@ sub _extract_external {
|
|||||||
|
|
||||||
if (proc_status_ok($?, $errno)) {
|
if (proc_status_ok($?, $errno)) {
|
||||||
dbg("extracttext: [%s] (%s) finished successfully", $pid, $cmd[0]);
|
dbg("extracttext: [%s] (%s) finished successfully", $pid, $cmd[0]);
|
||||||
} elsif (proc_status_ok($?, $errno, 0, 1)) { # sometimes it exits with 1
|
} elsif (proc_status_ok($?, $errno, 0, 1, 4)) { # sometimes it exits with error 1 or 4
|
||||||
dbg("extracttext: [%s] (%s) finished: %s", $pid, $cmd[0], exit_status_str($?, $errno));
|
dbg("extracttext: [%s] (%s) finished: %s", $pid, $cmd[0], exit_status_str($?, $errno));
|
||||||
} else {
|
} else {
|
||||||
info("extracttext: [%s] (%s) error: %s", $pid, $cmd[0], exit_status_str($?, $errno));
|
info("extracttext: [%s] (%s) error: %s", $pid, $cmd[0], exit_status_str($?, $errno));
|
||||||
@ -502,6 +516,8 @@ sub _extract_external {
|
|||||||
}
|
}
|
||||||
elsif ($err_resp =~ /^\S+ is not a Word Document/) {
|
elsif ($err_resp =~ /^\S+ is not a Word Document/) {
|
||||||
# Ignore antiword
|
# Ignore antiword
|
||||||
|
} elsif((($pipe_errno/256) eq 4) and ($cmd[0] =~ /zbarimg/)) {
|
||||||
|
# Ignore zbarimg
|
||||||
}
|
}
|
||||||
elsif (!$resp) {
|
elsif (!$resp) {
|
||||||
warn "extracttext: error (".($pipe_errno/256).") from $cmd[0]: $err_resp\n";
|
warn "extracttext: error (".($pipe_errno/256).") from $cmd[0]: $err_resp\n";
|
||||||
@ -585,6 +601,14 @@ sub _extract {
|
|||||||
push @{$coll->{flags}}, 'ActionURI';
|
push @{$coll->{flags}}, 'ActionURI';
|
||||||
dbg("extracttext: ActionURI: $1");
|
dbg("extracttext: ActionURI: $1");
|
||||||
push @{$coll->{text}}, $text;
|
push @{$coll->{text}}, $text;
|
||||||
|
push @{$coll->{uris}}, $2;
|
||||||
|
} elsif($text =~ /QR-Code\:([^\s]*)/) {
|
||||||
|
# zbarimg(1) prefixes the url with "QR-Code:" string
|
||||||
|
my $qrurl = $1;
|
||||||
|
push @{$coll->{flags}},'QR-Code';
|
||||||
|
dbg("extracttext: QR-Code: $qrurl");
|
||||||
|
push @{$coll->{text}}, $text;
|
||||||
|
push @{$coll->{uris}}, $qrurl;
|
||||||
}
|
}
|
||||||
if ($text =~ /NoText/) {
|
if ($text =~ /NoText/) {
|
||||||
push @{$coll->{flags}},'NoText';
|
push @{$coll->{flags}},'NoText';
|
||||||
@ -616,6 +640,7 @@ sub _extract {
|
|||||||
#
|
#
|
||||||
sub _check_extract {
|
sub _check_extract {
|
||||||
my ($self, $coll, $checked, $part, $decoded, $data, $type, $name) = @_;
|
my ($self, $coll, $checked, $part, $decoded, $data, $type, $name) = @_;
|
||||||
|
my $ret = 0;
|
||||||
return 0 unless (defined $type || defined $name);
|
return 0 unless (defined $type || defined $name);
|
||||||
foreach my $match (@{$self->{match}}) {
|
foreach my $match (@{$self->{match}}) {
|
||||||
next unless $self->{tools}->{$match->{tool}};
|
next unless $self->{tools}->{$match->{tool}};
|
||||||
@ -630,9 +655,11 @@ sub _check_extract {
|
|||||||
}
|
}
|
||||||
$checked->{$match->{tool}} = 1;
|
$checked->{$match->{tool}} = 1;
|
||||||
# dbg("extracttext: coll: $coll, part: $part, type: $type, name: $name, data: $data, tool: $self->{tools}->{$match->{tool}}");
|
# dbg("extracttext: coll: $coll, part: $part, type: $type, name: $name, data: $data, tool: $self->{tools}->{$match->{tool}}");
|
||||||
return 1 if $self->_extract($coll,$part,$type,$name,$data,$self->{tools}->{$match->{tool}});
|
if($self->_extract($coll,$part,$type,$name,$data,$self->{tools}->{$match->{tool}})) {
|
||||||
|
$ret = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub post_message_parse {
|
sub post_message_parse {
|
||||||
@ -652,6 +679,7 @@ sub post_message_parse {
|
|||||||
'chars' => 0,
|
'chars' => 0,
|
||||||
'words' => 0,
|
'words' => 0,
|
||||||
'text' => [],
|
'text' => [],
|
||||||
|
'uris' => [],
|
||||||
);
|
);
|
||||||
|
|
||||||
my $conf = $self->{main}->{conf};
|
my $conf = $self->{main}->{conf};
|
||||||
@ -685,6 +713,7 @@ sub post_message_parse {
|
|||||||
my @uniq_types = do { my %seen; grep { !$seen{$_}++ } @{$collect{types}} };
|
my @uniq_types = do { my %seen; grep { !$seen{$_}++ } @{$collect{types}} };
|
||||||
my @uniq_ext = do { my %seen; grep { !$seen{$_}++ } @{$collect{extensions}} };
|
my @uniq_ext = do { my %seen; grep { !$seen{$_}++ } @{$collect{extensions}} };
|
||||||
my @uniq_flags = do { my %seen; grep { !$seen{$_}++ } @{$collect{flags}} };
|
my @uniq_flags = do { my %seen; grep { !$seen{$_}++ } @{$collect{flags}} };
|
||||||
|
my @uniq_uris = do { my %seen; grep { !$seen{$_}++ } @{$collect{uris}} };
|
||||||
|
|
||||||
$msg->put_metadata('X-ExtractText-Words', $collect{words});
|
$msg->put_metadata('X-ExtractText-Words', $collect{words});
|
||||||
$msg->put_metadata('X-ExtractText-Chars', $collect{chars});
|
$msg->put_metadata('X-ExtractText-Chars', $collect{chars});
|
||||||
@ -692,6 +721,7 @@ sub post_message_parse {
|
|||||||
$msg->put_metadata('X-ExtractText-Types', join(' ', @uniq_types));
|
$msg->put_metadata('X-ExtractText-Types', join(' ', @uniq_types));
|
||||||
$msg->put_metadata('X-ExtractText-Extensions', join(' ', @uniq_ext));
|
$msg->put_metadata('X-ExtractText-Extensions', join(' ', @uniq_ext));
|
||||||
$msg->put_metadata('X-ExtractText-Flags', join(' ', @uniq_flags));
|
$msg->put_metadata('X-ExtractText-Flags', join(' ', @uniq_flags));
|
||||||
|
$msg->put_metadata('X-ExtractText-Uris', join(' ', @uniq_uris));
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -700,7 +730,7 @@ sub parsed_metadata {
|
|||||||
my ($self, $opts) = @_;
|
my ($self, $opts) = @_;
|
||||||
my $pms = $opts->{permsgstatus};
|
my $pms = $opts->{permsgstatus};
|
||||||
my $msg = $pms->get_message();
|
my $msg = $pms->get_message();
|
||||||
foreach my $tag (('Words','Chars','Tools','Types','Extensions','Flags')) {
|
foreach my $tag (('Words','Chars','Tools','Types','Extensions','Flags','Uris')) {
|
||||||
my $v = $msg->get_metadata("X-ExtractText-$tag");
|
my $v = $msg->get_metadata("X-ExtractText-$tag");
|
||||||
if (defined $v) {
|
if (defined $v) {
|
||||||
$pms->set_tag("ExtractText$tag", $v);
|
$pms->set_tag("ExtractText$tag", $v);
|
||||||
|
@ -286,7 +286,6 @@ sub set_config {
|
|||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'hashbl_ignore',
|
setting => 'hashbl_ignore',
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
|
||||||
default => {},
|
default => {},
|
||||||
code => sub {
|
code => sub {
|
||||||
@ -302,7 +301,6 @@ sub set_config {
|
|||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'hashbl_email_domain_alias',
|
setting => 'hashbl_email_domain_alias',
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
|
||||||
default => {},
|
default => {},
|
||||||
code => sub {
|
code => sub {
|
||||||
@ -323,7 +321,6 @@ sub set_config {
|
|||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'hashbl_email_regex',
|
setting => 'hashbl_email_regex',
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
||||||
# Some regexp tips courtesy of http://www.regular-expressions.info/email.html
|
# Some regexp tips courtesy of http://www.regular-expressions.info/email.html
|
||||||
# full email regex v0.02
|
# full email regex v0.02
|
||||||
@ -355,7 +352,6 @@ sub set_config {
|
|||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'hashbl_email_welcomelist',
|
setting => 'hashbl_email_welcomelist',
|
||||||
aliases => ['hashbl_email_whitelist'], # removed in 4.1
|
aliases => ['hashbl_email_whitelist'], # removed in 4.1
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
||||||
default => qr/(?i)
|
default => qr/(?i)
|
||||||
^(?:
|
^(?:
|
||||||
|
@ -241,7 +241,7 @@ sub _check_for_forged_hotmail_received_headers {
|
|||||||
{ return; }
|
{ return; }
|
||||||
if ($rcvd =~ /from \S*\.outbound\.protection\.outlook\.com \(\S+\.outbound\.protection\.outlook\.com[ \)]/ && $orig)
|
if ($rcvd =~ /from \S*\.outbound\.protection\.outlook\.com \(\S+\.outbound\.protection\.outlook\.com[ \)]/ && $orig)
|
||||||
{ return; }
|
{ return; }
|
||||||
if ($rcvd =~ /from \S*\.eurprd\d+\.prod\.outlook\.com \($IP_ADDRESS\)/ && $orig)
|
if ($rcvd =~ /from \S*\.(?:eur|nam)prd\d+\.prod\.outlook\.com \($IP_ADDRESS\)/ && $orig)
|
||||||
{ return; }
|
{ return; }
|
||||||
if ($rcvd =~ /from \S*\.hotmail.com \(\[$IP_ADDRESS\][ \):]/ && $ip)
|
if ($rcvd =~ /from \S*\.hotmail.com \(\[$IP_ADDRESS\][ \):]/ && $ip)
|
||||||
{ return; }
|
{ return; }
|
||||||
|
@ -81,7 +81,7 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Mail::SpamAssassin::Plugin;
|
use Mail::SpamAssassin::Plugin;
|
||||||
use Mail::SpamAssassin::Util qw(compile_regexp);
|
use Mail::SpamAssassin::Util qw(compile_regexp get_part_details);
|
||||||
|
|
||||||
use constant HAS_ARCHIVE_ZIP => eval { require Archive::Zip; };
|
use constant HAS_ARCHIVE_ZIP => eval { require Archive::Zip; };
|
||||||
use constant HAS_IO_STRING => eval { require IO::String; };
|
use constant HAS_IO_STRING => eval { require IO::String; };
|
||||||
@ -550,7 +550,7 @@ sub _check_attachments {
|
|||||||
foreach my $part ($pms->{msg}->find_parts(qr/./, 1)) {
|
foreach my $part ($pms->{msg}->find_parts(qr/./, 1)) {
|
||||||
next if $part->{type} =~ /$conf->{olemacro_skip_ctypes}/i;
|
next if $part->{type} =~ /$conf->{olemacro_skip_ctypes}/i;
|
||||||
|
|
||||||
my ($ctt, $ctd, $cte, $name) = _get_part_details($pms, $part);
|
my ($ctt, $ctd, $cte, $name) = get_part_details($pms, $part, $conf->{olemacro_prefer_contentdisposition});
|
||||||
next unless defined $ctt;
|
next unless defined $ctt;
|
||||||
next if $name eq '';
|
next if $name eq '';
|
||||||
|
|
||||||
@ -771,53 +771,6 @@ sub _check_zip {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _get_part_details {
|
|
||||||
my ($pms, $part) = @_;
|
|
||||||
#https://en.wikipedia.org/wiki/MIME#Content-Disposition
|
|
||||||
#https://github.com/mikel/mail/pull/464
|
|
||||||
|
|
||||||
my $ctt = $part->get_header('content-type');
|
|
||||||
return undef unless defined $ctt; ## no critic (ProhibitExplicitReturnUndef)
|
|
||||||
|
|
||||||
my $cte = lc($part->get_header('content-transfer-encoding') || '');
|
|
||||||
return undef unless ($cte =~ /^(?:base64|quoted\-printable)$/); ## no critic (ProhibitExplicitReturnUndef)
|
|
||||||
|
|
||||||
$ctt = _decode_part_header($part, $ctt || '');
|
|
||||||
|
|
||||||
my $name = '';
|
|
||||||
my $cttname = '';
|
|
||||||
my $ctdname = '';
|
|
||||||
|
|
||||||
if ($ctt =~ m/name\s*=\s*["']?([^"';]*)/is) {
|
|
||||||
$cttname = $1;
|
|
||||||
$cttname =~ s/\s+$//;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $ctd = $part->get_header('content-disposition');
|
|
||||||
$ctd = _decode_part_header($part, $ctd || '');
|
|
||||||
|
|
||||||
if ($ctd =~ m/filename\s*=\s*["']?([^"';]*)/is) {
|
|
||||||
$ctdname = $1;
|
|
||||||
$ctdname =~ s/\s+$//;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lc $ctdname eq lc $cttname) {
|
|
||||||
$name = $ctdname;
|
|
||||||
} elsif ($ctdname eq '') {
|
|
||||||
$name = $cttname;
|
|
||||||
} elsif ($cttname eq '') {
|
|
||||||
$name = $ctdname;
|
|
||||||
} else {
|
|
||||||
if ($pms->{conf}->{olemacro_prefer_contentdisposition}) {
|
|
||||||
$name = $ctdname;
|
|
||||||
} else {
|
|
||||||
$name = $cttname;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ctt, $ctd, $cte, $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _open_zip_handle {
|
sub _open_zip_handle {
|
||||||
my ($data) = @_;
|
my ($data) = @_;
|
||||||
|
|
||||||
@ -1093,35 +1046,6 @@ sub _zip_error_handler {
|
|||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _decode_part_header {
|
|
||||||
my($part, $header_field_body) = @_;
|
|
||||||
|
|
||||||
return '' unless defined $header_field_body && $header_field_body ne '';
|
|
||||||
|
|
||||||
# deal with folding and cream the newlines and such
|
|
||||||
$header_field_body =~ s/\n[ \t]+/\n /g;
|
|
||||||
$header_field_body =~ s/\015?\012//gs;
|
|
||||||
|
|
||||||
local($1,$2,$3);
|
|
||||||
|
|
||||||
# Multiple encoded sections must ignore the interim whitespace.
|
|
||||||
# To avoid possible FPs with (\s+(?==\?))?, look for the whole RE
|
|
||||||
# separated by whitespace.
|
|
||||||
1 while $header_field_body =~
|
|
||||||
s{ ( = \? [A-Za-z0-9_-]+ \? [bqBQ] \? [^?]* \? = ) \s+
|
|
||||||
( = \? [A-Za-z0-9_-]+ \? [bqBQ] \? [^?]* \? = ) }
|
|
||||||
{$1$2}xsg;
|
|
||||||
|
|
||||||
# transcode properly encoded RFC 2047 substrings into UTF-8 octets,
|
|
||||||
# leave everything else unchanged as it is supposed to be UTF-8 (RFC 6532)
|
|
||||||
# or plain US-ASCII
|
|
||||||
$header_field_body =~
|
|
||||||
s{ (?: = \? ([A-Za-z0-9_-]+) \? ([bqBQ]) \? ([^?]*) \? = ) }
|
|
||||||
{ $part->__decode_header($1, uc($2), $3) }xsge;
|
|
||||||
|
|
||||||
return $header_field_body;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Version features
|
# Version features
|
||||||
sub has_olemacro_redirect_uri { 1 }
|
sub has_olemacro_redirect_uri { 1 }
|
||||||
sub has_olemacro_mhtml_uri { 1 }
|
sub has_olemacro_mhtml_uri { 1 }
|
||||||
|
@ -328,9 +328,16 @@ sub _get_pdf_details {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# XXX some pdf have uris but are stored inside binary data
|
# XXX some pdf have uris but are stored inside binary data
|
||||||
if (keys %uris < 20 && $line =~ /(?:\/S\s{0,2}\/URI\s{0,2}|^\s*)\/URI\s{0,2}( \( .*? (?<!\\) \) | < [^>]* > )/x) {
|
if (keys %uris < 20 && $line =~ /(?:\/S\s{0,2}\/URI\s{0,2}|^\s*)\/URI\s{0,2}( \( .*? (?<!\\) \) | < [^>]* > )|\((https?:\/\/.{8,256})\)>>/x) {
|
||||||
my $location = _parse_string($1);
|
my $location;
|
||||||
|
if (defined $1 and (index($1, '.') > 0)) {
|
||||||
|
$location = _parse_string($1);
|
||||||
|
}
|
||||||
|
if (not defined($location) or index($location, '.') <= 0) {
|
||||||
|
$location = _parse_string($2);
|
||||||
|
}
|
||||||
next unless index($location, '.') > 0; # ignore some binary mess
|
next unless index($location, '.') > 0; # ignore some binary mess
|
||||||
|
next if $location =~ /\0/; # ignore urls with NUL characters
|
||||||
if (!exists $uris{$location}) {
|
if (!exists $uris{$location}) {
|
||||||
$uris{$location} = 1;
|
$uris{$location} = 1;
|
||||||
dbg("pdfinfo: found URI: $location");
|
dbg("pdfinfo: found URI: $location");
|
||||||
|
@ -31,7 +31,6 @@ Mail::SpamAssassin::Plugin::Phishing - check uris against phishing feed
|
|||||||
ifplugin Mail::SpamAssassin::Plugin::Phishing
|
ifplugin Mail::SpamAssassin::Plugin::Phishing
|
||||||
phishing_openphish_feed /etc/mail/spamassassin/openphish-feed.txt
|
phishing_openphish_feed /etc/mail/spamassassin/openphish-feed.txt
|
||||||
phishing_phishtank_feed /etc/mail/spamassassin/phishtank-feed.csv
|
phishing_phishtank_feed /etc/mail/spamassassin/phishtank-feed.csv
|
||||||
phishing_phishstats_feed /etc/mail/spamassassin/phishstats-feed.csv
|
|
||||||
body URI_PHISHING eval:check_phishing()
|
body URI_PHISHING eval:check_phishing()
|
||||||
describe URI_PHISHING Url match phishing in feed
|
describe URI_PHISHING Url match phishing in feed
|
||||||
endif
|
endif
|
||||||
@ -48,9 +47,6 @@ The PhishTank free feed is updated every 1 hours and can be downloaded from
|
|||||||
http://data.phishtank.com/data/online-valid.csv.
|
http://data.phishtank.com/data/online-valid.csv.
|
||||||
To avoid download limits a registration is required.
|
To avoid download limits a registration is required.
|
||||||
|
|
||||||
The PhishStats feed is updated every 90 minutes and can be downloaded from
|
|
||||||
https://phishstats.info/phish_score.csv.
|
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
package Mail::SpamAssassin::Plugin::Phishing;
|
package Mail::SpamAssassin::Plugin::Phishing;
|
||||||
@ -143,40 +139,6 @@ skipped.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=cut
|
|
||||||
push(@cmds, {
|
|
||||||
setting => 'phishing_phishstats_feed',
|
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=item phishing_phishstats_feed
|
|
||||||
|
|
||||||
Absolute path of the downloaded PhishStats datafeed.
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
=cut
|
|
||||||
push(@cmds, {
|
|
||||||
setting => 'phishing_phishstats_minscore',
|
|
||||||
is_admin => 1,
|
|
||||||
default => 6,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=item phishing_phishstats_minscore ( 0 - 10 ) (default: 6)
|
|
||||||
|
|
||||||
Minimum score to take into consideration for phishing uris downloaded
|
|
||||||
from PhishStats datafeed.
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
$conf->{parser}->register_commands(\@cmds);
|
$conf->{parser}->register_commands(\@cmds);
|
||||||
}
|
}
|
||||||
@ -189,7 +151,7 @@ sub finish_parsing_end {
|
|||||||
sub _read_configfile {
|
sub _read_configfile {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
my $conf = $self->{main}->{registryboundaries}->{conf};
|
my $conf = $self->{main}->{registryboundaries}->{conf};
|
||||||
my (@phtank_ln, @phstats_ln);
|
my @phtank_ln;
|
||||||
my $stripped_cluri;
|
my $stripped_cluri;
|
||||||
|
|
||||||
local *F;
|
local *F;
|
||||||
@ -244,39 +206,6 @@ sub _read_configfile {
|
|||||||
close(F) or die "error closing config file: $!";
|
close(F) or die "error closing config file: $!";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( defined($conf->{phishing_phishstats_feed}) && (-f $conf->{phishing_phishstats_feed} ) ) {
|
|
||||||
open(F, '<', $conf->{phishing_phishstats_feed});
|
|
||||||
for ($!=0; <F>; $!=0) {
|
|
||||||
#skip first line
|
|
||||||
next if ( $. eq 1);
|
|
||||||
chomp;
|
|
||||||
#lines that start with pound are comments
|
|
||||||
next if(/^\s*\#/);
|
|
||||||
|
|
||||||
# CSV: Date,Score,URL,IP
|
|
||||||
@phstats_ln = split(/,/, $_);
|
|
||||||
$phstats_ln[1] =~ s/\"//g;
|
|
||||||
$phstats_ln[2] =~ s/\"//g;
|
|
||||||
if ( $conf->{phishing_phishstats_minscore} >= $phstats_ln[1] ) {
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
$stripped_cluri = $phstats_ln[2];
|
|
||||||
if ( $conf->{phishing_uri_noparam} eq 1 ) {
|
|
||||||
$stripped_cluri =~ s/\?.*//;
|
|
||||||
}
|
|
||||||
my $phishdomain = $self->{main}->{registryboundaries}->uri_to_domain($phstats_ln[2]);
|
|
||||||
if ( defined $phishdomain ) {
|
|
||||||
push @{$self->{PHISHING}->{$stripped_cluri}->{phishdomain}}, $phishdomain;
|
|
||||||
push @{$self->{PHISHING}->{$stripped_cluri}->{phishinfo}->{$phishdomain}}, "PhishStats";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defined $_ || $!==0 or
|
|
||||||
$!==EBADF ? dbg("PHISHING: error reading config file: $!")
|
|
||||||
: die "error reading config file: $!";
|
|
||||||
close(F) or die "error closing config file: $!";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_phishing {
|
sub check_phishing {
|
||||||
|
@ -79,7 +79,7 @@ sub set_config {
|
|||||||
my ($self, $conf) = @_;
|
my ($self, $conf) = @_;
|
||||||
my @cmds;
|
my @cmds;
|
||||||
|
|
||||||
=head1 USER OPTIONS
|
=head1 USER SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
@ -91,26 +91,10 @@ Whether to use Pyzor, if it is available.
|
|||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'use_pyzor',
|
setting => 'use_pyzor',
|
||||||
is_admin => 1,
|
|
||||||
default => 1,
|
default => 1,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL
|
||||||
});
|
});
|
||||||
|
|
||||||
=item pyzor_fork (0|1) (default: 1)
|
|
||||||
|
|
||||||
Instead of running Pyzor synchronously, fork separate process for it and
|
|
||||||
read the results in later (similar to async DNS lookups). Increases
|
|
||||||
throughput. Considered experimental on Windows, where default is 0.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
push(@cmds, {
|
|
||||||
setting => 'pyzor_fork',
|
|
||||||
is_admin => 1,
|
|
||||||
default => am_running_on_windows()?0:1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
|
||||||
});
|
|
||||||
|
|
||||||
=item pyzor_count_min NUMBER (default: 5)
|
=item pyzor_count_min NUMBER (default: 5)
|
||||||
|
|
||||||
This option sets how often a message's body checksum must have been
|
This option sets how often a message's body checksum must have been
|
||||||
@ -124,7 +108,6 @@ set this to a relatively low value, e.g. C<5>.
|
|||||||
|
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'pyzor_count_min',
|
setting => 'pyzor_count_min',
|
||||||
is_admin => 1,
|
|
||||||
default => 5,
|
default => 5,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
});
|
});
|
||||||
@ -132,7 +115,6 @@ set this to a relatively low value, e.g. C<5>.
|
|||||||
# Deprecated setting, the name makes no sense!
|
# Deprecated setting, the name makes no sense!
|
||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'pyzor_max',
|
setting => 'pyzor_max',
|
||||||
is_admin => 1,
|
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
||||||
code => sub {
|
code => sub {
|
||||||
my ($self, $key, $value, $line) = @_;
|
my ($self, $key, $value, $line) = @_;
|
||||||
@ -157,7 +139,6 @@ result. Final decision is made by pyzor_welcomelist_factor.
|
|||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'pyzor_welcomelist_min',
|
setting => 'pyzor_welcomelist_min',
|
||||||
aliases => ['pyzor_whitelist_min'], # removed in 4.1
|
aliases => ['pyzor_whitelist_min'], # removed in 4.1
|
||||||
is_admin => 1,
|
|
||||||
default => 10,
|
default => 10,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
});
|
});
|
||||||
@ -166,7 +147,7 @@ result. Final decision is made by pyzor_welcomelist_factor.
|
|||||||
|
|
||||||
Previously pyzor_whitelist_factor which will work interchangeably until 4.1.
|
Previously pyzor_whitelist_factor which will work interchangeably until 4.1.
|
||||||
|
|
||||||
Ignore Pyzor result if REPORTCOUNT x NUMBER >= pyzor_welcomelist_min.
|
Ignore Pyzor result if REPORTCOUNT x NUMBER E<gt>= pyzor_welcomelist_min.
|
||||||
For default setting this means: 50 reports requires 10 welcomelistings.
|
For default setting this means: 50 reports requires 10 welcomelistings.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
@ -174,17 +155,44 @@ For default setting this means: 50 reports requires 10 welcomelistings.
|
|||||||
push (@cmds, {
|
push (@cmds, {
|
||||||
setting => 'pyzor_welcomelist_factor',
|
setting => 'pyzor_welcomelist_factor',
|
||||||
aliases => ['pyzor_whitelist_factor'], # removed in 4.1
|
aliases => ['pyzor_whitelist_factor'], # removed in 4.1
|
||||||
is_admin => 1,
|
|
||||||
default => 0.2,
|
default => 0.2,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
});
|
});
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 ADMINISTRATOR OPTIONS
|
=head1 ADMINISTRATOR SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
|
=item pyzor_fork (0|1) (default: 1)
|
||||||
|
|
||||||
|
Instead of running Pyzor synchronously, fork separate process for it and
|
||||||
|
read the results in later (similar to async DNS lookups). Increases
|
||||||
|
throughput. Considered experimental on Windows, where default is 0.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
push(@cmds, {
|
||||||
|
setting => 'pyzor_fork',
|
||||||
|
is_admin => 1,
|
||||||
|
default => am_running_on_windows()?0:1,
|
||||||
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
||||||
|
});
|
||||||
|
|
||||||
|
=item pyzor_perl (0|1) (default: 0)
|
||||||
|
|
||||||
|
Instead of running Pyzor client, use a pure Perl client.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
push(@cmds, {
|
||||||
|
setting => 'pyzor_perl',
|
||||||
|
is_admin => 1,
|
||||||
|
default => 0,
|
||||||
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
||||||
|
});
|
||||||
|
|
||||||
=item pyzor_timeout n (default: 5)
|
=item pyzor_timeout n (default: 5)
|
||||||
|
|
||||||
How many seconds you wait for Pyzor to complete, before scanning continues
|
How many seconds you wait for Pyzor to complete, before scanning continues
|
||||||
@ -232,11 +240,11 @@ characters in the range [0-9A-Za-z =,._/-] are allowed for security reasons.
|
|||||||
code => sub {
|
code => sub {
|
||||||
my ($self, $key, $value, $line) = @_;
|
my ($self, $key, $value, $line) = @_;
|
||||||
if ($value !~ m{^([0-9A-Za-z =,._/-]+)$}) {
|
if ($value !~ m{^([0-9A-Za-z =,._/-]+)$}) {
|
||||||
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
|
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
|
||||||
}
|
}
|
||||||
$self->{pyzor_options} = $1;
|
$self->{pyzor_options} = $1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
=item pyzor_path STRING
|
=item pyzor_path STRING
|
||||||
|
|
||||||
@ -255,38 +263,89 @@ you should use this, as the current PATH will have been cleared.
|
|||||||
code => sub {
|
code => sub {
|
||||||
my ($self, $key, $value, $line) = @_;
|
my ($self, $key, $value, $line) = @_;
|
||||||
if (!defined $value || !length $value) {
|
if (!defined $value || !length $value) {
|
||||||
return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
|
return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
|
||||||
}
|
}
|
||||||
$value = untaint_file_path($value);
|
$value = untaint_file_path($value);
|
||||||
if (!-x $value) {
|
if (!-x $value) {
|
||||||
info("config: pyzor_path \"$value\" isn't an executable");
|
info("config: pyzor_path \"$value\" isn't an executable");
|
||||||
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
|
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->{pyzor_path} = $value;
|
$self->{pyzor_path} = $value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
=item pyzor_server_file FILE
|
||||||
|
|
||||||
|
Pyzor servers configuration file path, used by Pyzor Perl implementation.
|
||||||
|
By default Pyzor will connect to public.pyzor.org on port 24441.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
push (@cmds, {
|
||||||
|
setting => 'pyzor_server_file',
|
||||||
|
is_admin => 1,
|
||||||
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
||||||
|
code => sub {
|
||||||
|
my ($self, $key, $value, $line) = @_;
|
||||||
|
if (! -f $value) {
|
||||||
|
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
|
||||||
|
} else {
|
||||||
|
my $i = 0;
|
||||||
|
open(PSF, '<', untaint_file_path($value));
|
||||||
|
while(<PSF>) {
|
||||||
|
chomp;
|
||||||
|
next if (/^#/);
|
||||||
|
my ($host, $port) = split(/:/, $_);
|
||||||
|
$self->{pyzor_host}[$i] = $host;
|
||||||
|
$self->{pyzor_port}[$i] = $port;
|
||||||
|
dbg("Setting Pyzor host to $host and port to $port");
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
close(PSF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$conf->{parser}->register_commands(\@cmds);
|
$conf->{parser}->register_commands(\@cmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub is_pyzor_available {
|
sub is_pyzor_available {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
my $pyzor = $self->{main}->{conf}->{pyzor_path} ||
|
if($self->{main}->{conf}->{pyzor_perl} and $self->{main}->{conf}->{pyzor_fork}) {
|
||||||
Mail::SpamAssassin::Util::find_executable_in_env_path('pyzor');
|
info("pyzor: pyzor_perl and pyzor_fork cannot be enabled at the same time, Pyzor support will be disabled");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
local $@;
|
||||||
|
if($self->{main}->{conf}->{pyzor_perl}) {
|
||||||
|
eval {
|
||||||
|
require Mail::SpamAssassin::Pyzor::Digest;
|
||||||
|
require Mail::SpamAssassin::Pyzor::Client;
|
||||||
|
};
|
||||||
|
if($@) {
|
||||||
|
$self->{pyzor_available} = 0;
|
||||||
|
dbg("pyzor: pyzor Perl module is not available");
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
$self->{pyzor_available} = 1;
|
||||||
|
dbg("pyzor: pyzor Perl module is available");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
my $pyzor = $self->{main}->{conf}->{pyzor_path} ||
|
||||||
|
Mail::SpamAssassin::Util::find_executable_in_env_path('pyzor');
|
||||||
|
unless ($pyzor && -x $pyzor) {
|
||||||
|
dbg("pyzor: no pyzor executable found");
|
||||||
|
$self->{pyzor_available} = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
unless ($pyzor && -x $pyzor) {
|
# remember any found pyzor
|
||||||
dbg("pyzor: no pyzor executable found");
|
$self->{main}->{conf}->{pyzor_path} = $pyzor;
|
||||||
$self->{pyzor_available} = 0;
|
dbg("pyzor: pyzor is available: $pyzor");
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# remember any found pyzor
|
|
||||||
$self->{main}->{conf}->{pyzor_path} = $pyzor;
|
|
||||||
|
|
||||||
dbg("pyzor: pyzor is available: $pyzor");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub finish_parsing_start {
|
sub finish_parsing_start {
|
||||||
@ -319,17 +378,23 @@ sub check_pyzor {
|
|||||||
$pms->{tag_data}->{PYZOR} = '';
|
$pms->{tag_data}->{PYZOR} = '';
|
||||||
|
|
||||||
# create fulltext tmpfile now (before possible forking)
|
# create fulltext tmpfile now (before possible forking)
|
||||||
$pms->{pyzor_tmpfile} = $pms->create_fulltext_tmpfile();
|
if(!$self->{main}->{conf}->{pyzor_perl}) {
|
||||||
|
$pms->{pyzor_tmpfile} = $pms->create_fulltext_tmpfile();
|
||||||
|
}
|
||||||
|
|
||||||
## non-forking method
|
## non-forking method
|
||||||
|
if ((!$self->{main}->{conf}->{pyzor_fork}) and (!$self->{main}->{conf}->{pyzor_perl})) {
|
||||||
if (!$self->{main}->{conf}->{pyzor_fork}) {
|
|
||||||
my @results = $self->pyzor_lookup($pms);
|
my @results = $self->pyzor_lookup($pms);
|
||||||
return $self->_check_result($pms, \@results);
|
return $self->_check_result($pms, \@results);
|
||||||
}
|
}
|
||||||
|
|
||||||
## forking method
|
## Pure Perl implementation
|
||||||
|
if($self->{main}->{conf}->{pyzor_perl}) {
|
||||||
|
my @results = $self->pyzor_lookup($pms, $full);
|
||||||
|
return $self->_check_result($pms, \@results);
|
||||||
|
}
|
||||||
|
|
||||||
|
## forking method
|
||||||
$pms->{pyzor_rulename} = $pms->get_current_eval_rule_name();
|
$pms->{pyzor_rulename} = $pms->get_current_eval_rule_name();
|
||||||
|
|
||||||
# create socketpair for communication
|
# create socketpair for communication
|
||||||
@ -389,76 +454,121 @@ sub check_pyzor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub pyzor_lookup {
|
sub pyzor_lookup {
|
||||||
my ($self, $pms) = @_;
|
my ( $self, $pms, $fulltext ) = @_;
|
||||||
|
|
||||||
my $conf = $self->{main}->{conf};
|
my $conf = $self->{main}->{conf};
|
||||||
my $timeout = $conf->{pyzor_timeout};
|
my $timeout = $conf->{pyzor_timeout};
|
||||||
|
my $server_port;
|
||||||
|
my @pyzor_hosts;
|
||||||
|
my @pyzor_ports;
|
||||||
|
if(defined $conf->{pyzor_host}) {
|
||||||
|
@pyzor_hosts = @{$conf->{pyzor_host}};
|
||||||
|
}
|
||||||
|
if(defined $conf->{pyzor_port}) {
|
||||||
|
@pyzor_ports = @{$conf->{pyzor_port}};
|
||||||
|
}
|
||||||
|
$pyzor_hosts[0] //= 'public.pyzor.org';
|
||||||
|
$pyzor_ports[0] //= 24441;
|
||||||
|
|
||||||
# note: not really tainted, this came from system configuration file
|
if($self->{main}->{conf}->{pyzor_perl}) {
|
||||||
my $path = untaint_file_path($conf->{pyzor_path});
|
my @resp;
|
||||||
my $opts = untaint_var($conf->{pyzor_options}) || '';
|
my $cnt = 0;
|
||||||
|
my $resp;
|
||||||
|
my $ref;
|
||||||
|
|
||||||
$pms->enter_helper_run_mode();
|
my $digest = Mail::SpamAssassin::Pyzor::Digest::get( $pms, $fulltext );
|
||||||
|
|
||||||
my $pid;
|
foreach my $server_host ( @pyzor_hosts ) {
|
||||||
my @resp;
|
$server_port = $pyzor_ports[$cnt];
|
||||||
my $timer = Mail::SpamAssassin::Timeout->new(
|
$cnt++;
|
||||||
{ secs => $timeout, deadline => $pms->{master_deadline} });
|
my $client = Mail::SpamAssassin::Pyzor::Client->new( 'timeout' => $timeout,
|
||||||
my $err = $timer->run_and_catch(sub {
|
'server_host' => $server_host,
|
||||||
local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
|
'server_port' => $server_port );
|
||||||
|
local $@;
|
||||||
|
$ref = eval { $client->check($digest); };
|
||||||
|
# $client reply must be an hash
|
||||||
|
return 0 if (not (ref $ref eq ref {}));
|
||||||
|
if ($@) {
|
||||||
|
my $err = $@;
|
||||||
|
|
||||||
dbg("pyzor: opening pipe: ".
|
$err = eval { $err->get_message() } || $err;
|
||||||
join(' ', $path, $opts, "check", "<".$pms->{pyzor_tmpfile}));
|
|
||||||
|
|
||||||
$pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*PYZOR,
|
warn("pyzor: check failed: $err\n");
|
||||||
$pms->{pyzor_tmpfile}, 1, $path, split(' ', $opts), "check");
|
return 0;
|
||||||
$pid or die "$!\n";
|
|
||||||
|
|
||||||
# read+split avoids a Perl I/O bug (Bug 5985)
|
|
||||||
my($inbuf, $nread);
|
|
||||||
my $resp = '';
|
|
||||||
while ($nread = read(PYZOR, $inbuf, 8192)) { $resp .= $inbuf }
|
|
||||||
defined $nread or die "error reading from pipe: $!";
|
|
||||||
@resp = split(/^/m, $resp, -1);
|
|
||||||
|
|
||||||
my $errno = 0;
|
|
||||||
close PYZOR or $errno = $!;
|
|
||||||
if (proc_status_ok($?, $errno)) {
|
|
||||||
dbg("pyzor: [%s] finished successfully", $pid);
|
|
||||||
} elsif (proc_status_ok($?, $errno, 0, 1)) { # sometimes it exits with 1
|
|
||||||
dbg("pyzor: [%s] finished: %s", $pid, exit_status_str($?, $errno));
|
|
||||||
} else {
|
|
||||||
info("pyzor: [%s] error: %s", $pid, exit_status_str($?, $errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
if (defined(fileno(*PYZOR))) { # still open
|
|
||||||
if ($pid) {
|
|
||||||
if (kill('TERM', $pid)) {
|
|
||||||
dbg("pyzor: killed stale helper [$pid]");
|
|
||||||
} else {
|
|
||||||
dbg("pyzor: killing helper application [$pid] failed: $!");
|
|
||||||
}
|
}
|
||||||
|
# mimic pyzor client reply
|
||||||
|
# public.pyzor.org:24441\t(200, 'OK')\t1\t0
|
||||||
|
$resp .= "$server_host:$server_port\t($ref->{'Code'}, '" . $ref->{'Diag'} . "')\t$ref->{'Count'}\t$ref->{'WL-Count'}\n";
|
||||||
|
undef $ref;
|
||||||
}
|
}
|
||||||
my $errno = 0;
|
@resp = split(/^/m, $resp, -1);
|
||||||
close PYZOR or $errno = $!;
|
return @resp;
|
||||||
proc_status_ok($?, $errno)
|
} else {
|
||||||
or info("pyzor: [%s] error: %s", $pid, exit_status_str($?, $errno));
|
# note: not really tainted, this came from system configuration file
|
||||||
|
my $path = untaint_file_path($conf->{pyzor_path});
|
||||||
|
my $opts = untaint_var($conf->{pyzor_options}) || '';
|
||||||
|
|
||||||
|
$pms->enter_helper_run_mode();
|
||||||
|
|
||||||
|
my $pid;
|
||||||
|
my @resp;
|
||||||
|
my $timer = Mail::SpamAssassin::Timeout->new(
|
||||||
|
{ secs => $timeout, deadline => $pms->{master_deadline} });
|
||||||
|
my $err = $timer->run_and_catch(sub {
|
||||||
|
local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
|
||||||
|
|
||||||
|
dbg("pyzor: opening pipe: ".
|
||||||
|
join(' ', $path, $opts, "check", "<".$pms->{pyzor_tmpfile}));
|
||||||
|
|
||||||
|
$pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*PYZOR,
|
||||||
|
$pms->{pyzor_tmpfile}, 1, $path, split(' ', $opts), "check");
|
||||||
|
$pid or die "$!\n";
|
||||||
|
|
||||||
|
# read+split avoids a Perl I/O bug (Bug 5985)
|
||||||
|
my($inbuf, $nread);
|
||||||
|
my $resp = '';
|
||||||
|
while ($nread = read(PYZOR, $inbuf, 8192)) { $resp .= $inbuf }
|
||||||
|
defined $nread or die "error reading from pipe: $!";
|
||||||
|
@resp = split(/^/m, $resp, -1);
|
||||||
|
|
||||||
|
my $errno = 0;
|
||||||
|
close PYZOR or $errno = $!;
|
||||||
|
if (proc_status_ok($?, $errno)) {
|
||||||
|
dbg("pyzor: [%s] finished successfully", $pid);
|
||||||
|
} elsif (proc_status_ok($?, $errno, 0, 1)) { # sometimes it exits with 1
|
||||||
|
dbg("pyzor: [%s] finished: %s", $pid, exit_status_str($?, $errno));
|
||||||
|
} else {
|
||||||
|
info("pyzor: [%s] error: %s", $pid, exit_status_str($?, $errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (defined(fileno(*PYZOR))) { # still open
|
||||||
|
if ($pid) {
|
||||||
|
if (kill('TERM', $pid)) {
|
||||||
|
dbg("pyzor: killed stale helper [$pid]");
|
||||||
|
} else {
|
||||||
|
dbg("pyzor: killing helper application [$pid] failed: $!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
my $errno = 0;
|
||||||
|
close PYZOR or $errno = $!;
|
||||||
|
proc_status_ok($?, $errno)
|
||||||
|
or info("pyzor: [%s] error: %s", $pid, exit_status_str($?, $errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
$pms->leave_helper_run_mode();
|
||||||
|
|
||||||
|
if ($timer->timed_out()) {
|
||||||
|
dbg("pyzor: check timed out after $timeout seconds");
|
||||||
|
return ();
|
||||||
|
} elsif ($err) {
|
||||||
|
chomp $err;
|
||||||
|
info("pyzor: check failed: $err");
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return @resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
$pms->leave_helper_run_mode();
|
|
||||||
|
|
||||||
if ($timer->timed_out()) {
|
|
||||||
dbg("pyzor: check timed out after $timeout seconds");
|
|
||||||
return ();
|
|
||||||
} elsif ($err) {
|
|
||||||
chomp $err;
|
|
||||||
info("pyzor: check failed: $err");
|
|
||||||
return ();
|
|
||||||
}
|
|
||||||
|
|
||||||
return @resp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_tick {
|
sub check_tick {
|
||||||
@ -537,7 +647,7 @@ sub _check_forked_result {
|
|||||||
sub _check_result {
|
sub _check_result {
|
||||||
my ($self, $pms, $results) = @_;
|
my ($self, $pms, $results) = @_;
|
||||||
|
|
||||||
if (!@$results) {
|
unless ((defined $results) && @$results) {
|
||||||
dbg("pyzor: no response from server");
|
dbg("pyzor: no response from server");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -569,7 +679,7 @@ sub _check_result {
|
|||||||
my $wl_min = $conf->{pyzor_welcomelist_min};
|
my $wl_min = $conf->{pyzor_welcomelist_min};
|
||||||
|
|
||||||
my $wl_limit = $count_wl >= $wl_min ?
|
my $wl_limit = $count_wl >= $wl_min ?
|
||||||
$count * $conf->{pyzor_welcomelist_factor} : 0;
|
$count * $conf->{pyzor_welcomelist_factor} : 0;
|
||||||
|
|
||||||
dbg("pyzor: result: COUNT=$count/$count_min WELCOMELIST=$count_wl/$wl_min/%.1f",
|
dbg("pyzor: result: COUNT=$count/$count_min WELCOMELIST=$count_wl/$wl_min/%.1f",
|
||||||
$wl_limit);
|
$wl_limit);
|
||||||
@ -606,84 +716,138 @@ sub plugin_report {
|
|||||||
return if $options->{report}->{options}->{dont_report_to_pyzor};
|
return if $options->{report}->{options}->{dont_report_to_pyzor};
|
||||||
return if !$self->is_pyzor_available();
|
return if !$self->is_pyzor_available();
|
||||||
|
|
||||||
# use temporary file: open2() is unreliable due to buffering under spamd
|
if($self->{main}->{conf}->{pyzor_perl}) {
|
||||||
my $tmpf = $options->{report}->create_fulltext_tmpfile($options->{text});
|
if (!$options->{report}->{options}->{dont_report_to_pyzor} && $self->is_pyzor_available()) {
|
||||||
if ($self->pyzor_report($options, $tmpf)) {
|
if ($self->pyzor_report($options)) {
|
||||||
$options->{report}->{report_available} = 1;
|
$options->{report}->{report_available} = 1;
|
||||||
info("reporter: spam reported to Pyzor");
|
info("reporter: spam reported to Pyzor");
|
||||||
$options->{report}->{report_return} = 1;
|
$options->{report}->{report_return} = 1;
|
||||||
|
} else {
|
||||||
|
info("reporter: could not report spam to Pyzor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# use temporary file: open2() is unreliable due to buffering under spamd
|
||||||
|
my $tmpf = $options->{report}->create_fulltext_tmpfile($options->{text});
|
||||||
|
if ($self->pyzor_report($options, $tmpf)) {
|
||||||
|
$options->{report}->{report_available} = 1;
|
||||||
|
info("reporter: spam reported to Pyzor");
|
||||||
|
$options->{report}->{report_return} = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
info("reporter: could not report spam to Pyzor");
|
||||||
|
}
|
||||||
|
$options->{report}->delete_fulltext_tmpfile($tmpf);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
info("reporter: could not report spam to Pyzor");
|
|
||||||
}
|
|
||||||
$options->{report}->delete_fulltext_tmpfile($tmpf);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub pyzor_report {
|
sub pyzor_report {
|
||||||
my ($self, $options, $tmpf) = @_;
|
my ( $self, $options, $tmpf ) = @_;
|
||||||
|
|
||||||
# note: not really tainted, this came from system configuration file
|
my $conf = $self->{main}->{conf};
|
||||||
my $path = untaint_file_path($options->{report}->{conf}->{pyzor_path});
|
|
||||||
my $opts = untaint_var($options->{report}->{conf}->{pyzor_options}) || '';
|
my $server_port;
|
||||||
|
my @pyzor_hosts;
|
||||||
|
my @pyzor_ports;
|
||||||
|
if(defined $conf->{pyzor_host}) {
|
||||||
|
@pyzor_hosts = @{$conf->{pyzor_host}};
|
||||||
|
}
|
||||||
|
if(defined $conf->{pyzor_port}) {
|
||||||
|
@pyzor_ports = @{$conf->{pyzor_port}};
|
||||||
|
}
|
||||||
|
$pyzor_hosts[0] //= 'public.pyzor.org';
|
||||||
|
$pyzor_ports[0] //= 24441;
|
||||||
|
|
||||||
my $timeout = $self->{main}->{conf}->{pyzor_timeout};
|
my $timeout = $self->{main}->{conf}->{pyzor_timeout};
|
||||||
|
|
||||||
$options->{report}->enter_helper_run_mode();
|
if($self->{main}->{conf}->{pyzor_perl}) {
|
||||||
|
my $pms =
|
||||||
|
Mail::SpamAssassin::PerMsgStatus->new($self->{main}, $options->{msg});
|
||||||
|
|
||||||
my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
|
my $cnt = 0;
|
||||||
my $err = $timer->run_and_catch(sub {
|
foreach my $server_host ( @pyzor_hosts ) {
|
||||||
|
$server_port = $pyzor_ports[$cnt];
|
||||||
|
$cnt++;
|
||||||
|
my $client = Mail::SpamAssassin::Pyzor::Client->new( 'timeout' => $timeout,
|
||||||
|
'server_host' => $server_host,
|
||||||
|
'server_port' => $server_port );
|
||||||
|
|
||||||
local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
|
my $digest = Mail::SpamAssassin::Pyzor::Digest::get( $pms );
|
||||||
|
|
||||||
dbg("pyzor: opening pipe: " . join(' ', $path, $opts, "report", "< $tmpf"));
|
local $@;
|
||||||
|
my $ref = eval { $client->report($digest); };
|
||||||
|
if ($@) {
|
||||||
|
warn("pyzor: report failed: $@");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
elsif ( $ref->{'Code'} ne 200 ) {
|
||||||
|
dbg("pyzor: report failed with invalid code: $ref->{'Code'}: $ref->{'Diag'}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# note: not really tainted, this came from system configuration file
|
||||||
|
my $path = untaint_file_path($options->{report}->{conf}->{pyzor_path});
|
||||||
|
my $opts = untaint_var($options->{report}->{conf}->{pyzor_options}) || '';
|
||||||
|
|
||||||
my $pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*PYZOR,
|
my $timeout = $self->{main}->{conf}->{pyzor_timeout};
|
||||||
$tmpf, 1, $path, split(' ', $opts), "report");
|
|
||||||
$pid or die "$!\n";
|
|
||||||
|
|
||||||
my($inbuf,$nread,$nread_all); $nread_all = 0;
|
$options->{report}->enter_helper_run_mode();
|
||||||
# response is ignored, just check its existence
|
|
||||||
while ( $nread=read(PYZOR,$inbuf,8192) ) { $nread_all += $nread }
|
|
||||||
defined $nread or die "error reading from pipe: $!";
|
|
||||||
|
|
||||||
dbg("pyzor: empty response") if $nread_all < 1;
|
my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
|
||||||
|
my $err = $timer->run_and_catch(sub {
|
||||||
|
|
||||||
my $errno = 0; close PYZOR or $errno = $!;
|
local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
|
||||||
# closing a pipe also waits for the process executing on the pipe to
|
|
||||||
# complete, no need to explicitly call waitpid
|
dbg("pyzor: opening pipe: " . join(' ', $path, $opts, "report", "< $tmpf"));
|
||||||
# my $child_stat = waitpid($pid,0) > 0 ? $? : undef;
|
|
||||||
if (proc_status_ok($?,$errno, 0)) {
|
my $pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*PYZOR,
|
||||||
dbg("pyzor: [%s] reporter finished successfully", $pid);
|
$tmpf, 1, $path, split(' ', $opts), "report");
|
||||||
} else {
|
$pid or die "$!\n";
|
||||||
info("pyzor: [%s] reporter error: %s", $pid, exit_status_str($?,$errno));
|
|
||||||
|
my($inbuf,$nread,$nread_all); $nread_all = 0;
|
||||||
|
# response is ignored, just check its existence
|
||||||
|
while ( $nread=read(PYZOR,$inbuf,8192) ) { $nread_all += $nread }
|
||||||
|
defined $nread or die "error reading from pipe: $!";
|
||||||
|
|
||||||
|
dbg("pyzor: empty response") if $nread_all < 1;
|
||||||
|
|
||||||
|
my $errno = 0; close PYZOR or $errno = $!;
|
||||||
|
# closing a pipe also waits for the process executing on the pipe to
|
||||||
|
# complete, no need to explicitly call waitpid
|
||||||
|
# my $child_stat = waitpid($pid,0) > 0 ? $? : undef;
|
||||||
|
if (proc_status_ok($?,$errno, 0)) {
|
||||||
|
dbg("pyzor: [%s] reporter finished successfully", $pid);
|
||||||
|
} else {
|
||||||
|
info("pyzor: [%s] reporter error: %s", $pid, exit_status_str($?,$errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$options->{report}->leave_helper_run_mode();
|
||||||
|
|
||||||
|
if ($timer->timed_out()) {
|
||||||
|
dbg("reporter: pyzor report timed out after $timeout seconds");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
if ($err) {
|
||||||
|
chomp $err;
|
||||||
$options->{report}->leave_helper_run_mode();
|
if ($err eq '__brokenpipe__ignore__') {
|
||||||
|
dbg("reporter: pyzor report failed: broken pipe");
|
||||||
if ($timer->timed_out()) {
|
} else {
|
||||||
dbg("reporter: pyzor report timed out after $timeout seconds");
|
warn("reporter: pyzor report failed: $err\n");
|
||||||
return 0;
|
}
|
||||||
}
|
return 0;
|
||||||
|
|
||||||
if ($err) {
|
|
||||||
chomp $err;
|
|
||||||
if ($err eq '__brokenpipe__ignore__') {
|
|
||||||
dbg("reporter: pyzor report failed: broken pipe");
|
|
||||||
} else {
|
|
||||||
warn("reporter: pyzor report failed: $err\n");
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Version features
|
# Version features
|
||||||
sub has_fork { 1 }
|
sub has_fork { 1 }
|
||||||
|
sub has_perl { 1 }
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
@ -32,10 +32,6 @@ input is validated through reputation assignments.
|
|||||||
|
|
||||||
See http://razor.sourceforge.net/ for more information about Razor.
|
See http://razor.sourceforge.net/ for more information about Razor.
|
||||||
|
|
||||||
=head1 USER SETTINGS
|
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
package Mail::SpamAssassin::Plugin::Razor2;
|
package Mail::SpamAssassin::Plugin::Razor2;
|
||||||
@ -90,6 +86,38 @@ sub set_config {
|
|||||||
my ($self, $conf) = @_;
|
my ($self, $conf) = @_;
|
||||||
my @cmds;
|
my @cmds;
|
||||||
|
|
||||||
|
=head1 DEPENDENCIES
|
||||||
|
|
||||||
|
Razor2 requires the C<Razor2::Client::Agent> Perl module to be installed.
|
||||||
|
|
||||||
|
=head1 RULE DEFINITIONS
|
||||||
|
|
||||||
|
Razor2 calculates a signature for each part of a multipart message and then
|
||||||
|
compares those signatures to a database of known spam signatures. The server returns a confidence
|
||||||
|
value (0-100) for each part of the message. The part with the highest confidence value is used as the confidence value
|
||||||
|
for the message.
|
||||||
|
|
||||||
|
The following eval rules are provided by this plugin:
|
||||||
|
|
||||||
|
full RULENAME eval:check_razor2()
|
||||||
|
|
||||||
|
Returns true if the confidence value of the message is greater than or equal to `min_cf` as defined in
|
||||||
|
the Razor2 configuration file 'razor-agent.conf(1)'.
|
||||||
|
|
||||||
|
full RULENAME eval:check_razor2_range(<engine>,<min>,<max>)
|
||||||
|
|
||||||
|
<engine> Engine number (4, 8 or '')
|
||||||
|
<min> Minimum confidence value (0-100)
|
||||||
|
<max> Maximum confidence value (0-100)
|
||||||
|
|
||||||
|
Returns true if the spam confidence value for the message is greater than or equal to <min> and
|
||||||
|
less than or equal to <max>. If <engine> is not specified, the engine with the highest
|
||||||
|
confidence value is used.
|
||||||
|
|
||||||
|
=head1 USER SETTINGS
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
=item use_razor2 (0|1) (default: 1)
|
=item use_razor2 (0|1) (default: 1)
|
||||||
|
|
||||||
Whether to use Razor2, if it is available.
|
Whether to use Razor2, if it is available.
|
||||||
@ -98,11 +126,16 @@ Whether to use Razor2, if it is available.
|
|||||||
|
|
||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'use_razor2',
|
setting => 'use_razor2',
|
||||||
is_admin => 1,
|
|
||||||
default => 1,
|
default => 1,
|
||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 ADMINISTRATOR SETTINGS
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
=item razor_fork (0|1) (default: 1)
|
=item razor_fork (0|1) (default: 1)
|
||||||
|
|
||||||
Instead of running Razor2 synchronously, fork separate process for it and
|
Instead of running Razor2 synchronously, fork separate process for it and
|
||||||
@ -118,12 +151,6 @@ throughput. Considered experimental on Windows, where default is 0.
|
|||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
||||||
});
|
});
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
=head1 ADMINISTRATOR SETTINGS
|
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=item razor_timeout n (default: 5)
|
=item razor_timeout n (default: 5)
|
||||||
|
|
||||||
How many seconds you wait for Razor to complete before you go on without
|
How many seconds you wait for Razor to complete before you go on without
|
||||||
|
@ -171,7 +171,7 @@ by a local or site-specific configuration or by C<user_prefs>.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 ADMINISTRATOR OPTIONS
|
=head1 ADMINISTRATOR SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
|
@ -211,6 +211,32 @@ for that rule.
|
|||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
});
|
});
|
||||||
|
|
||||||
|
=item shortcircuit_min_ham_score n.nn (default: undef)
|
||||||
|
|
||||||
|
When shortcircuit_min_ham_score is set, SpamAssassin will stop processing when total score
|
||||||
|
will be lower then this value.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
push (@cmds, {
|
||||||
|
setting => 'shortcircuit_min_ham_score',
|
||||||
|
default => undef,
|
||||||
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
|
});
|
||||||
|
|
||||||
|
=item shortcircuit_max_spam_score n.nn (default: undef)
|
||||||
|
|
||||||
|
When shortcircuit_max_spam_score is set, SpamAssassin will stop processing when total score
|
||||||
|
will be higher then this value.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
push (@cmds, {
|
||||||
|
setting => 'shortcircuit_max_spam_score',
|
||||||
|
default => undef,
|
||||||
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
||||||
|
});
|
||||||
|
|
||||||
$conf->{parser}->register_commands(\@cmds);
|
$conf->{parser}->register_commands(\@cmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,18 +259,36 @@ sub hit_rule {
|
|||||||
my $scan = $params->{permsgstatus};
|
my $scan = $params->{permsgstatus};
|
||||||
my $rule = $params->{rulename};
|
my $rule = $params->{rulename};
|
||||||
|
|
||||||
|
my $conf = $scan->{conf};
|
||||||
|
my $score = $params->{score};
|
||||||
|
|
||||||
|
return if $scan->{shortcircuited};
|
||||||
|
|
||||||
# don't s/c if we're linting
|
# don't s/c if we're linting
|
||||||
return if ($self->{main}->{lint_rules});
|
return if ($self->{main}->{lint_rules});
|
||||||
|
|
||||||
# don't s/c if we're in compile_now()
|
# don't s/c if we're in compile_now()
|
||||||
return if ($self->{am_compiling});
|
return if ($self->{am_compiling});
|
||||||
|
|
||||||
|
if((defined $conf->{shortcircuit_min_ham_score} and ($scan->{score} < $conf->{shortcircuit_min_ham_score})) or
|
||||||
|
(defined $conf->{shortcircuit_max_spam_score} and ($scan->{score} > $conf->{shortcircuit_max_spam_score}))) {
|
||||||
|
$scan->{shortcircuited} = 1;
|
||||||
|
|
||||||
|
# bug 5256: if we short-circuit, don't do auto-learning
|
||||||
|
$scan->{disable_auto_learning} = 1;
|
||||||
|
$scan->{shortcircuit_type} = ($scan->{score} < 0 ? 'ham' : 'spam');
|
||||||
|
if($scan->{shortcircuit_type} eq 'ham') {
|
||||||
|
dbg("shortcircuit: s/c due to shortcircuit_min_ham_score $conf->{shortcircuit_min_ham_score}, total score is $scan->{score}");
|
||||||
|
$scan->got_hit('SHORTCIRCUIT', '', score => -0.001);
|
||||||
|
} elsif($scan->{shortcircuit_type} eq 'spam') {
|
||||||
|
dbg("shortcircuit: s/c due to shortcircuit_max_spam_score $conf->{shortcircuit_max_spam_score}, total score is $scan->{score}");
|
||||||
|
$scan->got_hit('SHORTCIRCUIT', '', score => 0.001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my $sctype = $scan->{conf}->{shortcircuit}->{$rule};
|
my $sctype = $scan->{conf}->{shortcircuit}->{$rule};
|
||||||
return unless $sctype;
|
return unless $sctype;
|
||||||
|
|
||||||
my $conf = $scan->{conf};
|
|
||||||
my $score = $params->{score};
|
|
||||||
|
|
||||||
$scan->{shortcircuit_rule} = $rule;
|
$scan->{shortcircuit_rule} = $rule;
|
||||||
my $scscore;
|
my $scscore;
|
||||||
if ($sctype eq 'on') { # guess by rule score
|
if ($sctype eq 'on') { # guess by rule score
|
||||||
|
@ -44,7 +44,6 @@ package Mail::SpamAssassin::Plugin::SpamCop;
|
|||||||
use Mail::SpamAssassin::Plugin;
|
use Mail::SpamAssassin::Plugin;
|
||||||
use Mail::SpamAssassin::Logger;
|
use Mail::SpamAssassin::Logger;
|
||||||
use Mail::SpamAssassin::Util qw(untaint_var);
|
use Mail::SpamAssassin::Util qw(untaint_var);
|
||||||
use IO::Socket;
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
# use bytes;
|
# use bytes;
|
||||||
@ -81,7 +80,7 @@ sub set_config {
|
|||||||
my($self, $conf) = @_;
|
my($self, $conf) = @_;
|
||||||
my @cmds;
|
my @cmds;
|
||||||
|
|
||||||
=head1 USER OPTIONS
|
=head1 USER SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ sub set_config {
|
|||||||
my ($self, $conf) = @_;
|
my ($self, $conf) = @_;
|
||||||
my @cmds;
|
my @cmds;
|
||||||
|
|
||||||
=head1 USER OPTIONS
|
=head1 USER SETTINGS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
@ -630,6 +630,10 @@ sub check_language {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $rulename = $scan->get_current_eval_rule_name();
|
||||||
|
my $matched_languages = join(' ', @matches);
|
||||||
|
$scan->test_log("Languages detected: $matched_languages", $rulename);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -897,6 +897,48 @@ some testing it could be likely at least slightly increased.
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
=item B<txrep_report_details>
|
||||||
|
|
||||||
|
0 | 1 | 2 (default: 0)
|
||||||
|
|
||||||
|
Add TxRep details to the rule's description in the message report or summary,
|
||||||
|
similar to how RBL rules commonly are showing listed domains.
|
||||||
|
|
||||||
|
If enabled (value 1) the identificators (From address bound to originating IP
|
||||||
|
address fraction, From address alone, domain name bound to originating IP
|
||||||
|
address fraction, originating IP address and HELO if available) used in
|
||||||
|
calculating the sender's overall reputation are listed, including the
|
||||||
|
originating IP address fraction (according to the mask settings) where
|
||||||
|
applicable.
|
||||||
|
|
||||||
|
If this option is set to 2, the listed identificators' individual mean
|
||||||
|
reputation and count are reported in addition.
|
||||||
|
|
||||||
|
Identificators and additional data will only be added to the description on a
|
||||||
|
message's initial scan. Re-processing a previously already scanned message
|
||||||
|
will not list the individual idenficators and their respective reputation
|
||||||
|
values used originally.
|
||||||
|
|
||||||
|
This option is disabled by default for now, due to potential formatting issues
|
||||||
|
caused by the number and length of additional description details.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
push (@cmds, {
|
||||||
|
setting => 'txrep_report_details',
|
||||||
|
default => 0,
|
||||||
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
|
||||||
|
code => sub {
|
||||||
|
my ($self, $key, $value, $line) = @_;
|
||||||
|
|
||||||
|
return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE
|
||||||
|
if ($value eq '');
|
||||||
|
return $Mail::SpamAssassin::Conf::INVALID_VALUE
|
||||||
|
unless ($value =~ /^[012]$/);
|
||||||
|
|
||||||
|
$self->{txrep_report_details} = $value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
@ -1240,9 +1282,13 @@ sub check_senders_reputation {
|
|||||||
|
|
||||||
my $autolearn = defined $self->{autolearn};
|
my $autolearn = defined $self->{autolearn};
|
||||||
$self->{last_pms} = $self->{autolearn} = undef;
|
$self->{last_pms} = $self->{autolearn} = undef;
|
||||||
|
$self->{pms} = $pms;
|
||||||
|
|
||||||
# Cases where we would not be able to use TxRep
|
# Cases where we would not be able to use TxRep
|
||||||
return 0 unless ($self->{conf}->{use_txrep});
|
if(not $self->{conf}->{use_txrep}) {
|
||||||
|
dbg("TxRep is disabled, quitting");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if ($self->{conf}->{use_auto_welcomelist}) {
|
if ($self->{conf}->{use_auto_welcomelist}) {
|
||||||
warn("TxRep: cannot run when Auto-Welcomelist is enabled. Please disable it!\n");
|
warn("TxRep: cannot run when Auto-Welcomelist is enabled. Please disable it!\n");
|
||||||
return 0;
|
return 0;
|
||||||
@ -1294,7 +1340,7 @@ sub check_senders_reputation {
|
|||||||
if ($self->{conf}->{txrep_track_messages}) {
|
if ($self->{conf}->{txrep_track_messages}) {
|
||||||
if ($msg_id) {
|
if ($msg_id) {
|
||||||
my $msg_rep = $self->check_reputations($pms, 'MSG_ID', $msg_id, undef, $date, undef);
|
my $msg_rep = $self->check_reputations($pms, 'MSG_ID', $msg_id, undef, $date, undef);
|
||||||
if (defined $msg_rep && $self->count()) {
|
if (defined $msg_rep && ($self->count() > 0)) {
|
||||||
if (defined $self->{learning} && !defined $self->{forgetting}) {
|
if (defined $self->{learning} && !defined $self->{forgetting}) {
|
||||||
# already learned, forget only if already learned (count>1), and relearn
|
# already learned, forget only if already learned (count>1), and relearn
|
||||||
# when only scanned (count=1), go ahead with normal rep scan
|
# when only scanned (count=1), go ahead with normal rep scan
|
||||||
@ -1312,6 +1358,9 @@ sub check_senders_reputation {
|
|||||||
$pms->got_hit("TXREP", "TXREP: ", ruletype => 'eval', score => sprintf("%0.3f", $delta));
|
$pms->got_hit("TXREP", "TXREP: ", ruletype => 'eval', score => sprintf("%0.3f", $delta));
|
||||||
}
|
}
|
||||||
dbg("TxRep: message %s already scanned, using old data; post-TxRep score: %0.3f", $msg_id, $pms->{score} || 'undef');
|
dbg("TxRep: message %s already scanned, using old data; post-TxRep score: %0.3f", $msg_id, $pms->{score} || 'undef');
|
||||||
|
if (!defined $self->{txKeepStoreTied}) {
|
||||||
|
$self->finish();
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} # no stored reputation found, go ahead with normal rep scan
|
} # no stored reputation found, go ahead with normal rep scan
|
||||||
@ -1345,9 +1394,16 @@ sub check_senders_reputation {
|
|||||||
);
|
);
|
||||||
|
|
||||||
my $ip = $origip;
|
my $ip = $origip;
|
||||||
|
my $spf_domain;
|
||||||
if ($signedby) {
|
if ($signedby) {
|
||||||
$ip = undef;
|
$ip = undef;
|
||||||
$domain = $signedby;
|
$domain = $signedby;
|
||||||
|
} elsif ($pms->{spf_pass} && $self->{conf}->{txrep_spf} && defined $pms->{spf_sender}) {
|
||||||
|
$ip = undef;
|
||||||
|
$spf_domain = $pms->{spf_sender};
|
||||||
|
$spf_domain =~ s/^.+@//;
|
||||||
|
$signedby = 'spf-'.$spf_domain;
|
||||||
|
dbg("TxRep: email signed by spf domain $spf_domain");
|
||||||
} elsif ($pms->{spf_pass} && $self->{conf}->{txrep_spf}) {
|
} elsif ($pms->{spf_pass} && $self->{conf}->{txrep_spf}) {
|
||||||
$ip = undef;
|
$ip = undef;
|
||||||
$signedby = 'spf';
|
$signedby = 'spf';
|
||||||
@ -1463,7 +1519,7 @@ sub check_reputation {
|
|||||||
# TEMPLATE TAGS should match [A-Z] in their name
|
# TEMPLATE TAGS should match [A-Z] in their name
|
||||||
# and "_" must be avoided
|
# and "_" must be avoided
|
||||||
$tag_id =~ s/_//g;
|
$tag_id =~ s/_//g;
|
||||||
if (defined $found && $self->count()) {
|
if (defined $found && ($self->count() > 0)) {
|
||||||
$meanrep = $self->total() / $self->count();
|
$meanrep = $self->total() / $self->count();
|
||||||
}
|
}
|
||||||
if ($self->{learning} && defined $msgscore) {
|
if ($self->{learning} && defined $msgscore) {
|
||||||
@ -1479,13 +1535,28 @@ sub check_reputation {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$self->{totalweight} += $weight;
|
$self->{totalweight} += $weight;
|
||||||
if ($key eq 'MSG_ID' && $self->count() > 0) {
|
if ($key eq 'MSG_ID' && ($self->count() > 0)) {
|
||||||
$delta = $self->total() / $self->count();
|
$delta = $self->total() / $self->count();
|
||||||
$pms->set_tag('TXREP'.$tag_id, sprintf("%2.1f", $delta));
|
$pms->set_tag('TXREP'.$tag_id, sprintf("%2.1f", $delta));
|
||||||
} elsif (defined $self->total()) {
|
} elsif (defined $self->total()) {
|
||||||
#Bug 7164 - $msgscore undefined
|
#Bug 7164 - $msgscore undefined
|
||||||
if (defined $msgscore) {
|
# in some cases we can have negative number
|
||||||
$delta = ($self->total() + $msgscore) / (1 + $self->count()) - $msgscore;
|
# even if both total and $msgscore are positive numbers
|
||||||
|
my $deltacheck;
|
||||||
|
my $skipmsgscore = 0;
|
||||||
|
if(defined $msgscore) {
|
||||||
|
$deltacheck = ($self->total() + $msgscore) / (1 + $self->count()) - $msgscore;
|
||||||
|
if(($self->total() > 0) && ($msgscore > 0) && ($deltacheck < 0)) {
|
||||||
|
$skipmsgscore = 1;
|
||||||
|
} elsif(($self->total() < 0) && ($msgscore < 0) && ($deltacheck > 0)) {
|
||||||
|
$skipmsgscore = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($skipmsgscore) {
|
||||||
|
dbg("TxRep: skipping msg score $msgscore when calculating delta");
|
||||||
|
}
|
||||||
|
if (defined $msgscore and not $skipmsgscore) {
|
||||||
|
$delta = $deltacheck;
|
||||||
} else {
|
} else {
|
||||||
$delta = ($self->total()) / (1 + $self->count());
|
$delta = ($self->total()) / (1 + $self->count());
|
||||||
}
|
}
|
||||||
@ -1506,6 +1577,26 @@ sub check_reputation {
|
|||||||
$delta || 0,
|
$delta || 0,
|
||||||
$id || 'none'
|
$id || 'none'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($self->{conf}->{txrep_report_details}
|
||||||
|
&& defined $id && defined $meanrep && $tag_id ne "MSGID") {
|
||||||
|
|
||||||
|
my $log = sprintf("%s: %s",
|
||||||
|
$tag_id,
|
||||||
|
(defined $ip) ? $id."|".$self->ip_to_awl_key($ip) : $id
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($self->{conf}->{txrep_report_details} == 2) {
|
||||||
|
$log .= sprintf(", rep: %.2f, count: %d",
|
||||||
|
$meanrep,
|
||||||
|
$self->count() || 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pms->test_log($log, "TXREP");
|
||||||
|
# dbg ("TxRep: test_log: $log");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
$timer = $self->{main}->time_method('update_txrep_'.lc($key));
|
$timer = $self->{main}->time_method('update_txrep_'.lc($key));
|
||||||
if (defined $msgscore) {
|
if (defined $msgscore) {
|
||||||
@ -1538,7 +1629,7 @@ sub check_reputation {
|
|||||||
#--------------------------------------------------------------------------
|
#--------------------------------------------------------------------------
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
sub count {my $self=shift; return (defined $self->{checker})? $self->{entry}->{msgcount} : undef;}
|
sub count {my $self=shift; return (defined $self->{checker})? $self->{entry}->{msgcount} : 0;}
|
||||||
sub total {my $self=shift; return (defined $self->{checker})? $self->{entry}->{totscore} : undef;}
|
sub total {my $self=shift; return (defined $self->{checker})? $self->{entry}->{totscore} : undef;}
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
@ -1577,7 +1668,7 @@ sub add_score {
|
|||||||
$self->{entry}->{msgcount} ||= 0;
|
$self->{entry}->{msgcount} ||= 0;
|
||||||
|
|
||||||
# performing the dilution aging correction
|
# performing the dilution aging correction
|
||||||
if (defined $self->total() && defined $self->count() && defined $self->{txrep_dilution_factor}) {
|
if (defined $self->total() && defined $self->count() && $self->count() > 0 && defined $self->{txrep_dilution_factor}) {
|
||||||
my $diluted_total =
|
my $diluted_total =
|
||||||
($self->count() + 1) *
|
($self->count() + 1) *
|
||||||
($self->{txrep_dilution_factor} * $self->total() + $score) /
|
($self->{txrep_dilution_factor} * $self->total() + $score) /
|
||||||
@ -1802,6 +1893,12 @@ sub pack_addr {
|
|||||||
|
|
||||||
if ( defined $origip) {$origip = $self->ip_to_awl_key($origip);}
|
if ( defined $origip) {$origip = $self->ip_to_awl_key($origip);}
|
||||||
if (!defined $origip) {$origip = 'none';}
|
if (!defined $origip) {$origip = 'none';}
|
||||||
|
if ( $self->{conf}->{txrep_welcomelist_out} &&
|
||||||
|
defined $self->{pms}->{relays_internal} && @{$self->{pms}->{relays_internal}} &&
|
||||||
|
(!defined $self->{pms}->{relays_external} || !@{$self->{pms}->{relays_external}})
|
||||||
|
and $addr =~ /(?:[^\s\@]+)\@(?:[^\s\@]+)/) {
|
||||||
|
$origip = 'WELCOMELIST_OUT';
|
||||||
|
}
|
||||||
return $addr . "|ip=" . $origip;
|
return $addr . "|ip=" . $origip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ any of: decimal digits, 0x followed by up to 8 hexadecimal digits, or an IPv4
|
|||||||
address in quad-dot form. The 'A' records (IPv4 dotted address) as returned
|
address in quad-dot form. The 'A' records (IPv4 dotted address) as returned
|
||||||
by DNSBLs lookups are converted into a numerical form (r) and checked against
|
by DNSBLs lookups are converted into a numerical form (r) and checked against
|
||||||
the specified sub-test as follows:
|
the specified sub-test as follows:
|
||||||
for a range n1-n2 the following must be true: (r >= n1 && r <= n2);
|
for a range n1-n2 the following must be true: (r E<gt>= n1 && r E<lt>= n3);
|
||||||
for a n/m form the following must be true: (r & m) == (n & m);
|
for a n/m form the following must be true: (r & m) == (n & m);
|
||||||
for a single value in quad-dot form the following must be true: r == n;
|
for a single value in quad-dot form the following must be true: r == n;
|
||||||
for a single decimal or hex form the following must be true:
|
for a single decimal or hex form the following must be true:
|
||||||
@ -181,7 +181,7 @@ any of: decimal digits, 0x followed by up to 8 hexadecimal digits, or an IPv4
|
|||||||
address in quad-dot form. The 'A' records (IPv4 dotted address) as returned
|
address in quad-dot form. The 'A' records (IPv4 dotted address) as returned
|
||||||
by DNSBLs lookups are converted into a numerical form (r) and checked against
|
by DNSBLs lookups are converted into a numerical form (r) and checked against
|
||||||
the specified sub-test as follows:
|
the specified sub-test as follows:
|
||||||
for a range n1-n2 the following must be true: (r >= n1 && r <= n2);
|
for a range n1-n2 the following must be true: (r E<gt>= n1 && r E<lt>= n2);
|
||||||
for a n/m form the following must be true: (r & m) == (n & m);
|
for a n/m form the following must be true: (r & m) == (n & m);
|
||||||
for a single value in quad-dot form the following must be true: r == n;
|
for a single value in quad-dot form the following must be true: r == n;
|
||||||
for a single decimal or hex form the following must be true:
|
for a single decimal or hex form the following must be true:
|
||||||
@ -221,7 +221,7 @@ Specify a RHSBL-style domain-NS lookup, as above, with a sub-test.
|
|||||||
C<NAME_OF_RULE> is the name of the rule to be used, C<rhsbl_zone> is the zone
|
C<NAME_OF_RULE> is the name of the rule to be used, C<rhsbl_zone> is the zone
|
||||||
to look up domain names in, and C<lookuptype> is the type of lookup (B<TXT> or
|
to look up domain names in, and C<lookuptype> is the type of lookup (B<TXT> or
|
||||||
B<A>). C<subtest> is the sub-test to run against the returned data; see
|
B<A>). C<subtest> is the sub-test to run against the returned data; see
|
||||||
<urirhssub>.
|
C<urirhssub>.
|
||||||
|
|
||||||
Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
|
Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
|
||||||
C<check_uridnsbl()> to use this.
|
C<check_uridnsbl()> to use this.
|
||||||
@ -246,7 +246,7 @@ Specify a RHSBL-style domain-NS lookup, as above, with a sub-test.
|
|||||||
C<NAME_OF_RULE> is the name of the rule to be used, C<rhsbl_zone> is the zone
|
C<NAME_OF_RULE> is the name of the rule to be used, C<rhsbl_zone> is the zone
|
||||||
to look up domain names in, and C<lookuptype> is the type of lookup (B<TXT> or
|
to look up domain names in, and C<lookuptype> is the type of lookup (B<TXT> or
|
||||||
B<A>). C<subtest> is the sub-test to run against the returned data; see
|
B<A>). C<subtest> is the sub-test to run against the returned data; see
|
||||||
<urirhssub>.
|
C<urirhssub>.
|
||||||
|
|
||||||
Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
|
Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
|
||||||
C<check_uridnsbl()> to use this.
|
C<check_uridnsbl()> to use this.
|
||||||
@ -296,6 +296,10 @@ The maximum number of domains to look up.
|
|||||||
Include DKIM uris in lookups. This option is documented in
|
Include DKIM uris in lookups. This option is documented in
|
||||||
Mail::SpamAssassin::Conf.
|
Mail::SpamAssassin::Conf.
|
||||||
|
|
||||||
|
=item uridnsbl_skip_mailto ( 0 / 1) (default: 1)
|
||||||
|
|
||||||
|
Skip mailto links on uris lookups.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 NOTES
|
=head1 NOTES
|
||||||
@ -419,9 +423,13 @@ sub check_dnsbl {
|
|||||||
# 3: !a_empty
|
# 3: !a_empty
|
||||||
# 4: parsed
|
# 4: parsed
|
||||||
# 5: a_empty
|
# 5: a_empty
|
||||||
while (my($uri, $info) = each %{$uris}) {
|
my %huris = %{$uris};
|
||||||
|
foreach my $uri (keys %huris) {
|
||||||
|
my $info = $huris{$uri};
|
||||||
# we want to skip mailto: uris
|
# we want to skip mailto: uris
|
||||||
next if ($uri =~ /^mailto:/i);
|
if ($conf->{uridnsbl_skip_mailto}) {
|
||||||
|
next if ($uri =~ /^mailto:/i);
|
||||||
|
}
|
||||||
|
|
||||||
# no hosts/domains were found via this uri, so skip
|
# no hosts/domains were found via this uri, so skip
|
||||||
next unless ($info->{hosts});
|
next unless ($info->{hosts});
|
||||||
@ -558,6 +566,13 @@ sub set_config {
|
|||||||
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
push(@cmds, {
|
||||||
|
setting => 'uridnsbl_skip_mailto',
|
||||||
|
is_admin => 1,
|
||||||
|
default => 1,
|
||||||
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
|
||||||
|
});
|
||||||
|
|
||||||
push(@cmds, {
|
push(@cmds, {
|
||||||
setting => 'uridnsbl_max_domains',
|
setting => 'uridnsbl_max_domains',
|
||||||
is_admin => 1,
|
is_admin => 1,
|
||||||
@ -1187,5 +1202,6 @@ sub has_subtest_for_ranges { 1 }
|
|||||||
sub has_uridnsbl_for_a { 1 } # uridnsbl rules recognize tflags 'a' and 'ns'
|
sub has_uridnsbl_for_a { 1 } # uridnsbl rules recognize tflags 'a' and 'ns'
|
||||||
sub has_uridnsbl_a_ns { 1 } # has an actually working 'a' flag, unlike above :-(
|
sub has_uridnsbl_a_ns { 1 } # has an actually working 'a' flag, unlike above :-(
|
||||||
sub has_tflags_notrim { 1 } # Bug 7835
|
sub has_tflags_notrim { 1 } # Bug 7835
|
||||||
|
sub has_uridnsbl_skip_mailto { 1 }
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -47,7 +47,7 @@ C<cleaned> is a list including the raw URI and various cleaned
|
|||||||
versions of the raw URI (http://spamassassin.apache%2Eorg/,
|
versions of the raw URI (http://spamassassin.apache%2Eorg/,
|
||||||
https://spamassassin.apache.org/).
|
https://spamassassin.apache.org/).
|
||||||
|
|
||||||
C<text> is the anchor text(s) (text between <a> and </a>) that
|
C<text> is the anchor text(s) (text between E<lt>aE<gt> and E<lt>/aE<gt>) that
|
||||||
linked to the raw URI.
|
linked to the raw URI.
|
||||||
|
|
||||||
C<domain> is the domain(s) found in the cleaned URIs, as trimmed to
|
C<domain> is the domain(s) found in the cleaned URIs, as trimmed to
|
||||||
@ -156,14 +156,13 @@ sub set_config {
|
|||||||
sub check_uri_detail {
|
sub check_uri_detail {
|
||||||
my ($self, $permsg) = @_;
|
my ($self, $permsg) = @_;
|
||||||
|
|
||||||
|
my $test = $permsg->{current_rule_name};
|
||||||
|
my $rule = $permsg->{conf}->{uri_detail}->{$test};
|
||||||
|
|
||||||
my %uri_detail = %{ $permsg->get_uri_detail_list() };
|
my %uri_detail = %{ $permsg->get_uri_detail_list() };
|
||||||
|
|
||||||
while (my ($raw, $info) = each %uri_detail) {
|
while (my ($raw, $info) = each %uri_detail) {
|
||||||
my $test = $permsg->{current_rule_name};
|
dbg("uri: running uri_detail $test: $raw");
|
||||||
|
|
||||||
dbg("uri: running $test\n");
|
|
||||||
|
|
||||||
my $rule = $permsg->{conf}->{uri_detail}->{$test};
|
|
||||||
|
|
||||||
if (exists $rule->{raw}) {
|
if (exists $rule->{raw}) {
|
||||||
my($op,$patt) = @{$rule->{raw}};
|
my($op,$patt) = @{$rule->{raw}};
|
||||||
@ -235,13 +234,7 @@ sub check_uri_detail {
|
|||||||
dbg("uri: host matched: '%s' %s /%s/", $match,$op,$patt);
|
dbg("uri: host matched: '%s' %s /%s/", $match,$op,$patt);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (would_log('dbg', 'rules') > 1) {
|
dbg("uri: all criteria for $test met - HIT");
|
||||||
dbg("uri: criteria for $test met");
|
|
||||||
}
|
|
||||||
|
|
||||||
# reset hash
|
|
||||||
keys %uri_detail;
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ URILocalBL - blocklist URIs using local information (ISP names, address lists, a
|
|||||||
|
|
||||||
This plugin creates some new rule test types, such as "uri_block_cc",
|
This plugin creates some new rule test types, such as "uri_block_cc",
|
||||||
"uri_block_cidr", and "uri_block_isp". These rules apply to the URIs
|
"uri_block_cidr", and "uri_block_isp". These rules apply to the URIs
|
||||||
found in the HTML portion of a message, i.e. <a href=...> markup.
|
found in the HTML portion of a message, i.e. E<lt>a href=...E<gt> markup.
|
||||||
|
|
||||||
loadplugin Mail::SpamAssassin::Plugin::URILocalBL
|
loadplugin Mail::SpamAssassin::Plugin::URILocalBL
|
||||||
|
|
||||||
|
56
upstream/lib/Mail/SpamAssassin/Pyzor.pm
Normal file
56
upstream/lib/Mail/SpamAssassin/Pyzor.pm
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package Mail::SpamAssassin::Pyzor;
|
||||||
|
|
||||||
|
# Copyright 2018 cPanel, LLC.
|
||||||
|
# All rights reserved.
|
||||||
|
# http://cpanel.net
|
||||||
|
#
|
||||||
|
# <@LICENSE>
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to you under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at:
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
# </@LICENSE>
|
||||||
|
#
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
our $VERSION = '0.06_01';
|
||||||
|
|
||||||
|
=encoding utf-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Mail::SpamAssassin::Pyzor - Pyzor spam filtering in Perl
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This distribution contains Perl implementations of parts of
|
||||||
|
L<Pyzor|http://pyzor.org>, a tool for use in spam email filtering.
|
||||||
|
It is intended for use with L<Mail::SpamAssassin> but may be useful
|
||||||
|
in other contexts.
|
||||||
|
|
||||||
|
See the following modules for information on specific tools that
|
||||||
|
the distribution includes:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item * L<Mail::SpamAssassin::Pyzor::Client>
|
||||||
|
|
||||||
|
=item * L<Mail::SpamAssassin::Pyzor::Digest>
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
1;
|
419
upstream/lib/Mail/SpamAssassin/Pyzor/Client.pm
Normal file
419
upstream/lib/Mail/SpamAssassin/Pyzor/Client.pm
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
package Mail::SpamAssassin::Pyzor::Client;
|
||||||
|
|
||||||
|
# Copyright 2018 cPanel, LLC.
|
||||||
|
# All rights reserved.
|
||||||
|
# http://cpanel.net
|
||||||
|
#
|
||||||
|
# <@LICENSE>
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to you under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at:
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
# </@LICENSE>
|
||||||
|
#
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
=encoding utf-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Mail::SpamAssassin::Pyzor::Client - Pyzor client logic
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use Mail::SpamAssassin::Pyzor::Client ();
|
||||||
|
use Mail::SpamAssassin::Pyzor::Digest ();
|
||||||
|
|
||||||
|
my $client = Mail::SpamAssassin::Pyzor::Client->new();
|
||||||
|
|
||||||
|
my $digest = Mail::SpamAssassin::Pyzor::Digest::get( $msg );
|
||||||
|
|
||||||
|
my $check_ref = $client->check($digest);
|
||||||
|
die $check_ref->{'Diag'} if $check_ref->{'Code'} ne '200';
|
||||||
|
|
||||||
|
my $report_ref = $client->report($digest);
|
||||||
|
die $report_ref->{'Diag'} if $report_ref->{'Code'} ne '200';
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
A bare-bones L<Pyzor|http://pyzor.org> client that currently only
|
||||||
|
implements the functionality needed for L<Mail::SpamAssassin>.
|
||||||
|
|
||||||
|
=head1 PROTOCOL DETAILS
|
||||||
|
|
||||||
|
The Pyzor protocol is not a published standard, and there appears to be
|
||||||
|
no meaningful public documentation. What follows is enough information,
|
||||||
|
largely gleaned through forum posts and reverse engineering, to facilitate
|
||||||
|
effective use of this module:
|
||||||
|
|
||||||
|
Pyzor is an RPC-oriented, message-based protocol. Each message
|
||||||
|
is a simple dictionary of 7-bit ASCII keys and values. Server responses
|
||||||
|
always include at least the following:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item * C<Code> - Similar to HTTP status codes; anything besides C<200>
|
||||||
|
is an error.
|
||||||
|
|
||||||
|
=item * C<Diag> - Similar to HTTP status reasons: a text description
|
||||||
|
of the status.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
(NB: There are additional standard response headers that are useful only for
|
||||||
|
the protocol itself and thus are not part of this module's returns.)
|
||||||
|
|
||||||
|
=head2 Reliability
|
||||||
|
|
||||||
|
Pyzor uses UDP rather than TCP, so no message is guaranteed to reach its
|
||||||
|
destination. A transmission failure can happen in either the request or
|
||||||
|
the response; in either case, a timeout error will result. Such errors
|
||||||
|
are represented as thrown instances of L<Mail::Pyzor::X::Timeout>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
our $VERSION = '0.04';
|
||||||
|
|
||||||
|
our $DEFAULT_SERVER_HOST = 'public.pyzor.org';
|
||||||
|
our $DEFAULT_SERVER_PORT = 24441;
|
||||||
|
our $DEFAULT_USERNAME = 'anonymous';
|
||||||
|
our $DEFAULT_PASSWORD = '';
|
||||||
|
our $DEFAULT_OP_SPEC = '20,3,60,3';
|
||||||
|
our $PYZOR_PROTOCOL_VERSION = 2.1;
|
||||||
|
our $DEFAULT_TIMEOUT = 3.5;
|
||||||
|
our $READ_SIZE = 8192;
|
||||||
|
|
||||||
|
use IO::Socket::INET ();
|
||||||
|
use Digest::SHA qw(sha1 sha1_hex);
|
||||||
|
use Mail::SpamAssassin::Util qw(untaint_var);
|
||||||
|
|
||||||
|
my @hash_order = ( 'Op', 'Op-Digest', 'Op-Spec', 'Thread', 'PV', 'User', 'Time', 'Sig' );
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CONSTRUCTOR
|
||||||
|
|
||||||
|
=head2 new(%OPTS)
|
||||||
|
|
||||||
|
Create a new pyzor client.
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item Input
|
||||||
|
|
||||||
|
%OPTS are (all optional):
|
||||||
|
|
||||||
|
=over 3
|
||||||
|
|
||||||
|
=item * C<server_host> - The pyzor server host to connect to (default is
|
||||||
|
C<public.pyzor.org>)
|
||||||
|
|
||||||
|
=item * C<server_port> - The pyzor server port to connect to (default is
|
||||||
|
24441)
|
||||||
|
|
||||||
|
=item * C<username> - The username to present to the pyzor server (default
|
||||||
|
is C<anonymous>)
|
||||||
|
|
||||||
|
=item * C<password> - The password to present to the pyzor server (default
|
||||||
|
is empty)
|
||||||
|
|
||||||
|
=item * C<timeout> - The maximum time, in seconds, to wait for a response
|
||||||
|
from the pyzor server (defeault is 3.5)
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item Output
|
||||||
|
|
||||||
|
=over 3
|
||||||
|
|
||||||
|
Returns a L<Mail::SpamAssassin::Pyzor::Client> object.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ( $class, %OPTS ) = @_;
|
||||||
|
|
||||||
|
$OPTS{'server_host'} = untaint_var($OPTS{'server_host'});
|
||||||
|
$OPTS{'server_port'} = untaint_var($OPTS{'server_port'});
|
||||||
|
$OPTS{'username'} = untaint_var($OPTS{'username'});
|
||||||
|
$OPTS{'password'} = untaint_var($OPTS{'password'});
|
||||||
|
$OPTS{'timeout'} = untaint_var($OPTS{'timeout'});
|
||||||
|
|
||||||
|
return bless {
|
||||||
|
'server_host' => $OPTS{'server_host'} || $DEFAULT_SERVER_HOST,
|
||||||
|
'server_port' => $OPTS{'server_port'} || $DEFAULT_SERVER_PORT,
|
||||||
|
'username' => $OPTS{'username'} || $DEFAULT_USERNAME,
|
||||||
|
'password' => $OPTS{'password'} || $DEFAULT_PASSWORD,
|
||||||
|
'op_spec' => $DEFAULT_OP_SPEC,
|
||||||
|
'timeout' => $OPTS{'timeout'} || $DEFAULT_TIMEOUT,
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 REQUEST METHODS
|
||||||
|
|
||||||
|
=head2 report($digest)
|
||||||
|
|
||||||
|
Report the digest of a spam message to the pyzor server. This function
|
||||||
|
will throw if a messaging failure or timeout happens.
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item Input
|
||||||
|
|
||||||
|
=over 3
|
||||||
|
|
||||||
|
=item $digest C<SCALAR>
|
||||||
|
|
||||||
|
The message digest to report, as given by
|
||||||
|
C<Mail::SpamAssassin::Pyzor::Digest::get()>.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item Output
|
||||||
|
|
||||||
|
=over 3
|
||||||
|
|
||||||
|
=item C<HASHREF>
|
||||||
|
|
||||||
|
Returns a hashref of the standard attributes noted above.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub report {
|
||||||
|
my ( $self, $digest ) = @_;
|
||||||
|
|
||||||
|
my $msg_ref = $self->_get_base_msg( 'report', $digest );
|
||||||
|
|
||||||
|
$msg_ref->{'Op-Spec'} = $self->{'op_spec'};
|
||||||
|
|
||||||
|
return $self->_send_receive_msg($msg_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 check($digest)
|
||||||
|
|
||||||
|
Check the digest of a message to see if
|
||||||
|
the pyzor server has a report for it. This function
|
||||||
|
will throw if a messaging failure or timeout happens.
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item Input
|
||||||
|
|
||||||
|
=over 3
|
||||||
|
|
||||||
|
=item $digest C<SCALAR>
|
||||||
|
|
||||||
|
The message digest to check, as given by
|
||||||
|
C<Mail::SpamAssassin::Pyzor::Digest::get()>.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item Output
|
||||||
|
|
||||||
|
=over 3
|
||||||
|
|
||||||
|
=item C<HASHREF>
|
||||||
|
|
||||||
|
Returns a hashref of the standard attributes noted above
|
||||||
|
as well as the following:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item * C<Count> - The number of reports the server has received
|
||||||
|
for the given digest.
|
||||||
|
|
||||||
|
=item * C<WL-Count> - The number of whitelist requests the server has received
|
||||||
|
for the given digest.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub check {
|
||||||
|
my ( $self, $digest ) = @_;
|
||||||
|
|
||||||
|
return $self->_send_receive_msg( $self->_get_base_msg( 'check', $digest ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
|
||||||
|
sub _send_receive_msg {
|
||||||
|
my ( $self, $msg_ref ) = @_;
|
||||||
|
|
||||||
|
my $thread_id = $msg_ref->{'Thread'} or warn 'No thread ID?';
|
||||||
|
|
||||||
|
$self->_sign_msg($msg_ref);
|
||||||
|
|
||||||
|
return $self->_do_send_receive(
|
||||||
|
$self->_generate_packet_from_message($msg_ref) . "\n\n",
|
||||||
|
$thread_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _get_base_msg {
|
||||||
|
my ( $self, $op, $digest ) = @_;
|
||||||
|
|
||||||
|
die "Implementor error: op is required" if !$op;
|
||||||
|
die "error: digest is required" if !$digest;
|
||||||
|
|
||||||
|
return {
|
||||||
|
'User' => $self->{'_username'},
|
||||||
|
'PV' => $PYZOR_PROTOCOL_VERSION,
|
||||||
|
'Time' => time(),
|
||||||
|
'Op' => $op,
|
||||||
|
'Op-Digest' => $digest,
|
||||||
|
'Thread' => $self->_generate_thread_id()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _do_send_receive {
|
||||||
|
my ( $self, $packet, $thread_id ) = @_;
|
||||||
|
|
||||||
|
my $sock = $self->_get_connection_or_die();
|
||||||
|
|
||||||
|
$self->_send_packet( $sock, $packet );
|
||||||
|
my $response = $self->_receive_packet( $sock, $thread_id );
|
||||||
|
|
||||||
|
return 0 if not defined $response;
|
||||||
|
|
||||||
|
my $resp_hr = { map { ( split(m{: }) )[ 0, 1 ] } split( m{\n}, $response ) };
|
||||||
|
|
||||||
|
delete $resp_hr->{'Thread'};
|
||||||
|
|
||||||
|
my $response_pv = delete $resp_hr->{'PV'};
|
||||||
|
|
||||||
|
if ( $PYZOR_PROTOCOL_VERSION ne $response_pv ) {
|
||||||
|
warn "Unexpected protocol version ($response_pv) in Pyzor response!";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $resp_hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _receive_packet {
|
||||||
|
my ( $self, $sock, $thread_id ) = @_;
|
||||||
|
|
||||||
|
my $timeout = $self->{'timeout'} * 1000;
|
||||||
|
|
||||||
|
my $end_time = time + $self->{'timeout'};
|
||||||
|
|
||||||
|
$sock->blocking(0);
|
||||||
|
my $response = '';
|
||||||
|
my $rout = '';
|
||||||
|
my $rin = '';
|
||||||
|
vec( $rin, fileno($sock), 1 ) = 1;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
my $time_left = $end_time - time;
|
||||||
|
|
||||||
|
if ( $time_left <= 0 ) {
|
||||||
|
warn("Did not receive a response from the pyzor server $self->{'server_host'}:$self->{'server_port'} for $self->{'timeout'} seconds!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $bytes = sysread( $sock, $response, $READ_SIZE, length $response );
|
||||||
|
if ( !defined($bytes) && !$!{'EAGAIN'} && !$!{'EWOULDBLOCK'} ) {
|
||||||
|
warn "read from socket: $!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( index( $response, "\n\n" ) > -1 ) {
|
||||||
|
|
||||||
|
# Reject the response unless its thread ID matches what we sent.
|
||||||
|
# This prevents confusion among concurrent Pyzor requests.
|
||||||
|
if ( index( $response, "\nThread: $thread_id\n" ) != -1 ) {
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$response = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $found = select( $rout = $rin, undef, undef, $time_left );
|
||||||
|
warn "select(): $!" if $found == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _send_packet {
|
||||||
|
my ( $self, $sock, $packet ) = @_;
|
||||||
|
|
||||||
|
$sock->blocking(1);
|
||||||
|
syswrite( $sock, $packet ) or warn "write to socket: $!";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _get_connection_or_die {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
# clear the socket
|
||||||
|
undef $self->{'_sock_pid'};
|
||||||
|
undef $self->{'_sock'};
|
||||||
|
|
||||||
|
$self->{'_sock_pid'} ||= $$;
|
||||||
|
$self->{'_sock'} ||= IO::Socket::INET->new(
|
||||||
|
'PeerHost' => $self->{'server_host'},
|
||||||
|
'PeerPort' => $self->{'server_port'},
|
||||||
|
'Proto' => 'udp'
|
||||||
|
) or die "Cannot connect to $self->{'server_host'}:$self->{'server_port'}: $@ $!";
|
||||||
|
return $self->{'_sock'};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _sign_msg {
|
||||||
|
my ( $self, $msg_ref ) = @_;
|
||||||
|
|
||||||
|
$msg_ref->{'Sig'} = lc Digest::SHA::sha1_hex(
|
||||||
|
Digest::SHA::sha1( $self->_generate_packet_from_message($msg_ref) )
|
||||||
|
);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _generate_packet_from_message {
|
||||||
|
my ( $self, $msg_ref ) = @_;
|
||||||
|
|
||||||
|
return join( "\n", map { "$_: $msg_ref->{$_}" } grep { length $msg_ref->{$_} } @hash_order );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _generate_thread_id {
|
||||||
|
my $RAND_MAX = 2**16;
|
||||||
|
my $val = 0;
|
||||||
|
$val = int rand($RAND_MAX) while $val < 1024;
|
||||||
|
return $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _get_user_pass_hash_key {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
return lc Digest::SHA::sha1_hex( $self->{'username'} . ':' . $self->{'password'} );
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
99
upstream/lib/Mail/SpamAssassin/Pyzor/Digest.pm
Normal file
99
upstream/lib/Mail/SpamAssassin/Pyzor/Digest.pm
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package Mail::SpamAssassin::Pyzor::Digest;
|
||||||
|
|
||||||
|
# Copyright 2018 cPanel, LLC.
|
||||||
|
# All rights reserved.
|
||||||
|
# http://cpanel.net
|
||||||
|
#
|
||||||
|
# <@LICENSE>
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to you under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at:
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
# </@LICENSE>
|
||||||
|
#
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
=encoding utf-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Mail::SpamAssassin::Pyzor::Digest - Pyzor Digest module
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
my $digest = Mail::SpamAssassin::Pyzor::Digest::get( $mime_text );
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
A reimplementation of L<https://github.com/SpamExperts/pyzor/blob/master/pyzor/digest.py>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
use Mail::SpamAssassin::Pyzor::Digest::Pieces ();
|
||||||
|
use Digest::SHA qw(sha1_hex);
|
||||||
|
|
||||||
|
our $VERSION = '0.03';
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 FUNCTIONS
|
||||||
|
|
||||||
|
=head2 $hex = get( $MSG )
|
||||||
|
|
||||||
|
This takes an email message in raw MIME text format (i.e., as saved in the
|
||||||
|
standard mbox format) and returns the message's Pyzor digest in lower-case
|
||||||
|
hexadecimal.
|
||||||
|
|
||||||
|
The output from this function should normally be identical to that of
|
||||||
|
the C<pyzor> script's C<digest> command. It is suitable for use in
|
||||||
|
L<Mail::SpamAssassin::Pyzor::Client>'s request methods.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get {
|
||||||
|
my ($pms) = @_;
|
||||||
|
return Digest::SHA::sha1_hex( ${ _get_predigest( $pms ) } );
|
||||||
|
}
|
||||||
|
|
||||||
|
# NB: This is called from the test.
|
||||||
|
sub _get_predigest { ## no critic qw(RequireArgUnpacking)
|
||||||
|
my ($pms) = @_;
|
||||||
|
|
||||||
|
my $parsed = $pms->get_message();
|
||||||
|
|
||||||
|
my @lines;
|
||||||
|
|
||||||
|
my $payloads_ar = Mail::SpamAssassin::Pyzor::Digest::Pieces::digest_payloads($parsed);
|
||||||
|
for my $payload (@$payloads_ar) {
|
||||||
|
my @p_lines = Mail::SpamAssassin::Pyzor::Digest::Pieces::splitlines($payload);
|
||||||
|
for my $line (@p_lines) {
|
||||||
|
Mail::SpamAssassin::Pyzor::Digest::Pieces::normalize($line);
|
||||||
|
|
||||||
|
next if !Mail::SpamAssassin::Pyzor::Digest::Pieces::should_handle_line($line);
|
||||||
|
|
||||||
|
# Make sure we have an octet string.
|
||||||
|
utf8::encode($line) if utf8::is_utf8($line);
|
||||||
|
|
||||||
|
push @lines, $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $digest_sr = Mail::SpamAssassin::Pyzor::Digest::Pieces::assemble_lines( \@lines );
|
||||||
|
return $digest_sr;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
311
upstream/lib/Mail/SpamAssassin/Pyzor/Digest/Pieces.pm
Normal file
311
upstream/lib/Mail/SpamAssassin/Pyzor/Digest/Pieces.pm
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
package Mail::SpamAssassin::Pyzor::Digest::Pieces;
|
||||||
|
|
||||||
|
# Copyright 2018 cPanel, LLC.
|
||||||
|
# All rights reserved.
|
||||||
|
# http://cpanel.net
|
||||||
|
#
|
||||||
|
# <@LICENSE>
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to you under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at:
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
# </@LICENSE>
|
||||||
|
#
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
=encoding utf-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Mail::SpamAssassin::Pyzor::Digest::Pieces - Pyzor backend logic module
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This module houses backend logic for L<Mail::SpamAssassin::Pyzor::Digest>.
|
||||||
|
|
||||||
|
It reimplements logic found in pyzor's F<digest.py> module
|
||||||
|
(L<https://github.com/SpamExperts/pyzor/blob/master/pyzor/digest.py>).
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
use Encode ();
|
||||||
|
|
||||||
|
our $VERSION = '0.03';
|
||||||
|
|
||||||
|
# each tuple is [ offset, length ]
|
||||||
|
use constant _HASH_SPEC => ( [ 20, 3 ], [ 60, 3 ] );
|
||||||
|
|
||||||
|
use constant {
|
||||||
|
_MIN_LINE_LENGTH => 8,
|
||||||
|
|
||||||
|
_ATOMIC_NUM_LINES => 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 FUNCTIONS
|
||||||
|
|
||||||
|
=head2 $strings_ar = digest_payloads( $EMAIL_MIME )
|
||||||
|
|
||||||
|
This imitates the corresponding object method in F<digest.py>.
|
||||||
|
It returns a reference to an array of strings. Each string can be either
|
||||||
|
a byte string or a character string (e.g., UTF-8 decoded).
|
||||||
|
|
||||||
|
NB: RFC 2822 stipulates that message bodies should use CRLF
|
||||||
|
line breaks, not plain LF (nor plain CR).
|
||||||
|
We will thus convert any plain CRs in a quoted-printable message
|
||||||
|
body into CRLF. Python, though, doesn't do this, so the output of
|
||||||
|
our implementation of C<digest_payloads()> diverges from that of the Python
|
||||||
|
original. It doesn't ultimately make a difference since the line-ending
|
||||||
|
whitespace gets trimmed regardless, but it's necessary to factor in when
|
||||||
|
comparing the output of our implementation with the Python output.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub digest_payloads {
|
||||||
|
my ($parsed) = @_;
|
||||||
|
|
||||||
|
my @subparts;
|
||||||
|
|
||||||
|
foreach my $part ($parsed->find_parts(qr/./, 1)) {
|
||||||
|
push(@subparts, $part);
|
||||||
|
}
|
||||||
|
my @payloads;
|
||||||
|
|
||||||
|
foreach my $p (@subparts) {
|
||||||
|
my ( $main_type, $subtype, $encoding, $encode_check ) = parse_content_type( $p->{'type'} );
|
||||||
|
|
||||||
|
my $payload;
|
||||||
|
|
||||||
|
if ( $main_type eq 'text' ) {
|
||||||
|
|
||||||
|
if ( $subtype eq 'plain' ) {
|
||||||
|
$payload = $p->{'decoded'};
|
||||||
|
$payload =~ s/\\'/\'/gx;
|
||||||
|
} else {
|
||||||
|
$payload = $p->{'rendered'};
|
||||||
|
}
|
||||||
|
|
||||||
|
utf8::upgrade($payload) if defined $payload;
|
||||||
|
|
||||||
|
if ( $subtype eq 'html' ) {
|
||||||
|
require Mail::SpamAssassin::Pyzor::Digest::StripHtml;
|
||||||
|
$payload = Mail::SpamAssassin::Pyzor::Digest::StripHtml::strip($payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# This does no decoding, even of, e.g., quoted-printable or base64.
|
||||||
|
$payload = $p->{'pristine_body'};
|
||||||
|
}
|
||||||
|
next if not defined $payload;
|
||||||
|
push @payloads, $payload;
|
||||||
|
}
|
||||||
|
return \@payloads;
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 normalize( $STRING )
|
||||||
|
|
||||||
|
This imitates the corresponding object method in F<digest.py>.
|
||||||
|
It modifies C<$STRING> in-place.
|
||||||
|
|
||||||
|
As with the original implementation, if C<$STRING> contains (decoded)
|
||||||
|
Unicode characters, those characters will be parsed accordingly. So:
|
||||||
|
|
||||||
|
$str = "123\xc2\xa0"; # [ c2 a0 ] == \u00a0, non-breaking space
|
||||||
|
|
||||||
|
normalize($str);
|
||||||
|
|
||||||
|
The above will leave C<$str> alone, but this:
|
||||||
|
|
||||||
|
utf8::decode($str);
|
||||||
|
|
||||||
|
normalize($str);
|
||||||
|
|
||||||
|
... will trim off the last two bytes from C<$str>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub normalize { ## no critic qw( Subroutines::RequireArgUnpacking )
|
||||||
|
|
||||||
|
# NULs are bad, mm-kay?
|
||||||
|
$_[0] =~ tr<\0><>d;
|
||||||
|
|
||||||
|
# NB: Python's \s without re.UNICODE is the same as Perl's \s
|
||||||
|
# with the /a modifier.
|
||||||
|
#
|
||||||
|
# https://docs.python.org/2/library/re.html
|
||||||
|
# https://perldoc.perl.org/perlrecharclass.html#Backslash-sequences
|
||||||
|
|
||||||
|
# Python: re.compile(r'\S{10,}')
|
||||||
|
$_[0] =~ s<\S{10,}><>ag;
|
||||||
|
|
||||||
|
# Python: re.compile(r'\S+@\S+')
|
||||||
|
$_[0] =~ s<\S+ @ \S+><>agx;
|
||||||
|
|
||||||
|
# Python: re.compile(r'[a-z]+:\S+', re.IGNORECASE)
|
||||||
|
$_[0] =~ s<[a-zA-Z]+ : \S+><>agx;
|
||||||
|
|
||||||
|
# (from digest.py ...)
|
||||||
|
# Make sure we do the whitespace last because some of the previous
|
||||||
|
# patterns rely on whitespace.
|
||||||
|
$_[0] =~ tr< \x09-\x0d><>d;
|
||||||
|
|
||||||
|
# This is fun. digest.py's normalize() does a non-UNICODE whitespace
|
||||||
|
# strip, then calls strip() on the string, which *will* strip Unicode
|
||||||
|
# whitespace from the ends.
|
||||||
|
$_[0] =~ s<\A\s+><>;
|
||||||
|
$_[0] =~ s<\s+\z><>;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 $yn = should_handle_line( $STRING )
|
||||||
|
|
||||||
|
This imitates the corresponding object method in F<digest.py>.
|
||||||
|
It returns a boolean.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub should_handle_line {
|
||||||
|
return $_[0] && length( $_[0] ) >= _MIN_LINE_LENGTH();
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 $sr = assemble_lines( \@LINES )
|
||||||
|
|
||||||
|
This assembles a string buffer out of @LINES. The string is the buffer
|
||||||
|
of octets that will be hashed to produce the message digest.
|
||||||
|
|
||||||
|
Each member of @LINES is expected to be an B<octet string>, not a
|
||||||
|
character string.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub assemble_lines {
|
||||||
|
my ($lines_ar) = @_;
|
||||||
|
|
||||||
|
if ( @$lines_ar <= _ATOMIC_NUM_LINES() ) {
|
||||||
|
|
||||||
|
# cf. handle_atomic() in digest.py
|
||||||
|
return \join( q<>, @$lines_ar );
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
# cf. handle_atomic() in digest.py
|
||||||
|
|
||||||
|
my $str = q<>;
|
||||||
|
|
||||||
|
for my $ofs_len ( _HASH_SPEC() ) {
|
||||||
|
my ( $offset, $length ) = @$ofs_len;
|
||||||
|
|
||||||
|
for my $i ( 0 .. ( $length - 1 ) ) {
|
||||||
|
my $idx = int( $offset * @$lines_ar / 100 ) + $i;
|
||||||
|
|
||||||
|
next if !defined $lines_ar->[$idx];
|
||||||
|
|
||||||
|
$str .= $lines_ar->[$idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return \$str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 ($main, $sub, $encoding, $checkval) = parse_content_type( $CONTENT_TYPE )
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
use constant _QUOTED_PRINTABLE_NAMES => (
|
||||||
|
"quopri-codec",
|
||||||
|
"quopri",
|
||||||
|
"quoted-printable",
|
||||||
|
"quotedprintable",
|
||||||
|
);
|
||||||
|
|
||||||
|
# Make Encode::decode() ignore anything that doesn't fit the
|
||||||
|
# given encoding.
|
||||||
|
use constant _encode_check_ignore => q<>;
|
||||||
|
|
||||||
|
sub parse_content_type {
|
||||||
|
my ($content_type) = @_;
|
||||||
|
|
||||||
|
# text/plain; charset=us-ascii
|
||||||
|
my $ct_parse;
|
||||||
|
if($content_type =~ /(\w+)\/(\w+); charset=(.*)/) {
|
||||||
|
$ct_parse->{type} = $1;
|
||||||
|
$ct_parse->{subtype} = $2;
|
||||||
|
$ct_parse->{'attributes'}{'charset'} = $3;
|
||||||
|
} elsif($content_type =~ /(\w+)\/(\w+)/) {
|
||||||
|
$ct_parse->{type} = $1;
|
||||||
|
$ct_parse->{subtype} = $2;
|
||||||
|
$ct_parse->{'attributes'}{'charset'} = 'us-ascii';
|
||||||
|
} else {
|
||||||
|
$ct_parse->{type} = 'text';
|
||||||
|
$ct_parse->{subtype} = 'plain';
|
||||||
|
$ct_parse->{'attributes'}{'charset'} = 'us-ascii';
|
||||||
|
}
|
||||||
|
|
||||||
|
my $main = $ct_parse->{'type'} || q<>;
|
||||||
|
my $sub = $ct_parse->{'subtype'} || q<>;
|
||||||
|
|
||||||
|
my $encoding = $ct_parse->{'attributes'}{'charset'};
|
||||||
|
|
||||||
|
my $checkval;
|
||||||
|
|
||||||
|
if ($encoding) {
|
||||||
|
|
||||||
|
# Lower-case everything, convert underscore to dash, and remove NUL.
|
||||||
|
$encoding =~ tr<A-Z_\0><a-z->d;
|
||||||
|
|
||||||
|
# Apparently pyzor accommodates messages that put the transfer
|
||||||
|
# encoding in the Content-Type.
|
||||||
|
if ( grep { $_ eq $encoding } _QUOTED_PRINTABLE_NAMES() ) {
|
||||||
|
$checkval = Encode::FB_CROAK();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$encoding = 'ascii';
|
||||||
|
}
|
||||||
|
|
||||||
|
# Match Python .decode()'s 'ignore' behavior
|
||||||
|
$checkval ||= \&_encode_check_ignore;
|
||||||
|
|
||||||
|
return ( $main, $sub, $encoding, $checkval );
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 @lines = splitlines( $TEXT )
|
||||||
|
|
||||||
|
Imitates C<str.splitlines()>. (cf. C<pydoc str>)
|
||||||
|
|
||||||
|
Returns a plain list in list context. Returns the number of
|
||||||
|
items to be returned in scalar context.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub splitlines {
|
||||||
|
return split m<\r\n?|\n>, $_[0] if defined $_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
177
upstream/lib/Mail/SpamAssassin/Pyzor/Digest/StripHtml.pm
Normal file
177
upstream/lib/Mail/SpamAssassin/Pyzor/Digest/StripHtml.pm
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
package Mail::SpamAssassin::Pyzor::Digest::StripHtml;
|
||||||
|
|
||||||
|
# Copyright 2018 cPanel, LLC.
|
||||||
|
# All rights reserved.
|
||||||
|
# http://cpanel.net
|
||||||
|
#
|
||||||
|
# <@LICENSE>
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to you under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at:
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
# </@LICENSE>
|
||||||
|
#
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
=encoding utf-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Mail::SpamAssassin::Pyzor::Digest::StripHtml - Pyzor HTML-stripping module
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
my $stripped = Mail::SpamAssassin::Pyzor::Digest::StripHtml::strip($html);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This module attempts to duplicate pyzor's HTML-stripping logic.
|
||||||
|
|
||||||
|
=head1 ACCURACY
|
||||||
|
|
||||||
|
This library cannot achieve 100%, bug-for-bug parity with pyzor
|
||||||
|
because to do so would require duplicating Python's own HTML parsing
|
||||||
|
library. Since that library's output has changed over time, and those
|
||||||
|
changes in turn affect pyzor, it's literally impossible to arrive at
|
||||||
|
a single, fully-compatible reimplementation.
|
||||||
|
|
||||||
|
That said, all known divergences between pyzor and this library involve
|
||||||
|
invalid HTML as input.
|
||||||
|
|
||||||
|
Please open bug reports for any divergences you identify, particularly
|
||||||
|
if the input is valid HTML.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
use HTML::Parser ();
|
||||||
|
|
||||||
|
our $VERSION = '0.03';
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 FUNCTIONS
|
||||||
|
|
||||||
|
=head2 $stripped = strip( $HTML )
|
||||||
|
|
||||||
|
Give it some HTML, and it'll give back the stripped text.
|
||||||
|
|
||||||
|
In B<general>, the stripping consists of removing tags as well as
|
||||||
|
C<E<lt>scriptE<gt>> and C<E<lt>styleE<gt>> elements; however, it also
|
||||||
|
removes HTML entities.
|
||||||
|
|
||||||
|
This tries very hard to duplicate pyzor's behavior with invalid HTML.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub strip {
|
||||||
|
my ($html) = @_;
|
||||||
|
|
||||||
|
$html =~ s<\A\s+><>;
|
||||||
|
$html =~ s<\s+\z><>;
|
||||||
|
|
||||||
|
my $p = HTML::Parser->new( api_version => 3 );
|
||||||
|
|
||||||
|
my @pieces;
|
||||||
|
|
||||||
|
my $accumulate = 1;
|
||||||
|
|
||||||
|
$p->handler(
|
||||||
|
start => sub {
|
||||||
|
my ($tagname) = @_;
|
||||||
|
|
||||||
|
$accumulate = 0 if $tagname eq 'script';
|
||||||
|
$accumulate = 0 if $tagname eq 'style';
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
'tagname',
|
||||||
|
);
|
||||||
|
|
||||||
|
$p->handler(
|
||||||
|
end => sub {
|
||||||
|
$accumulate = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$p->handler(
|
||||||
|
text => sub {
|
||||||
|
my ($copy) = @_;
|
||||||
|
|
||||||
|
return if !$accumulate;
|
||||||
|
|
||||||
|
# pyzor's HTML parser discards HTML entities. On top of that,
|
||||||
|
# we need to match, as closely as possible, pyzor's handling of
|
||||||
|
# invalid HTML entities ... which is a function of Python's
|
||||||
|
# standard HTML parsing library. This will probably never be
|
||||||
|
# fully compatible with the pyzor, but we can get it close.
|
||||||
|
|
||||||
|
# The original is:
|
||||||
|
#
|
||||||
|
# re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]')
|
||||||
|
#
|
||||||
|
# The parsing loop then "backs up" one byte if the last
|
||||||
|
# character isn't a ";". We use a look-ahead assertion to
|
||||||
|
# mimic that behavior.
|
||||||
|
$copy =~ s<\&\# (?:[0-9]+ | [xX][0-9a-fA-F]+) (?: ; | \z | (?=[^0-9a-fA-F]) )>< >gx;
|
||||||
|
|
||||||
|
# The original is:
|
||||||
|
#
|
||||||
|
# re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]')
|
||||||
|
#
|
||||||
|
# We again use a look-ahead assertion to mimic Python.
|
||||||
|
$copy =~ s<\& [a-zA-Z] [-.a-zA-Z0-9]* (?: ; | \z | (?=[^a-zA-Z0-9]) )>< >gx;
|
||||||
|
|
||||||
|
# Python's HTMLParser aborts its parsing loop when it encounters
|
||||||
|
# an invalid numeric reference.
|
||||||
|
$copy =~ s<\&\#
|
||||||
|
(?:
|
||||||
|
[^0-9xX] # anything but the expected first char
|
||||||
|
|
|
||||||
|
[0-9]+[a-fA-F] # hex within decimal
|
||||||
|
|
|
||||||
|
[xX][^0-9a-fA-F]
|
||||||
|
)
|
||||||
|
(.*)
|
||||||
|
><
|
||||||
|
( -1 == index($1, ';') ) ? q<> : '&#'
|
||||||
|
>exs;
|
||||||
|
|
||||||
|
# Python's HTMLParser treats invalid entities as incomplete
|
||||||
|
$copy =~ s<(\&\#?)><$1 >gx;
|
||||||
|
|
||||||
|
$copy =~ s<\A\s+><>;
|
||||||
|
$copy =~ s<\s+\z><>;
|
||||||
|
|
||||||
|
push @pieces, \$copy if length $copy;
|
||||||
|
},
|
||||||
|
'text,tagname',
|
||||||
|
);
|
||||||
|
|
||||||
|
$p->parse($html);
|
||||||
|
$p->eof();
|
||||||
|
|
||||||
|
my $payload = join( q< >, map { $$_ } @pieces );
|
||||||
|
|
||||||
|
# Convert all sequences of whitespace OTHER THAN non-breaking spaces to
|
||||||
|
# plain spaces.
|
||||||
|
$payload =~ s<[^\S\x{a0}]+>< >g;
|
||||||
|
|
||||||
|
return $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -201,7 +201,37 @@ sub get_addr_entry {
|
|||||||
|
|
||||||
return $entry unless $email ne '' && (defined $ip || defined $signedby);
|
return $entry unless $email ne '' && (defined $ip || defined $signedby);
|
||||||
|
|
||||||
my $sql = "SELECT msgcount, totscore FROM $self->{tablename} " .
|
my $sql;
|
||||||
|
my $sth;
|
||||||
|
my $rc;
|
||||||
|
if($self->{main}->{conf}->{txrep_welcomelist_out} and ($email =~ /(?:[^\s\@]+)\@(?:[^\s\@]+)/)) {
|
||||||
|
$sql = "SELECT msgcount, totscore FROM $self->{tablename} " .
|
||||||
|
"WHERE username = ? AND email = ? AND ip = 'WELCOMELIST_OUT'";
|
||||||
|
$sth = $self->{dbh}->prepare($sql);
|
||||||
|
unless (defined($sth)) {
|
||||||
|
info("auto-welcomelist: sql-based get_addr_entry %s: SQL prepare error: %s",
|
||||||
|
"$self->{_username}|$ip", $self->{dbh}->errstr);
|
||||||
|
}
|
||||||
|
$rc = $sth->execute($self->{_username}, $email);
|
||||||
|
my $cnt = 0;
|
||||||
|
my $aryref;
|
||||||
|
# how to combine data if there are several entries (like signed by
|
||||||
|
# an author domain and by a remailer)? for now just take an average
|
||||||
|
while ( defined($aryref = $sth->fetchrow_arrayref()) ) {
|
||||||
|
if (defined $entry->{msgcount} && defined $aryref->[1]) {
|
||||||
|
$entry->{msgcount} = $aryref->[0];
|
||||||
|
$entry->{totscore} = $aryref->[1];
|
||||||
|
}
|
||||||
|
$entry->{exists_p} = 1;
|
||||||
|
$cnt++;
|
||||||
|
}
|
||||||
|
$sth->finish();
|
||||||
|
return $entry if $rc;
|
||||||
|
}
|
||||||
|
undef $sth;
|
||||||
|
undef $rc;
|
||||||
|
|
||||||
|
$sql = "SELECT msgcount, totscore FROM $self->{tablename} " .
|
||||||
"WHERE username = ? AND email = ?";
|
"WHERE username = ? AND email = ?";
|
||||||
my @args = ( $email );
|
my @args = ( $email );
|
||||||
if (!$self->{_with_awl_signer}) {
|
if (!$self->{_with_awl_signer}) {
|
||||||
@ -221,7 +251,7 @@ sub get_addr_entry {
|
|||||||
}
|
}
|
||||||
$sql .= " ORDER BY last_hit";
|
$sql .= " ORDER BY last_hit";
|
||||||
|
|
||||||
my $sth = $self->{dbh}->prepare($sql);
|
$sth = $self->{dbh}->prepare($sql);
|
||||||
|
|
||||||
unless (defined($sth)) {
|
unless (defined($sth)) {
|
||||||
info("auto-welcomelist: sql-based get_addr_entry %s: SQL prepare error: %s",
|
info("auto-welcomelist: sql-based get_addr_entry %s: SQL prepare error: %s",
|
||||||
@ -229,7 +259,7 @@ sub get_addr_entry {
|
|||||||
return $entry;
|
return $entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $rc = $sth->execute($self->{_username}, @args);
|
$rc = $sth->execute($self->{_username}, @args);
|
||||||
|
|
||||||
if (!$rc) { # there was an error, but try to go on
|
if (!$rc) { # there was an error, but try to go on
|
||||||
info("auto-welcomelist: sql-based get_addr_entry %s: SQL error: %s",
|
info("auto-welcomelist: sql-based get_addr_entry %s: SQL error: %s",
|
||||||
@ -298,7 +328,7 @@ sub add_score {
|
|||||||
|
|
||||||
{ my @fields = qw(username email ip msgcount totscore);
|
{ my @fields = qw(username email ip msgcount totscore);
|
||||||
my @signedby;
|
my @signedby;
|
||||||
if ($self->{_with_awl_signer}) {
|
if ($self->{_with_awl_signer} or (defined $signedby and $signedby =~ /^spf\-/) and not ($self->{main}->{conf}->{txrep_welcomelist_out} and ($email =~ /(?:[^\s\@]+)\@(?:[^\s\@]+)/)) ) {
|
||||||
push(@fields, 'signedby');
|
push(@fields, 'signedby');
|
||||||
@signedby = !defined $signedby ? () : split(' ', lc $signedby);
|
@signedby = !defined $signedby ? () : split(' ', lc $signedby);
|
||||||
@signedby = ( '' ) if !@signedby;
|
@signedby = ( '' ) if !@signedby;
|
||||||
@ -307,7 +337,7 @@ sub add_score {
|
|||||||
my $sql = sprintf("INSERT INTO %s (%s) VALUES (%s)", $self->{tablename},
|
my $sql = sprintf("INSERT INTO %s (%s) VALUES (%s)", $self->{tablename},
|
||||||
join(',', @fields), join(',', ('?') x @fields));
|
join(',', @fields), join(',', ('?') x @fields));
|
||||||
if ($self->{dsn} =~ /^DBI:(?:pg|SQLite)/i) {
|
if ($self->{dsn} =~ /^DBI:(?:pg|SQLite)/i) {
|
||||||
$sql .= " ON CONFLICT (username, email, signedby, ip) DO UPDATE set msgcount = ?, totscore = totscore + ?";
|
$sql .= " ON CONFLICT (username, email, signedby, ip) DO UPDATE set msgcount = ?, totscore = " . $self->{tablename} . ".totscore + ?";
|
||||||
} elsif ($self->{dsn} =~ /^DBI:(?:mysql|MariaDB)/i) {
|
} elsif ($self->{dsn} =~ /^DBI:(?:mysql|MariaDB)/i) {
|
||||||
$sql .= " ON DUPLICATE KEY UPDATE msgcount = ?, totscore = totscore + ?";
|
$sql .= " ON DUPLICATE KEY UPDATE msgcount = ?, totscore = totscore + ?";
|
||||||
}
|
}
|
||||||
@ -320,7 +350,7 @@ sub add_score {
|
|||||||
return $entry;
|
return $entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$self->{_with_awl_signer}) {
|
if (!$self->{_with_awl_signer} && !(defined $signedby and $signedby =~ /^spf\-/)) {
|
||||||
my $rc;
|
my $rc;
|
||||||
if ($self->{dsn} =~ /^DBI:(?:pg|SQLite|mysql|MariaDB)/i) {
|
if ($self->{dsn} =~ /^DBI:(?:pg|SQLite|mysql|MariaDB)/i) {
|
||||||
$rc = $sth->execute(@args, $entry->{msgcount}, $score);
|
$rc = $sth->execute(@args, $entry->{msgcount}, $score);
|
||||||
|
@ -65,18 +65,18 @@ our @ISA = qw();
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item my $t = Mail::SpamAssassin::Timeout->new({ ... options ... });
|
=item my $t = Mail::SpamAssassin::Timeout-E<gt>new({ ... options ... });
|
||||||
|
|
||||||
Constructor. Options include:
|
Constructor. Options include:
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item secs => $seconds
|
=item secs =E<gt> $seconds
|
||||||
|
|
||||||
time interval, in seconds. Optional; if neither C<secs> nor C<deadline> is
|
time interval, in seconds. Optional; if neither C<secs> nor C<deadline> is
|
||||||
specified, no timeouts will be applied.
|
specified, no timeouts will be applied.
|
||||||
|
|
||||||
=item deadline => $unix_timestamp
|
=item deadline =E<gt> $unix_timestamp
|
||||||
|
|
||||||
Unix timestamp (seconds since epoch) when a timeout is reached in the latest.
|
Unix timestamp (seconds since epoch) when a timeout is reached in the latest.
|
||||||
Optional; if neither B<secs> nor B<deadline> is specified, no timeouts will
|
Optional; if neither B<secs> nor B<deadline> is specified, no timeouts will
|
||||||
@ -108,7 +108,7 @@ sub new {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $t->run($coderef)
|
=item $t-E<gt>run($coderef)
|
||||||
|
|
||||||
Run a code reference within the currently-defined timeout.
|
Run a code reference within the currently-defined timeout.
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ The timeout is as defined by the B<secs> and B<deadline> parameters
|
|||||||
to the constructor.
|
to the constructor.
|
||||||
|
|
||||||
Returns whatever the subroutine returns, or C<undef> on timeout.
|
Returns whatever the subroutine returns, or C<undef> on timeout.
|
||||||
If the timer times out, C<$t-<gt>timed_out()> will return C<1>.
|
If the timer times out, C<$t-E<gt>timed_out()> will return C<1>.
|
||||||
|
|
||||||
Time elapsed is not cumulative; multiple runs of C<run> will restart the
|
Time elapsed is not cumulative; multiple runs of C<run> will restart the
|
||||||
timeout from scratch. On the other hand, nested timers do observe outer
|
timeout from scratch. On the other hand, nested timers do observe outer
|
||||||
@ -125,9 +125,9 @@ established them, i.e. code running under an inner timer can not exceed
|
|||||||
the time limit established by an outer timer. When restarting an outer
|
the time limit established by an outer timer. When restarting an outer
|
||||||
timer on return, elapsed time of a running code is taken into account.
|
timer on return, elapsed time of a running code is taken into account.
|
||||||
|
|
||||||
=item $t->run_and_catch($coderef)
|
=item $t-E<gt>run_and_catch($coderef)
|
||||||
|
|
||||||
Run a code reference, as per C<$t-<gt>run()>, but also catching any
|
Run a code reference, as per C<$t-E<gt>run()>, but also catching any
|
||||||
C<die()> calls within the code reference.
|
C<die()> calls within the code reference.
|
||||||
|
|
||||||
Returns C<undef> if no C<die()> call was executed and C<$@> was unset, or the
|
Returns C<undef> if no C<die()> call was executed and C<$@> was unset, or the
|
||||||
@ -291,7 +291,7 @@ sub _run { # private
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $t->timed_out()
|
=item $t-E<gt>timed_out()
|
||||||
|
|
||||||
Returns C<1> if the most recent code executed in C<run()> timed out, or
|
Returns C<1> if the most recent code executed in C<run()> timed out, or
|
||||||
C<undef> if it did not.
|
C<undef> if it did not.
|
||||||
@ -305,7 +305,7 @@ sub timed_out {
|
|||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=item $t->reset()
|
=item $t-E<gt>reset()
|
||||||
|
|
||||||
If called within a C<run()> code reference, causes the current alarm timer
|
If called within a C<run()> code reference, causes the current alarm timer
|
||||||
to be restored to its original setting (useful after our alarm setting was
|
to be restored to its original setting (useful after our alarm setting was
|
||||||
|
@ -60,7 +60,7 @@ our @EXPORT_OK = qw(&local_tz &base64_decode &base64_encode &base32_encode
|
|||||||
&parse_rfc822_date &idn_to_ascii &is_valid_utf_8
|
&parse_rfc822_date &idn_to_ascii &is_valid_utf_8
|
||||||
&get_user_groups &compile_regexp &qr_to_string
|
&get_user_groups &compile_regexp &qr_to_string
|
||||||
&is_fqdn_valid &parse_header_addresses &force_die
|
&is_fqdn_valid &parse_header_addresses &force_die
|
||||||
&domain_to_search_list);
|
&domain_to_search_list &get_part_details);
|
||||||
|
|
||||||
our $AM_TAINTED;
|
our $AM_TAINTED;
|
||||||
|
|
||||||
@ -1392,7 +1392,7 @@ Touch or create a file.
|
|||||||
|
|
||||||
Possible args:
|
Possible args:
|
||||||
|
|
||||||
create_exclusive => 1
|
create_exclusive =E<gt> 1
|
||||||
Create a new empty file safely, only if not existing before
|
Create a new empty file safely, only if not existing before
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
@ -1936,10 +1936,32 @@ sub setuid_to_euid {
|
|||||||
|
|
||||||
# helper app command-line open
|
# helper app command-line open
|
||||||
sub helper_app_pipe_open {
|
sub helper_app_pipe_open {
|
||||||
|
|
||||||
|
my @cmdline;
|
||||||
|
my $startquote = 0;
|
||||||
|
my $ntok;
|
||||||
|
foreach my $tok ( @_ ) {
|
||||||
|
if(defined $tok && ($tok =~ /^\"/) && ($tok !~ /\"$/)) {
|
||||||
|
$startquote = 1;
|
||||||
|
}
|
||||||
|
if($startquote) {
|
||||||
|
$ntok .= " " if($tok !~ /^\"/);
|
||||||
|
$ntok =~ s/\"// if defined $ntok;
|
||||||
|
$ntok .= $tok;
|
||||||
|
}
|
||||||
|
if($startquote && defined $tok && ($tok =~ /\"$/)) {
|
||||||
|
$startquote = 0;
|
||||||
|
$ntok =~ s/\"// if defined $ntok;
|
||||||
|
push(@cmdline, $ntok);
|
||||||
|
undef $ntok;
|
||||||
|
} elsif(not $startquote) {
|
||||||
|
push(@cmdline, $tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (RUNNING_ON_WINDOWS) {
|
if (RUNNING_ON_WINDOWS) {
|
||||||
return helper_app_pipe_open_windows (@_);
|
return helper_app_pipe_open_windows (@cmdline);
|
||||||
} else {
|
} else {
|
||||||
return helper_app_pipe_open_unix (@_);
|
return helper_app_pipe_open_unix (@cmdline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2557,6 +2579,82 @@ sub parse_header_addresses {
|
|||||||
return @results;
|
return @results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_part_details {
|
||||||
|
my ($pms, $part, $prefer_contentdisposition) = @_;
|
||||||
|
#https://en.wikipedia.org/wiki/MIME#Content-Disposition
|
||||||
|
#https://github.com/mikel/mail/pull/464
|
||||||
|
|
||||||
|
my $ctt = $part->get_header('content-type');
|
||||||
|
return undef unless defined $ctt; ## no critic (ProhibitExplicitReturnUndef)
|
||||||
|
|
||||||
|
my $cte = lc($part->get_header('content-transfer-encoding') || '');
|
||||||
|
return undef unless ($cte =~ /^(?:base64|quoted\-printable)$/); ## no critic (ProhibitExplicitReturnUndef)
|
||||||
|
|
||||||
|
$ctt = _decode_part_header($part, $ctt || '');
|
||||||
|
|
||||||
|
my $name = '';
|
||||||
|
my $cttname = '';
|
||||||
|
my $ctdname = '';
|
||||||
|
|
||||||
|
if ($ctt =~ m/name\s*=\s*["']?([^"';]*)/is) {
|
||||||
|
$cttname = $1;
|
||||||
|
$cttname =~ s/\s+$//;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $ctd = $part->get_header('content-disposition');
|
||||||
|
$ctd = _decode_part_header($part, $ctd || '');
|
||||||
|
|
||||||
|
if ($ctd =~ m/filename\s*=\s*["']?([^"';]*)/is) {
|
||||||
|
$ctdname = $1;
|
||||||
|
$ctdname =~ s/\s+$//;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lc $ctdname eq lc $cttname) {
|
||||||
|
$name = $ctdname;
|
||||||
|
} elsif ($ctdname eq '') {
|
||||||
|
$name = $cttname;
|
||||||
|
} elsif ($cttname eq '') {
|
||||||
|
$name = $ctdname;
|
||||||
|
} else {
|
||||||
|
if ((defined $ctdname) and $prefer_contentdisposition) {
|
||||||
|
$name = $ctdname;
|
||||||
|
} else {
|
||||||
|
$name = $cttname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ctt, $ctd, $cte, $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _decode_part_header {
|
||||||
|
my($part, $header_field_body) = @_;
|
||||||
|
|
||||||
|
return '' unless defined $header_field_body && $header_field_body ne '';
|
||||||
|
|
||||||
|
# deal with folding and cream the newlines and such
|
||||||
|
$header_field_body =~ s/\n[ \t]+/\n /g;
|
||||||
|
$header_field_body =~ s/\015?\012//gs;
|
||||||
|
|
||||||
|
local($1,$2,$3);
|
||||||
|
|
||||||
|
# Multiple encoded sections must ignore the interim whitespace.
|
||||||
|
# To avoid possible FPs with (\s+(?==\?))?, look for the whole RE
|
||||||
|
# separated by whitespace.
|
||||||
|
1 while $header_field_body =~
|
||||||
|
s{ ( = \? [A-Za-z0-9_-]+ \? [bqBQ] \? [^?]* \? = ) \s+
|
||||||
|
( = \? [A-Za-z0-9_-]+ \? [bqBQ] \? [^?]* \? = ) }
|
||||||
|
{$1$2}xsg;
|
||||||
|
|
||||||
|
# transcode properly encoded RFC 2047 substrings into UTF-8 octets,
|
||||||
|
# leave everything else unchanged as it is supposed to be UTF-8 (RFC 6532)
|
||||||
|
# or plain US-ASCII
|
||||||
|
$header_field_body =~
|
||||||
|
s{ (?: = \? ([A-Za-z0-9_-]+) \? ([bqBQ]) \? ([^?]*) \? = ) }
|
||||||
|
{ $part->__decode_header($1, uc($2), $3) }xsge;
|
||||||
|
|
||||||
|
return $header_field_body;
|
||||||
|
}
|
||||||
|
|
||||||
# Check some basic parsing mistakes
|
# Check some basic parsing mistakes
|
||||||
sub _valid_parsed_address {
|
sub _valid_parsed_address {
|
||||||
return 0 if !defined $_[0];
|
return 0 if !defined $_[0];
|
||||||
|
@ -53,7 +53,7 @@ our @MODULES = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
module => 'Net::DNS',
|
module => 'Net::DNS',
|
||||||
version => '0.69',
|
version => '1.10',
|
||||||
desc => 'Used for all DNS-based tests (SBL, XBL, SpamCop, DSBL, etc.),
|
desc => 'Used for all DNS-based tests (SBL, XBL, SpamCop, DSBL, etc.),
|
||||||
perform MX checks, and is also used when manually reporting spam to
|
perform MX checks, and is also used when manually reporting spam to
|
||||||
SpamCop.',
|
SpamCop.',
|
||||||
@ -251,6 +251,12 @@ our @OPTIONAL_MODULES = (
|
|||||||
backend for the DBI module, this is the DBD driver required. Version 1.59_01
|
backend for the DBI module, this is the DBD driver required. Version 1.59_01
|
||||||
or later is needed to provide SQLite 3.25.0 or later.',
|
or later is needed to provide SQLite 3.25.0 or later.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
module => 'LWP::Protocol::https',
|
||||||
|
version => 0,
|
||||||
|
desc => 'The "sa-update" program can use this module to make HTTPS requests.
|
||||||
|
Also used by DecodeShortURLs plugin.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
module => 'LWP::UserAgent',
|
module => 'LWP::UserAgent',
|
||||||
version => 0,
|
version => 0,
|
||||||
@ -323,6 +329,18 @@ our @OPTIONAL_MODULES = (
|
|||||||
version => 0,
|
version => 0,
|
||||||
desc => 'Mail::DMARC is used by the optional DMARC plugin.',
|
desc => 'Mail::DMARC is used by the optional DMARC plugin.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
module => 'Devel::Cycle',
|
||||||
|
version => 0,
|
||||||
|
desc => 'Devel::Cycle is used in make test in tests that will be harmelessly
|
||||||
|
skipped if it is not available',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
module => 'Text::Diff',
|
||||||
|
version => 0,
|
||||||
|
desc => 'Text::Diff is used in make test in tests that will be harmelessly
|
||||||
|
skipped if it is not available',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
our @BINARIES = ();
|
our @BINARIES = ();
|
||||||
@ -382,13 +400,28 @@ if ($^O eq 'freebsd') {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# The numbers being tested only have to be anything more than truncated packets will have
|
||||||
|
# That allows some flexibility if the exact test records are changed in the future
|
||||||
|
our @NETWORK_TESTS = (
|
||||||
|
{
|
||||||
|
'name' => 'txttcp.spamassassin.org',
|
||||||
|
'type' => 'TXT',
|
||||||
|
'min_answers' => 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name' => 'multihomed.dnsbltest.spamassassin.org',
|
||||||
|
'type' => 'A',
|
||||||
|
'min_answers' => 4,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
=head1 METHODS
|
=head1 METHODS
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item $f->debug_diagnostics ()
|
=item $f-E<gt>debug_diagnostics ()
|
||||||
|
|
||||||
Output some diagnostic information, useful for debugging SpamAssassin
|
Output some diagnostic information, useful for debugging SpamAssassin
|
||||||
problems.
|
problems.
|
||||||
|
@ -59,6 +59,8 @@ Options:
|
|||||||
--add-addr-to-welcomelist=addr Add addr to persistent address welcomelist
|
--add-addr-to-welcomelist=addr Add addr to persistent address welcomelist
|
||||||
--add-addr-to-blocklist=addr Add addr to persistent address blocklist
|
--add-addr-to-blocklist=addr Add addr to persistent address blocklist
|
||||||
--remove-addr-from-welcomelist=addr Remove addr from persistent address list
|
--remove-addr-from-welcomelist=addr Remove addr from persistent address list
|
||||||
|
-u username, --username=username Override username taken from the runtime
|
||||||
|
environment, used with SQL
|
||||||
-4 --ipv4only, --ipv4-only, --ipv4 Use IPv4, disable use of IPv6 for DNS etc.
|
-4 --ipv4only, --ipv4-only, --ipv4 Use IPv4, disable use of IPv6 for DNS etc.
|
||||||
-6 Use IPv6, disable use of IPv4 where possible
|
-6 Use IPv6, disable use of IPv4 where possible
|
||||||
--progress Print progress bar
|
--progress Print progress bar
|
||||||
@ -219,6 +221,17 @@ Remove the named email address from a persistent address welcomelist. Note that
|
|||||||
you must be running C<spamassassin> or C<spamd> with a persistent address
|
you must be running C<spamassassin> or C<spamd> with a persistent address
|
||||||
list plugin enabled for this to work.
|
list plugin enabled for this to work.
|
||||||
|
|
||||||
|
=item B<-u> I<username>, B<--username>=I<username>
|
||||||
|
|
||||||
|
If specified this username will override the username taken from the runtime
|
||||||
|
environment. You can use this option to specify users in a virtual user
|
||||||
|
configuration when using SQL as the Bayes backend.
|
||||||
|
|
||||||
|
NOTE: This option will not change to the given I<username>, it will only attempt
|
||||||
|
to act on behalf of that user. Because of this you will need to have proper
|
||||||
|
permissions to be able to change files owned by I<username>. In the case of SQL
|
||||||
|
this generally is not a problem.
|
||||||
|
|
||||||
=item B< --ipv4only>, B<--ipv4-only>, B<--ipv4>
|
=item B< --ipv4only>, B<--ipv4-only>, B<--ipv4>
|
||||||
|
|
||||||
Do not use IPv6 for DNS tests. Normally, SpamAssassin will try to detect if
|
Do not use IPv6 for DNS tests. Normally, SpamAssassin will try to detect if
|
||||||
@ -338,11 +351,11 @@ C<Mail::SpamAssassin>
|
|||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
||||||
|
|
||||||
See <https://issues.apache.org/SpamAssassin/>
|
See E<lt>https://issues.apache.org/SpamAssassin/E<gt>
|
||||||
|
|
||||||
=head1 AUTHORS
|
=head1 AUTHORS
|
||||||
|
|
||||||
The SpamAssassin(tm) Project <https://spamassassin.apache.org/>
|
The SpamAssassin(tm) Project E<lt>https://spamassassin.apache.org/E<gt>
|
||||||
|
|
||||||
=head1 COPYRIGHT
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ endif
|
|||||||
# wget https://data.iana.org/TLD/tlds-alpha-by-domain.txt -q -O - | grep -i '^xn--' | idn -u | tr '\n' ' ' | fold -w 80 -s | perl -pe 's/\s+$//; s/.*/util_rb_tld \L$_\n/'
|
# wget https://data.iana.org/TLD/tlds-alpha-by-domain.txt -q -O - | grep -i '^xn--' | idn -u | tr '\n' ' ' | fold -w 80 -s | perl -pe 's/\s+$//; s/.*/util_rb_tld \L$_\n/'
|
||||||
|
|
||||||
if can(Mail::SpamAssassin::Conf::feature_registryboundaries)
|
if can(Mail::SpamAssassin::Conf::feature_registryboundaries)
|
||||||
# Updated 2022-10-18
|
# Updated 2023-11-17
|
||||||
util_rb_tld xn--11b4c3d xn--1ck2e1b xn--1qqw23a xn--2scrj9c xn--30rr7y xn--3bst00m
|
util_rb_tld xn--11b4c3d xn--1ck2e1b xn--1qqw23a xn--2scrj9c xn--30rr7y xn--3bst00m
|
||||||
util_rb_tld xn--3ds443g xn--3e0b707e xn--3hcrj9c xn--3pxu8k xn--42c2d9a xn--45br5cyl
|
util_rb_tld xn--3ds443g xn--3e0b707e xn--3hcrj9c xn--3pxu8k xn--42c2d9a xn--45br5cyl
|
||||||
util_rb_tld xn--45brj9c xn--45q11c xn--4dbrk0ce xn--4gbrim xn--54b7fta0cc xn--55qw42g
|
util_rb_tld xn--45brj9c xn--45q11c xn--4dbrk0ce xn--4gbrim xn--54b7fta0cc xn--55qw42g
|
||||||
@ -70,130 +70,128 @@ util_rb_tld xn--fiq64b xn--fiqs8s xn--fiqz9s xn--fjq720a xn--flw351e xn--fpcrj9c
|
|||||||
util_rb_tld xn--fzc2c9e2c xn--fzys8d69uvgm xn--g2xx48c xn--gckr3f0f xn--gecrj9c xn--gk3at1e
|
util_rb_tld xn--fzc2c9e2c xn--fzys8d69uvgm xn--g2xx48c xn--gckr3f0f xn--gecrj9c xn--gk3at1e
|
||||||
util_rb_tld xn--h2breg3eve xn--h2brj9c xn--h2brj9c8c xn--hxt814e xn--i1b6b1a6a2e
|
util_rb_tld xn--h2breg3eve xn--h2brj9c xn--h2brj9c8c xn--hxt814e xn--i1b6b1a6a2e
|
||||||
util_rb_tld xn--imr513n xn--io0a7i xn--j1aef xn--j1amh xn--j6w193g xn--jlq480n2rg
|
util_rb_tld xn--imr513n xn--io0a7i xn--j1aef xn--j1amh xn--j6w193g xn--jlq480n2rg
|
||||||
util_rb_tld xn--jlq61u9w7b xn--jvr189m xn--kcrx77d1x4a xn--kprw13d xn--kpry57d xn--kput3i
|
util_rb_tld xn--jvr189m xn--kcrx77d1x4a xn--kprw13d xn--kpry57d xn--kput3i xn--l1acc
|
||||||
util_rb_tld xn--l1acc xn--lgbbat1ad8j xn--mgb9awbf xn--mgba3a3ejt xn--mgba3a4f16a
|
util_rb_tld xn--lgbbat1ad8j xn--mgb9awbf xn--mgba3a3ejt xn--mgba3a4f16a xn--mgba7c0bbn0a
|
||||||
util_rb_tld xn--mgba7c0bbn0a xn--mgbaakc7dvf xn--mgbaam7a8h xn--mgbab2bd xn--mgbah1a3hjkrd
|
util_rb_tld xn--mgbaakc7dvf xn--mgbaam7a8h xn--mgbab2bd xn--mgbah1a3hjkrd xn--mgbai9azgqp6j
|
||||||
util_rb_tld xn--mgbai9azgqp6j xn--mgbayh7gpa xn--mgbbh1a xn--mgbbh1a71e xn--mgbc0a9azcg
|
util_rb_tld xn--mgbayh7gpa xn--mgbbh1a xn--mgbbh1a71e xn--mgbc0a9azcg xn--mgbca7dzdo
|
||||||
util_rb_tld xn--mgbca7dzdo xn--mgbcpq6gpa1a xn--mgberp4a5d4ar xn--mgbgu82a xn--mgbi4ecexp
|
util_rb_tld xn--mgbcpq6gpa1a xn--mgberp4a5d4ar xn--mgbgu82a xn--mgbi4ecexp xn--mgbpl2fh
|
||||||
util_rb_tld xn--mgbpl2fh xn--mgbt3dhd xn--mgbtx2b xn--mgbx4cd0ab xn--mix891f xn--mk1bu44c
|
util_rb_tld xn--mgbt3dhd xn--mgbtx2b xn--mgbx4cd0ab xn--mix891f xn--mk1bu44c xn--mxtq1m
|
||||||
util_rb_tld xn--mxtq1m xn--ngbc5azd xn--ngbe9e0a xn--ngbrx xn--node xn--nqv7f
|
util_rb_tld xn--ngbc5azd xn--ngbe9e0a xn--ngbrx xn--node xn--nqv7f xn--nqv7fs00ema
|
||||||
util_rb_tld xn--nqv7fs00ema xn--nyqy26a xn--o3cw4h xn--ogbpf8fl xn--otu796d xn--p1acf
|
util_rb_tld xn--nyqy26a xn--o3cw4h xn--ogbpf8fl xn--otu796d xn--p1acf xn--p1ai xn--pgbs0dh
|
||||||
util_rb_tld xn--p1ai xn--pgbs0dh xn--pssy2u xn--q7ce6a xn--q9jyb4c xn--qcka1pmc xn--qxa6a
|
util_rb_tld xn--pssy2u xn--q7ce6a xn--q9jyb4c xn--qcka1pmc xn--qxa6a xn--qxam xn--rhqv96g
|
||||||
util_rb_tld xn--qxam xn--rhqv96g xn--rovu88b xn--rvc1e0am3e xn--s9brj9c xn--ses554g
|
util_rb_tld xn--rovu88b xn--rvc1e0am3e xn--s9brj9c xn--ses554g xn--t60b56a xn--tckwe
|
||||||
util_rb_tld xn--t60b56a xn--tckwe xn--tiq49xqyj xn--unup4y xn--vermgensberater-ctb
|
util_rb_tld xn--tiq49xqyj xn--unup4y xn--vermgensberater-ctb xn--vermgensberatung-pwb
|
||||||
util_rb_tld xn--vermgensberatung-pwb xn--vhquv xn--vuq861b xn--w4r85el8fhu5dnra xn--w4rs40l
|
util_rb_tld xn--vhquv xn--vuq861b xn--w4r85el8fhu5dnra xn--w4rs40l xn--wgbh1c xn--wgbl6a
|
||||||
util_rb_tld xn--wgbh1c xn--wgbl6a xn--xhq521b xn--xkc2al3hye2a xn--xkc2dl3a5ee0h xn--y9a3aq
|
util_rb_tld xn--xhq521b xn--xkc2al3hye2a xn--xkc2dl3a5ee0h xn--y9a3aq xn--yfro4i67o
|
||||||
util_rb_tld xn--yfro4i67o xn--ygbi2ammx xn--zfr164b
|
util_rb_tld xn--ygbi2ammx xn--zfr164b
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Standard List
|
# Standard List
|
||||||
# For an up to date list of TLDs that can be pasted into this block, run this command:
|
# For an up to date list of TLDs that can be pasted into this block, run this command:
|
||||||
# wget https://data.iana.org/TLD/tlds-alpha-by-domain.txt -q -O - | tail -n+2 | grep -vi '^xn--' | tr '\n' ' ' | fold -w 80 -s | perl -pe 's/\s+$//; s/.*/util_rb_tld \L$_\n/'
|
# wget https://data.iana.org/TLD/tlds-alpha-by-domain.txt -q -O - | tail -n+2 | grep -vi '^xn--' | tr '\n' ' ' | fold -w 80 -s | perl -pe 's/\s+$//; s/.*/util_rb_tld \L$_\n/'
|
||||||
|
|
||||||
# Updated 2022-10-18
|
# Updated 2023-11-17
|
||||||
util_rb_tld aaa aarp abarth abb abbott abbvie abc able abogado abudhabi ac academy
|
util_rb_tld aaa aarp abb abbott abbvie abc able abogado abudhabi ac academy accenture
|
||||||
util_rb_tld accenture accountant accountants aco actor ad adac ads adult ae aeg aero aetna
|
util_rb_tld accountant accountants aco actor ad ads adult ae aeg aero aetna af afl africa
|
||||||
util_rb_tld af afl africa ag agakhan agency ai aig airbus airforce airtel akdn al alfaromeo
|
util_rb_tld ag agakhan agency ai aig airbus airforce airtel akdn al alibaba alipay
|
||||||
util_rb_tld alibaba alipay allfinanz allstate ally alsace alstom am amazon americanexpress
|
util_rb_tld allfinanz allstate ally alsace alstom am amazon americanexpress americanfamily
|
||||||
util_rb_tld americanfamily amex amfam amica amsterdam analytics android anquan anz ao aol
|
util_rb_tld amex amfam amica amsterdam analytics android anquan anz ao aol apartments app
|
||||||
util_rb_tld apartments app apple aq aquarelle ar arab aramco archi army arpa art arte as
|
util_rb_tld apple aq aquarelle ar arab aramco archi army arpa art arte as asda asia
|
||||||
util_rb_tld asda asia associates at athleta attorney au auction audi audible audio auspost
|
util_rb_tld associates at athleta attorney au auction audi audible audio auspost author
|
||||||
util_rb_tld author auto autos avianca aw aws ax axa az azure ba baby baidu banamex
|
util_rb_tld auto autos avianca aw aws ax axa az azure ba baby baidu banamex bananarepublic
|
||||||
util_rb_tld bananarepublic band bank bar barcelona barclaycard barclays barefoot bargains
|
util_rb_tld band bank bar barcelona barclaycard barclays barefoot bargains baseball
|
||||||
util_rb_tld baseball basketball bauhaus bayern bb bbc bbt bbva bcg bcn bd be beats beauty
|
util_rb_tld basketball bauhaus bayern bb bbc bbt bbva bcg bcn bd be beats beauty beer
|
||||||
util_rb_tld beer bentley berlin best bestbuy bet bf bg bh bharti bi bible bid bike bing
|
util_rb_tld bentley berlin best bestbuy bet bf bg bh bharti bi bible bid bike bing bingo
|
||||||
util_rb_tld bingo bio biz bj black blackfriday blockbuster blog bloomberg blue bm bms bmw
|
util_rb_tld bio biz bj black blackfriday blockbuster blog bloomberg blue bm bms bmw bn
|
||||||
util_rb_tld bn bnpparibas bo boats boehringer bofa bom bond boo book booking bosch bostik
|
util_rb_tld bnpparibas bo boats boehringer bofa bom bond boo book booking bosch bostik
|
||||||
util_rb_tld boston bot boutique box br bradesco bridgestone broadway broker brother
|
util_rb_tld boston bot boutique box br bradesco bridgestone broadway broker brother
|
||||||
util_rb_tld brussels bs bt build builders business buy buzz bv bw by bz bzh ca cab cafe cal
|
util_rb_tld brussels bs bt build builders business buy buzz bv bw by bz bzh ca cab cafe cal
|
||||||
util_rb_tld call calvinklein cam camera camp canon capetown capital capitalone car caravan
|
util_rb_tld call calvinklein cam camera camp canon capetown capital capitalone car caravan
|
||||||
util_rb_tld cards care career careers cars casa case cash casino cat catering catholic cba
|
util_rb_tld cards care career careers cars casa case cash casino cat catering catholic cba
|
||||||
util_rb_tld cbn cbre cbs cc cd center ceo cern cf cfa cfd cg ch chanel channel charity
|
util_rb_tld cbn cbre cc cd center ceo cern cf cfa cfd cg ch chanel channel charity chase
|
||||||
util_rb_tld chase chat cheap chintai christmas chrome church ci cipriani circle cisco
|
util_rb_tld chat cheap chintai christmas chrome church ci cipriani circle cisco citadel
|
||||||
util_rb_tld citadel citi citic city cityeats ck cl claims cleaning click clinic clinique
|
util_rb_tld citi citic city ck cl claims cleaning click clinic clinique clothing cloud club
|
||||||
util_rb_tld clothing cloud club clubmed cm cn co coach codes coffee college cologne com
|
util_rb_tld clubmed cm cn co coach codes coffee college cologne com comcast commbank
|
||||||
util_rb_tld comcast commbank community company compare computer comsec condos construction
|
util_rb_tld community company compare computer comsec condos construction consulting
|
||||||
util_rb_tld consulting contact contractors cooking cookingchannel cool coop corsica country
|
util_rb_tld contact contractors cooking cool coop corsica country coupon coupons courses
|
||||||
util_rb_tld coupon coupons courses cpa cr credit creditcard creditunion cricket crown crs
|
util_rb_tld cpa cr credit creditcard creditunion cricket crown crs cruise cruises cu
|
||||||
util_rb_tld cruise cruises cu cuisinella cv cw cx cy cymru cyou cz dabur dad dance data
|
util_rb_tld cuisinella cv cw cx cy cymru cyou cz dabur dad dance data date dating datsun
|
||||||
util_rb_tld date dating datsun day dclk dds de deal dealer deals degree delivery dell
|
util_rb_tld day dclk dds de deal dealer deals degree delivery dell deloitte delta democrat
|
||||||
util_rb_tld deloitte delta democrat dental dentist desi design dev dhl diamonds diet
|
util_rb_tld dental dentist desi design dev dhl diamonds diet digital direct directory
|
||||||
util_rb_tld digital direct directory discount discover dish diy dj dk dm dnp do docs doctor
|
util_rb_tld discount discover dish diy dj dk dm dnp do docs doctor dog domains dot download
|
||||||
util_rb_tld dog domains dot download drive dtv dubai dunlop dupont durban dvag dvr dz earth
|
util_rb_tld drive dtv dubai dunlop dupont durban dvag dvr dz earth eat ec eco edeka edu
|
||||||
util_rb_tld eat ec eco edeka edu education ee eg email emerck energy engineer engineering
|
util_rb_tld education ee eg email emerck energy engineer engineering enterprises epson
|
||||||
util_rb_tld enterprises epson equipment er ericsson erni es esq estate et etisalat eu
|
util_rb_tld equipment er ericsson erni es esq estate et etisalat eu eurovision eus events
|
||||||
util_rb_tld eurovision eus events exchange expert exposed express extraspace fage fail
|
util_rb_tld exchange expert exposed express extraspace fage fail fairwinds faith family fan
|
||||||
util_rb_tld fairwinds faith family fan fans farm farmers fashion fast fedex feedback
|
util_rb_tld fans farm farmers fashion fast fedex feedback ferrari ferrero fi fidelity fido
|
||||||
util_rb_tld ferrari ferrero fi fiat fidelity fido film final finance financial fire
|
util_rb_tld film final finance financial fire firestone firmdale fish fishing fit fitness
|
||||||
util_rb_tld firestone firmdale fish fishing fit fitness fj fk flickr flights flir florist
|
util_rb_tld fj fk flickr flights flir florist flowers fly fm fo foo food football ford
|
||||||
util_rb_tld flowers fly fm fo foo food foodnetwork football ford forex forsale forum
|
util_rb_tld forex forsale forum foundation fox fr free fresenius frl frogans frontier ftr
|
||||||
util_rb_tld foundation fox fr free fresenius frl frogans frontdoor frontier ftr fujitsu fun
|
util_rb_tld fujitsu fun fund furniture futbol fyi ga gal gallery gallo gallup game games
|
||||||
util_rb_tld fund furniture futbol fyi ga gal gallery gallo gallup game games gap garden gay
|
util_rb_tld gap garden gay gb gbiz gd gdn ge gea gent genting george gf gg ggee gh gi gift
|
||||||
util_rb_tld gb gbiz gd gdn ge gea gent genting george gf gg ggee gh gi gift gifts gives
|
util_rb_tld gifts gives giving gl glass gle global globo gm gmail gmbh gmo gmx gn godaddy
|
||||||
util_rb_tld giving gl glass gle global globo gm gmail gmbh gmo gmx gn godaddy gold
|
util_rb_tld gold goldpoint golf goo goodyear goog google gop got gov gp gq gr grainger
|
||||||
util_rb_tld goldpoint golf goo goodyear goog google gop got gov gp gq gr grainger graphics
|
util_rb_tld graphics gratis green gripe grocery group gs gt gu guardian gucci guge guide
|
||||||
util_rb_tld gratis green gripe grocery group gs gt gu guardian gucci guge guide guitars
|
util_rb_tld guitars guru gw gy hair hamburg hangout haus hbo hdfc hdfcbank health
|
||||||
util_rb_tld guru gw gy hair hamburg hangout haus hbo hdfc hdfcbank health healthcare help
|
util_rb_tld healthcare help helsinki here hermes hiphop hisamitsu hitachi hiv hk hkt hm hn
|
||||||
util_rb_tld helsinki here hermes hgtv hiphop hisamitsu hitachi hiv hk hkt hm hn hockey
|
util_rb_tld hockey holdings holiday homedepot homegoods homes homesense honda horse
|
||||||
util_rb_tld holdings holiday homedepot homegoods homes homesense honda horse hospital host
|
util_rb_tld hospital host hosting hot hotels hotmail house how hr hsbc ht hu hughes hyatt
|
||||||
util_rb_tld hosting hot hoteles hotels hotmail house how hr hsbc ht hu hughes hyatt hyundai
|
util_rb_tld hyundai ibm icbc ice icu id ie ieee ifm ikano il im imamat imdb immo immobilien
|
||||||
util_rb_tld ibm icbc ice icu id ie ieee ifm ikano il im imamat imdb immo immobilien in inc
|
util_rb_tld in inc industries infiniti info ing ink institute insurance insure int
|
||||||
util_rb_tld industries infiniti info ing ink institute insurance insure int international
|
util_rb_tld international intuit investments io ipiranga iq ir irish is ismaili ist
|
||||||
util_rb_tld intuit investments io ipiranga iq ir irish is ismaili ist istanbul it itau itv
|
util_rb_tld istanbul it itau itv jaguar java jcb je jeep jetzt jewelry jio jll jm jmp jnj
|
||||||
util_rb_tld jaguar java jcb je jeep jetzt jewelry jio jll jm jmp jnj jo jobs joburg jot joy
|
util_rb_tld jo jobs joburg jot joy jp jpmorgan jprs juegos juniper kaufen kddi ke
|
||||||
util_rb_tld jp jpmorgan jprs juegos juniper kaufen kddi ke kerryhotels kerrylogistics
|
util_rb_tld kerryhotels kerrylogistics kerryproperties kfh kg kh ki kia kids kim kindle
|
||||||
util_rb_tld kerryproperties kfh kg kh ki kia kids kim kinder kindle kitchen kiwi km kn
|
util_rb_tld kitchen kiwi km kn koeln komatsu kosher kp kpmg kpn kr krd kred kuokgroup kw ky
|
||||||
util_rb_tld koeln komatsu kosher kp kpmg kpn kr krd kred kuokgroup kw ky kyoto kz la
|
util_rb_tld kyoto kz la lacaixa lamborghini lamer lancaster land landrover lanxess lasalle
|
||||||
util_rb_tld lacaixa lamborghini lamer lancaster lancia land landrover lanxess lasalle lat
|
util_rb_tld lat latino latrobe law lawyer lb lc lds lease leclerc lefrak legal lego lexus
|
||||||
util_rb_tld latino latrobe law lawyer lb lc lds lease leclerc lefrak legal lego lexus lgbt
|
util_rb_tld lgbt li lidl life lifeinsurance lifestyle lighting like lilly limited limo
|
||||||
util_rb_tld li lidl life lifeinsurance lifestyle lighting like lilly limited limo lincoln
|
util_rb_tld lincoln link lipsy live living lk llc llp loan loans locker locus lol london
|
||||||
util_rb_tld linde link lipsy live living lk llc llp loan loans locker locus loft lol london
|
|
||||||
util_rb_tld lotte lotto love lpl lplfinancial lr ls lt ltd ltda lu lundbeck luxe luxury lv
|
util_rb_tld lotte lotto love lpl lplfinancial lr ls lt ltd ltda lu lundbeck luxe luxury lv
|
||||||
util_rb_tld ly ma macys madrid maif maison makeup man management mango map market marketing
|
util_rb_tld ly ma madrid maif maison makeup man management mango map market marketing
|
||||||
util_rb_tld markets marriott marshalls maserati mattel mba mc mckinsey md me med media meet
|
util_rb_tld markets marriott marshalls mattel mba mc mckinsey md me med media meet
|
||||||
util_rb_tld melbourne meme memorial men menu merckmsd mg mh miami microsoft mil mini mint
|
util_rb_tld melbourne meme memorial men menu merckmsd mg mh miami microsoft mil mini mint
|
||||||
util_rb_tld mit mitsubishi mk ml mlb mls mm mma mn mo mobi mobile moda moe moi mom monash
|
util_rb_tld mit mitsubishi mk ml mlb mls mm mma mn mo mobi mobile moda moe moi mom monash
|
||||||
util_rb_tld money monster mormon mortgage moscow moto motorcycles mov movie mp mq mr ms msd
|
util_rb_tld money monster mormon mortgage moscow moto motorcycles mov movie mp mq mr ms msd
|
||||||
util_rb_tld mt mtn mtr mu museum music mutual mv mw mx my mz na nab nagoya name natura navy
|
util_rb_tld mt mtn mtr mu museum music mv mw mx my mz na nab nagoya name natura navy nba nc
|
||||||
util_rb_tld nba nc ne nec net netbank netflix network neustar new news next nextdirect
|
util_rb_tld ne nec net netbank netflix network neustar new news next nextdirect nexus nf
|
||||||
util_rb_tld nexus nf nfl ng ngo nhk ni nico nike nikon ninja nissan nissay nl no nokia
|
util_rb_tld nfl ng ngo nhk ni nico nike nikon ninja nissan nissay nl no nokia norton now
|
||||||
util_rb_tld northwesternmutual norton now nowruz nowtv np nr nra nrw ntt nu nyc nz obi
|
util_rb_tld nowruz nowtv np nr nra nrw ntt nu nyc nz obi observer office okinawa olayan
|
||||||
util_rb_tld observer office okinawa olayan olayangroup oldnavy ollo om omega one ong onl
|
util_rb_tld olayangroup oldnavy ollo om omega one ong onl online ooo open oracle orange org
|
||||||
util_rb_tld online ooo open oracle orange org organic origins osaka otsuka ott ovh pa page
|
util_rb_tld organic origins osaka otsuka ott ovh pa page panasonic paris pars partners
|
||||||
util_rb_tld panasonic paris pars partners parts party passagens pay pccw pe pet pf pfizer
|
util_rb_tld parts party pay pccw pe pet pf pfizer pg ph pharmacy phd philips phone photo
|
||||||
util_rb_tld pg ph pharmacy phd philips phone photo photography photos physio pics pictet
|
util_rb_tld photography photos physio pics pictet pictures pid pin ping pink pioneer pizza
|
||||||
util_rb_tld pictures pid pin ping pink pioneer pizza pk pl place play playstation plumbing
|
util_rb_tld pk pl place play playstation plumbing plus pm pn pnc pohl poker politie porn
|
||||||
util_rb_tld plus pm pn pnc pohl poker politie porn post pr pramerica praxi press prime pro
|
util_rb_tld post pr pramerica praxi press prime pro prod productions prof progressive promo
|
||||||
util_rb_tld prod productions prof progressive promo properties property protection pru
|
util_rb_tld properties property protection pru prudential ps pt pub pw pwc py qa qpon
|
||||||
util_rb_tld prudential ps pt pub pw pwc py qa qpon quebec quest racing radio re read
|
util_rb_tld quebec quest racing radio re read realestate realtor realty recipes red
|
||||||
util_rb_tld realestate realtor realty recipes red redstone redumbrella rehab reise reisen
|
util_rb_tld redstone redumbrella rehab reise reisen reit reliance ren rent rentals repair
|
||||||
util_rb_tld reit reliance ren rent rentals repair report republican rest restaurant review
|
util_rb_tld report republican rest restaurant review reviews rexroth rich richardli ricoh
|
||||||
util_rb_tld reviews rexroth rich richardli ricoh ril rio rip ro rocher rocks rodeo rogers
|
util_rb_tld ril rio rip ro rocks rodeo rogers room rs rsvp ru rugby ruhr run rw rwe ryukyu
|
||||||
util_rb_tld room rs rsvp ru rugby ruhr run rw rwe ryukyu sa saarland safe safety sakura
|
util_rb_tld sa saarland safe safety sakura sale salon samsclub samsung sandvik
|
||||||
util_rb_tld sale salon samsclub samsung sandvik sandvikcoromant sanofi sap sarl sas save
|
util_rb_tld sandvikcoromant sanofi sap sarl sas save saxo sb sbi sbs sc sca scb schaeffler
|
||||||
util_rb_tld saxo sb sbi sbs sc sca scb schaeffler schmidt scholarships school schule
|
util_rb_tld schmidt scholarships school schule schwarz science scot sd se search seat
|
||||||
util_rb_tld schwarz science scot sd se search seat secure security seek select sener
|
util_rb_tld secure security seek select sener services seven sew sex sexy sfr sg sh
|
||||||
util_rb_tld services ses seven sew sex sexy sfr sg sh shangrila sharp shaw shell shia
|
util_rb_tld shangrila sharp shaw shell shia shiksha shoes shop shopping shouji show si silk
|
||||||
util_rb_tld shiksha shoes shop shopping shouji show showtime si silk sina singles site sj
|
util_rb_tld sina singles site sj sk ski skin sky skype sl sling sm smart smile sn sncf so
|
||||||
util_rb_tld sk ski skin sky skype sl sling sm smart smile sn sncf so soccer social softbank
|
util_rb_tld soccer social softbank software sohu solar solutions song sony soy spa space
|
||||||
util_rb_tld software sohu solar solutions song sony soy spa space sport spot sr srl ss st
|
util_rb_tld sport spot sr srl ss st stada staples star statebank statefarm stc stcgroup
|
||||||
util_rb_tld stada staples star statebank statefarm stc stcgroup stockholm storage store
|
util_rb_tld stockholm storage store stream studio study style su sucks supplies supply
|
||||||
util_rb_tld stream studio study style su sucks supplies supply support surf surgery suzuki
|
util_rb_tld support surf surgery suzuki sv swatch swiss sx sy sydney systems sz tab taipei
|
||||||
util_rb_tld sv swatch swiss sx sy sydney systems sz tab taipei talk taobao target
|
util_rb_tld talk taobao target tatamotors tatar tattoo tax taxi tc tci td tdk team tech
|
||||||
util_rb_tld tatamotors tatar tattoo tax taxi tc tci td tdk team tech technology tel temasek
|
util_rb_tld technology tel temasek tennis teva tf tg th thd theater theatre tiaa tickets
|
||||||
util_rb_tld tennis teva tf tg th thd theater theatre tiaa tickets tienda tiffany tips tires
|
util_rb_tld tienda tips tires tirol tj tjmaxx tjx tk tkmaxx tl tm tmall tn to today tokyo
|
||||||
util_rb_tld tirol tj tjmaxx tjx tk tkmaxx tl tm tmall tn to today tokyo tools top toray
|
util_rb_tld tools top toray toshiba total tours town toyota toys tr trade trading training
|
||||||
util_rb_tld toshiba total tours town toyota toys tr trade trading training travel
|
util_rb_tld travel travelers travelersinsurance trust trv tt tube tui tunes tushu tv tvs tw
|
||||||
util_rb_tld travelchannel travelers travelersinsurance trust trv tt tube tui tunes tushu tv
|
util_rb_tld tz ua ubank ubs ug uk unicom university uno uol ups us uy uz va vacations vana
|
||||||
util_rb_tld tvs tw tz ua ubank ubs ug uk unicom university uno uol ups us uy uz va
|
util_rb_tld vanguard vc ve vegas ventures verisign versicherung vet vg vi viajes video vig
|
||||||
util_rb_tld vacations vana vanguard vc ve vegas ventures verisign versicherung vet vg vi
|
util_rb_tld viking villas vin vip virgin visa vision viva vivo vlaanderen vn vodka
|
||||||
util_rb_tld viajes video vig viking villas vin vip virgin visa vision viva vivo vlaanderen
|
util_rb_tld volkswagen volvo vote voting voto voyage vu wales walmart walter wang wanggou
|
||||||
util_rb_tld vn vodka volkswagen volvo vote voting voto voyage vu vuelos wales walmart
|
util_rb_tld watch watches weather weatherchannel webcam weber website wed wedding weibo
|
||||||
util_rb_tld walter wang wanggou watch watches weather weatherchannel webcam weber website
|
util_rb_tld weir wf whoswho wien wiki williamhill win windows wine winners wme
|
||||||
util_rb_tld wed wedding weibo weir wf whoswho wien wiki williamhill win windows wine
|
util_rb_tld wolterskluwer woodside work works world wow ws wtc wtf xbox xerox xfinity
|
||||||
util_rb_tld winners wme wolterskluwer woodside work works world wow ws wtc wtf xbox xerox
|
util_rb_tld xihuan xin xxx xyz yachts yahoo yamaxun yandex ye yodobashi yoga yokohama you
|
||||||
util_rb_tld xfinity xihuan xin xxx xyz yachts yahoo yamaxun yandex ye yodobashi yoga
|
util_rb_tld youtube yt yun za zappos zara zero zip zm zone zuerich zw
|
||||||
util_rb_tld yokohama you youtube yt yun za zappos zara zero zip zm zone zuerich zw
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# 2nd level TLD list
|
# 2nd level TLD list
|
||||||
@ -495,9 +493,17 @@ util_rb_2tld weebly.com
|
|||||||
util_rb_2tld ifrance.com
|
util_rb_2tld ifrance.com
|
||||||
util_rb_2tld jimdo.com
|
util_rb_2tld jimdo.com
|
||||||
util_rb_2tld kimsufi.com
|
util_rb_2tld kimsufi.com
|
||||||
util_rb_2tld mail333.su
|
util_rb_2tld azerbaijan.su
|
||||||
util_rb_2tld pisem.su
|
util_rb_2tld east-kazakhstan.su
|
||||||
|
util_rb_2tld exnet.su
|
||||||
|
util_rb_2tld georgia.su
|
||||||
|
util_rb_2tld kalmykia.su
|
||||||
util_rb_2tld mail15.su
|
util_rb_2tld mail15.su
|
||||||
|
util_rb_2tld mail333.su
|
||||||
|
util_rb_2tld mangyshlak.su
|
||||||
|
util_rb_2tld nov.su
|
||||||
|
util_rb_2tld pisem.su
|
||||||
|
util_rb_2tld tashkent.su
|
||||||
util_rb_2tld prserv.net
|
util_rb_2tld prserv.net
|
||||||
util_rb_2tld angelfire.com
|
util_rb_2tld angelfire.com
|
||||||
util_rb_2tld 163.to
|
util_rb_2tld 163.to
|
||||||
@ -638,6 +644,8 @@ util_rb_2tld tumblr.com
|
|||||||
util_rb_2tld fileave.com
|
util_rb_2tld fileave.com
|
||||||
util_rb_2tld de.tl
|
util_rb_2tld de.tl
|
||||||
util_rb_2tld co.com
|
util_rb_2tld co.com
|
||||||
|
util_rb_2tld sendpul.se
|
||||||
|
util_rb_2tld amplifyapp.com
|
||||||
# Dyndns.com
|
# Dyndns.com
|
||||||
util_rb_2tld dyndns-at-home.com
|
util_rb_2tld dyndns-at-home.com
|
||||||
util_rb_2tld dyndns-at-work.com
|
util_rb_2tld dyndns-at-work.com
|
||||||
@ -740,6 +748,7 @@ util_rb_3tld no-ip.co.uk
|
|||||||
#
|
#
|
||||||
util_rb_3tld mobile.web.tr
|
util_rb_3tld mobile.web.tr
|
||||||
util_rb_3tld ct.sendgrid.net
|
util_rb_3tld ct.sendgrid.net
|
||||||
|
util_rb_3tld on.fleek.co
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
# DO NOT EDIT: file generated by build/mkupdates/listpromotable
|
# DO NOT EDIT: file generated by build/mkupdates/listpromotable
|
||||||
# active ruleset list, automatically generated from https://ruleqa.spamassassin.org/
|
# active ruleset list, automatically generated from https://ruleqa.spamassassin.org/
|
||||||
# with results from: last-net: net-ena-week0 net-ena-week1 net-ena-week2 net-ena-week3 net-ena-week4 net-giovanni-ham net-giovanni-spam net-giovanni-spammy net-grenier net-hege net-jhardin net-llanga net-mmiroslaw-mails-ham net-mmiroslaw-mails-spam net-spamsponge net-thendrikx net-tsz- corpus; day 1: ena-week0 ena-week1 ena-week2 ena-week3 ena-week4 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam spamsponge thendrikx tsz- corpus; day 2: ena-week0 ena-week1 ena-week2 ena-week3 ena-week4 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam spamsponge thendrikx tsz- corpus; day 3: ena-week0 ena-week1 ena-week2 ena-week3 ena-week4 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam spamsponge thendrikx tsz- corpus; day 4: ena-week0 ena-week1 ena-week2 ena-week3 ena-week4 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam spamsponge thendrikx tsz- corpus; day 5: ena-week0 ena-week1 ena-week2 ena-week3 ena-week4 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam spamsponge thendrikx tsz- corpus
|
# with results from: last-net: net-ena-week0 net-ena-week1 net-ena-week2 net-ena-week3 net-ena-week4 net-giovanni-ham net-giovanni-spam net-giovanni-spammy net-grenier net-hege net-jhardin net-llanga net-mmiroslaw-mails-ham net-mmiroslaw-mails-spam net-spamsponge net-tsz-corpus net-whyscream; day 1: ena-week0 ena-week1 ena-week2 ena-week3 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam spamsponge tsz-corpus whyscream; day 2: ena-week0 ena-week1 ena-week2 ena-week3 ena-week4 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam tsz-corpus whyscream; day 3: ena-week0 ena-week1 ena-week2 ena-week3 ena-week4 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam spamsponge tsz-corpus whyscream; day 4: ena-week0 ena-week1 ena-week2 ena-week3 ena-week4 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam spamsponge tsz-corpus whyscream; day 5: ena-week0 ena-week1 ena-week2 ena-week3 ena-week4 giovanni-ham giovanni-spam giovanni-spammy grenier hege jhardin llanga mmiroslaw-mails-ham mmiroslaw-mails-spam spamsponge tsz-corpus whyscream
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
ACCT_PHISHING_MANY
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
AC_BR_BONANZA
|
AC_BR_BONANZA
|
||||||
@ -110,6 +113,9 @@ ALL_TRUSTED
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
AMAZON_IMG_NOT_RCVD_AMZN
|
AMAZON_IMG_NOT_RCVD_AMZN
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
APOSTROPHE_TOCC
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
APP_DEVELOPMENT_FREEM
|
APP_DEVELOPMENT_FREEM
|
||||||
|
|
||||||
@ -131,9 +137,6 @@ AXB_XMAILER_MIMEOLE_OL_024C2
|
|||||||
# good enough
|
# good enough
|
||||||
AXB_X_FF_SEZ_S
|
AXB_X_FF_SEZ_S
|
||||||
|
|
||||||
# good enough
|
|
||||||
BAT_BDRY_TO_MALF
|
|
||||||
|
|
||||||
# tflags learn
|
# tflags learn
|
||||||
BAYES_00
|
BAYES_00
|
||||||
|
|
||||||
@ -188,10 +191,7 @@ BITCOIN_EXTORT_02
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
BITCOIN_IMGUR
|
BITCOIN_IMGUR
|
||||||
|
|
||||||
# good enough
|
# tflags net
|
||||||
BITCOIN_MALF_HTML
|
|
||||||
|
|
||||||
# tflags publish
|
|
||||||
BITCOIN_MALWARE
|
BITCOIN_MALWARE
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
@ -242,6 +242,12 @@ BITCOIN_SPAM_12
|
|||||||
# tflags net
|
# tflags net
|
||||||
BITCOIN_SPF_ONLYALL
|
BITCOIN_SPF_ONLYALL
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
BITCOIN_TOEQFM
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
BITCOIN_VISTA
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
BITCOIN_WFH_01
|
BITCOIN_WFH_01
|
||||||
|
|
||||||
@ -251,7 +257,10 @@ BITCOIN_XPRIO
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
BITCOIN_YOUR_INFO
|
BITCOIN_YOUR_INFO
|
||||||
|
|
||||||
# tflags publish
|
# good enough
|
||||||
|
BODY_SINGLE_URI
|
||||||
|
|
||||||
|
# tflags net
|
||||||
BODY_URI_ONLY
|
BODY_URI_ONLY
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
@ -392,15 +401,15 @@ DOS_DEREK_AUG08
|
|||||||
# good enough
|
# good enough
|
||||||
DOS_OE_TO_MX
|
DOS_OE_TO_MX
|
||||||
|
|
||||||
# good enough
|
|
||||||
DOS_OE_TO_MX_IMAGE
|
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
DOS_OUTLOOK_TO_MX
|
DOS_OUTLOOK_TO_MX
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
DOTGOV_IMAGE
|
DOTGOV_IMAGE
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
DSN_NO_MIMEVERSION
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
DX_TEXT_02
|
DX_TEXT_02
|
||||||
|
|
||||||
@ -431,6 +440,9 @@ ENV_AND_HDR_SPF_MATCH
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
FACEBOOK_IMG_NOT_RCVD_FB
|
FACEBOOK_IMG_NOT_RCVD_FB
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
FAKE_REPLY_C
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
FBI_MONEY
|
FBI_MONEY
|
||||||
|
|
||||||
@ -440,6 +452,9 @@ FBI_SPOOF
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
FILL_THIS_FORM
|
FILL_THIS_FORM
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
FILL_THIS_FORM_LOAN
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
FILL_THIS_FORM_LONG
|
FILL_THIS_FORM_LONG
|
||||||
|
|
||||||
@ -467,10 +482,10 @@ FONT_INVIS_POSTEXTRAS
|
|||||||
# tflags net
|
# tflags net
|
||||||
FORGED_SPF_HELO
|
FORGED_SPF_HELO
|
||||||
|
|
||||||
# tflags publish
|
# tflags net
|
||||||
FORM_FRAUD
|
FORM_FRAUD
|
||||||
|
|
||||||
# tflags publish
|
# tflags net
|
||||||
FORM_FRAUD_3
|
FORM_FRAUD_3
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
@ -494,9 +509,6 @@ FREEM_FRNUM_UNICD_EMPTY
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
FRNAME_IN_MSG_XPRIO_NO_SUB
|
FRNAME_IN_MSG_XPRIO_NO_SUB
|
||||||
|
|
||||||
# good enough
|
|
||||||
FROM_2_EMAILS_SHORT
|
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
FROM_ADDR_WS
|
FROM_ADDR_WS
|
||||||
|
|
||||||
@ -548,17 +560,23 @@ FROM_MISSP_FREEMAIL
|
|||||||
# good enough
|
# good enough
|
||||||
FROM_MISSP_MSFT
|
FROM_MISSP_MSFT
|
||||||
|
|
||||||
# tflags net
|
# good enough
|
||||||
|
FROM_MISSP_PHISH
|
||||||
|
|
||||||
|
# good enough
|
||||||
FROM_MISSP_REPLYTO
|
FROM_MISSP_REPLYTO
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
FROM_MISSP_SPF_FAIL
|
FROM_MISSP_SPF_FAIL
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
FROM_MISSP_TO_UNDISC
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
FROM_MISSP_USER
|
FROM_MISSP_USER
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
FROM_MULTI_NORDNS
|
FROM_MISSP_XPRIO
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
FROM_NEWDOM_BTC
|
FROM_NEWDOM_BTC
|
||||||
@ -581,6 +599,9 @@ FROM_SUSPICIOUS_NTLD
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
FROM_SUSPICIOUS_NTLD_FP
|
FROM_SUSPICIOUS_NTLD_FP
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
FROM_UNBAL2
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
FROM_WSP_TRAIL
|
FROM_WSP_TRAIL
|
||||||
|
|
||||||
@ -590,6 +611,9 @@ FSL_BULK_SIG
|
|||||||
# good enough
|
# good enough
|
||||||
FSL_CTYPE_WIN1251
|
FSL_CTYPE_WIN1251
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
FSL_HELO_BARE_IP_1
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
FSL_NEW_HELO_USER
|
FSL_NEW_HELO_USER
|
||||||
|
|
||||||
@ -653,21 +677,24 @@ FUZZY_SAVINGS
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
FUZZY_SECURITY
|
FUZZY_SECURITY
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
FUZZY_TRUSTWALLET
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
FUZZY_UNSUBSCRIBE
|
FUZZY_UNSUBSCRIBE
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
FUZZY_WALLET
|
FUZZY_WALLET
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
FUZZY_WELLSFARGO
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
GAPPY_SALES_LEADS_FREEM
|
GAPPY_SALES_LEADS_FREEM
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
GB_BITCOIN_CP
|
GB_BITCOIN_CP
|
||||||
|
|
||||||
# good enough
|
|
||||||
GB_BITCOIN_NH
|
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
GB_CUSTOM_HTM_URI
|
GB_CUSTOM_HTM_URI
|
||||||
|
|
||||||
@ -689,12 +716,6 @@ GB_GOOGLE_OBFUR
|
|||||||
# tflags net
|
# tflags net
|
||||||
GB_HASHBL_BTC
|
GB_HASHBL_BTC
|
||||||
|
|
||||||
# tflags publish
|
|
||||||
GB_STORAGE_GOOGLE_EMAIL
|
|
||||||
|
|
||||||
# good enough
|
|
||||||
GB_URI_FLEEK_STO_HTM
|
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
GOOGLE_DOCS_PHISH
|
GOOGLE_DOCS_PHISH
|
||||||
|
|
||||||
@ -713,9 +734,6 @@ GOOG_MALWARE_DNLD
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
GOOG_REDIR_DOCUSIGN
|
GOOG_REDIR_DOCUSIGN
|
||||||
|
|
||||||
# good enough
|
|
||||||
GOOG_REDIR_HTML_ONLY
|
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
GOOG_REDIR_NORDNS
|
GOOG_REDIR_NORDNS
|
||||||
|
|
||||||
@ -768,7 +786,7 @@ HEADER_FROM_DIFFERENT_DOMAINS
|
|||||||
HEAD_LONG
|
HEAD_LONG
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
HELO_LOCALHOST
|
HELO_LH_HOME
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
HELO_NO_DOMAIN
|
HELO_NO_DOMAIN
|
||||||
@ -783,7 +801,7 @@ HK_CTE_RAW
|
|||||||
HK_LOTTO
|
HK_LOTTO
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
HK_NAME_DRUGS
|
HK_NAME_FM_MR_MRS
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
HK_NAME_MR_MRS
|
HK_NAME_MR_MRS
|
||||||
@ -803,6 +821,9 @@ HK_RCVD_IP_MULTICAST
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
HK_SCAM
|
HK_SCAM
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
HK_WIN
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
HOSTED_IMG_DIRECT_MX
|
HOSTED_IMG_DIRECT_MX
|
||||||
|
|
||||||
@ -818,6 +839,21 @@ HOSTED_IMG_MULTI
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
HOSTED_IMG_MULTI_PUB_01
|
HOSTED_IMG_MULTI_PUB_01
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
HREF_EMPTY_NORDNS
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
HREF_EMPTY_PHPMAIL
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
HREF_EMPTY_XANTIABUSE
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
HREF_EMPTY_XAUTHED
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
HTML_BADATTR
|
||||||
|
|
||||||
# tflags userconf
|
# tflags userconf
|
||||||
HTML_CHARSET_FARAWAY
|
HTML_CHARSET_FARAWAY
|
||||||
|
|
||||||
@ -864,11 +900,14 @@ JH_SPAMMY_PATTERN02
|
|||||||
KHOP_HELO_FCRDNS
|
KHOP_HELO_FCRDNS
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
KHOP_JS_OBFUSCATION
|
KHOP_UNSUB_EMAIL
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
LINKEDIN_IMG_NOT_RCVD_LNKN
|
LINKEDIN_IMG_NOT_RCVD_LNKN
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
LIST_PARTIAL_SHORT_MSG
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
LIST_PRTL_PUMPDUMP
|
LIST_PRTL_PUMPDUMP
|
||||||
|
|
||||||
@ -899,6 +938,9 @@ LOTTO_DEPT
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
LUCRATIVE
|
LUCRATIVE
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
MALFORMED_FREEMAIL
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
MALF_HTML_B64
|
MALF_HTML_B64
|
||||||
|
|
||||||
@ -914,8 +956,8 @@ MALW_ATTACH
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
MANY_SPAN_IN_TEXT
|
MANY_SPAN_IN_TEXT
|
||||||
|
|
||||||
# tflags net
|
# good enough
|
||||||
MAY_BE_FORGED
|
MANY_SUBDOM
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
MILLION_HUNDRED
|
MILLION_HUNDRED
|
||||||
@ -992,6 +1034,9 @@ MONEY_FROM_41
|
|||||||
# good enough
|
# good enough
|
||||||
MONEY_FROM_MISSP
|
MONEY_FROM_MISSP
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
MONEY_NOHTML
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
MSGID_DOLLARS_URI_IMG
|
MSGID_DOLLARS_URI_IMG
|
||||||
|
|
||||||
@ -999,7 +1044,7 @@ MSGID_DOLLARS_URI_IMG
|
|||||||
MSGID_HDR_MALF
|
MSGID_HDR_MALF
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
MSMAIL_PRI_ABNORMAL
|
MSGID_NOFQDN1
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
MSM_PRIO_REPTO
|
MSM_PRIO_REPTO
|
||||||
@ -1007,9 +1052,6 @@ MSM_PRIO_REPTO
|
|||||||
# good enough
|
# good enough
|
||||||
MSOE_MID_WRONG_CASE
|
MSOE_MID_WRONG_CASE
|
||||||
|
|
||||||
# good enough
|
|
||||||
NAME_EMAIL_DIFF
|
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
NA_DOLLARS
|
NA_DOLLARS
|
||||||
|
|
||||||
@ -1055,9 +1097,6 @@ NSL_RCVD_FROM_USER
|
|||||||
# good enough
|
# good enough
|
||||||
NSL_RCVD_HELO_USER
|
NSL_RCVD_HELO_USER
|
||||||
|
|
||||||
# good enough
|
|
||||||
NUMBERONLY_BITCOIN_EXP
|
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
OBFU_BITCOIN
|
OBFU_BITCOIN
|
||||||
|
|
||||||
@ -1076,41 +1115,38 @@ ODD_FREEM_REPTO
|
|||||||
# good enough
|
# good enough
|
||||||
PDS_BAD_THREAD_QP_64
|
PDS_BAD_THREAD_QP_64
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
PDS_BRAND_SUBJ_NAKED_TO
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
PDS_BTC_ID
|
PDS_BTC_ID
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
PDS_BTC_MSGID
|
PDS_BTC_MSGID
|
||||||
|
|
||||||
# good enough
|
|
||||||
PDS_BTC_NTLD
|
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
PDS_DBL_URL_TNB_RUNON
|
PDS_DBL_URL_TNB_RUNON
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
PDS_FROM_2_EMAILS
|
PDS_FRNOM_TODOM_DBL_URL
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
PDS_FRNOM_TODOM_NAKED_TO
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
PDS_FROM_NAME_TO_DOMAIN
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
PDS_HELO_SPF_FAIL
|
PDS_HELO_SPF_FAIL
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
PDS_NAKED_TO_NUMERO
|
PDS_HP_HELO_NORDNS
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
PDS_NO_FULL_NAME_SPOOFED_URL
|
PDS_OTHER_BAD_TLD
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
PDS_RDNS_DYNAMIC_FP
|
PDS_PHP_EVAL
|
||||||
|
|
||||||
# good enough
|
|
||||||
PDS_SHORT_SPOOFED_URL
|
|
||||||
|
|
||||||
# good enough
|
|
||||||
PDS_TINYSUBJ_URISHRT
|
|
||||||
|
|
||||||
# good enough
|
|
||||||
PDS_TONAME_EQ_TOLOCAL_FREEM_FORGE
|
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
PHISH_ATTACH
|
PHISH_ATTACH
|
||||||
@ -1127,6 +1163,9 @@ PHP_NOVER_MUA
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
PHP_ORIG_SCRIPT
|
PHP_ORIG_SCRIPT
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
PHP_ORIG_SCRIPT_EVAL
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
PHP_SCRIPT
|
PHP_SCRIPT
|
||||||
|
|
||||||
@ -1211,6 +1250,9 @@ RCVD_IN_DNSWL_MED
|
|||||||
# tflags net
|
# tflags net
|
||||||
RCVD_IN_DNSWL_NONE
|
RCVD_IN_DNSWL_NONE
|
||||||
|
|
||||||
|
# tflags net
|
||||||
|
RCVD_IN_IADB_COURT
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
RCVD_IN_IADB_DK
|
RCVD_IN_IADB_DK
|
||||||
|
|
||||||
@ -1232,6 +1274,9 @@ RCVD_IN_IADB_EPIA
|
|||||||
# tflags net
|
# tflags net
|
||||||
RCVD_IN_IADB_GOODMAIL
|
RCVD_IN_IADB_GOODMAIL
|
||||||
|
|
||||||
|
# tflags net
|
||||||
|
RCVD_IN_IADB_LEG_MAND
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
RCVD_IN_IADB_LISTED
|
RCVD_IN_IADB_LISTED
|
||||||
|
|
||||||
@ -1454,6 +1499,9 @@ REPTO_419_FRAUD_YN
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
REPTO_INFONUMSCOM
|
REPTO_INFONUMSCOM
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
RISK_FREE
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
SCC_BOGUS_CTE_1
|
SCC_BOGUS_CTE_1
|
||||||
|
|
||||||
@ -1475,6 +1523,9 @@ SCC_ISEMM_LID_1A
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
SCC_ISEMM_LID_1B
|
SCC_ISEMM_LID_1B
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
SCC_SPAMMER_ADDR_2
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
SCC_SPECIAL_GUID
|
SCC_SPECIAL_GUID
|
||||||
|
|
||||||
@ -1487,9 +1538,6 @@ SENDGRID_REDIR_PHISH
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
SEO_SUSP_NTLD
|
SEO_SUSP_NTLD
|
||||||
|
|
||||||
# good enough
|
|
||||||
SERGIO_SUBJECT_VIAGRA01
|
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
SHOPIFY_IMG_NOT_RCVD_SFY
|
SHOPIFY_IMG_NOT_RCVD_SFY
|
||||||
|
|
||||||
@ -1502,6 +1550,15 @@ SHORT_IMG_SUSP_NTLD
|
|||||||
# good enough
|
# good enough
|
||||||
SHORT_SHORTNER
|
SHORT_SHORTNER
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
SHY_OBFU_EXPIRE
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
SHY_OBFU_PASSWORD
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
SPAM_CWINDOWSNET
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
SPF_FAIL
|
SPF_FAIL
|
||||||
|
|
||||||
@ -1557,7 +1614,7 @@ STATIC_XPRIO_OLE
|
|||||||
STOCK_TIP
|
STOCK_TIP
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
STOX_BOUND_090909_B
|
STY_INVIS_DIRECT
|
||||||
|
|
||||||
# tflags userconf
|
# tflags userconf
|
||||||
SUBJECT_IN_BLOCKLIST
|
SUBJECT_IN_BLOCKLIST
|
||||||
@ -1565,6 +1622,9 @@ SUBJECT_IN_BLOCKLIST
|
|||||||
# tflags userconf
|
# tflags userconf
|
||||||
SUBJECT_IN_WELCOMELIST
|
SUBJECT_IN_WELCOMELIST
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
SUBJECT_NEEDS_ENCODING
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
SUBJ_ATTENTION
|
SUBJ_ATTENTION
|
||||||
|
|
||||||
@ -1574,6 +1634,15 @@ SUBJ_BRKN_WORDNUMS
|
|||||||
# tflags net
|
# tflags net
|
||||||
SURBL_BLOCKED
|
SURBL_BLOCKED
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
SUSP_UTF8_WORD_COMBO
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
SUSP_UTF8_WORD_FROM
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
SUSP_UTF8_WORD_MANY
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
SUSP_UTF8_WORD_SUBJ
|
SUSP_UTF8_WORD_SUBJ
|
||||||
|
|
||||||
@ -1592,7 +1661,7 @@ TEQF_USR_IMAGE
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
TEQF_USR_MSGID_HEX
|
TEQF_USR_MSGID_HEX
|
||||||
|
|
||||||
# tflags net
|
# tflags publish
|
||||||
TEQF_USR_MSGID_MALF
|
TEQF_USR_MSGID_MALF
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
@ -1604,12 +1673,18 @@ THIS_IS_ADV_SUSP_NTLD
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
TONLINE_FAKE_DKIM
|
TONLINE_FAKE_DKIM
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
TONOM_EQ_TOLOC_SHRT_SHRTNER
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
TO_EQ_FM_DIRECT_MX
|
TO_EQ_FM_DIRECT_MX
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
TO_EQ_FM_DOM_SPF_FAIL
|
TO_EQ_FM_DOM_SPF_FAIL
|
||||||
|
|
||||||
|
# tflags net
|
||||||
|
TO_EQ_FM_HTML_ONLY
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
TO_EQ_FM_SPF_FAIL
|
TO_EQ_FM_SPF_FAIL
|
||||||
|
|
||||||
@ -1628,7 +1703,7 @@ TO_NO_BRKTS_HTML_IMG
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
TO_NO_BRKTS_HTML_ONLY
|
TO_NO_BRKTS_HTML_ONLY
|
||||||
|
|
||||||
# good enough
|
# tflags net
|
||||||
TO_NO_BRKTS_MSFT
|
TO_NO_BRKTS_MSFT
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
@ -1641,17 +1716,29 @@ TO_NO_BRKTS_PCNT
|
|||||||
TO_TOO_MANY_WFH_01
|
TO_TOO_MANY_WFH_01
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
TVD_PH_BODY_META
|
TT_MSGID_TRUNC
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
TVD_RCVD_SPACE_BRACKET
|
TVD_DOLLARS_US
|
||||||
|
|
||||||
# tflags net
|
# good enough
|
||||||
|
TVD_FROM_1
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
TVD_PH_7
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
TVD_PH_BODY_ACCOUNTS_PRE
|
||||||
|
|
||||||
|
# good enough
|
||||||
TVD_SPACE_ENCODED
|
TVD_SPACE_ENCODED
|
||||||
|
|
||||||
# tflags net
|
# good enough
|
||||||
TVD_SPACE_RATIO_MINFP
|
TVD_SPACE_RATIO_MINFP
|
||||||
|
|
||||||
|
# tflags net
|
||||||
|
TVD_SUBJ_NUM_OBFU_MINFP
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
TW_GIBBERISH_MANY
|
TW_GIBBERISH_MANY
|
||||||
|
|
||||||
@ -1670,6 +1757,9 @@ UNICODE_OBFU_ASC
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
UNICODE_OBFU_ZW
|
UNICODE_OBFU_ZW
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
UNICODE_OBFU_ZW_MANY
|
||||||
|
|
||||||
# tflags userconf
|
# tflags userconf
|
||||||
UNPARSEABLE_RELAY
|
UNPARSEABLE_RELAY
|
||||||
|
|
||||||
@ -1751,9 +1841,6 @@ URIBL_SBL
|
|||||||
# tflags net
|
# tflags net
|
||||||
URIBL_SBL_A
|
URIBL_SBL_A
|
||||||
|
|
||||||
# tflags net
|
|
||||||
URIBL_WS_SURBL
|
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
URIBL_ZEN_BLOCKED
|
URIBL_ZEN_BLOCKED
|
||||||
|
|
||||||
@ -1766,6 +1853,9 @@ URI_ADOBESPARK
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
URI_AZURE_CLOUDAPP
|
URI_AZURE_CLOUDAPP
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
URI_CLOUDFLAREIPFS
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
URI_DASHGOVEDU
|
URI_DASHGOVEDU
|
||||||
|
|
||||||
@ -1802,6 +1892,9 @@ URI_HOST_IN_BLOCKLIST
|
|||||||
# tflags userconf
|
# tflags userconf
|
||||||
URI_HOST_IN_WELCOMELIST
|
URI_HOST_IN_WELCOMELIST
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
URI_IMG_CWINDOWSNET
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
URI_IMG_WP_REDIR
|
URI_IMG_WP_REDIR
|
||||||
|
|
||||||
@ -1814,9 +1907,6 @@ URI_MALWARE_SCMS
|
|||||||
# tflags userconf
|
# tflags userconf
|
||||||
URI_NOVOWEL
|
URI_NOVOWEL
|
||||||
|
|
||||||
# good enough
|
|
||||||
URI_OBFU_DOM
|
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
URI_ONLY_MSGID_MALF
|
URI_ONLY_MSGID_MALF
|
||||||
|
|
||||||
@ -1844,7 +1934,7 @@ URI_WPADMIN
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
URI_WP_DIRINDEX
|
URI_WP_DIRINDEX
|
||||||
|
|
||||||
# tflags net
|
# tflags publish
|
||||||
URI_WP_HACKED
|
URI_WP_HACKED
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
@ -1886,15 +1976,27 @@ USER_IN_WELCOMELIST
|
|||||||
# tflags userconf
|
# tflags userconf
|
||||||
USER_IN_WELCOMELIST_TO
|
USER_IN_WELCOMELIST_TO
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
US_DOLLARS_3
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
VFY_ACCT_NORDNS
|
VFY_ACCT_NORDNS
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
VISTA_COST
|
||||||
|
|
||||||
|
# tflags publish
|
||||||
|
VISTA_TONOM_EQ_TOLOC
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
VPS_NO_NTLD
|
VPS_NO_NTLD
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
WALMART_IMG_NOT_RCVD_WAL
|
WALMART_IMG_NOT_RCVD_WAL
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
WIKI_IMG
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
WORD_INVIS
|
WORD_INVIS
|
||||||
|
|
||||||
@ -1907,20 +2009,26 @@ XFER_LOTSA_MONEY
|
|||||||
# tflags publish
|
# tflags publish
|
||||||
XM_DIGITS_ONLY
|
XM_DIGITS_ONLY
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
XM_LIGHT_HEAVY
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
XM_PHPMAILER_FORGED
|
XM_PHPMAILER_FORGED
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
XM_RANDOM
|
XM_RANDOM
|
||||||
|
|
||||||
|
# good enough
|
||||||
|
XM_RECPTID
|
||||||
|
|
||||||
# tflags net
|
# tflags net
|
||||||
XPRIO
|
XPRIO
|
||||||
|
|
||||||
# tflags publish
|
# tflags publish
|
||||||
XPRIO_SHORT_SUBJ
|
XPRIO_SHORT_SUBJ
|
||||||
|
|
||||||
# good enough
|
# tflags publish
|
||||||
XPRIO_URL_SHORTNER
|
XPRIO_VISTA
|
||||||
|
|
||||||
# good enough
|
# good enough
|
||||||
YOUR_DELIVERY_ADDRESS
|
YOUR_DELIVERY_ADDRESS
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
# HashBL - Query hashed/unhashed strings, emails, uris etc from DNS lists
|
# HashBL - Query hashed/unhashed strings, emails, uris etc from DNS lists
|
||||||
#
|
# NOTE: This was enabled by default in v4.0.0
|
||||||
loadplugin Mail::SpamAssassin::Plugin::HashBL
|
loadplugin Mail::SpamAssassin::Plugin::HashBL
|
||||||
|
|
||||||
# ResourceLimits - assure your spamd child processes
|
# ResourceLimits - assure your spamd child processes
|
||||||
|
24
upstream/rules/v401.pre
Normal file
24
upstream/rules/v401.pre
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# This is the right place to customize your installation of SpamAssassin.
|
||||||
|
#
|
||||||
|
# See 'perldoc Mail::SpamAssassin::Conf' for details of what can be
|
||||||
|
# tweaked.
|
||||||
|
#
|
||||||
|
# This file was installed during the installation of SpamAssassin 4.0.0,
|
||||||
|
# and contains plugin loading commands for the new plugins added in that
|
||||||
|
# release. It will not be overwritten during future SpamAssassin installs,
|
||||||
|
# so you can modify it to enable some disabled-by-default plugins below,
|
||||||
|
# if you so wish.
|
||||||
|
#
|
||||||
|
# There are now multiple files read to enable plugins in the
|
||||||
|
# /etc/mail/spamassassin directory; previously only one, "init.pre" was
|
||||||
|
# read. Now both "init.pre", "v310.pre", and any other files ending in
|
||||||
|
# ".pre" will be read. As future releases are made, new plugins will be
|
||||||
|
# added to new files, named according to the release they're added in.
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
# AuthRes - use Authentication-Results header fields
|
||||||
|
#
|
||||||
|
# This plugin parses Authentication-Results header fields and can supply
|
||||||
|
# the results obtained to other plugins.
|
||||||
|
#
|
||||||
|
# loadplugin Mail::SpamAssassin::Plugin::AuthRes
|
@ -41,7 +41,7 @@ BEGIN { # see comments in "spamassassin.raw" for doco
|
|||||||
|
|
||||||
sub usage {
|
sub usage {
|
||||||
die "
|
die "
|
||||||
usage: sa-awl [--clean] [--min n] [dbfile]
|
usage: sa-awl [--clean] [--dry-run] [--min n] [dbfile]
|
||||||
";
|
";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,10 +51,11 @@ use POSIX qw(locale_h setsid sigprocmask _exit);
|
|||||||
|
|
||||||
POSIX::setlocale(LC_TIME,'C');
|
POSIX::setlocale(LC_TIME,'C');
|
||||||
|
|
||||||
our ( $opt_clean, $opt_min, $opt_help );
|
our ( $opt_clean, $opt_dryrun, $opt_min, $opt_help );
|
||||||
|
|
||||||
GetOptions(
|
GetOptions(
|
||||||
'clean' => \$opt_clean,
|
'clean' => \$opt_clean,
|
||||||
|
'dry-run' => \$opt_dryrun,
|
||||||
'min:i' => \$opt_min,
|
'min:i' => \$opt_min,
|
||||||
'help' => \$opt_help
|
'help' => \$opt_help
|
||||||
) or usage();
|
) or usage();
|
||||||
@ -79,7 +80,7 @@ if ($#ARGV == -1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
my %h;
|
my %h;
|
||||||
if ($opt_clean) {
|
if ($opt_clean and not $opt_dryrun) {
|
||||||
tie %h, "AnyDBM_File",$db, O_RDWR,0600
|
tie %h, "AnyDBM_File",$db, O_RDWR,0600
|
||||||
or die "Cannot open r/w file $db: $!\n";
|
or die "Cannot open r/w file $db: $!\n";
|
||||||
} else {
|
} else {
|
||||||
@ -87,11 +88,15 @@ if ($opt_clean) {
|
|||||||
or die "Cannot open file $db: $!\n";
|
or die "Cannot open file $db: $!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$opt_clean) { # just pretend to be cleaning
|
if (not $opt_clean or $opt_dryrun) { # just pretend to be cleaning
|
||||||
while (my($key, $count) = each %h) {
|
while (my($key, $count) = each %h) {
|
||||||
next if $key =~ /totscore$/;
|
next if $key =~ /totscore$/;
|
||||||
|
next if (($count >= $opt_min) and ($opt_dryrun and $opt_clean));
|
||||||
my $totscore = $h{"$key|totscore"};
|
my $totscore = $h{"$key|totscore"};
|
||||||
next unless defined($totscore);
|
next unless defined($totscore);
|
||||||
|
if($opt_dryrun and $opt_clean) {
|
||||||
|
printf("cleaning [dry-run]: ");
|
||||||
|
}
|
||||||
printf("%8.1f %15s -- %s\n",
|
printf("%8.1f %15s -- %s\n",
|
||||||
$totscore/$count, sprintf("(%.1f/%d)",$totscore,$count), $key);
|
$totscore/$count, sprintf("(%.1f/%d)",$totscore,$count), $key);
|
||||||
}
|
}
|
||||||
@ -130,7 +135,7 @@ sa-awl - examine and manipulate SpamAssassin's auto-welcomelist db
|
|||||||
|
|
||||||
=head1 SYNOPSIS
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
B<sa-awl> [--clean] [--min n] [dbfile]
|
B<sa-awl> [--clean] [--dry-run] [--min n] [dbfile]
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
@ -148,6 +153,12 @@ The default is C<$HOME/.spamassassin/auto-welcomelist>.
|
|||||||
Clean out infrequently-used AWL entries. The C<--min> switch can be
|
Clean out infrequently-used AWL entries. The C<--min> switch can be
|
||||||
used to select the threshold at which entries are kept or deleted.
|
used to select the threshold at which entries are kept or deleted.
|
||||||
|
|
||||||
|
=item --dry-run
|
||||||
|
|
||||||
|
When specified with th C<--clean> option it displays the infrequently-used AWL entries
|
||||||
|
that will be deleted. The C<--min> switch can be
|
||||||
|
used to select the threshold at which entries are kept or deleted.
|
||||||
|
|
||||||
=item --min n
|
=item --min n
|
||||||
|
|
||||||
Select the threshold at which entries are kept or deleted when C<--clean> is
|
Select the threshold at which entries are kept or deleted when C<--clean> is
|
||||||
|
@ -455,7 +455,7 @@ C<Mail::SpamAssassin> version 3.1.1 or higher (3.1.6 or higher recommended)
|
|||||||
|
|
||||||
=head1 AUTHOR
|
=head1 AUTHOR
|
||||||
|
|
||||||
Daryl C. W. O'Shea, DOS Technologies <spamassassin@dostech.ca>
|
Daryl C. W. O'Shea, DOS Technologies E<lt>spamassassin@dostech.caE<gt>
|
||||||
|
|
||||||
=head1 COPYRIGHT AND LICENSE
|
=head1 COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
|
@ -255,6 +255,7 @@ sub compile_base_strings {
|
|||||||
die "secure_tmpdir failed" unless $dirpath && -w $dirpath;
|
die "secure_tmpdir failed" unless $dirpath && -w $dirpath;
|
||||||
|
|
||||||
my $sudo = ($opt{sudo} ? 'sudo ' : '');
|
my $sudo = ($opt{sudo} ? 'sudo ' : '');
|
||||||
|
delete $ENV{'PERL_MM_OPT'}; # bug 8199
|
||||||
|
|
||||||
foreach my $ruletype (sort keys %{$conf->{base_orig}})
|
foreach my $ruletype (sort keys %{$conf->{base_orig}})
|
||||||
{
|
{
|
||||||
@ -803,11 +804,11 @@ C<Mail::SpamAssassin::Plugin::Rule2XSBody>
|
|||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
||||||
|
|
||||||
See <https://issues.apache.org/SpamAssassin/>
|
See E<lt>https://issues.apache.org/SpamAssassin/E<gt>
|
||||||
|
|
||||||
=head1 AUTHORS
|
=head1 AUTHORS
|
||||||
|
|
||||||
The Apache SpamAssassin(tm) Project <https://spamassassin.apache.org/>
|
The Apache SpamAssassin(tm) Project E<lt>https://spamassassin.apache.org/E<gt>
|
||||||
|
|
||||||
=head1 LICENSE AND COPYRIGHT
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
@ -917,7 +917,7 @@ perform a similar procedure for each one of them.
|
|||||||
|
|
||||||
This will sync any outstanding journal entries
|
This will sync any outstanding journal entries
|
||||||
|
|
||||||
=item sa-learn --backup > backup.txt
|
=item sa-learn --backup E<gt> backup.txt
|
||||||
|
|
||||||
This will save all your Bayes data to a plain text file.
|
This will save all your Bayes data to a plain text file.
|
||||||
|
|
||||||
@ -945,7 +945,7 @@ Again, you need to do this for every database.
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
If you are migrating to SQL you can make use of the -u <username>
|
If you are migrating to SQL you can make use of the -u I<username>
|
||||||
option in sa-learn to populate each user's database. Otherwise, you
|
option in sa-learn to populate each user's database. Otherwise, you
|
||||||
must run sa-learn as the user who database you are restoring.
|
must run sa-learn as the user who database you are restoring.
|
||||||
|
|
||||||
@ -1238,9 +1238,9 @@ or if all of the following are true (opportunistic):
|
|||||||
|
|
||||||
=item - bayes_auto_expire does not equal 0
|
=item - bayes_auto_expire does not equal 0
|
||||||
|
|
||||||
=item - the number of tokens in the DB is > 100,000
|
=item - the number of tokens in the DB is E<gt> 100,000
|
||||||
|
|
||||||
=item - the number of tokens in the DB is > bayes_expiry_max_db_size
|
=item - the number of tokens in the DB is E<gt> bayes_expiry_max_db_size
|
||||||
|
|
||||||
=item - there is at least a 12 hr difference between the oldest and newest token atimes
|
=item - there is at least a 12 hr difference between the oldest and newest token atimes
|
||||||
|
|
||||||
@ -1276,7 +1276,7 @@ old_atime_delta * old_reduction_count / goal)
|
|||||||
|
|
||||||
=item - estimated new atime delta is < 12 hrs
|
=item - estimated new atime delta is < 12 hrs
|
||||||
|
|
||||||
=item - the difference between the last reduction count and the goal reduction count is > 50%
|
=item - the difference between the last reduction count and the goal reduction count is E<gt> 50%
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
|
@ -22,11 +22,11 @@ use warnings;
|
|||||||
use re 'taint';
|
use re 'taint';
|
||||||
|
|
||||||
my $VERSION = 'svnunknown';
|
my $VERSION = 'svnunknown';
|
||||||
if ('$Id: sa-update.raw 1900642 2022-05-07 06:01:02Z hege $' =~ ':') {
|
if ('$Id: sa-update.raw 1914735 2023-12-17 09:11:35Z sidney $' =~ ':') {
|
||||||
# Subversion keyword "$Id: sa-update.raw 1900642 2022-05-07 06:01:02Z hege $" has been successfully expanded.
|
# Subversion keyword "$Id: sa-update.raw 1914735 2023-12-17 09:11:35Z sidney $" has been successfully expanded.
|
||||||
# Doesn't happen with automated launchpad builds:
|
# Doesn't happen with automated launchpad builds:
|
||||||
# https://bugs.launchpad.net/launchpad/+bug/780916
|
# https://bugs.launchpad.net/launchpad/+bug/780916
|
||||||
$VERSION = &Mail::SpamAssassin::Version . ' / svn' . (split(/\s+/, '$Id: sa-update.raw 1900642 2022-05-07 06:01:02Z hege $'))[2];
|
$VERSION = &Mail::SpamAssassin::Version . ' / svn' . (split(/\s+/, '$Id: sa-update.raw 1914735 2023-12-17 09:11:35Z sidney $'))[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
my $PREFIX = '@@PREFIX@@'; # substituted at 'make' time
|
my $PREFIX = '@@PREFIX@@'; # substituted at 'make' time
|
||||||
@ -100,6 +100,7 @@ BEGIN {
|
|||||||
|
|
||||||
$have_lwp = eval {
|
$have_lwp = eval {
|
||||||
require LWP::UserAgent;
|
require LWP::UserAgent;
|
||||||
|
require LWP::Protocol::https;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (eval { require IO::Socket::IP }) { # handles IPv6 and IPv4
|
if (eval { require IO::Socket::IP }) { # handles IPv6 and IPv4
|
||||||
@ -1458,7 +1459,7 @@ sub do_dns_query {
|
|||||||
next if !$rr; # no answer records, only rcode
|
next if !$rr; # no answer records, only rcode
|
||||||
next if $rr->type ne $rr_type;
|
next if $rr->type ne $rr_type;
|
||||||
# scalar context!
|
# scalar context!
|
||||||
my $text = $rr->UNIVERSAL::can('txtdata') ? $rr->txtdata : $rr->rdatastr;
|
my $text = $rr->UNIVERSAL::can('txtdata') ? $rr->txtdata : $rr->rdstring;
|
||||||
push(@result,$text) if defined $text && $text ne '';
|
push(@result,$text) if defined $text && $text ne '';
|
||||||
}
|
}
|
||||||
printf("DNS %s query: %s -> %s\n", $rr_type, $query, join(", ",@result))
|
printf("DNS %s query: %s -> %s\n", $rr_type, $query, join(", ",@result))
|
||||||
@ -2056,6 +2057,19 @@ configuration, based on channels. The default channel is
|
|||||||
I<updates.spamassassin.org>, which has updated rules since the previous
|
I<updates.spamassassin.org>, which has updated rules since the previous
|
||||||
release.
|
release.
|
||||||
|
|
||||||
|
NOTE: channel names are domain names, but DO NOT typically have any DNS
|
||||||
|
records other than (maybe) NS records. There is a tree of records below that
|
||||||
|
name which denote the SpamAssassin version and resolve that name to the
|
||||||
|
version number of the latest rules, e.g. to find the latest update
|
||||||
|
version number for SpamAssassin v4.0.0:
|
||||||
|
|
||||||
|
$ host -t txt 0.0.4.updates.spamassassin.org
|
||||||
|
0.0.4.updates.spamassassin.org is an alias for 3.3.3.updates.spamassassin.org.
|
||||||
|
3.3.3.updates.spamassassin.org descriptive text "1907730"
|
||||||
|
|
||||||
|
That also illuminates the fact that the current ruleset is supposed to be
|
||||||
|
backward-compatible to v3.3.3.
|
||||||
|
|
||||||
Update archives are verified using GPG signatures by default. If GPG is
|
Update archives are verified using GPG signatures by default. If GPG is
|
||||||
disabled (not recommended), file integrity is checked with SHA512 or SHA256
|
disabled (not recommended), file integrity is checked with SHA512 or SHA256
|
||||||
checksums.
|
checksums.
|
||||||
@ -2265,7 +2279,7 @@ Mail::SpamAssassin(3)
|
|||||||
Mail::SpamAssassin::Conf(3)
|
Mail::SpamAssassin::Conf(3)
|
||||||
spamassassin(1)
|
spamassassin(1)
|
||||||
spamd(1)
|
spamd(1)
|
||||||
<https://wiki.apache.org/spamassassin/RuleUpdates>
|
E<lt>https://wiki.apache.org/spamassassin/RuleUpdatesE<gt>
|
||||||
|
|
||||||
=head1 PREREQUISITES
|
=head1 PREREQUISITES
|
||||||
|
|
||||||
@ -2273,11 +2287,11 @@ C<Mail::SpamAssassin>
|
|||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
||||||
|
|
||||||
See <https://issues.apache.org/SpamAssassin/>
|
See E<lt>https://issues.apache.org/SpamAssassin/E<gt>
|
||||||
|
|
||||||
=head1 AUTHORS
|
=head1 AUTHORS
|
||||||
|
|
||||||
The Apache SpamAssassin(tm) Project <https://spamassassin.apache.org/>
|
The Apache SpamAssassin(tm) Project E<lt>https://spamassassin.apache.org/E<gt>
|
||||||
|
|
||||||
=head1 LICENSE AND COPYRIGHT
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
@ -182,6 +182,7 @@ GetOptions(
|
|||||||
'add-to-welcomelist|W' => \$opt{'add-to-welcomelist'},
|
'add-to-welcomelist|W' => \$opt{'add-to-welcomelist'},
|
||||||
'add-to-blacklist' => \$opt{'add-to-blocklist'}, # removed in 4.1
|
'add-to-blacklist' => \$opt{'add-to-blocklist'}, # removed in 4.1
|
||||||
'add-to-whitelist' => \$opt{'add-to-welcomelist'}, # removed in 4.1
|
'add-to-whitelist' => \$opt{'add-to-welcomelist'}, # removed in 4.1
|
||||||
|
'username|u=s' => \$opt{'username'},
|
||||||
'configpath|config-file|config-dir|c|C=s' => \$opt{'configpath'},
|
'configpath|config-file|config-dir|c|C=s' => \$opt{'configpath'},
|
||||||
'create-prefs!' => \$opt{'create-prefs'},
|
'create-prefs!' => \$opt{'create-prefs'},
|
||||||
'pre=s' => \@{$opt{'pre'}},
|
'pre=s' => \@{$opt{'pre'}},
|
||||||
@ -265,6 +266,7 @@ my $spamtest = Mail::SpamAssassin->new(
|
|||||||
rules_filename => $opt{'configpath'},
|
rules_filename => $opt{'configpath'},
|
||||||
site_rules_filename => $opt{'siteconfigpath'},
|
site_rules_filename => $opt{'siteconfigpath'},
|
||||||
userprefs_filename => $opt{'prefspath'},
|
userprefs_filename => $opt{'prefspath'},
|
||||||
|
username => $opt{'username'},
|
||||||
force_ipv4 => $opt{'force_ipv4'},
|
force_ipv4 => $opt{'force_ipv4'},
|
||||||
force_ipv6 => $opt{'force_ipv6'},
|
force_ipv6 => $opt{'force_ipv6'},
|
||||||
local_tests_only => $opt{'local'},
|
local_tests_only => $opt{'local'},
|
||||||
@ -288,7 +290,7 @@ if ($opt{'lint'}) {
|
|||||||
# make sure we notice any write errors while flushing output buffer
|
# make sure we notice any write errors while flushing output buffer
|
||||||
close STDOUT or die "error closing STDOUT: $!";
|
close STDOUT or die "error closing STDOUT: $!";
|
||||||
close STDIN or die "error closing STDIN: $!";
|
close STDIN or die "error closing STDIN: $!";
|
||||||
exit $res ? 1 : 0;
|
exit($res ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($opt{'remove-addr-from-welcomelist'} ||
|
if ($opt{'remove-addr-from-welcomelist'} ||
|
||||||
@ -894,11 +896,11 @@ from the SpamAssassin distribution.
|
|||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
||||||
|
|
||||||
See <https://issues.apache.org/SpamAssassin/>
|
See E<lt>https://issues.apache.org/SpamAssassin/E<gt>
|
||||||
|
|
||||||
=head1 AUTHORS
|
=head1 AUTHORS
|
||||||
|
|
||||||
The SpamAssassin(tm) Project <https://spamassassin.apache.org/>
|
The SpamAssassin(tm) Project E<lt>https://spamassassin.apache.org/E<gt>
|
||||||
|
|
||||||
=head1 COPYRIGHT AND LICENSE
|
=head1 COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
|
@ -33,6 +33,10 @@ To build SpamAssassin you must have installed a Windows version of Perl
|
|||||||
and the modules that are listed as required in the general SpamAssassin
|
and the modules that are listed as required in the general SpamAssassin
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
|
Strawberry Perl provides a C compiler that is enough to build spamc.exe.
|
||||||
|
If you want to use Microsoft Visual C++ instead, you should download
|
||||||
|
Microsoft Visual C++ Toolkit from Microsoft website.
|
||||||
|
|
||||||
Building spamc for Windows has been tested with Microsoft Visual C++ 6.0
|
Building spamc for Windows has been tested with Microsoft Visual C++ 6.0
|
||||||
and with Microsoft Visual C++ Toolkit 2003. It will probably just work
|
and with Microsoft Visual C++ Toolkit 2003. It will probably just work
|
||||||
with any recent version of VC++. Some installation files would have to
|
with any recent version of VC++. Some installation files would have to
|
||||||
@ -73,7 +77,7 @@ by unpacking the source files into some directory, for example,
|
|||||||
/usr/local/src/spamassassin, then in a Cygwin bash shell
|
/usr/local/src/spamassassin, then in a Cygwin bash shell
|
||||||
|
|
||||||
cd /usr/local/src/spamassassin
|
cd /usr/local/src/spamassassin
|
||||||
perl Makefile.PL
|
perl Makefile.PL BUILD_SPAMC=yes
|
||||||
make
|
make
|
||||||
make test
|
make test
|
||||||
make install
|
make install
|
||||||
@ -91,7 +95,7 @@ Building the Windows version
|
|||||||
Unpack the SpamAssassin source tree into a different directory than you
|
Unpack the SpamAssassin source tree into a different directory than you
|
||||||
used for building the Cygwin version, for example C:\spamassassin\.
|
used for building the Cygwin version, for example C:\spamassassin\.
|
||||||
|
|
||||||
Start up a Widows command shell. In the shell, set the environment
|
Start up a Windows command shell. In the shell, set the environment
|
||||||
variable SPAMD_HOST to be the host name or ip address of the spamd
|
variable SPAMD_HOST to be the host name or ip address of the spamd
|
||||||
server to be used for testing. In this case you would use the command
|
server to be used for testing. In this case you would use the command
|
||||||
|
|
||||||
@ -102,9 +106,11 @@ environment variable SPAMD_PORT. To use SSL during the test,
|
|||||||
|
|
||||||
set SC_ARGS=-S
|
set SC_ARGS=-S
|
||||||
|
|
||||||
Make sure that the environment is set up for running VC++. In VC++ 6.0
|
If you want to build spamc using Microsoft VC++, make sure that
|
||||||
there is a batch file created during installation that sets the
|
the environment is set up for running VC++.
|
||||||
environment. In a typical installation that would be found at
|
In VC++ 6.0 there is a batch file created during installation that
|
||||||
|
sets the environment.
|
||||||
|
In a typical installation that would be found at
|
||||||
|
|
||||||
"\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"
|
"\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"
|
||||||
|
|
||||||
|
@ -46,9 +46,6 @@
|
|||||||
/* Define to 1 if you have the `z' library (-lz). */
|
/* Define to 1 if you have the `z' library (-lz). */
|
||||||
#undef HAVE_LIBZ
|
#undef HAVE_LIBZ
|
||||||
|
|
||||||
/* Define to 1 if you have the <memory.h> header file. */
|
|
||||||
#undef HAVE_MEMORY_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <netdb.h> header file. */
|
/* Define to 1 if you have the <netdb.h> header file. */
|
||||||
#undef HAVE_NETDB_H
|
#undef HAVE_NETDB_H
|
||||||
|
|
||||||
@ -76,6 +73,9 @@
|
|||||||
/* Define to 1 if you have the <stdint.h> header file. */
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
#undef HAVE_STDINT_H
|
#undef HAVE_STDINT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdio.h> header file. */
|
||||||
|
#undef HAVE_STDIO_H
|
||||||
|
|
||||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
#undef HAVE_STDLIB_H
|
#undef HAVE_STDLIB_H
|
||||||
|
|
||||||
@ -136,10 +136,15 @@
|
|||||||
/* Define to the one symbol short name of this package. */
|
/* Define to the one symbol short name of this package. */
|
||||||
#undef PACKAGE_TARNAME
|
#undef PACKAGE_TARNAME
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#undef PACKAGE_URL
|
||||||
|
|
||||||
/* Define to the version of this package. */
|
/* Define to the version of this package. */
|
||||||
#undef PACKAGE_VERSION
|
#undef PACKAGE_VERSION
|
||||||
|
|
||||||
/* Define to 1 if you have the ANSI C header files. */
|
/* Define to 1 if all of the C90 standard headers exist (not just the ones
|
||||||
|
required in a freestanding environment). This macro is provided for
|
||||||
|
backward compatibility; new code need not use it. */
|
||||||
#undef STDC_HEADERS
|
#undef STDC_HEADERS
|
||||||
|
|
||||||
/* Define to empty if `const' does not conform to ANSI C. */
|
/* Define to empty if `const' does not conform to ANSI C. */
|
||||||
@ -151,13 +156,13 @@
|
|||||||
/* Define to `unsigned long' if <sys/types.h> does not define. */
|
/* Define to `unsigned long' if <sys/types.h> does not define. */
|
||||||
#undef in_addr_t
|
#undef in_addr_t
|
||||||
|
|
||||||
/* Define to `long' if <sys/types.h> does not define. */
|
/* Define to `long int' if <sys/types.h> does not define. */
|
||||||
#undef off_t
|
#undef off_t
|
||||||
|
|
||||||
/* Define to `int' if <sys/types.h> does not define. */
|
/* Define as a signed integer type capable of holding a process identifier. */
|
||||||
#undef pid_t
|
#undef pid_t
|
||||||
|
|
||||||
/* Define to `unsigned' if <sys/types.h> does not define. */
|
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||||
#undef size_t
|
#undef size_t
|
||||||
|
|
||||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||||
|
8807
upstream/spamc/configure
vendored
8807
upstream/spamc/configure
vendored
File diff suppressed because it is too large
Load Diff
@ -88,20 +88,34 @@ else
|
|||||||
{
|
{
|
||||||
# These are the defaults for the Makefile.
|
# These are the defaults for the Makefile.
|
||||||
my %env = (
|
my %env = (
|
||||||
CC => 'cl',
|
CC => 'gcc',
|
||||||
|
|
||||||
WINCFLAGS => '/DWIN32 /W4',
|
WINCFLAGS => '',
|
||||||
SSLCFLAGS => '/DSPAMC_SSL',
|
SSLCFLAGS => '-DSPAMC_SSL',
|
||||||
|
|
||||||
SRCDIR => $srcdir,
|
SRCDIR => $srcdir,
|
||||||
|
|
||||||
WINLIBS => 'ws2_32.lib',
|
WINLIBS => '-lws2_32 -o spamc.exe',
|
||||||
SSLLIBS => 'ssleay32.lib libeay32.lib',
|
SSLLIBS => '-lcrypto -lssl',
|
||||||
|
|
||||||
SPAMC_FILES => 'spamc.c getopt.c',
|
SPAMC_FILES => 'spamc.c getopt.c',
|
||||||
LIBSPAMC_FILES => 'libspamc.c utils.c',
|
LIBSPAMC_FILES => 'libspamc.c utils.c',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
my $cl_found = 0;
|
||||||
|
foreach my $path (File::Spec->path()) {
|
||||||
|
if( -x $path . '\cl.exe' ) {
|
||||||
|
$cl_found = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($cl_found) {
|
||||||
|
$env{'CC'} = 'cl';
|
||||||
|
$env{'WINCFLAGS'} = '/DWIN32 /W4';
|
||||||
|
$env{'SSLCFLAGS'} = '/DSPAMC_SSL';
|
||||||
|
$env{'WINLIBS'} = 'ws2_32.lib';
|
||||||
|
$env{'SSLLIBS'} = 'ssleay32.lib libeay32.lib';
|
||||||
|
}
|
||||||
|
|
||||||
# Enable SSL only if requested.
|
# Enable SSL only if requested.
|
||||||
if ($args{'enable-ssl'} and $args{'enable-ssl'} ne 'yes') {
|
if ($args{'enable-ssl'} and $args{'enable-ssl'} ne 'yes') {
|
||||||
delete $env{SSLCFLAGS};
|
delete $env{SSLCFLAGS};
|
||||||
|
@ -124,9 +124,9 @@ Send log messages to stderr, instead of to the syslog.
|
|||||||
=item B<-L> I<learn type>, B<--learntype>=I<type>
|
=item B<-L> I<learn type>, B<--learntype>=I<type>
|
||||||
|
|
||||||
Send message to spamd for learning. The C<learn type> can be either spam,
|
Send message to spamd for learning. The C<learn type> can be either spam,
|
||||||
ham or forget. The exitcode for spamc will be set to 5 if the message
|
ham or forget. The exitcode for spamc will be set to 0 if the message
|
||||||
was learned, or 6 if it was already learned, under a condition that
|
was learned or if it had already been learned. Non-zero exitcodes indicate an
|
||||||
a B<--no-safe-fallback> option is selected too.
|
actual failure of some sort.
|
||||||
|
|
||||||
Note that the C<spamd> must run with the C<--allow-tell> option for
|
Note that the C<spamd> must run with the C<--allow-tell> option for
|
||||||
this to work.
|
this to work.
|
||||||
@ -373,7 +373,7 @@ C<Mail::SpamAssassin>
|
|||||||
|
|
||||||
=head1 AUTHORS
|
=head1 AUTHORS
|
||||||
|
|
||||||
The SpamAssassin(tm) Project <https://spamassassin.apache.org/>
|
The SpamAssassin(tm) Project E<lt>https://spamassassin.apache.org/E<gt>
|
||||||
|
|
||||||
=head1 COPYRIGHT
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
@ -621,7 +621,7 @@ The following methods must be overloaded:
|
|||||||
|
|
||||||
Information about the client.
|
Information about the client.
|
||||||
|
|
||||||
=item C<new( spamtest => $sa_object, foo => 'bar', ... )>
|
=item C<new( spamtest =E<gt> $sa_object, foo =E<gt> 'bar', ... )>
|
||||||
|
|
||||||
Creates new object; C<shift && bless { @_ }>, basically.
|
Creates new object; C<shift && bless { @_ }>, basically.
|
||||||
|
|
||||||
@ -632,7 +632,7 @@ chdir, set $ENV, etc. Do not call this directly.
|
|||||||
|
|
||||||
=item C<read_body()>
|
=item C<read_body()>
|
||||||
|
|
||||||
Read body from the client, run $self->spamtest->parse and store result
|
Read body from the client, run $self-E<gt>spamtest-E<gt>parse and store result
|
||||||
as the C<parsed> key.
|
as the C<parsed> key.
|
||||||
|
|
||||||
=item C<read_headers()>
|
=item C<read_headers()>
|
||||||
|
@ -377,7 +377,7 @@ NetSet
|
|||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
||||||
|
|
||||||
See <http://bugzilla.spamassassin.org/>.
|
See E<lt>http://bugzilla.spamassassin.org/E<gt>.
|
||||||
|
|
||||||
=head1 SEE ALSO
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ DoS this way.
|
|||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
||||||
|
|
||||||
See <http://bugzilla.spamassassin.org/>
|
See E<lt>http://bugzilla.spamassassin.org/E<gt>
|
||||||
|
|
||||||
=head1 SEE ALSO
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ Mail::SpamAssassin::Spamd::Apache2::AclRFC1413 - check spamd's client ident
|
|||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
Queries remote ident server using mod_ident.so, saves result in
|
Queries remote ident server using mod_ident.so, saves result in
|
||||||
C<$c->notes()>.
|
C<$c-E<gt>notes()>.
|
||||||
|
|
||||||
Returns C<Apache2::Const::FORBIDDEN> on failure.
|
Returns C<Apache2::Const::FORBIDDEN> on failure.
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ Nothing.
|
|||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
||||||
|
|
||||||
See <http://bugzilla.spamassassin.org/>
|
See E<lt>http://bugzilla.spamassassin.org/E<gt>
|
||||||
|
|
||||||
=head1 SEE ALSO
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ push @directives, { # inherited
|
|||||||
|
|
||||||
=item C<SANew key "value">
|
=item C<SANew key "value">
|
||||||
|
|
||||||
Additional arguments to C<Mail::SpamAssassin->new()>. Refer to
|
Additional arguments to C<Mail::SpamAssassin-E<gt>new()>. Refer to
|
||||||
C<Mail::SpamAssassin(3)>.
|
C<Mail::SpamAssassin(3)>.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
@ -559,7 +559,7 @@ C<@INC>, you can use something like:
|
|||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
||||||
|
|
||||||
See <http://bugzilla.spamassassin.org/>.
|
See E<lt>http://bugzilla.spamassassin.org/E<gt>.
|
||||||
|
|
||||||
=head1 SEE ALSO
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ disable something else for whatever reason... well, keep that in mind.
|
|||||||
|
|
||||||
Error messages are not unified.
|
Error messages are not unified.
|
||||||
|
|
||||||
See <http://bugzilla.spamassassin.org/>
|
See E<lt>http://bugzilla.spamassassin.org/E<gt>
|
||||||
|
|
||||||
=head1 SEE ALSO
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ our ($have_getaddrinfo_in_core, $have_getaddrinfo_legacy, $io_socket_module_name
|
|||||||
|
|
||||||
# don't force requirement on IO::Socket::IP or IO::Socket::INET6
|
# don't force requirement on IO::Socket::IP or IO::Socket::INET6
|
||||||
BEGIN {
|
BEGIN {
|
||||||
|
require Socket;
|
||||||
$have_getaddrinfo_in_core = eval {
|
$have_getaddrinfo_in_core = eval {
|
||||||
# The Socket module (1.94) bundled with Perl 5.14.* provides
|
# The Socket module (1.94) bundled with Perl 5.14.* provides
|
||||||
# new affordances for IPv6, including implementations of the
|
# new affordances for IPv6, including implementations of the
|
||||||
@ -83,7 +84,6 @@ BEGIN {
|
|||||||
&NI_NUMERICHOST; &NI_NUMERICSERV; &NI_NAMEREQD; 1;
|
&NI_NUMERICHOST; &NI_NUMERICSERV; &NI_NAMEREQD; 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
require Socket;
|
|
||||||
Socket->import(qw(:DEFAULT IPPROTO_TCP));
|
Socket->import(qw(:DEFAULT IPPROTO_TCP));
|
||||||
|
|
||||||
&SOCK_STREAM; &IPPROTO_TCP; &SOMAXCONN; # enable inlining
|
&SOCK_STREAM; &IPPROTO_TCP; &SOMAXCONN; # enable inlining
|
||||||
@ -3294,6 +3294,9 @@ sub do_sighup_restart {
|
|||||||
chdir($ORIG_CWD)
|
chdir($ORIG_CWD)
|
||||||
or die "spamd: restart failed: chdir failed: ${ORIG_CWD}: $!\n";
|
or die "spamd: restart failed: chdir failed: ${ORIG_CWD}: $!\n";
|
||||||
|
|
||||||
|
# Close GeoDB, Geo::IP leaks fds on restart (Bug 8127)
|
||||||
|
delete $spamtest->{geodb};
|
||||||
|
|
||||||
# ensure we re-run spamd using the right perl interpreter, and
|
# ensure we re-run spamd using the right perl interpreter, and
|
||||||
# with the right switches (taint mode and warnings) (bug 5255)
|
# with the right switches (taint mode and warnings) (bug 5255)
|
||||||
# Also need -I options (bug 8030) because there is no way
|
# Also need -I options (bug 8030) because there is no way
|
||||||
@ -3476,12 +3479,12 @@ Print a brief help message, then exit without further action.
|
|||||||
|
|
||||||
Print version information, then exit without further action.
|
Print version information, then exit without further action.
|
||||||
|
|
||||||
=item B<-i> [I<ipaddress>[:<port>]], B<--listen>[=I<ipaddress>[:<port>]]
|
=item B<-i> [I<ipaddress>[:E<lt>portE<gt>]], B<--listen>[=I<ipaddress>[:E<lt>portE<gt>]]
|
||||||
|
|
||||||
Additional alias names for this option are --listen-ip and --ip-address.
|
Additional alias names for this option are --listen-ip and --ip-address.
|
||||||
Tells spamd to listen on the specified IP address, defaults to a loopback
|
Tells spamd to listen on the specified IP address, defaults to a loopback
|
||||||
interface, i.e. C<--listen localhost>). If no value is specified after the
|
interface, i.e. C<--listen localhost>). If no value is specified after the
|
||||||
switch, or if an asterisk '*' stands in place of an <ipaddress>, spamd will
|
switch, or if an asterisk '*' stands in place of an E<lt>ipaddressE<gt>, spamd will
|
||||||
listen on all interfaces - this is equivalent to address '0.0.0.0' for IPv4
|
listen on all interfaces - this is equivalent to address '0.0.0.0' for IPv4
|
||||||
and to '::' for IPv6. You can also use a valid hostname which will make spamd
|
and to '::' for IPv6. You can also use a valid hostname which will make spamd
|
||||||
listen on all addresses that a name resolves to. The option may be specified
|
listen on all addresses that a name resolves to. The option may be specified
|
||||||
|
@ -130,7 +130,7 @@ CREATE TABLE userpref (
|
|||||||
prefid int(11) NOT NULL auto_increment,
|
prefid int(11) NOT NULL auto_increment,
|
||||||
PRIMARY KEY (prefid),
|
PRIMARY KEY (prefid),
|
||||||
INDEX (username)
|
INDEX (username)
|
||||||
) TYPE=MyISAM;
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
For PostgreSQL, use the following command:
|
For PostgreSQL, use the following command:
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ CREATE TABLE awl (
|
|||||||
totscore float NOT NULL default '0',
|
totscore float NOT NULL default '0',
|
||||||
signedby varchar(255) NOT NULL default '',
|
signedby varchar(255) NOT NULL default '',
|
||||||
PRIMARY KEY (username,email,signedby,ip)
|
PRIMARY KEY (username,email,signedby,ip)
|
||||||
) TYPE=MyISAM;
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
|
||||||
For PostgreSQL, use the following:
|
For PostgreSQL, use the following:
|
||||||
|
@ -15,7 +15,7 @@ CREATE TRIGGER [UpdateLastHit]
|
|||||||
AFTER UPDATE
|
AFTER UPDATE
|
||||||
ON txrep
|
ON txrep
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
WHEN NEW.last_hit < OLD.last_hit
|
WHEN NEW.last_hit <= OLD.last_hit
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE txrep SET last_hit=CURRENT_TIMESTAMP
|
UPDATE txrep SET last_hit=CURRENT_TIMESTAMP
|
||||||
WHERE (username=OLD.username AND email=OLD.email AND signedby=OLD.signedby AND ip=OLD.ip);
|
WHERE (username=OLD.username AND email=OLD.email AND signedby=OLD.signedby AND ip=OLD.ip);
|
||||||
|
@ -5,4 +5,4 @@ CREATE TABLE userpref (
|
|||||||
prefid int(11) NOT NULL auto_increment,
|
prefid int(11) NOT NULL auto_increment,
|
||||||
PRIMARY KEY (prefid),
|
PRIMARY KEY (prefid),
|
||||||
KEY username (username)
|
KEY username (username)
|
||||||
) TYPE=MyISAM;
|
) ENGINE=InnoDB;
|
||||||
|
@ -203,9 +203,8 @@ sub sa_t_init {
|
|||||||
(-f "t/test_dir") && chdir("t"); # run from ..
|
(-f "t/test_dir") && chdir("t"); # run from ..
|
||||||
-f "test_dir" or die "FATAL: not in test directory?\n";
|
-f "test_dir" or die "FATAL: not in test directory?\n";
|
||||||
|
|
||||||
unless (-d "log") {
|
mkdir ("log", 0755);
|
||||||
mkdir ("log", 0755) or die ("Error creating log dir: $!");
|
-d "log" or die "FATAL: failed to create log dir\n";
|
||||||
}
|
|
||||||
chmod (0755, "log"); # set in case log already exists with wrong permissions
|
chmod (0755, "log"); # set in case log already exists with wrong permissions
|
||||||
|
|
||||||
if (!$RUNNING_ON_WINDOWS) {
|
if (!$RUNNING_ON_WINDOWS) {
|
||||||
@ -222,6 +221,7 @@ sub sa_t_init {
|
|||||||
# individual work directory to make parallel tests possible
|
# individual work directory to make parallel tests possible
|
||||||
$workdir = tempdir("$tname.XXXXXX", DIR => "log");
|
$workdir = tempdir("$tname.XXXXXX", DIR => "log");
|
||||||
die "FATAL: failed to create workdir: $!" unless -d $workdir;
|
die "FATAL: failed to create workdir: $!" unless -d $workdir;
|
||||||
|
chmod (0755, $workdir); # sometimes tempdir() ignores umask
|
||||||
$keep_workdir = 0;
|
$keep_workdir = 0;
|
||||||
# $siterules contains all stock *.pre files
|
# $siterules contains all stock *.pre files
|
||||||
$siterules = "$workdir/siterules";
|
$siterules = "$workdir/siterules";
|
||||||
@ -748,7 +748,7 @@ sub start_spamd {
|
|||||||
last if ($spamd_pid);
|
last if ($spamd_pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $sleep = (int($wait++ / 4) + 1);
|
my $sleep = (int(($wait++) / 4) + 1);
|
||||||
warn "spam_pid not found: Sleeping $sleep - Retry # $retries\n" if $retries && $retries < 20;
|
warn "spam_pid not found: Sleeping $sleep - Retry # $retries\n" if $retries && $retries < 20;
|
||||||
|
|
||||||
sleep $sleep if $retries > 0;
|
sleep $sleep if $retries > 0;
|
||||||
|
@ -13,8 +13,8 @@ use Test::More;
|
|||||||
plan skip_all => "Net tests disabled" unless conf_bool('run_net_tests');
|
plan skip_all => "Net tests disabled" unless conf_bool('run_net_tests');
|
||||||
plan skip_all => "Can't use Net::DNS Safely" unless can_use_net_dns_safely();
|
plan skip_all => "Can't use Net::DNS Safely" unless can_use_net_dns_safely();
|
||||||
|
|
||||||
my $tests = 4;
|
my $tests = 6;
|
||||||
$tests += 3 if (HAS_DKIM_VERIFIER);
|
$tests += 4 if (HAS_DKIM_VERIFIER);
|
||||||
|
|
||||||
plan tests => $tests;
|
plan tests => $tests;
|
||||||
|
|
||||||
@ -57,3 +57,14 @@ ok sarun ("-t -D < data/nice/001 2>&1", \&patterns_run_cb);
|
|||||||
ok_all_patterns();
|
ok_all_patterns();
|
||||||
clear_pattern_counters();
|
clear_pattern_counters();
|
||||||
|
|
||||||
|
tstlocalrules(q{
|
||||||
|
askdns ASKDNS_TXT_SPF2 txttcp.spamassassin.org TXT /^v=spf1 include:dnsbltest.spamassassin.org -all$/
|
||||||
|
});
|
||||||
|
%patterns = (
|
||||||
|
q{ ASKDNS_TXT_SPF2 } => 'ASKDNS_TXT_SPF2',
|
||||||
|
'[txttcp.spamassassin.org TXT:v=spf1]' => 'ASKDNS_TXT_SPF2_LOG',
|
||||||
|
);
|
||||||
|
ok sarun ("-t -D < data/nice/001 2>&1", \&patterns_run_cb);
|
||||||
|
ok_all_patterns();
|
||||||
|
clear_pattern_counters();
|
||||||
|
|
||||||
|
@ -25,9 +25,9 @@ tstprefs("
|
|||||||
%patterns = (
|
%patterns = (
|
||||||
'parsing Authentication-Results: authrestest1int', 'hdr1',
|
'parsing Authentication-Results: authrestest1int', 'hdr1',
|
||||||
'parsing Authentication-Results: authrestest2int', 'hdr2',
|
'parsing Authentication-Results: authrestest2int', 'hdr2',
|
||||||
'parsing Authentication-Results: authrestest3int', 'hdr3',
|
'parsing authentication-Results: authrestest3int', 'hdr3',
|
||||||
'parsing Authentication-Results: authrestest4int', 'hdr4',
|
'parsing Authentication-Results: authrestest4int', 'hdr4',
|
||||||
'parsing Authentication-Results: authrestest5int', 'hdr5',
|
'parsing Authentication-RESULTS: authrestest5int', 'hdr5',
|
||||||
'parsing Authentication-Results: authrestest6int', 'hdr6',
|
'parsing Authentication-Results: authrestest6int', 'hdr6',
|
||||||
'authres: results: dkim=pass dmarc=none spf=pass', 'results',
|
'authres: results: dkim=pass dmarc=none spf=pass', 'results',
|
||||||
);
|
);
|
||||||
@ -58,9 +58,9 @@ tstprefs("
|
|||||||
%patterns = (
|
%patterns = (
|
||||||
'parsing Authentication-Results: authrestest1int', 'hdr1',
|
'parsing Authentication-Results: authrestest1int', 'hdr1',
|
||||||
'parsing Authentication-Results: authrestest2int', 'hdr2',
|
'parsing Authentication-Results: authrestest2int', 'hdr2',
|
||||||
'parsing Authentication-Results: authrestest3int', 'hdr3',
|
'parsing authentication-Results: authrestest3int', 'hdr3',
|
||||||
'parsing Authentication-Results: authrestest4int', 'hdr4',
|
'parsing Authentication-Results: authrestest4int', 'hdr4',
|
||||||
'parsing Authentication-Results: authrestest5int', 'hdr5',
|
'parsing Authentication-RESULTS: authrestest5int', 'hdr5',
|
||||||
'parsing Authentication-Results: authrestest6int', 'hdr6',
|
'parsing Authentication-Results: authrestest6int', 'hdr6',
|
||||||
'parsing Authentication-Results: authrestest7tru', 'hdr7',
|
'parsing Authentication-Results: authrestest7tru', 'hdr7',
|
||||||
'authres: results: dkim=pass dmarc=none spf=pass', 'results',
|
'authres: results: dkim=pass dmarc=none spf=pass', 'results',
|
||||||
@ -92,9 +92,9 @@ tstprefs("
|
|||||||
%patterns = (
|
%patterns = (
|
||||||
'parsing Authentication-Results: authrestest1int', 'hdr1',
|
'parsing Authentication-Results: authrestest1int', 'hdr1',
|
||||||
'parsing Authentication-Results: authrestest2int', 'hdr2',
|
'parsing Authentication-Results: authrestest2int', 'hdr2',
|
||||||
'parsing Authentication-Results: authrestest3int', 'hdr3',
|
'parsing authentication-Results: authrestest3int', 'hdr3',
|
||||||
'parsing Authentication-Results: authrestest4int', 'hdr4',
|
'parsing Authentication-Results: authrestest4int', 'hdr4',
|
||||||
'parsing Authentication-Results: authrestest5int', 'hdr5',
|
'parsing Authentication-RESULTS: authrestest5int', 'hdr5',
|
||||||
'parsing Authentication-Results: authrestest6int', 'hdr6',
|
'parsing Authentication-Results: authrestest6int', 'hdr6',
|
||||||
'parsing Authentication-Results: authrestest7tru', 'hdr7',
|
'parsing Authentication-Results: authrestest7tru', 'hdr7',
|
||||||
'parsing Authentication-Results: authrestest8ext', 'hdr8',
|
'parsing Authentication-Results: authrestest8ext', 'hdr8',
|
||||||
|
@ -5,12 +5,16 @@ use SATest; sa_t_init("basic_lint");
|
|||||||
|
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
|
tstpre ("
|
||||||
|
loadplugin Mail::SpamAssassin::Plugin::AWL
|
||||||
|
");
|
||||||
|
|
||||||
@test_locales = qw(C);
|
@test_locales = qw(C);
|
||||||
|
|
||||||
if (!$RUNNING_ON_WINDOWS) {
|
if (!$RUNNING_ON_WINDOWS) {
|
||||||
# Test with few random additional locales if available
|
# Test with few random additional locales if available
|
||||||
my $locales = untaint_cmd("locale -a");
|
my $locales = untaint_cmd("locale -a");
|
||||||
while ($locales =~ /^((?:C|en_US|fr_FR|zh_CN)\.(?:utf|iso|gb).*)$/gmi) {
|
while ($locales =~ /^((?:C|en_US|fr_FR|zh_CN)\.(?:utf|iso).*)$/gmi) {
|
||||||
push @test_locales, $1;
|
push @test_locales, $1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,10 @@ plan tests => 2;
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
tstpre ("
|
||||||
|
loadplugin Mail::SpamAssassin::Plugin::AWL
|
||||||
|
");
|
||||||
|
|
||||||
%patterns = (
|
%patterns = (
|
||||||
qr/^/, 'anything',
|
qr/^/, 'anything',
|
||||||
);
|
);
|
||||||
|
53
upstream/t/basic_lint_without_plugins.t
Executable file
53
upstream/t/basic_lint_without_plugins.t
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/perl -T
|
||||||
|
|
||||||
|
use lib '.'; use lib 't';
|
||||||
|
use SATest; sa_t_init("basic_lint_without_plugins");
|
||||||
|
|
||||||
|
use Test::More;
|
||||||
|
|
||||||
|
plan tests => 4;
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
%patterns = (
|
||||||
|
qr/^/, 'anything',
|
||||||
|
);
|
||||||
|
%anti_patterns = (
|
||||||
|
q{ . }, 'should be silent on success',
|
||||||
|
);
|
||||||
|
|
||||||
|
# override locale for this test!
|
||||||
|
$ENV{'LANGUAGE'} = $ENV{'LC_ALL'} = 'C';
|
||||||
|
|
||||||
|
# Comment out any loadplugin other than Check
|
||||||
|
foreach $tainted (<$workdir/*/*.pre>) {
|
||||||
|
$tainted =~ /(.*)/;
|
||||||
|
my $file = $1;
|
||||||
|
open(IN, $file) or die;
|
||||||
|
open(OUT, ">$file.tmp") or die;
|
||||||
|
while (<IN>) {
|
||||||
|
s/^loadplugin(?!.*::Check\b)/#loadplugin/;
|
||||||
|
print OUT $_ or die;
|
||||||
|
}
|
||||||
|
close OUT or die;
|
||||||
|
close IN or die;
|
||||||
|
rename("$file.tmp", "$file") or die;
|
||||||
|
}
|
||||||
|
# Just want to test sa-update rules
|
||||||
|
unlink("$localrules/01_test_rules.cf");
|
||||||
|
unlink("$localrules/99_test_default.cf");
|
||||||
|
|
||||||
|
my $scoresfile = "$localrules/50_scores.cf";
|
||||||
|
|
||||||
|
# when running from the built tarball or make disttest, we will not have a full
|
||||||
|
# rules dir -- therefore no 50_scores.cf,
|
||||||
|
# so we can use that to tell if this is the case
|
||||||
|
SKIP: {
|
||||||
|
skip( "Not on a repo checkout", 4 ) unless -f $scoresfile;
|
||||||
|
|
||||||
|
sarun ("--lint", \&patterns_run_cb);
|
||||||
|
ok_all_patterns();
|
||||||
|
sarun ("--lint --net", \&patterns_run_cb);
|
||||||
|
ok_all_patterns();
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,10 @@ use Test::More tests => 3;
|
|||||||
qr/^/, 'anything',
|
qr/^/, 'anything',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
tstpre ("
|
||||||
|
loadplugin Mail::SpamAssassin::Plugin::AWL
|
||||||
|
");
|
||||||
|
|
||||||
# override locale for this test!
|
# override locale for this test!
|
||||||
$ENV{'LANGUAGE'} = $ENV{'LC_ALL'} = 'C';
|
$ENV{'LANGUAGE'} = $ENV{'LC_ALL'} = 'C';
|
||||||
|
|
||||||
|
@ -21,11 +21,7 @@ run_ipv6_dns_tests=n
|
|||||||
run_dcc_tests=n
|
run_dcc_tests=n
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Run SQL-based user pref tests during 'make test' REQUIRES DBD::SQLite 1.59_01 or later
|
# Run SQL-based Auto-welcomelist tests during 'make test'
|
||||||
run_sql_pref_tests=n
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Run SQL-based Auto-whitelist tests during 'make test'
|
|
||||||
# NOTE: AWL test is always run with DBD::SQLite when available, only enable
|
# NOTE: AWL test is always run with DBD::SQLite when available, only enable
|
||||||
# this when you want to additionally test for example MySQL or PostgresSQL
|
# this when you want to additionally test for example MySQL or PostgresSQL
|
||||||
# (for which database needs to be created manually and configured below).
|
# (for which database needs to be created manually and configured below).
|
||||||
|
@ -4,7 +4,6 @@ dns_query_restriction deny *
|
|||||||
dns_query_restriction allow spamassassin.org
|
dns_query_restriction allow spamassassin.org
|
||||||
|
|
||||||
# Load selection of non-default plugins for all tests
|
# Load selection of non-default plugins for all tests
|
||||||
loadplugin Mail::SpamAssassin::Plugin::AWL
|
|
||||||
loadplugin Mail::SpamAssassin::Plugin::RelayCountry
|
loadplugin Mail::SpamAssassin::Plugin::RelayCountry
|
||||||
loadplugin Mail::SpamAssassin::Plugin::DCC
|
loadplugin Mail::SpamAssassin::Plugin::DCC
|
||||||
loadplugin Mail::SpamAssassin::Plugin::TextCat
|
loadplugin Mail::SpamAssassin::Plugin::TextCat
|
||||||
|
26
upstream/t/data/nice/spf4
Normal file
26
upstream/t/data/nice/spf4
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Return-Path: <newsalerts-noreply@txttcp.spamassassin.org>
|
||||||
|
Received: from txttcp.spamassassin.org (txttcp.spamassassin.org
|
||||||
|
[64.142.3.173]) by amgod.boxhost.net (Postfix) with SMTP id B9B2931016D for
|
||||||
|
<jm-google-news-alerts@jmason.org>; Tue, 10 Feb 2004 18:18:49 +0000 (GMT)
|
||||||
|
Received: by proxy.google.com with SMTP id so1951389 for
|
||||||
|
<jm-google-news-alerts@jmason.org>; Tue, 10 Feb 2004 10:14:01 -0800 (PST)
|
||||||
|
Received: by abbulk2 with SMTP id mr733125; Tue,
|
||||||
|
10 Feb 2004 10:14:01 -0800 (PST)
|
||||||
|
Message-ID: <1076436841.67074.8fa05ccdc458abe5.1446041b@persist.google.com>
|
||||||
|
Date: Tue, 10 Feb 2004 10:14:01 -0800 (PST)
|
||||||
|
From: newsalerts-noreply@txttcp.spamassassin.org
|
||||||
|
To: jm-google-news-alerts@jmason.org
|
||||||
|
Subject: Google News Alert - spamassassin
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset="ISO-8859-1";
|
||||||
|
|
||||||
|
SWSOFT Unveils Plesk 7, Deployed by 1&1
|
||||||
|
Web Host Industry Review - USA
|
||||||
|
... The software also features a newly designed Windows XP-like user interface,
|
||||||
|
is equipped SpamAssassin, an open source anti-spam tool, and includes
|
||||||
|
"Application ...
|
||||||
|
<http://thewhir.com/marketwatch/sws021004.cfm>
|
||||||
|
See all stories on this topic:
|
||||||
|
<http://news.google.com/news?hl=en&lr=&ie=UTF-8&oe=utf8&client=google&num=30&newsc
|
||||||
|
lusterurl=http://thewhir.com/marketwatch/sws021004.cfm>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user