Avoid minor memleak during multi-message STARTTLS'd conns
[exim.git] / src / src / malware.c
index f9c4c414f71633c5af3c8bc21cca3b4a33f55a23..5b9d5ad582614068615b880b1367db02f81ac44a 100644 (file)
@@ -4,13 +4,13 @@
 
 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
  * License: GPL
 
 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
  * License: GPL
- * Copyright (c) The Exim Maintainers 2016
+ * Copyright (c) The Exim Maintainers 2015 - 2018
  */
 
 /* Code for calling virus (malware) scanners. Called from acl.c. */
 
 #include "exim.h"
  */
 
 /* Code for calling virus (malware) scanners. Called from acl.c. */
 
 #include "exim.h"
-#ifdef WITH_CONTENT_SCAN
+#ifdef WITH_CONTENT_SCAN       /* entire file */
 
 typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
                M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST, M_FPROT6D} scanner_t;
 
 typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
                M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST, M_FPROT6D} scanner_t;
@@ -23,72 +23,131 @@ static struct scan
   contype_t    conn;
 } m_scans[] =
 {
   contype_t    conn;
 } m_scans[] =
 {
+#ifndef DISABLE_MAL_FFROTD
   { M_FPROTD,  US"f-protd",    US"localhost 10200-10204",            MC_TCP },
   { M_FPROTD,  US"f-protd",    US"localhost 10200-10204",            MC_TCP },
+#endif
+#ifndef DISABLE_MAL_FFROT6D
+  { M_FPROT6D, US"f-prot6d",   US"localhost 10200",                  MC_TCP },
+#endif
+#ifndef DISABLE_MAL_DRWEB
   { M_DRWEB,   US"drweb",      US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
   { M_DRWEB,   US"drweb",      US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
+#endif
+#ifndef DISABLE_MAL_AVE
   { M_AVES,    US"aveserver",  US"/var/run/aveserver",               MC_UNIX },
   { M_AVES,    US"aveserver",  US"/var/run/aveserver",               MC_UNIX },
+#endif
+#ifndef DISABLE_MAL_FSECURE
   { M_FSEC,    US"fsecure",    US"/var/run/.fsav",                   MC_UNIX },
   { M_FSEC,    US"fsecure",    US"/var/run/.fsav",                   MC_UNIX },
+#endif
+#ifndef DISABLE_MAL_KAV
   { M_KAVD,    US"kavdaemon",  US"/var/run/AvpCtl",                  MC_UNIX },
   { M_KAVD,    US"kavdaemon",  US"/var/run/AvpCtl",                  MC_UNIX },
-  { M_CMDL,    US"cmdline",    NULL,                                 MC_NONE },
+#endif
+#ifndef DISABLE_MAL_SOPHIE
   { M_SOPHIE,  US"sophie",     US"/var/run/sophie",                  MC_UNIX },
   { M_SOPHIE,  US"sophie",     US"/var/run/sophie",                  MC_UNIX },
+#endif
+#ifndef DISABLE_MAL_CLAM
   { M_CLAMD,   US"clamd",      US"/tmp/clamd",                       MC_NONE },
   { M_CLAMD,   US"clamd",      US"/tmp/clamd",                       MC_NONE },
-  { M_SOCK,    US"sock",       US"/tmp/malware.sock",                MC_STRM },
+#endif
+#ifndef DISABLE_MAL_MKS
   { M_MKSD,    US"mksd",       NULL,                                 MC_NONE },
   { M_MKSD,    US"mksd",       NULL,                                 MC_NONE },
+#endif
+#ifndef DISABLE_MAL_AVAST
   { M_AVAST,   US"avast",      US"/var/run/avast/scan.sock",         MC_STRM },
   { M_AVAST,   US"avast",      US"/var/run/avast/scan.sock",         MC_STRM },
-  { M_FPROT6D, US"f-prot6d",   US"localhost 10200",                  MC_TCP },
+#endif
+#ifndef DISABLE_MAL_SOCK
+  { M_SOCK,    US"sock",       US"/tmp/malware.sock",                MC_STRM },
+#endif
+#ifndef DISABLE_MAL_CMDLINE
+  { M_CMDL,    US"cmdline",    NULL,                                 MC_NONE },
+#endif
   { -1,                NULL,           NULL, MC_NONE }         /* end-marker */
 };
 
   { -1,                NULL,           NULL, MC_NONE }         /* end-marker */
 };
 
+/******************************************************************************/
+# ifdef MACRO_PREDEF           /* build solely to predefine macros */
+
+#  include "macro_predef.h"
+
+void
+features_malware(void)
+{
+const struct scan * sc;
+const uschar * s;
+uschar * t;
+uschar buf[64];
+
+spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
+
+for (sc = m_scans; sc->scancode != -1; sc++)
+  {
+  for(s = sc->name, t = buf+14; *s; s++) if (*s != '-') *t++ = toupper(*s);
+  *t = '\0';
+  builtin_macro_create(buf);
+  }
+}
+
+/******************************************************************************/
+# else /*!MACRO_PREDEF, main build*/
+
+
+#define MALWARE_TIMEOUT 120    /* default timeout, seconds */
+
+static const uschar * malware_regex_default = US ".+";
+static const pcre * malware_default_re = NULL;
+
+
+
+#ifndef DISABLE_MAL_CLAM
 /* The maximum number of clamd servers that are supported in the configuration */
 /* The maximum number of clamd servers that are supported in the configuration */
-#define MAX_CLAMD_SERVERS 32
-#define MAX_CLAMD_SERVERS_S "32"
+# define MAX_CLAMD_SERVERS 32
+# define MAX_CLAMD_SERVERS_S "32"
 
 typedef struct clamd_address {
   uschar * hostspec;
   unsigned tcp_port;
   unsigned retry;
 } clamd_address;
 
 typedef struct clamd_address {
   uschar * hostspec;
   unsigned tcp_port;
   unsigned retry;
 } clamd_address;
-
-#ifndef nelements
-# define nelements(arr) (sizeof(arr) / sizeof(arr[0]))
 #endif
 
 
 #endif
 
 
-#define MALWARE_TIMEOUT 120    /* default timeout, seconds */
-
+#ifndef DISABLE_MAL_DRWEB
+# define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
+# define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
+# define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
 
 
-#define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
-#define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
-#define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
-
-#define DERR_READ_ERR               (1<<0)   /* read error */
-#define DERR_NOMEMORY               (1<<2)   /* no memory */
-#define DERR_TIMEOUT                (1<<9)   /* scan timeout has run out */
-#define DERR_BAD_CALL               (1<<15)  /* wrong command */
-
-
-static const uschar * malware_regex_default = US ".+";
-static const pcre * malware_default_re = NULL;
+# define DERR_READ_ERR               (1<<0)   /* read error */
+# define DERR_NOMEMORY               (1<<2)   /* no memory */
+# define DERR_TIMEOUT                (1<<9)   /* scan timeout has run out */
+# define DERR_BAD_CALL               (1<<15)  /* wrong command */
 
 static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
 static const pcre * drweb_re = NULL;
 
 static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
 static const pcre * drweb_re = NULL;
+#endif
 
 
+#ifndef DISABLE_MAL_FSECURE
 static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
 static const pcre * fsec_re = NULL;
 static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
 static const pcre * fsec_re = NULL;
+#endif
 
 
+#ifndef DISABLE_MAL_KAV
 static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
 static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
 static const pcre * kav_re_sus = NULL;
 static const pcre * kav_re_inf = NULL;
 static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
 static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
 static const pcre * kav_re_sus = NULL;
 static const pcre * kav_re_inf = NULL;
+#endif
 
 
+#ifndef DISABLE_MAL_AVAST
 static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
 static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\s(.*)";
 static const pcre * ava_re_clean = NULL;
 static const pcre * ava_re_virus = NULL;
 static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
 static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\s(.*)";
 static const pcre * ava_re_clean = NULL;
 static const pcre * ava_re_virus = NULL;
+#endif
 
 
+#ifndef DISABLE_MAL_FFROT6D
 static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
 static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
 static const pcre * fprot6d_re_error = NULL;
 static const pcre * fprot6d_re_virus = NULL;
 static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
 static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
 static const pcre * fprot6d_re_error = NULL;
 static const pcre * fprot6d_re_virus = NULL;
+#endif
 
 
 
 
 
 
@@ -104,7 +163,7 @@ static inline int
 test_byte_order()
 {
   short int word = 0x0001;
 test_byte_order()
 {
   short int word = 0x0001;
-  char *byte = (char *) &word;
+  char *byte = CS  &word;
   return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
 }
 
   return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
 }
 
@@ -142,15 +201,18 @@ return m_errlog_defer(scanent, hostport, str);
 
 /*************************************************/
 
 
 /*************************************************/
 
+#ifndef DISABLE_MAL_CLAM
 /* Only used by the Clamav code, which is working from a list of servers and
 uses the returned in_addr to get a second connection to the same system.
 */
 static inline int
 m_tcpsocket(const uschar * hostname, unsigned int port,
 /* Only used by the Clamav code, which is working from a list of servers and
 uses the returned in_addr to get a second connection to the same system.
 */
 static inline int
 m_tcpsocket(const uschar * hostname, unsigned int port,
-       host_item * host, uschar ** errstr)
+       host_item * host, uschar ** errstr, const blob * fastopen_blob)
 {
 {
-return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr);
+return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
+                         host, errstr, fastopen_blob);
 }
 }
+#endif
 
 static int
 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
 
 static int
 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
@@ -185,7 +247,7 @@ m_pcre_exec(const pcre * cre, uschar * text)
 {
 int ovector[10*3];
 int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
 {
 int ovector[10*3];
 int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
-             ovector, nelements(ovector));
+             ovector, nelem(ovector));
 uschar * substr = NULL;
 if (i >= 2)                            /* Got it */
   pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr);
 uschar * substr = NULL;
 if (i >= 2)                            /* Got it */
   pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr);
@@ -202,7 +264,11 @@ const pcre * cre = NULL;
 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
   *errstr = US listerr;
 else
 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
   *errstr = US listerr;
 else
+  {
+  DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
+    string_printing(list_ele));
   cre = m_pcre_compile(CUS list_ele, errstr);
   cre = m_pcre_compile(CUS list_ele, errstr);
+  }
 return cre;
 }
 
 return cre;
 }
 
@@ -258,9 +324,10 @@ return fd_ready(sock, tmo-time(NULL))
 
 
 
 
 
 
+#ifndef DISABLE_MAL_MKS
 /* ============= private routines for the "mksd" scanner type ============== */
 
 /* ============= private routines for the "mksd" scanner type ============== */
 
-#include <sys/uio.h>
+# include <sys/uio.h>
 
 static inline int
 mksd_writev (int sock, struct iovec * iov, int iovcnt)
 
 static inline int
 mksd_writev (int sock, struct iovec * iov, int iovcnt)
@@ -383,8 +450,10 @@ if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
 
 return mksd_parse_line (scanent, CS av_buffer);
 }
 
 return mksd_parse_line (scanent, CS av_buffer);
 }
+#endif /* MKSD */
 
 
 
 
+#ifndef DISABLE_MAL_CLAM
 static int
 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
 {
 static int
 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
 {
@@ -403,6 +472,9 @@ while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
     return FAIL;
 return OK;
 }
     return FAIL;
 return OK;
 }
+#endif
+
+
 
 /*************************************************
 *          Scan content for malware              *
 
 /*************************************************
 *          Scan content for malware              *
@@ -413,16 +485,16 @@ is via malware(), or there's malware_in_file() used for testing/debugging.
 
 Arguments:
   malware_re    match condition for "malware="
 
 Arguments:
   malware_re    match condition for "malware="
-  eml_filename  the file holding the email to be scanned
+  scan_filename  the file holding the email to be scanned, if we're faking
+               this up for the -bmalware test, else NULL
   timeout      if nonzero, non-default timeoutl
   timeout      if nonzero, non-default timeoutl
-  faking        whether or not we're faking this up for the -bmalware test
 
 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
                 where true means malware was found (condition applies)
 */
 static int
 
 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
                 where true means malware was found (condition applies)
 */
 static int
-malware_internal(const uschar * malware_re, const uschar * eml_filename,
-  int timeout, BOOL faking)
+malware_internal(const uschar * malware_re, const uschar * scan_filename,
+  int timeout)
 {
 int sep = 0;
 const uschar *av_scanner_work = av_scanner;
 {
 int sep = 0;
 const uschar *av_scanner_work = av_scanner;
@@ -435,21 +507,24 @@ struct scan * scanent;
 const uschar * scanner_options;
 int sock = -1;
 time_t tmo;
 const uschar * scanner_options;
 int sock = -1;
 time_t tmo;
+uschar * eml_filename, * eml_dir;
+
+if (!malware_re)
+  return FAIL;         /* empty means "don't match anything" */
 
 
-/* make sure the eml mbox file is spooled up */
-if (!(mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL)))
+/* Ensure the eml mbox file is spooled up */
+
+if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
   return malware_errlog_defer(US"error while creating mbox spool file");
 
   return malware_errlog_defer(US"error while creating mbox spool file");
 
-/* none of our current scanners need the mbox
-   file as a stream, so we can close it right away */
-(void)fclose(mbox_file);
+/* None of our current scanners need the mbox file as a stream (they use
+the name), so we can close it right away.  Get the directory too. */
 
 
-if (!malware_re)
-  return FAIL;         /* empty means "don't match anything" */
+(void) fclose(mbox_file);
+eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
 
 /* parse 1st option */
 
 /* parse 1st option */
-  if ( (strcmpic(malware_re, US"false") == 0) ||
-     (Ustrcmp(malware_re,"0") == 0) )
+if (strcmpic(malware_re, US"false") == 0  ||  Ustrcmp(malware_re,"0") == 0)
   return FAIL;         /* explicitly no matching */
 
 /* special cases (match anything except empty) */
   return FAIL;         /* explicitly no matching */
 
 /* special cases (match anything except empty) */
@@ -469,9 +544,6 @@ if (  strcmpic(malware_re,US"true") == 0
 else if (!(re = m_pcre_compile(malware_re, &errstr)))
   return malware_errlog_defer(errstr);
 
 else if (!(re = m_pcre_compile(malware_re, &errstr)))
   return malware_errlog_defer(errstr);
 
-/* Reset sep that is set by previous string_nextinlist() call */
-sep = 0;
-
 /* if av_scanner starts with a dollar, expand it first */
 if (*av_scanner == '$')
   {
 /* if av_scanner starts with a dollar, expand it first */
 if (*av_scanner == '$')
   {
@@ -503,10 +575,15 @@ if (!malware_ok)
        scanner_name));
     if (strcmpic(scanner_name, US scanent->name) != 0)
       continue;
        scanner_name));
     if (strcmpic(scanner_name, US scanent->name) != 0)
       continue;
+    DEBUG(D_acl) debug_printf_indent("Malware scan:  %s tmo=%s\n",
+      scanner_name, readconf_printtime(timeout));
+
     if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
       scanner_options = scanent->options_default;
     if (scanent->conn == MC_NONE)
       break;
     if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
       scanner_options = scanent->options_default;
     if (scanent->conn == MC_NONE)
       break;
+
+    DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
     switch(scanent->conn)
     {
     case MC_TCP:  sock = ip_tcpsocket(scanner_options, &errstr, 5);    break;
     switch(scanent->conn)
     {
     case MC_TCP:  sock = ip_tcpsocket(scanner_options, &errstr, 5);    break;
@@ -518,10 +595,10 @@ if (!malware_ok)
       return m_errlog_defer(scanent, CUS callout_address, errstr);
     break;
   }
       return m_errlog_defer(scanent, CUS callout_address, errstr);
     break;
   }
-  DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo %s\n", scanner_name, readconf_printtime(timeout));
 
   switch (scanent->scancode)
     {
 
   switch (scanent->scancode)
     {
+#ifndef DISABLE_MAL_FFROTD
     case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
       {
       uschar *fp_scan_option;
     case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
       {
       uschar *fp_scan_option;
@@ -575,7 +652,56 @@ if (!malware_ok)
        }
       break;
       }        /* f-protd */
        }
       break;
       }        /* f-protd */
+#endif
+
+#ifndef DISABLE_MAL_FFROT6D
+    case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
+      {
+      int bread;
+      uschar * e;
+      uschar * linebuffer;
+      uschar * scanrequest;
+      uschar av_buffer[1024];
+
+      if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
+        || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
+        return malware_errlog_defer(errstr);
+
+      scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
+      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
+        scanner_name, scanrequest);
+
+      if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
+        return m_errlog_defer(scanent, CUS callout_address, errstr);
+
+      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
 
 
+      if (bread <= 0)
+        return m_errlog_defer_3(scanent, CUS callout_address,
+          string_sprintf("unable to read from socket (%s)", strerror(errno)),
+          sock);
+
+      if (bread == sizeof(av_buffer))
+        return m_errlog_defer_3(scanent, CUS callout_address,
+          US"buffer too small", sock);
+
+      av_buffer[bread] = '\0';
+      linebuffer = string_copy(av_buffer);
+
+      m_sock_send(sock, US"QUIT\n", 5, 0);
+
+      if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
+        return m_errlog_defer_3(scanent, CUS callout_address,
+          string_sprintf("scanner reported error (%s)", e), sock);
+
+      if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
+        malware_name = NULL;
+
+      break;
+      }  /* f-prot6d */
+#endif
+
+#ifndef DISABLE_MAL_DRWEB
     case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
   /* v0.1 - added support for tcp sockets          */
   /* v0.0 - initial release -- support for unix sockets      */
     case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
   /* v0.1 - added support for tcp sockets          */
   /* v0.0 - initial release -- support for unix sockets      */
@@ -602,7 +728,8 @@ if (!malware_ok)
 
        if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
          {
 
        if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
          {
-         int err = errno;
+         int err;
+badseek:  err = errno;
          (void)close(drweb_fd);
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
          (void)close(drweb_fd);
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
@@ -619,7 +746,8 @@ if (!malware_ok)
            sock);
          }
        drweb_slen = htonl(fsize);
            sock);
          }
        drweb_slen = htonl(fsize);
-       lseek(drweb_fd, 0, SEEK_SET);
+       if (lseek(drweb_fd, 0, SEEK_SET) < 0)
+         goto badseek;
 
        DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
            scanner_name, scanner_options);
 
        DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
            scanner_name, scanner_options);
@@ -699,6 +827,7 @@ if (!malware_ok)
       if (drweb_vnum)
        {
        int i;
       if (drweb_vnum)
        {
        int i;
+       gstring * g = NULL;
 
        /* setup default virus name */
        malware_name = US"unknown";
 
        /* setup default virus name */
        malware_name = US"unknown";
@@ -710,7 +839,8 @@ if (!malware_ok)
        /* read and concatenate virus names into one string */
        for (i = 0; i < drweb_vnum; i++)
          {
        /* read and concatenate virus names into one string */
        for (i = 0; i < drweb_vnum; i++)
          {
-         int size = 0, off = 0, ovector[10*3];
+         int ovector[10*3];
+
          /* read the size of report */
          if (!recv_len(sock, &drweb_slen, sizeof(drweb_slen), tmo))
            return m_errlog_defer_3(scanent, CUS callout_address,
          /* read the size of report */
          if (!recv_len(sock, &drweb_slen, sizeof(drweb_slen), tmo))
            return m_errlog_defer_3(scanent, CUS callout_address,
@@ -726,7 +856,7 @@ if (!malware_ok)
 
          /* try matcher on the line, grab substring */
          result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
 
          /* try matcher on the line, grab substring */
          result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
-                                 ovector, nelements(ovector));
+                                 ovector, nelem(ovector));
          if (result >= 2)
            {
            const char * pre_malware_nb;
          if (result >= 2)
            {
            const char * pre_malware_nb;
@@ -734,16 +864,16 @@ if (!malware_ok)
            pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
 
            if (i==0)   /* the first name we just copy to malware_name */
            pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
 
            if (i==0)   /* the first name we just copy to malware_name */
-             malware_name = string_append(NULL, &size, &off,
-                                         1, pre_malware_nb);
+             g = string_cat(NULL, US pre_malware_nb);
 
 
+           /*XXX could be string_append_listele? */
            else        /* concatenate each new virus name to previous */
            else        /* concatenate each new virus name to previous */
-             malware_name = string_append(malware_name, &size, &off,
-                                         2, "/", pre_malware_nb);
+             g = string_append(g, 2, "/", pre_malware_nb);
 
            pcre_free_substring(pre_malware_nb);
            }
          }
 
            pcre_free_substring(pre_malware_nb);
            }
          }
+         malware_name = string_from_gstring(g);
        }
       else
        {
        }
       else
        {
@@ -767,7 +897,9 @@ if (!malware_ok)
        }
       break;
       }        /* drweb */
        }
       break;
       }        /* drweb */
+#endif
 
 
+#ifndef DISABLE_MAL_AVE
     case M_AVES: /* "aveserver" scanner type -------------------------------- */
       {
       uschar buf[32768];
     case M_AVES: /* "aveserver" scanner type -------------------------------- */
       {
       uschar buf[32768];
@@ -780,7 +912,7 @@ if (!malware_ok)
       if (buf[0] != '2')               /* aveserver is having problems */
        return m_errlog_defer_3(scanent, CUS callout_address,
          string_sprintf("unavailable (Responded: %s).",
       if (buf[0] != '2')               /* aveserver is having problems */
        return m_errlog_defer_3(scanent, CUS callout_address,
          string_sprintf("unavailable (Responded: %s).",
-                         ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
+                         ((buf[0] != 0) ? buf : US "nothing") ),
          sock);
 
       /* prepare our command */
          sock);
 
       /* prepare our command */
@@ -825,7 +957,7 @@ if (!malware_ok)
       if (buf[0] != '2')               /* aveserver is having problems */
        return m_errlog_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to quit dialogue (Responded: %s).",
       if (buf[0] != '2')               /* aveserver is having problems */
        return m_errlog_defer_3(scanent, CUS callout_address,
          string_sprintf("unable to quit dialogue (Responded: %s).",
-                       ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
+                       ((buf[0] != 0) ? buf : US "nothing") ),
          sock);
 
       if (result == DEFER)
          sock);
 
       if (result == DEFER)
@@ -835,7 +967,9 @@ if (!malware_ok)
        }
       break;
       }        /* aveserver */
        }
       break;
       }        /* aveserver */
+#endif
 
 
+#ifndef DISABLE_MAL_FSECURE
     case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
       {
       int i, j, bread = 0;
     case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
       {
       int i, j, bread = 0;
@@ -852,7 +986,7 @@ if (!malware_ok)
          scanner_name, scanner_options);
       /* pass options */
       memset(av_buffer, 0, sizeof(av_buffer));
          scanner_name, scanner_options);
       /* pass options */
       memset(av_buffer, 0, sizeof(av_buffer));
-      for (i = 0; i != nelements(cmdopt); i++)
+      for (i = 0; i != nelem(cmdopt); i++)
        {
 
        if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
        {
 
        if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
@@ -918,7 +1052,9 @@ if (!malware_ok)
       fsec_found:
        break;
       }        /* fsecure */
       fsec_found:
        break;
       }        /* fsecure */
+#endif
 
 
+#ifndef DISABLE_MAL_KAV
     case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
       {
       time_t t;
     case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
       {
       time_t t;
@@ -1030,7 +1166,9 @@ if (!malware_ok)
 
       break;
       }
 
       break;
       }
+#endif
 
 
+#ifndef DISABLE_MAL_CMDLINE
     case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
       {
       const uschar *cmdline_scanner = scanner_options;
     case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
       {
       const uschar *cmdline_scanner = scanner_options;
@@ -1091,8 +1229,7 @@ if (!malware_ok)
        }
       scanner_fd = fileno(scanner_out);
 
        }
       scanner_fd = fileno(scanner_out);
 
-      file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
-                               spool_directory, message_id, message_id);
+      file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
 
       if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
        {
 
       if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
        {
@@ -1140,7 +1277,7 @@ if (!malware_ok)
       sep = pclose(scanner_out);
       signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
       if (sep != 0)
       sep = pclose(scanner_out);
       signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
       if (sep != 0)
-         return m_errlog_defer(scanent, NULL, 
+         return m_errlog_defer(scanent, NULL,
              sep == -1
              ? string_sprintf("running scanner failed: %s", strerror(sep))
              : string_sprintf("scanner returned error code: %d", sep));
              sep == -1
              ? string_sprintf("running scanner failed: %s", strerror(sep))
              : string_sprintf("scanner returned error code: %d", sep));
@@ -1165,7 +1302,9 @@ if (!malware_ok)
        malware_name = NULL;
       break;
       }        /* cmdline */
        malware_name = NULL;
       break;
       }        /* cmdline */
+#endif
 
 
+#ifndef DISABLE_MAL_SOPHIE
     case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
       {
       int bread = 0;
     case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
       {
       int bread = 0;
@@ -1210,7 +1349,9 @@ if (!malware_ok)
 
       break;
       }
 
       break;
       }
+#endif
 
 
+#ifndef DISABLE_MAL_CLAM
     case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
       {
 /* This code was originally contributed by David Saez */
     case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
       {
 /* This code was originally contributed by David Saez */
@@ -1221,13 +1362,11 @@ if (!malware_ok)
 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
-* the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
-* WITH_OLD_CLAMAV_STREAM is defined.
+* the TCP-connected daemon is actually local; otherwise we use zINSTREAM
 * See Exim bug 926 for details.  */
 
       uschar *p, *vname, *result_tag;
       int bread=0;
 * See Exim bug 926 for details.  */
 
       uschar *p, *vname, *result_tag;
       int bread=0;
-      uschar * file_name;
       uschar av_buffer[1024];
       uschar *hostname = US"";
       host_item connhost;
       uschar av_buffer[1024];
       uschar *hostname = US"";
       host_item connhost;
@@ -1238,13 +1377,8 @@ if (!malware_ok)
       BOOL use_scan_command = FALSE;
       clamd_address * cv[MAX_CLAMD_SERVERS];
       int num_servers = 0;
       BOOL use_scan_command = FALSE;
       clamd_address * cv[MAX_CLAMD_SERVERS];
       int num_servers = 0;
-#ifdef WITH_OLD_CLAMAV_STREAM
-      unsigned int port;
-      uschar av_buffer2[1024];
-      int sockData;
-#else
       uint32_t send_size, send_final_zeroblock;
       uint32_t send_size, send_final_zeroblock;
-#endif
+      blob cmd_str;
 
       /*XXX if unixdomain socket, only one server supported. Needs fixing;
       there's no reason we should not mix local and remote servers */
 
       /*XXX if unixdomain socket, only one server supported. Needs fixing;
       there's no reason we should not mix local and remote servers */
@@ -1298,13 +1432,13 @@ if (!malware_ok)
          sublist = scanner_options;
          if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
            {
          sublist = scanner_options;
          if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
            {
-           (void) m_errlog_defer(scanent, NULL, 
+           (void) m_errlog_defer(scanent, NULL,
                      string_sprintf("missing address: '%s'", scanner_options));
            continue;
            }
          if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
            {
                      string_sprintf("missing address: '%s'", scanner_options));
            continue;
            }
          if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
            {
-           (void) m_errlog_defer(scanent, NULL, 
+           (void) m_errlog_defer(scanent, NULL,
                      string_sprintf("missing port: '%s'", scanner_options));
            continue;
            }
                      string_sprintf("missing port: '%s'", scanner_options));
            continue;
            }
@@ -1340,6 +1474,15 @@ if (!malware_ok)
          string_sprintf("local/SCAN mode incompatible with" \
            " : in path to email filename [%s]", eml_filename));
 
          string_sprintf("local/SCAN mode incompatible with" \
            " : in path to email filename [%s]", eml_filename));
 
+      /* Set up the very first data we will be sending */
+      if (!use_scan_command)
+       { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
+      else
+       {
+       cmd_str.data = string_sprintf("SCAN %s\n", eml_filename);
+       cmd_str.len = Ustrlen(cmd_str.data);
+       }
+
       /* We have some network servers specified */
       if (num_servers)
        {
       /* We have some network servers specified */
       if (num_servers)
        {
@@ -1349,7 +1492,7 @@ if (!malware_ok)
 
        while (num_servers > 0)
          {
 
        while (num_servers > 0)
          {
-         int i = random_number( num_servers );
+         int i = random_number(num_servers);
          clamd_address * cd = cv[i];
 
          DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
          clamd_address * cd = cv[i];
 
          DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
@@ -1359,11 +1502,12 @@ if (!malware_ok)
           * on both connections (as one host could resolve to multiple ips) */
          for (;;)
            {
           * on both connections (as one host could resolve to multiple ips) */
          for (;;)
            {
-           sock= m_tcpsocket(cd->hostspec, cd->tcp_port, &connhost, &errstr);
-           if (sock >= 0)
+           if ((sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
+                                   &connhost, &errstr, &cmd_str)) >= 0)
              {
              /* Connection successfully established with a server */
              hostname = cd->hostspec;
              {
              /* Connection successfully established with a server */
              hostname = cd->hostspec;
+             cmd_str.len = 0;
              break;
              }
            if (cd->retry <= 0) break;
              break;
              }
            if (cd->retry <= 0) break;
@@ -1403,49 +1547,6 @@ if (!malware_ok)
 
       if (!use_scan_command)
        {
 
       if (!use_scan_command)
        {
-#ifdef WITH_OLD_CLAMAV_STREAM
-       /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
-        * that port on a second connection; then in the scan-method-neutral
-        * part, read the response back on the original connection. */
-
-       DEBUG(D_acl) debug_printf_indent(
-           "Malware scan: issuing %s old-style remote scan (PORT)\n",
-           scanner_name);
-
-       /* Pass the string to ClamAV (7 = "STREAM\n") */
-       if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
-         return m_errlog_defer(scanent, CUS callout_address, errstr);
-
-       memset(av_buffer2, 0, sizeof(av_buffer2));
-       bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), tmo-time(NULL));
-
-       if (bread < 0)
-         return m_errlog_defer_3(scanent, CUS callout_address,
-           string_sprintf("unable to read PORT from socket (%s)",
-               strerror(errno)),
-           sock);
-
-       if (bread == sizeof(av_buffer2))
-         return m_errlog_defer_3(scanent, CUS callout_address,
-                 "buffer too small", sock);
-
-       if (!(*av_buffer2))
-         return m_errlog_defer_3(scanent, CUS callout_address,
-                 "ClamAV returned null", sock);
-
-       av_buffer2[bread] = '\0';
-       if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 )
-         return m_errlog_defer_3(scanent, CUS callout_address,
-           string_sprintf("Expected port information from clamd, got '%s'",
-             av_buffer2),
-           sock);
-
-       sockData = m_tcpsocket(connhost.address, port, NULL, &errstr);
-       if (sockData < 0)
-         return m_errlog_defer_3(scanent, CUS callout_address, errstr, sock);
-
-# define CLOSE_SOCKDATA (void)close(sockData)
-#else /* WITH_OLD_CLAMAV_STREAM not defined */
        /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
        chunks, <n> a 4-byte number (network order), terminated by a zero-length
        chunk. */
        /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
        chunks, <n> a 4-byte number (network order), terminated by a zero-length
        chunk. */
@@ -1454,21 +1555,18 @@ if (!malware_ok)
            "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
            scanner_name);
 
            "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
            scanner_name);
 
-       /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
-       if (send(sock, "zINSTREAM", 10, 0) < 0)
-         return m_errlog_defer_3(scanent, CUS hostname,
-           string_sprintf("unable to send zINSTREAM to socket (%s)",
-             strerror(errno)),
-           sock);
-
-# define CLOSE_SOCKDATA /**/
-#endif
+       /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
+       if (cmd_str.len)
+         if (send(sock, cmd_str.data, cmd_str.len, 0) < 0)
+           return m_errlog_defer_3(scanent, CUS hostname,
+             string_sprintf("unable to send zINSTREAM to socket (%s)",
+               strerror(errno)),
+             sock);
 
        /* calc file size */
        if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0)
          {
          int err = errno;
 
        /* calc file size */
        if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0)
          {
          int err = errno;
-         CLOSE_SOCKDATA;
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't open spool file %s: %s",
              eml_filename, strerror(err)),
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't open spool file %s: %s",
              eml_filename, strerror(err)),
@@ -1476,8 +1574,9 @@ if (!malware_ok)
          }
        if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0)
          {
          }
        if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0)
          {
-         int err = errno;
-         CLOSE_SOCKDATA; (void)close(clam_fd);
+         int err;
+b_seek:   err = errno;
+         (void)close(clam_fd);
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
              eml_filename, strerror(err)),
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
              eml_filename, strerror(err)),
@@ -1486,17 +1585,18 @@ if (!malware_ok)
        fsize_uint = (unsigned int) fsize;
        if ((off_t)fsize_uint != fsize)
          {
        fsize_uint = (unsigned int) fsize;
        if ((off_t)fsize_uint != fsize)
          {
-         CLOSE_SOCKDATA; (void)close(clam_fd);
+         (void)close(clam_fd);
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("seeking spool file %s, size overflow",
              eml_filename),
            sock);
          }
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("seeking spool file %s, size overflow",
              eml_filename),
            sock);
          }
-       lseek(clam_fd, 0, SEEK_SET);
+       if (lseek(clam_fd, 0, SEEK_SET) < 0)
+         goto b_seek;
 
        if (!(clamav_fbuf = US malloc(fsize_uint)))
          {
 
        if (!(clamav_fbuf = US malloc(fsize_uint)))
          {
-         CLOSE_SOCKDATA; (void)close(clam_fd);
+         (void)close(clam_fd);
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("unable to allocate memory %u for file (%s)",
              fsize_uint, eml_filename),
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("unable to allocate memory %u for file (%s)",
              fsize_uint, eml_filename),
@@ -1506,7 +1606,7 @@ if (!malware_ok)
        if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0)
          {
          int err = errno;
        if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0)
          {
          int err = errno;
-         free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd);
+         free(clamav_fbuf); (void)close(clam_fd);
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't read spool file %s: %s",
              eml_filename, strerror(err)),
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't read spool file %s: %s",
              eml_filename, strerror(err)),
@@ -1515,16 +1615,6 @@ if (!malware_ok)
        (void)close(clam_fd);
 
        /* send file body to socket */
        (void)close(clam_fd);
 
        /* send file body to socket */
-#ifdef WITH_OLD_CLAMAV_STREAM
-       if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0)
-         {
-         free(clamav_fbuf); CLOSE_SOCKDATA;
-         return m_errlog_defer_3(scanent, NULL,
-           string_sprintf("unable to send file body to socket (%s:%u)",
-             hostname, port),
-           sock);
-         }
-#else
        send_size = htonl(fsize_uint);
        send_final_zeroblock = 0;
        if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
        send_size = htonl(fsize_uint);
        send_final_zeroblock = 0;
        if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
@@ -1536,12 +1626,8 @@ if (!malware_ok)
            string_sprintf("unable to send file body to socket (%s)", hostname),
            sock);
          }
            string_sprintf("unable to send file body to socket (%s)", hostname),
            sock);
          }
-#endif
 
        free(clamav_fbuf);
 
        free(clamav_fbuf);
-
-       CLOSE_SOCKDATA;
-#undef CLOSE_SOCKDATA
        }
       else
        { /* use scan command */
        }
       else
        { /* use scan command */
@@ -1558,17 +1644,17 @@ if (!malware_ok)
        scanned twice, in the broken out files and from the original .eml.
        Since ClamAV now handles emails (and has for quite some time) we can
        just use the email file itself. */
        scanned twice, in the broken out files and from the original .eml.
        Since ClamAV now handles emails (and has for quite some time) we can
        just use the email file itself. */
-       /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
-       file_name = string_sprintf("SCAN %s\n", eml_filename);
+       /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
 
        DEBUG(D_acl) debug_printf_indent(
            "Malware scan: issuing %s local-path scan [%s]\n",
            scanner_name, scanner_options);
 
 
        DEBUG(D_acl) debug_printf_indent(
            "Malware scan: issuing %s local-path scan [%s]\n",
            scanner_name, scanner_options);
 
-       if (send(sock, file_name, Ustrlen(file_name), 0) < 0)
-         return m_errlog_defer_3(scanent, CUS callout_address,
-           string_sprintf("unable to write to socket (%s)", strerror(errno)),
-           sock);
+       if (cmd_str.len)
+         if (send(sock, cmd_str.data, cmd_str.len, 0) < 0)
+           return m_errlog_defer_3(scanent, CUS callout_address,
+             string_sprintf("unable to write to socket (%s)", strerror(errno)),
+             sock);
 
        /* Do not shut down the socket for writing; a user report noted that
         * clamd 0.70 does not react well to this. */
 
        /* Do not shut down the socket for writing; a user report noted that
         * clamd 0.70 does not react well to this. */
@@ -1679,7 +1765,9 @@ if (!malware_ok)
 
       break;
       } /* clamd */
 
       break;
       } /* clamd */
+#endif
 
 
+#ifndef DISABLE_MAL_SOCK
     case M_SOCK: /* "sock" scanner type ------------------------------------- */
     /* This code was derived by Martin Poole from the clamd code contributed
        by David Saez and the cmdline code
     case M_SOCK: /* "sock" scanner type ------------------------------------- */
     /* This code was derived by Martin Poole from the clamd code contributed
        by David Saez and the cmdline code
@@ -1695,8 +1783,10 @@ if (!malware_ok)
       const pcre *sockline_name_re;
 
       /* find scanner command line */
       const pcre *sockline_name_re;
 
       /* find scanner command line */
-      if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
-                                         NULL, 0)))
+      if (  (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
+                                         NULL, 0))
+        && *sockline_scanner
+        )
       {        /* check for no expansions apart from one %s */
        uschar * s = Ustrchr(sockline_scanner, '%');
        if (s++)
       {        /* check for no expansions apart from one %s */
        uschar * s = Ustrchr(sockline_scanner, '%');
        if (s++)
@@ -1706,6 +1796,8 @@ if (!malware_ok)
       }
       else
        sockline_scanner = sockline_scanner_default;
       }
       else
        sockline_scanner = sockline_scanner_default;
+      DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
+       string_printing(sockline_scanner));
 
       /* find scanner output trigger */
       sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
 
       /* find scanner output trigger */
       sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
@@ -1720,9 +1812,9 @@ if (!malware_ok)
        return m_errlog_defer_3(scanent, NULL, errstr, sock);
 
       /* prepare scanner call - security depends on expansions check above */
        return m_errlog_defer_3(scanent, NULL, errstr, sock);
 
       /* prepare scanner call - security depends on expansions check above */
-      commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
-      commandline = string_sprintf( CS sockline_scanner, CS commandline);
-
+      commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
+      DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
+       string_printing(commandline));
 
       /* Pass the command string to the socket */
       if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
 
       /* Pass the command string to the socket */
       if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
@@ -1741,18 +1833,24 @@ if (!malware_ok)
                US"buffer too small", sock);
       av_buffer[bread] = '\0';
       linebuffer = string_copy(av_buffer);
                US"buffer too small", sock);
       av_buffer[bread] = '\0';
       linebuffer = string_copy(av_buffer);
+      DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
+       string_printing(linebuffer));
 
       /* try trigger match */
       if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
        {
        if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
          malware_name = US "unknown";
 
       /* try trigger match */
       if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
        {
        if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
          malware_name = US "unknown";
+       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
+         string_printing(malware_name));
        }
       else /* no virus found */
        malware_name = NULL;
       break;
       }
        }
       else /* no virus found */
        malware_name = NULL;
       break;
       }
+#endif
 
 
+#ifndef DISABLE_MAL_MKS
     case M_MKSD: /* "mksd" scanner type ------------------------------------- */
       {
       char *mksd_options_end;
     case M_MKSD: /* "mksd" scanner type ------------------------------------- */
       {
       char *mksd_options_end;
@@ -1785,7 +1883,9 @@ if (!malware_ok)
        }
       break;
       }
        }
       break;
       }
+#endif
 
 
+#ifndef DISABLE_MAL_AVAST
     case M_AVAST: /* "avast" scanner type ----------------------------------- */
       {
       int ovector[1*3];
     case M_AVAST: /* "avast" scanner type ----------------------------------- */
       {
       int ovector[1*3];
@@ -1844,8 +1944,7 @@ if (!malware_ok)
                }
              else
                {
                }
              else
                {
-               scanrequest = string_sprintf("SCAN %s/scan/%s\n",
-                   spool_directory, message_id);
+               scanrequest = string_sprintf("SCAN %s\n", eml_dir);
                avast_stage = AVA_RSP;          /* just sent command */
                }
 
                avast_stage = AVA_RSP;          /* just sent command */
                }
 
@@ -1866,18 +1965,20 @@ if (!malware_ok)
                break;  /* ignore the "210 SCAN DATA" message */
 
              if (pcre_exec(ava_re_clean, NULL, CS buf, slen,
                break;  /* ignore the "210 SCAN DATA" message */
 
              if (pcre_exec(ava_re_clean, NULL, CS buf, slen,
-                   0, 0, ovector, nelements(ovector)) > 0)
+                   0, 0, ovector, nelem(ovector)) > 0)
                break;
 
                break;
 
-             if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
+             if (  !malware_name
+                && (malware_name = m_pcre_exec(ava_re_virus, buf)))
                { /* remove backslash in front of [whitespace|backslash] */
                uschar * p, * p0;
                for (p = malware_name; *p; ++p)
                  if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
                    for (p0 = p; *p0; ++p0) *p0 = p0[1];
 
                { /* remove backslash in front of [whitespace|backslash] */
                uschar * p, * p0;
                for (p = malware_name; *p; ++p)
                  if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
                    for (p0 = p; *p0; ++p0) *p0 = p0[1];
 
-               avast_stage = AVA_DONE;
-               goto endloop;
+               DEBUG(D_acl)
+                 debug_printf_indent("unescaped m-name: '%s'\n", malware_name);
+               break;
                }
 
              if (Ustrncmp(buf, "200 SCAN OK", 11) == 0)
                }
 
              if (Ustrncmp(buf, "200 SCAN OK", 11) == 0)
@@ -1888,15 +1989,14 @@ if (!malware_ok)
                              "unable to send quit request to socket (%s): %s",
                              scanner_options, strerror(errno)),
                              sock);
                              "unable to send quit request to socket (%s): %s",
                              scanner_options, strerror(errno)),
                              sock);
-               malware_name = NULL;
+
                avast_stage = AVA_DONE;
                avast_stage = AVA_DONE;
-               goto endloop;
                }
 
                }
 
-             /* here for any unexpected response from the scanner */
+             /* here also for any unexpected response from the scanner */
              goto endloop;
 
              goto endloop;
 
-           case AVA_DONE:      log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
+           default:    log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
                            __FILE__, __LINE__, __FUNCTION__);
            }
          }
                            __FILE__, __LINE__, __FUNCTION__);
            }
          }
@@ -1919,51 +2019,7 @@ if (!malware_ok)
        }
       break;
       }
        }
       break;
       }
-
-    case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
-      {
-      int bread;
-      uschar * e;
-      uschar * linebuffer;
-      uschar * scanrequest;
-      uschar av_buffer[1024];
-
-      if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
-        || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
-        return malware_errlog_defer(errstr);
-
-      scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
-      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
-        scanner_name, scanrequest);
-
-      if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
-        return m_errlog_defer(scanent, CUS callout_address, errstr);
-
-      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
-
-      if (bread <= 0)
-        return m_errlog_defer_3(scanent, CUS callout_address,
-          string_sprintf("unable to read from socket (%s)", strerror(errno)),
-          sock);
-
-      if (bread == sizeof(av_buffer))
-        return m_errlog_defer_3(scanent, CUS callout_address,
-          US"buffer too small", sock);
-
-      av_buffer[bread] = '\0';
-      linebuffer = string_copy(av_buffer);
-
-      m_sock_send(sock, US"QUIT\n", 5, 0);
-
-      if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
-        return m_errlog_defer_3(scanent, CUS callout_address,
-          string_sprintf("scanner reported error (%s)", e), sock);
-
-      if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
-        malware_name = NULL;
-
-      break;
-      }  /* f-prot6d */
+#endif
   }    /* scanner type switch */
 
   if (sock >= 0)
   }    /* scanner type switch */
 
   if (sock >= 0)
@@ -2000,14 +2056,9 @@ Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
 int
 malware(const uschar * malware_re, int timeout)
 {
 int
 malware(const uschar * malware_re, int timeout)
 {
-uschar * scan_filename;
-int ret;
+int ret = malware_internal(malware_re, NULL, timeout);
 
 
-scan_filename = string_sprintf("%s/scan/%s/%s.eml",
-                 spool_directory, message_id, message_id);
-ret = malware_internal(malware_re, scan_filename, timeout, FALSE);
 if (ret == DEFER) av_failed = TRUE;
 if (ret == DEFER) av_failed = TRUE;
-
 return ret;
 }
 
 return ret;
 }
 
@@ -2045,7 +2096,7 @@ recipients_list = NULL;
 receive_add_recipient(US"malware-victim@example.net", -1);
 enable_dollar_recipients = TRUE;
 
 receive_add_recipient(US"malware-victim@example.net", -1);
 enable_dollar_recipients = TRUE;
 
-ret = malware_internal(US"*", eml_filename, 0,  TRUE);
+ret = malware_internal(US"*", eml_filename, 0);
 
 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
 spool_mbox_ok = 1;
 
 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
 spool_mbox_ok = 1;
@@ -2067,24 +2118,47 @@ malware_init(void)
 {
 if (!malware_default_re)
   malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
 {
 if (!malware_default_re)
   malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
+
+#ifndef DISABLE_MAL_DRWEB
 if (!drweb_re)
   drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
 if (!drweb_re)
   drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
+#endif
+#ifndef DISABLE_MAL_FSECURE
 if (!fsec_re)
   fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
 if (!fsec_re)
   fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
+#endif
+#ifndef DISABLE_MAL_KAV
 if (!kav_re_sus)
   kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
 if (!kav_re_inf)
   kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
 if (!kav_re_sus)
   kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
 if (!kav_re_inf)
   kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
+#endif
+#ifndef DISABLE_MAL_AVA
 if (!ava_re_clean)
   ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
 if (!ava_re_virus)
   ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
 if (!ava_re_clean)
   ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
 if (!ava_re_virus)
   ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
+#endif
+#ifndef DISABLE_MAL_FPROT6D
 if (!fprot6d_re_error)
   fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
 if (!fprot6d_re_virus)
   fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
 if (!fprot6d_re_error)
   fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
 if (!fprot6d_re_virus)
   fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
+#endif
 }
 
 }
 
+
+void
+malware_show_supported(FILE * f)
+{
+struct scan * sc;
+fprintf(f, "Malware:");
+for (sc = m_scans; sc->scancode != -1; sc++) fprintf(f, " %s", sc->name);
+fprintf(f, "\n");
+}
+
+
+# endif        /*!MACRO_PREDEF*/
 #endif /*WITH_CONTENT_SCAN*/
 /*
  * vi: aw ai sw=2
 #endif /*WITH_CONTENT_SCAN*/
 /*
  * vi: aw ai sw=2