You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by jm...@apache.org on 2006/12/03 21:45:54 UTC

svn commit: r481882 - in /spamassassin/branches/jm_spamc_hacks: ./ lib/Mail/SpamAssassin/Util/ spamc/ spamd/ t/

Author: jm
Date: Sun Dec  3 12:45:50 2006
New Revision: 481882

URL: http://svn.apache.org/viewvc?view=rev&rev=481882
Log:
add spamc -z switch: compression for spamc-spamd protocol, useful for cross-internet spamd protocol use

Added:
    spamassassin/branches/jm_spamc_hacks/t/spamc_z.t
Modified:
    spamassassin/branches/jm_spamc_hacks/INSTALL
    spamassassin/branches/jm_spamc_hacks/MANIFEST
    spamassassin/branches/jm_spamc_hacks/lib/Mail/SpamAssassin/Util/DependencyInfo.pm
    spamassassin/branches/jm_spamc_hacks/spamc/config.h.in
    spamassassin/branches/jm_spamc_hacks/spamc/configure
    spamassassin/branches/jm_spamc_hacks/spamc/configure.in
    spamassassin/branches/jm_spamc_hacks/spamc/libspamc.c
    spamassassin/branches/jm_spamc_hacks/spamc/libspamc.h
    spamassassin/branches/jm_spamc_hacks/spamc/spamc.c
    spamassassin/branches/jm_spamc_hacks/spamc/spamc.pod
    spamassassin/branches/jm_spamc_hacks/spamd/spamd.raw

Modified: spamassassin/branches/jm_spamc_hacks/INSTALL
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/INSTALL?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/INSTALL (original)
+++ spamassassin/branches/jm_spamc_hacks/INSTALL Sun Dec  3 12:45:50 2006
@@ -289,6 +289,13 @@
     compatibile spamc.)
 
 
+  - Compress::Zlib (from CPAN)
+
+    If you wish to use the optional zlib compression for communication
+    between spamc and spamd (the -z option to spamc), you need to install
+    this module.
+
+
   - Time::HiRes (from CPAN)
 
     If this module is installed, the processing times are logged/reported

Modified: spamassassin/branches/jm_spamc_hacks/MANIFEST
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/MANIFEST?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/MANIFEST (original)
+++ spamassassin/branches/jm_spamc_hacks/MANIFEST Sun Dec  3 12:45:50 2006
@@ -345,6 +345,7 @@
 t/spamc_l.t
 t/spamc_optC.t
 t/spamc_optL.t
+t/spamc_z.t
 t/spamd.t
 t/spamd_allow_user_rules.t
 t/spamd_hup.t

Modified: spamassassin/branches/jm_spamc_hacks/lib/Mail/SpamAssassin/Util/DependencyInfo.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/lib/Mail/SpamAssassin/Util/DependencyInfo.pm?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/lib/Mail/SpamAssassin/Util/DependencyInfo.pm (original)
+++ spamassassin/branches/jm_spamc_hacks/lib/Mail/SpamAssassin/Util/DependencyInfo.pm Sun Dec  3 12:45:50 2006
@@ -122,6 +122,13 @@
   compatibile spamc.)',
 },
 {
+  module => 'Compress::Zlib',
+  version => '0.00',
+  desc => 'If you wish to use the optional zlib compression for communication
+  between spamc and spamd (the -z option to spamc), you need to install
+  this module.',
+},
+{
   module => 'Time::HiRes',
   version => '0.00',
   desc => 'If this module is installed, the processing times are logged/reported

Modified: spamassassin/branches/jm_spamc_hacks/spamc/config.h.in
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/spamc/config.h.in?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/spamc/config.h.in (original)
+++ spamassassin/branches/jm_spamc_hacks/spamc/config.h.in Sun Dec  3 12:45:50 2006
@@ -34,6 +34,9 @@
 /* Define to 1 if you have the `socket' library (-lsocket). */
 #undef HAVE_LIBSOCKET
 
+/* Define to 1 if you have the `z' library (-lz). */
+#undef HAVE_LIBZ
+
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
@@ -108,6 +111,9 @@
 
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#undef HAVE_ZLIB_H
 
 /* Define to the address where bug reports for this package should be sent. */
 #undef PACKAGE_BUGREPORT

Modified: spamassassin/branches/jm_spamc_hacks/spamc/configure
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/spamc/configure?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/spamc/configure (original)
+++ spamassassin/branches/jm_spamc_hacks/spamc/configure Sun Dec  3 12:45:50 2006
@@ -3035,7 +3035,8 @@
 
 
 
-for ac_header in pwd.h signal.h openssl/crypto.h
+
+for ac_header in pwd.h signal.h openssl/crypto.h zlib.h
 do
 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
 if eval "test \"\${$as_ac_Header+set}\" = set"; then
@@ -3957,6 +3958,79 @@
 _ACEOF
 
   LIBS="-lnsl $LIBS"
+
+fi
+
+
+echo "$as_me:$LINENO: checking for deflate in -lz" >&5
+echo $ECHO_N "checking for deflate in -lz... $ECHO_C" >&6
+if test "${ac_cv_lib_z_deflate+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lz  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char deflate ();
+int
+main ()
+{
+deflate ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_z_deflate=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_z_deflate=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_z_deflate" >&5
+echo "${ECHO_T}$ac_cv_lib_z_deflate" >&6
+if test $ac_cv_lib_z_deflate = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBZ 1
+_ACEOF
+
+  LIBS="-lz $LIBS"
 
 fi
 

Modified: spamassassin/branches/jm_spamc_hacks/spamc/configure.in
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/spamc/configure.in?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/spamc/configure.in (original)
+++ spamassassin/branches/jm_spamc_hacks/spamc/configure.in Sun Dec  3 12:45:50 2006
@@ -29,7 +29,7 @@
 AC_HEADER_STDC
 AC_CHECK_HEADERS(sys/time.h syslog.h unistd.h errno.h sys/errno.h)
 AC_CHECK_HEADERS(time.h sysexits.h sys/socket.h netdb.h netinet/in.h)
-AC_CHECK_HEADERS(pwd.h signal.h openssl/crypto.h)
+AC_CHECK_HEADERS(pwd.h signal.h openssl/crypto.h zlib.h)
 dnl AC_CHECK_HEADERS(getopt.h)
 
 AC_C_CONST
@@ -75,6 +75,7 @@
 fi
 AC_CHECK_LIB(inet, connect)
 AC_CHECK_LIB(nsl, t_accept)
+AC_CHECK_LIB(z, deflate)
 AC_CHECK_LIB(dl, dlopen)
 
 AC_CHECK_FUNCS(socket strdup strtod strtol snprintf shutdown)

Modified: spamassassin/branches/jm_spamc_hacks/spamc/libspamc.c
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/spamc/libspamc.c?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/spamc/libspamc.c (original)
+++ spamassassin/branches/jm_spamc_hacks/spamc/libspamc.c Sun Dec  3 12:45:50 2006
@@ -57,6 +57,9 @@
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
 
 /* FIXME: Make this configurable */
 #define MAX_CONNECT_RETRIES 3
@@ -932,6 +935,61 @@
     return EX_OK;
 }
 
+static int
+_zlib_compress (char *m_msg, int m_msg_len,
+        unsigned char **zlib_buf, int *zlib_bufsiz)
+{
+    int rc;
+    int len, totallen;
+
+#ifndef HAVE_LIBZ
+
+    UNUSED_VARIABLE(rc);
+    UNUSED_VARIABLE(len);
+    UNUSED_VARIABLE(totallen);
+    libspamc_log(flags, LOG_ERR, "spamc not built with zlib support");
+    return EX_SOFTWARE;
+
+#else
+
+    /* worst-case, according to http://www.zlib.org/zlib_tech.html ;
+      * same as input, plus 5 bytes per 16k, plus 6 bytes.  this should
+      * be plenty */
+    *zlib_bufsiz = (int) (m_msg_len * 1.0005) + 1024;
+    *zlib_buf = (unsigned char *) malloc (*zlib_bufsiz);
+    if (*zlib_buf == NULL) {
+        return EX_OSERR;
+    }
+
+    z_stream strm;
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    strm.opaque = Z_NULL;
+    rc = deflateInit(&strm, 3);
+    if (rc != Z_OK) {
+        return EX_OSERR;
+    }
+
+    strm.avail_in = m_msg_len;
+    strm.next_in = (unsigned char *) m_msg;
+    strm.avail_out = *zlib_bufsiz;
+    strm.next_out = (unsigned char *) *zlib_buf;
+
+    totallen = 0;
+    do {
+        rc = deflate(&strm, Z_FINISH);
+        assert(rc != Z_STREAM_ERROR);
+        len = (size_t) (*zlib_bufsiz - strm.avail_out);
+        strm.next_out += len;
+        totallen += len;
+    } while (strm.avail_out == 0);
+
+    *zlib_bufsiz = totallen;
+    return EX_OK;
+
+#endif
+}
+
 int message_filter(struct transport *tp, const char *username,
                    int flags, struct message *m)
 {
@@ -948,10 +1006,19 @@
     SSL_CTX *ctx = NULL;
     SSL *ssl = NULL;
     SSL_METHOD *meth;
+    char zlib_on = 0;
+    unsigned char *zlib_buf = NULL;
+    int zlib_bufsiz = 0;
+    unsigned char *towrite_buf;
+    int towrite_len;
 
     assert(tp != NULL);
     assert(m != NULL);
 
+    if ((flags & SPAMC_USE_ZLIB) != 0) {
+      zlib_on = 1;
+    }
+
     if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
 	SSLeay_add_ssl_algorithms();
@@ -999,6 +1066,17 @@
     strcat(buf, "\r\n");
     len = strlen(buf);
 
+    towrite_buf = (unsigned char *) m->msg;
+    towrite_len = (int) m->msg_len;
+    if (zlib_on) {
+        if (_zlib_compress(m->msg, m->msg_len, &zlib_buf, &zlib_bufsiz) != EX_OK)
+        {
+            return EX_OSERR;
+        }
+        towrite_buf = zlib_buf;
+        towrite_len = zlib_bufsiz;
+    }
+
     if (!(flags & SPAMC_PING)) {
       if (username != NULL) {
 	if (strlen(username) + 8 >= (bufsiz - len)) {
@@ -1010,11 +1088,14 @@
 	strcat(buf + len, "\r\n");
 	len += strlen(buf + len);
       }
+      if (zlib_on) {
+	len += snprintf(buf + len, 8192-len, "Compress: zlib\r\n");
+      }
       if ((m->msg_len > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
 	_use_msg_for_out(m);
 	return EX_DATAERR;
       }
-      len += sprintf(buf + len, "Content-length: %d\r\n\r\n", (int) m->msg_len);
+      len += snprintf(buf + len, 8192-len, "Content-length: %d\r\n\r\n", (int) towrite_len);
     }
 
     libspamc_timeout = m->timeout;
@@ -1041,12 +1122,12 @@
     if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
 	SSL_write(ssl, buf, len);
-	SSL_write(ssl, m->msg, m->msg_len);
+	SSL_write(ssl, towrite_buf, towrite_len);
 #endif
     }
     else {
 	full_write(sock, 0, buf, len);
-	full_write(sock, 0, m->msg, m->msg_len);
+	full_write(sock, 0, towrite_buf, towrite_len);
 	shutdown(sock, SHUT_WR);
     }
 

Modified: spamassassin/branches/jm_spamc_hacks/spamc/libspamc.h
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/spamc/libspamc.h?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/spamc/libspamc.h (original)
+++ spamassassin/branches/jm_spamc_hacks/spamc/libspamc.h Sun Dec  3 12:45:50 2006
@@ -112,6 +112,9 @@
 /* Oct 21, 2005 sidney: added ping test */
 #define SPAMC_PING      (1<<19)
 
+/* Nov 30, 2006 jm: add -z, zlib support */
+#define SPAMC_USE_ZLIB	(1<<18)
+
 
 #define SPAMC_MESSAGE_CLASS_SPAM 1
 #define SPAMC_MESSAGE_CLASS_HAM  2

Modified: spamassassin/branches/jm_spamc_hacks/spamc/spamc.c
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/spamc/spamc.c?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/spamc/spamc.c (original)
+++ spamassassin/branches/jm_spamc_hacks/spamc/spamc.c Sun Dec  3 12:45:50 2006
@@ -190,6 +190,7 @@
     usg("  -h, --help          Print this help message and exit.\n");
     usg("  -V, --version       Print spamc version and exit.\n");
     usg("  -K                  Keepalive check of spamd.\n");
+    usg("  -z                  Compress mail message sent to spamd.\n");
     usg("  -f                  (Now default, ignored.)\n");
 
     usg("\n");
@@ -208,9 +209,9 @@
           struct transport *ptrn)
 {
 #ifndef _WIN32
-    const char *opts = "-BcrRd:e:fyp:t:s:u:L:C:xSHU:ElhVKF:";
+    const char *opts = "-BcrRd:e:fyp:t:s:u:L:C:xzSHU:ElhVKF:";
 #else
-    const char *opts = "-BcrRd:fyp:t:s:u:L:C:xSHElhVKF:";
+    const char *opts = "-BcrRd:fyp:t:s:u:L:C:xzSHElhVKF:";
 #endif
     int opt;
     int ret = EX_OK;
@@ -239,6 +240,7 @@
        { "pipe-to", required_argument, 0, 'e' },
        { "help", no_argument, 0, 'h' },
        { "version", no_argument, 0, 'V' },
+       { "compress", no_argument, 0, 'z' },
        { 0, 0, 0, 0} /* last element _must_ be all zeroes */
     };
     
@@ -416,6 +418,11 @@
             {
                 print_version();
                 return(EX_TEMPFAIL);
+            }
+            case 'z':
+            {
+                flags |= SPAMC_USE_ZLIB;
+                break;
             }
         }
     }

Modified: spamassassin/branches/jm_spamc_hacks/spamc/spamc.pod
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/spamc/spamc.pod?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/spamc/spamc.pod (original)
+++ spamassassin/branches/jm_spamc_hacks/spamc/spamc.pod Sun Dec  3 12:45:50 2006
@@ -204,6 +204,13 @@
 
 Perform a keep-alive check of spamd, instead of a full message check.
 
+=item B<-z>
+
+Use gzip compression to compress the mail message sent to C<spamd>.  Note
+that this relies on C<zlib> being installed on the C<spamc> client side,
+and the C<Compress::Zlib> perl module on the server side; an error
+will be returned otherwise.
+
 =back
 
 =head1 CONFIGURATION FILE

Modified: spamassassin/branches/jm_spamc_hacks/spamd/spamd.raw
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/spamd/spamd.raw?view=diff&rev=481882&r1=481881&r2=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/spamd/spamd.raw (original)
+++ spamassassin/branches/jm_spamc_hacks/spamd/spamd.raw Sun Dec  3 12:45:50 2006
@@ -144,6 +144,8 @@
   printf("  running on Perl %s\n", join(".", map { $_||=0; $_*1 } ($] =~ /(\d)\.(\d{3})(\d{3})?/)));
   eval { require IO::Socket::SSL; };
   printf("  with SSL support (%s %s)\n", "IO::Socket::SSL", $IO::Socket::SSL::VERSION) unless ($@);
+  eval { require Compress::Zlib; };
+  printf("  with zlib support (%s %s)\n", "Compress::Zlib", $Compress::Zlib::VERSION) unless ($@);
 }
 
 sub print_usage_and_exit {
@@ -1264,15 +1266,29 @@
 }
 
 sub parse_body {
-  my ($client, $expected_length) = @_;
+  my ($client, $expected_length, $compress_zlib) = @_;
 
   my @msglines;
-  my $actual_length = 0;
+  my $actual_length;
 
-  while (defined($_ = $client->getline())) {
-    $actual_length += length($_);
-    push(@msglines, $_);    
-    last if (defined $expected_length && $actual_length >= $expected_length);
+  if ($compress_zlib && !defined($expected_length)) {
+    service_unavailable_error("Compress requires Content-length header");
+    return;
+  }
+
+  if ($compress_zlib) {
+    $actual_length = zlib_inflate_read($client, $expected_length, \@msglines);
+    if ($actual_length < 0) { return; }
+    $expected_length = $actual_length;
+  }
+  else {
+    @msglines = ();
+    $actual_length = 0;
+    while (defined($_ = $client->getline())) {
+      $actual_length += length($_);
+      push(@msglines, $_);    
+      last if (defined $expected_length && $actual_length >= $expected_length);
+    }
   }
   
   # Now parse *only* the message headers; the MIME tree won't be generated 
@@ -1282,6 +1298,54 @@
   return ($mail, $actual_length);
 }
 
+sub zlib_inflate_read {
+  my ($client, $expected_length, $msglinesref) = @_;
+  my $out;
+  my $actual_length;
+
+  eval {
+    require Compress::Zlib;
+    my ($zlib, $status) = Compress::Zlib::inflateInit();
+    if (!$zlib) { die "inflateInit failed: $status"; }
+
+    my $red = 0;
+    my $buf;
+
+    # TODO: inflate in smaller buffers instead of at EOF
+    while (1) {
+      my $numbytes = $client->read($buf, (1024 * 64) + $red, $red);
+      if (!defined $numbytes) {
+        die "read of zlib data failed: $!";
+        return -1;
+      }
+      last if $numbytes == 0;
+      $red += $numbytes;
+    }
+
+    if ($red > $expected_length) {
+      warn "hmm, zlib read $red > expected_length $expected_length";
+      substr ($buf, $expected_length) = '';
+    }
+
+    ($out, $status) = $zlib->inflate($buf);
+    if ($status != Compress::Zlib::Z_STREAM_END()) {
+      die "failed to find end of zlib stream";
+    }
+  };
+
+  if ($@) { 
+    service_unavailable_error("zlib: $@");
+    return -1;
+  }
+
+  $actual_length = length($out);
+
+  # TODO: split during inflate, too
+  # note that this preserves line endings
+  @{$msglinesref} = map { s/$/\n/gs; $_; } split(/\n/, $out);
+  return $actual_length;
+}
+
 sub parse_msgids {
   my ($mail) = @_;
 
@@ -1307,6 +1371,7 @@
   my ( $method, $version, $start_time, $remote_hostname, $remote_hostaddr ) = @_;
   local ($_);
   my $expected_length;
+  my $compress_zlib;
 
   # used to ensure we don't accidentally fork (bug 4370)
   my $starting_self_pid = $$;
@@ -1320,6 +1385,7 @@
     return 0 unless (parse_headers($hdrs, $client));
 
     $expected_length = $hdrs->{expected_length};
+    $compress_zlib = $hdrs->{compress_zlib};
   }
 
   handle_setuid_to_user if ($setuid_to_user && $> == 0);
@@ -1338,7 +1404,13 @@
   my $resp = "EX_OK";
 
   # generate mail object from input
-  my ($mail, $actual_length) = parse_body($client, $expected_length);
+  my ($mail, $actual_length) = parse_body($client, $expected_length,
+                $compress_zlib);
+  return 0 unless defined($mail);       # error
+
+  if ($compress_zlib) {
+    $expected_length = $actual_length;  # previously it was the gzipped length
+  }
 
   # attempt to fetch the message ids
   my ($msgid, $rmsgid) = parse_msgids($mail);
@@ -1491,13 +1563,13 @@
 sub dotell {
   my ($method, $version, $start_time, $remote_hostname, $remote_hostaddr) = @_;
   local ($_);
-  my $expected_length;
 
   my $hdrs = {};
 
   return 0 unless (parse_headers($hdrs, $client));
 
-  $expected_length = $hdrs->{expected_length};
+  my $expected_length = $hdrs->{expected_length};
+  my $compress_zlib = $hdrs->{compress_zlib};
 
   if (!$opt{tell}) {
     service_unavailable_error("TELL commands have not been enabled.");
@@ -1530,7 +1602,13 @@
   my $resp = "EX_OK";
 
   # generate mail object from input
-  my ($mail, $actual_length) = parse_body($client, $expected_length);
+  my ($mail, $actual_length) = parse_body($client, $expected_length,
+                $compress_zlib);
+  return 0 unless defined($mail);       # error
+
+  if ($compress_zlib) {
+    $expected_length = $actual_length;  # previously it was the gzipped length
+  }
 
   if ( $mail->get_header("X-Spam-Checker-Version") ) {
     my $new_mail = $spamtest->parse($spamtest->remove_spamassassin_markup($mail), 1);
@@ -1649,6 +1727,9 @@
     elsif ($header eq 'Remove') {
       return 0 unless &got_remove_header($hdrs, $header, $value);
     }
+    elsif ($header eq 'Compress') {
+      return 0 unless &got_compress_header($hdrs, $header, $value);
+    }
   }
 
   # avoid too-many-headers DOS attack
@@ -1766,6 +1847,25 @@
 
   if ($value =~ /remote/i) {
     $hdrs->{remove_remote} = 1;
+  }
+
+  return 1;
+}
+
+sub got_compress_header {
+  my ($hdrs, $header, $value) = @_;
+
+  if ($value =~ /zlib/i) {
+    eval { require Compress::Zlib; };
+    if ($@) {
+      protocol_error("(compression not supported, Compress::Zlib not installed)");
+      return 0;
+    }
+    $hdrs->{compress_zlib} = 1;
+  }
+  else {
+    protocol_error("(compression type not supported)");
+    return 0;
   }
 
   return 1;

Added: spamassassin/branches/jm_spamc_hacks/t/spamc_z.t
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_spamc_hacks/t/spamc_z.t?view=auto&rev=481882
==============================================================================
--- spamassassin/branches/jm_spamc_hacks/t/spamc_z.t (added)
+++ spamassassin/branches/jm_spamc_hacks/t/spamc_z.t Sun Dec  3 12:45:50 2006
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+use constant HAVE_ZLIB => eval { require Compress::Zlib; };
+
+use lib '.'; use lib 't';
+use SATest; sa_t_init("spamc_z");
+use Test; plan tests => (($SKIP_SPAMD_TESTS || !HAVE_ZLIB) ? 0 : 9);
+
+exit if ($SKIP_SPAMD_TESTS || !HAVE_ZLIB);
+
+# ---------------------------------------------------------------------------
+
+%patterns = (
+
+q{ Return-Path: sb55sb55@yahoo.com}, 'firstline',
+q{ Subject: There yours for FREE!}, 'subj',
+q{ X-Spam-Status: Yes, score=}, 'status',
+q{ X-Spam-Flag: YES}, 'flag',
+q{ X-Spam-Level: **********}, 'stars',
+q{ TEST_ENDSNUMS}, 'endsinnums',
+q{ TEST_NOREALNAME}, 'noreal',
+q{ This must be the very last line}, 'lastline',
+
+
+);
+
+ok (sdrun ("-L",
+           "-z < data/spam/001",
+           \&patterns_run_cb));
+ok_all_patterns();
+