Date: Mon, 3 Mar 2014 21:10:01 GMT From: "Adam Weinberger" <adamw@adamw.org> To: perl@FreeBSD.org Subject: Re: ports/186756: [UPDATE] mail/p5-Mail-SpamAssassin: update to 3.4.0 Message-ID: <201403032110.s23LA1pP000756@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
The following reply was made to PR ports/186756; it has been noted by GNATS. From: "Adam Weinberger" <adamw@adamw.org> To: bug-followup@FreeBSD.org, takefu@airport.fm Cc: Subject: Re: ports/186756: [UPDATE] mail/p5-Mail-SpamAssassin: update to 3.4.0 Date: Mon, 03 Mar 2014 16:05:44 -0500 --=_MailMate_A5D8A8A9-69FB-401E-B057-33D7FB177BD5_= Content-Type: text/plain; format=flowed I've incorporated Takefu's patch and included a nearly complete rewrite of the Makefile, pkg-plist, and install mechanism. It removes the SPAMC option, as it was a NO-OP. As I've said, I'm interested in maintaining this port if perl@ wishes. # Adam --=_MailMate_A5D8A8A9-69FB-401E-B057-33D7FB177BD5_= Content-Disposition: attachment; filename=sa-3.4.0.patch Content-Transfer-Encoding: quoted-printable Index: Makefile =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- Makefile (revision 346955) +++ Makefile (working copy) @@ -1,27 +1,27 @@ # Created by: Anthony Kim # $FreeBSD$ = -PORTNAME=3D Mail-SpamAssassin -PORTVERSION=3D 3.3.2 -PORTREVISION?=3D 9 # committer: please bump PORTREVISION on Slaves +PORTNAME=3D Mail-SpamAssassin +PORTVERSION=3D 3.4.0 +PORTREVISION?=3D 0 # committer: please bump PORTREVISION on Slaves CATEGORIES?=3D mail perl5 MASTER_SITES=3D ${MASTER_SITE_APACHE:S/$/:apache/} ${MASTER_SITE_PERL_CP= AN:S/$/:cpan/} MASTER_SITE_SUBDIR=3D spamassassin/source/:apache Mail/:cpan PKGNAMEPREFIX?=3D p5- -DISTFILES=3D ${DISTNAME}${EXTRACT_SUFX}:apache,cpan +DISTFILES=3D ${DISTNAME}${EXTRACT_SUFX}:apache,cpan = MAINTAINER?=3D perl@FreeBSD.org -COMMENT?=3D Highly efficient mail filter for identifying spam +COMMENT?=3D Highly efficient mail filter for identifying spam = LICENSE=3D APACHE20 = BUILD_DEPENDS=3D p5-NetAddr-IP>=3D4.00.7:${PORTSDIR}/net-mgmt/p5-NetAddr= -IP \ - p5-Net-DNS>=3D0.63:${PORTSDIR}/dns/p5-Net-DNS \ - p5-HTML-Parser>=3D3.46:${PORTSDIR}/www/p5-HTML-Parser \ - p5-libwww>=3D0:${PORTSDIR}/www/p5-libwww \ - p5-Encode-Detect>=3D0:${PORTSDIR}/converters/p5-Encode-Detect \ - p5-Mail-Tools>=3D0:${PORTSDIR}/mail/p5-Mail-Tools -RUN_DEPENDS:=3D ${BUILD_DEPENDS} + p5-Net-DNS>=3D0.63:${PORTSDIR}/dns/p5-Net-DNS \ + p5-HTML-Parser>=3D3.46:${PORTSDIR}/www/p5-HTML-Parser \ + p5-libwww>=3D0:${PORTSDIR}/www/p5-libwww \ + p5-Encode-Detect>=3D0:${PORTSDIR}/converters/p5-Encode-Detect \ + p5-Mail-Tools>=3D0:${PORTSDIR}/mail/p5-Mail-Tools +RUN_DEPENDS=3D ${BUILD_DEPENDS} = CONFLICTS?=3D ja-p5-Mail-SpamAssassin-[0-9]* p5-Mail-SpamAssassin-devel-= [0-9]* p5-Mail-SpamAssassin-Alt-[0-9]* = @@ -28,118 +28,79 @@ USES=3D perl5 USE_PERL5=3D configure USE_LDCONFIG=3D yes + +DATADIR=3D ${PREFIX}/share/spamassassin DBDIR?=3D /var/db +DOCSDIR=3D ${PREFIX}/share/doc/${PKGNAMEPREFIX}${PORTNAME} ETCDIR?=3D ${PREFIX}/etc/mail/spamassassin + CONTACT_ADDRESS?=3D The administrator of that system USERS?=3D spamd GROUPS?=3D spamd CONFIGURE_ARGS=3D SYSCONFDIR=3D"${PREFIX}/etc" \ - CONTACT_ADDRESS=3D"${CONTACT_ADDRESS}" \ - LOCALSTATEDIR=3D"${DBDIR}/spamassassin" + CONTACT_ADDRESS=3D"${CONTACT_ADDRESS}" \ + LOCALSTATEDIR=3D"${DBDIR}/spamassassin" \ + BUILD_SPAMC=3Dyes = -OPTIONS_DEFINE=3D AS_ROOT SPAMC SACOMPILE DKIM SSL GNUPG MYSQL PGSQL RAZ= OR \ - SPF_QUERY RELAY_COUNTRY DCC IPV6 -OPTIONS_DEFAULT=3D AS_ROOT SPAMC DKIM SSL GNUPG RAZOR +OPTIONS_DEFINE=3D AS_ROOT SACOMPILE DKIM SSL GNUPG MYSQL PGSQL RAZOR \ + SPF_QUERY RELAY_COUNTRY DCC IPV6 +OPTIONS_DEFAULT=3D AS_ROOT DKIM SSL GNUPG RAZOR + AS_ROOT_DESC=3D Run spamd as root (recommended) -SPAMC_DESC=3D Build spamd/spamc (not for amavisd) -SACOMPILE_DESC=3D sa-compile -DKIM_DESC=3D DKIM/DomainKeys Identified Mail -SSL_DESC=3D Build with SSL support for spamd/spamc -GNUPG_DESC=3D Install GnuPG (for sa-update) -RAZOR_DESC=3D Add Vipul's Razor support +DCC_DESC=3D Add DCC support (see LICENSE) +DKIM_DESC=3D DKIM/DomainKeys Identified Mail +GNUPG_DESC=3D Install GnuPG (for sa-update, optional) +IPV6_DESC=3D IPv6 sockets support +RAZOR_DESC=3D Add Vipul's Razor support +RELAY_COUNTRY_DESC=3D Relay country support +SACOMPILE_DESC=3D Compile rulesets (improves speed) SPF_QUERY_DESC=3D Add SPF query support -RELAY_COUNTRY_DESC=3D Relay country support -DCC_DESC=3D Add DCC support (see LICENSE) -IPV6_DESC=3D IPv6 sockets support +SSL_DESC=3D Build spamd/spamc with SSL support = SUB_FILES=3D pkg-install SUB_LIST=3D USER=3D${USERS} GROUP=3D${GROUPS} INSTALL=3D"${INSTALL}" +PLIST_SUB+=3D USER=3D${USERS} GROUP=3D${GROUPS} +OPTIONS_SUB=3Dyes = -.include <bsd.port.options.mk> +DCC_RUN_DEPENDS=3D dcc-dccd>=3D1.3.111:${PORTSDIR}/mail/dcc-dccd +DKIM_RUN_DEPENDS=3D p5-IO-Socket-SSL>=3D0:${PORTSDIR}/security/p5-IO-Soc= ket-SSL \ + p5-Mail-DKIM>=3D0.37:${PORTSDIR}/mail/p5-Mail-DKIM \ + p5-Crypt-OpenSSL-RSA>=3D0.26_1:${PORTSDIR}/security/p5-Crypt-OpenSS= L-RSA +GNUPG_RUN_DEPENDS=3D gnupg1>=3D1.4.7:${PORTSDIR}/security/gnupg1 +IPV6_RUN_DEPENDS=3D p5-IO-Socket-INET6>=3D0:${PORTSDIR}/net/p5-IO-Socket= -INET6 +MYSQL_RUN_DEPENDS=3D p5-DBD-mysql>=3D0:${PORTSDIR}/databases/p5-DBD-mysq= l +PGSQL_RUN_DEPENDS=3D p5-DBD-Pg>=3D0:${PORTSDIR}/databases/p5-DBD-Pg +RAZOR_RUN_DEPENDS=3D razor-agents>=3D2.84:${PORTSDIR}/mail/razor-agents +RELAY_COUNTRY_RUN_DEPENDS=3D p5-IP-Country>=3D0:${PORTSDIR}/net/p5-IP-Co= untry +SACOMPILE_RUN_DEPENDS=3D re2c>=3D.12.0:${PORTSDIR}/devel/re2c +SPF_QUERY_RUN_DEPENDS=3D p5-Mail-SPF>=3D0:${PORTSDIR}/mail/p5-Mail-SPF = -.if ${PORT_OPTIONS:MSSL} -USE_OPENSSL=3D yes -.endif +SSL_USE=3D OPENSSL=3Dyes +SSL_RUN_DEPENDS=3D p5-IO-Socket-SSL>=3D0:${PORTSDIR}/security/p5-IO-Sock= et-SSL +SSL_CONFIGURE_ON=3D ENABLE_SSL=3Dyes +SSL_CONFIGURE_OFF=3D ENABLE_SSL=3Dno = -.if ${PORT_OPTIONS:MSPAMC} -CONFIGURE_ARGS+=3D BUILD_SPAMC=3Dyes -.else -CONFIGURE_ARGS+=3D BUILD_SPAMC=3Dno -WITH_AS_ROOT=3D -WITHOUT_SSL=3D1 -.endif +DOCS=3D CREDITS Changes INSTALL NOTICE PACKAGING README TRADEMARK UPGRA= DE USAGE procmailrc.example +DOCSSQL=3D README README.awl README.bayes awl_mysql.sql awl_pg.sql bayes= _mysql.sql bayes_pg.sql userpref_mysql.sql userpref_pg.sql +DOCSLDAP=3D README README.testing sa_test.ldif +PORTDOCS=3D ${DOCS} sql ldap = -.if ${PORT_OPTIONS:MSPF_QUERY} -RUN_DEPENDS+=3D p5-Mail-SPF>=3D0:${PORTSDIR}/mail/p5-Mail-SPF -.endif -.if ${PORT_OPTIONS:MIPV6} -RUN_DEPENDS+=3D p5-IO-Socket-INET6>=3D0:${PORTSDIR}/net/p5-IO-Socket-INE= T6 -.endif +USE_RC_SUBR=3D sa-spamd = +.include <bsd.port.pre.mk> + .if ${PORT_OPTIONS:MSSL} -.include "${PORTSDIR}/Mk/bsd.openssl.mk" -RUN_DEPENDS+=3D p5-IO-Socket-SSL>=3D0:${PORTSDIR}/security/p5-IO-Socket-= SSL CFLAGS+=3D -I${OPENSSLINC} LDFLAGS+=3D -L${OPENSSLLIB} -CONFIGURE_ARGS+=3D ENABLE_SSL=3Dyes -PLIST_SUB+=3D SSL=3D"" -.else -CONFIGURE_ARGS+=3D ENABLE_SSL=3Dno -PLIST_SUB+=3D SSL=3D"@comment " .endif = -.if ${PORT_OPTIONS:MGNUPG} -RUN_DEPENDS+=3D gnupg1>=3D1.4.7:${PORTSDIR}/security/gnupg1 -.endif - -.if ${PORT_OPTIONS:MMYSQL} -RUN_DEPENDS+=3D p5-DBD-mysql>=3D0:${PORTSDIR}/databases/p5-DBD-mysql -.endif - -.if ${PORT_OPTIONS:MPGSQL} -RUN_DEPENDS+=3D p5-DBD-Pg>=3D0:${PORTSDIR}/databases/p5-DBD-Pg -.endif - -.include <bsd.port.pre.mk> - -.if ${PORT_OPTIONS:MRAZOR} -RUN_DEPENDS+=3D razor-agents>=3D2.84:${PORTSDIR}/mail/razor-agents -.else -.endif - -.if ${PORT_OPTIONS:MDKIM} -RUN_DEPENDS+=3D p5-IO-Socket-SSL>=3D0:${PORTSDIR}/security/p5-IO-Socket-= SSL -RUN_DEPENDS+=3D p5-Mail-DKIM>=3D0.37:${PORTSDIR}/mail/p5-Mail-DKIM -RUN_DEPENDS+=3D p5-Crypt-OpenSSL-RSA>=3D0.26_1:${PORTSDIR}/security/p5-C= rypt-OpenSSL-RSA -.endif - -.if ${PORT_OPTIONS:MSACOMPILE} -RUN_DEPENDS+=3D re2c>=3D.12.0:${PORTSDIR}/devel/re2c -.endif - -.if ${PORT_OPTIONS:MRELAY_COUNTRY} -RUN_DEPENDS+=3D p5-IP-Country>=3D0:${PORTSDIR}/net/p5-IP-Country -.endif - -.if ${PORT_OPTIONS:MDCC} -RUN_DEPENDS+=3D dcc-dccd>=3D1.3.111:${PORTSDIR}/mail/dcc-dccd -.endif - -DOCSDIR=3D ${PREFIX}/share/doc/${PKGNAMEPREFIX}${PORTNAME} -DATADIR=3D ${PREFIX}/share/spamassassin -DOCS=3D CREDITS Changes INSTALL NOTICE PACKAGING README TRADEMARK UPGRA= DE USAGE procmailrc.example -DOCSSQL=3D README README.awl README.bayes awl_mysql.sql awl_pg.sql bayes= _mysql.sql bayes_pg.sql userpref_mysql.sql userpref_pg.sql -DOCSLDAP=3D README README.testing sa_test.ldif -PORTDOCS=3D ${DOCS} sql ldap - -USE_RC_SUBR=3D sa-spamd - .if ${PORT_OPTIONS:MMYSQL} || ${PORT_OPTIONS:MPGSQL} SUB_LIST+=3D SQL_FLAG=3D"-Q" .else SUB_LIST+=3D SQL_FLAG=3D"" .endif -.if ! ${PORT_OPTIONS:MAS_ROOT} + +.if empty(PORT_OPTIONS:MAS_ROOT) SUB_LIST+=3D RUN_AS_USER=3D"-u ${USERS} -H /var/spool/spamd" .else SUB_LIST+=3D RUN_AS_USER=3D"" @@ -153,6 +114,7 @@ -e 's#B_CONFDIR)/v312.pre#B_CONFDIR)/v312.pre.sample#g' \ -e 's#B_CONFDIR)/v320.pre#B_CONFDIR)/v320.pre.sample#g' \ -e 's#B_CONFDIR)/v330.pre#B_CONFDIR)/v330.pre.sample#g' \ + -e 's#B_CONFDIR)/v340.pre#B_CONFDIR)/v340.pre.sample#g' \ -e 's/require DBI/0/' \ ${WRKSRC}/Makefile.PL @${REINPLACE_CMD} -e '/^CC =3D/d; \ @@ -164,10 +126,10 @@ .if ${PORT_OPTIONS:MRELAY_COUNTRY} ${REINPLACE_CMD} -e '/RelayCountry/s/^# ?loadplugin/loadplugin/' ${WRKS= RC}/rules/init.pre .endif -.if ! ${PORT_OPTIONS:MDKIM} +.if empty(PORT_OPTIONS:MDKIM) ${REINPLACE_CMD} -e '/DKIM/s/^loadplugin/#loadplugin/' ${WRKSRC}/rules/= v312.pre .endif -.if ! ${PORT_OPTIONS:MSPF_QUERY} +.if empty(PORT_OPTIONS:MSPF_QUERY) ${REINPLACE_CMD} -e '/SPF/s/^loadplugin/#loadplugin/' ${WRKSRC}/rules/i= nit.pre .endif .if ${PORT_OPTIONS:MDCC} @@ -178,31 +140,27 @@ .endif = post-build: - @(cd ${BUILD_WRKSRC}; ${SETENV} ${MAKE_ENV} ${MAKE} ${MAKE_FLAGS} ${MAK= EFILE} ${MAKE_ARGS} spamc/libspamc.so) + (cd ${BUILD_WRKSRC}; ${SETENV} ${MAKE_ENV} ${MAKE} ${MAKE_FLAGS} ${MAKE= FILE} ${MAKE_ARGS} spamc/libspamc.so) .if ${PORT_OPTIONS:MSSL} - @(cd ${BUILD_WRKSRC}; ${SETENV} ${MAKE_ENV} ${MAKE} ${MAKE_FLAGS} ${MAK= EFILE} ${MAKE_ARGS} spamc/libsslspamc.so) + (cd ${BUILD_WRKSRC}; ${SETENV} ${MAKE_ENV} ${MAKE} ${MAKE_FLAGS} ${MAKE= FILE} ${MAKE_ARGS} spamc/libsslspamc.so) .endif = pre-su-install: @${MKDIR} ${STAGEDIR}${DATADIR} - @${INSTALL_PROGRAM} ${WRKSRC}/spamc/libspamc.so ${STAGEDIR}${PREFIX}/li= b/libspamc.so.0 + ${INSTALL_LIB} ${WRKSRC}/spamc/libspamc.so ${STAGEDIR}${PREFIX}/lib/lib= spamc.so.0 @${LN} -sf libspamc.so.0 ${STAGEDIR}${PREFIX}/lib/libspamc.so .if ${PORT_OPTIONS:MSSL} - @${INSTALL_PROGRAM} ${WRKSRC}/spamc/libsslspamc.so ${STAGEDIR}${PREFIX}= /lib/libsslspamc.so.0 - @${LN} -sf libsslspamc.so.0 ${STAGEDIR}${PREFIX}/lib/libsslspamc.so + ${INSTALL_LIB} ${WRKSRC}/spamc/libsslspamc.so ${STAGEDIR}${PREFIX}/lib/= libsslspamc.so.0 + ${LN} -sf libsslspamc.so.0 ${STAGEDIR}${PREFIX}/lib/libsslspamc.so .endif - @${INSTALL_DATA} ${WRKSRC}/spamc/libspamc.h ${STAGEDIR}${PREFIX}/includ= e + ${INSTALL_DATA} ${WRKSRC}/spamc/libspamc.h ${STAGEDIR}${PREFIX}/include= = post-install:: -.if ${PORT_OPTIONS:MSPAMC} - @${STRIP_CMD} ${STAGEDIR}${PREFIX}/bin/spamc -.endif - -.if ${PORT_OPTIONS:MDOCS} + ${STRIP_CMD} ${STAGEDIR}${PREFIX}/bin/spamc + @${MKDIR} ${STAGEDIR}/var/lib/spamassassin ${STAGEDIR}${DBDIR}/spamassa= ssin @${MKDIR} ${STAGEDIR}${DOCSDIR} ${STAGEDIR}${DOCSDIR}/sql ${STAGEDIR}${= DOCSDIR}/ldap - @${INSTALL_DATA} ${DOCS:S|^|${WRKSRC}/|} ${STAGEDIR}${DOCSDIR} - @${INSTALL_DATA} ${DOCSSQL:S|^|${WRKSRC}/sql/|} ${STAGEDIR}${DOCSDIR}/s= ql - @${INSTALL_DATA} ${DOCSLDAP:S|^|${WRKSRC}/ldap/|} ${STAGEDIR}${DOCSDIR}= /ldap -.endif + ${INSTALL_DATA} ${DOCS:S|^|${WRKSRC}/|} ${STAGEDIR}${DOCSDIR} + ${INSTALL_DATA} ${WRKSRC}/sql/* ${STAGEDIR}${DOCSDIR}/sql + ${INSTALL_DATA} ${WRKSRC}/ldap/* ${STAGEDIR}${DOCSDIR}/ldap = .include <bsd.port.post.mk> Index: distinfo =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- distinfo (revision 346955) +++ distinfo (working copy) @@ -1,2 +1,2 @@ -SHA256 (Mail-SpamAssassin-3.3.2.tar.gz) =3D 5323038939a0ef9fc97d5264defc= e3ae1d95e98b3a94c4c3b583341c927f32df -SIZE (Mail-SpamAssassin-3.3.2.tar.gz) =3D 1208182 +SHA256 (Mail-SpamAssassin-3.4.0.tar.gz) =3D 244914c30976844878a7f129fd50= 3eb40986c68a3800f416c3a68b14507c0a64 +SIZE (Mail-SpamAssassin-3.4.0.tar.gz) =3D 1269753 Index: files/patch-bug6624 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- files/patch-bug6624 (revision 346955) +++ files/patch-bug6624 (working copy) @@ -1,88 +0,0 @@ ---- lib/Mail/SpamAssassin/BayesStore/MySQL.pm (revision 1138970) -+++ lib/Mail/SpamAssassin/BayesStore/MySQL.pm (working copy) -@@ -840,14 +840,28 @@ - return 0; - } - = -+ # With ON DUPLICATE KEY UPDATE, the affected-rows value per row is = 1 if -+ # the row is inserted as a new row and 2 if an existing row is upda= ted. -+ # -+ # Due to a MySQL server bug a value of 3 can be seen. -+ # See: http://bugs.mysql.com/bug.php?id=3D46675 -+ # When executing the INSERT ... ON DUPLICATE KEY UPDATE statement= -+ # and checking the rows return count: -+ # mysql_client_found_rows =3D 0: The second INSERT returns a row = count -+ # of 2 in all MySQL versions. -+ # mysql_client_found_rows =3D 1: The second INSERT returns this r= ow count: -+ # Before MySQL 5.1.20: 2 -+ # MySQL 5.1.20: undef on Mac OS X, 139775481 on Linux (garbage?= ) -+ # MySQL 5.1.21 and up: 3 -+ # - my $num_rows =3D $rc; - = - $sth->finish(); - = -- if ($num_rows =3D=3D 1 || $num_rows =3D=3D 2) { -+ if ($num_rows =3D=3D 1 || $num_rows =3D=3D 2 || $num_rows =3D=3D 3)= { - my $token_count_update =3D ''; - = -- $token_count_update =3D "token_count =3D token_count + 1," if ($n= um_rows =3D=3D 1); -+ $token_count_update =3D "token_count =3D token_count + 1," if $nu= m_rows =3D=3D 1; - $sql =3D "UPDATE bayes_vars SET - $token_count_update - newest_token_age =3D GREATEST(newest_token_age, ?)= , -@@ -872,7 +886,11 @@ - } - else { - # $num_rows was not what we expected -- dbg("bayes: _put_token: Updated an unexpected number of rows."); -+ my $token_displ =3D $token; -+ $token_displ =3D~ s/(.)/sprintf('%02x',ord($1))/egs; -+ dbg("bayes: _put_token: Updated an unexpected number of rows: %s,= ". -+ "id: %s, token (hex): %s", -+ $num_rows, $self->{_userid}, $token_displ); - $self->{_dbh}->rollback(); - return 0; - } -@@ -987,8 +1005,24 @@ - else { - my $num_rows =3D $rc; - = -- $need_atime_update_p =3D 1 if ($num_rows =3D=3D 1 || $num_rows =3D=3D = 2); -- $new_tokens++ if ($num_rows =3D=3D 1); -+ # With ON DUPLICATE KEY UPDATE, the affected-rows value per row= is 1 if -+ # the row is inserted as a new row and 2 if an existing row is = updated. -+ # But see MySQL bug (as above): http://bugs.mysql.com/bug.php?i= d=3D46675 -+ -+ if ($num_rows =3D=3D 1) { -+ $new_tokens++; -+ $need_atime_update_p =3D 1; -+ } elsif ($num_rows =3D=3D 2 || $num_rows =3D=3D 3) { -+ $need_atime_update_p =3D 1; -+ } else { -+ # $num_rows was not what we expected -+ my $token_displ =3D $token; -+ $token_displ =3D~ s/(.)/sprintf('%02x',ord($1))/egs; -+ dbg("bayes: _put_tokens: Updated an unexpected number of rows= : %s, ". -+ "id: %s, token (hex): %s", -+ $num_rows, $self->{_userid}, $token_displ); -+ $error_p =3D 1; -+ } - } - } - = -@@ -1026,10 +1060,10 @@ - } - } - else { -- # $num_rows was not what we expected -- dbg("bayes: _put_tokens: Updated an unexpected number of rows.");= -- $self->{_dbh}->rollback(); -- return 0; -+ info("bayes: _put_tokens: no atime updates needed? Num of tokens= : %d", -+ scalar keys %{$tokens}); -+# $self->{_dbh}->rollback(); -+# return 0; - } - } - = Index: files/patch-bug6655 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- files/patch-bug6655 (revision 346955) +++ files/patch-bug6655 (working copy) @@ -1,50 +0,0 @@ -$FreeBSD$ - -https://issues.apache.org/SpamAssassin/show_bug.cgi?id=3D6655 - ---- lib/Mail/SpamAssassin/Util.pm 2011-06-06 19:59:17.000000000 -0400 -+++ lib/Mail/SpamAssassin/Util.pm 2011-08-26 17:12:19.000000000 -0400 -@@ -1025,6 +1024,8 @@ - return; - } - = -+ opendir(my $dh, $tmpdir) || die "Could not open $tmpdir: $!"; -+ closedir $dh; - my ($reportfile, $tmpfile); - my $umask =3D umask 077; - = -@@ -1052,7 +1053,10 @@ - = - # ensure the file handle is not semi-open in some way - if ($tmpfile) { -- close $tmpfile or info("error closing $reportfile: $!"); -+ if (! close $tmpfile) { -+ info("error closing $reportfile: $!"); -+ $tmpfile=3Dundef; -+ } - } - } - ---- sa-update.raw 2011-06-24 13:38:50.000000000 -0400 -+++ sa-update.raw 2011-08-29 09:38:50.000000000 -0400 -@@ -677,9 +677,9 @@ - = - # Write the content out to a temp file for GPG/Archive::Tar interacti= on - dbg("channel: populating temp content file"); -- open(TMP, ">$content_file") || die "fatal: can't write to content tem= p file $content_file: $!\n"; -+ open(TMP, ">$content_file") || die "fatal: couldn't create content te= mp file $content_file: $!\n"; - binmode TMP; -- print TMP $content; -+ print TMP $content || die "fatal: can't write to content temp file $c= ontent_file: $!\n"; - close(TMP); - = - # to sign : gpg -bas file -@@ -695,7 +695,7 @@ - die "fatal: couldn't create temp file for GPG signature: $!\n"; - } - binmode $tfh; -- print $tfh $GPG; -+ print $tfh $GPG || die "fatal: can't write temp file for GPG signat= ure: $!\n"; - close($tfh); - = - dbg("gpg: calling gpg"); Index: files/patch-bug6698 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- files/patch-bug6698 (revision 346955) +++ files/patch-bug6698 (working copy) @@ -1,1471 +0,0 @@ ---- lib/Mail/SpamAssassin/Plugin/DCC.pm 2011-06-06 19:59:17.000000000 -0= 400 -+++ lib/Mail/SpamAssassin/Plugin/DCC.pm 2011-11-26 07:22:36.000000000 -0= 500 -@@ -15,6 +15,20 @@ - # limitations under the License. - # </@LICENSE> - = -+# Changes since SpamAssassin 3.3.2: -+# support for DCC learning. See dcc_learn_score. -+# deal with orphan dccifd sockets -+# use `cdcc -q` to not stall waiting to find a DCC server when decidi= ng -+# whether DCC checks are enabled -+# use dccproc -Q or dccifd query if a pre-existing X-DCC header shows= -+# the message has already been reported -+# dccproc now uses -w /var/dcc/whiteclnt so it acts more like dccifd -+# warn about the use of ancient versions of dccproc and dccifd -+# turn off dccifd greylisting -+# query instead of reporting mail messages that contain X-DCC headers= and -+# and so has probably already been reported -+# try harder to find dccproc and cdcc when not explicitly configured -+ - =3Dhead1 NAME - = - Mail::SpamAssassin::Plugin::DCC - perform DCC check of messages -@@ -30,30 +44,31 @@ - = - The DCC or Distributed Checksum Clearinghouse is a system of servers - collecting and counting checksums of millions of mail messages. --TheSpamAssassin.pm counts can be used by SpamAssassin to detect and --reject or filter spam. -- --Because simplistic checksums of spam can be easily defeated, the main --DCC checksums are fuzzy and ignore aspects of messages. The fuzzy --checksums are changed as spam evolves. -+The counts can be used by SpamAssassin to detect and filter spam. - = --Note that DCC is disabled by default in C<init.pre> because it is not --open source. See the DCC license for more details. -+See http://www.dcc-servers.net/dcc/ for more information about DCC. - = --See http://www.rhyolite.com/anti-spam/dcc/ for more information about --DCC. -+Note that DCC is disabled by default in C<v310.pre> because its use req= uires -+software that is not distributed with SpamAssassin and that has license= -+restrictions for certain commercial uses. -+See the DCC license at http://www.dcc-servers.net/dcc/LICENSE for detai= ls. -+ -+Enable it by uncommenting the "loadplugin Mail::SpamAssassin::Plugin::D= CC" -+confdir/v310.pre or by adding this line to your local.pre. It might al= so -+be necessary to install a DCC package, port, rpm, or equivalent from yo= ur -+operating system distributor or a tarball from the primary DCC source -+at http://www.dcc-servers.net/dcc/#download -+See also http://www.dcc-servers.net/dcc/INSTALL.html - = - =3Dhead1 TAGS - = - The following tags are added to the set, available for use in reports, - header fields, other plugins, etc.: - = -- _DCCB_ DCC server ID in a response -- _DCCR_ response from DCC - header field body in X-DCC-*-Metrics -- _DCCREP_ response from DCC - DCC reputation in percents (0..100) -- --Tag _DCCREP_ provides a nonempty value only with commercial DCC systems= =2E --This is the percentage of spam vs. ham sent from the first untrusted re= lay. -+ _DCCB_ DCC server ID in X-DCC-*-Metrics header field name -+ _DCCR_ X-DCC-*-Metrics header field body -+ _DCCREP_ DCC Reputation or percent bulk mail (0..100) from -+ commercial DCC software - = - =3Dcut - = -@@ -75,8 +90,6 @@ - use vars qw(@ISA); - @ISA =3D qw(Mail::SpamAssassin::Plugin); - = --use vars qw($have_inet6); -- - sub new { - my $class =3D shift; - my $mailsaobject =3D shift; -@@ -87,7 +100,7 @@ - = - # are network tests enabled? - if ($mailsaobject->{local_tests_only}) { -- $self->{dcc_disabled} =3D 1; -+ $self->{use_dcc} =3D 0; - dbg("dcc: local tests only, disabling DCC"); - } - else { -@@ -128,20 +141,23 @@ - = - =3Ditem dcc_fuz2_max NUMBER - = --This option sets how often a message's body/fuz1/fuz2 checksum must hav= e been --reported to the DCC server before SpamAssassin will consider the DCC ch= eck as --matched. -- --As nearly all DCC clients are auto-reporting these checksums, you shoul= d set --this to a relatively high value, e.g. C<999999> (this is DCC's MANY cou= nt). -+Sets how often a message's body/fuz1/fuz2 checksum must have been repor= ted -+to the DCC server before SpamAssassin will consider the DCC check hit. -+C<999999> is DCC's MANY count. - = - The default is C<999999> for all these options. - = - =3Ditem dcc_rep_percent NUMBER - = --Only commercial DCC systems provide DCC reputation information. This is= the --percentage of spam vs. ham sent from the first untrusted relay. It wil= l hit --on new spam from spam sources. Default is C<90>. -+Only the commercial DCC software provides DCC Reputations. A DCC Reput= ation -+is the percentage of bulk mail received from the last untrusted relay i= n the -+path taken by a mail message as measured by all commercial DCC installa= tions. -+See http://www.rhyolite.com/dcc/reputations.html -+You C<must> whitelist your trusted relays or MX servers with MX or -+MXDCC lines in /var/dcc/whiteclnt as described in the main DCC man page= -+to avoid seeing your own MX servers as sources of bulk mail. -+See http://www.dcc-servers.net/dcc/dcc-tree/dcc.html#White-and-Blacklis= ts -+The default is C<90>. - = - =3Dcut - = -@@ -189,13 +205,9 @@ - =3Ditem dcc_home STRING - = - This option tells SpamAssassin where to find the dcc homedir. --If not given, it will try to get dcc to specify one, and if that fails = it --will try dcc's own default homedir of '/var/dcc'. --If C<dcc_path> is not specified, it will default to looking in --C<dcc_home/bin> for dcc client instead of relying on SpamAssassin to fi= nd it --in the current PATH. If it isn't found there, it will look in the curr= ent --PATH. If a C<dccifd> socket is found in C<dcc_home> or specified explic= itly, --it will use that interface instead of C<dccproc>. -+If not specified, try to use the locally configured directory -+from the C<cdcc homedir> command. -+Try /var/dcc if that command fails. - = - =3Dcut - = -@@ -205,7 +217,7 @@ - type =3D> $Mail::SpamAssassin::Conf::CONF_TYPE_STRING, - code =3D> sub { - my ($self, $key, $value, $line) =3D @_; -- if (!defined $value || !length $value) { -+ if (!defined $value || $value eq '') { - return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE; - } - $value =3D untaint_file_path($value); -@@ -223,14 +235,16 @@ - = - =3Ditem dcc_dccifd_path STRING - = --This option tells SpamAssassin where to find the dccifd socket. If --C<dcc_dccifd_path> is not specified, it will default to looking for a s= ocket --named C<dccifd> in a directory C<dcc_home>. The C<dcc_dccifd_path> can= be --a Unix socket name (absolute path), or an INET socket specification in = a form --C<[host]:port> or C<host:port>, where a host can be an IPv4 or IPv6 add= ress --or a host name, and port is a TCP port number. In case of an IPv6 addre= ss the --brackets are required syntax. If a C<dccifd> socket is found, the plugi= n will --use it instead of C<dccproc>. -+This option tells SpamAssassin where to find the dccifd socket instead -+of a local Unix socket named C<dccifd> in the C<dcc_home> directory. -+If a socket is specified or found, use it instead of C<dccproc>. -+ -+If specifed, C<dcc_dccifd_path> is the absolute path of local Unix sock= et -+or an INET socket specified as C<[Host]:Port> or C<Host:Port>. -+Host can be an IPv4 or IPv6 address or a host name -+Port is a TCP port number. The brackets are required for an IPv6 addres= s. -+ -+The default is C<undef>. - = - =3Dcut - = -@@ -240,45 +254,60 @@ - type =3D> $Mail::SpamAssassin::Conf::CONF_TYPE_STRING, - code =3D> sub { - my ($self, $key, $value, $line) =3D @_; -- $value =3D '' if !defined $value; -- $self->{dcc_dccifd_path_raw} =3D $value; # for logging purposes -- undef $self->{dcc_dccifd_host}; -- undef $self->{dcc_dccifd_port}; -- undef $self->{dcc_dccifd_socket}; -- local($1,$2,$3); -- if ($value eq '') { -+ -+ if (!defined $value || $value eq '') { - return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE; -- } elsif ($value =3D~ m{^ (?: \[ ([^\]]*) \] | ([^:]*) ) : ([^:]*)= \z}sx) { -- # "[host]:port" or "host:port", where a host can be an IPv4 or = IPv6 -- # address or a host name, and port is a TCP port number or serv= ice name -- my $host =3D defined $1 ? $1 : $2; -- my $port =3D $3; -- $self->{dcc_dccifd_host} =3D untaint_var($host); -- $self->{dcc_dccifd_port} =3D untaint_var($port); -- dbg("config: dcc_dccifd_path set to [%s]:%s", $host,$port); -- } else { # assume a unix socket -+ } -+ -+ local($1,$2,$3); -+ if ($value =3D~ m{^ (?: \[ ([^\]]*) \] | ([^:]*) ) : ([^:]*) \z}s= x) { -+ my $host =3D untaint_var(defined $1 ? $1 : $2); -+ my $port =3D untaint_var($3); -+ if (!$host) { -+ info("config: missing or bad host name in dcc_dccifd_path '$value'")= ; -+ return $Mail::SpamAssassin::Conf::INVALID_VALUE; -+ } -+ if (!$port || $port !~ /^\d+\z/ || $port < 1 || $port > 65535) { -+ info("config: bad TCP port number in dcc_dccifd_path '$value'"); -+ return $Mail::SpamAssassin::Conf::INVALID_VALUE; -+ } -+ -+ $self->{dcc_dccifd_host} =3D $host; -+ $self->{dcc_dccifd_port} =3D $port; -+ if ($host !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/) { -+ # remember to try IPv6 if we can with a host name or non-IPv4 addres= s -+ $self->{dcc_dccifd_IPv6} =3D eval { require IO::Socket::INET6 }; -+ } -+ dbg("config: dcc_dccifd_path set to [%s]:%s", $host, $port); -+ -+ } else { -+ # assume a unix socket - if ($value !~ m{^/}) { -- info("config: dcc_dccifd_path should be an absolute socket pa= th"); -+ info("config: dcc_dccifd_path '$value' is not an absolute path"); - # return $Mail::SpamAssassin::Conf::INVALID_VALUE; # abort or = accept? - } - $value =3D untaint_file_path($value); -- # test disabled, dccifd may not yet be running at spamd startup t= ime -- # if (!-S $value) { -- # info("config: dcc_dccifd_path '$value' isn't a local socket")= ; -- # return $Mail::SpamAssassin::Conf::INVALID_VALUE; -- # } -+ - $self->{dcc_dccifd_socket} =3D $value; - dbg("config: dcc_dccifd_path set to local socket %s", $value); -+ dbg("dcc: dcc_dccifd_path set to local socket %s", $value); - } -+ -+ $self->{dcc_dccifd_path_raw} =3D $value; - } - }); - = - =3Ditem dcc_path STRING - = --This option tells SpamAssassin specifically where to find the C<dccproc= > --client instead of relying on SpamAssassin to find it in the current PAT= H. --Note that if I<taint mode> is enabled in the Perl interpreter, you shou= ld --use this, as the current PATH will have been cleared. -+Where to find the C<dccproc> client program instead of relying on SpamA= ssassin -+to find it in the current PATH or C<dcc_home/bin>. This must often be s= et, -+because the current PATH is cleared by I<taint mode> in the Perl interp= reter, -+ -+If a C<dccifd> socket is found in C<dcc_home> or specified explicitly -+with C<dcc_dccifd_path>, use the C<dccifd(8)> interface instead of C<dc= cproc>. -+ -+The default is C<undef>. -+ - = - =3Dcut - = -@@ -289,12 +318,12 @@ - type =3D> $Mail::SpamAssassin::Conf::CONF_TYPE_STRING, - code =3D> sub { - my ($self, $key, $value, $line) =3D @_; -- if (!defined $value || !length $value) { -+ if (!defined $value || $value eq '') { - return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE; - } - $value =3D untaint_file_path($value); - if (!-x $value) { -- info("config: dcc_path '$value' isn't an executable"); -+ info("config: dcc_path '$value' is not executable"); - return $Mail::SpamAssassin::Conf::INVALID_VALUE; - } - = -@@ -304,7 +333,7 @@ - = - =3Ditem dcc_options options - = --Specify additional options to the dccproc(8) command. Please note that = only -+Specify additional options to the dccproc(8) command. Only - characters in the range [0-9A-Za-z ,._/-] are allowed for security reas= ons. - = - The default is C<undef>. -@@ -319,6 +348,7 @@ - code =3D> sub { - my ($self, $key, $value, $line) =3D @_; - if ($value !~ m{^([0-9A-Za-z ,._/-]+)$}) { -+ info("config: dcc_options '$value' contains impermissible characters")= ; - return $Mail::SpamAssassin::Conf::INVALID_VALUE; - } - $self->{dcc_options} =3D $1; -@@ -327,8 +357,9 @@ - = - =3Ditem dccifd_options options - = --Specify additional options to send to the dccifd(8) daemon. Please note= that only --characters in the range [0-9A-Za-z ,._/-] are allowed for security reas= ons. -+Specify additional options to send to the dccifd daemon with -+the ASCII protocol described on the dccifd(8) man page. -+Only characters in the range [0-9A-Za-z ,._/-] are allowed for security= reasons. - = - The default is C<undef>. - = -@@ -342,265 +373,306 @@ - code =3D> sub { - my ($self, $key, $value, $line) =3D @_; - if ($value !~ m{^([0-9A-Za-z ,._/-]+)$}) { -+ info("config: dccifd_options '$value' contains impermissible character= s"); - return $Mail::SpamAssassin::Conf::INVALID_VALUE; - } - $self->{dccifd_options} =3D $1; - } - }); - = -+=3Ditem dcc_learn_score n (default: undef) -+ -+Report messages with total scores this much larger than the -+SpamAssassin spam threshold to DCC as spam. -+ -+=3Dcut -+ -+ push (@cmds, { -+ setting =3D> 'dcc_learn_score', -+ is_admin =3D> 1, -+ default =3D> undef, -+ type =3D> $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC, -+ }); -+ - $conf->{parser}->register_commands(\@cmds); - } - = -+ -+ -+ -+sub ck_dir { -+ my ($self, $dir, $tgt, $src) =3D @_; -+ -+ $dir =3D untaint_file_path($dir); -+ if (!stat($dir)) { -+ my $dir_errno =3D 0+$!; -+ if ($dir_errno =3D=3D ENOENT) { -+ dbg("dcc: $tgt $dir from $src does not exist"); -+ } else { -+ dbg("dcc: $tgt $dir from $src is not accessible: $!"); -+ } -+ return; -+ } -+ if (!-d _) { -+ dbg("dcc: $tgt $dir from $src is not a directory"); -+ return; -+ } -+ -+ $self->{main}->{conf}->{$tgt} =3D $dir; -+ dbg("dcc: use '$tgt $dir' from $src"); -+} -+ - sub find_dcc_home { - my ($self) =3D @_; -+ my $dcc_libexec; -+ -+ # just once -+ return if defined $self->{dcc_version}; -+ $self->{dcc_version} =3D '?'; - = - my $conf =3D $self->{main}->{conf}; -- return if !$conf->{use_dcc}; - = -- my $dcchome =3D $conf->{dcc_home} || ''; - = -- # If we're not given the DCC homedir, try getting DCC to tell us it. -- # If that fails, try the DCC default homedir of '/var/dcc'. -- if ($dcchome eq '') { -+ # Get the DCC software version for talking to dccifd and formating th= e -+ # dccifd options and the built-in DCC homedir. Use -q to prevent del= ays. -+ my $cdcc_home; -+ my $cdcc =3D $self->dcc_pgm_path('cdcc'); -+ my $cmd =3D '-qV homedir libexecdir'; -+ if ($cdcc && open(CDCC, "$cdcc $cmd 2>&1 |")) { -+ my $cdcc_output =3D do { local $/ =3D undef; <CDCC> }; -+ close CDCC; - = -- my $cdcc =3D Mail::SpamAssassin::Util::find_executable_in_env_path(= 'cdcc'); -+ $cdcc_output =3D~ s/\n/ /g; # everything in 1 line for debugging -+ dbg("dcc: `%s %s` reports '%s'", $cdcc, $cmd, $cdcc_output); -+ $self->{dcc_version} =3D ($cdcc_output =3D~ /^(\d+\.\d+\.\d+)/) ? $= 1 : ''; -+ $cdcc_home =3D ($cdcc_output =3D~ /\s+homedir=3D(\S+)/) ? $1 : ''; -+ if ($cdcc_output =3D~ /\s+libexecdir=3D(\S+)/) { -+ $self->ck_dir($1, 'dcc_libexec', 'cdcc'); -+ } -+ } - = -- my $cdcc_home =3D ''; -- if ($cdcc && -x $cdcc && open(CDCC, "$cdcc homedir 2>&1|")) { -- dbg("dcc: dcc_home not set, querying cdcc utility"); -- $cdcc_home =3D <CDCC> || ''; -- close CDCC; -+ # without a home, try the homedir from cdcc -+ if (!$conf->{dcc_home} && $cdcc_home) { -+ $self->ck_dir($cdcc_home, 'dcc_home', 'cdcc'); -+ } -+ # finally fall back to /var/dcc -+ if (!$conf->{dcc_home}) { -+ $self->ck_dir($conf->{dcc_home} =3D '/var/dcc', 'dcc_home', 'defaul= t') -+ } - = -- chomp $cdcc_home; -- $cdcc_home =3D~ s/\s+homedir=3D//; -- dbg("dcc: cdcc reports homedir as '%s'", $cdcc_home); -- } -- -- # try first with whatever the cdcc utility reported -- my $cdcc_home_errno =3D 0; -- if ($cdcc_home eq '') { -- $cdcc_home_errno =3D ENOENT; -- } elsif (!stat($cdcc_home)) { -- $cdcc_home_errno =3D 0+$!; -- } -- if ($cdcc_home_errno =3D=3D ENOENT) { -- # no such file -- } elsif ($cdcc_home_errno !=3D 0) { -- dbg("dcc: cdcc reported homedir $cdcc_home is not accessible: $!"= ); -- } elsif (!-d _) { -- dbg("dcc: cdcc reported homedir $cdcc_home is not a directory"); -- } else { # ok -- dbg("dcc: cdcc reported homedir $cdcc_home exists, using it"); -- $dcchome =3D untaint_var($cdcc_home); -- } -- -- # try falling back to /var/dcc -- if ($dcchome eq '') { -- my $var_dcc_errno =3D stat('/var/dcc') ? 0 : 0+$!; -- if ($var_dcc_errno =3D=3D ENOENT) { -- # no such file -- } elsif ($var_dcc_errno !=3D 0) { -- dbg("dcc: dcc_home not set and dcc default homedir /var/dcc ". -- "is not accessible: $!"); -- } elsif (!-d _) { -- dbg("dcc: dcc_home not set and dcc default homedir /var/dcc ". -- "is not a directory"); -- } else { # ok -- dbg("dcc: dcc_home not set but dcc default homedir /var/dcc exi= sts, ". -- "using it"); -- $dcchome =3D '/var/dcc'; -+ # fall back to $conf->{dcc_home}/libexec or /var/dcc/libexec for dccs= ight -+ if (!$conf->{dcc_libexec}) { -+ $self->ck_dir($conf->{dcc_home} . '/libexec', 'dcc_libexec', 'dcc_h= ome'); - } -+ if (!$conf->{dcc_libexec}) { -+ $self->ck_dir('/var/dcc/libexec', 'dcc_libexec', 'dcc_home'); - } - = -- if ($dcchome eq '') { -- dbg("dcc: unable to get homedir from cdcc ". -- "and the dcc default homedir was not found"); -- } -- -- # Remember found homedir path -- dbg("dcc: using '%s' as DCC homedir", $dcchome); -- $conf->{dcc_home} =3D $dcchome; -+ # format options for dccifd -+ my $opts =3D ($conf->{dccifd_options} || '') . "\n"; -+ if ($self->{dcc_version} =3D~ /\d+\.(\d+)\.(\d+)$/ && -+ ($1 < 3 || ($1 =3D=3D 3 && $2 < 123))) { -+ if ($1 < 3 || ($1 =3D=3D 3 && $2 < 50)) { -+ info("dcc: DCC version $self->{dcc_version} is years old, ". -+ "obsolete, and likely to cause problems. ". -+ "See http://www.dcc-servers.net/dcc/old-versions.html"); -+ } -+ $self->{dccifd_lookup_options} =3D "header " . $opts; -+ $self->{dccifd_report_options} =3D "header spam " . $opts; -+ } else { -+ # dccifd after version 1.2.123 understands "cksums" and "no-grey" -+ $self->{dccifd_lookup_options} =3D "cksums grey-off " . $opts; -+ $self->{dccifd_report_options} =3D "header spam grey-off " . $opts;= - } - } - = --sub is_dccifd_available { -- my ($self) =3D @_; -- -+sub dcc_pgm_path { -+ my ($self, $pgm) =3D @_; -+ my $pgmpath; - my $conf =3D $self->{main}->{conf}; -- $self->{dccifd_available} =3D 0; - = -- if (!$conf->{use_dcc}) { -- dbg("dcc: dccifd is not available: use_dcc is false"); -- } elsif (defined $conf->{dcc_dccifd_host}) { -- dbg("dcc: dccifd inet socket chosen: [%s]:%s", -- $conf->{dcc_dccifd_host}, $conf->{dcc_dccifd_port}); -- $self->{dccifd_available} =3D 1; -- } else { -- my $sockpath =3D $conf->{dcc_dccifd_socket}; -- my $dcchome =3D $conf->{dcc_home}; -- if (defined $sockpath) { -- dbg("dcc: dccifd local socket chosen: %s", $sockpath); -- } elsif (defined $conf->{dcc_dccifd_path_raw}) { -- # avoid falling back to defaults if explicitly provided but wrong= -- } elsif (defined $dcchome && $dcchome ne '' && -S "$dcchome/dccifd"= ) { -- $sockpath =3D "$dcchome/dccifd"; -- $conf->{dcc_dccifd_socket} =3D $sockpath; -- dbg("dcc: dccifd default local socket chosen: %s", $sockpath); -+ $pgmpath =3D $conf->{dcc_path}; -+ if (defined $pgmpath && $pgmpath ne '') { -+ # accept explicit setting for dccproc -+ return $pgmpath if $pgm eq 'dccproc'; -+ # try adapting it for cdcc and everything else -+ if ($pgmpath =3D~ s{[^/]+\z}{$pgm}s) { -+ $pgmpath =3D untaint_file_path($pgmpath); -+ if (-x $pgmpath) { -+ dbg("dcc: dcc_pgm_path, found %s in dcc_path: %s", $pgm,$pgmpat= h); -+ return $pgmpath; - } -- if (defined $sockpath && -S $sockpath && -w _ && -r _) { -- $self->{dccifd_available} =3D 1; -- } elsif (!defined $conf->{dcc_dccifd_path_raw}) { -- dbg("dcc: dccifd is not available: no r/w dccifd socket found"); -- } else { -- dbg("dcc: dccifd is not available: no r/w dccifd socket found: %s= ", -- $conf->{dcc_dccifd_path_raw}); - } - } - = -- return $self->{dccifd_available}; -+ $pgmpath =3D Mail::SpamAssassin::Util::find_executable_in_env_path($p= gm); -+ if (defined $pgmpath) { -+ dbg("dcc: dcc_pgm_path, found %s in env.path: %s", $pgm,$pgmpath); -+ return $pgmpath; -+ } -+ -+ # try dcc_home/bin, dcc_libexec, and some desperate last attempts -+ foreach my $dir ($conf->{dcc_home}.'/bin', $conf->{dcc_libexec}, -+ '/usr/local/bin', '/usr/local/dcc', '/var/dcc') { -+ $pgmpath =3D $dir . '/' . $pgm; -+ if (-x $pgmpath) { -+ dbg("dcc: dcc_pgm_path, found %s in %s: %s", $pgm,$dir,$pgmpath);= -+ return $pgmpath; -+ } -+ } -+ -+ return; - } - = --sub is_dccproc_available { -+sub is_dccifd_available { - my ($self) =3D @_; - my $conf =3D $self->{main}->{conf}; - = -- $self->{dccproc_available} =3D 0; -+ # dccifd remains available until it breaks -+ return $self->{dccifd_available} if $self->{dccifd_available}; - = -- if (!$conf->{use_dcc}) { -- dbg("dcc: dccproc is not available: use_dcc is false"); -- return 0; -+ # deal with configured INET socket -+ if (defined $conf->{dcc_dccifd_host}) { -+ dbg("dcc: dccifd is available via INET socket [%s]:%s", -+ $conf->{dcc_dccifd_host}, $conf->{dcc_dccifd_port}); -+ return ($self->{dccifd_available} =3D 1); - } -- my $dcchome =3D $conf->{dcc_home} || ''; -- my $dccproc =3D $conf->{dcc_path} || ''; - = -- if ($dccproc eq '' && ($dcchome ne '' && -x "$dcchome/bin/dccproc")) = { -- $dccproc =3D "$dcchome/bin/dccproc"; -+ # the first time here, compute a default local socket based on DCC ho= me -+ # from self->find_dcc_home() called elsewhere -+ my $sockpath =3D $conf->{dcc_dccifd_socket}; -+ if (!$sockpath) { -+ if ($conf->{dcc_dccifd_path_raw}) { -+ $sockpath =3D $conf->{dcc_dccifd_path_raw}; -+ } else { -+ $sockpath =3D "$conf->{dcc_home}/dccifd"; - } -- if ($dccproc eq '') { -- $dccproc =3D Mail::SpamAssassin::Util::find_executable_in_env_path(= 'dccproc'); -+ $conf->{dcc_dccifd_socket} =3D $sockpath; - } - = -- unless (defined $dccproc && $dccproc ne '' && -x $dccproc) { -- dbg("dcc: dccproc is not available: no dccproc executable found"); -- return 0; -- } -+ # check the socket every time because it can appear and disappear -+ return ($self->{dccifd_available} =3D 1) if (-S $sockpath && -w _ && = -r _); - = -- # remember any found dccproc -+ dbg("dcc: dccifd is not available; no r/w socket at %s", $sockpath); -+ return ($self->{dccifd_available} =3D 0); -+} -+ -+sub is_dccproc_available { -+ my ($self) =3D @_; -+ my $conf =3D $self->{main}->{conf}; -+ -+ # dccproc remains (un)available so check only once -+ return $self->{dccproc_available} if defined $self->{dccproc_availab= le}; -+ -+ my $dccproc =3D $conf->{dcc_path}; -+ if (!defined $dccproc || $dccproc eq '') { -+ $dccproc =3D $self->dcc_pgm_path('dccproc'); - $conf->{dcc_path} =3D $dccproc; -+ if (!$dccproc || ! -x $dccproc) { -+ dbg("dcc: dccproc is not available: no dccproc executable found")= ; -+ return ($self->{dccproc_available} =3D 0); -+ } -+ } - = -- dbg("dcc: dccproc is available: %s", $conf->{dcc_path}); -- $self->{dccproc_available} =3D 1; -- return 1; -+ dbg("dcc: %s is available", $conf->{dcc_path}); -+ return ($self->{dccproc_available} =3D 1); - } - = - sub dccifd_connect { -- my($self) =3D @_; -+ my($self, $tag) =3D @_; - my $conf =3D $self->{main}->{conf}; - my $sockpath =3D $conf->{dcc_dccifd_socket}; -- my $host =3D $conf->{dcc_dccifd_host}; -- my $port =3D $conf->{dcc_dccifd_port}; - my $sock; -+ - if (defined $sockpath) { -- dbg("dcc: connecting to a local socket %s", $sockpath); -- $sock =3D IO::Socket::UNIX->new( -- Type =3D> SOCK_STREAM, Peer =3D> $sockpath); -- $sock or die "dcc: failed to connect to a socket $sockpath: $!\n"; -- } elsif (defined $host) { -- my $specified_path =3D $conf->{dcc_dccifd_path_raw}; -- if ($host eq '') { -- die "dcc: empty host specification: $specified_path\n"; -- } -- if (!defined $port || $port !~ /^\d+\z/ || $port < 1 || $port > 655= 35) { -- die "dcc: bad TCP port number: $specified_path\n"; -- } -- my $is_inet4 =3D $host =3D~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/= ; -- if ($is_inet4) { # inet4 socket (IPv4 address) -- dbg("dcc: connecting to inet4 socket [%s]:%s", $host,$port); -- $sock =3D IO::Socket::INET->new( -- Proto =3D> 'tcp', PeerAddr =3D> $host, PeerPort =3D> $p= ort); -- } else { -- if (!defined $have_inet6) { -- $have_inet6 =3D eval { require IO::Socket::INET6 }; -- $have_inet6 =3D 0 if !defined $have_inet6; -+ $sock =3D IO::Socket::UNIX->new(Type =3D> SOCK_STREAM, Peer =3D> $s= ockpath); -+ if ($sock) { -+ dbg("$tag connected to local socket %s", $sockpath); -+ return $sock; - } -- if (!$have_inet6) { # fallback to an inet4 socket (IPv4) -- dbg("dcc: connecting(2) to inet4 socket [%s]:%s", $host,$port);= -- $sock =3D IO::Socket::INET->new( -- Proto =3D> 'tcp', PeerAddr =3D> $host, PeerPort =3D> = $port); -- } else { # inet6 socket (IPv6) or a host name -- dbg("dcc: connecting to inet6 socket [%s]:%s", $host,$port); -+ $self->{dccifd_available} =3D 0; -+ info("$tag failed to connect to local socket $sockpath"); -+ return $sock -+ } -+ -+ # must be TCP/IP -+ my $host =3D $conf->{dcc_dccifd_host}; -+ my $port =3D $conf->{dcc_dccifd_port}; -+ -+ if ($conf->{dcc_dccifd_IPv6}) { -+ # try IPv6 if we can with a host name or non-IPv4 address -+ dbg("$tag connecting to inet6 socket [%s]:%s", $host,$port); - $sock =3D IO::Socket::INET6->new( - Proto =3D> 'tcp', PeerAddr =3D> $host, PeerPort =3D> = $port); -+ # fall back to IPv4 if that failed - } -+ if (!$sock) { -+ dbg("$tag connecting to inet4 socket [%s]:%s", $host, $port); -+ $sock =3D IO::Socket::INET->new( -+ Proto =3D> 'tcp', PeerAddr =3D> $host, PeerPort =3D> $port); - } -- $sock or die "dcc: failed to connect to [$host]:$port : $!\n"; -- } else { -- die "dcc: dccifd socket not provided: $conf->{dcc_dccifd_path_raw}\= n"; -- } -+ -+ info("failed to connect to [$host]:$port : $!") if !$sock; - return $sock; - } - = -+# check for dccifd every time in case enough uses of dccproc starts dcc= ifd - sub get_dcc_interface { - my ($self) =3D @_; -+ my $conf =3D $self->{main}->{conf}; - = -- if ($self->is_dccifd_available()) { -- $self->{dcc_interface} =3D "dccifd"; -- $self->{dcc_disabled} =3D 0; -- } -- elsif ($self->is_dccproc_available()) { -- $self->{dcc_interface} =3D "dccproc"; -- $self->{dcc_disabled} =3D 0; -+ if (!$conf->{use_dcc}) { -+ $self->{dcc_disabled} =3D 1; -+ return; - } -- else { -- dbg("dcc: dccifd and dccproc are not available, disabling DCC"); -- $self->{dcc_interface} =3D "none"; -+ -+ $self->find_dcc_home(); -+ if (!$self->is_dccifd_available() && !$self->is_dccproc_available()) = { -+ dbg("dcc: dccifd and dccproc are not available"); - $self->{dcc_disabled} =3D 1; - } -+ -+ $self->{dcc_disabled} =3D 0; - } - = - sub dcc_query { -- my ($self, $permsgstatus, $full) =3D @_; -+ my ($self, $permsgstatus, $fulltext) =3D @_; - = - $permsgstatus->{dcc_checked} =3D 1; - = -+ if (!$self->{main}->{conf}->{use_dcc}) { -+ dbg("dcc: DCC is not available: use_dcc is 0"); -+ return; -+ } -+ - # initialize valid tags - $permsgstatus->{tag_data}->{DCCB} =3D ""; - $permsgstatus->{tag_data}->{DCCR} =3D ""; - $permsgstatus->{tag_data}->{DCCREP} =3D ""; - = -- # short-circuit if there's already a X-DCC header with value of -- # "bulk" from an upstream DCC check -- if ($permsgstatus->get('ALL') =3D~ -- /^(X-DCC-([^:]{1,80})?-?Metrics:.*bulk.*)$/m) { -- $permsgstatus->{dcc_response} =3D $1; -+ if ($$fulltext eq '') { -+ dbg("dcc: empty message; skipping dcc check"); - return; - } - = -- my $timer =3D $self->{main}->time_method("check_dcc"); -+ if ($permsgstatus->get('ALL') =3D~ /^(X-DCC-.*-Metrics:.*)$/m) { -+ $permsgstatus->{dcc_raw_x_dcc} =3D $1; -+ # short-circuit if there is already a X-DCC header with value of -+ # "bulk" from an upstream DCC check -+ # require "bulk" because then at least one body checksum will be "m= any" -+ # and so we know the X-DCC header is not forged by spammers -+ return if $permsgstatus->{dcc_raw_x_dcc} =3D~ / bulk /; -+ } - = -- $self->find_dcc_home(); -+ my $timer =3D $self->{main}->time_method("check_dcc"); - = - $self->get_dcc_interface(); -- my $result; -- if ($self->{dcc_disabled}) { -- $result =3D 0; -- } elsif ($$full eq '') { -- dbg("dcc: empty message, skipping dcc check"); -- $result =3D 0; -- } elsif ($self->{dccifd_available}) { -- my $client =3D $permsgstatus->{relays_external}->[0]->{ip}; -- my $clientname =3D $permsgstatus->{relays_external}->[0]->{rdns}; -- my $helo =3D $permsgstatus->{relays_external}->[0]->{helo} || ""; -- if ($client) { -- $client =3D $client . "\r" . $clientname if $clientname; -- } else { -- $client =3D "0.0.0.0"; -- } -- $self->dccifd_lookup($permsgstatus, $full, $client, $clientname, $h= elo); -- } else { -- my $client =3D $permsgstatus->{relays_external}->[0]->{ip}; -- $self->dccproc_lookup($permsgstatus, $full, $client); -- } -+ return if $self->{dcc_disabled}; -+ -+ my $envelope =3D $permsgstatus->{relays_external}->[0]; -+ ($permsgstatus->{dcc_raw_x_dcc}, -+ $permsgstatus->{dcc_cksums}) =3D $self->ask_dcc("dcc:", $permsgstatu= s, -+ $fulltext, $envelope); - } - = - sub check_dcc { -@@ -609,28 +681,27 @@ - = - $self->dcc_query($permsgstatus, $full) if !$permsgstatus->{dcc_check= ed}; - = -- my $response =3D $permsgstatus->{dcc_response}; -- return 0 if !defined $response || $response eq ''; -+ my $x_dcc =3D $permsgstatus->{dcc_raw_x_dcc}; -+ return 0 if !defined $x_dcc || $x_dcc eq ''; - = -- local($1,$2); -- if ($response =3D~ /^X-DCC-(.*)-Metrics: (.*)$/) { -- $permsgstatus->{tag_data}->{DCCB} =3D $1; -- $permsgstatus->{tag_data}->{DCCR} =3D $2; -+ if ($x_dcc =3D~ /^X-DCC-(.*)-Metrics: (.*)$/) { -+ $permsgstatus->set_tag('DCCB', $1); -+ $permsgstatus->set_tag('DCCR', $2); - } -- $response =3D~ s/many/999999/ig; -- $response =3D~ s/ok\d?/0/ig; -+ $x_dcc =3D~ s/many/999999/ig; -+ $x_dcc =3D~ s/ok\d?/0/ig; - = - my %count =3D (body =3D> 0, fuz1 =3D> 0, fuz2 =3D> 0, rep =3D> 0); -- if ($response =3D~ /\bBody=3D(\d+)/) { -+ if ($x_dcc =3D~ /\bBody=3D(\d+)/) { - $count{body} =3D $1+0; - } -- if ($response =3D~ /\bFuz1=3D(\d+)/) { -+ if ($x_dcc =3D~ /\bFuz1=3D(\d+)/) { - $count{fuz1} =3D $1+0; - } -- if ($response =3D~ /\bFuz2=3D(\d+)/) { -+ if ($x_dcc =3D~ /\bFuz2=3D(\d+)/) { - $count{fuz2} =3D $1+0; - } -- if ($response =3D~ /\brep=3D(\d+)/) { -+ if ($x_dcc =3D~ /\brep=3D(\d+)/) { - $count{rep} =3D $1+0; - } - if ($count{body} >=3D $conf->{dcc_body_max} || -@@ -651,185 +722,185 @@ - } - = - sub check_dcc_reputation_range { -- my ($self, $permsgstatus, $full, $min, $max) =3D @_; -- $self->dcc_query($permsgstatus, $full) if !$permsgstatus->{dcc_check= ed}; -+ my ($self, $permsgstatus, $fulltext, $min, $max) =3D @_; -+ -+ # this is called several times per message, so parse the X-DCC header= once -+ my $dcc_rep =3D $permsgstatus->{dcc_rep}; -+ if (!defined $dcc_rep) { -+ $self->dcc_query($permsgstatus, $fulltext) if !$permsgstatus->{dcc= _checked}; -+ my $x_dcc =3D $permsgstatus->{dcc_raw_x_dcc}; -+ if (defined $x_dcc && $x_dcc =3D~ /\brep=3D(\d+)/) { -+ $dcc_rep =3D $1+0; -+ $permsgstatus->set_tag('DCCREP', $dcc_rep); -+ } else { -+ $dcc_rep =3D -1; -+ } -+ $permsgstatus->{dcc_rep} =3D $dcc_rep; -+ } - = -- my $response =3D $permsgstatus->{dcc_response}; -- return 0 if !defined $response || $response eq ''; -+ # no X-DCC header or no reputation in the X-DCC header, perhaps for l= ack -+ # of data in the DCC Reputation server -+ return 0 if $dcc_rep < 0; - = -+ # cover the entire range of reputations if not told otherwise - $min =3D 0 if !defined $min; -- $max =3D 999 if !defined $max; -+ $max =3D 100 if !defined $max; - = -- local $1; -- my $dcc_rep; -- $dcc_rep =3D $1+0 if defined $response && $response =3D~ /\brep=3D(\= d+)/; -- if (defined $dcc_rep) { -- $dcc_rep =3D int($dcc_rep); # just in case, rule ranges are intege= r percents - my $result =3D $dcc_rep >=3D $min && $dcc_rep <=3D $max ? 1 : 0; - dbg("dcc: dcc_rep %s, min %s, max %s =3D> result=3D%s", - $dcc_rep, $min, $max, $result?'YES':'no'); -- $permsgstatus->{tag_data}->{DCCREP} =3D $dcc_rep; -- return $dcc_rep >=3D $min && $dcc_rep <=3D $max ? 1 : 0; -+ return $result; -+} -+ -+# get the X-DCC header line and save the checksums from dccifd or dccpr= oc -+sub parse_dcc_response { -+ my ($self, $resp) =3D @_; -+ my ($raw_x_dcc, $cksums); -+ -+ # The first line is the header we want. It uses SMTP folded whitespa= ce -+ # if it is long. The folded whitespace is always a single \t. -+ chomp($raw_x_dcc =3D shift @$resp); -+ my $v; -+ while (($v =3D shift @$resp) && $v =3D~ s/^\t(.+)\s*\n/ $1/) { -+ $raw_x_dcc .=3D $v; -+ } -+ -+ # skip the "reported:" line between the X-DCC header and any checksum= s -+ # remove ':' to avoid a bug in versions 1.3.115 - 1.3.122 in dccsight= -+ # with the length of "Message-ID:" -+ $cksums =3D ''; -+ while (($v =3D shift @$resp) && $v =3D~ s/^([^:]*):/$1/) { -+ $cksums .=3D $v; - } -- return 0; -+ -+ return ($raw_x_dcc, $cksums); - } - = --sub dccifd_lookup { -- my ($self, $permsgstatus, $fulltext, $client, $clientname, $helo) =3D= @_; -+sub ask_dcc { -+ my ($self, $tag, $permsgstatus, $fulltext, $envelope) =3D @_; - my $conf =3D $self->{main}->{conf}; -- my $response; -- my $left; -- my $right; -- my $timeout =3D $conf->{dcc_timeout}; -- my $opts =3D $conf->{dccifd_options}; -- my @opts =3D !defined $opts ? () : split(' ',$opts); -+ my ($pgm, $err, $sock, $pid, @resp); -+ my ($client, $clientname, $helo, $opts); - = - $permsgstatus->enter_helper_run_mode(); - = -+ my $timeout =3D $conf->{dcc_timeout}; - my $timer =3D Mail::SpamAssassin::Timeout->new( - { secs =3D> $timeout, deadline =3D> $permsgstatus->{master_d= eadline} }); -- my $err =3D $timer->run_and_catch(sub { - = -+ $err =3D $timer->run_and_catch(sub { - local $SIG{PIPE} =3D sub { die "__brokenpipe__ignore__\n" }; - = -- my $sock =3D $self->dccifd_connect(); -- $sock or die "dcc: failed to connect to a dccifd socket"; -- -- # send the options and other parameters to the daemon -- $sock->print("header " . join(" ",@opts) . "\n") -- or die "dcc: failed write"; # options= -- $sock->print($client . "\n") or die "dcc: failed write"; # client -- $sock->print($helo . "\n") or die "dcc: failed write"; # HELO va= lue -- $sock->print("\n") or die "dcc: failed write"; # sender -- $sock->print("unknown\r\n") or die "dcc: failed write"; # recipie= nts -- $sock->print("\n") or die "dcc: failed write"; # recipie= nts -- -- $sock->print($$fulltext) or die "dcc: failed write"; -- -- $sock->shutdown(1) or die "dcc: failed socket shutdown: $!"; -- -- $sock->getline() or die "dcc: failed read status"; -- $sock->getline() or die "dcc: failed read multistatus"; -+ # prefer dccifd to dccproc -+ if ($self->{dccifd_available}) { -+ $pgm =3D 'dccifd'; - = -- my @null =3D $sock->getlines(); -- if (!@null) { -- # no facility prefix on this -- die "dcc: failed to read header\n"; -- } -+ $sock =3D $self->dccifd_connect($tag); -+ if (!$sock) { -+ $self->{dccifd_available} =3D 0; -+ die("dccproc not available") if (!$self->is_dccproc_available()); - = -- # the first line will be the header we want to look at -- chomp($response =3D shift @null); -- # but newer versions of DCC fold the header if it's too long... -- while (my $v =3D shift @null) { -- last unless ($v =3D~ s/^\s+/ /); # if this line wasn't folded, s= top -- chomp $v; -- $response .=3D $v; -+ # fall back on dccproc if the socket is an orphan from -+ # a killed dccifd daemon or some other obvious (no timeout) problem -+ dbg("$tag fall back on dccproc"); - } -- -- dbg("dcc: dccifd got response: %s", $response); -- = -- }); -- -- $permsgstatus->leave_helper_run_mode(); -- -- if ($timer->timed_out()) { -- dbg("dcc: dccifd check timed out after $timeout secs."); -- return; - } - = -- if ($err) { -- chomp $err; -- warn("dcc: dccifd -> check skipped: $err\n"); -- return; -- } -+ if ($self->{dccifd_available}) { - = -- if (!defined $response || $response !~ /^X-DCC/) { -- dbg("dcc: dccifd check failed - no X-DCC returned: %s", $response);= -- return; -+ # send the options and other parameters to the daemon -+ $client =3D $envelope->{ip}; -+ $clientname =3D $envelope->{rdns}; -+ if (!defined $client) { -+ $client =3D ''; -+ } else { -+ $client .=3D ("\r" . $clientname) if defined $clientname; - } -+ $helo =3D $envelope->{helo} || ''; -+ if ($tag ne "dcc:") { -+ $opts =3D $self->{dccifd_report_options} -+ } else { -+ $opts =3D $self->{dccifd_lookup_options}; -+ # only query if there is an X-DCC header -+ $opts =3D~ s/grey-off/& query/ if defined $permsgstatus->{dcc_raw_x_dc= c}; -+ } -+ $sock->print($opts) or die "failed write options\n"; -+ $sock->print($client . "\n") or die "failed write SMTP client\n";= -+ $sock->print($helo . "\n") or die "failed write HELO value\n"; -+ $sock->print("\n") or die "failed write sender\n"; -+ $sock->print("unknown\n\n") or die "failed write 1 recipient\n";= -+ $sock->print($$fulltext) or die "failed write mail message\n"= ; -+ $sock->shutdown(1) or die "failed socket shutdown: $!"; - = -- $response =3D~ s/[ \t]\z//; # strip trailing whitespace -- $permsgstatus->{dcc_response} =3D $response; --} -+ $sock->getline() or die "failed read status\n"; -+ $sock->getline() or die "failed read multistatus\n"; - = --sub dccproc_lookup { -- my ($self, $permsgstatus, $fulltext, $client) =3D @_; -- my $conf =3D $self->{main}->{conf}; -- my $response; -- my %count =3D (body =3D> 0, fuz1 =3D> 0, fuz2 =3D> 0, rep =3D> 0); -- my $timeout =3D $conf->{dcc_timeout}; -+ @resp =3D $sock->getlines(); -+ die "failed to read dccifd response\n" if !@resp; - = -- $permsgstatus->enter_helper_run_mode(); -- -- # use a temp file here -- open2() is unreliable, buffering-wise, unde= r spamd -+ } else { -+ $pgm =3D 'dccproc'; -+ # use a temp file -- open2() is unreliable, buffering-wise, under= spamd -+ # first ensure that we do not hit a stray file from some other fi= lter. -+ $permsgstatus->delete_fulltext_tmpfile(); - my $tmpf =3D $permsgstatus->create_fulltext_tmpfile($fulltext); -- my $pid; -- -- my $timer =3D Mail::SpamAssassin::Timeout->new( -- { secs =3D> $timeout, deadline =3D> $permsgstatus->{master_d= eadline} }); -- my $err =3D $timer->run_and_catch(sub { -- -- local $SIG{PIPE} =3D sub { die "__brokenpipe__ignore__\n" }; - = -- # note: not really tainted, this came from system configuration fil= e -- my $path =3D untaint_file_path($conf->{dcc_path}); -- -- my $opts =3D $conf->{dcc_options}; -+ my $path =3D $conf->{dcc_path}; -+ $opts =3D $conf->{dcc_options}; - my @opts =3D !defined $opts ? () : split(' ',$opts); - untaint_var(\@opts); -+ unshift(@opts, '-w', 'whiteclnt'); -+ $client =3D $envelope->{ip}; -+ if ($client) { -+ unshift(@opts, '-a', untaint_var($client)); -+ } else { -+ # get external relay IP address from Received: header if not available= -+ unshift(@opts, '-R'); -+ } -+ if ($tag eq "dcc:") { -+ # query instead of report if there is an X-DCC header from upstream -+ unshift(@opts, '-Q') if defined $permsgstatus->{dcc_raw_x_dcc}; -+ } else { -+ # learn or report spam -+ unshift(@opts, '-t', 'many'); -+ } - = -- unshift(@opts, "-a", -- untaint_var($client)) if defined $client && $client ne '';= -- -- dbg("dcc: opening pipe: %s", -- join(' ', $path, "-H", "-x", "0", @opts, "< $tmpf")); -+ dbg("$tag opening pipe to %s", -+ join(' ', $path, "-C", "-x", "0", @opts, "<$tmpf")); - = - $pid =3D Mail::SpamAssassin::Util::helper_app_pipe_open(*DCC, -- $tmpf, 1, $path, "-H", "-x", "0", @opts); -+ $tmpf, 1, $path, "-C", "-x", "0", @opts); - $pid or die "$!\n"; - = - # read+split avoids a Perl I/O bug (Bug 5985) - my($inbuf,$nread,$resp); $resp =3D ''; - while ( $nread=3Dread(DCC,$inbuf,8192) ) { $resp .=3D $inbuf } - defined $nread or die "error reading from pipe: $!"; -- my @null =3D split(/^/m, $resp, -1); undef $resp; -+ @resp =3D split(/^/m, $resp, -1); undef $resp; - = - my $errno =3D 0; close DCC or $errno =3D $!; - proc_status_ok($?,$errno) -- or info("dcc: [%s] finished: %s", $pid, exit_status_str($?,$errno= )); -- -- if (!@null) { -- # no facility prefix on this -- die "failed to read header\n"; -- } -+ or info("$tag [%s] finished: %s", $pid, exit_status_str($?,$errno));= - = -- # the first line will be the header we want to look at -- chomp($response =3D shift @null); -- # but newer versions of DCC fold the header if it's too long... -- while (my $v =3D shift @null) { -- last unless ($v =3D~ s/^\s+/ /); # if this line wasn't folded, s= top -- chomp $v; -- $response .=3D $v; -+ die "failed to read X-DCC header from dccproc\n" if !@resp; - } -- -- unless (defined($response)) { -- # no facility prefix on this -- die "no response\n"; # yes, this is possible -- } -- -- dbg("dcc: got response: %s", $response); -- - }); - = -+ if ($pgm eq 'dccproc') { - if (defined(fileno(*DCC))) { # still open - if ($pid) { -- if (kill('TERM',$pid)) { dbg("dcc: killed stale helper [$pid]") }= -- else { dbg("dcc: killing helper application [$pid] failed: $!") }= -+ if (kill('TERM',$pid)) { -+ dbg("$tag killed stale dccproc process [$pid]") -+ } else { -+ dbg("$tag killing dccproc process [$pid] failed: $!") -+ } - } - my $errno =3D 0; close(DCC) or $errno =3D $!; -- proc_status_ok($?,$errno) -- or info("dcc: [%s] terminated: %s", $pid, exit_status_str($?,$err= no)); -+ proc_status_ok($?,$errno) or info("$tag [%s] dccproc terminated: = %s", -+ $pid, exit_status_str($?,$errno)); -+ } - } -+ - $permsgstatus->leave_helper_run_mode(); - = - if ($timer->timed_out()) { -@@ -833,204 +904,182 @@ - $permsgstatus->leave_helper_run_mode(); - = - if ($timer->timed_out()) { -- dbg("dcc: check timed out after $timeout seconds"); -- return; -+ dbg("$tag $pgm timed out after $timeout seconds"); -+ return (undef, undef); - } - = - if ($err) { - chomp $err; -- if ($err eq "__brokenpipe__ignore__") { -- dbg("dcc: check failed: broken pipe"); -- } elsif ($err eq "no response") { -- dbg("dcc: check failed: no response"); -- } else { -- warn("dcc: check failed: $err\n"); -- } -- return; -+ info("$tag $pgm failed: $err\n"); -+ return (undef, undef); - } - = -- if (!defined($response) || $response !~ /^X-DCC/) { -- $response ||=3D ''; -- dbg("dcc: check failed: no X-DCC returned (did you create a map fil= e?): %s", $response); -- return; -+ my ($raw_x_dcc, $cksums) =3D $self->parse_dcc_response(\@resp); -+ if (!defined $raw_x_dcc || $raw_x_dcc !~ /^X-DCC/) { -+ info("$tag instead of X-DCC header, $pgm returned '%s'", $raw_x_dcc= ); -+ return (undef, undef); - } -- -- $permsgstatus->{dcc_response} =3D $response; -+ dbg("$tag %s responded with '%s'", $pgm, $raw_x_dcc); -+ return ($raw_x_dcc, $cksums); - } - = --# only supports dccproc right now --sub plugin_report { -+# tell DCC server that the message is spam according to SpamAssassin -+sub check_post_learn { - my ($self, $options) =3D @_; - = -- return if $options->{report}->{options}->{dont_report_to_dcc}; -- $self->get_dcc_interface(); -- return if $self->{dcc_disabled}; -- -- # get the metadata from the message so we can pass the external relay= information -- $options->{msg}->extract_message_metadata($options->{report}->{main})= ; -- my $client =3D $options->{msg}->{metadata}->{relays_external}->[0]->{= ip}; -- if ($self->{dccifd_available}) { -- my $clientname =3D $options->{msg}->{metadata}->{relays_external}->= [0]->{rdns}; -- my $helo =3D $options->{msg}->{metadata}->{relays_external}->[0]->{= helo} || ""; -- if ($client) { -- if ($clientname) { -- $client =3D $client . "\r" . $clientname; -- } -- } else { -- $client =3D "0.0.0.0"; -- } -- if ($self->dccifd_report($options, $options->{text}, $client, $helo= )) { -- $options->{report}->{report_available} =3D 1; -- info("reporter: spam reported to DCC"); -- $options->{report}->{report_return} =3D 1; -+ # learn only if allowed -+ return if $self->{learn_disabled}; -+ my $conf =3D $self->{main}->{conf}; -+ if (!$conf->{use_dcc}) { -+ $self->{learn_disabled} =3D 1; -+ return; - } -- else { -- info("reporter: could not report spam to DCC via dccifd"); -+ my $learn_score =3D $conf->{dcc_learn_score}; -+ if (!defined $learn_score || $learn_score eq '') { -+ dbg("dcc: DCC learning not enabled by dcc_learn_score"); -+ $self->{learn_disabled} =3D 1; -+ return; - } -- } else { -- # use temporary file: open2() is unreliable due to buffering under = spamd -- my $tmpf =3D $options->{report}->create_fulltext_tmpfile($options->= {text}); - = -- if ($self->dcc_report($options, $tmpf, $client)) { -- $options->{report}->{report_available} =3D 1; -- info("reporter: spam reported to DCC"); -- $options->{report}->{report_return} =3D 1; -+ # and if SpamAssassin concluded that the message is spam -+ # worse than our threshold -+ my $permsgstatus =3D $options->{permsgstatus}; -+ if ($permsgstatus->is_spam()) { -+ my $score =3D $permsgstatus->get_score(); -+ my $required_score =3D $permsgstatus->get_required_score(); -+ if ($score < $required_score + $learn_score) { -+ dbg("dcc: score=3D%d required_score=3D%d dcc_learn_score=3D%d", -+ $score, $required_score, $learn_score); -+ return; - } -- else { -- info("reporter: could not report spam to DCC via dccproc"); - } -- $options->{report}->delete_fulltext_tmpfile(); -+ -+ # and if we checked the message -+ return if (!defined $permsgstatus->{dcc_raw_x_dcc}); -+ -+ # and if the DCC server thinks it was not spam -+ if ($permsgstatus->{dcc_raw_x_dcc} !~ /\b(Body|Fuz1|Fuz2)=3D\d/) { -+ dbg("dcc: already known as spam; no need to learn"); -+ return; - } -+ -+ # dccsight is faster than dccifd or dccproc if we have checksums, -+ # which we do not have with dccifd before 1.3.123 -+ my $old_cksums =3D $permsgstatus->{dcc_cksums}; -+ return if ($old_cksums && $self->dccsight_learn($permsgstatus, $old_c= ksums)); -+ -+ # Fall back on dccifd or dccproc without saved checksums or dccsight.= -+ # get_dcc_interface() was called when the message was checked -+ -+ # is getting the full text this way kosher? Is get_pristine() public= ? -+ my $fulltext =3D $permsgstatus->{msg}->get_pristine(); -+ my $envelope =3D $permsgstatus->{relays_external}->[0]; -+ my ($raw_x_dcc, $cksums) =3D $self->ask_dcc("dcc: learn:", $permsgsta= tus, -+ \$fulltext, $envelope); -+ dbg("dcc: learned as spam") if defined $raw_x_dcc; - } - = --sub dccifd_report { -- my ($self, $options, $fulltext, $client, $helo) =3D @_; -- my $conf =3D $self->{main}->{conf}; -- my $timeout =3D $conf->{dcc_timeout}; -- # instead of header use whatever the report option is -- my $opts =3D $conf->{dccifd_options}; -- my @opts =3D !defined $opts ? () : split(' ',$opts); -+sub dccsight_learn { -+ my ($self, $permsgstatus, $old_cksums) =3D @_; -+ my ($raw_x_dcc, $new_cksums); -+ -+ return 0 if !$old_cksums; -+ -+ my $dccsight =3D $self->dcc_pgm_path('dccsight'); -+ if (!$dccsight) { -+ info("dcc: cannot find dccsight") if $dccsight eq ''; -+ return 0; -+ } - = -- $options->{report}->enter_helper_run_mode(); -- my $timer =3D Mail::SpamAssassin::Timeout->new({ secs =3D> $timeout }= ); -+ $permsgstatus->enter_helper_run_mode(); - = -- my $err =3D $timer->run_and_catch(sub { -+ # use a temp file here -- open2() is unreliable, buffering-wise, unde= r spamd -+ # ensure that we do not hit a stray file from some other filter. -+ $permsgstatus->delete_fulltext_tmpfile(); -+ my $tmpf =3D $permsgstatus->create_fulltext_tmpfile(\$old_cksums); -+ my $pid; - = -+ my $timeout =3D $self->{main}->{conf}->{dcc_timeout}; -+ my $timer =3D Mail::SpamAssassin::Timeout->new( -+ { secs =3D> $timeout, deadline =3D> $permsgstatus->{master_deadline= } }); -+ my $err =3D $timer->run_and_catch(sub { - local $SIG{PIPE} =3D sub { die "__brokenpipe__ignore__\n" }; - = -- my $sock =3D $self->dccifd_connect(); -- $sock or die "report: failed to connect to a dccifd socket"; -+ dbg("dcc: opening pipe to %s", -+ join(' ', $dccsight, "-t", "many", "<$tmpf")); - = -- # send the options and other parameters to the daemon -- $sock->print("spam " . join(" ",@opts) . "\n") -- or die "report: dccifd failed write"; # options -- $sock->print($client . "\n") -- or die "report: dccifd failed write"; # client -- $sock->print($helo . "\n") -- or die "report: dccifd failed write"; # HELO value -- $sock->print("\n") -- or die "report: dccifd failed write"; # sender -- $sock->print("unknown\r\n") -- or die "report: dccifd failed write"; # recipients -- $sock->print("\n") -- or die "report: dccifd failed write"; # recipients -+ $pid =3D Mail::SpamAssassin::Util::helper_app_pipe_open(*DCC, -+ $tmpf, 1, $dccsight, "-t", "many"); -+ $pid or die "$!\n"; - = -- $sock->print($$fulltext) or die "report: dccifd failed write"; -+ # read+split avoids a Perl I/O bug (Bug 5985) -+ my($inbuf,$nread,$resp); $resp =3D ''; -+ while ( $nread=3Dread(DCC,$inbuf,8192) ) { $resp .=3D $inbuf } -+ defined $nread or die "error reading from pipe: $!"; -+ my @resp =3D split(/^/m, $resp, -1); undef $resp; - = -- $sock->shutdown(1) or die "report: dccifd failed socket shutdown: $= !"; -+ my $errno =3D 0; close DCC or $errno =3D $!; -+ proc_status_ok($?,$errno) -+ or info("dcc: [%s] finished: %s", $pid, exit_status_str($?,$errno));= - = -- $sock->getline() or die "report: dccifd failed read status"; -- $sock->getline() or die "report: dccifd failed read multistatus"; -+ die "dcc: failed to read learning response\n" if !@resp; - = -- my @ignored =3D $sock->getlines(); -+ ($raw_x_dcc, $new_cksums) =3D $self->parse_dcc_response(\@resp); - }); - = -- $options->{report}->leave_helper_run_mode(); -+ if (defined(fileno(*DCC))) { # still open -+ if ($pid) { -+ if (kill('TERM',$pid)) { -+ dbg("dcc: killed stale dccsight process [$pid]") -+ } else { -+ dbg("dcc: killing stale dccsight process [$pid] failed: $!") } -+ } -+ my $errno =3D 0; close(DCC) or $errno =3D $!; -+ proc_status_ok($?,$errno) or info("dcc: dccsight [%s] terminated: %= s", -+ $pid, exit_status_str($?,$errno)); -+ } -+ $permsgstatus->delete_fulltext_tmpfile(); -+ $permsgstatus->leave_helper_run_mode(); - = - if ($timer->timed_out()) { -- dbg("reporter: DCC report via dccifd timed out after $timeout secs.= "); -+ dbg("dcc: dccsight timed out after $timeout seconds"); - return 0; - } - = - if ($err) { - chomp $err; -- if ($err eq "__brokenpipe__ignore__") { -- dbg("reporter: DCC report via dccifd failed: broken pipe"); -- } else { -- warn("reporter: DCC report via dccifd failed: $err\n"); -- } -+ info("dcc: dccsight failed: $err\n"); - return 0; - } - = -+ if ($raw_x_dcc) { -+ dbg("dcc: learned response: %s", $raw_x_dcc); - return 1; --} -- = --sub dcc_report { -- my ($self, $options, $tmpf, $client) =3D @_; -- my $conf =3D $self->{main}->{conf}; -- my $timeout =3D $options->{report}->{conf}->{dcc_timeout}; -- -- # note: not really tainted, this came from system configuration file -- my $path =3D untaint_file_path($options->{report}->{conf}->{dcc_path}= ); -- my $opts =3D $conf->{dcc_options}; -- my @opts =3D !defined $opts ? () : split(' ',$opts); -- untaint_var(\@opts); -- -- # get the metadata from the message so we can pass the external relay= info -- -- unshift(@opts, "-a", -- untaint_var($client)) if defined $client && $client ne ''; -- -- my $timer =3D Mail::SpamAssassin::Timeout->new({ secs =3D> $timeout }= ); -- -- $options->{report}->enter_helper_run_mode(); -- my $err =3D $timer->run_and_catch(sub { -- -- local $SIG{PIPE} =3D sub { die "__brokenpipe__ignore__\n" }; -- -- dbg("report: opening pipe: %s", -- join(' ', $path, "-H", "-t", "many", "-x", "0", @opts, "< $tmpf= ")); -- -- my $pid =3D Mail::SpamAssassin::Util::helper_app_pipe_open(*DCC, -- $tmpf, 1, $path, "-H", "-t", "many", "-x", "0", @opts);= -- $pid or die "$!\n"; -+ } - = -- my($inbuf,$nread,$nread_all); $nread_all =3D 0; -- # response is ignored, just check its existence -- while ( $nread=3Dread(DCC,$inbuf,8192) ) { $nread_all +=3D $nread }= -- defined $nread or die "error reading from pipe: $!"; -+ return 0; -+} - = -- dbg("dcc: empty response") if $nread_all < 1; -+sub plugin_report { -+ my ($self, $options) =3D @_; - = -- my $errno =3D 0; close DCC or $errno =3D $!; -- # closing a pipe also waits for the process executing on the pipe t= o -- # complete, no need to explicitly call waitpid -- # my $child_stat =3D waitpid($pid,0) > 0 ? $? : undef; -- proc_status_ok($?,$errno) -- or die "dcc: reporter error: ".exit_status_str($?,$errno)."\n"; -- }); -- $options->{report}->leave_helper_run_mode(); -+ return if $options->{report}->{options}->{dont_report_to_dcc}; -+ $self->get_dcc_interface(); -+ return if $self->{dcc_disabled}; - = -- if ($timer->timed_out()) { -- dbg("reporter: DCC report via dccproc timed out after $timeout seco= nds"); -- return 0; -- } -+ # get the metadata from the message so we can report the external rel= ay -+ $options->{msg}->extract_message_metadata($options->{report}->{main})= ; -+ my $envelope =3D $options->{msg}->{metadata}->{relays_external}->[0];= -+ my ($raw_x_dcc, $cksums) =3D $self->ask_dcc("reporter:", $options->{r= eport}, -+ $options->{text}, $envelope); - = -- if ($err) { -- chomp $err; -- if ($err eq "__brokenpipe__ignore__") { -- dbg("reporter: DCC report via dccproc failed: broken pipe"); -+ if (defined $raw_x_dcc) { -+ $options->{report}->{report_available} =3D 1; -+ info("reporter: spam reported to DCC"); -+ $options->{report}->{report_return} =3D 1; - } else { -- warn("reporter: DCC report via dccproc failed: $err\n"); -+ info("reporter: could not report spam to DCC"); - } -- return 0; -- } -- -- return 1; - } - = - 1; -- --=3Dback -- --=3Dcut Index: files/patch-bug6745 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- files/patch-bug6745 (revision 346955) +++ files/patch-bug6745 (working copy) @@ -1,106 +0,0 @@ ---- lib/Mail/SpamAssassin/Logger/Syslog.pm 2012/05/14 16:28:23 1338277 -+++ lib/Mail/SpamAssassin/Logger/Syslog.pm 2012/05/14 16:31:09 1338278 -@@ -167,17 +167,21 @@ - } - $msg =3D $timestamp . ' ' . $msg if $timestamp ne ''; - = -- # important: do not call syslog() from the SIGCHLD handler -- # child_handler(). otherwise we can get into a loop if syslog() -- # forks a process -- as it does in syslog-ng apparently! (bug 3625) -- $Mail::SpamAssassin::Logger::LOG_SA{INHIBIT_LOGGING_IN_SIGCHLD_HANDLE= R} =3D 1; -+# no longer needed since a patch to bug 6745: -+# # important: do not call syslog() from the SIGCHLD handler -+# # child_handler(). otherwise we can get into a loop if syslog() -+# # forks a process -- as it does in syslog-ng apparently! (bug 3625) -+# $Mail::SpamAssassin::Logger::LOG_SA{INHIBIT_LOGGING_IN_SIGCHLD_HANDLE= R} =3D 1; -+ - my $eval_stat; - eval { - syslog($level, "%s", $msg); 1; - } or do { - $eval_stat =3D $@ ne '' ? $@ : "errno=3D$!"; chomp $eval_stat; - }; -- $Mail::SpamAssassin::Logger::LOG_SA{INHIBIT_LOGGING_IN_SIGCHLD_HANDLE= R} =3D 0; -+ -+# no longer needed since a patch to bug 6745: -+# $Mail::SpamAssassin::Logger::LOG_SA{INHIBIT_LOGGING_IN_SIGCHLD_HANDLE= R} =3D 0; - = - if (defined $eval_stat) { - if ($self->check_syslog_sigpipe($msg)) { ---- spamd/spamd.raw 2012/05/14 16:28:23 1338277 -+++ spamd/spamd.raw 2012/05/14 16:31:09 1338278 -@@ -589,6 +589,7 @@ - my $timeout_child; # processing timeout (headers->finish), 0=3Dn= o timeout - my $clients_per_child; # number of clients each child should process= - my %children; # current children -+my @children_exited; - = - if ( defined $opt{'max-children'} ) { - $childlimit =3D $opt{'max-children'}; -@@ -1033,6 +1034,8 @@ - # child_handler() if !$scaling || am_running_on_windows(); - child_handler(); # it doesn't hurt to call child_handler uncondition= ally - = -+ child_cleaner(); -+ - do_sighup_restart() if defined $got_sighup; - = - for (my $i =3D keys %children; $i < $childlimit; $i++) { -@@ -2523,7 +2526,8 @@ - my ($sig) =3D @_; - = - # do NOT call syslog here unless the child's pid is in our list of kn= own -- # children. This is due to syslog-ng brokenness -- bugs 3625, 4237. -+ # children. This is due to syslog-ng brokenness -- bugs 3625, 4237; -+ # see also bug 6745. - = - # clean up any children which have exited - for (;;) { -@@ -2534,12 +2538,23 @@ - # - my $pid =3D waitpid(-1, WNOHANG); - last if !$pid || $pid =3D=3D -1; -- my $child_stat =3D $?; -+ push(@children_exited, [$pid, $?, $sig, time]); -+ } - = -- if (!defined $children{$pid}) { -- # ignore this child; we didn't realise we'd forked it. bug 4237 -- next; -- } -+ $SIG{CHLD} =3D \&child_handler; # reset as necessary, should be at= end -+} -+ -+# takes care of dead children, as noted by a child_handler() -+# called in a main program flow (not from a signal handler) -+# -+sub child_cleaner { -+ while (@children_exited) { -+ my $tuple =3D shift(@children_exited); -+ next if !$tuple; # just in case -+ my($pid, $child_stat, $sig, $timestamp) =3D @$tuple; -+ -+ # ignore this child if we didn't realise we'd forked it. bug 4237 -+ next if !defined $children{$pid}; - = - # remove them from our child listing - delete $children{$pid}; -@@ -2550,15 +2565,10 @@ - my $sock =3D $backchannel->get_socket_for_child($pid); - if ($sock) { $sock->close(); } - } -- -- unless ($Mail::SpamAssassin::Logger::LOG_SA{INHIBIT_LOGGING_IN_SIGC= HLD_HANDLER}) { -- info("spamd: handled cleanup of child pid [%s]%s: %s", -- $pid, (defined $sig ? " due to SIG$sig" : ""), -- exit_status_str($child_stat,0)); -- } -+ info("spamd: handled cleanup of child pid [%s]%s: %s", -+ $pid, (defined $sig ? " due to SIG$sig" : ""), -+ exit_status_str($child_stat,0)); - } -- -- $SIG{CHLD} =3D \&child_handler; # reset as necessary, should be at= end - } - = - sub restart_handler { Index: files/patch-bug6937 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- files/patch-bug6937 (revision 346955) +++ files/patch-bug6937 (working copy) @@ -1,97 +0,0 @@ ---- lib/Mail/SpamAssassin/AsyncLoop.pm.orig 2011-06-07 01:59:17.00000000= 0 +0200 -+++ lib/Mail/SpamAssassin/AsyncLoop.pm 2013-05-29 01:37:58.000000000 +02= 00 -@@ -361,5 +361,12 @@ = - $now =3D time; # capture new timestamp, after possible sleep in 's= elect' - = -- while (my($key,$ent) =3D each %$pending) { -+ # A callback routine may generate another DNS query, which may inse= rt -+ # an entry into the %$pending hash thus invalidating the each() con= text. -+ # So, make sure that callbacks are not called while the each() cont= ext -+ # is open, or avoid using each(). [Bug 6937] -+ # -+ # while (my($key,$ent) =3D each %$pending) { -+ foreach my $key (keys %$pending) { -+ my $ent =3D $pending->{$key}; - my $id =3D $ent->{id}; - if (defined $ent->{poll_callback}) { # call a "poll_callback" if= exists -@@ -449,5 +456,6 @@ = - my $foundcnt =3D 0; - my $now =3D time; -- while (my($key,$ent) =3D each %$pending) { -+ foreach my $key (keys %$pending) { -+ my $ent =3D $pending->{$key}; - dbg("async: aborting after %.3f s, %s: %s", - $now - $ent->{start_time}, ---- lib/Mail/SpamAssassin/Conf/Parser.pm.orig 2011-06-07 01:59:17.000000= 000 +0200 -+++ lib/Mail/SpamAssassin/Conf/Parser.pm 2013-05-29 01:32:06.000000000 += 0200 -@@ -1249,5 +1249,5 @@ = - my $mods =3D ''; - local ($1,$2); -- if ($re =3D~ s/^m{//) { -+ if ($re =3D~ s/^m\{//) { - $re =3D~ s/}([a-z]*)$//; $mods =3D $1; - } ---- lib/Mail/SpamAssassin/DnsResolver.pm.orig 2011-06-07 01:59:17.000000= 000 +0200 -+++ lib/Mail/SpamAssassin/DnsResolver.pm 2013-05-29 01:32:06.000000000 += 0200 -@@ -441,8 +441,14 @@ = - if (!defined($timeout) || $timeout > 0) - { $timer =3D $self->{main}->time_method("poll_dns_idle") } -+ $! =3D 0; - ($nfound, $timeleft) =3D select($rout=3D$rin, undef, undef, $time= out); - } - if (!defined $nfound || $nfound < 0) { -- warn "dns: select failed: $!"; -+ if ($!) { warn "dns: select failed: $!\n" } -+ else { info("dns: select interrupted") } -+ return; -+ } elsif (!$nfound) { -+ if (!defined $timeout) { warn("dns: select returned empty-handed\= n") } -+ elsif ($timeout > 0) { dbg("dns: select timed out %.3f s", $timeo= ut) } - return; - } ---- lib/Mail/SpamAssassin/Message.pm.orig 2011-06-07 01:59:17.000000000 = +0200 -+++ lib/Mail/SpamAssassin/Message.pm 2013-05-29 01:32:06.000000000 +0200= -@@ -567,5 +567,5 @@ = - # bug 5557: windows requires tmp file be closed before it can be rm= 'd - if (ref $part->{'raw'} eq 'GLOB') { -- close($part->{'raw'}) or die "error closing input file: $!"; -+ close($part->{'raw'}) or warn "error closing input file: $!"; - } - = ---- lib/Mail/SpamAssassin/PerMsgStatus.pm.orig 2011-06-07 01:59:17.00000= 0000 +0200 -+++ lib/Mail/SpamAssassin/PerMsgStatus.pm 2013-05-29 01:32:06.000000000 = +0200 -@@ -421,6 +421,6 @@ = - } - = -- # ignore tests with 0 score in this scoreset -- next if ($scores->{$test} =3D=3D 0); -+ # ignore tests with 0 score (or undefined) in this scoreset -+ next if !$scores->{$test}; - = - # Go ahead and add points to the proper locations -@@ -1253,11 +1253,10 @@ = - my $line =3D ''; - foreach my $test (sort @{$self->{test_names_hit}}) { -- if (!$line) { -- $line .=3D $test . "=3D" . $self->{conf}->{scores}->{= $test}; -- } else { -- $line .=3D $arg . $test . "=3D" . $self->{conf}->{sco= res}->{$test}; -- } -+ my $score =3D $self->{conf}->{scores}->{$test}; -+ $score =3D '0' if !defined $score; -+ $line .=3D $arg if $line ne ''; -+ $line .=3D $test . "=3D" . $score; - } -- $line ? $line : 'none'; -+ $line ne '' ? $line : 'none'; - }, - = ---- lib/Mail/SpamAssassin/Util.pm.orig 2013-05-29 01:29:59.000000000 +02= 00 -+++ lib/Mail/SpamAssassin/Util.pm 2013-05-29 01:33:16.000000000 +0200 -@@ -1588,5 +1588,5 @@ = - return undef; # invalid - } -- elsif ($re =3D~ s/^m{//) { # m{foo/bar} -+ elsif ($re =3D~ s/^m\{//) { # m{foo/bar} - $delim =3D '}'; - } Index: pkg-deinstall =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- pkg-deinstall (revision 346955) +++ pkg-deinstall (working copy) @@ -1,17 +0,0 @@ -#!/bin/sh - -if [ "$2" !=3D "POST-DEINSTALL" ]; then - exit 0 -fi - -if [ -d /var/db/spamassassin ]; then - echo "To delete /var/db/spamassassin, use 'rm -rf /var/db/spamas= sassin'" -fi - -USER=3Dspamd - -if pw usershow "${USER}" 2>/dev/null 1>&2; then - echo "To delete ${USER} user permanently, use 'rmuser ${USER}'" -fi - -exit 0 Index: pkg-descr =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- pkg-descr (revision 346955) +++ pkg-descr (working copy) @@ -11,4 +11,4 @@ Additional drop-in rule sets are available at http://wiki.apache.org/spamassassin/CustomRulesets = -WWW: http://spamassassin.apache.org/ +WWW: http://spamassassin.apache.org/ Index: pkg-message =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- pkg-message (revision 346955) +++ pkg-message (working copy) @@ -1,39 +1,26 @@ -************************************************************************= * -* _ _____ _____ _____ _ _ _____ ___ ___ _ _ * -* / \|_ _|_ _| ____| \ | |_ _|_ _/ _ \| \ | | * -* / _ \ | | | | | _| | \| | | | | | | | | \| | * -* / ___ \| | | | | |___| |\ | | | | | |_| | |\ | * -* /_/ \_\_| |_| |_____|_| \_| |_| |___\___/|_| \_| * -* * -* See PREFIX/share/doc/p5-Mail-SpamAssassin/INSTALL, * -* and PREFIX/share/doc/p5-Mail-SpamAssassin/UPGRADE, * -* or http://spamassassin.org/dist/INSTALL and * -* http://spamassassin.org/dist/UPGRADE BEFORE enabling * -* this version of SpamAssassin for important information * -* regarding changes in this version. * -* * -* SpamAssassin may require additional configuration in * -* PREFIX/etc/mail/spamassassin/init.pre depending on * -* the options you have installed. Otherwise, annoying * -* (but harmless) error messages may result. Read the * -* files listed above. * -* * -************************************************************************= * -You may wish to run sa-update now to obtain the latest rules. +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= = -NOTE: FREEBSD users: If you are updating from a version prior to 3.20. -sa-update now places state files in /var/db/spamassassin and not = -/var/lib/spamassassin. This is to be consistant with FreeBSD file -directory conventions. +You should complete the following post-installation tasks: = -If you run sa-compile, you will notice that files are in = -/var/db/spamassassin/compiled/<perlversion>/<version> instead of -/var/db/spamassassin/compiled/<version>. -No attempts have been made to move old versions over. You must recompile= =2E + 1) Read %%DOCSDIR%%/INSTALL + and %%DOCSDIR%%/UPGRADE + BEFORE enabling SpamAssassin for important changes + = + 2) Edit the configuration in %%ETCDIR%%, + in particular %%ETCDIR%%/init.pre + You may get lots of annoying (but harmless) error messages + if you skip this step. + = + 3) Run 'sa-update' to obtain the latest rules. + Then, run 'sa-compile' for a big speed boost (if you + enabled SA_COMPILE) + = + 4) To run spamd, add the following to /etc/rc.conf: + spamd_enable=3D"YES" + = +SECURITY NOTE: +Unless you deselected the default AS_ROOT, spamd will be running +as root. If you wish to change this, add the following to /etc/rc.conf: +spamd_flags=3D"-u spamd -H /var/spool/spamd" = -If you are running with spamd, you must add the following to rc.conf: -spamd_enable=3D"YES" - -Security Note: If you did NOT deselect AS_ROOT, spamd will be running -as root. To change this, also add this to rc.conf: -spamd_flags=3D"-u spamd -H /var/spool/spamd" +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= Index: pkg-plist =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- pkg-plist (revision 346955) +++ pkg-plist (working copy) @@ -7,23 +7,26 @@ bin/spamassassin bin/spamc bin/spamd -@unexec rm -rf %D/etc/mail/spamassassin/sa-update-keys -etc/mail/spamassassin/local.cf.sample +@unexec rm -rf %D/%%ETCDIR%%/sa-update-keys +%%ETCDIR%%/local.cf.sample @unexec if cmp -s %B/init.pre.sample %B/init.pre; then rm -f %B/init.pre= ; fi -etc/mail/spamassassin/init.pre.sample +%%ETCDIR%%/init.pre.sample @exec [ -f %B/init.pre ] || cp %B/%f %B/init.pre @unexec if cmp -s %B/v310.pre.sample %B/v310.pre; then rm -f %B/v310.pre= ; fi -etc/mail/spamassassin/v310.pre.sample +%%ETCDIR%%/v310.pre.sample @exec [ -f %B/v310.pre ] || cp %B/%f %B/v310.pre @unexec if cmp -s %B/v312.pre.sample %B/v312.pre; then rm -f %B/v312.pre= ; fi -etc/mail/spamassassin/v312.pre.sample +%%ETCDIR%%/v312.pre.sample @exec [ -f %B/v312.pre ] || cp %B/%f %B/v312.pre @unexec if cmp -s %B/v320.pre.sample %B/v320.pre; then rm -f %B/v320.pre= ; fi -etc/mail/spamassassin/v320.pre.sample +%%ETCDIR%%/v320.pre.sample @exec [ -f %B/v320.pre ] || cp %B/%f %B/v320.pre @unexec if cmp -s %B/v330.pre.sample %B/v330.pre; then rm -f %B/v330.pre= ;fi -etc/mail/spamassassin/v330.pre.sample +%%ETCDIR%%/v330.pre.sample @exec [ -f %B/v330.pre ] || cp %B/%f %B/v330.pre +@unexec if cmp -s %B/v340.pre.sample %B/v340.pre; then rm -f %B/v340.pre= ;fi +%%ETCDIR%%/v340.pre.sample +@exec [ -f %B/v340.pre ] || cp %B/%f %B/v340.pre include/libspamc.h lib/libspamc.so lib/libspamc.so.0 @@ -39,6 +42,7 @@ %%PERL5_MAN3%%/Mail::SpamAssassin::BayesStore::BDB.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::BayesStore::MySQL.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::BayesStore::PgSQL.3.gz +%%PERL5_MAN3%%/Mail::SpamAssassin::BayesStore::Redis.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::BayesStore::SQL.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Client.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Conf.3.gz @@ -61,6 +65,7 @@ %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::AWL.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::AccessDB.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::AntiVirus.3.gz +%%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::AskDNS.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::AutoLearnThreshold.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::Bayes.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::BodyRuleBaseExtractor.3.gz @@ -67,6 +72,7 @@ %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::Check.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::DCC.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::DKIM.3.gz +%%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::DNSEval.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::Hashcash.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::MIMEHeader.3.gz %%PERL5_MAN3%%/Mail::SpamAssassin::Plugin::OneLineBodyRuleType.3.gz @@ -116,6 +122,7 @@ %%SITE_PERL%%/Mail/SpamAssassin/BayesStore/DBM.pm %%SITE_PERL%%/Mail/SpamAssassin/BayesStore/MySQL.pm %%SITE_PERL%%/Mail/SpamAssassin/BayesStore/PgSQL.pm +%%SITE_PERL%%/Mail/SpamAssassin/BayesStore/Redis.pm %%SITE_PERL%%/Mail/SpamAssassin/BayesStore/SDBM.pm %%SITE_PERL%%/Mail/SpamAssassin/BayesStore/SQL.pm %%SITE_PERL%%/Mail/SpamAssassin/Client.pm @@ -151,6 +158,7 @@ %%SITE_PERL%%/Mail/SpamAssassin/Plugin/AWL.pm %%SITE_PERL%%/Mail/SpamAssassin/Plugin/AccessDB.pm %%SITE_PERL%%/Mail/SpamAssassin/Plugin/AntiVirus.pm +%%SITE_PERL%%/Mail/SpamAssassin/Plugin/AskDNS.pm %%SITE_PERL%%/Mail/SpamAssassin/Plugin/AutoLearnThreshold.pm %%SITE_PERL%%/Mail/SpamAssassin/Plugin/Bayes.pm %%SITE_PERL%%/Mail/SpamAssassin/Plugin/BodyEval.pm @@ -199,15 +207,12 @@ %%SITE_PERL%%/Mail/SpamAssassin/Util/RegistrarBoundaries.pm %%SITE_PERL%%/Mail/SpamAssassin/Util/ScopedTimer.pm %%SITE_PERL%%/Mail/SpamAssassin/Util/TieOneStringHash.pm +%%SITE_PERL%%/Mail/SpamAssassin/Util/TinyRedis.pm %%SITE_PERL%%/%%PERL_ARCH%%/auto/Mail/SpamAssassin/.packlist %%SITE_PERL%%/spamassassin-run.pod %%DATADIR%%/languages %%DATADIR%%/sa-update-pubkey.txt %%DATADIR%%/user_prefs.template -@unexec rm -rf /var/lib/spamassassin/2* -@unexec rmdir /var/lib/spamassassin 2>/dev/null || true -@unexec rmdir /var/lib 2>/dev/null || true -@unexec rmdir /var/db/spamassassin 2>/dev/null || true @dirrm %%DATADIR%% @dirrm %%SITE_PERL%%/%%PERL_ARCH%%/auto/Mail/SpamAssassin @dirrmtry %%SITE_PERL%%/%%PERL_ARCH%%/auto/Mail @@ -222,7 +227,13 @@ @dirrm %%SITE_PERL%%/Mail/SpamAssassin/Bayes @dirrm %%SITE_PERL%%/Mail/SpamAssassin @dirrmtry %%SITE_PERL%%/Mail -@dirrmtry etc/mail/spamassassin +@dirrmtry %%ETCDIR%% @dirrmtry etc/mail @unexec rm -rf /var/run/spamd -@unexec rm -rf /var/spool/spamd +@unexec rm -rf /var/lib/spamassassin/2* +@dirrmtry /var/lib/spamassassin +@dirrmtry /var/lib +@dirrmtry /var/db/spamassassin +@unexec [ -d /var/db/spamassassin ] && echo "If you are no longer using = SpamAssassin, remove /var/db/spamassassin" +@unexec pw usershow "%%USER%%" 2>/dev/null 1>&2 && echo "To delete %%USE= R%% permanently, 'rmuser %%USER%%'" +@unexec pw usershow "%%USER%%" 2>/dev/null 1>&2 && [ -d /var/spool/spamd= ] && echo " Note that this will remove /var/spool/spamd" --=_MailMate_A5D8A8A9-69FB-401E-B057-33D7FB177BD5_=--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201403032110.s23LA1pP000756>