+ case M_AVAST: /* "avast" scanner type ----------------------------------- */
+ {
+ int ovector[1*3];
+ uschar buf[1024];
+ uschar * scanrequest;
+ const pcre * avast_scan_ok_re, * avast_virus_re;
+ enum {AVA_HELO, AVA_OPT, AVA_CMD, AVA_RSP, AVA_POS, AVA_NEG} avast_stage;
+
+ if ( !(avast_scan_ok_re = m_pcre_compile(US"\\[\\+\\]", &errstr))
+ || !(avast_virus_re =
+ m_pcre_compile(US"\\[L\\]\\d\\.\\d\\t\\d\\s(.*)", &errstr))
+ )
+ return malware_errlog_defer(errstr);
+
+ /* wait for result */
+ for (avast_stage = AVA_HELO; recv_line(sock, buf, sizeof(buf)) > 0; )
+ {
+ int slen = Ustrlen(buf);
+ if (slen >= 1) switch (avast_stage)
+ {
+ case AVA_HELO:
+ if (Ustrncmp(buf, "220", 3) != 0)
+ goto endloop; /* require a 220 */
+ goto sendreq;
+
+ case AVA_OPT:
+ if (Ustrncmp(buf, "210", 3) == 0)
+ break; /* ignore 210 responses */
+ if (Ustrncmp(buf, "200", 3) != 0)
+ goto endloop; /* require a 200 */
+
+ sendreq:
+ /* Check for another option to send. Newline-terminate it. */
+ if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
+ NULL, 0)))
+ {
+ scanrequest = string_sprintf("%s\n", scanrequest);
+ avast_stage = AVA_OPT; /* just sent option */
+ }
+ else
+ {
+ scanrequest = string_sprintf("SCAN %s/scan/%s\r\n",
+ spool_directory, message_id);
+ avast_stage = AVA_CMD; /* just sent command */
+ }
+
+ /* send config-cmd or scan-request to socket */
+
+ if (send(sock, scanrequest, Ustrlen(scanrequest), 0) < 0)
+ return m_errlog_defer_3(scanent,
+ string_sprintf("unable to send scan request to socket (%s): %s",
+ scanner_options, strerror(errno)),
+ sock);
+ break;
+
+ case AVA_CMD:
+ if (Ustrncmp(buf, "210", 3) == 0)
+ break; /* ignore 210 responses */
+
+ /* send (pipelined) quit request to socket */
+ if (send(sock, "QUIT\n", 5, 0) < 0)
+ return m_errlog_defer_3(scanent,
+ string_sprintf("unable to send quit request to socket (%s): %s",
+ scanner_options, strerror(errno)),
+ sock);
+ malware_name = NULL;
+ avast_stage = AVA_RSP; /* waiting for actual response line */
+ break;
+
+ default:
+ if (Ustrncmp(buf, "221", 3) == 0)
+ goto endloop; /* a "quit" response */
+
+ if ((malware_name = m_pcre_exec(avast_virus_re, buf)))
+ {
+ /* remove backslashes from the virus string */
+ char *p, *q;
+ for (p = malware_name; *p; p++) if (*p == '\\')
+ for (q = p; *q; q++) *q = q[1];
+
+ avast_stage = AVA_POS;
+ goto endloop;
+ }
+
+ if (pcre_exec(avast_scan_ok_re, NULL, CS buf, slen,
+ 0, 0, ovector, nelements(ovector)) > 0)
+ avast_stage = AVA_NEG;
+ break;
+ }
+ }
+ endloop:
+
+ switch(avast_stage)
+ {
+ case AVA_HELO: return m_errlog_defer_3(scanent,
+ US"invalid response from scanner", sock);
+ case AVA_CMD: return m_errlog_defer_3(scanent,
+ US"unable to read return code", sock);
+ case AVA_RSP: return m_errlog_defer_3(scanent,
+ US"response interrupted", sock);
+ default: break;
+ }
+ }
+ } /* scanner type switch */