diff --git a/CVE-2019-15961-1.patch b/CVE-2019-15961-1.patch deleted file mode 100644 index a2ac841c67ce42a04bb71c18fc4736834ddd3cdb..0000000000000000000000000000000000000000 --- a/CVE-2019-15961-1.patch +++ /dev/null @@ -1,1106 +0,0 @@ -From bd7b591bbf27fda6fff092f7f707725088ca8769 Mon Sep 17 00:00:00 2001 -From: Andy Ragusa -Date: Thu, 7 Nov 2019 09:10:26 -0800 -Subject: [PATCH] bb12380: Added limits to the mailbox parser. - -Implemented several maximums in parsing MIME messages to avoid Denial of -Service attempts, as well as improving parsing logic to avoid repeatedly -calling the realloc function. These are in response to excessively long scan -times for specially crafted files. -This is in response to CVE-2019-15961. -The limits added are - 1. Limit on number of MIME parts per message. - 2. Limit on number of header bytes. - 3. Limit on number of email headers. - 4. Limit on number of line folds. - 5. Limit on numbef of MIME arguments. ---- - libclamav/mbox.c | 610 +++++++++++++++++++++++++++++++++++++++-------- - 1 file changed, 513 insertions(+), 97 deletions(-) - -diff --git a/libclamav/mbox.c b/libclamav/mbox.c -index d4afa78cd2..684f0d7e34 100644 ---- a/libclamav/mbox.c -+++ b/libclamav/mbox.c -@@ -23,7 +23,6 @@ - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ -- - #if HAVE_CONFIG_H - #include "clamav-config.h" - #endif -@@ -201,9 +200,9 @@ typedef struct mbox_ctx { - #endif - - static int cli_parse_mbox(const char *dir, cli_ctx *ctx); --static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir); --static message *parseEmailHeaders(message *m, const table_t *rfc821Table); --static int parseEmailHeader(message *m, const char *line, const table_t *rfc821Table); -+static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir, cli_ctx *ctx, bool *heuristicFound); -+static message *parseEmailHeaders(message *m, const table_t *rfc821Table, bool *heuristicFound); -+static int parseEmailHeader(message *m, const char *line, const table_t *rfc821, cli_ctx *ctx, bool *heuristicFound); - static int parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata); - static mbox_status parseRootMHTML(mbox_ctx *mctx, message *m, text *t); - static mbox_status parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level); -@@ -212,7 +211,7 @@ static int boundaryEnd(const char *line, const char *boundary); - static int initialiseTables(table_t **rfc821Table, table_t **subtypeTable); - static int getTextPart(message *const messages[], size_t size); - static size_t strip(char *buf, int len); --static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg); -+static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg, cli_ctx *ctx, bool *heuristicFound); - static int saveTextPart(mbox_ctx *mctx, message *m, int destroy_text); - static char *rfc2047(const char *in); - static char *rfc822comments(const char *in, char *out); -@@ -233,6 +232,12 @@ static blob *getHrefs(message *m, tag_arguments_t *hrefs); - static void hrefs_done(blob *b, tag_arguments_t *hrefs); - static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html); - -+static bool haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx); -+static bool hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx); -+static bool haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx); -+static bool haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx); -+static bool haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx); -+ - /* Maximum line length according to RFC2821 */ - #define RFC2821LENGTH 1000 - -@@ -281,6 +286,12 @@ static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html); - */ - #define KNOWBOT 14 /* Unknown and undocumented format? */ - -+#define HEURISTIC_EMAIL_MAX_LINE_FOLDS_PER_HEADER (256 * 1024) -+#define HEURISTIC_EMAIL_MAX_HEADER_BYTES (1024 * 256) -+#define HEURISTIC_EMAIL_MAX_HEADERS 1024 -+#define HEURISTIC_EMAIL_MAX_MIME_PARTS_PER_MESSAGE 1024 -+#define HEURISTIC_EMAIL_MAX_ARGUMENTS_PER_HEADER 256 -+ - static const struct tableinit { - const char *key; - int value; -@@ -423,7 +434,7 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - */ - bool lastLineWasEmpty; - int messagenumber; -- message *m = messageCreate(); -+ message *m = messageCreate(); /*Create an empty email */ - - if (m == NULL) - return CL_EMEM; -@@ -434,15 +445,20 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - - do { - cli_chomp(buffer); -- /*if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ -+ /*if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) */ - if (lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0)) { - cli_dbgmsg("Deal with message number %d\n", messagenumber++); - /* - * End of a message in the mail box - */ -- body = parseEmailHeaders(m, rfc821); -+ bool heuristicFound = FALSE; -+ body = parseEmailHeaders(m, rfc821, &heuristicFound); - if (body == NULL) { - messageReset(m); -+ if (heuristicFound) { -+ retcode = CL_VIRUS; -+ break; -+ } - continue; - } - messageSetCTX(body, ctx); -@@ -493,7 +509,11 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - - if (retcode == CL_SUCCESS) { - cli_dbgmsg("Extract attachments from email %d\n", messagenumber); -- body = parseEmailHeaders(m, rfc821); -+ bool heuristicFound = FALSE; -+ body = parseEmailHeaders(m, rfc821, &heuristicFound); -+ if (heuristicFound) { -+ retcode = CL_VIRUS; -+ } - } - if (m) - messageDestroy(m); -@@ -520,7 +540,11 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - - buffer[sizeof(buffer) - 1] = '\0'; - -- body = parseEmailFile(map, &at, rfc821, buffer, dir); -+ bool heuristicFound = FALSE; -+ body = parseEmailFile(map, &at, rfc821, buffer, dir, ctx, &heuristicFound); -+ if (heuristicFound) { -+ retcode = CL_VIRUS; -+ } - } - - if (body) { -@@ -576,6 +600,253 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - return retcode; - } - -+/*TODO: move these to a header.*/ -+#define DO_STRDUP(buf, var) \ -+ do { \ -+ var = strdup(buf); \ -+ if (NULL == var) { \ -+ goto done; \ -+ } \ -+ } while (0) -+ -+#define DO_FREE(var) \ -+ do { \ -+ if (NULL != var) { \ -+ free(var); \ -+ var = NULL; \ -+ } \ -+ } while (0) -+ -+#define DO_MALLOC(var, size) \ -+ do { \ -+ var = malloc(size); \ -+ if (NULL == var) { \ -+ goto done; \ -+ } \ -+ } while (0) -+ -+#define DO_CALLOC(var, size) \ -+ do { \ -+ (var) = calloc((size), sizeof *(var)); \ -+ if (NULL == var) { \ -+ goto done; \ -+ } \ -+ } while (0) -+ -+#define DO_VERIFY_POINTER(ptr) \ -+ do { \ -+ if (NULL == ptr) { \ -+ goto done; \ -+ } \ -+ } while (0) -+ -+#define READ_STRUCT_BUFFER_LEN 1024 -+typedef struct _ReadStruct { -+ char buffer[READ_STRUCT_BUFFER_LEN + 1]; -+ -+ size_t bufferLen; -+ -+ struct _ReadStruct *next; -+ -+} ReadStruct; -+ -+static ReadStruct * -+appendReadStruct(ReadStruct *rs, const char *const buffer) -+{ -+ if (NULL == rs) { -+ assert(rs && "Invalid argument"); -+ goto done; -+ } -+ -+ size_t spaceLeft = (READ_STRUCT_BUFFER_LEN - rs->bufferLen); -+ -+ if (strlen(buffer) > spaceLeft) { -+ ReadStruct *next = NULL; -+ int part = spaceLeft; -+ strncpy(&(rs->buffer[rs->bufferLen]), buffer, part); -+ rs->bufferLen += part; -+ -+ DO_CALLOC(next, 1); -+ -+ rs->next = next; -+ strcpy(next->buffer, &(buffer[part])); -+ next->bufferLen = strlen(&(buffer[part])); -+ -+ rs = next; -+ } else { -+ strcpy(&(rs->buffer[rs->bufferLen]), buffer); -+ rs->bufferLen += strlen(buffer); -+ } -+ -+done: -+ return rs; -+} -+ -+static char * -+getMallocedBufferFromList(const ReadStruct *head) -+{ -+ -+ const ReadStruct *rs = head; -+ int bufferLen = 1; -+ char *working = NULL; -+ char *ret = NULL; -+ -+ while (rs) { -+ bufferLen += rs->bufferLen; -+ rs = rs->next; -+ } -+ -+ DO_MALLOC(working, bufferLen); -+ -+ rs = head; -+ bufferLen = 0; -+ while (rs) { -+ memcpy(&(working[bufferLen]), rs->buffer, rs->bufferLen); -+ bufferLen += rs->bufferLen; -+ working[bufferLen] = 0; -+ rs = rs->next; -+ } -+ -+ ret = working; -+done: -+ if (NULL == ret) { -+ DO_FREE(working); -+ } -+ -+ return ret; -+} -+ -+static void -+freeList(ReadStruct *head) -+{ -+ while (head) { -+ ReadStruct *rs = head->next; -+ DO_FREE(head); -+ head = rs; -+ } -+} -+ -+#ifndef FREELIST_REALLOC -+#define FREELIST_REALLOC(head, curr) \ -+ do { \ -+ if (curr != head) { \ -+ freeList(head->next); \ -+ } \ -+ head->bufferLen = 0; \ -+ head->next = 0; \ -+ curr = head; \ -+ } while (0) -+#endif /*FREELIST_REALLOC*/ -+ -+/*Check if we have repeated blank lines with only a semicolon at the end. Semicolon is a delimiter for parameters, -+ * but if there is no data, it isn't a parameter. Allow the first one because it may be continuation of a previous line -+ * that actually had data in it.*/ -+static bool -+doContinueMultipleEmptyOptions(const char *const line, bool *lastWasOnlySemi) -+{ -+ if (line) { -+ size_t i = 0; -+ int doCont = 1; -+ for (; i < strlen(line); i++) { -+ if (isblank(line[i])) { -+ } else if (';' == line[i]) { -+ } else { -+ doCont = 0; -+ break; -+ } -+ } -+ -+ if (1 == doCont) { -+ if (*lastWasOnlySemi) { -+ return TRUE; -+ } -+ *lastWasOnlySemi = TRUE; -+ } else { -+ *lastWasOnlySemi = FALSE; -+ } -+ } -+ return FALSE; -+} -+ -+static bool -+hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx) -+{ -+ -+ if (line) { -+ if (isblank(line[0])) { -+ (*lineFoldCnt)++; -+ } else { -+ (*lineFoldCnt) = 0; -+ } -+ -+ if ((*lineFoldCnt) >= HEURISTIC_EMAIL_MAX_LINE_FOLDS_PER_HEADER) { -+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { -+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxLineFoldCnt"); -+ } -+ -+ return TRUE; -+ } -+ } -+ return FALSE; -+} -+ -+static bool -+haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx) -+{ -+ -+ if (totalLen > HEURISTIC_EMAIL_MAX_HEADER_BYTES) { -+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { -+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxHeaderBytes"); -+ } -+ -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+static bool -+haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx) -+{ -+ -+ if (totalHeaderCnt > HEURISTIC_EMAIL_MAX_HEADERS) { -+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { -+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxEmailHeaders"); -+ } -+ -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+static bool -+haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx) -+{ -+ -+ if (mimePartCnt >= HEURISTIC_EMAIL_MAX_MIME_PARTS_PER_MESSAGE) { -+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { -+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxMIMEPartsPerMessage"); -+ } -+ -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+static bool -+haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx) -+{ -+ -+ if (argCnt >= HEURISTIC_EMAIL_MAX_ARGUMENTS_PER_HEADER) { -+ if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { -+ cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxMIMEArguments"); -+ } -+ -+ return TRUE; -+ } -+ -+ return FALSE; -+} -+ - /* - * Read in an email message from fin, parse it, and return the message - * -@@ -583,7 +854,7 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - * handled ungracefully... - */ - static message * --parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *firstLine, const char *dir) -+parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *firstLine, const char *dir, cli_ctx *ctx, bool *heuristicFound) - { - bool inHeader = TRUE; - bool bodyIsEmpty = TRUE; -@@ -591,9 +862,21 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - message *ret; - bool anyHeadersFound = FALSE; - int commandNumber = -1; -- char *fullline = NULL, *boundary = NULL; -- size_t fulllinelength = 0; -+ char *boundary = NULL; - char buffer[RFC2821LENGTH + 1]; -+ bool lastWasOnlySemi = FALSE; -+ int err = 1; -+ size_t totalHeaderBytes = 0; -+ size_t totalHeaderCnt = 0; -+ -+ size_t lineFoldCnt = 0; -+ -+ *heuristicFound = FALSE; -+ -+ ReadStruct *head = NULL; -+ ReadStruct *curr = NULL; -+ DO_CALLOC(head, 1); -+ curr = head; - - cli_dbgmsg("parseEmailFile\n"); - -@@ -612,6 +895,15 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - else - line = buffer; - -+ if (doContinueMultipleEmptyOptions(line, &lastWasOnlySemi)) { -+ continue; -+ } -+ -+ if (hitLineFoldCnt(line, &lineFoldCnt, ctx)) { -+ *heuristicFound = TRUE; -+ break; -+ } -+ - /* - * Don't blank lines which are only spaces from headers, - * otherwise they'll be treated as the end of header marker -@@ -624,8 +916,8 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - } - } - if (inHeader) { -- cli_dbgmsg("parseEmailFile: check '%s' fullline %p\n", -- buffer, fullline); -+ cli_dbgmsg("parseEmailFile: check '%s'\n", buffer); -+ - /* - * Ensure wide characters are handled where - * sizeof(char) > 1 -@@ -649,13 +941,30 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - * content-type line. So we just have - * to make a best guess. Sigh. - */ -- if (fullline) { -- if (parseEmailHeader(ret, fullline, rfc821) < 0) -- continue; -+ if (head->bufferLen) { -+ char *header = getMallocedBufferFromList(head); -+ int needContinue = 0; -+ DO_VERIFY_POINTER(header); -+ -+ totalHeaderCnt++; -+ if (haveTooManyEmailHeaders(totalHeaderCnt, ctx)) { -+ *heuristicFound = TRUE; -+ break; -+ } -+ needContinue = (parseEmailHeader(ret, header, rfc821, ctx, heuristicFound) < 0); -+ if (*heuristicFound) { -+ DO_FREE(header); -+ break; -+ } - -- free(fullline); -- fullline = NULL; -+ DO_FREE(header); -+ FREELIST_REALLOC(head, curr); -+ -+ if (needContinue) { -+ continue; -+ } - } -+ - if (boundary || - ((boundary = (char *)messageFindArgument(ret, "boundary")) != NULL)) { - lastWasBlank = TRUE; -@@ -663,7 +972,7 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - } - } - } -- if ((line == NULL) && (fullline == NULL)) { /* empty line */ -+ if ((line == NULL) && (0 == head->bufferLen)) { /* empty line */ - /* - * A blank line signifies the end of - * the header and the start of the text -@@ -678,8 +987,9 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - } else { - char *ptr; - const char *lookahead; -+ bool lineAdded = TRUE; - -- if (fullline == NULL) { -+ if (0 == head->bufferLen) { - char cmd[RFC2821LENGTH + 1], out[RFC2821LENGTH + 1]; - - /* -@@ -712,23 +1022,26 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - anyHeadersFound = usefulHeader(commandNumber, cmd); - continue; - } -- fullline = cli_strdup(line); -- fulllinelength = strlen(line) + 1; -- if (!fullline) { -- if (ret) -+ curr = appendReadStruct(curr, line); -+ if (NULL == curr) { -+ if (ret) { - ret->isTruncated = TRUE; -+ } - break; - } - } else if (line != NULL) { -- fulllinelength += strlen(line) + 1; -- ptr = cli_realloc(fullline, fulllinelength); -- if (ptr == NULL) -- continue; -- fullline = ptr; -- cli_strlcat(fullline, line, fulllinelength); -+ curr = appendReadStruct(curr, line); -+ } else { -+ lineAdded = FALSE; - } - -- assert(fullline != NULL); -+ if (lineAdded) { -+ totalHeaderBytes += strlen(line); -+ if (haveTooManyHeaderBytes(totalHeaderBytes, ctx)) { -+ *heuristicFound = TRUE; -+ break; -+ } -+ } - - if ((lookahead = fmap_need_off_once(map, *at, 1))) { - /* -@@ -746,24 +1059,34 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - * Handle broken headers, where the next - * line isn't indented by whitespace - */ -- if (fullline[strlen(fullline) - 1] == ';') -- /* Add arguments to this line */ -- continue; -+ { -+ char *header = getMallocedBufferFromList(head); /*This is the issue */ -+ int needContinue = 0; -+ needContinue = (header[strlen(header) - 1] == ';'); -+ if (0 == needContinue) { -+ needContinue = (line && (count_quotes(header) & 1)); -+ } - -- if (line && (count_quotes(fullline) & 1)) -- continue; -+ if (0 == needContinue) { -+ totalHeaderCnt++; -+ if (haveTooManyEmailHeaders(totalHeaderCnt, ctx)) { -+ *heuristicFound = TRUE; -+ break; -+ } -+ needContinue = (parseEmailHeader(ret, header, rfc821, ctx, heuristicFound) < 0); -+ if (*heuristicFound) { -+ DO_FREE(header); -+ break; -+ } -+ /*Check total headers here;*/ -+ } - -- ptr = rfc822comments(fullline, NULL); -- if (ptr) { -- free(fullline); -- fullline = ptr; -+ DO_FREE(header); -+ if (needContinue) { -+ continue; -+ } -+ FREELIST_REALLOC(head, curr); - } -- -- if (parseEmailHeader(ret, fullline, rfc821) < 0) -- continue; -- -- free(fullline); -- fullline = NULL; - } - } else if (line && isuuencodebegin(line)) { - /* -@@ -807,19 +1130,17 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - } - } while (getline_from_mbox(buffer, sizeof(buffer) - 1, map, at) != NULL); - -- if (boundary) -- free(boundary); -- -- if (fullline) { -- if (*fullline) switch (commandNumber) { -- case CONTENT_TRANSFER_ENCODING: -- case CONTENT_DISPOSITION: -- case CONTENT_TYPE: -- cli_dbgmsg("parseEmailFile: Fullline unparsed '%s'\n", fullline); -- } -- free(fullline); -+ err = 0; -+done: -+ if (err) { -+ cli_errmsg("parseEmailFile: ERROR parsing file\n"); -+ ret->isTruncated = TRUE; - } - -+ DO_FREE(boundary); -+ -+ freeList(head); -+ - if (!anyHeadersFound) { - /* - * False positive in believing we have an e-mail when we don't -@@ -829,6 +1150,12 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - return NULL; - } - -+ if (*heuristicFound) { -+ messageDestroy(ret); -+ cli_dbgmsg("parseEmailFile: found heuristic\n"); -+ return NULL; -+ } -+ - cli_dbgmsg("parseEmailFile: return\n"); - - return ret; -@@ -843,7 +1170,7 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - * TODO: remove the duplication with parseEmailFile - */ - static message * --parseEmailHeaders(message *m, const table_t *rfc821) -+parseEmailHeaders(message *m, const table_t *rfc821, bool *heuristicFound) - { - bool inHeader = TRUE; - bool bodyIsEmpty = TRUE; -@@ -853,9 +1180,14 @@ parseEmailHeaders(message *m, const table_t *rfc821) - int commandNumber = -1; - char *fullline = NULL; - size_t fulllinelength = 0; -+ bool lastWasOnlySemi = FALSE; -+ size_t lineFoldCnt = 0; -+ size_t totalHeaderCnt = 0; - - cli_dbgmsg("parseEmailHeaders\n"); - -+ *heuristicFound = FALSE; -+ - if (m == NULL) - return NULL; - -@@ -869,6 +1201,15 @@ parseEmailHeaders(message *m, const table_t *rfc821) - else - line = NULL; - -+ if (doContinueMultipleEmptyOptions(line, &lastWasOnlySemi)) { -+ continue; -+ } -+ -+ if (hitLineFoldCnt(line, &lineFoldCnt, m->ctx)) { -+ *heuristicFound = TRUE; -+ break; -+ } -+ - if (inHeader) { - cli_dbgmsg("parseEmailHeaders: check '%s'\n", - line ? line : ""); -@@ -886,6 +1227,7 @@ parseEmailHeaders(message *m, const table_t *rfc821) - bodyIsEmpty = TRUE; - } else { - char *ptr; -+ bool lineAdded = TRUE; - - if (fullline == NULL) { - char cmd[RFC2821LENGTH + 1]; -@@ -931,8 +1273,21 @@ parseEmailHeaders(message *m, const table_t *rfc821) - continue; - fullline = ptr; - cli_strlcat(fullline, line, fulllinelength); -+ } else { -+ lineAdded = FALSE; - } - assert(fullline != NULL); -+ /*continue doesn't seem right here, but that is what is done everywhere else when a malloc fails.*/ -+ if (NULL == fullline) { -+ continue; -+ } -+ -+ if (lineAdded) { -+ if (haveTooManyHeaderBytes(fulllinelength, m->ctx)) { -+ *heuristicFound = TRUE; -+ break; -+ } -+ } - - if (next_is_folded_header(t)) - /* Add arguments to this line */ -@@ -950,8 +1305,17 @@ parseEmailHeaders(message *m, const table_t *rfc821) - fullline = ptr; - } - -- if (parseEmailHeader(ret, fullline, rfc821) < 0) -+ totalHeaderCnt++; -+ if (haveTooManyEmailHeaders(totalHeaderCnt, m->ctx)) { -+ *heuristicFound = TRUE; -+ break; -+ } -+ if (parseEmailHeader(ret, fullline, rfc821, m->ctx, heuristicFound) < 0) { - continue; -+ } -+ if (*heuristicFound) { -+ break; -+ } - - free(fullline); - fullline = NULL; -@@ -997,6 +1361,11 @@ parseEmailHeaders(message *m, const table_t *rfc821) - cli_dbgmsg("parseEmailHeaders: no headers found, assuming it isn't an email\n"); - return NULL; - } -+ if (*heuristicFound) { -+ messageDestroy(ret); -+ cli_dbgmsg("parseEmailHeaders: found a heuristic, delete message and stop parsing.\n"); -+ return NULL; -+ } - - cli_dbgmsg("parseEmailHeaders: return\n"); - -@@ -1007,9 +1376,9 @@ parseEmailHeaders(message *m, const table_t *rfc821) - * Handle a header line of an email message - */ - static int --parseEmailHeader(message *m, const char *line, const table_t *rfc821) -+parseEmailHeader(message *m, const char *line, const table_t *rfc821, cli_ctx *ctx, bool *heuristicFound) - { -- int ret; -+ int ret = -1; - #ifdef CL_THREAD_SAFE - char *strptr; - #endif -@@ -1032,15 +1401,17 @@ parseEmailHeader(message *m, const char *line, const table_t *rfc821) - return -1; - - copy = rfc2047(line); -- if (copy == NULL) -+ if (copy == NULL) { - /* an RFC checker would return -1 here */ - copy = cli_strdup(line); -+ if (NULL == copy) { -+ goto done; -+ } -+ } - - tokenseparator[0] = *separator; - tokenseparator[1] = '\0'; - -- ret = -1; -- - #ifdef CL_THREAD_SAFE - cmd = strtok_r(copy, tokenseparator, &strptr); - #else -@@ -1054,7 +1425,7 @@ parseEmailHeader(message *m, const char *line, const table_t *rfc821) - char *arg = strtok(NULL, ""); - #endif - -- if (arg) -+ if (arg) { - /* - * Found a header such as - * Content-Type: multipart/mixed; -@@ -1062,9 +1433,12 @@ parseEmailHeader(message *m, const char *line, const table_t *rfc821) - * "multipart/mixed" and cmd to - * be "Content-Type" - */ -- ret = parseMimeHeader(m, cmd, rfc821, arg); -+ ret = parseMimeHeader(m, cmd, rfc821, arg, ctx, heuristicFound); -+ } - } -- free(copy); -+done: -+ DO_FREE(copy); -+ - return ret; - } - -@@ -1115,9 +1489,6 @@ parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata - #if HAVE_LIBXML2 - const char *xmlsrt, *xmlend; - xmlTextReaderPtr reader; --#if HAVE_JSON -- json_object *thisjobj = (json_object *)wrkjobj; --#endif - int ret = CL_SUCCESS; - - UNUSEDPARAM(cbdata); -@@ -1310,6 +1681,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - #if HAVE_JSON - json_object *saveobj = mctx->wrkobj; - #endif -+ bool heuristicFound = FALSE; - - cli_dbgmsg("in parseEmailBody, %u files saved so far\n", - mctx->files); -@@ -1391,12 +1763,14 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - cli_dbgmsg("Not a mime encoded message\n"); - aText = textAddMessage(aText, mainMessage); - -- if (!doPhishingScan) -+ if (!doPhishingScan) { - break; -+ } - /* - * Fall through: some phishing mails claim they are - * text/plain, when they are in fact html - */ -+ /* fall through */ - case TEXT: - /* text/plain has been preprocessed as no encoding */ - if (doPhishingScan) { -@@ -1605,7 +1979,11 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * Content-Type: application/octet-stream; - * Content-Transfer-Encoding: base64 - */ -- parseEmailHeader(aMessage, line, mctx->rfc821Table); -+ parseEmailHeader(aMessage, line, mctx->rfc821Table, mctx->ctx, &heuristicFound); -+ if (heuristicFound) { -+ rc = VIRUS; -+ break; -+ } - - while (isspace((int)*line)) - line++; -@@ -1750,8 +2128,11 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - cli_dbgmsg("Multipart %d: About to parse folded header '%s'\n", - multiparts, fullline); - -- parseEmailHeader(aMessage, fullline, mctx->rfc821Table); -+ parseEmailHeader(aMessage, fullline, mctx->rfc821Table, mctx->ctx, &heuristicFound); - free(fullline); -+ if (heuristicFound) { -+ rc = VIRUS; -+ } - } else if (boundaryEnd(line, boundary)) { - /* - * Some viruses put information -@@ -1828,6 +2209,12 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - - free((char *)boundary); - -+ if (haveTooManyMIMEPartsPerMessage(multiparts, mctx->ctx)) { -+ DO_FREE(messages); -+ rc = VIRUS; -+ break; -+ } -+ - /* - * Preprocess. Anything special to be done before - * we handle the multiparts? -@@ -1906,25 +2293,28 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - htmltextPart = getTextPart(messages, multiparts); - - if (htmltextPart >= 0 && messages) { -- if (messageGetBody(messages[htmltextPart])) -+ if (messageGetBody(messages[htmltextPart])) { - - aText = textAddMessage(aText, messages[htmltextPart]); -- } else -+ } -+ } else { - /* - * There isn't an HTML bit. If there's a - * multipart bit, it'll may be in there - * somewhere - */ -- for (i = 0; i < multiparts; i++) -+ for (i = 0; i < multiparts; i++) { - if (messageGetMimeType(messages[i]) == MULTIPART) { - aMessage = messages[i]; - htmltextPart = i; - break; - } -+ } -+ } - -- if (htmltextPart == -1) -+ if (htmltextPart == -1) { - cli_dbgmsg("No HTML code found to be scanned\n"); -- else { -+ } else { - #if HAVE_JSON - /* Send root HTML file for preclassification */ - if (mctx->ctx->wrkproperty) -@@ -1950,6 +2340,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * Content-Type: multipart/related; - * type="multipart/alternative" - */ -+ /* fall through */ - case DIGEST: - /* - * According to section 5.1.5 RFC2046, the -@@ -1971,6 +2362,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * virus is broken that way, and anyway we - * wish to scan all of the alternatives - */ -+ /* fall through */ - case REPORT: - /* - * According to section 1 of RFC1892, the -@@ -2081,7 +2473,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - rc = FAIL; - if ((strcasecmp(mimeSubtype, "rfc822") == 0) || - (strcasecmp(mimeSubtype, "delivery-status") == 0)) { -- message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table); -+ message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table, &heuristicFound); - if (m) { - cli_dbgmsg("Decode rfc822\n"); - -@@ -2096,6 +2488,8 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - rc = parseEmailBody(m, NULL, mctx, recursion_level + 1); - - messageDestroy(m); -+ } else if (heuristicFound) { -+ rc = VIRUS; - } - break; - } else if (strcasecmp(mimeSubtype, "disposition-notification") == 0) { -@@ -2134,6 +2528,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * Content-Type: application/unknown; - * so let's try our best to salvage something - */ -+ /* fall through */ - case APPLICATION: - /*cptr = messageGetMimeSubtype(mainMessage); - -@@ -2734,11 +3129,14 @@ strstrip(char *s) - * Returns 0 for OK, -1 for error - */ - static int --parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg) -+parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg, cli_ctx *ctx, bool *heuristicFound) - { - char *copy, *p, *buf; - const char *ptr; - int commandNumber; -+ size_t argCnt = 0; -+ -+ *heuristicFound = FALSE; - - cli_dbgmsg("parseMimeHeader: cmd='%s', arg='%s'\n", cmd, arg); - -@@ -2746,15 +3144,17 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c - if (copy) { - commandNumber = tableFind(rfc821Table, copy); - free(copy); -- } else -+ } else { - commandNumber = tableFind(rfc821Table, cmd); -+ } - - copy = rfc822comments(arg, NULL); - -- if (copy) -+ if (copy) { - ptr = copy; -- else -+ } else { - ptr = arg; -+ } - - buf = NULL; - -@@ -2889,6 +3289,11 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c - while (cli_strtokbuf(ptr, i++, ";", buf) != NULL) { - cli_dbgmsg("mimeArgs = '%s'\n", buf); - -+ argCnt++; -+ if (haveTooManyMIMEArguments(argCnt, ctx)) { -+ *heuristicFound = TRUE; -+ break; -+ } - messageAddArguments(m, buf); - } - } -@@ -3277,7 +3682,7 @@ rfc1341(message *m, const char *dir) - while ((dent = readdir(dd))) { - #endif - FILE *fin; -- char buffer[BUFSIZ], fullname[NAME_MAX + 1]; -+ char buffer[BUFSIZ], fullname[PATH_MAX + 1]; - int nblanks; - STATBUF statb; - const char *dentry_idpart; -@@ -3802,8 +4207,9 @@ static const char *getMimeTypeStr(mime_type mimetype) - const struct tableinit *entry = mimeTypeStr; - - while (entry->key) { -- if (mimetype == entry->value) -+ if (mimetype == ((mime_type)entry->value)) { - return entry->key; -+ } - entry++; - } - return "UNKNOWN"; -@@ -3817,8 +4223,9 @@ static const char *getEncTypeStr(encoding_type enctype) - const struct tableinit *entry = encTypeStr; - - while (entry->key) { -- if (enctype == entry->value) -+ if (enctype == ((encoding_type)entry->value)) { - return entry->key; -+ } - entry++; - } - return "UNKNOWN"; -@@ -3838,7 +4245,6 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m - message *aMessage = messages[i]; - const int doPhishingScan = mctx->ctx->engine->dboptions & CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); - #if HAVE_JSON -- const char *mtype = NULL; - json_object *thisobj = NULL, *saveobj = mctx->wrkobj; - - if (mctx->wrkobj != NULL) { -@@ -4058,8 +4464,9 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m - messages[i] = NULL; - } else { - *rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); -- if (mainMessage && (mainMessage != messageIn)) -+ if (mainMessage && (mainMessage != messageIn)) { - messageDestroy(mainMessage); -+ } - mainMessage = NULL; - } - #if HAVE_JSON -@@ -4080,18 +4487,21 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m - - if (thisobj != NULL) { - /* attempt to determine container size - prevents incorrect type reporting */ -- if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) -+ if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) { - arrlen = json_object_array_length(arrobj); -+ } - } - - #endif - if (fb) { - /* aMessage doesn't always have a ctx set */ - fileblobSetCTX(fb, mctx->ctx); -- if (fileblobScanAndDestroy(fb) == CL_VIRUS) -+ if (fileblobScanAndDestroy(fb) == CL_VIRUS) { - *rc = VIRUS; -- if (!addToText) -+ } -+ if (!addToText) { - mctx->files++; -+ } - } - #if HAVE_JSON - if (thisobj != NULL) { -@@ -4099,20 +4509,24 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m - const char *dtype = NULL; - - /* attempt to acquire container type */ -- if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) -- if (json_object_array_length(arrobj) > arrlen) -+ if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) { -+ if (json_object_array_length(arrobj) > ((int)arrlen)) { - entry = json_object_array_get_idx(arrobj, arrlen); -+ } -+ } - if (entry) { - json_object_object_get_ex(entry, "FileType", &entry); -- if (entry) -+ if (entry) { - dtype = json_object_get_string(entry); -+ } - } - cli_jsonint(thisobj, "ContainedObjectsIndex", arrlen); - cli_jsonstr(thisobj, "ClamAVFileType", dtype ? dtype : "UNKNOWN"); - } - #endif -- if (messageContainsVirus(aMessage)) -+ if (messageContainsVirus(aMessage)) { - *rc = VIRUS; -+ } - } - messageDestroy(aMessage); - messages[i] = NULL; -@@ -4216,5 +4630,7 @@ newline_in_header(const char *line) - if (strncmp(line, "Date: ", 6) == 0) - return TRUE; - -+ cli_dbgmsg("newline_in_header, returning \"%s\"\n", line); -+ - return FALSE; - } diff --git a/CVE-2019-15961-2.patch b/CVE-2019-15961-2.patch deleted file mode 100644 index 5e1f653840317ee554b1b1b662f7fe53daec3737..0000000000000000000000000000000000000000 --- a/CVE-2019-15961-2.patch +++ /dev/null @@ -1,199 +0,0 @@ -From 482fcd413b07e9fd3ef9850e6d01a45f4e187108 Mon Sep 17 00:00:00 2001 -From: Andy Ragusa -Date: Tue, 19 Nov 2019 15:55:47 -0800 -Subject: [PATCH] Modified mbox.c only mark files as infected with heuristic - alerts if heuristic alerts are enabled. - ---- - libclamav/mbox.c | 52 ++++++++++++++++++++++-------------------------- - 1 file changed, 24 insertions(+), 28 deletions(-) - -diff --git a/libclamav/mbox.c b/libclamav/mbox.c -index 684f0d7e34..fc63245255 100644 ---- a/libclamav/mbox.c -+++ b/libclamav/mbox.c -@@ -232,11 +232,11 @@ static blob *getHrefs(message *m, tag_arguments_t *hrefs); - static void hrefs_done(blob *b, tag_arguments_t *hrefs); - static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html); - --static bool haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx); --static bool hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx); --static bool haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx); --static bool haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx); --static bool haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx); -+static bool haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx, mbox_status * rc); -+static bool hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx, bool * heuristicFound); -+static bool haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx, bool * heuristicFound); -+static bool haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx, bool * heuristicFound); -+static bool haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx, bool * heuristicFound); - - /* Maximum line length according to RFC2821 */ - #define RFC2821LENGTH 1000 -@@ -769,7 +769,7 @@ doContinueMultipleEmptyOptions(const char *const line, bool *lastWasOnlySemi) - } - - static bool --hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx) -+hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx, bool * heuristicFound) - { - - if (line) { -@@ -782,6 +782,7 @@ hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx) - if ((*lineFoldCnt) >= HEURISTIC_EMAIL_MAX_LINE_FOLDS_PER_HEADER) { - if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { - cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxLineFoldCnt"); -+ *heuristicFound = TRUE; - } - - return TRUE; -@@ -791,12 +792,13 @@ hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx) - } - - static bool --haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx) -+haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx, bool * heuristicFound) - { - - if (totalLen > HEURISTIC_EMAIL_MAX_HEADER_BYTES) { - if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { - cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxHeaderBytes"); -+ *heuristicFound = TRUE; - } - - return TRUE; -@@ -805,12 +807,13 @@ haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx) - } - - static bool --haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx) -+haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx, bool * heuristicFound) - { - - if (totalHeaderCnt > HEURISTIC_EMAIL_MAX_HEADERS) { - if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { - cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxEmailHeaders"); -+ *heuristicFound = TRUE; - } - - return TRUE; -@@ -819,12 +822,13 @@ haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx) - } - - static bool --haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx) -+haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx, mbox_status * rc) - { - - if (mimePartCnt >= HEURISTIC_EMAIL_MAX_MIME_PARTS_PER_MESSAGE) { - if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { - cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxMIMEPartsPerMessage"); -+ *rc = VIRUS; - } - - return TRUE; -@@ -833,12 +837,13 @@ haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx) - } - - static bool --haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx) -+haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx, bool * heuristicFound) - { - - if (argCnt >= HEURISTIC_EMAIL_MAX_ARGUMENTS_PER_HEADER) { - if (ctx->options->general & CL_SCAN_GENERAL_HEURISTICS) { - cli_append_virus(ctx, "Heuristics.Email.ExceedsMaxMIMEArguments"); -+ *heuristicFound = TRUE; - } - - return TRUE; -@@ -899,8 +904,7 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - continue; - } - -- if (hitLineFoldCnt(line, &lineFoldCnt, ctx)) { -- *heuristicFound = TRUE; -+ if (hitLineFoldCnt(line, &lineFoldCnt, ctx, heuristicFound )) { - break; - } - -@@ -947,8 +951,7 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - DO_VERIFY_POINTER(header); - - totalHeaderCnt++; -- if (haveTooManyEmailHeaders(totalHeaderCnt, ctx)) { -- *heuristicFound = TRUE; -+ if (haveTooManyEmailHeaders(totalHeaderCnt, ctx, heuristicFound)) { - break; - } - needContinue = (parseEmailHeader(ret, header, rfc821, ctx, heuristicFound) < 0); -@@ -1037,8 +1040,7 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - - if (lineAdded) { - totalHeaderBytes += strlen(line); -- if (haveTooManyHeaderBytes(totalHeaderBytes, ctx)) { -- *heuristicFound = TRUE; -+ if (haveTooManyHeaderBytes(totalHeaderBytes, ctx, heuristicFound)) { - break; - } - } -@@ -1069,8 +1071,7 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - - if (0 == needContinue) { - totalHeaderCnt++; -- if (haveTooManyEmailHeaders(totalHeaderCnt, ctx)) { -- *heuristicFound = TRUE; -+ if (haveTooManyEmailHeaders(totalHeaderCnt, ctx, heuristicFound)) { - break; - } - needContinue = (parseEmailHeader(ret, header, rfc821, ctx, heuristicFound) < 0); -@@ -1205,8 +1206,7 @@ parseEmailHeaders(message *m, const table_t *rfc821, bool *heuristicFound) - continue; - } - -- if (hitLineFoldCnt(line, &lineFoldCnt, m->ctx)) { -- *heuristicFound = TRUE; -+ if (hitLineFoldCnt(line, &lineFoldCnt, m->ctx, heuristicFound)) { - break; - } - -@@ -1283,8 +1283,7 @@ parseEmailHeaders(message *m, const table_t *rfc821, bool *heuristicFound) - } - - if (lineAdded) { -- if (haveTooManyHeaderBytes(fulllinelength, m->ctx)) { -- *heuristicFound = TRUE; -+ if (haveTooManyHeaderBytes(fulllinelength, m->ctx, heuristicFound)) { - break; - } - } -@@ -1306,8 +1305,7 @@ parseEmailHeaders(message *m, const table_t *rfc821, bool *heuristicFound) - } - - totalHeaderCnt++; -- if (haveTooManyEmailHeaders(totalHeaderCnt, m->ctx)) { -- *heuristicFound = TRUE; -+ if (haveTooManyEmailHeaders(totalHeaderCnt, m->ctx, heuristicFound)) { - break; - } - if (parseEmailHeader(ret, fullline, rfc821, m->ctx, heuristicFound) < 0) { -@@ -2209,9 +2207,8 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - - free((char *)boundary); - -- if (haveTooManyMIMEPartsPerMessage(multiparts, mctx->ctx)) { -+ if (haveTooManyMIMEPartsPerMessage(multiparts, mctx->ctx, &rc)) { - DO_FREE(messages); -- rc = VIRUS; - break; - } - -@@ -3290,8 +3287,7 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c - cli_dbgmsg("mimeArgs = '%s'\n", buf); - - argCnt++; -- if (haveTooManyMIMEArguments(argCnt, ctx)) { -- *heuristicFound = TRUE; -+ if (haveTooManyMIMEArguments(argCnt, ctx, heuristicFound )) { - break; - } - messageAddArguments(m, buf); diff --git a/CVE-2019-15961-pre-1.patch b/CVE-2019-15961-pre-1.patch deleted file mode 100644 index 7e8a54d894cbd1445fb98f64a78c054fa679cd94..0000000000000000000000000000000000000000 --- a/CVE-2019-15961-pre-1.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 4619f636cb3a2df8162a3677b6c2918868a953da Mon Sep 17 00:00:00 2001 -From: Micah Snyder -Date: Thu, 31 Oct 2019 16:05:29 -0400 -Subject: [PATCH] Fixes null-dereference in mail message parser. - ---- - libclamav/mbox.c | 16 ++++++++-------- - libclamav/message.c | 4 +++- - 2 files changed, 11 insertions(+), 9 deletions(-) - -diff --git a/libclamav/mbox.c b/libclamav/mbox.c -index d9746f1e95..7fee0cab4c 100644 ---- a/libclamav/mbox.c -+++ b/libclamav/mbox.c -@@ -3,7 +3,7 @@ - * Copyright (C) 2007-2013 Sourcefire, Inc. - * - * Authors: Nigel Horne -- * -+ * - * Acknowledgements: Some ideas came from Stephen White , - * Michael Dankov , Gianluigi Tiesi , - * Everton da Silva Marques, Thomas Lamy , -@@ -586,7 +586,7 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - */ - messageDestroy(body); - } -- -+ - if((retcode == CL_CLEAN) && ctx->found_possibly_unwanted && - (*ctx->virname == NULL || SCAN_ALLMATCHES)) { - retcode = cli_append_virus(ctx, "Heuristics.Phishing.Email"); -@@ -1840,8 +1840,8 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * must be listed here */ - break; - default: -- /* this is a subtype that we -- * don't handle anyway, -+ /* this is a subtype that we -+ * don't handle anyway, - * don't store */ - if(messages[multiparts]) { - messageDestroy(messages[multiparts]); -@@ -3617,7 +3617,7 @@ getline_from_mbox(char *buffer, size_t buffer_len, fmap_t *map, size_t *at) - src = cursrc = fmap_need_off_once(map, *at, input_len); - - /* we check for eof from the result of GETC() -- * if(feof(fin)) -+ * if(feof(fin)) - return NULL;*/ - if(!src) { - cli_dbgmsg("getline_from_mbox: fmap need failed\n"); -@@ -3629,7 +3629,7 @@ getline_from_mbox(char *buffer, size_t buffer_len, fmap_t *map, size_t *at) - } - - curbuf = buffer; -- -+ - for(i=0; ijobj == NULL) - m->jobj = cli_jsonobj(NULL, NULL); diff --git a/CVE-2019-15961-pre-2.patch b/CVE-2019-15961-pre-2.patch deleted file mode 100644 index 8f994411dc091d6384fb22742221b2a53ed19612..0000000000000000000000000000000000000000 --- a/CVE-2019-15961-pre-2.patch +++ /dev/null @@ -1,6750 +0,0 @@ -From fd62a171b65353c038cd93455c6493c78acebe26 Mon Sep 17 00:00:00 2001 -From: Andy Ragusa -Date: Tue, 19 Nov 2019 07:33:18 -0800 -Subject: [PATCH] clang-formatted mbox.c - ---- - libclamav/mbox.c | 5547 +++++++++++++++++++++++----------------------- - 1 file changed, 2762 insertions(+), 2785 deletions(-) - -diff --git a/libclamav/mbox.c b/libclamav/mbox.c -index 7fee0cab4c..d4afa78cd2 100644 ---- a/libclamav/mbox.c -+++ b/libclamav/mbox.c -@@ -29,8 +29,8 @@ - #endif - - #ifdef CL_THREAD_SAFE --#ifndef _REENTRANT --#define _REENTRANT /* for Solaris 2.8 */ -+#ifndef _REENTRANT -+#define _REENTRANT /* for Solaris 2.8 */ - #endif - #endif - -@@ -39,23 +39,23 @@ - #include - #include - #include --#ifdef HAVE_STRINGS_H -+#ifdef HAVE_STRINGS_H - #include - #endif --#ifdef HAVE_STRING_H -+#ifdef HAVE_STRING_H - #include - #endif - #include - #include - #include --#ifdef HAVE_SYS_PARAM_H -+#ifdef HAVE_SYS_PARAM_H - #include - #endif - #include - #include - #include - --#ifdef HAVE_UNISTD_H -+#ifdef HAVE_UNISTD_H - #include - #endif - -@@ -63,7 +63,7 @@ - #include - #endif - --#ifdef CL_THREAD_SAFE -+#ifdef CL_THREAD_SAFE - #include - #endif - -@@ -86,9 +86,9 @@ - - #define DCONF_PHISHING mctx->ctx->dconf->phishing - --#ifdef CL_DEBUG -+#ifdef CL_DEBUG - --#if defined(C_LINUX) -+#if defined(C_LINUX) - #include - #endif - -@@ -101,52 +101,53 @@ - #include - #include - --static void sigsegv(int sig); --static void print_trace(int use_syslog); -+static void sigsegv(int sig); -+static void print_trace(int use_syslog); - --/*#define SAVE_TMP */ /* Save the file being worked on in tmp */ -+/*#define SAVE_TMP */ /* Save the file being worked on in tmp */ - #endif - --#if defined(NO_STRTOK_R) || !defined(CL_THREAD_SAFE) -+#if defined(NO_STRTOK_R) || !defined(CL_THREAD_SAFE) - #undef strtok_r - #undef __strtok_r --#define strtok_r(a,b,c) strtok(a,b) -+#define strtok_r(a, b, c) strtok(a, b) - #endif - --#ifdef HAVE_STDBOOL_H --#ifdef C_BEOS -+#ifdef HAVE_STDBOOL_H -+#ifdef C_BEOS - #include "SupportDefs.h" - #else - #include - #endif - #else --#ifdef FALSE --typedef unsigned char bool; -+#ifdef FALSE -+typedef unsigned char bool; - #else --typedef enum { FALSE = 0, TRUE = 1 } bool; -+typedef enum { FALSE = 0, -+ TRUE = 1 } bool; - #endif - #endif - --typedef enum { -- FAIL, -- OK, -- OK_ATTACHMENTS_NOT_SAVED, -- VIRUS, -- MAXREC, -- MAXFILES -+typedef enum { -+ FAIL, -+ OK, -+ OK_ATTACHMENTS_NOT_SAVED, -+ VIRUS, -+ MAXREC, -+ MAXFILES - } mbox_status; - - #ifndef isblank --#define isblank(c) (((c) == ' ') || ((c) == '\t')) -+#define isblank(c) (((c) == ' ') || ((c) == '\t')) - #endif - --#define SAVE_TO_DISC /* multipart/message are saved in a temporary file */ -+#define SAVE_TO_DISC /* multipart/message are saved in a temporary file */ - - #include "htmlnorm.h" - - #include "phishcheck.h" - --#ifndef _WIN32 -+#ifndef _WIN32 - #include - #include - #include -@@ -170,19 +171,19 @@ typedef enum { - */ - /*#define NEW_WORLD*/ - --/*#define SCAN_UNENCODED_BOUNCES *//* -+/*#define SCAN_UNENCODED_BOUNCES */ /* - * Slows things down a lot and only catches unencoded copies - * of EICAR within bounces, which don't matter - */ - --typedef struct mbox_ctx { -- const char *dir; -- const table_t *rfc821Table; -- const table_t *subtypeTable; -- cli_ctx *ctx; -- unsigned int files; /* number of files extracted */ -+typedef struct mbox_ctx { -+ const char *dir; -+ const table_t *rfc821Table; -+ const table_t *subtypeTable; -+ cli_ctx *ctx; -+ unsigned int files; /* number of files extracted */ - #if HAVE_JSON -- json_object *wrkobj; -+ json_object *wrkobj; - #endif - } mbox_ctx; - -@@ -199,157 +200,134 @@ typedef struct mbox_ctx { - #define UNLOCKFILE(fp) - #endif - --static int cli_parse_mbox(const char *dir, cli_ctx *ctx); --static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir); --static message *parseEmailHeaders(message *m, const table_t *rfc821Table); --static int parseEmailHeader(message *m, const char *line, const table_t *rfc821Table); --static int parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata); --static mbox_status parseRootMHTML(mbox_ctx *mctx, message *m, text *t); --static mbox_status parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level); --static int boundaryStart(const char *line, const char *boundary); --static int boundaryEnd(const char *line, const char *boundary); --static int initialiseTables(table_t **rfc821Table, table_t **subtypeTable); --static int getTextPart(message *const messages[], size_t size); --static size_t strip(char *buf, int len); --static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg); --static int saveTextPart(mbox_ctx *mctx, message *m, int destroy_text); --static char *rfc2047(const char *in); --static char *rfc822comments(const char *in, char *out); --static int rfc1341(message *m, const char *dir); --static bool usefulHeader(int commandNumber, const char *cmd); --static char *getline_from_mbox(char *buffer, size_t len, fmap_t *map, size_t *at); --static bool isBounceStart(mbox_ctx *mctx, const char *line); --static bool exportBinhexMessage(mbox_ctx *mctx, message *m); --static int exportBounceMessage(mbox_ctx *ctx, text *start); --static const char *getMimeTypeStr(mime_type mimetype); --static const char *getEncTypeStr(encoding_type enctype); --static message *do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, mbox_ctx *mctx, message *messageIn, text **tptr, unsigned int recursion_level); --static int count_quotes(const char *buf); --static bool next_is_folded_header(const text *t); --static bool newline_in_header(const char *line); -- --static blob *getHrefs(message *m, tag_arguments_t *hrefs); --static void hrefs_done(blob *b, tag_arguments_t *hrefs); --static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html); -+static int cli_parse_mbox(const char *dir, cli_ctx *ctx); -+static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir); -+static message *parseEmailHeaders(message *m, const table_t *rfc821Table); -+static int parseEmailHeader(message *m, const char *line, const table_t *rfc821Table); -+static int parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata); -+static mbox_status parseRootMHTML(mbox_ctx *mctx, message *m, text *t); -+static mbox_status parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level); -+static int boundaryStart(const char *line, const char *boundary); -+static int boundaryEnd(const char *line, const char *boundary); -+static int initialiseTables(table_t **rfc821Table, table_t **subtypeTable); -+static int getTextPart(message *const messages[], size_t size); -+static size_t strip(char *buf, int len); -+static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg); -+static int saveTextPart(mbox_ctx *mctx, message *m, int destroy_text); -+static char *rfc2047(const char *in); -+static char *rfc822comments(const char *in, char *out); -+static int rfc1341(message *m, const char *dir); -+static bool usefulHeader(int commandNumber, const char *cmd); -+static char *getline_from_mbox(char *buffer, size_t len, fmap_t *map, size_t *at); -+static bool isBounceStart(mbox_ctx *mctx, const char *line); -+static bool exportBinhexMessage(mbox_ctx *mctx, message *m); -+static int exportBounceMessage(mbox_ctx *ctx, text *start); -+static const char *getMimeTypeStr(mime_type mimetype); -+static const char *getEncTypeStr(encoding_type enctype); -+static message *do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, mbox_ctx *mctx, message *messageIn, text **tptr, unsigned int recursion_level); -+static int count_quotes(const char *buf); -+static bool next_is_folded_header(const text *t); -+static bool newline_in_header(const char *line); -+ -+static blob *getHrefs(message *m, tag_arguments_t *hrefs); -+static void hrefs_done(blob *b, tag_arguments_t *hrefs); -+static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html); - - /* Maximum line length according to RFC2821 */ --#define RFC2821LENGTH 1000 -+#define RFC2821LENGTH 1000 - - /* Hashcodes for our hash tables */ --#define CONTENT_TYPE 1 --#define CONTENT_TRANSFER_ENCODING 2 --#define CONTENT_DISPOSITION 3 -+#define CONTENT_TYPE 1 -+#define CONTENT_TRANSFER_ENCODING 2 -+#define CONTENT_DISPOSITION 3 - - /* Mime sub types */ --#define PLAIN 1 --#define ENRICHED 2 --#define HTML 3 --#define RICHTEXT 4 --#define MIXED 5 --#define ALTERNATIVE 6 /* RFC1521*/ --#define DIGEST 7 --#define SIGNED 8 --#define PARALLEL 9 --#define RELATED 10 /* RFC2387 */ --#define REPORT 11 /* RFC1892 */ --#define APPLEDOUBLE 12 /* Handling of this in only noddy for now */ --#define FAX MIXED /* -- * RFC3458 -- * Drafts stated to treat is as mixed if it is -- * not known. This disappeared in the final -- * version (except when talking about -- * voice-message), but it is good enough for us -- * since we do no validation of coversheet -- * presence etc. (which also has disappeared -- * in the final version) -- */ --#define ENCRYPTED 13 /* -- * e.g. RFC2015 -- * Content-Type: multipart/encrypted; -- * boundary="nextPart1383049.XCRrrar2yq"; -- * protocol="application/pgp-encrypted" -- */ --#define X_BFILE RELATED /* -- * BeOS, expert two parts: the file and it's -- * attributes. The attributes part comes as -- * Content-Type: application/x-be_attribute -- * name="foo" -- * I can't find where it is defined, any -- * pointers would be appreciated. For now -- * we treat it as multipart/related -- */ --#define KNOWBOT 14 /* Unknown and undocumented format? */ -- --static const struct tableinit { -- const char *key; -- int value; -+#define PLAIN 1 -+#define ENRICHED 2 -+#define HTML 3 -+#define RICHTEXT 4 -+#define MIXED 5 -+#define ALTERNATIVE 6 /* RFC1521*/ -+#define DIGEST 7 -+#define SIGNED 8 -+#define PARALLEL 9 -+#define RELATED 10 /* RFC2387 */ -+#define REPORT 11 /* RFC1892 */ -+#define APPLEDOUBLE 12 /* Handling of this in only noddy for now */ -+#define FAX MIXED /* \ -+ * RFC3458 \ -+ * Drafts stated to treat is as mixed if it is \ -+ * not known. This disappeared in the final \ -+ * version (except when talking about \ -+ * voice-message), but it is good enough for us \ -+ * since we do no validation of coversheet \ -+ * presence etc. (which also has disappeared \ -+ * in the final version) \ -+ */ -+#define ENCRYPTED 13 /* \ -+ * e.g. RFC2015 \ -+ * Content-Type: multipart/encrypted; \ -+ * boundary="nextPart1383049.XCRrrar2yq"; \ -+ * protocol="application/pgp-encrypted" \ -+ */ -+#define X_BFILE RELATED /* \ -+ * BeOS, expert two parts: the file and it's \ -+ * attributes. The attributes part comes as \ -+ * Content-Type: application/x-be_attribute \ -+ * name="foo" \ -+ * I can't find where it is defined, any \ -+ * pointers would be appreciated. For now \ -+ * we treat it as multipart/related \ -+ */ -+#define KNOWBOT 14 /* Unknown and undocumented format? */ -+ -+static const struct tableinit { -+ const char *key; -+ int value; - } rfc821headers[] = { -- /* TODO: make these regular expressions */ -- { "Content-Type", CONTENT_TYPE }, -- { "Content-Transfer-Encoding", CONTENT_TRANSFER_ENCODING }, -- { "Content-Disposition", CONTENT_DISPOSITION }, -- { NULL, 0 } --}, mimeSubtypes[] = { /* see RFC2045 */ -- /* subtypes of Text */ -- { "plain", PLAIN }, -- { "enriched", ENRICHED }, -- { "html", HTML }, -- { "richtext", RICHTEXT }, -- /* subtypes of Multipart */ -- { "mixed", MIXED }, -- { "alternative", ALTERNATIVE }, -- { "digest", DIGEST }, -- { "signed", SIGNED }, -- { "parallel", PARALLEL }, -- { "related", RELATED }, -- { "report", REPORT }, -- { "appledouble", APPLEDOUBLE }, -- { "fax-message", FAX }, -- { "encrypted", ENCRYPTED }, -- { "x-bfile", X_BFILE }, /* BeOS */ -- { "knowbot", KNOWBOT }, /* ??? */ -- { "knowbot-metadata", KNOWBOT }, /* ??? */ -- { "knowbot-code", KNOWBOT }, /* ??? */ -- { "knowbot-state", KNOWBOT }, /* ??? */ -- { NULL, 0 } --}, mimeTypeStr[] = { -- { "NOMIME", NOMIME }, -- { "APPLICATION", APPLICATION }, -- { "AUDIO", AUDIO }, -- { "IMAGE", IMAGE }, -- { "MESSAGE", MESSAGE }, -- { "MULTIPART", MULTIPART }, -- { "TEXT", TEXT }, -- { "VIDEO", VIDEO }, -- { "MEXTENSION", MEXTENSION }, -- { NULL, 0 } --}, encTypeStr[] = { -- { "NOENCODING", NOENCODING }, -- { "QUOTEDPRINTABLE", QUOTEDPRINTABLE }, -- { "BASE64", BASE64 }, -- { "EIGHTBIT", EIGHTBIT }, -- { "BINARY", BINARY }, -- { "UUENCODE", UUENCODE }, -- { "YENCODE", YENCODE }, -- { "EEXTENSION", EEXTENSION }, -- { "BINHEX", BINHEX }, -- { NULL, 0 } --}; -- --#ifdef CL_THREAD_SAFE --static pthread_mutex_t tables_mutex = PTHREAD_MUTEX_INITIALIZER; -+ /* TODO: make these regular expressions */ -+ {"Content-Type", CONTENT_TYPE}, -+ {"Content-Transfer-Encoding", CONTENT_TRANSFER_ENCODING}, -+ {"Content-Disposition", CONTENT_DISPOSITION}, -+ {NULL, 0}}, -+ mimeSubtypes[] = {/* see RFC2045 */ -+ /* subtypes of Text */ -+ {"plain", PLAIN}, -+ {"enriched", ENRICHED}, -+ {"html", HTML}, -+ {"richtext", RICHTEXT}, -+ /* subtypes of Multipart */ -+ {"mixed", MIXED}, -+ {"alternative", ALTERNATIVE}, -+ {"digest", DIGEST}, -+ {"signed", SIGNED}, -+ {"parallel", PARALLEL}, -+ {"related", RELATED}, -+ {"report", REPORT}, -+ {"appledouble", APPLEDOUBLE}, -+ {"fax-message", FAX}, -+ {"encrypted", ENCRYPTED}, -+ {"x-bfile", X_BFILE}, /* BeOS */ -+ {"knowbot", KNOWBOT}, /* ??? */ -+ {"knowbot-metadata", KNOWBOT}, /* ??? */ -+ {"knowbot-code", KNOWBOT}, /* ??? */ -+ {"knowbot-state", KNOWBOT}, /* ??? */ -+ {NULL, 0}}, -+ mimeTypeStr[] = {{"NOMIME", NOMIME}, {"APPLICATION", APPLICATION}, {"AUDIO", AUDIO}, {"IMAGE", IMAGE}, {"MESSAGE", MESSAGE}, {"MULTIPART", MULTIPART}, {"TEXT", TEXT}, {"VIDEO", VIDEO}, {"MEXTENSION", MEXTENSION}, {NULL, 0}}, encTypeStr[] = {{"NOENCODING", NOENCODING}, {"QUOTEDPRINTABLE", QUOTEDPRINTABLE}, {"BASE64", BASE64}, {"EIGHTBIT", EIGHTBIT}, {"BINARY", BINARY}, {"UUENCODE", UUENCODE}, {"YENCODE", YENCODE}, {"EEXTENSION", EEXTENSION}, {"BINHEX", BINHEX}, {NULL, 0}}; -+ -+#ifdef CL_THREAD_SAFE -+static pthread_mutex_t tables_mutex = PTHREAD_MUTEX_INITIALIZER; - #endif --static table_t *rfc821 = NULL; --static table_t *subtype = NULL; -+static table_t *rfc821 = NULL; -+static table_t *subtype = NULL; - --int --cli_mbox(const char *dir, cli_ctx *ctx) -+int cli_mbox(const char *dir, cli_ctx *ctx) - { -- if(dir == NULL) { -- cli_dbgmsg("cli_mbox called with NULL dir\n"); -- return CL_ENULLARG; -- } -- return cli_parse_mbox(dir, ctx); -+ if (dir == NULL) { -+ cli_dbgmsg("cli_mbox called with NULL dir\n"); -+ return CL_ENULLARG; -+ } -+ return cli_parse_mbox(dir, ctx); - } - - /* -@@ -370,51 +348,51 @@ cli_mbox(const char *dir, cli_ctx *ctx) - static int - cli_parse_mbox(const char *dir, cli_ctx *ctx) - { -- int retcode; -- message *body; -- char buffer[RFC2821LENGTH + 1]; -- mbox_ctx mctx; -- size_t at = 0; -- fmap_t *map = *ctx->fmap; -- -- cli_dbgmsg("in mbox()\n"); -- -- if(!fmap_gets(map, buffer, &at, sizeof(buffer) - 1)) { -- /* empty message */ -- return CL_CLEAN; -- } --#ifdef CL_THREAD_SAFE -- pthread_mutex_lock(&tables_mutex); -+ int retcode; -+ message *body; -+ char buffer[RFC2821LENGTH + 1]; -+ mbox_ctx mctx; -+ size_t at = 0; -+ fmap_t *map = *ctx->fmap; -+ -+ cli_dbgmsg("in mbox()\n"); -+ -+ if (!fmap_gets(map, buffer, &at, sizeof(buffer) - 1)) { -+ /* empty message */ -+ return CL_CLEAN; -+ } -+#ifdef CL_THREAD_SAFE -+ pthread_mutex_lock(&tables_mutex); - #endif -- if(rfc821 == NULL) { -- assert(subtype == NULL); -- -- if(initialiseTables(&rfc821, &subtype) < 0) { -- rfc821 = NULL; -- subtype = NULL; --#ifdef CL_THREAD_SAFE -- pthread_mutex_unlock(&tables_mutex); -+ if (rfc821 == NULL) { -+ assert(subtype == NULL); -+ -+ if (initialiseTables(&rfc821, &subtype) < 0) { -+ rfc821 = NULL; -+ subtype = NULL; -+#ifdef CL_THREAD_SAFE -+ pthread_mutex_unlock(&tables_mutex); - #endif -- return CL_EMEM; -- } -- } --#ifdef CL_THREAD_SAFE -- pthread_mutex_unlock(&tables_mutex); -+ return CL_EMEM; -+ } -+ } -+#ifdef CL_THREAD_SAFE -+ pthread_mutex_unlock(&tables_mutex); - #endif - -- retcode = CL_SUCCESS; -- body = NULL; -+ retcode = CL_SUCCESS; -+ body = NULL; - -- mctx.dir = dir; -- mctx.rfc821Table = rfc821; -- mctx.subtypeTable = subtype; -- mctx.ctx = ctx; -- mctx.files = 0; -+ mctx.dir = dir; -+ mctx.rfc821Table = rfc821; -+ mctx.subtypeTable = subtype; -+ mctx.ctx = ctx; -+ mctx.files = 0; - #if HAVE_JSON -- mctx.wrkobj = ctx->wrkproperty; -+ mctx.wrkobj = ctx->wrkproperty; - #endif - -- /* -+ /* - * Is it a UNIX style mbox with more than one - * mail message, or just a single mail message? - * -@@ -423,9 +401,9 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - * than one message is handled, e.g. giving a better indication of - * which message within the mailbox is infected - */ -- /*if((strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ -- if(strncmp(buffer, "From ", 5) == 0) { -- /* -+ /*if((strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ -+ if (strncmp(buffer, "From ", 5) == 0) { -+ /* - * Have been asked to check a UNIX style mbox file, which - * may contain more than one e-mail message to decode - * -@@ -443,47 +421,47 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - * This would remove a problem with this code that it can - * fill up the tmp directory before it starts scanning - */ -- bool lastLineWasEmpty; -- int messagenumber; -- message *m = messageCreate(); -- -- if(m == NULL) -- return CL_EMEM; -- -- lastLineWasEmpty = FALSE; -- messagenumber = 1; -- messageSetCTX(m, ctx); -- -- do { -- cli_chomp(buffer); -- /*if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ -- if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0)) { -- cli_dbgmsg("Deal with message number %d\n", messagenumber++); -- /* -+ bool lastLineWasEmpty; -+ int messagenumber; -+ message *m = messageCreate(); -+ -+ if (m == NULL) -+ return CL_EMEM; -+ -+ lastLineWasEmpty = FALSE; -+ messagenumber = 1; -+ messageSetCTX(m, ctx); -+ -+ do { -+ cli_chomp(buffer); -+ /*if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ -+ if (lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0)) { -+ cli_dbgmsg("Deal with message number %d\n", messagenumber++); -+ /* - * End of a message in the mail box - */ -- body = parseEmailHeaders(m, rfc821); -- if(body == NULL) { -- messageReset(m); -- continue; -- } -- messageSetCTX(body, ctx); -- messageDestroy(m); -- if(messageGetBody(body)) { -- mbox_status rc = parseEmailBody(body, NULL, &mctx, 0); -- if(rc == FAIL) { -- messageReset(body); -- m = body; -- continue; -- } else if(rc == VIRUS) { -- cli_dbgmsg("Message number %d is infected\n", -- messagenumber-1); -- retcode = CL_VIRUS; -- m = NULL; -- break; -- } -- } -- /* -+ body = parseEmailHeaders(m, rfc821); -+ if (body == NULL) { -+ messageReset(m); -+ continue; -+ } -+ messageSetCTX(body, ctx); -+ messageDestroy(m); -+ if (messageGetBody(body)) { -+ mbox_status rc = parseEmailBody(body, NULL, &mctx, 0); -+ if (rc == FAIL) { -+ messageReset(body); -+ m = body; -+ continue; -+ } else if (rc == VIRUS) { -+ cli_dbgmsg("Message number %d is infected\n", -+ messagenumber - 1); -+ retcode = CL_VIRUS; -+ m = NULL; -+ break; -+ } -+ } -+ /* - * Starting a new message, throw away all the - * information about the old one. It would - * be best to be able to scan this message -@@ -491,72 +469,72 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - * that haven't been passed here so it can't be - * called - */ -- m = body; -- messageReset(body); -- messageSetCTX(body, ctx); -+ m = body; -+ messageReset(body); -+ messageSetCTX(body, ctx); - -- cli_dbgmsg("Finished processing message\n"); -- } else -- lastLineWasEmpty = (bool)(buffer[0] == '\0'); -+ cli_dbgmsg("Finished processing message\n"); -+ } else -+ lastLineWasEmpty = (bool)(buffer[0] == '\0'); - -- if(isuuencodebegin(buffer)) { -- /* -+ if (isuuencodebegin(buffer)) { -+ /* - * Fast track visa to uudecode. - * TODO: binhex, yenc - */ -- if(uudecodeFile(m, buffer, dir, map, &at) < 0) -- if(messageAddStr(m, buffer) < 0) -- break; -- } else -- /* at this point, the \n has been removed */ -- if(messageAddStr(m, buffer) < 0) -- break; -- } while(fmap_gets(map, buffer, &at, sizeof(buffer) - 1)); -- -- if(retcode == CL_SUCCESS) { -- cli_dbgmsg("Extract attachments from email %d\n", messagenumber); -- body = parseEmailHeaders(m, rfc821); -- } -- if(m) -- messageDestroy(m); -- } else { -- /* -+ if (uudecodeFile(m, buffer, dir, map, &at) < 0) -+ if (messageAddStr(m, buffer) < 0) -+ break; -+ } else -+ /* at this point, the \n has been removed */ -+ if (messageAddStr(m, buffer) < 0) -+ break; -+ } while (fmap_gets(map, buffer, &at, sizeof(buffer) - 1)); -+ -+ if (retcode == CL_SUCCESS) { -+ cli_dbgmsg("Extract attachments from email %d\n", messagenumber); -+ body = parseEmailHeaders(m, rfc821); -+ } -+ if (m) -+ messageDestroy(m); -+ } else { -+ /* - * It's a single message, parse the headers then the body - */ -- if(strncmp(buffer, "P I ", 4) == 0) -- /* -+ if (strncmp(buffer, "P I ", 4) == 0) -+ /* - * CommuniGate Pro format: ignore headers until - * blank line - */ -- while(fmap_gets(map, buffer, &at, sizeof(buffer) - 1) && -- (strchr("\r\n", buffer[0]) == NULL)) -- ; -- /* getline_from_mbox could be using unlocked_stdio(3), -+ while (fmap_gets(map, buffer, &at, sizeof(buffer) - 1) && -+ (strchr("\r\n", buffer[0]) == NULL)) -+ ; -+ /* getline_from_mbox could be using unlocked_stdio(3), - * so lock file here */ -- /* -+ /* - * Ignore any blank lines at the top of the message - */ -- while(strchr("\r\n", buffer[0]) && -- (getline_from_mbox(buffer, sizeof(buffer) - 1, map, &at) != NULL)) -- ; -+ while (strchr("\r\n", buffer[0]) && -+ (getline_from_mbox(buffer, sizeof(buffer) - 1, map, &at) != NULL)) -+ ; - -- buffer[sizeof(buffer) - 1] = '\0'; -+ buffer[sizeof(buffer) - 1] = '\0'; - -- body = parseEmailFile(map, &at, rfc821, buffer, dir); -- } -+ body = parseEmailFile(map, &at, rfc821, buffer, dir); -+ } - -- if(body) { -- /* -+ if (body) { -+ /* - * Write out the last entry in the mailbox - */ -- if((retcode == CL_SUCCESS) && messageGetBody(body)) { -- messageSetCTX(body, ctx); -- switch(parseEmailBody(body, NULL, &mctx, 0)) { -- case OK: -- case OK_ATTACHMENTS_NOT_SAVED: -- break; -- case FAIL: -- /* -+ if ((retcode == CL_SUCCESS) && messageGetBody(body)) { -+ messageSetCTX(body, ctx); -+ switch (parseEmailBody(body, NULL, &mctx, 0)) { -+ case OK: -+ case OK_ATTACHMENTS_NOT_SAVED: -+ break; -+ case FAIL: -+ /* - * beware: cli_magic_scandesc(), - * changes this into CL_CLEAN, so only - * use it to inform the higher levels -@@ -565,37 +543,37 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - * decoding errors on what *is* a valid - * mbox - */ -- retcode = CL_EFORMAT; -- break; -- case MAXREC: -- retcode = CL_EMAXREC; -- break; -- case MAXFILES: -- retcode = CL_EMAXFILES; -- break; -- case VIRUS: -- retcode = CL_VIRUS; -- break; -- } -- } -- -- if(body->isTruncated && retcode == CL_SUCCESS) -- retcode = CL_EMEM; -- /* -+ retcode = CL_EFORMAT; -+ break; -+ case MAXREC: -+ retcode = CL_EMAXREC; -+ break; -+ case MAXFILES: -+ retcode = CL_EMAXFILES; -+ break; -+ case VIRUS: -+ retcode = CL_VIRUS; -+ break; -+ } -+ } -+ -+ if (body->isTruncated && retcode == CL_SUCCESS) -+ retcode = CL_EMEM; -+ /* - * Tidy up and quit - */ -- messageDestroy(body); -- } -+ messageDestroy(body); -+ } - -- if((retcode == CL_CLEAN) && ctx->found_possibly_unwanted && -- (*ctx->virname == NULL || SCAN_ALLMATCHES)) { -- retcode = cli_append_virus(ctx, "Heuristics.Phishing.Email"); -- ctx->found_possibly_unwanted = 0; -- } -+ if ((retcode == CL_CLEAN) && ctx->found_possibly_unwanted && -+ (*ctx->virname == NULL || SCAN_ALLMATCHES)) { -+ retcode = cli_append_virus(ctx, "Heuristics.Phishing.Email"); -+ ctx->found_possibly_unwanted = 0; -+ } - -- cli_dbgmsg("cli_mbox returning %d\n", retcode); -+ cli_dbgmsg("cli_mbox returning %d\n", retcode); - -- return retcode; -+ return retcode; - } - - /* -@@ -607,58 +585,58 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) - static message * - parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *firstLine, const char *dir) - { -- bool inHeader = TRUE; -- bool bodyIsEmpty = TRUE; -- bool lastWasBlank = FALSE, lastBodyLineWasBlank = FALSE; -- message *ret; -- bool anyHeadersFound = FALSE; -- int commandNumber = -1; -- char *fullline = NULL, *boundary = NULL; -- size_t fulllinelength = 0; -- char buffer[RFC2821LENGTH + 1]; -- -- cli_dbgmsg("parseEmailFile\n"); -- -- ret = messageCreate(); -- if(ret == NULL) -- return NULL; -- -- strncpy(buffer, firstLine, sizeof(buffer)-1); -- do { -- const char *line; -- -- (void)cli_chomp(buffer); -- -- if(buffer[0] == '\0') -- line = NULL; -- else -- line = buffer; -- -- /* -+ bool inHeader = TRUE; -+ bool bodyIsEmpty = TRUE; -+ bool lastWasBlank = FALSE, lastBodyLineWasBlank = FALSE; -+ message *ret; -+ bool anyHeadersFound = FALSE; -+ int commandNumber = -1; -+ char *fullline = NULL, *boundary = NULL; -+ size_t fulllinelength = 0; -+ char buffer[RFC2821LENGTH + 1]; -+ -+ cli_dbgmsg("parseEmailFile\n"); -+ -+ ret = messageCreate(); -+ if (ret == NULL) -+ return NULL; -+ -+ strncpy(buffer, firstLine, sizeof(buffer) - 1); -+ do { -+ const char *line; -+ -+ (void)cli_chomp(buffer); -+ -+ if (buffer[0] == '\0') -+ line = NULL; -+ else -+ line = buffer; -+ -+ /* - * Don't blank lines which are only spaces from headers, - * otherwise they'll be treated as the end of header marker - */ -- if(lastWasBlank) { -- lastWasBlank = FALSE; -- if(boundaryStart(buffer, boundary)) { -- cli_dbgmsg("Found a header line with space that should be blank\n"); -- inHeader = FALSE; -- } -- } -- if(inHeader) { -- cli_dbgmsg("parseEmailFile: check '%s' fullline %p\n", -- buffer, fullline); -- /* -+ if (lastWasBlank) { -+ lastWasBlank = FALSE; -+ if (boundaryStart(buffer, boundary)) { -+ cli_dbgmsg("Found a header line with space that should be blank\n"); -+ inHeader = FALSE; -+ } -+ } -+ if (inHeader) { -+ cli_dbgmsg("parseEmailFile: check '%s' fullline %p\n", -+ buffer, fullline); -+ /* - * Ensure wide characters are handled where - * sizeof(char) > 1 - */ -- if(line && isspace(line[0] & 0xFF)) { -- char copy[sizeof(buffer)]; -+ if (line && isspace(line[0] & 0xFF)) { -+ char copy[sizeof(buffer)]; - -- strcpy(copy, buffer); -- strstrip(copy); -- if(copy[0] == '\0') { -- /* -+ strcpy(copy, buffer); -+ strstrip(copy); -+ if (copy[0] == '\0') { -+ /* - * The header line contains only white - * space. This is not the end of the - * headers according to RFC2822, but -@@ -671,189 +649,189 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - * content-type line. So we just have - * to make a best guess. Sigh. - */ -- if(fullline) { -- if(parseEmailHeader(ret, fullline, rfc821) < 0) -- continue; -- -- free(fullline); -- fullline = NULL; -- } -- if(boundary || -- ((boundary = (char *)messageFindArgument(ret, "boundary")) != NULL)) { -- lastWasBlank = TRUE; -- continue; -- } -- } -- } -- if((line == NULL) && (fullline == NULL)) { /* empty line */ -- /* -+ if (fullline) { -+ if (parseEmailHeader(ret, fullline, rfc821) < 0) -+ continue; -+ -+ free(fullline); -+ fullline = NULL; -+ } -+ if (boundary || -+ ((boundary = (char *)messageFindArgument(ret, "boundary")) != NULL)) { -+ lastWasBlank = TRUE; -+ continue; -+ } -+ } -+ } -+ if ((line == NULL) && (fullline == NULL)) { /* empty line */ -+ /* - * A blank line signifies the end of - * the header and the start of the text - */ -- if(!anyHeadersFound) -- /* Ignore the junk at the top */ -- continue; -+ if (!anyHeadersFound) -+ /* Ignore the junk at the top */ -+ continue; - -- cli_dbgmsg("End of header information\n"); -- inHeader = FALSE; -- bodyIsEmpty = TRUE; -- } else { -- char *ptr; -- const char *lookahead; -+ cli_dbgmsg("End of header information\n"); -+ inHeader = FALSE; -+ bodyIsEmpty = TRUE; -+ } else { -+ char *ptr; -+ const char *lookahead; - -- if(fullline == NULL) { -- char cmd[RFC2821LENGTH + 1], out[RFC2821LENGTH + 1]; -+ if (fullline == NULL) { -+ char cmd[RFC2821LENGTH + 1], out[RFC2821LENGTH + 1]; - -- /* -+ /* - * Continuation of line we're ignoring? - */ -- if(isblank(line[0])) -- continue; -+ if (isblank(line[0])) -+ continue; - -- /* -+ /* - * Is this a header we're interested in? - */ -- if((strchr(line, ':') == NULL) || -- (cli_strtokbuf(line, 0, ":", cmd) == NULL)) { -- if(strncmp(line, "From ", 5) == 0) -- anyHeadersFound = TRUE; -- continue; -- } -- -- ptr = rfc822comments(cmd, out); -- commandNumber = tableFind(rfc821, ptr ? ptr : cmd); -- -- switch(commandNumber) { -- case CONTENT_TRANSFER_ENCODING: -- case CONTENT_DISPOSITION: -- case CONTENT_TYPE: -- anyHeadersFound = TRUE; -- break; -- default: -- if(!anyHeadersFound) -- anyHeadersFound = usefulHeader(commandNumber, cmd); -- continue; -- } -- fullline = cli_strdup(line); -- fulllinelength = strlen(line) + 1; -- if(!fullline) { -- if(ret) -- ret->isTruncated = TRUE; -- break; -- } -- } else if(line != NULL) { -- fulllinelength += strlen(line) + 1; -- ptr = cli_realloc(fullline, fulllinelength); -- if(ptr == NULL) -- continue; -- fullline = ptr; -- cli_strlcat(fullline, line, fulllinelength); -- } -- -- assert(fullline != NULL); -- -- if((lookahead = fmap_need_off_once(map, *at, 1))) { -- /* -+ if ((strchr(line, ':') == NULL) || -+ (cli_strtokbuf(line, 0, ":", cmd) == NULL)) { -+ if (strncmp(line, "From ", 5) == 0) -+ anyHeadersFound = TRUE; -+ continue; -+ } -+ -+ ptr = rfc822comments(cmd, out); -+ commandNumber = tableFind(rfc821, ptr ? ptr : cmd); -+ -+ switch (commandNumber) { -+ case CONTENT_TRANSFER_ENCODING: -+ case CONTENT_DISPOSITION: -+ case CONTENT_TYPE: -+ anyHeadersFound = TRUE; -+ break; -+ default: -+ if (!anyHeadersFound) -+ anyHeadersFound = usefulHeader(commandNumber, cmd); -+ continue; -+ } -+ fullline = cli_strdup(line); -+ fulllinelength = strlen(line) + 1; -+ if (!fullline) { -+ if (ret) -+ ret->isTruncated = TRUE; -+ break; -+ } -+ } else if (line != NULL) { -+ fulllinelength += strlen(line) + 1; -+ ptr = cli_realloc(fullline, fulllinelength); -+ if (ptr == NULL) -+ continue; -+ fullline = ptr; -+ cli_strlcat(fullline, line, fulllinelength); -+ } -+ -+ assert(fullline != NULL); -+ -+ if ((lookahead = fmap_need_off_once(map, *at, 1))) { -+ /* - * Section B.2 of RFC822 says TAB or - * SPACE means a continuation of the - * previous entry. - * - * Add all the arguments on the line - */ -- if(isblank(*lookahead)) -- continue; -- } -+ if (isblank(*lookahead)) -+ continue; -+ } - -- /* -+ /* - * Handle broken headers, where the next - * line isn't indented by whitespace - */ -- if(fullline[strlen(fullline) - 1] == ';') -- /* Add arguments to this line */ -- continue; -- -- if(line && (count_quotes(fullline) & 1)) -- continue; -- -- ptr = rfc822comments(fullline, NULL); -- if(ptr) { -- free(fullline); -- fullline = ptr; -- } -- -- if(parseEmailHeader(ret, fullline, rfc821) < 0) -- continue; -- -- free(fullline); -- fullline = NULL; -- } -- } else if(line && isuuencodebegin(line)) { -- /* -+ if (fullline[strlen(fullline) - 1] == ';') -+ /* Add arguments to this line */ -+ continue; -+ -+ if (line && (count_quotes(fullline) & 1)) -+ continue; -+ -+ ptr = rfc822comments(fullline, NULL); -+ if (ptr) { -+ free(fullline); -+ fullline = ptr; -+ } -+ -+ if (parseEmailHeader(ret, fullline, rfc821) < 0) -+ continue; -+ -+ free(fullline); -+ fullline = NULL; -+ } -+ } else if (line && isuuencodebegin(line)) { -+ /* - * Fast track visa to uudecode. - * TODO: binhex, yenc - */ -- bodyIsEmpty = FALSE; -- if(uudecodeFile(ret, line, dir, map, at) < 0) -- if(messageAddStr(ret, line) < 0) -- break; -- } else { -- if(line == NULL) { -- /* -+ bodyIsEmpty = FALSE; -+ if (uudecodeFile(ret, line, dir, map, at) < 0) -+ if (messageAddStr(ret, line) < 0) -+ break; -+ } else { -+ if (line == NULL) { -+ /* - * Although this would save time and RAM, some - * phish signatures have been built which need - * the blank lines - */ -- if(lastBodyLineWasBlank && -- (messageGetMimeType(ret) != TEXT)) { -- cli_dbgmsg("Ignoring consecutive blank lines in the body\n"); -- continue; -- } -- lastBodyLineWasBlank = TRUE; -- } else { -- if(bodyIsEmpty) { -- /* -+ if (lastBodyLineWasBlank && -+ (messageGetMimeType(ret) != TEXT)) { -+ cli_dbgmsg("Ignoring consecutive blank lines in the body\n"); -+ continue; -+ } -+ lastBodyLineWasBlank = TRUE; -+ } else { -+ if (bodyIsEmpty) { -+ /* - * Broken message: new line in the - * middle of the headers, so the first - * line of the body is in fact - * the last lines of the header - */ -- if(newline_in_header(line)) -- continue; -- bodyIsEmpty = FALSE; -- } -- lastBodyLineWasBlank = FALSE; -- } -- -- if(messageAddStr(ret, line) < 0) -- break; -- } -- } while(getline_from_mbox(buffer, sizeof(buffer) - 1, map, at) != NULL); -- -- if(boundary) -- free(boundary); -- -- if(fullline) { -- if(*fullline) switch(commandNumber) { -- case CONTENT_TRANSFER_ENCODING: -- case CONTENT_DISPOSITION: -- case CONTENT_TYPE: -- cli_dbgmsg("parseEmailFile: Fullline unparsed '%s'\n", fullline); -- } -- free(fullline); -- } -- -- if(!anyHeadersFound) { -- /* -+ if (newline_in_header(line)) -+ continue; -+ bodyIsEmpty = FALSE; -+ } -+ lastBodyLineWasBlank = FALSE; -+ } -+ -+ if (messageAddStr(ret, line) < 0) -+ break; -+ } -+ } while (getline_from_mbox(buffer, sizeof(buffer) - 1, map, at) != NULL); -+ -+ if (boundary) -+ free(boundary); -+ -+ if (fullline) { -+ if (*fullline) switch (commandNumber) { -+ case CONTENT_TRANSFER_ENCODING: -+ case CONTENT_DISPOSITION: -+ case CONTENT_TYPE: -+ cli_dbgmsg("parseEmailFile: Fullline unparsed '%s'\n", fullline); -+ } -+ free(fullline); -+ } -+ -+ if (!anyHeadersFound) { -+ /* - * False positive in believing we have an e-mail when we don't - */ -- messageDestroy(ret); -- cli_dbgmsg("parseEmailFile: no headers found, assuming it isn't an email\n"); -- return NULL; -- } -+ messageDestroy(ret); -+ cli_dbgmsg("parseEmailFile: no headers found, assuming it isn't an email\n"); -+ return NULL; -+ } - -- cli_dbgmsg("parseEmailFile: return\n"); -+ cli_dbgmsg("parseEmailFile: return\n"); - -- return ret; -+ return ret; - } - - /* -@@ -867,162 +845,162 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first - static message * - parseEmailHeaders(message *m, const table_t *rfc821) - { -- bool inHeader = TRUE; -- bool bodyIsEmpty = TRUE; -- text *t; -- message *ret; -- bool anyHeadersFound = FALSE; -- int commandNumber = -1; -- char *fullline = NULL; -- size_t fulllinelength = 0; -- -- cli_dbgmsg("parseEmailHeaders\n"); -- -- if(m == NULL) -- return NULL; -- -- ret = messageCreate(); -- -- for(t = messageGetBody(m); t; t = t->t_next) { -- const char *line; -- -- if(t->t_line) -- line = lineGetData(t->t_line); -- else -- line = NULL; -- -- if(inHeader) { -- cli_dbgmsg("parseEmailHeaders: check '%s'\n", -- line ? line : ""); -- if(line == NULL) { -- /* -+ bool inHeader = TRUE; -+ bool bodyIsEmpty = TRUE; -+ text *t; -+ message *ret; -+ bool anyHeadersFound = FALSE; -+ int commandNumber = -1; -+ char *fullline = NULL; -+ size_t fulllinelength = 0; -+ -+ cli_dbgmsg("parseEmailHeaders\n"); -+ -+ if (m == NULL) -+ return NULL; -+ -+ ret = messageCreate(); -+ -+ for (t = messageGetBody(m); t; t = t->t_next) { -+ const char *line; -+ -+ if (t->t_line) -+ line = lineGetData(t->t_line); -+ else -+ line = NULL; -+ -+ if (inHeader) { -+ cli_dbgmsg("parseEmailHeaders: check '%s'\n", -+ line ? line : ""); -+ if (line == NULL) { -+ /* - * A blank line signifies the end of - * the header and the start of the text - */ -- cli_dbgmsg("End of header information\n"); -- if(!anyHeadersFound) { -- cli_dbgmsg("Nothing interesting in the header\n"); -- break; -- } -- inHeader = FALSE; -- bodyIsEmpty = TRUE; -- } else { -- char *ptr; -- -- if(fullline == NULL) { -- char cmd[RFC2821LENGTH + 1]; -- -- /* -+ cli_dbgmsg("End of header information\n"); -+ if (!anyHeadersFound) { -+ cli_dbgmsg("Nothing interesting in the header\n"); -+ break; -+ } -+ inHeader = FALSE; -+ bodyIsEmpty = TRUE; -+ } else { -+ char *ptr; -+ -+ if (fullline == NULL) { -+ char cmd[RFC2821LENGTH + 1]; -+ -+ /* - * Continuation of line we're ignoring? - */ -- if(isblank(line[0])) -- continue; -+ if (isblank(line[0])) -+ continue; - -- /* -+ /* - * Is this a header we're interested in? - */ -- if((strchr(line, ':') == NULL) || -- (cli_strtokbuf(line, 0, ":", cmd) == NULL)) { -- if(strncmp(line, "From ", 5) == 0) -- anyHeadersFound = TRUE; -- continue; -- } -- -- ptr = rfc822comments(cmd, NULL); -- commandNumber = tableFind(rfc821, ptr ? ptr : cmd); -- if(ptr) -- free(ptr); -- -- switch(commandNumber) { -- case CONTENT_TRANSFER_ENCODING: -- case CONTENT_DISPOSITION: -- case CONTENT_TYPE: -- anyHeadersFound = TRUE; -- break; -- default: -- if(!anyHeadersFound) -- anyHeadersFound = usefulHeader(commandNumber, cmd); -- continue; -- } -- fullline = cli_strdup(line); -- fulllinelength = strlen(line) + 1; -- } else if(line) { -- fulllinelength += strlen(line) + 1; -- ptr = cli_realloc(fullline, fulllinelength); -- if(ptr == NULL) -- continue; -- fullline = ptr; -- cli_strlcat(fullline, line, fulllinelength); -- } -- assert(fullline != NULL); -- -- if(next_is_folded_header(t)) -- /* Add arguments to this line */ -- continue; -- -- lineUnlink(t->t_line); -- t->t_line = NULL; -- -- if(count_quotes(fullline) & 1) -- continue; -- -- ptr = rfc822comments(fullline, NULL); -- if(ptr) { -- free(fullline); -- fullline = ptr; -- } -- -- if(parseEmailHeader(ret, fullline, rfc821) < 0) -- continue; -- -- free(fullline); -- fullline = NULL; -- } -- } else { -- if(bodyIsEmpty) { -- if(line == NULL) -- /* throw away leading blank lines */ -- continue; -- /* -+ if ((strchr(line, ':') == NULL) || -+ (cli_strtokbuf(line, 0, ":", cmd) == NULL)) { -+ if (strncmp(line, "From ", 5) == 0) -+ anyHeadersFound = TRUE; -+ continue; -+ } -+ -+ ptr = rfc822comments(cmd, NULL); -+ commandNumber = tableFind(rfc821, ptr ? ptr : cmd); -+ if (ptr) -+ free(ptr); -+ -+ switch (commandNumber) { -+ case CONTENT_TRANSFER_ENCODING: -+ case CONTENT_DISPOSITION: -+ case CONTENT_TYPE: -+ anyHeadersFound = TRUE; -+ break; -+ default: -+ if (!anyHeadersFound) -+ anyHeadersFound = usefulHeader(commandNumber, cmd); -+ continue; -+ } -+ fullline = cli_strdup(line); -+ fulllinelength = strlen(line) + 1; -+ } else if (line) { -+ fulllinelength += strlen(line) + 1; -+ ptr = cli_realloc(fullline, fulllinelength); -+ if (ptr == NULL) -+ continue; -+ fullline = ptr; -+ cli_strlcat(fullline, line, fulllinelength); -+ } -+ assert(fullline != NULL); -+ -+ if (next_is_folded_header(t)) -+ /* Add arguments to this line */ -+ continue; -+ -+ lineUnlink(t->t_line); -+ t->t_line = NULL; -+ -+ if (count_quotes(fullline) & 1) -+ continue; -+ -+ ptr = rfc822comments(fullline, NULL); -+ if (ptr) { -+ free(fullline); -+ fullline = ptr; -+ } -+ -+ if (parseEmailHeader(ret, fullline, rfc821) < 0) -+ continue; -+ -+ free(fullline); -+ fullline = NULL; -+ } -+ } else { -+ if (bodyIsEmpty) { -+ if (line == NULL) -+ /* throw away leading blank lines */ -+ continue; -+ /* - * Broken message: new line in the - * middle of the headers, so the first - * line of the body is in fact - * the last lines of the header - */ -- if(newline_in_header(line)) -- continue; -- bodyIsEmpty = FALSE; -- } -- /*if(t->t_line && isuuencodebegin(t->t_line)) -+ if (newline_in_header(line)) -+ continue; -+ bodyIsEmpty = FALSE; -+ } -+ /*if(t->t_line && isuuencodebegin(t->t_line)) - puts("FIXME: add fast visa here");*/ -- cli_dbgmsg("parseEmailHeaders: finished with headers, moving body\n"); -- messageMoveText(ret, t, m); -- break; -- } -- } -- -- if(fullline) { -- if(*fullline) switch(commandNumber) { -- case CONTENT_TRANSFER_ENCODING: -- case CONTENT_DISPOSITION: -- case CONTENT_TYPE: -- cli_dbgmsg("parseEmailHeaders: Fullline unparsed '%s'\n", fullline); -- } -- free(fullline); -- } -- -- if(!anyHeadersFound) { -- /* -+ cli_dbgmsg("parseEmailHeaders: finished with headers, moving body\n"); -+ messageMoveText(ret, t, m); -+ break; -+ } -+ } -+ -+ if (fullline) { -+ if (*fullline) switch (commandNumber) { -+ case CONTENT_TRANSFER_ENCODING: -+ case CONTENT_DISPOSITION: -+ case CONTENT_TYPE: -+ cli_dbgmsg("parseEmailHeaders: Fullline unparsed '%s'\n", fullline); -+ } -+ free(fullline); -+ } -+ -+ if (!anyHeadersFound) { -+ /* - * False positive in believing we have an e-mail when we don't - */ -- messageDestroy(ret); -- cli_dbgmsg("parseEmailHeaders: no headers found, assuming it isn't an email\n"); -- return NULL; -- } -+ messageDestroy(ret); -+ cli_dbgmsg("parseEmailHeaders: no headers found, assuming it isn't an email\n"); -+ return NULL; -+ } - -- cli_dbgmsg("parseEmailHeaders: return\n"); -+ cli_dbgmsg("parseEmailHeaders: return\n"); - -- return ret; -+ return ret; - } - - /* -@@ -1031,99 +1009,97 @@ parseEmailHeaders(message *m, const table_t *rfc821) - static int - parseEmailHeader(message *m, const char *line, const table_t *rfc821) - { -- int ret; -+ int ret; - #ifdef CL_THREAD_SAFE -- char *strptr; -+ char *strptr; - #endif -- const char *separator; -- char *cmd, *copy, tokenseparator[2]; -+ const char *separator; -+ char *cmd, *copy, tokenseparator[2]; - -- cli_dbgmsg("parseEmailHeader '%s'\n", line); -+ cli_dbgmsg("parseEmailHeader '%s'\n", line); - -- /* -+ /* - * In RFC822 the separator between the key a value is a colon, - * e.g. Content-Transfer-Encoding: base64 - * However some MUA's are lapse about this and virus writers exploit - * this hole, so we need to check all known possibilities - */ -- for(separator = ":= "; *separator; separator++) -- if(strchr(line, *separator) != NULL) -- break; -+ for (separator = ":= "; *separator; separator++) -+ if (strchr(line, *separator) != NULL) -+ break; - -- if(*separator == '\0') -- return -1; -+ if (*separator == '\0') -+ return -1; - -- copy = rfc2047(line); -- if(copy == NULL) -- /* an RFC checker would return -1 here */ -- copy = cli_strdup(line); -+ copy = rfc2047(line); -+ if (copy == NULL) -+ /* an RFC checker would return -1 here */ -+ copy = cli_strdup(line); - -- tokenseparator[0] = *separator; -- tokenseparator[1] = '\0'; -+ tokenseparator[0] = *separator; -+ tokenseparator[1] = '\0'; - -- ret = -1; -+ ret = -1; - --#ifdef CL_THREAD_SAFE -- cmd = strtok_r(copy, tokenseparator, &strptr); -+#ifdef CL_THREAD_SAFE -+ cmd = strtok_r(copy, tokenseparator, &strptr); - #else -- cmd = strtok(copy, tokenseparator); -+ cmd = strtok(copy, tokenseparator); - #endif - -- if(cmd && (strstrip(cmd) > 0)) { --#ifdef CL_THREAD_SAFE -- char *arg = strtok_r(NULL, "", &strptr); -+ if (cmd && (strstrip(cmd) > 0)) { -+#ifdef CL_THREAD_SAFE -+ char *arg = strtok_r(NULL, "", &strptr); - #else -- char *arg = strtok(NULL, ""); -+ char *arg = strtok(NULL, ""); - #endif - -- if(arg) -- /* -+ if (arg) -+ /* - * Found a header such as - * Content-Type: multipart/mixed; - * set arg to be - * "multipart/mixed" and cmd to - * be "Content-Type" - */ -- ret = parseMimeHeader(m, cmd, rfc821, arg); -- } -- free(copy); -- return ret; -+ ret = parseMimeHeader(m, cmd, rfc821, arg); -+ } -+ free(copy); -+ return ret; - } - - #if HAVE_LIBXML2 - static const struct key_entry mhtml_keys[] = { -- /* root html tags for microsoft office document */ -- { "html", "RootHTML", MSXML_JSON_ROOT | MSXML_JSON_ATTRIB }, -- -- { "head", "Head", MSXML_JSON_WRKPTR | MSXML_COMMENT_CB }, -- { "meta", "Meta", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_ATTRIB }, -- { "link", "Link", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_ATTRIB }, -- { "script", "Script", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_VALUE } --}; -+ /* root html tags for microsoft office document */ -+ {"html", "RootHTML", MSXML_JSON_ROOT | MSXML_JSON_ATTRIB}, -+ -+ {"head", "Head", MSXML_JSON_WRKPTR | MSXML_COMMENT_CB}, -+ {"meta", "Meta", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_ATTRIB}, -+ {"link", "Link", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_ATTRIB}, -+ {"script", "Script", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_VALUE}}; - static size_t num_mhtml_keys = sizeof(mhtml_keys) / sizeof(struct key_entry); - - static const struct key_entry mhtml_comment_keys[] = { -- /* embedded xml tags (comment) for microsoft office document */ -- { "o:documentproperties", "DocumentProperties", MSXML_JSON_ROOT | MSXML_JSON_ATTRIB }, -- { "o:author", "Author", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:lastauthor", "LastAuthor", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:revision", "Revision", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:totaltime", "TotalTime", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:created", "Created", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:lastsaved", "LastSaved", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:pages", "Pages", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:words", "Words", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:characters", "Characters", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:company", "Company", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:lines", "Lines", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:paragraphs", "Paragraphs", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:characterswithspaces", "CharactersWithSpaces", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- { "o:version", "Version", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, -- -- { "o:officedocumentsettings", "DocumentSettings", MSXML_IGNORE_ELEM }, -- { "w:worddocument", "WordDocument", MSXML_IGNORE_ELEM }, -- { "w:latentstyles", "LatentStyles", MSXML_IGNORE_ELEM } --}; -+ /* embedded xml tags (comment) for microsoft office document */ -+ {"o:documentproperties", "DocumentProperties", MSXML_JSON_ROOT | MSXML_JSON_ATTRIB}, -+ {"o:author", "Author", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:lastauthor", "LastAuthor", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:revision", "Revision", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:totaltime", "TotalTime", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:created", "Created", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:lastsaved", "LastSaved", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:pages", "Pages", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:words", "Words", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:characters", "Characters", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:company", "Company", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:lines", "Lines", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:paragraphs", "Paragraphs", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:characterswithspaces", "CharactersWithSpaces", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ {"o:version", "Version", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, -+ -+ {"o:officedocumentsettings", "DocumentSettings", MSXML_IGNORE_ELEM}, -+ {"w:worddocument", "WordDocument", MSXML_IGNORE_ELEM}, -+ {"w:latentstyles", "LatentStyles", MSXML_IGNORE_ELEM}}; - static size_t num_mhtml_comment_keys = sizeof(mhtml_comment_keys) / sizeof(struct key_entry); - #endif - -@@ -1137,55 +1113,55 @@ static int - parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata) - { - #if HAVE_LIBXML2 -- const char *xmlsrt, *xmlend; -- xmlTextReaderPtr reader; -+ const char *xmlsrt, *xmlend; -+ xmlTextReaderPtr reader; - #if HAVE_JSON -- json_object *thisjobj = (json_object *)wrkjobj; -+ json_object *thisjobj = (json_object *)wrkjobj; - #endif -- int ret = CL_SUCCESS; -+ int ret = CL_SUCCESS; - -- UNUSEDPARAM(cbdata); -- UNUSEDPARAM(wrkjobj); -+ UNUSEDPARAM(cbdata); -+ UNUSEDPARAM(wrkjobj); - -- xmlend = comment; -- while ((xmlsrt = strstr(xmlend, ""))) { -- xmlend = strstr(xmlsrt, ""); -- if (xmlend == NULL) { -- cli_dbgmsg("parseMHTMLComment: unbounded xml tag\n"); -- break; -- } -+ xmlend = comment; -+ while ((xmlsrt = strstr(xmlend, ""))) { -+ xmlend = strstr(xmlsrt, ""); -+ if (xmlend == NULL) { -+ cli_dbgmsg("parseMHTMLComment: unbounded xml tag\n"); -+ break; -+ } - -- reader = xmlReaderForMemory(xmlsrt, xmlend-xmlsrt+6, "comment.xml", NULL, CLAMAV_MIN_XMLREADER_FLAGS); -- if (!reader) { -- cli_dbgmsg("parseMHTMLComment: cannot initialize xmlReader\n"); -+ reader = xmlReaderForMemory(xmlsrt, xmlend - xmlsrt + 6, "comment.xml", NULL, CLAMAV_MIN_XMLREADER_FLAGS); -+ if (!reader) { -+ cli_dbgmsg("parseMHTMLComment: cannot initialize xmlReader\n"); - - #if HAVE_JSON -- if (ctx->wrkproperty != NULL) -- ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_XML_READER_MEM"); -+ if (ctx->wrkproperty != NULL) -+ ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_XML_READER_MEM"); - #endif -- return ret; // libxml2 failed! -- } -- -- /* comment callback is not set to prevent recursion */ -- /* TODO: should we separate the key dictionaries? */ -- /* TODO: should we use the json object pointer? */ -- ret = cli_msxml_parse_document(ctx, reader, mhtml_comment_keys, num_mhtml_comment_keys, MSXML_FLAG_JSON, NULL); -- -- xmlTextReaderClose(reader); -- xmlFreeTextReader(reader); -- if (ret != CL_SUCCESS) -- return ret; -- } -+ return ret; // libxml2 failed! -+ } -+ -+ /* comment callback is not set to prevent recursion */ -+ /* TODO: should we separate the key dictionaries? */ -+ /* TODO: should we use the json object pointer? */ -+ ret = cli_msxml_parse_document(ctx, reader, mhtml_comment_keys, num_mhtml_comment_keys, MSXML_FLAG_JSON, NULL); -+ -+ xmlTextReaderClose(reader); -+ xmlFreeTextReader(reader); -+ if (ret != CL_SUCCESS) -+ return ret; -+ } - #else -- UNUSEDPARAM(comment); -- UNUSEDPARAM(ctx); -- UNUSEDPARAM(wrkjobj); -- UNUSEDPARAM(cbdata); -+ UNUSEDPARAM(comment); -+ UNUSEDPARAM(ctx); -+ UNUSEDPARAM(wrkjobj); -+ UNUSEDPARAM(cbdata); - -- cli_dbgmsg("in parseMHTMLComment\n"); -- cli_dbgmsg("parseMHTMLComment: parsing html xml-comments requires libxml2!\n"); -+ cli_dbgmsg("in parseMHTMLComment\n"); -+ cli_dbgmsg("parseMHTMLComment: parsing html xml-comments requires libxml2!\n"); - #endif -- return CL_SUCCESS; -+ return CL_SUCCESS; - } - - /* -@@ -1197,117 +1173,117 @@ parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata - static mbox_status - parseRootMHTML(mbox_ctx *mctx, message *m, text *t) - { -- cli_ctx *ctx = mctx->ctx; -+ cli_ctx *ctx = mctx->ctx; - #if HAVE_LIBXML2 - #ifdef LIBXML_HTML_ENABLED -- struct msxml_ctx mxctx; -- blob *input = NULL; -- htmlDocPtr htmlDoc; -- xmlTextReaderPtr reader; -- int ret = CL_SUCCESS; -- mbox_status rc = OK; -+ struct msxml_ctx mxctx; -+ blob *input = NULL; -+ htmlDocPtr htmlDoc; -+ xmlTextReaderPtr reader; -+ int ret = CL_SUCCESS; -+ mbox_status rc = OK; - #if HAVE_JSON -- json_object *rhtml; -+ json_object *rhtml; - #endif - -- cli_dbgmsg("in parseRootMHTML\n"); -+ cli_dbgmsg("in parseRootMHTML\n"); - -- if (ctx == NULL) -- return OK; -+ if (ctx == NULL) -+ return OK; - -- if (m == NULL && t == NULL) -- return OK; -+ if (m == NULL && t == NULL) -+ return OK; - -- if (m != NULL) -- input = messageToBlob(m, 0); -- else /* t != NULL */ -- input = textToBlob(t, NULL, 0); -+ if (m != NULL) -+ input = messageToBlob(m, 0); -+ else /* t != NULL */ -+ input = textToBlob(t, NULL, 0); - -- if (input == NULL) -- return OK; -+ if (input == NULL) -+ return OK; - -- htmlDoc = htmlReadMemory((char*)input->data, input->len, "mhtml.html", NULL, CLAMAV_MIN_XMLREADER_FLAGS); -- if (htmlDoc == NULL) { -- cli_dbgmsg("parseRootMHTML: cannot initialize read html document\n"); -+ htmlDoc = htmlReadMemory((char *)input->data, input->len, "mhtml.html", NULL, CLAMAV_MIN_XMLREADER_FLAGS); -+ if (htmlDoc == NULL) { -+ cli_dbgmsg("parseRootMHTML: cannot initialize read html document\n"); - #if HAVE_JSON -- if (ctx->wrkproperty != NULL) -- ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_HTML_READ"); -- if (ret != CL_SUCCESS) -- rc = FAIL; -+ if (ctx->wrkproperty != NULL) -+ ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_HTML_READ"); -+ if (ret != CL_SUCCESS) -+ rc = FAIL; - #endif -- blobDestroy(input); -- return rc; -- } -+ blobDestroy(input); -+ return rc; -+ } - - #if HAVE_JSON -- if (mctx->wrkobj) { -- rhtml = cli_jsonobj(mctx->wrkobj, "RootHTML"); -- if (rhtml != NULL) { -- /* MHTML-specific properties */ -- cli_jsonstr(rhtml, "Encoding", (const char*)htmlGetMetaEncoding(htmlDoc)); -- cli_jsonint(rhtml, "CompressMode", xmlGetDocCompressMode(htmlDoc)); -- } -- } -+ if (mctx->wrkobj) { -+ rhtml = cli_jsonobj(mctx->wrkobj, "RootHTML"); -+ if (rhtml != NULL) { -+ /* MHTML-specific properties */ -+ cli_jsonstr(rhtml, "Encoding", (const char *)htmlGetMetaEncoding(htmlDoc)); -+ cli_jsonint(rhtml, "CompressMode", xmlGetDocCompressMode(htmlDoc)); -+ } -+ } - #endif - -- reader = xmlReaderWalker(htmlDoc); -- if (reader == NULL) { -- cli_dbgmsg("parseRootMHTML: cannot initialize xmlTextReader\n"); -+ reader = xmlReaderWalker(htmlDoc); -+ if (reader == NULL) { -+ cli_dbgmsg("parseRootMHTML: cannot initialize xmlTextReader\n"); - #if HAVE_JSON -- if (ctx->wrkproperty != NULL) -- ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_XML_READER_IO"); -- if (ret != CL_SUCCESS) -- rc = FAIL; -+ if (ctx->wrkproperty != NULL) -+ ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_XML_READER_IO"); -+ if (ret != CL_SUCCESS) -+ rc = FAIL; - #endif -- blobDestroy(input); -- return rc; -- } -- -- memset(&mxctx, 0, sizeof(mxctx)); -- /* no scanning callback set */ -- mxctx.comment_cb = parseMHTMLComment; -- ret = cli_msxml_parse_document(ctx, reader, mhtml_keys, num_mhtml_keys, MSXML_FLAG_JSON | MSXML_FLAG_WALK, &mxctx); -- switch (ret) { -- case CL_SUCCESS: -- case CL_ETIMEOUT: -- case CL_BREAK: -- rc = OK; -- break; -- -- case CL_EMAXREC: -- rc = MAXREC; -- break; -- -- case CL_EMAXFILES: -- rc = MAXFILES; -- break; -- -- case CL_VIRUS: -- rc = VIRUS; -- break; -- -- default: -- rc = FAIL; -- } -- -- xmlTextReaderClose(reader); -- xmlFreeTextReader(reader); -- xmlFreeDoc(htmlDoc); -- blobDestroy(input); -- return rc; -+ blobDestroy(input); -+ return rc; -+ } -+ -+ memset(&mxctx, 0, sizeof(mxctx)); -+ /* no scanning callback set */ -+ mxctx.comment_cb = parseMHTMLComment; -+ ret = cli_msxml_parse_document(ctx, reader, mhtml_keys, num_mhtml_keys, MSXML_FLAG_JSON | MSXML_FLAG_WALK, &mxctx); -+ switch (ret) { -+ case CL_SUCCESS: -+ case CL_ETIMEOUT: -+ case CL_BREAK: -+ rc = OK; -+ break; -+ -+ case CL_EMAXREC: -+ rc = MAXREC; -+ break; -+ -+ case CL_EMAXFILES: -+ rc = MAXFILES; -+ break; -+ -+ case CL_VIRUS: -+ rc = VIRUS; -+ break; -+ -+ default: -+ rc = FAIL; -+ } -+ -+ xmlTextReaderClose(reader); -+ xmlFreeTextReader(reader); -+ xmlFreeDoc(htmlDoc); -+ blobDestroy(input); -+ return rc; - #else /* LIBXML_HTML_ENABLED */ -- UNUSEDPARAM(m); -- UNUSEDPARAM(t); -- cli_dbgmsg("in parseRootMHTML\n"); -- cli_dbgmsg("parseRootMHTML: parsing html documents disabled in libxml2!\n"); -+ UNUSEDPARAM(m); -+ UNUSEDPARAM(t); -+ cli_dbgmsg("in parseRootMHTML\n"); -+ cli_dbgmsg("parseRootMHTML: parsing html documents disabled in libxml2!\n"); - #endif /* LIBXML_HTML_ENABLED */ - #else /* HAVE_LIBXML2 */ -- UNUSEDPARAM(m); -- UNUSEDPARAM(t); -- cli_dbgmsg("in parseRootMHTML\n"); -- cli_dbgmsg("parseRootMHTML: parsing html documents requires libxml2!\n"); -+ UNUSEDPARAM(m); -+ UNUSEDPARAM(t); -+ cli_dbgmsg("in parseRootMHTML\n"); -+ cli_dbgmsg("parseRootMHTML: parsing html documents requires libxml2!\n"); - -- return OK; -+ return OK; - #endif /* HAVE_LIBXML2 */ - } - -@@ -1324,182 +1300,181 @@ parseRootMHTML(mbox_ctx *mctx, message *m, text *t) - static mbox_status - parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level) - { -- mbox_status rc; -- text *aText = textIn; -- message *mainMessage = messageIn; -- fileblob *fb; -- bool infected = FALSE; -- const struct cl_engine *engine = mctx->ctx->engine; -- const int doPhishingScan = engine->dboptions&CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); -+ mbox_status rc; -+ text *aText = textIn; -+ message *mainMessage = messageIn; -+ fileblob *fb; -+ bool infected = FALSE; -+ const struct cl_engine *engine = mctx->ctx->engine; -+ const int doPhishingScan = engine->dboptions & CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); - #if HAVE_JSON -- json_object *saveobj = mctx->wrkobj; -+ json_object *saveobj = mctx->wrkobj; - #endif - -- cli_dbgmsg("in parseEmailBody, %u files saved so far\n", -- mctx->files); -+ cli_dbgmsg("in parseEmailBody, %u files saved so far\n", -+ mctx->files); - -- /* FIXMELIMITS: this should be better integrated */ -- if(engine->maxreclevel) -- /* -+ /* FIXMELIMITS: this should be better integrated */ -+ if (engine->maxreclevel) -+ /* - * This is approximate - */ -- if(recursion_level > engine->maxreclevel) { -+ if (recursion_level > engine->maxreclevel) { - -- cli_dbgmsg("parseEmailBody: hit maximum recursion level (%u)\n", recursion_level); -- return MAXREC; -- } -- if(engine->maxfiles && (mctx->files >= engine->maxfiles)) { -- /* -+ cli_dbgmsg("parseEmailBody: hit maximum recursion level (%u)\n", recursion_level); -+ return MAXREC; -+ } -+ if (engine->maxfiles && (mctx->files >= engine->maxfiles)) { -+ /* - * FIXME: This is only approx - it may have already - * been exceeded - */ -- cli_dbgmsg("parseEmailBody: number of files exceeded %u\n", engine->maxfiles); -- return MAXFILES; -- } -- -- rc = OK; -- -- /* Anything left to be parsed? */ -- if(mainMessage && (messageGetBody(mainMessage) != NULL)) { -- mime_type mimeType; -- int subtype, inhead, htmltextPart, inMimeHead, i; -- const char *mimeSubtype; -- char *boundary; -- const text *t_line; -- /*bool isAlternative;*/ -- message *aMessage; -- int multiparts = 0; -- message **messages = NULL; /* parts of a multipart message */ -- -- cli_dbgmsg("Parsing mail file\n"); -- -- mimeType = messageGetMimeType(mainMessage); -- mimeSubtype = messageGetMimeSubtype(mainMessage); -+ cli_dbgmsg("parseEmailBody: number of files exceeded %u\n", engine->maxfiles); -+ return MAXFILES; -+ } -+ -+ rc = OK; -+ -+ /* Anything left to be parsed? */ -+ if (mainMessage && (messageGetBody(mainMessage) != NULL)) { -+ mime_type mimeType; -+ int subtype, inhead, htmltextPart, inMimeHead, i; -+ const char *mimeSubtype; -+ char *boundary; -+ const text *t_line; -+ /*bool isAlternative;*/ -+ message *aMessage; -+ int multiparts = 0; -+ message **messages = NULL; /* parts of a multipart message */ -+ -+ cli_dbgmsg("Parsing mail file\n"); -+ -+ mimeType = messageGetMimeType(mainMessage); -+ mimeSubtype = messageGetMimeSubtype(mainMessage); - #if HAVE_JSON -- if (mctx->wrkobj != NULL) { -- mctx->wrkobj = cli_jsonobj(mctx->wrkobj, "Body"); -- cli_jsonstr(mctx->wrkobj, "MimeType", getMimeTypeStr(mimeType)); -- cli_jsonstr(mctx->wrkobj, "MimeSubtype", mimeSubtype); -- cli_jsonstr(mctx->wrkobj, "EncodingType", getEncTypeStr(messageGetEncoding(mainMessage))); -- cli_jsonstr(mctx->wrkobj, "Disposition", messageGetDispositionType(mainMessage)); -- cli_jsonstr(mctx->wrkobj, "Filename", messageHasFilename(mainMessage) ? -- messageGetFilename(mainMessage): "(inline)"); -- } -+ if (mctx->wrkobj != NULL) { -+ mctx->wrkobj = cli_jsonobj(mctx->wrkobj, "Body"); -+ cli_jsonstr(mctx->wrkobj, "MimeType", getMimeTypeStr(mimeType)); -+ cli_jsonstr(mctx->wrkobj, "MimeSubtype", mimeSubtype); -+ cli_jsonstr(mctx->wrkobj, "EncodingType", getEncTypeStr(messageGetEncoding(mainMessage))); -+ cli_jsonstr(mctx->wrkobj, "Disposition", messageGetDispositionType(mainMessage)); -+ cli_jsonstr(mctx->wrkobj, "Filename", messageHasFilename(mainMessage) ? messageGetFilename(mainMessage) : "(inline)"); -+ } - #endif - -- /* pre-process */ -- subtype = tableFind(mctx->subtypeTable, mimeSubtype); -- if((mimeType == TEXT) && (subtype == PLAIN)) { -- /* -+ /* pre-process */ -+ subtype = tableFind(mctx->subtypeTable, mimeSubtype); -+ if ((mimeType == TEXT) && (subtype == PLAIN)) { -+ /* - * This is effectively no encoding, notice that we - * don't check that charset is us-ascii - */ -- cli_dbgmsg("text/plain: Assume no attachments\n"); -- mimeType = NOMIME; -- messageSetMimeSubtype(mainMessage, ""); -- } else if((mimeType == MESSAGE) && -- (strcasecmp(mimeSubtype, "rfc822-headers") == 0)) { -- /* -+ cli_dbgmsg("text/plain: Assume no attachments\n"); -+ mimeType = NOMIME; -+ messageSetMimeSubtype(mainMessage, ""); -+ } else if ((mimeType == MESSAGE) && -+ (strcasecmp(mimeSubtype, "rfc822-headers") == 0)) { -+ /* - * RFC1892/RFC3462: section 2 text/rfc822-headers - * incorrectly sent as message/rfc822-headers - * - * Parse as text/plain, i.e. no mime - */ -- cli_dbgmsg("Changing message/rfc822-headers to text/rfc822-headers\n"); -- mimeType = NOMIME; -- messageSetMimeSubtype(mainMessage, ""); -- } else -- cli_dbgmsg("mimeType = %d\n", (int)mimeType); -- -- switch(mimeType) { -- case NOMIME: -- cli_dbgmsg("Not a mime encoded message\n"); -- aText = textAddMessage(aText, mainMessage); -- -- if(!doPhishingScan) -- break; -- /* -+ cli_dbgmsg("Changing message/rfc822-headers to text/rfc822-headers\n"); -+ mimeType = NOMIME; -+ messageSetMimeSubtype(mainMessage, ""); -+ } else -+ cli_dbgmsg("mimeType = %d\n", (int)mimeType); -+ -+ switch (mimeType) { -+ case NOMIME: -+ cli_dbgmsg("Not a mime encoded message\n"); -+ aText = textAddMessage(aText, mainMessage); -+ -+ if (!doPhishingScan) -+ break; -+ /* - * Fall through: some phishing mails claim they are - * text/plain, when they are in fact html - */ -- case TEXT: -- /* text/plain has been preprocessed as no encoding */ -- if(doPhishingScan) { -- /* -+ case TEXT: -+ /* text/plain has been preprocessed as no encoding */ -+ if (doPhishingScan) { -+ /* - * It would be better to save and scan the - * file and only checkURLs if it's found to be - * clean - */ -- checkURLs(mainMessage, mctx, &rc, (subtype == HTML)); -- /* -+ checkURLs(mainMessage, mctx, &rc, (subtype == HTML)); -+ /* - * There might be html sent without subtype - * html too, so scan them for phishing - */ -- if(rc == VIRUS) -- infected = TRUE; -- } -- break; -- case MULTIPART: -- cli_dbgmsg("Content-type 'multipart' handler\n"); -- boundary = messageFindArgument(mainMessage, "boundary"); -+ if (rc == VIRUS) -+ infected = TRUE; -+ } -+ break; -+ case MULTIPART: -+ cli_dbgmsg("Content-type 'multipart' handler\n"); -+ boundary = messageFindArgument(mainMessage, "boundary"); - - #if HAVE_JSON -- if (mctx->wrkobj != NULL) -- cli_jsonstr(mctx->wrkobj, "Boundary", boundary); -+ if (mctx->wrkobj != NULL) -+ cli_jsonstr(mctx->wrkobj, "Boundary", boundary); - #endif - -- if(boundary == NULL) { -- cli_dbgmsg("Multipart/%s MIME message contains no boundary header\n", -- mimeSubtype); -- /* Broken e-mail message */ -- mimeType = NOMIME; -- /* -+ if (boundary == NULL) { -+ cli_dbgmsg("Multipart/%s MIME message contains no boundary header\n", -+ mimeSubtype); -+ /* Broken e-mail message */ -+ mimeType = NOMIME; -+ /* - * The break means that we will still - * check if the file contains a uuencoded file - */ -- break; -- } -+ break; -+ } - -- cli_chomp(boundary); -+ cli_chomp(boundary); - -- /* Perhaps it should assume mixed? */ -- if(mimeSubtype[0] == '\0') { -- cli_dbgmsg("Multipart has no subtype assuming alternative\n"); -- mimeSubtype = "alternative"; -- messageSetMimeSubtype(mainMessage, "alternative"); -- } -+ /* Perhaps it should assume mixed? */ -+ if (mimeSubtype[0] == '\0') { -+ cli_dbgmsg("Multipart has no subtype assuming alternative\n"); -+ mimeSubtype = "alternative"; -+ messageSetMimeSubtype(mainMessage, "alternative"); -+ } - -- /* -+ /* - * Get to the start of the first message - */ -- t_line = messageGetBody(mainMessage); -- -- if(t_line == NULL) { -- cli_dbgmsg("Multipart MIME message has no body\n"); -- free((char *)boundary); -- mimeType = NOMIME; -- break; -- } -- -- do -- if(t_line->t_line) { -- if(boundaryStart(lineGetData(t_line->t_line), boundary)) -- break; -- /* -+ t_line = messageGetBody(mainMessage); -+ -+ if (t_line == NULL) { -+ cli_dbgmsg("Multipart MIME message has no body\n"); -+ free((char *)boundary); -+ mimeType = NOMIME; -+ break; -+ } -+ -+ do -+ if (t_line->t_line) { -+ if (boundaryStart(lineGetData(t_line->t_line), boundary)) -+ break; -+ /* - * Found a binhex file before - * the first multipart - * TODO: check yEnc - */ -- if(binhexBegin(mainMessage) == t_line) { -- if(exportBinhexMessage(mctx, mainMessage)) { -- /* virus found */ -- rc = VIRUS; -- infected = TRUE; -- break; -- } -- } else if(t_line->t_next && -- (encodingLine(mainMessage) == t_line->t_next)) { -- /* -+ if (binhexBegin(mainMessage) == t_line) { -+ if (exportBinhexMessage(mctx, mainMessage)) { -+ /* virus found */ -+ rc = VIRUS; -+ infected = TRUE; -+ break; -+ } -+ } else if (t_line->t_next && -+ (encodingLine(mainMessage) == t_line->t_next)) { -+ /* - * We look for the next line - * since later on we'll skip - * over the important line when -@@ -1508,39 +1483,39 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * which it would have been in - * an RFC compliant world - */ -- cli_dbgmsg("Found MIME attachment before the first MIME section \"%s\"\n", -- lineGetData(t_line->t_next->t_line)); -- if(messageGetEncoding(mainMessage) == NOENCODING) -- break; -- } -- } -- while((t_line = t_line->t_next) != NULL); -- -- if(t_line == NULL) { -- cli_dbgmsg("Multipart MIME message contains no boundary lines (%s)\n", -- boundary); -- free((char *)boundary); -- mimeType = NOMIME; -- /* -+ cli_dbgmsg("Found MIME attachment before the first MIME section \"%s\"\n", -+ lineGetData(t_line->t_next->t_line)); -+ if (messageGetEncoding(mainMessage) == NOENCODING) -+ break; -+ } -+ } -+ while ((t_line = t_line->t_next) != NULL); -+ -+ if (t_line == NULL) { -+ cli_dbgmsg("Multipart MIME message contains no boundary lines (%s)\n", -+ boundary); -+ free((char *)boundary); -+ mimeType = NOMIME; -+ /* - * The break means that we will still - * check if the file contains a yEnc/binhex file - */ -- break; -- } -- /* -+ break; -+ } -+ /* - * Build up a table of all of the parts of this - * multipart message. Remember, each part may itself - * be a multipart message. - */ -- inhead = 1; -- inMimeHead = 0; -+ inhead = 1; -+ inMimeHead = 0; - -- /* -+ /* - * Re-read this variable in case mimeSubtype has changed - */ -- subtype = tableFind(mctx->subtypeTable, mimeSubtype); -+ subtype = tableFind(mctx->subtypeTable, mimeSubtype); - -- /* -+ /* - * Parse the mainMessage object and create an array - * of objects called messages, one for each of the - * multiparts that mainMessage contains. -@@ -1554,99 +1529,99 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * which of those elements it will be, except in - * the case of mixed, when all parts need to be scanned. - */ -- for(multiparts = 0; t_line && !infected; multiparts++) { -- int lines = 0; -- message **m; -- mbox_status old_rc; -- -- m = cli_realloc(messages, ((multiparts + 1) * sizeof(message *))); -- if(m == NULL) -- break; -- messages = m; -- -- aMessage = messages[multiparts] = messageCreate(); -- if(aMessage == NULL) { -- multiparts--; -- /* if allocation failed the first time, -+ for (multiparts = 0; t_line && !infected; multiparts++) { -+ int lines = 0; -+ message **m; -+ mbox_status old_rc; -+ -+ m = cli_realloc(messages, ((multiparts + 1) * sizeof(message *))); -+ if (m == NULL) -+ break; -+ messages = m; -+ -+ aMessage = messages[multiparts] = messageCreate(); -+ if (aMessage == NULL) { -+ multiparts--; -+ /* if allocation failed the first time, - * there's no point in retrying, just - * break out */ -- break; -- } -- messageSetCTX(aMessage, mctx->ctx); -+ break; -+ } -+ messageSetCTX(aMessage, mctx->ctx); - -- cli_dbgmsg("Now read in part %d\n", multiparts); -+ cli_dbgmsg("Now read in part %d\n", multiparts); - -- /* -+ /* - * Ignore blank lines. There shouldn't be ANY - * but some viruses insert them - */ -- while((t_line = t_line->t_next) != NULL) -- if(t_line->t_line && -- /*(cli_chomp(t_line->t_text) > 0))*/ -- (strlen(lineGetData(t_line->t_line)) > 0)) -- break; -- -- if(t_line == NULL) { -- cli_dbgmsg("Empty part\n"); -- /* -+ while ((t_line = t_line->t_next) != NULL) -+ if (t_line->t_line && -+ /*(cli_chomp(t_line->t_text) > 0))*/ -+ (strlen(lineGetData(t_line->t_line)) > 0)) -+ break; -+ -+ if (t_line == NULL) { -+ cli_dbgmsg("Empty part\n"); -+ /* - * Remove this part unless there's - * a binhex portion somewhere in - * the complete message that we may - * throw away by mistake if the MIME - * encoding information is incorrect - */ -- if(mainMessage && -- (binhexBegin(mainMessage) == NULL)) { -- messageDestroy(aMessage); -- --multiparts; -- } -- continue; -- } -- -- do { -- const char *line = lineGetData(t_line->t_line); -- -- /*cli_dbgmsg("multipart %d: inMimeHead %d inhead %d boundary '%s' line '%s' next '%s'\n", -+ if (mainMessage && -+ (binhexBegin(mainMessage) == NULL)) { -+ messageDestroy(aMessage); -+ --multiparts; -+ } -+ continue; -+ } -+ -+ do { -+ const char *line = lineGetData(t_line->t_line); -+ -+ /*cli_dbgmsg("multipart %d: inMimeHead %d inhead %d boundary '%s' line '%s' next '%s'\n", - multiparts, inMimeHead, inhead, boundary, line, - t_line->t_next && t_line->t_next->t_line ? lineGetData(t_line->t_next->t_line) : "(null)");*/ - -- if(inMimeHead) { /* continuation line */ -- if(line == NULL) { -- /*inhead =*/ inMimeHead = 0; -- continue; -- } -- /* -+ if (inMimeHead) { /* continuation line */ -+ if (line == NULL) { -+ /*inhead =*/inMimeHead = 0; -+ continue; -+ } -+ /* - * Handle continuation lines - * because the previous line - * ended with a ; or this line - * starts with a white space - */ -- cli_dbgmsg("Multipart %d: About to add mime Argument '%s'\n", -- multiparts, line); -- /* -+ cli_dbgmsg("Multipart %d: About to add mime Argument '%s'\n", -+ multiparts, line); -+ /* - * Handle the case when it - * isn't really a continuation - * line: - * Content-Type: application/octet-stream; - * Content-Transfer-Encoding: base64 - */ -- parseEmailHeader(aMessage, line, mctx->rfc821Table); -- -- while(isspace((int)*line)) -- line++; -- -- if(*line == '\0') { -- inhead = inMimeHead = 0; -- continue; -- } -- inMimeHead = FALSE; -- messageAddArgument(aMessage, line); -- } else if(inhead) { /* handling normal headers */ -- /*int quotes;*/ -- char *fullline, *ptr; -- -- if(line == NULL) { -- /* -+ parseEmailHeader(aMessage, line, mctx->rfc821Table); -+ -+ while (isspace((int)*line)) -+ line++; -+ -+ if (*line == '\0') { -+ inhead = inMimeHead = 0; -+ continue; -+ } -+ inMimeHead = FALSE; -+ messageAddArgument(aMessage, line); -+ } else if (inhead) { /* handling normal headers */ -+ /*int quotes;*/ -+ char *fullline, *ptr; -+ -+ if (line == NULL) { -+ /* - * empty line, should the end of the headers, - * but some base64 decoders, e.g. uudeview, are broken - * and will handle this type of entry, decoding the -@@ -1663,15 +1638,15 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * - * UEsDBAoAAAAAAACgPjJ2RHw676gAAO+oAABEAAAAbWFpbF90ZXh0LWluZm8udHh0ICAgICAgICAg - */ -- const text *next = t_line->t_next; -+ const text *next = t_line->t_next; - -- if(next && next->t_line) { -- const char *data = lineGetData(next->t_line); -+ if (next && next->t_line) { -+ const char *data = lineGetData(next->t_line); - -- if((messageGetEncoding(aMessage) == NOENCODING) && -- (messageGetMimeType(aMessage) == APPLICATION) && -- data && strstr(data, "base64")) { -- /* -+ if ((messageGetEncoding(aMessage) == NOENCODING) && -+ (messageGetMimeType(aMessage) == APPLICATION) && -+ data && strstr(data, "base64")) { -+ /* - * Handle this nightmare (note the blank - * line in the header and the incorrect - * content-transfer-encoding header) -@@ -1681,23 +1656,23 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * r-Encoding: base64 - * Content-Disposition: attachment; filename="zipped_files.EXE" - */ -- messageSetEncoding(aMessage, "base64"); -- cli_dbgmsg("Ignoring fake end of headers\n"); -- continue; -- } -- if((strncmp(data, "Content", 7) == 0) || -- (strncmp(data, "filename=", 9) == 0)) { -- cli_dbgmsg("Ignoring fake end of headers\n"); -- continue; -- } -- } -- cli_dbgmsg("Multipart %d: End of header information\n", -- multiparts); -- inhead = 0; -- continue; -- } -- if(isspace((int)*line)) { -- /* -+ messageSetEncoding(aMessage, "base64"); -+ cli_dbgmsg("Ignoring fake end of headers\n"); -+ continue; -+ } -+ if ((strncmp(data, "Content", 7) == 0) || -+ (strncmp(data, "filename=", 9) == 0)) { -+ cli_dbgmsg("Ignoring fake end of headers\n"); -+ continue; -+ } -+ } -+ cli_dbgmsg("Multipart %d: End of header information\n", -+ multiparts); -+ inhead = 0; -+ continue; -+ } -+ if (isspace((int)*line)) { -+ /* - * The first line is - * continuation line. - * This is tricky -@@ -1705,10 +1680,10 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * all we can do is our - * best - */ -- cli_dbgmsg("Part %d starts with a continuation line\n", -- multiparts); -- messageAddArgument(aMessage, line); -- /* -+ cli_dbgmsg("Part %d starts with a continuation line\n", -+ multiparts); -+ messageAddArgument(aMessage, line); -+ /* - * Give it a default - * MIME type since - * that may be the -@@ -1717,68 +1692,68 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * Choose application to - * force a save - */ -- if(messageGetMimeType(aMessage) == NOMIME) -- messageSetMimeType(aMessage, "application"); -- continue; -- } -+ if (messageGetMimeType(aMessage) == NOMIME) -+ messageSetMimeType(aMessage, "application"); -+ continue; -+ } - -- inMimeHead = FALSE; -+ inMimeHead = FALSE; - -- assert(strlen(line) <= RFC2821LENGTH); -+ assert(strlen(line) <= RFC2821LENGTH); - -- fullline = rfc822comments(line, NULL); -- if(fullline == NULL) -- fullline = cli_strdup(line); -+ fullline = rfc822comments(line, NULL); -+ if (fullline == NULL) -+ fullline = cli_strdup(line); - -- /*quotes = count_quotes(fullline);*/ -+ /*quotes = count_quotes(fullline);*/ - -- /* -+ /* - * Fold next lines to the end of this - * if they start with a white space - * or if this line has an odd number of quotes: - * Content-Type: application/octet-stream; name="foo - * " - */ -- while(t_line && next_is_folded_header(t_line)) { -- const char *data; -- size_t datasz; -+ while (t_line && next_is_folded_header(t_line)) { -+ const char *data; -+ size_t datasz; - -- t_line = t_line->t_next; -+ t_line = t_line->t_next; - -- data = lineGetData(t_line->t_line); -+ data = lineGetData(t_line->t_line); - -- if(data[1] == '\0') { -- /* -+ if (data[1] == '\0') { -+ /* - * Broken message: the - * blank line at the end - * of the headers isn't blank - - * it contains a space - */ -- cli_dbgmsg("Multipart %d: headers not terminated by blank line\n", -- multiparts); -- inhead = FALSE; -- break; -- } -+ cli_dbgmsg("Multipart %d: headers not terminated by blank line\n", -+ multiparts); -+ inhead = FALSE; -+ break; -+ } - -- datasz = strlen(fullline) + strlen(data) + 1; -- ptr = cli_realloc(fullline, datasz); -+ datasz = strlen(fullline) + strlen(data) + 1; -+ ptr = cli_realloc(fullline, datasz); - -- if(ptr == NULL) -- break; -+ if (ptr == NULL) -+ break; - -- fullline = ptr; -- cli_strlcat(fullline, data, datasz); -+ fullline = ptr; -+ cli_strlcat(fullline, data, datasz); - -- /*quotes = count_quotes(data);*/ -- } -+ /*quotes = count_quotes(data);*/ -+ } - -- cli_dbgmsg("Multipart %d: About to parse folded header '%s'\n", -- multiparts, fullline); -+ cli_dbgmsg("Multipart %d: About to parse folded header '%s'\n", -+ multiparts, fullline); - -- parseEmailHeader(aMessage, fullline, mctx->rfc821Table); -- free(fullline); -- } else if(boundaryEnd(line, boundary)) { -- /* -+ parseEmailHeader(aMessage, fullline, mctx->rfc821Table); -+ free(fullline); -+ } else if (boundaryEnd(line, boundary)) { -+ /* - * Some viruses put information - * *after* the end of message, - * which presumably some broken -@@ -1786,184 +1761,187 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * can't assume that this - * is the end of the message - */ -- /* t_line = NULL;*/ -- break; -- } else if(boundaryStart(line, boundary)) { -- inhead = 1; -- break; -- } else { -- if(messageAddLine(aMessage, t_line->t_line) < 0) -- break; -- lines++; -- } -- } while((t_line = t_line->t_next) != NULL); -- -- cli_dbgmsg("Part %d has %d lines, rc = %d\n", -- multiparts, lines, (int)rc); -- -- /* -+ /* t_line = NULL;*/ -+ break; -+ } else if (boundaryStart(line, boundary)) { -+ inhead = 1; -+ break; -+ } else { -+ if (messageAddLine(aMessage, t_line->t_line) < 0) -+ break; -+ lines++; -+ } -+ } while ((t_line = t_line->t_next) != NULL); -+ -+ cli_dbgmsg("Part %d has %d lines, rc = %d\n", -+ multiparts, lines, (int)rc); -+ -+ /* - * Only save in the array of messages if some - * decision will be taken on whether to scan. - * If all parts will be scanned then save to - * file straight away - */ -- switch(subtype) { -- case MIXED: -- case ALTERNATIVE: -- case REPORT: -- case DIGEST: -- case APPLEDOUBLE: -- case KNOWBOT: -- case -1: -- old_rc = rc; -- mainMessage = do_multipart(mainMessage, -- messages, multiparts, -- &rc, mctx, messageIn, -- &aText, recursion_level); -- if((rc == OK_ATTACHMENTS_NOT_SAVED) && (old_rc == OK)) -- rc = OK; -- if(messages[multiparts]) { -- messageDestroy(messages[multiparts]); -- messages[multiparts] = NULL; -- } -- --multiparts; -- if(rc == VIRUS) -- infected = TRUE; -- break; -- -- case RELATED: -- case ENCRYPTED: -- case SIGNED: -- case PARALLEL: -- /* all the subtypes that we handle -+ switch (subtype) { -+ case MIXED: -+ case ALTERNATIVE: -+ case REPORT: -+ case DIGEST: -+ case APPLEDOUBLE: -+ case KNOWBOT: -+ case -1: -+ old_rc = rc; -+ mainMessage = do_multipart(mainMessage, -+ messages, multiparts, -+ &rc, mctx, messageIn, -+ &aText, recursion_level); -+ if ((rc == OK_ATTACHMENTS_NOT_SAVED) && (old_rc == OK)) -+ rc = OK; -+ if (messages[multiparts]) { -+ messageDestroy(messages[multiparts]); -+ messages[multiparts] = NULL; -+ } -+ --multiparts; -+ if (rc == VIRUS) -+ infected = TRUE; -+ break; -+ -+ case RELATED: -+ case ENCRYPTED: -+ case SIGNED: -+ case PARALLEL: -+ /* all the subtypes that we handle - * (all from the switch(tableFind...) below) - * must be listed here */ -- break; -- default: -- /* this is a subtype that we -+ break; -+ default: -+ /* this is a subtype that we - * don't handle anyway, - * don't store */ -- if(messages[multiparts]) { -- messageDestroy(messages[multiparts]); -- messages[multiparts] = NULL; -- } -- --multiparts; -- } -- } -+ if (messages[multiparts]) { -+ messageDestroy(messages[multiparts]); -+ messages[multiparts] = NULL; -+ } -+ --multiparts; -+ } -+ } - -- free((char *)boundary); -+ free((char *)boundary); - -- /* -+ /* - * Preprocess. Anything special to be done before - * we handle the multiparts? - */ -- switch(subtype) { -- case KNOWBOT: -- /* TODO */ -- cli_dbgmsg("multipart/knowbot parsed as multipart/mixed for now\n"); -- mimeSubtype = "mixed"; -- break; -- case -1: -- /* -+ switch (subtype) { -+ case KNOWBOT: -+ /* TODO */ -+ cli_dbgmsg("multipart/knowbot parsed as multipart/mixed for now\n"); -+ mimeSubtype = "mixed"; -+ break; -+ case -1: -+ /* - * According to section 7.2.6 of - * RFC1521, unrecognized multiparts - * should be treated as multipart/mixed. - */ -- cli_dbgmsg("Unsupported multipart format `%s', parsed as mixed\n", mimeSubtype); -- mimeSubtype = "mixed"; -- break; -- } -+ cli_dbgmsg("Unsupported multipart format `%s', parsed as mixed\n", mimeSubtype); -+ mimeSubtype = "mixed"; -+ break; -+ } - -- /* -+ /* - * We've finished message we're parsing - */ -- if(mainMessage && (mainMessage != messageIn)) { -- messageDestroy(mainMessage); -- mainMessage = NULL; -- } -- -- cli_dbgmsg("The message has %d parts\n", multiparts); -- -- if(infected || ((multiparts == 0) && (aText == NULL))) { -- if(messages) { -- for(i = 0; i < multiparts; i++) -- if(messages[i]) -- messageDestroy(messages[i]); -- free(messages); -- } -- if(aText && (textIn == NULL)) -- textDestroy(aText); -+ if (mainMessage && (mainMessage != messageIn)) { -+ messageDestroy(mainMessage); -+ mainMessage = NULL; -+ } -+ -+ cli_dbgmsg("The message has %d parts\n", multiparts); -+ -+ if (infected || ((multiparts == 0) && (aText == NULL))) { -+ if (messages) { -+ for (i = 0; i < multiparts; i++) -+ if (messages[i]) -+ messageDestroy(messages[i]); -+ free(messages); -+ } -+ if (aText && (textIn == NULL)) -+ textDestroy(aText); - - #if HAVE_JSON -- mctx->wrkobj = saveobj; -+ mctx->wrkobj = saveobj; - #endif -- /* -+ /* - * Nothing to do - */ -- switch(rc) { -- case VIRUS: return VIRUS; -- case MAXREC: return MAXREC; -- default: return OK_ATTACHMENTS_NOT_SAVED; -- } -- } -+ switch (rc) { -+ case VIRUS: -+ return VIRUS; -+ case MAXREC: -+ return MAXREC; -+ default: -+ return OK_ATTACHMENTS_NOT_SAVED; -+ } -+ } - -- cli_dbgmsg("Find out the multipart type (%s)\n", mimeSubtype); -+ cli_dbgmsg("Find out the multipart type (%s)\n", mimeSubtype); - -- /* -+ /* - * We now have all the parts of the multipart message - * in the messages array: - * message *messages[multiparts] - * Let's decide what to do with them all - */ -- switch(tableFind(mctx->subtypeTable, mimeSubtype)) { -- case RELATED: -- cli_dbgmsg("Multipart related handler\n"); -- /* -+ switch (tableFind(mctx->subtypeTable, mimeSubtype)) { -+ case RELATED: -+ cli_dbgmsg("Multipart related handler\n"); -+ /* - * Have a look to see if there's HTML code - * which will need scanning - */ -- aMessage = NULL; -- assert(multiparts > 0); -+ aMessage = NULL; -+ assert(multiparts > 0); - -- htmltextPart = getTextPart(messages, multiparts); -+ htmltextPart = getTextPart(messages, multiparts); - -- if(htmltextPart >= 0 && messages) { -- if(messageGetBody(messages[htmltextPart])) -+ if (htmltextPart >= 0 && messages) { -+ if (messageGetBody(messages[htmltextPart])) - -- aText = textAddMessage(aText, messages[htmltextPart]); -- } else -- /* -+ aText = textAddMessage(aText, messages[htmltextPart]); -+ } else -+ /* - * There isn't an HTML bit. If there's a - * multipart bit, it'll may be in there - * somewhere - */ -- for(i = 0; i < multiparts; i++) -- if(messageGetMimeType(messages[i]) == MULTIPART) { -- aMessage = messages[i]; -- htmltextPart = i; -- break; -- } -- -- if(htmltextPart == -1) -- cli_dbgmsg("No HTML code found to be scanned\n"); -- else { -+ for (i = 0; i < multiparts; i++) -+ if (messageGetMimeType(messages[i]) == MULTIPART) { -+ aMessage = messages[i]; -+ htmltextPart = i; -+ break; -+ } -+ -+ if (htmltextPart == -1) -+ cli_dbgmsg("No HTML code found to be scanned\n"); -+ else { - #if HAVE_JSON -- /* Send root HTML file for preclassification */ -- if (mctx->ctx->wrkproperty) -- parseRootMHTML(mctx, aMessage, aText); -+ /* Send root HTML file for preclassification */ -+ if (mctx->ctx->wrkproperty) -+ parseRootMHTML(mctx, aMessage, aText); - #endif -- rc = parseEmailBody(aMessage, aText, mctx, recursion_level + 1); -- if((rc == OK) && aMessage) { -- assert(aMessage == messages[htmltextPart]); -- messageDestroy(aMessage); -- messages[htmltextPart] = NULL; -- } else if(rc == VIRUS) { -- infected = TRUE; -- break; -- } -- } -- -- /* -+ rc = parseEmailBody(aMessage, aText, mctx, recursion_level + 1); -+ if ((rc == OK) && aMessage) { -+ assert(aMessage == messages[htmltextPart]); -+ messageDestroy(aMessage); -+ messages[htmltextPart] = NULL; -+ } else if (rc == VIRUS) { -+ infected = TRUE; -+ break; -+ } -+ } -+ -+ /* - * The message is confused about the difference - * between alternative and related. Badtrans.B - * suffers from this problem. -@@ -1972,8 +1950,8 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * Content-Type: multipart/related; - * type="multipart/alternative" - */ -- case DIGEST: -- /* -+ case DIGEST: -+ /* - * According to section 5.1.5 RFC2046, the - * default mime type of multipart/digest parts - * is message/rfc822 -@@ -1984,29 +1962,29 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * OK for our needs since it means each part - * will be scanned - */ -- case ALTERNATIVE: -- cli_dbgmsg("Multipart alternative handler\n"); -+ case ALTERNATIVE: -+ cli_dbgmsg("Multipart alternative handler\n"); - -- /* -+ /* - * Fall through - some clients are broken and - * say alternative instead of mixed. The Klez - * virus is broken that way, and anyway we - * wish to scan all of the alternatives - */ -- case REPORT: -- /* -+ case REPORT: -+ /* - * According to section 1 of RFC1892, the - * syntax of multipart/report is the same - * as multipart/mixed. There are some required - * parameters, but there's no need for us to - * verify that they exist - */ -- case ENCRYPTED: -- /* MUAs without encryption plugins can display as multipart/mixed, -+ case ENCRYPTED: -+ /* MUAs without encryption plugins can display as multipart/mixed, - * just scan it*/ -- case MIXED: -- case APPLEDOUBLE: /* not really supported */ -- /* -+ case MIXED: -+ case APPLEDOUBLE: /* not really supported */ -+ /* - * Look for attachments - * - * Not all formats are supported. If an -@@ -2014,32 +1992,32 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * common enough to implement, it is a simple - * matter to add it - */ -- if(aText) { -- if(mainMessage && (mainMessage != messageIn)) -- messageDestroy(mainMessage); -- mainMessage = NULL; -- } -- -- cli_dbgmsg("Mixed message with %d parts\n", multiparts); -- for(i = 0; i < multiparts; i++) { -- mainMessage = do_multipart(mainMessage, -- messages, i, &rc, mctx, -- messageIn, &aText, recursion_level + 1); -- if(rc == VIRUS) { -- infected = TRUE; -- break; -- } -- if(rc == MAXREC) -- break; -- if (rc == OK_ATTACHMENTS_NOT_SAVED) -- rc = OK; -- } -- -- /* rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); */ -- break; -- case SIGNED: -- case PARALLEL: -- /* -+ if (aText) { -+ if (mainMessage && (mainMessage != messageIn)) -+ messageDestroy(mainMessage); -+ mainMessage = NULL; -+ } -+ -+ cli_dbgmsg("Mixed message with %d parts\n", multiparts); -+ for (i = 0; i < multiparts; i++) { -+ mainMessage = do_multipart(mainMessage, -+ messages, i, &rc, mctx, -+ messageIn, &aText, recursion_level + 1); -+ if (rc == VIRUS) { -+ infected = TRUE; -+ break; -+ } -+ if (rc == MAXREC) -+ break; -+ if (rc == OK_ATTACHMENTS_NOT_SAVED) -+ rc = OK; -+ } -+ -+ /* rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); */ -+ break; -+ case SIGNED: -+ case PARALLEL: -+ /* - * If we're here it could be because we have a - * multipart/mixed message, consisting of a - * message followed by an attachment. That -@@ -2047,226 +2025,225 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * message and we need to dig out the plain - * text part of that alternative - */ -- if(messages) { -- htmltextPart = getTextPart(messages, multiparts); -- if(htmltextPart == -1) -- htmltextPart = 0; -- rc = parseEmailBody(messages[htmltextPart], aText, mctx, recursion_level + 1); -- } -- break; -- default: -- assert(0); -- } -- -- if(mainMessage && (mainMessage != messageIn)) -- messageDestroy(mainMessage); -- -- if(aText && (textIn == NULL)) { -- if((!infected) && (fb = fileblobCreate()) != NULL) { -- cli_dbgmsg("Save non mime and/or text/plain part\n"); -- fileblobSetFilename(fb, mctx->dir, "textpart"); -- /*fileblobAddData(fb, "Received: by clamd (textpart)\n", 30);*/ -- fileblobSetCTX(fb, mctx->ctx); -- (void)textToFileblob(aText, fb, 1); -- -- fileblobDestroy(fb); -- mctx->files++; -- } -- textDestroy(aText); -- } -- -- for(i = 0; i < multiparts; i++) -- if(messages[i]) -- messageDestroy(messages[i]); -- -- if(messages) -- free(messages); -+ if (messages) { -+ htmltextPart = getTextPart(messages, multiparts); -+ if (htmltextPart == -1) -+ htmltextPart = 0; -+ rc = parseEmailBody(messages[htmltextPart], aText, mctx, recursion_level + 1); -+ } -+ break; -+ default: -+ assert(0); -+ } -+ -+ if (mainMessage && (mainMessage != messageIn)) -+ messageDestroy(mainMessage); -+ -+ if (aText && (textIn == NULL)) { -+ if ((!infected) && (fb = fileblobCreate()) != NULL) { -+ cli_dbgmsg("Save non mime and/or text/plain part\n"); -+ fileblobSetFilename(fb, mctx->dir, "textpart"); -+ /*fileblobAddData(fb, "Received: by clamd (textpart)\n", 30);*/ -+ fileblobSetCTX(fb, mctx->ctx); -+ (void)textToFileblob(aText, fb, 1); -+ -+ fileblobDestroy(fb); -+ mctx->files++; -+ } -+ textDestroy(aText); -+ } -+ -+ for (i = 0; i < multiparts; i++) -+ if (messages[i]) -+ messageDestroy(messages[i]); -+ -+ if (messages) -+ free(messages); - - #if HAVE_JSON -- mctx->wrkobj = saveobj; -+ mctx->wrkobj = saveobj; - #endif -- return rc; -+ return rc; - -- case MESSAGE: -- /* -+ case MESSAGE: -+ /* - * Check for forbidden encodings - */ -- switch(messageGetEncoding(mainMessage)) { -- case NOENCODING: -- case EIGHTBIT: -- case BINARY: -- break; -- default: -- cli_dbgmsg("MIME type 'message' cannot be decoded\n"); -- break; -- } -- rc = FAIL; -- if((strcasecmp(mimeSubtype, "rfc822") == 0) || -- (strcasecmp(mimeSubtype, "delivery-status") == 0)) { -- message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table); -- if(m) { -- cli_dbgmsg("Decode rfc822\n"); -- -- messageSetCTX(m, mctx->ctx); -- -- if(mainMessage && (mainMessage != messageIn)) { -- messageDestroy(mainMessage); -- mainMessage = NULL; -- } else -- messageReset(mainMessage); -- if(messageGetBody(m)) -- rc = parseEmailBody(m, NULL, mctx, recursion_level + 1); -- -- messageDestroy(m); -- } -- break; -- } else if(strcasecmp(mimeSubtype, "disposition-notification") == 0) { -- /* RFC 2298 - handle like a normal email */ -- rc = OK; -- break; -- } else if(strcasecmp(mimeSubtype, "partial") == 0) { -- if(mctx->ctx->options->mail & CL_SCAN_MAIL_PARTIAL_MESSAGE) { -- /* RFC1341 message split over many emails */ -- if(rfc1341(mainMessage, mctx->dir) >= 0) -- rc = OK; -- } else { -- cli_warnmsg("Partial message received from MUA/MTA - message cannot be scanned\n"); -- } -- } else if(strcasecmp(mimeSubtype, "external-body") == 0) -- /* TODO */ -- cli_warnmsg("Attempt to send Content-type message/external-body trapped\n"); -- else -- cli_warnmsg("Unsupported message format `%s' - if you believe this file contains a virus, submit it to www.clamav.net\n", mimeSubtype); -- -- -- if(mainMessage && (mainMessage != messageIn)) -- messageDestroy(mainMessage); -- if(messages) -- free(messages); -+ switch (messageGetEncoding(mainMessage)) { -+ case NOENCODING: -+ case EIGHTBIT: -+ case BINARY: -+ break; -+ default: -+ cli_dbgmsg("MIME type 'message' cannot be decoded\n"); -+ break; -+ } -+ rc = FAIL; -+ if ((strcasecmp(mimeSubtype, "rfc822") == 0) || -+ (strcasecmp(mimeSubtype, "delivery-status") == 0)) { -+ message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table); -+ if (m) { -+ cli_dbgmsg("Decode rfc822\n"); -+ -+ messageSetCTX(m, mctx->ctx); -+ -+ if (mainMessage && (mainMessage != messageIn)) { -+ messageDestroy(mainMessage); -+ mainMessage = NULL; -+ } else -+ messageReset(mainMessage); -+ if (messageGetBody(m)) -+ rc = parseEmailBody(m, NULL, mctx, recursion_level + 1); -+ -+ messageDestroy(m); -+ } -+ break; -+ } else if (strcasecmp(mimeSubtype, "disposition-notification") == 0) { -+ /* RFC 2298 - handle like a normal email */ -+ rc = OK; -+ break; -+ } else if (strcasecmp(mimeSubtype, "partial") == 0) { -+ if (mctx->ctx->options->mail & CL_SCAN_MAIL_PARTIAL_MESSAGE) { -+ /* RFC1341 message split over many emails */ -+ if (rfc1341(mainMessage, mctx->dir) >= 0) -+ rc = OK; -+ } else { -+ cli_warnmsg("Partial message received from MUA/MTA - message cannot be scanned\n"); -+ } -+ } else if (strcasecmp(mimeSubtype, "external-body") == 0) -+ /* TODO */ -+ cli_warnmsg("Attempt to send Content-type message/external-body trapped\n"); -+ else -+ cli_warnmsg("Unsupported message format `%s' - if you believe this file contains a virus, submit it to www.clamav.net\n", mimeSubtype); -+ -+ if (mainMessage && (mainMessage != messageIn)) -+ messageDestroy(mainMessage); -+ if (messages) -+ free(messages); - #if HAVE_JSON -- mctx->wrkobj = saveobj; -+ mctx->wrkobj = saveobj; - #endif -- return rc; -+ return rc; - -- default: -- cli_dbgmsg("Message received with unknown mime encoding - assume application\n"); -- /* -+ default: -+ cli_dbgmsg("Message received with unknown mime encoding - assume application\n"); -+ /* - * Some Yahoo emails attach as - * Content-Type: X-unknown/unknown; - * instead of - * Content-Type: application/unknown; - * so let's try our best to salvage something - */ -- case APPLICATION: -- /*cptr = messageGetMimeSubtype(mainMessage); -+ case APPLICATION: -+ /*cptr = messageGetMimeSubtype(mainMessage); - - if((strcasecmp(cptr, "octet-stream") == 0) || - (strcasecmp(cptr, "x-msdownload") == 0)) {*/ -- { -- fb = messageToFileblob(mainMessage, mctx->dir, 1); -- -- if(fb) { -- cli_dbgmsg("Saving main message as attachment\n"); -- if(fileblobScanAndDestroy(fb) == CL_VIRUS) -- rc = VIRUS; -- mctx->files++; -- if(mainMessage != messageIn) { -- messageDestroy(mainMessage); -- mainMessage = NULL; -- } else -- messageReset(mainMessage); -- } -- } /*else -+ { -+ fb = messageToFileblob(mainMessage, mctx->dir, 1); -+ -+ if (fb) { -+ cli_dbgmsg("Saving main message as attachment\n"); -+ if (fileblobScanAndDestroy(fb) == CL_VIRUS) -+ rc = VIRUS; -+ mctx->files++; -+ if (mainMessage != messageIn) { -+ messageDestroy(mainMessage); -+ mainMessage = NULL; -+ } else -+ messageReset(mainMessage); -+ } -+ } /*else - cli_warnmsg("Discarded application not sent as attachment\n");*/ -- break; -- -- case AUDIO: -- case VIDEO: -- case IMAGE: -- break; -- } -- -- if(messages) { -- /* "can't happen" */ -- cli_warnmsg("messages != NULL\n"); -- free(messages); -- } -- } -- -- if(aText && (textIn == NULL)) { -- /* Look for a bounce in the text (non mime encoded) portion */ -- const text *t; -- /* isBounceStart() is expensive, reduce the number of calls */ -- bool lookahead_definately_is_bounce = FALSE; -- -- for(t = aText; t && (rc != VIRUS); t = t->t_next) { -- const line_t *l = t->t_line; -- const text *lookahead, *topofbounce; -- const char *s; -- bool inheader; -- -- if(l == NULL) { -- /* assert(lookahead_definately_is_bounce == FALSE) */ -- continue; -- } -- -- if(lookahead_definately_is_bounce) -- lookahead_definately_is_bounce = FALSE; -- else if(!isBounceStart(mctx, lineGetData(l))) -- continue; -- -- lookahead = t->t_next; -- if(lookahead) { -- if(isBounceStart(mctx, lineGetData(lookahead->t_line))) { -- lookahead_definately_is_bounce = TRUE; -- /* don't save worthless header lines */ -- continue; -- } -- } else /* don't save a single liner */ -- break; -- -- /* -+ break; -+ -+ case AUDIO: -+ case VIDEO: -+ case IMAGE: -+ break; -+ } -+ -+ if (messages) { -+ /* "can't happen" */ -+ cli_warnmsg("messages != NULL\n"); -+ free(messages); -+ } -+ } -+ -+ if (aText && (textIn == NULL)) { -+ /* Look for a bounce in the text (non mime encoded) portion */ -+ const text *t; -+ /* isBounceStart() is expensive, reduce the number of calls */ -+ bool lookahead_definately_is_bounce = FALSE; -+ -+ for (t = aText; t && (rc != VIRUS); t = t->t_next) { -+ const line_t *l = t->t_line; -+ const text *lookahead, *topofbounce; -+ const char *s; -+ bool inheader; -+ -+ if (l == NULL) { -+ /* assert(lookahead_definately_is_bounce == FALSE) */ -+ continue; -+ } -+ -+ if (lookahead_definately_is_bounce) -+ lookahead_definately_is_bounce = FALSE; -+ else if (!isBounceStart(mctx, lineGetData(l))) -+ continue; -+ -+ lookahead = t->t_next; -+ if (lookahead) { -+ if (isBounceStart(mctx, lineGetData(lookahead->t_line))) { -+ lookahead_definately_is_bounce = TRUE; -+ /* don't save worthless header lines */ -+ continue; -+ } -+ } else /* don't save a single liner */ -+ break; -+ -+ /* - * We've found what looks like the start of a bounce - * message. Only bother saving if it really is a bounce - * message, this helps to speed up scanning of ping-pong - * messages that have lots of bounces within bounces in - * them - */ -- for(; lookahead; lookahead = lookahead->t_next) { -- l = lookahead->t_line; -- -- if(l == NULL) -- break; -- s = lineGetData(l); -- if(strncasecmp(s, "Content-Type:", 13) == 0) { -- /* -+ for (; lookahead; lookahead = lookahead->t_next) { -+ l = lookahead->t_line; -+ -+ if (l == NULL) -+ break; -+ s = lineGetData(l); -+ if (strncasecmp(s, "Content-Type:", 13) == 0) { -+ /* - * Don't bother with text/plain or - * text/html - */ -- if(cli_strcasestr(s, "text/plain") != NULL) -- /* -+ if (cli_strcasestr(s, "text/plain") != NULL) -+ /* - * Don't bother to save the - * unuseful part, read past - * the headers then we'll go - * on to look for the next - * bounce message - */ -- continue; -- if((!doPhishingScan) && -- (cli_strcasestr(s, "text/html") != NULL)) -- continue; -- break; -- } -- } -- -- if(lookahead && (lookahead->t_line == NULL)) { -- cli_dbgmsg("Non mime part bounce message is not mime encoded, so it will not be scanned\n"); -- t = lookahead; -- /* look for next bounce message */ -- continue; -- } -- -- /* -+ continue; -+ if ((!doPhishingScan) && -+ (cli_strcasestr(s, "text/html") != NULL)) -+ continue; -+ break; -+ } -+ } -+ -+ if (lookahead && (lookahead->t_line == NULL)) { -+ cli_dbgmsg("Non mime part bounce message is not mime encoded, so it will not be scanned\n"); -+ t = lookahead; -+ /* look for next bounce message */ -+ continue; -+ } -+ -+ /* - * Prescan the bounce message to see if there's likely - * to be anything nasty. - * This algorithm is hand crafted and may be breakable -@@ -2275,155 +2252,156 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - * significantly speeds up the scanning of multiple - * bounces (i.e. bounces within many bounces) - */ -- for(; lookahead; lookahead = lookahead->t_next) { -- l = lookahead->t_line; -- -- if(l) { -- s = lineGetData(l); -- if((strncasecmp(s, "Content-Type:", 13) == 0) && -- (strstr(s, "multipart/") == NULL) && -- (strstr(s, "message/rfc822") == NULL) && -- (strstr(s, "text/plain") == NULL)) -- break; -- } -- } -- if(lookahead == NULL) { -- cli_dbgmsg("cli_mbox: I believe it's plain text which must be clean\n"); -- /* nothing here, move along please */ -- break; -- } -- if((fb = fileblobCreate()) == NULL) -- break; -- cli_dbgmsg("Save non mime part bounce message\n"); -- fileblobSetFilename(fb, mctx->dir, "bounce"); -- fileblobAddData(fb, (const unsigned char *)"Received: by clamd (bounce)\n", 28); -- fileblobSetCTX(fb, mctx->ctx); -- -- inheader = TRUE; -- topofbounce = NULL; -- do { -- l = t->t_line; -- -- if(l == NULL) { -- if(inheader) { -- inheader = FALSE; -- topofbounce = t; -- } -- } else { -- s = lineGetData(l); -- fileblobAddData(fb, (const unsigned char *)s, strlen(s)); -- } -- fileblobAddData(fb, (const unsigned char *)"\n", 1); -- lookahead = t->t_next; -- if(lookahead == NULL) -- break; -- t = lookahead; -- l = t->t_line; -- if((!inheader) && l) { -- s = lineGetData(l); -- if(isBounceStart(mctx, s)) { -- cli_dbgmsg("Found the start of another bounce candidate (%s)\n", s); -- lookahead_definately_is_bounce = TRUE; -- break; -- } -- } -- } while(!fileblobInfected(fb)); -- -- if(fileblobScanAndDestroy(fb) == CL_VIRUS) -- rc = VIRUS; -- mctx->files++; -- -- if(topofbounce) -- t = topofbounce; -- } -- textDestroy(aText); -- aText = NULL; -- } -- -- /* -+ for (; lookahead; lookahead = lookahead->t_next) { -+ l = lookahead->t_line; -+ -+ if (l) { -+ s = lineGetData(l); -+ if ((strncasecmp(s, "Content-Type:", 13) == 0) && -+ (strstr(s, "multipart/") == NULL) && -+ (strstr(s, "message/rfc822") == NULL) && -+ (strstr(s, "text/plain") == NULL)) -+ break; -+ } -+ } -+ if (lookahead == NULL) { -+ cli_dbgmsg("cli_mbox: I believe it's plain text which must be clean\n"); -+ /* nothing here, move along please */ -+ break; -+ } -+ if ((fb = fileblobCreate()) == NULL) -+ break; -+ cli_dbgmsg("Save non mime part bounce message\n"); -+ fileblobSetFilename(fb, mctx->dir, "bounce"); -+ fileblobAddData(fb, (const unsigned char *)"Received: by clamd (bounce)\n", 28); -+ fileblobSetCTX(fb, mctx->ctx); -+ -+ inheader = TRUE; -+ topofbounce = NULL; -+ do { -+ l = t->t_line; -+ -+ if (l == NULL) { -+ if (inheader) { -+ inheader = FALSE; -+ topofbounce = t; -+ } -+ } else { -+ s = lineGetData(l); -+ fileblobAddData(fb, (const unsigned char *)s, strlen(s)); -+ } -+ fileblobAddData(fb, (const unsigned char *)"\n", 1); -+ lookahead = t->t_next; -+ if (lookahead == NULL) -+ break; -+ t = lookahead; -+ l = t->t_line; -+ if ((!inheader) && l) { -+ s = lineGetData(l); -+ if (isBounceStart(mctx, s)) { -+ cli_dbgmsg("Found the start of another bounce candidate (%s)\n", s); -+ lookahead_definately_is_bounce = TRUE; -+ break; -+ } -+ } -+ } while (!fileblobInfected(fb)); -+ -+ if (fileblobScanAndDestroy(fb) == CL_VIRUS) -+ rc = VIRUS; -+ mctx->files++; -+ -+ if (topofbounce) -+ t = topofbounce; -+ } -+ textDestroy(aText); -+ aText = NULL; -+ } -+ -+ /* - * No attachments - scan the text portions, often files - * are hidden in HTML code - */ -- if(mainMessage && (rc != VIRUS)) { -- text *t_line; -+ if (mainMessage && (rc != VIRUS)) { -+ text *t_line; - -- /* -+ /* - * Look for uu-encoded main file - */ -- if(mainMessage->body_first != NULL && -- (encodingLine(mainMessage) != NULL) && -- ((t_line = bounceBegin(mainMessage)) != NULL)) -- rc = (exportBounceMessage(mctx, t_line) == CL_VIRUS) ? VIRUS : OK; -- else { -- bool saveIt; -- -- if(messageGetMimeType(mainMessage) == MESSAGE) -- /* -+ if (mainMessage->body_first != NULL && -+ (encodingLine(mainMessage) != NULL) && -+ ((t_line = bounceBegin(mainMessage)) != NULL)) -+ rc = (exportBounceMessage(mctx, t_line) == CL_VIRUS) ? VIRUS : OK; -+ else { -+ bool saveIt; -+ -+ if (messageGetMimeType(mainMessage) == MESSAGE) -+ /* - * Quick peek, if the encapsulated - * message has no - * content encoding statement don't - * bother saving to scan, it's safe - */ -- saveIt = (bool)(encodingLine(mainMessage) != NULL); -- else if(mainMessage->body_last != NULL && (t_line = encodingLine(mainMessage)) != NULL) { -- /* -+ saveIt = (bool)(encodingLine(mainMessage) != NULL); -+ else if (mainMessage->body_last != NULL && (t_line = encodingLine(mainMessage)) != NULL) { -+ /* - * Some bounces include the message - * body without the headers. - * FIXME: Unfortunately this generates a - * lot of false positives that a bounce - * has been found when it hasn't. - */ -- if((fb = fileblobCreate()) != NULL) { -- cli_dbgmsg("Found a bounce message with no header at '%s'\n", -- lineGetData(t_line->t_line)); -- fileblobSetFilename(fb, mctx->dir, "bounce"); -- fileblobAddData(fb, -- (const unsigned char *)"Received: by clamd (bounce)\n", -- 28); -- -- fileblobSetCTX(fb, mctx->ctx); -- if(fileblobScanAndDestroy(textToFileblob(t_line, fb, 1)) == CL_VIRUS) -- rc = VIRUS; -- mctx->files++; -- } -- saveIt = FALSE; -- } else -- /* -+ if ((fb = fileblobCreate()) != NULL) { -+ cli_dbgmsg("Found a bounce message with no header at '%s'\n", -+ lineGetData(t_line->t_line)); -+ fileblobSetFilename(fb, mctx->dir, "bounce"); -+ fileblobAddData(fb, -+ (const unsigned char *)"Received: by clamd (bounce)\n", -+ 28); -+ -+ fileblobSetCTX(fb, mctx->ctx); -+ if (fileblobScanAndDestroy(textToFileblob(t_line, fb, 1)) == CL_VIRUS) -+ rc = VIRUS; -+ mctx->files++; -+ } -+ saveIt = FALSE; -+ } else -+ /* - * Save the entire text portion, - * since it it may be an HTML file with - * a JavaScript virus or a phish - */ -- saveIt = TRUE; -- -- if(saveIt) { -- cli_dbgmsg("Saving text part to scan, rc = %d\n", -- (int)rc); -- if(saveTextPart(mctx, mainMessage, 1) == CL_VIRUS) -- rc = VIRUS; -- -- if(mainMessage != messageIn) { -- messageDestroy(mainMessage); -- mainMessage = NULL; -- } else -- messageReset(mainMessage); -- } -- } -- } /*else -- rc = OK_ATTACHMENTS_NOT_SAVED; */ /* nothing saved */ -- -- if(mainMessage && (mainMessage != messageIn)) -- messageDestroy(mainMessage); -- -- if((rc != FAIL) && infected) -- rc = VIRUS; -+ saveIt = TRUE; -+ -+ if (saveIt) { -+ cli_dbgmsg("Saving text part to scan, rc = %d\n", -+ (int)rc); -+ if (saveTextPart(mctx, mainMessage, 1) == CL_VIRUS) -+ rc = VIRUS; -+ -+ if (mainMessage != messageIn) { -+ messageDestroy(mainMessage); -+ mainMessage = NULL; -+ } else -+ messageReset(mainMessage); -+ } -+ } -+ } /*else -+ rc = OK_ATTACHMENTS_NOT_SAVED; */ -+ /* nothing saved */ -+ -+ if (mainMessage && (mainMessage != messageIn)) -+ messageDestroy(mainMessage); -+ -+ if ((rc != FAIL) && infected) -+ rc = VIRUS; - - #if HAVE_JSON -- mctx->wrkobj = saveobj; -+ mctx->wrkobj = saveobj; - #endif - -- cli_dbgmsg("parseEmailBody() returning %d\n", (int)rc); -+ cli_dbgmsg("parseEmailBody() returning %d\n", (int)rc); - -- return rc; -+ return rc; - } - - /* -@@ -2434,16 +2412,16 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re - static int - boundaryStart(const char *line, const char *boundary) - { -- const char *ptr; -- char *out; -- int rc; -- char buf[RFC2821LENGTH + 1]; -+ const char *ptr; -+ char *out; -+ int rc; -+ char buf[RFC2821LENGTH + 1]; - char *newline; - -- if(line == NULL || *line == '\0') -- return 0; /* empty line */ -- if(boundary == NULL) -- return 0; -+ if (line == NULL || *line == '\0') -+ return 0; /* empty line */ -+ if (boundary == NULL) -+ return 0; - - newline = strdup(line); - if (!(newline)) -@@ -2460,39 +2438,39 @@ boundaryStart(const char *line, const char *boundary) - if (newline != line) - cli_chomp(newline); - -- /* cli_dbgmsg("boundaryStart: line = '%s' boundary = '%s'\n", line, boundary); */ -+ /* cli_dbgmsg("boundaryStart: line = '%s' boundary = '%s'\n", line, boundary); */ - -- if((*newline != '-') && (*newline != '(')) { -+ if ((*newline != '-') && (*newline != '(')) { - if (newline != line) - free(newline); -- return 0; -+ return 0; - } - -- if(strchr(newline, '-') == NULL) { -+ if (strchr(newline, '-') == NULL) { - if (newline != line) - free(newline); -- return 0; -+ return 0; - } - -- if(strlen(newline) <= sizeof(buf)) { -- out = NULL; -- ptr = rfc822comments(newline, buf); -- } else -- ptr = out = rfc822comments(newline, NULL); -+ if (strlen(newline) <= sizeof(buf)) { -+ out = NULL; -+ ptr = rfc822comments(newline, buf); -+ } else -+ ptr = out = rfc822comments(newline, NULL); - -- if(ptr == NULL) -- ptr = newline; -+ if (ptr == NULL) -+ ptr = newline; - -- if((*ptr++ != '-') || (*ptr == '\0')) { -- if(out) -- free(out); -+ if ((*ptr++ != '-') || (*ptr == '\0')) { -+ if (out) -+ free(out); - if (newline != line) - free(newline); - -- return 0; -- } -+ return 0; -+ } - -- /* -+ /* - * Gibe.B3 is broken, it has: - * boundary="---- =_NextPart_000_01C31177.9DC7C000" - * but it's boundaries look like -@@ -2512,45 +2490,45 @@ boundaryStart(const char *line, const char *boundary) - * they're not. Irrespective of whatever RFC2822 says, we need to find - * viruses in both types of mails. - */ -- if((strstr(&ptr[1], boundary) != NULL) || (strstr(newline, boundary) != NULL)) { -- const char *k = ptr; -+ if ((strstr(&ptr[1], boundary) != NULL) || (strstr(newline, boundary) != NULL)) { -+ const char *k = ptr; - -- /* -+ /* - * We need to ensure that we don't match --11=-=-=11 when - * looking for --1=-=-=1 in well behaved headers, that's a - * false positive problem mentioned above - */ -- rc = 0; -- do -- if(strcmp(++k, boundary) == 0) { -- rc = 1; -- break; -- } -- while(*k == '-'); -- if(rc == 0) { -- k = &line[1]; -- do -- if(strcmp(++k, boundary) == 0) { -- rc = 1; -- break; -- } -- while(*k == '-'); -- } -- } else if(*ptr++ != '-') -- rc = 0; -- else -- rc = (strcasecmp(ptr, boundary) == 0); -- -- if(out) -- free(out); -- -- if(rc == 1) -- cli_dbgmsg("boundaryStart: found %s in %s\n", boundary, line); -+ rc = 0; -+ do -+ if (strcmp(++k, boundary) == 0) { -+ rc = 1; -+ break; -+ } -+ while (*k == '-'); -+ if (rc == 0) { -+ k = &line[1]; -+ do -+ if (strcmp(++k, boundary) == 0) { -+ rc = 1; -+ break; -+ } -+ while (*k == '-'); -+ } -+ } else if (*ptr++ != '-') -+ rc = 0; -+ else -+ rc = (strcasecmp(ptr, boundary) == 0); -+ -+ if (out) -+ free(out); -+ -+ if (rc == 1) -+ cli_dbgmsg("boundaryStart: found %s in %s\n", boundary, line); - - if (newline != line) - free(newline); - -- return rc; -+ return rc; - } - - /* -@@ -2561,15 +2539,15 @@ boundaryStart(const char *line, const char *boundary) - static int - boundaryEnd(const char *line, const char *boundary) - { -- size_t len; -+ size_t len; - char *newline, *p, *p2; - -- if(line == NULL || *line == '\0') -- return 0; -+ if (line == NULL || *line == '\0') -+ return 0; - - p = newline = strdup(line); - if (!(newline)) { -- p = (char *)line; -+ p = (char *)line; - newline = (char *)line; - } - -@@ -2580,59 +2558,59 @@ boundaryEnd(const char *line, const char *boundary) - *(p2--) = '\0'; - } - -- /* cli_dbgmsg("boundaryEnd: line = '%s' boundary = '%s'\n", newline, boundary); */ -+ /* cli_dbgmsg("boundaryEnd: line = '%s' boundary = '%s'\n", newline, boundary); */ - -- if(*p++ != '-') { -+ if (*p++ != '-') { - if (newline != line) - free(newline); -- return 0; -+ return 0; - } - -- if(*p++ != '-') { -+ if (*p++ != '-') { - if (newline != line) - free(newline); - -- return 0; -+ return 0; - } - -- len = strlen(boundary); -- if(strncasecmp(p, boundary, len) != 0) { -+ len = strlen(boundary); -+ if (strncasecmp(p, boundary, len) != 0) { - if (newline != line) - free(newline); - -- return 0; -+ return 0; - } -- /* -+ /* - * Use < rather than == because some broken mails have white - * space after the boundary - */ -- if(strlen(p) < (len + 2)) { -+ if (strlen(p) < (len + 2)) { - if (newline != line) - free(newline); - -- return 0; -+ return 0; - } - -- p = &p[len]; -- if(*p++ != '-') { -+ p = &p[len]; -+ if (*p++ != '-') { - if (newline != line) - free(newline); - -- return 0; -+ return 0; - } - -- if(*p == '-') { -- /* cli_dbgmsg("boundaryEnd: found %s in %s\n", boundary, p); */ -+ if (*p == '-') { -+ /* cli_dbgmsg("boundaryEnd: found %s in %s\n", boundary, p); */ - if (newline != line) - free(newline); - -- return 1; -- } -+ return 1; -+ } - - if (newline != line) - free(newline); - -- return 0; -+ return 0; - } - - /* -@@ -2641,34 +2619,34 @@ boundaryEnd(const char *line, const char *boundary) - static int - initialiseTables(table_t **rfc821Table, table_t **subtypeTable) - { -- const struct tableinit *tableinit; -+ const struct tableinit *tableinit; - -- /* -+ /* - * Initialise the various look up tables - */ -- *rfc821Table = tableCreate(); -- assert(*rfc821Table != NULL); -- -- for(tableinit = rfc821headers; tableinit->key; tableinit++) -- if(tableInsert(*rfc821Table, tableinit->key, tableinit->value) < 0) { -- tableDestroy(*rfc821Table); -- *rfc821Table = NULL; -- return -1; -- } -- -- *subtypeTable = tableCreate(); -- assert(*subtypeTable != NULL); -- -- for(tableinit = mimeSubtypes; tableinit->key; tableinit++) -- if(tableInsert(*subtypeTable, tableinit->key, tableinit->value) < 0) { -- tableDestroy(*rfc821Table); -- tableDestroy(*subtypeTable); -- *rfc821Table = NULL; -- *subtypeTable = NULL; -- return -1; -- } -- -- return 0; -+ *rfc821Table = tableCreate(); -+ assert(*rfc821Table != NULL); -+ -+ for (tableinit = rfc821headers; tableinit->key; tableinit++) -+ if (tableInsert(*rfc821Table, tableinit->key, tableinit->value) < 0) { -+ tableDestroy(*rfc821Table); -+ *rfc821Table = NULL; -+ return -1; -+ } -+ -+ *subtypeTable = tableCreate(); -+ assert(*subtypeTable != NULL); -+ -+ for (tableinit = mimeSubtypes; tableinit->key; tableinit++) -+ if (tableInsert(*subtypeTable, tableinit->key, tableinit->value) < 0) { -+ tableDestroy(*rfc821Table); -+ tableDestroy(*subtypeTable); -+ *rfc821Table = NULL; -+ *subtypeTable = NULL; -+ return -1; -+ } -+ -+ return 0; - } - - /* -@@ -2682,17 +2660,17 @@ initialiseTables(table_t **rfc821Table, table_t **subtypeTable) - static int - getTextPart(message *const messages[], size_t size) - { -- size_t i; -- int textpart = -1; -+ size_t i; -+ int textpart = -1; - -- for(i = 0; i < size; i++) -- if(messages[i] && (messageGetMimeType(messages[i]) == TEXT)) { -- if(strcasecmp(messageGetMimeSubtype(messages[i]), "html") == 0) -- return (int)i; -- textpart = (int)i; -- } -+ for (i = 0; i < size; i++) -+ if (messages[i] && (messageGetMimeType(messages[i]) == TEXT)) { -+ if (strcasecmp(messageGetMimeSubtype(messages[i]), "html") == 0) -+ return (int)i; -+ textpart = (int)i; -+ } - -- return textpart; -+ return textpart; - } - - /* -@@ -2712,31 +2690,31 @@ getTextPart(message *const messages[], size_t size) - static size_t - strip(char *buf, int len) - { -- register char *ptr; -- register size_t i; -- -- if((buf == NULL) || (len <= 0)) -- return 0; -- -- i = strlen(buf); -- if(len > (int)(i + 1)) -- return i; -- ptr = &buf[--len]; -- --#if defined(UNIX) || defined(C_LINUX) || defined(C_DARWIN) /* watch - it may be in shared text area */ -- do -- if(*ptr) -- *ptr = '\0'; -- while((--len >= 0) && (!isgraph(*--ptr)) && (*ptr != '\n') && (*ptr != '\r')); --#else /* more characters can be displayed on DOS */ -- do --#ifndef REAL_MODE_DOS -- if(*ptr) /* C8.0 puts into a text area */ -+ register char *ptr; -+ register size_t i; -+ -+ if ((buf == NULL) || (len <= 0)) -+ return 0; -+ -+ i = strlen(buf); -+ if (len > (int)(i + 1)) -+ return i; -+ ptr = &buf[--len]; -+ -+#if defined(UNIX) || defined(C_LINUX) || defined(C_DARWIN) /* watch - it may be in shared text area */ -+ do -+ if (*ptr) -+ *ptr = '\0'; -+ while ((--len >= 0) && (!isgraph(*--ptr)) && (*ptr != '\n') && (*ptr != '\r')); -+#else /* more characters can be displayed on DOS */ -+ do -+#ifndef REAL_MODE_DOS -+ if (*ptr) /* C8.0 puts into a text area */ - #endif -- *ptr = '\0'; -- while((--len >= 0) && ((*--ptr == '\0') || isspace((int)(*ptr & 0xFF)))); -+ *ptr = '\0'; -+ while ((--len >= 0) && ((*--ptr == '\0') || isspace((int)(*ptr & 0xFF)))); - #endif -- return((size_t)(len + 1)); -+ return ((size_t)(len + 1)); - } - - /* -@@ -2746,10 +2724,10 @@ strip(char *buf, int len) - size_t - strstrip(char *s) - { -- if(s == (char *)NULL) -- return(0); -+ if (s == (char *)NULL) -+ return (0); - -- return(strip(s, strlen(s) + 1)); -+ return (strip(s, strlen(s) + 1)); - } - - /* -@@ -2758,38 +2736,38 @@ strstrip(char *s) - static int - parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg) - { -- char *copy, *p, *buf; -- const char *ptr; -- int commandNumber; -+ char *copy, *p, *buf; -+ const char *ptr; -+ int commandNumber; - -- cli_dbgmsg("parseMimeHeader: cmd='%s', arg='%s'\n", cmd, arg); -+ cli_dbgmsg("parseMimeHeader: cmd='%s', arg='%s'\n", cmd, arg); - -- copy = rfc822comments(cmd, NULL); -- if(copy) { -- commandNumber = tableFind(rfc821Table, copy); -- free(copy); -- } else -- commandNumber = tableFind(rfc821Table, cmd); -+ copy = rfc822comments(cmd, NULL); -+ if (copy) { -+ commandNumber = tableFind(rfc821Table, copy); -+ free(copy); -+ } else -+ commandNumber = tableFind(rfc821Table, cmd); - -- copy = rfc822comments(arg, NULL); -+ copy = rfc822comments(arg, NULL); - -- if(copy) -- ptr = copy; -- else -- ptr = arg; -+ if (copy) -+ ptr = copy; -+ else -+ ptr = arg; - -- buf = NULL; -+ buf = NULL; - -- switch(commandNumber) { -- case CONTENT_TYPE: -- /* -+ switch (commandNumber) { -+ case CONTENT_TYPE: -+ /* - * Fix for non RFC1521 compliant mailers - * that send content-type: Text instead - * of content-type: Text/Plain, or - * just simply "Content-Type:" - */ -- if(arg == NULL) -- /* -+ if (arg == NULL) -+ /* - * According to section 4 of RFC1521: - * "Note also that a subtype specification is - * MANDATORY. There are no default subtypes" -@@ -2798,155 +2776,155 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c - * for the subtype because virus writers and - * email client writers don't get it right - */ -- cli_dbgmsg("Empty content-type received, no subtype specified, assuming text/plain; charset=us-ascii\n"); -- else if(strchr(ptr, '/') == NULL) -- /* -+ cli_dbgmsg("Empty content-type received, no subtype specified, assuming text/plain; charset=us-ascii\n"); -+ else if (strchr(ptr, '/') == NULL) -+ /* - * Empty field, such as - * Content-Type: - * which I believe is illegal according to - * RFC1521 - */ -- cli_dbgmsg("Invalid content-type '%s' received, no subtype specified, assuming text/plain; charset=us-ascii\n", ptr); -- else { -- int i; -+ cli_dbgmsg("Invalid content-type '%s' received, no subtype specified, assuming text/plain; charset=us-ascii\n", ptr); -+ else { -+ int i; - -- buf = cli_malloc(strlen(ptr) + 1); -- if(buf == NULL) { -+ buf = cli_malloc(strlen(ptr) + 1); -+ if (buf == NULL) { - cli_errmsg("parseMimeHeader: Unable to allocate memory for buf %llu\n", (long long unsigned)(strlen(ptr) + 1)); -- if(copy) -- free(copy); -- return -1; -- } -- /* -+ if (copy) -+ free(copy); -+ return -1; -+ } -+ /* - * Some clients are broken and - * put white space after the ; - */ -- if(*arg == '/') { -- cli_dbgmsg("Content-type '/' received, assuming application/octet-stream\n"); -- messageSetMimeType(m, "application"); -- messageSetMimeSubtype(m, "octet-stream"); -- } else { -- /* -+ if (*arg == '/') { -+ cli_dbgmsg("Content-type '/' received, assuming application/octet-stream\n"); -+ messageSetMimeType(m, "application"); -+ messageSetMimeSubtype(m, "octet-stream"); -+ } else { -+ /* - * The content type could be in quotes: - * Content-Type: "multipart/mixed" - * FIXME: this is a hack in that ignores - * the quotes, it doesn't handle - * them properly - */ -- while(isspace(*ptr)) -- ptr++; -- if(ptr[0] == '\"') -- ptr++; -+ while (isspace(*ptr)) -+ ptr++; -+ if (ptr[0] == '\"') -+ ptr++; - -- if(ptr[0] != '/') { -- char *s; -+ if (ptr[0] != '/') { -+ char *s; - #ifdef CL_THREAD_SAFE -- char *strptr = NULL; -+ char *strptr = NULL; - #endif - -- s = cli_strtokbuf(ptr, 0, ";", buf); -- /* -+ s = cli_strtokbuf(ptr, 0, ";", buf); -+ /* - * Handle - * Content-Type: foo/bar multipart/mixed - * and - * Content-Type: multipart/mixed foo/bar - */ -- if(s && *s) { -- char *buf2 = cli_strdup(buf); -- -- if(buf2 == NULL) { -- if(copy) -- free(copy); -- free(buf); -- return -1; -- } -- for(;;) { --#ifdef CL_THREAD_SAFE -- int set = messageSetMimeType(m, strtok_r(s, "/", &strptr)); -+ if (s && *s) { -+ char *buf2 = cli_strdup(buf); -+ -+ if (buf2 == NULL) { -+ if (copy) -+ free(copy); -+ free(buf); -+ return -1; -+ } -+ for (;;) { -+#ifdef CL_THREAD_SAFE -+ int set = messageSetMimeType(m, strtok_r(s, "/", &strptr)); - #else -- int set = messageSetMimeType(m, strtok(s, "/")); -+ int set = messageSetMimeType(m, strtok(s, "/")); - #endif - --#ifdef CL_THREAD_SAFE -- s = strtok_r(NULL, ";", &strptr); -+#ifdef CL_THREAD_SAFE -+ s = strtok_r(NULL, ";", &strptr); - #else -- s = strtok(NULL, ";"); -+ s = strtok(NULL, ";"); - #endif -- if(s == NULL) -- break; -- if(set) { -- size_t len = strstrip(s) - 1; -- if(s[len] == '\"') { -- s[len] = '\0'; -- len = strstrip(s); -- } -- if(len) { -- if(strchr(s, ' ')) -- messageSetMimeSubtype(m, -- cli_strtokbuf(s, 0, " ", buf2)); -- else -- messageSetMimeSubtype(m, s); -- } -- } -- -- while(*s && !isspace(*s)) -- s++; -- if(*s++ == '\0') -- break; -- if(*s == '\0') -- break; -- } -- free(buf2); -- } -- } -- } -- -- /* -+ if (s == NULL) -+ break; -+ if (set) { -+ size_t len = strstrip(s) - 1; -+ if (s[len] == '\"') { -+ s[len] = '\0'; -+ len = strstrip(s); -+ } -+ if (len) { -+ if (strchr(s, ' ')) -+ messageSetMimeSubtype(m, -+ cli_strtokbuf(s, 0, " ", buf2)); -+ else -+ messageSetMimeSubtype(m, s); -+ } -+ } -+ -+ while (*s && !isspace(*s)) -+ s++; -+ if (*s++ == '\0') -+ break; -+ if (*s == '\0') -+ break; -+ } -+ free(buf2); -+ } -+ } -+ } -+ -+ /* - * Add in all rest of the the arguments. - * e.g. if the header is this: - * Content-Type:', arg='multipart/mixed; boundary=foo - * we find the boundary argument set it - */ -- i = 1; -- while(cli_strtokbuf(ptr, i++, ";", buf) != NULL) { -- cli_dbgmsg("mimeArgs = '%s'\n", buf); -- -- messageAddArguments(m, buf); -- } -- } -- break; -- case CONTENT_TRANSFER_ENCODING: -- messageSetEncoding(m, ptr); -- break; -- case CONTENT_DISPOSITION: -- buf = cli_malloc(strlen(ptr) + 1); -- if(buf == NULL) { -+ i = 1; -+ while (cli_strtokbuf(ptr, i++, ";", buf) != NULL) { -+ cli_dbgmsg("mimeArgs = '%s'\n", buf); -+ -+ messageAddArguments(m, buf); -+ } -+ } -+ break; -+ case CONTENT_TRANSFER_ENCODING: -+ messageSetEncoding(m, ptr); -+ break; -+ case CONTENT_DISPOSITION: -+ buf = cli_malloc(strlen(ptr) + 1); -+ if (buf == NULL) { - cli_errmsg("parseMimeHeader: Unable to allocate memory for buf %llu\n", (long long unsigned)(strlen(ptr) + 1)); -- if(copy) -- free(copy); -- return -1; -- } -- p = cli_strtokbuf(ptr, 0, ";", buf); -- if(p && *p) { -- messageSetDispositionType(m, p); -- messageAddArgument(m, cli_strtokbuf(ptr, 1, ";", buf)); -- } -- if(!messageHasFilename(m)) -- /* -+ if (copy) -+ free(copy); -+ return -1; -+ } -+ p = cli_strtokbuf(ptr, 0, ";", buf); -+ if (p && *p) { -+ messageSetDispositionType(m, p); -+ messageAddArgument(m, cli_strtokbuf(ptr, 1, ";", buf)); -+ } -+ if (!messageHasFilename(m)) -+ /* - * Handle this type of header, without - * a filename (e.g. some Worm.Torvil.D) - * Content-ID: - * Content-Transfer-Encoding: base64 - * Content-Disposition: attachment - */ -- messageAddArgument(m, "filename=unknown"); -- } -- if(copy) -- free(copy); -- if(buf) -- free(buf); -- -- return 0; -+ messageAddArgument(m, "filename=unknown"); -+ } -+ if (copy) -+ free(copy); -+ if (buf) -+ free(buf); -+ -+ return 0; - } - - /* -@@ -2955,19 +2933,19 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c - static int - saveTextPart(mbox_ctx *mctx, message *m, int destroy_text) - { -- fileblob *fb; -+ fileblob *fb; - -- messageAddArgument(m, "filename=textportion"); -- if((fb = messageToFileblob(m, mctx->dir, destroy_text)) != NULL) { -- /* -+ messageAddArgument(m, "filename=textportion"); -+ if ((fb = messageToFileblob(m, mctx->dir, destroy_text)) != NULL) { -+ /* - * Save main part to scan that - */ -- cli_dbgmsg("Saving main message\n"); -+ cli_dbgmsg("Saving main message\n"); - -- mctx->files++; -- return fileblobScanAndDestroy(fb); -- } -- return CL_ETMPFILE; -+ mctx->files++; -+ return fileblobScanAndDestroy(fb); -+ } -+ return CL_ETMPFILE; - } - - /* -@@ -2981,73 +2959,74 @@ saveTextPart(mbox_ctx *mctx, message *m, int destroy_text) - static char * - rfc822comments(const char *in, char *out) - { -- const char *iptr; -- char *optr; -- int backslash, inquote, commentlevel; -+ const char *iptr; -+ char *optr; -+ int backslash, inquote, commentlevel; - -- if(in == NULL) -- return NULL; -+ if (in == NULL) -+ return NULL; - -- if(strchr(in, '(') == NULL) -- return NULL; -+ if (strchr(in, '(') == NULL) -+ return NULL; - -- assert(out != in); -+ assert(out != in); - -- while(isspace(*in)) -- in++; -+ while (isspace(*in)) -+ in++; - -- if(out == NULL) { -- out = cli_malloc(strlen(in) + 1); -- if(out == NULL) { -+ if (out == NULL) { -+ out = cli_malloc(strlen(in) + 1); -+ if (out == NULL) { - cli_errmsg("rfc822comments: Unable to allocate memory for out %llu\n", (long long unsigned)(strlen(in) + 1)); -- return NULL; -+ return NULL; - } -- } -- -- backslash = commentlevel = inquote = 0; -- optr = out; -- -- cli_dbgmsg("rfc822comments: contains a comment\n"); -- -- for(iptr = in; *iptr; iptr++) -- if(backslash) { -- if(commentlevel == 0) -- *optr++ = *iptr; -- backslash = 0; -- } else switch(*iptr) { -- case '\\': -- backslash = 1; -- break; -- case '\"': -- *optr++ = '\"'; -- inquote = !inquote; -- break; -- case '(': -- if(inquote) -- *optr++ = '('; -- else -- commentlevel++; -- break; -- case ')': -- if(inquote) -- *optr++ = ')'; -- else if(commentlevel > 0) -- commentlevel--; -- break; -- default: -- if(commentlevel == 0) -- *optr++ = *iptr; -- } -- -- if(backslash) /* last character was a single backslash */ -- *optr++ = '\\'; -- *optr = '\0'; -- -- /*strstrip(out);*/ -- -- cli_dbgmsg("rfc822comments '%s'=>'%s'\n", in, out); -- -- return out; -+ } -+ -+ backslash = commentlevel = inquote = 0; -+ optr = out; -+ -+ cli_dbgmsg("rfc822comments: contains a comment\n"); -+ -+ for (iptr = in; *iptr; iptr++) -+ if (backslash) { -+ if (commentlevel == 0) -+ *optr++ = *iptr; -+ backslash = 0; -+ } else -+ switch (*iptr) { -+ case '\\': -+ backslash = 1; -+ break; -+ case '\"': -+ *optr++ = '\"'; -+ inquote = !inquote; -+ break; -+ case '(': -+ if (inquote) -+ *optr++ = '('; -+ else -+ commentlevel++; -+ break; -+ case ')': -+ if (inquote) -+ *optr++ = ')'; -+ else if (commentlevel > 0) -+ commentlevel--; -+ break; -+ default: -+ if (commentlevel == 0) -+ *optr++ = *iptr; -+ } -+ -+ if (backslash) /* last character was a single backslash */ -+ *optr++ = '\\'; -+ *optr = '\0'; -+ -+ /*strstrip(out);*/ -+ -+ cli_dbgmsg("rfc822comments '%s'=>'%s'\n", in, out); -+ -+ return out; - } - - /* -@@ -3057,110 +3036,109 @@ rfc822comments(const char *in, char *out) - static char * - rfc2047(const char *in) - { -- char *out, *pout; -- size_t len; -+ char *out, *pout; -+ size_t len; - -- if((strstr(in, "=?") == NULL) || (strstr(in, "?=") == NULL)) -- return cli_strdup(in); -+ if ((strstr(in, "=?") == NULL) || (strstr(in, "?=") == NULL)) -+ return cli_strdup(in); - -- cli_dbgmsg("rfc2047 '%s'\n", in); -- out = cli_malloc(strlen(in) + 1); -+ cli_dbgmsg("rfc2047 '%s'\n", in); -+ out = cli_malloc(strlen(in) + 1); - -- if(out == NULL) { -+ if (out == NULL) { - cli_errmsg("rfc2047: Unable to allocate memory for out %llu\n", (long long unsigned)(strlen(in) + 1)); -- return NULL; -+ return NULL; - } - -- pout = out; -- -- /* For each RFC2047 string */ -- while(*in) { -- char encoding, *ptr, *enctext; -- message *m; -- blob *b; -- -- /* Find next RFC2047 string */ -- while(*in) { -- if((*in == '=') && (in[1] == '?')) { -- in += 2; -- break; -- } -- *pout++ = *in++; -- } -- /* Skip over charset, find encoding */ -- while((*in != '?') && *in) -- in++; -- if(*in == '\0') -- break; -- encoding = *++in; -- encoding = (char)tolower(encoding); -- -- if((encoding != 'q') && (encoding != 'b')) { -- cli_warnmsg("Unsupported RFC2047 encoding type '%c' - if you believe this file contains a virus, submit it to www.clamav.net\n", encoding); -- free(out); -- out = NULL; -- break; -- } -- /* Skip to encoded text */ -- if(*++in != '?') -- break; -- if(*++in == '\0') -- break; -- -- enctext = cli_strdup(in); -- if(enctext == NULL) { -- free(out); -- out = NULL; -- break; -- } -- in = strstr(in, "?="); -- if(in == NULL) { -- free(enctext); -- break; -- } -- in += 2; -- ptr = strstr(enctext, "?="); -- assert(ptr != NULL); -- *ptr = '\0'; -- /*cli_dbgmsg("Need to decode '%s' with method '%c'\n", enctext, encoding);*/ -- -- m = messageCreate(); -- if(m == NULL) -- break; -- messageAddStr(m, enctext); -- free(enctext); -- switch(encoding) { -- case 'q': -- messageSetEncoding(m, "quoted-printable"); -- break; -- case 'b': -- messageSetEncoding(m, "base64"); -- break; -- } -- b = messageToBlob(m, 1); -- if (b == NULL) { -- messageDestroy(m); -- break; -- } -- len = blobGetDataSize(b); -- cli_dbgmsg("Decoded as '%*.*s'\n", (int)len, (int)len, -- (const char *)blobGetData(b)); -- memcpy(pout, blobGetData(b), len); -- blobDestroy(b); -- messageDestroy(m); -- if(len > 0 && pout[len - 1] == '\n') -- pout += len - 1; -- else -- pout += len; -- -- } -- if(out == NULL) -- return NULL; -- -- *pout = '\0'; -- -- cli_dbgmsg("rfc2047 returns '%s'\n", out); -- return out; -+ pout = out; -+ -+ /* For each RFC2047 string */ -+ while (*in) { -+ char encoding, *ptr, *enctext; -+ message *m; -+ blob *b; -+ -+ /* Find next RFC2047 string */ -+ while (*in) { -+ if ((*in == '=') && (in[1] == '?')) { -+ in += 2; -+ break; -+ } -+ *pout++ = *in++; -+ } -+ /* Skip over charset, find encoding */ -+ while ((*in != '?') && *in) -+ in++; -+ if (*in == '\0') -+ break; -+ encoding = *++in; -+ encoding = (char)tolower(encoding); -+ -+ if ((encoding != 'q') && (encoding != 'b')) { -+ cli_warnmsg("Unsupported RFC2047 encoding type '%c' - if you believe this file contains a virus, submit it to www.clamav.net\n", encoding); -+ free(out); -+ out = NULL; -+ break; -+ } -+ /* Skip to encoded text */ -+ if (*++in != '?') -+ break; -+ if (*++in == '\0') -+ break; -+ -+ enctext = cli_strdup(in); -+ if (enctext == NULL) { -+ free(out); -+ out = NULL; -+ break; -+ } -+ in = strstr(in, "?="); -+ if (in == NULL) { -+ free(enctext); -+ break; -+ } -+ in += 2; -+ ptr = strstr(enctext, "?="); -+ assert(ptr != NULL); -+ *ptr = '\0'; -+ /*cli_dbgmsg("Need to decode '%s' with method '%c'\n", enctext, encoding);*/ -+ -+ m = messageCreate(); -+ if (m == NULL) -+ break; -+ messageAddStr(m, enctext); -+ free(enctext); -+ switch (encoding) { -+ case 'q': -+ messageSetEncoding(m, "quoted-printable"); -+ break; -+ case 'b': -+ messageSetEncoding(m, "base64"); -+ break; -+ } -+ b = messageToBlob(m, 1); -+ if (b == NULL) { -+ messageDestroy(m); -+ break; -+ } -+ len = blobGetDataSize(b); -+ cli_dbgmsg("Decoded as '%*.*s'\n", (int)len, (int)len, -+ (const char *)blobGetData(b)); -+ memcpy(pout, blobGetData(b), len); -+ blobDestroy(b); -+ messageDestroy(m); -+ if (len > 0 && pout[len - 1] == '\n') -+ pout += len - 1; -+ else -+ pout += len; -+ } -+ if (out == NULL) -+ return NULL; -+ -+ *pout = '\0'; -+ -+ cli_dbgmsg("rfc2047 returns '%s'\n", out); -+ return out; - } - - /* -@@ -3169,252 +3147,252 @@ rfc2047(const char *in) - static int - rfc1341(message *m, const char *dir) - { -- char *arg, *id, *number, *total, *oldfilename; -- const char *tmpdir; -- int n; -- char pdir[NAME_MAX + 1]; -- unsigned char md5_val[16]; -- char *md5_hex; -- -- id = (char *)messageFindArgument(m, "id"); -- if(id == NULL) -- return -1; -- -- tmpdir = cli_gettmpdir(); -- -- snprintf(pdir, sizeof(pdir) - 1, "%s"PATHSEP"clamav-partial", tmpdir); -- -- if((mkdir(pdir, S_IRWXU) < 0) && (errno != EEXIST)) { -- cli_errmsg("Can't create the directory '%s'\n", pdir); -- free(id); -- return -1; -- } else if(errno == EEXIST) { -- STATBUF statb; -- -- if(CLAMSTAT(pdir, &statb) < 0) { -- char err[128]; -- cli_errmsg("Partial directory %s: %s\n", pdir, -- cli_strerror(errno, err, sizeof(err))); -- free(id); -- return -1; -- } -- if(statb.st_mode & 077) -- cli_warnmsg("Insecure partial directory %s (mode 0%o)\n", -- pdir, --#ifdef ACCESSPERMS -- (int)(statb.st_mode&ACCESSPERMS) -+ char *arg, *id, *number, *total, *oldfilename; -+ const char *tmpdir; -+ int n; -+ char pdir[NAME_MAX + 1]; -+ unsigned char md5_val[16]; -+ char *md5_hex; -+ -+ id = (char *)messageFindArgument(m, "id"); -+ if (id == NULL) -+ return -1; -+ -+ tmpdir = cli_gettmpdir(); -+ -+ snprintf(pdir, sizeof(pdir) - 1, "%s" PATHSEP "clamav-partial", tmpdir); -+ -+ if ((mkdir(pdir, S_IRWXU) < 0) && (errno != EEXIST)) { -+ cli_errmsg("Can't create the directory '%s'\n", pdir); -+ free(id); -+ return -1; -+ } else if (errno == EEXIST) { -+ STATBUF statb; -+ -+ if (CLAMSTAT(pdir, &statb) < 0) { -+ char err[128]; -+ cli_errmsg("Partial directory %s: %s\n", pdir, -+ cli_strerror(errno, err, sizeof(err))); -+ free(id); -+ return -1; -+ } -+ if (statb.st_mode & 077) -+ cli_warnmsg("Insecure partial directory %s (mode 0%o)\n", -+ pdir, -+#ifdef ACCESSPERMS -+ (int)(statb.st_mode & ACCESSPERMS) - #else -- (int)(statb.st_mode & 0777) -+ (int)(statb.st_mode & 0777) - #endif -- ); -- } -- -- number = (char *)messageFindArgument(m, "number"); -- if(number == NULL) { -- free(id); -- return -1; -- } -- -- oldfilename = messageGetFilename(m); -- -- arg = cli_malloc(10 + strlen(id) + strlen(number)); -- if(arg) { -- sprintf(arg, "filename=%s%s", id, number); -- messageAddArgument(m, arg); -- free(arg); -- } -- -- if(oldfilename) { -- cli_dbgmsg("Must reset to %s\n", oldfilename); -- free(oldfilename); -- } -- -- n = atoi(number); -+ ); -+ } -+ -+ number = (char *)messageFindArgument(m, "number"); -+ if (number == NULL) { -+ free(id); -+ return -1; -+ } -+ -+ oldfilename = messageGetFilename(m); -+ -+ arg = cli_malloc(10 + strlen(id) + strlen(number)); -+ if (arg) { -+ sprintf(arg, "filename=%s%s", id, number); -+ messageAddArgument(m, arg); -+ free(arg); -+ } -+ -+ if (oldfilename) { -+ cli_dbgmsg("Must reset to %s\n", oldfilename); -+ free(oldfilename); -+ } -+ -+ n = atoi(number); - cl_hash_data("md5", id, strlen(id), md5_val, NULL); -- md5_hex = cli_str2hex((const char*)md5_val, 16); -- -- if(!md5_hex) { -- free(id); -- free(number); -- return CL_EMEM; -- } -- -- if(messageSavePartial(m, pdir, md5_hex, n) < 0) { -- free(md5_hex); -- free(id); -- free(number); -- return -1; -- } -- -- total = (char *)messageFindArgument(m, "total"); -- cli_dbgmsg("rfc1341: %s, %s of %s\n", id, number, (total) ? total : "?"); -- if(total) { -- int t = atoi(total); -- DIR *dd = NULL; -- -- free(total); -- /* -+ md5_hex = cli_str2hex((const char *)md5_val, 16); -+ -+ if (!md5_hex) { -+ free(id); -+ free(number); -+ return CL_EMEM; -+ } -+ -+ if (messageSavePartial(m, pdir, md5_hex, n) < 0) { -+ free(md5_hex); -+ free(id); -+ free(number); -+ return -1; -+ } -+ -+ total = (char *)messageFindArgument(m, "total"); -+ cli_dbgmsg("rfc1341: %s, %s of %s\n", id, number, (total) ? total : "?"); -+ if (total) { -+ int t = atoi(total); -+ DIR *dd = NULL; -+ -+ free(total); -+ /* - * If it's the last one - reassemble it - * FIXME: this assumes that we receive the parts in order - */ -- if((n == t) && ((dd = opendir(pdir)) != NULL)) { -- FILE *fout; -- char outname[NAME_MAX + 1]; -- time_t now; -- -- sanitiseName(id); -- -- snprintf(outname, sizeof(outname) - 1, "%s"PATHSEP"%s", dir, id); -- -- cli_dbgmsg("outname: %s\n", outname); -- -- fout = fopen(outname, "wb"); -- if(fout == NULL) { -- cli_errmsg("Can't open '%s' for writing", outname); -- free(id); -- free(number); -- free(md5_hex); -- closedir(dd); -- return -1; -- } -- -- time(&now); -- for(n = 1; n <= t; n++) { -- char filename[NAME_MAX + 1]; -- struct dirent *dent; -+ if ((n == t) && ((dd = opendir(pdir)) != NULL)) { -+ FILE *fout; -+ char outname[NAME_MAX + 1]; -+ time_t now; -+ -+ sanitiseName(id); -+ -+ snprintf(outname, sizeof(outname) - 1, "%s" PATHSEP "%s", dir, id); -+ -+ cli_dbgmsg("outname: %s\n", outname); -+ -+ fout = fopen(outname, "wb"); -+ if (fout == NULL) { -+ cli_errmsg("Can't open '%s' for writing", outname); -+ free(id); -+ free(number); -+ free(md5_hex); -+ closedir(dd); -+ return -1; -+ } -+ -+ time(&now); -+ for (n = 1; n <= t; n++) { -+ char filename[NAME_MAX + 1]; -+ struct dirent *dent; - #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2) -- union { -- struct dirent d; -- char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; -- } result; -+ union { -+ struct dirent d; -+ char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; -+ } result; - #endif - -- snprintf(filename, sizeof(filename), "_%s-%u", md5_hex, n); -+ snprintf(filename, sizeof(filename), "_%s-%u", md5_hex, n); - - #ifdef HAVE_READDIR_R_3 -- while((readdir_r(dd, &result.d, &dent) == 0) && dent) { -+ while ((readdir_r(dd, &result.d, &dent) == 0) && dent) { - #elif defined(HAVE_READDIR_R_2) -- while((dent = (struct dirent *)readdir_r(dd, &result.d))) { --#else /*!HAVE_READDIR_R*/ -- while((dent = readdir(dd))) { -+ while ((dent = (struct dirent *)readdir_r(dd, &result.d))) { -+#else /*!HAVE_READDIR_R*/ -+ while ((dent = readdir(dd))) { - #endif -- FILE *fin; -- char buffer[BUFSIZ], fullname[NAME_MAX + 1]; -- int nblanks; -- STATBUF statb; -- const char *dentry_idpart; -+ FILE *fin; -+ char buffer[BUFSIZ], fullname[NAME_MAX + 1]; -+ int nblanks; -+ STATBUF statb; -+ const char *dentry_idpart; - int test_fd; - -- if(dent->d_ino == 0) -- continue; -+ if (dent->d_ino == 0) -+ continue; - -- if(!strcmp(".",dent->d_name) || -- !strcmp("..", dent->d_name)) -- continue; -- snprintf(fullname, sizeof(fullname) - 1, -- "%s"PATHSEP"%s", pdir, dent->d_name); -- dentry_idpart = strchr(dent->d_name, '_'); -+ if (!strcmp(".", dent->d_name) || -+ !strcmp("..", dent->d_name)) -+ continue; -+ snprintf(fullname, sizeof(fullname) - 1, -+ "%s" PATHSEP "%s", pdir, dent->d_name); -+ dentry_idpart = strchr(dent->d_name, '_'); - -- if(!dentry_idpart || -- strcmp(filename, dentry_idpart) != 0) { -- if(!m->ctx->engine->keeptmp) -- continue; -+ if (!dentry_idpart || -+ strcmp(filename, dentry_idpart) != 0) { -+ if (!m->ctx->engine->keeptmp) -+ continue; - - if ((test_fd = open(fullname, O_RDONLY)) < 0) - continue; - -- if(FSTAT(test_fd, &statb) < 0) { -+ if (FSTAT(test_fd, &statb) < 0) { - close(test_fd); -- continue; -+ continue; - } - -- if(now - statb.st_mtime > (time_t)(7 * 24 * 3600)) { -- if (cli_unlink(fullname)) { -- cli_unlink(outname); -- fclose(fout); -- free(md5_hex); -- free(id); -- free(number); -- closedir(dd); -+ if (now - statb.st_mtime > (time_t)(7 * 24 * 3600)) { -+ if (cli_unlink(fullname)) { -+ cli_unlink(outname); -+ fclose(fout); -+ free(md5_hex); -+ free(id); -+ free(number); -+ closedir(dd); - close(test_fd); -- return -1; -- } -- } -+ return -1; -+ } -+ } - - close(test_fd); -- continue; -- } -- -- fin = fopen(fullname, "rb"); -- if(fin == NULL) { -- cli_errmsg("Can't open '%s' for reading", fullname); -- fclose(fout); -- cli_unlink(outname); -- free(md5_hex); -- free(id); -- free(number); -- closedir(dd); -- return -1; -- } -- nblanks = 0; -- while(fgets(buffer, sizeof(buffer) - 1, fin) != NULL) -- /* -+ continue; -+ } -+ -+ fin = fopen(fullname, "rb"); -+ if (fin == NULL) { -+ cli_errmsg("Can't open '%s' for reading", fullname); -+ fclose(fout); -+ cli_unlink(outname); -+ free(md5_hex); -+ free(id); -+ free(number); -+ closedir(dd); -+ return -1; -+ } -+ nblanks = 0; -+ while (fgets(buffer, sizeof(buffer) - 1, fin) != NULL) -+ /* - * Ensure that trailing newlines - * aren't copied - */ -- if(buffer[0] == '\n') -- nblanks++; -- else { -- if(nblanks) -- do { -- if (putc('\n', fout)==EOF) break; -- } while(--nblanks > 0); -- if (nblanks || fputs(buffer, fout)==EOF) { -- fclose(fin); -- fclose(fout); -- cli_unlink(outname); -- free(md5_hex); -- free(id); -- free(number); -- closedir(dd); -- return -1; -- } -- } -- fclose(fin); -- -- /* don't unlink if leave temps */ -- if(!m->ctx->engine->keeptmp) { -- if(cli_unlink(fullname)) { -- fclose(fout); -- cli_unlink(outname); -- free(md5_hex); -- free(id); -- free(number); -- closedir(dd); -- return -1; -- } -- } -- break; -- } -- rewinddir(dd); -- } -- closedir(dd); -- fclose(fout); -- } -- } -- free(number); -- free(id); -- free(md5_hex); -- -- return 0; -+ if (buffer[0] == '\n') -+ nblanks++; -+ else { -+ if (nblanks) -+ do { -+ if (putc('\n', fout) == EOF) break; -+ } while (--nblanks > 0); -+ if (nblanks || fputs(buffer, fout) == EOF) { -+ fclose(fin); -+ fclose(fout); -+ cli_unlink(outname); -+ free(md5_hex); -+ free(id); -+ free(number); -+ closedir(dd); -+ return -1; -+ } -+ } -+ fclose(fin); -+ -+ /* don't unlink if leave temps */ -+ if (!m->ctx->engine->keeptmp) { -+ if (cli_unlink(fullname)) { -+ fclose(fout); -+ cli_unlink(outname); -+ free(md5_hex); -+ free(id); -+ free(number); -+ closedir(dd); -+ return -1; -+ } -+ } -+ break; -+ } -+ rewinddir(dd); -+ } -+ closedir(dd); -+ fclose(fout); -+ } -+ } -+ free(number); -+ free(id); -+ free(md5_hex); -+ -+ return 0; - } - - static void - hrefs_done(blob *b, tag_arguments_t *hrefs) - { -- if(b) -- blobDestroy(b); -- html_tag_arg_free(hrefs); -+ if (b) -+ blobDestroy(b); -+ html_tag_arg_free(hrefs); - } - - /* extract URLs from static text */ -@@ -3422,28 +3400,28 @@ static void extract_text_urls(const unsigned char *mem, size_t len, tag_argument - { - char url[1024]; - size_t off; -- for (off=0;off + 10 < len;off++) { -- /* check whether this is the start of a URL */ -- int32_t proto = cli_readint32(mem + off); -- /* convert to lowercase */ -- proto |= 0x20202020; -- /* 'http:', 'https:', or 'ftp:' in little-endian */ -- if ((proto == 0x70747468 && -- (mem[off+4] == ':' || (mem[off+5] == 's' && mem[off+6] == ':'))) -- || proto == 0x3a707466) { -- size_t url_len; -- for (url_len=4; off + url_len < len && url_len < (sizeof(url)-1); url_len++) { -- unsigned char c = mem[off + url_len]; -- /* smart compilers will compile this if into -+ for (off = 0; off + 10 < len; off++) { -+ /* check whether this is the start of a URL */ -+ int32_t proto = cli_readint32(mem + off); -+ /* convert to lowercase */ -+ proto |= 0x20202020; -+ /* 'http:', 'https:', or 'ftp:' in little-endian */ -+ if ((proto == 0x70747468 && -+ (mem[off + 4] == ':' || (mem[off + 5] == 's' && mem[off + 6] == ':'))) || -+ proto == 0x3a707466) { -+ size_t url_len; -+ for (url_len = 4; off + url_len < len && url_len < (sizeof(url) - 1); url_len++) { -+ unsigned char c = mem[off + url_len]; -+ /* smart compilers will compile this if into - * a single bt + jb instruction */ -- if (c == ' ' || c == '\n' || c == '\t') -- break; -- } -- memcpy(url, mem + off, url_len); -- url[url_len] = '\0'; -- html_tag_arg_add(hrefs, "href", url); -- off += url_len; -- } -+ if (c == ' ' || c == '\n' || c == '\t') -+ break; -+ } -+ memcpy(url, mem + off, url_len); -+ url[url_len] = '\0'; -+ html_tag_arg_add(hrefs, "href", url); -+ off += url_len; -+ } - } - } - -@@ -3455,44 +3433,44 @@ static void extract_text_urls(const unsigned char *mem, size_t len, tag_argument - static blob * - getHrefs(message *m, tag_arguments_t *hrefs) - { -- unsigned char *mem; -- blob *b = messageToBlob(m, 0); -- size_t len; -- -- if(b == NULL) -- return NULL; -- -- len = blobGetDataSize(b); -- -- if(len == 0) { -- blobDestroy(b); -- return NULL; -- } -- -- /* TODO: make this size customisable */ -- if(len > 100*1024) { -- cli_dbgmsg("Viruses pointed to by URLs not scanned in large message\n"); -- blobDestroy(b); -- return NULL; -- } -- -- hrefs->count = 0; -- hrefs->tag = hrefs->value = NULL; -- hrefs->contents = NULL; -- -- cli_dbgmsg("getHrefs: calling html_normalise_mem\n"); -- mem = blobGetData(b); -- if(!html_normalise_mem(mem, (off_t)len, NULL, hrefs,m->ctx->dconf)) { -- blobDestroy(b); -- return NULL; -- } -- cli_dbgmsg("getHrefs: html_normalise_mem returned\n"); -- if (!hrefs->count && hrefs->scanContents) { -- extract_text_urls(mem, len, hrefs); -- } -- -- /* TODO: Do we need to call remove_html_comments? */ -- return b; -+ unsigned char *mem; -+ blob *b = messageToBlob(m, 0); -+ size_t len; -+ -+ if (b == NULL) -+ return NULL; -+ -+ len = blobGetDataSize(b); -+ -+ if (len == 0) { -+ blobDestroy(b); -+ return NULL; -+ } -+ -+ /* TODO: make this size customisable */ -+ if (len > 100 * 1024) { -+ cli_dbgmsg("Viruses pointed to by URLs not scanned in large message\n"); -+ blobDestroy(b); -+ return NULL; -+ } -+ -+ hrefs->count = 0; -+ hrefs->tag = hrefs->value = NULL; -+ hrefs->contents = NULL; -+ -+ cli_dbgmsg("getHrefs: calling html_normalise_mem\n"); -+ mem = blobGetData(b); -+ if (!html_normalise_mem(mem, (off_t)len, NULL, hrefs, m->ctx->dconf)) { -+ blobDestroy(b); -+ return NULL; -+ } -+ cli_dbgmsg("getHrefs: html_normalise_mem returned\n"); -+ if (!hrefs->count && hrefs->scanContents) { -+ extract_text_urls(mem, len, hrefs); -+ } -+ -+ /* TODO: Do we need to call remove_html_comments? */ -+ return b; - } - - /* -@@ -3502,84 +3480,84 @@ getHrefs(message *m, tag_arguments_t *hrefs) - static void - checkURLs(message *mainMessage, mbox_ctx *mctx, mbox_status *rc, int is_html) - { -- blob *b; -- tag_arguments_t hrefs; -+ blob *b; -+ tag_arguments_t hrefs; - - UNUSEDPARAM(is_html); - -- if(*rc == VIRUS) -- return; -+ if (*rc == VIRUS) -+ return; - -- hrefs.scanContents = mctx->ctx->engine->dboptions&CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); -+ hrefs.scanContents = mctx->ctx->engine->dboptions & CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); - -- if(!hrefs.scanContents) -- /* -+ if (!hrefs.scanContents) -+ /* - * Don't waste time extracting hrefs (parsing html), nobody - * will need it - */ -- return; -+ return; - -- hrefs.count = 0; -- hrefs.tag = hrefs.value = NULL; -- hrefs.contents = NULL; -+ hrefs.count = 0; -+ hrefs.tag = hrefs.value = NULL; -+ hrefs.contents = NULL; - -- b = getHrefs(mainMessage, &hrefs); -- if(b) { -- if(hrefs.scanContents) { -- if(phishingScan(mctx->ctx, &hrefs) == CL_VIRUS) { -- /* -+ b = getHrefs(mainMessage, &hrefs); -+ if (b) { -+ if (hrefs.scanContents) { -+ if (phishingScan(mctx->ctx, &hrefs) == CL_VIRUS) { -+ /* - * FIXME: message objects' contents are - * encapsulated so we should not access - * the members directly - */ -- mainMessage->isInfected = TRUE; -- *rc = VIRUS; -- cli_dbgmsg("PH:Phishing found\n"); -- } -- } -- } -- hrefs_done(b,&hrefs); -+ mainMessage->isInfected = TRUE; -+ *rc = VIRUS; -+ cli_dbgmsg("PH:Phishing found\n"); -+ } -+ } -+ } -+ hrefs_done(b, &hrefs); - } - - #ifdef HAVE_BACKTRACE - static void - sigsegv(int sig) - { -- signal(SIGSEGV, SIG_DFL); -- print_trace(1); -- exit(SIGSEGV); -+ signal(SIGSEGV, SIG_DFL); -+ print_trace(1); -+ exit(SIGSEGV); - } - - static void - print_trace(int use_syslog) - { -- void *array[10]; -- size_t size; -- char **strings; -- size_t i; -- pid_t pid = getpid(); -+ void *array[10]; -+ size_t size; -+ char **strings; -+ size_t i; -+ pid_t pid = getpid(); - -- cli_errmsg("Segmentation fault, attempting to print backtrace\n"); -+ cli_errmsg("Segmentation fault, attempting to print backtrace\n"); - -- size = backtrace(array, 10); -- strings = backtrace_symbols(array, size); -+ size = backtrace(array, 10); -+ strings = backtrace_symbols(array, size); - -- cli_errmsg("Backtrace of pid %d:\n", pid); -- if(use_syslog) -- syslog(LOG_ERR, "Backtrace of pid %d:", pid); -+ cli_errmsg("Backtrace of pid %d:\n", pid); -+ if (use_syslog) -+ syslog(LOG_ERR, "Backtrace of pid %d:", pid); - -- for(i = 0; i < size; i++) { -- cli_errmsg("%s\n", strings[i]); -- if(use_syslog) -- syslog(LOG_ERR, "bt[%llu]: %s", (unsigned long long)i, strings[i]); -- } -+ for (i = 0; i < size; i++) { -+ cli_errmsg("%s\n", strings[i]); -+ if (use_syslog) -+ syslog(LOG_ERR, "bt[%llu]: %s", (unsigned long long)i, strings[i]); -+ } - --#ifdef SAVE_TMP -- cli_errmsg("The errant mail file has been saved\n"); -+#ifdef SAVE_TMP -+ cli_errmsg("The errant mail file has been saved\n"); - #endif -- /* #else TODO: dump the current email */ -+ /* #else TODO: dump the current email */ - -- free(strings); -+ free(strings); - } - #endif - -@@ -3587,21 +3565,21 @@ print_trace(int use_syslog) - static bool - usefulHeader(int commandNumber, const char *cmd) - { -- switch(commandNumber) { -- case CONTENT_TRANSFER_ENCODING: -- case CONTENT_DISPOSITION: -- case CONTENT_TYPE: -- return TRUE; -- default: -- if(strcasecmp(cmd, "From") == 0) -- return TRUE; -- if(strcasecmp(cmd, "Received") == 0) -- return TRUE; -- if(strcasecmp(cmd, "De") == 0) -- return TRUE; -- } -- -- return FALSE; -+ switch (commandNumber) { -+ case CONTENT_TRANSFER_ENCODING: -+ case CONTENT_DISPOSITION: -+ case CONTENT_TYPE: -+ return TRUE; -+ default: -+ if (strcasecmp(cmd, "From") == 0) -+ return TRUE; -+ if (strcasecmp(cmd, "Received") == 0) -+ return TRUE; -+ if (strcasecmp(cmd, "De") == 0) -+ return TRUE; -+ } -+ -+ return FALSE; - } - - /* -@@ -3616,53 +3594,53 @@ getline_from_mbox(char *buffer, size_t buffer_len, fmap_t *map, size_t *at) - size_t input_len = MIN(map->len - *at, buffer_len + 1); - src = cursrc = fmap_need_off_once(map, *at, input_len); - --/* we check for eof from the result of GETC() -+ /* we check for eof from the result of GETC() - * if(feof(fin)) - return NULL;*/ -- if(!src) { -- cli_dbgmsg("getline_from_mbox: fmap need failed\n"); -- return NULL; -+ if (!src) { -+ cli_dbgmsg("getline_from_mbox: fmap need failed\n"); -+ return NULL; - } -- if((buffer_len == 0) || (buffer == NULL)) { -- cli_errmsg("Invalid call to getline_from_mbox(). Refer to https://www.clamav.net/documents/installing-clamav\n"); -- return NULL; -+ if ((buffer_len == 0) || (buffer == NULL)) { -+ cli_errmsg("Invalid call to getline_from_mbox(). Refer to https://www.clamav.net/documents/installing-clamav\n"); -+ return NULL; - } - - curbuf = buffer; - -- for(i=0; iFrom ", 6) == 0) && !isalnum(line[6])) - return FALSE;*/ - -- len = strlen(line); -- if((len < 6) || (len >= 72)) -- return FALSE; -+ len = strlen(line); -+ if ((len < 6) || (len >= 72)) -+ return FALSE; - -- if((memcmp(line, "From ", 5) == 0) || -- (memcmp(line, ">From ", 6) == 0)) { -- int numSpaces = 0, numDigits = 0; -- -- line += 4; -- -- do -- if(*line == ' ') -- numSpaces++; -- else if(isdigit((*line) & 0xFF)) -- numDigits++; -- while(*++line != '\0'); -- -- if(numSpaces < 6) -- return FALSE; -- if(numDigits < 11) -- return FALSE; -- return TRUE; -- } -- return (bool)(cli_filetype((const unsigned char *)line, len, mctx->ctx->engine) == CL_TYPE_MAIL); -+ if ((memcmp(line, "From ", 5) == 0) || -+ (memcmp(line, ">From ", 6) == 0)) { -+ int numSpaces = 0, numDigits = 0; -+ -+ line += 4; -+ -+ do -+ if (*line == ' ') -+ numSpaces++; -+ else if (isdigit((*line) & 0xFF)) -+ numDigits++; -+ while (*++line != '\0'); -+ -+ if (numSpaces < 6) -+ return FALSE; -+ if (numDigits < 11) -+ return FALSE; -+ return TRUE; -+ } -+ return (bool)(cli_filetype((const unsigned char *)line, len, mctx->ctx->engine) == CL_TYPE_MAIL); - } - - /* -@@ -3720,25 +3698,25 @@ isBounceStart(mbox_ctx *mctx, const char *line) - static bool - exportBinhexMessage(mbox_ctx *mctx, message *m) - { -- bool infected = FALSE; -- fileblob *fb; -+ bool infected = FALSE; -+ fileblob *fb; - -- if(messageGetEncoding(m) == NOENCODING) -- messageSetEncoding(m, "x-binhex"); -+ if (messageGetEncoding(m) == NOENCODING) -+ messageSetEncoding(m, "x-binhex"); - -- fb = messageToFileblob(m, mctx->dir, 0); -+ fb = messageToFileblob(m, mctx->dir, 0); - -- if(fb) { -- cli_dbgmsg("Binhex file decoded to %s\n", -- fileblobGetFilename(fb)); -+ if (fb) { -+ cli_dbgmsg("Binhex file decoded to %s\n", -+ fileblobGetFilename(fb)); - -- if(fileblobScanAndDestroy(fb) == CL_VIRUS) -- infected = TRUE; -- mctx->files++; -- } else -- cli_errmsg("Couldn't decode binhex file to %s\n", mctx->dir); -+ if (fileblobScanAndDestroy(fb) == CL_VIRUS) -+ infected = TRUE; -+ mctx->files++; -+ } else -+ cli_errmsg("Couldn't decode binhex file to %s\n", mctx->dir); - -- return infected; -+ return infected; - } - - /* -@@ -3747,11 +3725,11 @@ exportBinhexMessage(mbox_ctx *mctx, message *m) - static int - exportBounceMessage(mbox_ctx *mctx, text *start) - { -- int rc = CL_CLEAN; -- text *t; -- fileblob *fb; -+ int rc = CL_CLEAN; -+ text *t; -+ fileblob *fb; - -- /* -+ /* - * Attempt to save the original (unbounced) - * message - clamscan will find that in the - * directory and call us again (with any luck) -@@ -3770,80 +3748,80 @@ exportBounceMessage(mbox_ctx *mctx, text *start) - * must remain otherwise non bounce messages - * won't be scanned - */ -- for(t = start; t; t = t->t_next) { -- const char *txt = lineGetData(t->t_line); -- char cmd[RFC2821LENGTH + 1]; -- -- if(txt == NULL) -- continue; -- if(cli_strtokbuf(txt, 0, ":", cmd) == NULL) -- continue; -- -- switch(tableFind(mctx->rfc821Table, cmd)) { -- case CONTENT_TRANSFER_ENCODING: -- if((strstr(txt, "7bit") == NULL) && -- (strstr(txt, "8bit") == NULL)) -- break; -- continue; -- case CONTENT_DISPOSITION: -- break; -- case CONTENT_TYPE: -- if(strstr(txt, "text/plain") != NULL) -- t = NULL; -- break; -- default: -- if(strcasecmp(cmd, "From") == 0) -- start = t; -- else if(strcasecmp(cmd, "Received") == 0) -- start = t; -- continue; -- } -- break; -- } -- if(t && ((fb = fileblobCreate()) != NULL)) { -- cli_dbgmsg("Found a bounce message\n"); -- fileblobSetFilename(fb, mctx->dir, "bounce"); -- fileblobSetCTX(fb, mctx->ctx); -- if(textToFileblob(start, fb, 1) == NULL) { -- cli_dbgmsg("Nothing new to save in the bounce message\n"); -- fileblobDestroy(fb); -- } else -- rc = fileblobScanAndDestroy(fb); -- mctx->files++; -- } else -- cli_dbgmsg("Not found a bounce message\n"); -- -- return rc; -+ for (t = start; t; t = t->t_next) { -+ const char *txt = lineGetData(t->t_line); -+ char cmd[RFC2821LENGTH + 1]; -+ -+ if (txt == NULL) -+ continue; -+ if (cli_strtokbuf(txt, 0, ":", cmd) == NULL) -+ continue; -+ -+ switch (tableFind(mctx->rfc821Table, cmd)) { -+ case CONTENT_TRANSFER_ENCODING: -+ if ((strstr(txt, "7bit") == NULL) && -+ (strstr(txt, "8bit") == NULL)) -+ break; -+ continue; -+ case CONTENT_DISPOSITION: -+ break; -+ case CONTENT_TYPE: -+ if (strstr(txt, "text/plain") != NULL) -+ t = NULL; -+ break; -+ default: -+ if (strcasecmp(cmd, "From") == 0) -+ start = t; -+ else if (strcasecmp(cmd, "Received") == 0) -+ start = t; -+ continue; -+ } -+ break; -+ } -+ if (t && ((fb = fileblobCreate()) != NULL)) { -+ cli_dbgmsg("Found a bounce message\n"); -+ fileblobSetFilename(fb, mctx->dir, "bounce"); -+ fileblobSetCTX(fb, mctx->ctx); -+ if (textToFileblob(start, fb, 1) == NULL) { -+ cli_dbgmsg("Nothing new to save in the bounce message\n"); -+ fileblobDestroy(fb); -+ } else -+ rc = fileblobScanAndDestroy(fb); -+ mctx->files++; -+ } else -+ cli_dbgmsg("Not found a bounce message\n"); -+ -+ return rc; - } - - /* - * Get string representation of mimetype - */ --static const char *getMimeTypeStr(mime_type mimetype) -+static const char *getMimeTypeStr(mime_type mimetype) - { -- const struct tableinit *entry = mimeTypeStr; -- -- while (entry->key) { -- if (mimetype == entry->value) -- return entry->key; -- entry++; -- } -- return "UNKNOWN"; -+ const struct tableinit *entry = mimeTypeStr; -+ -+ while (entry->key) { -+ if (mimetype == entry->value) -+ return entry->key; -+ entry++; -+ } -+ return "UNKNOWN"; - } - - /* - * Get string representation of encoding type - */ --static const char *getEncTypeStr(encoding_type enctype) -+static const char *getEncTypeStr(encoding_type enctype) - { -- const struct tableinit *entry = encTypeStr; -- -- while (entry->key) { -- if (enctype == entry->value) -- return entry->key; -- entry++; -- } -- return "UNKNOWN"; -+ const struct tableinit *entry = encTypeStr; -+ -+ while (entry->key) { -+ if (enctype == entry->value) -+ return entry->key; -+ entry++; -+ } -+ return "UNKNOWN"; - } - - /* -@@ -3852,146 +3830,145 @@ static const char *getEncTypeStr(encoding_type enctype) - static message * - do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, mbox_ctx *mctx, message *messageIn, text **tptr, unsigned int recursion_level) - { -- bool addToText = FALSE; -- const char *dtype; --#ifndef SAVE_TO_DISC -- message *body; -+ bool addToText = FALSE; -+ const char *dtype; -+#ifndef SAVE_TO_DISC -+ message *body; - #endif -- message *aMessage = messages[i]; -- const int doPhishingScan = mctx->ctx->engine->dboptions&CL_DB_PHISHING_URLS && (DCONF_PHISHING&PHISHING_CONF_ENGINE); -+ message *aMessage = messages[i]; -+ const int doPhishingScan = mctx->ctx->engine->dboptions & CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); - #if HAVE_JSON -- const char *mtype = NULL; -- json_object *thisobj = NULL, *saveobj = mctx->wrkobj; -- -- if (mctx->wrkobj != NULL) { -- json_object *multiobj = cli_jsonarray(mctx->wrkobj, "Multipart"); -- if (multiobj == NULL) { -- cli_errmsg("Cannot get multipart preclass array\n"); -- *rc = -1; -- return mainMessage; -- } -- -- thisobj = messageGetJObj(aMessage); -- if (thisobj == NULL) { -- cli_dbgmsg("Cannot get message preclass object\n"); -- *rc = -1; -- return mainMessage; -- } -- if (cli_json_addowner(multiobj, thisobj, NULL, -1) != CL_SUCCESS) { -- cli_errmsg("Cannot assign message preclass object to multipart preclass array\n"); -- *rc = -1; -- return mainMessage; -- } -- } -+ const char *mtype = NULL; -+ json_object *thisobj = NULL, *saveobj = mctx->wrkobj; -+ -+ if (mctx->wrkobj != NULL) { -+ json_object *multiobj = cli_jsonarray(mctx->wrkobj, "Multipart"); -+ if (multiobj == NULL) { -+ cli_errmsg("Cannot get multipart preclass array\n"); -+ *rc = -1; -+ return mainMessage; -+ } -+ -+ thisobj = messageGetJObj(aMessage); -+ if (thisobj == NULL) { -+ cli_dbgmsg("Cannot get message preclass object\n"); -+ *rc = -1; -+ return mainMessage; -+ } -+ if (cli_json_addowner(multiobj, thisobj, NULL, -1) != CL_SUCCESS) { -+ cli_errmsg("Cannot assign message preclass object to multipart preclass array\n"); -+ *rc = -1; -+ return mainMessage; -+ } -+ } - #endif - -- if(aMessage == NULL) { -+ if (aMessage == NULL) { - #if HAVE_JSON -- if (thisobj != NULL) -- cli_jsonstr(thisobj, "MimeType", "NULL"); -+ if (thisobj != NULL) -+ cli_jsonstr(thisobj, "MimeType", "NULL"); - #endif -- return mainMessage; -- } -+ return mainMessage; -+ } - -- if(*rc != OK) -- return mainMessage; -+ if (*rc != OK) -+ return mainMessage; - -- cli_dbgmsg("Mixed message part %d is of type %d\n", -- i, messageGetMimeType(aMessage)); -+ cli_dbgmsg("Mixed message part %d is of type %d\n", -+ i, messageGetMimeType(aMessage)); - - #if HAVE_JSON -- if (thisobj != NULL) { -- cli_jsonstr(thisobj, "MimeType", getMimeTypeStr(messageGetMimeType(aMessage))); -- cli_jsonstr(thisobj, "MimeSubtype", messageGetMimeSubtype(aMessage)); -- cli_jsonstr(thisobj, "EncodingType", getEncTypeStr(messageGetEncoding(aMessage))); -- cli_jsonstr(thisobj, "Disposition", messageGetDispositionType(aMessage)); -- cli_jsonstr(thisobj, "Filename", messageHasFilename(aMessage) ? -- messageGetFilename(aMessage): "(inline)"); -- } -+ if (thisobj != NULL) { -+ cli_jsonstr(thisobj, "MimeType", getMimeTypeStr(messageGetMimeType(aMessage))); -+ cli_jsonstr(thisobj, "MimeSubtype", messageGetMimeSubtype(aMessage)); -+ cli_jsonstr(thisobj, "EncodingType", getEncTypeStr(messageGetEncoding(aMessage))); -+ cli_jsonstr(thisobj, "Disposition", messageGetDispositionType(aMessage)); -+ cli_jsonstr(thisobj, "Filename", messageHasFilename(aMessage) ? messageGetFilename(aMessage) : "(inline)"); -+ } - #endif - -- switch(messageGetMimeType(aMessage)) { -- case APPLICATION: -- case AUDIO: -- case IMAGE: -- case VIDEO: -- break; -- case NOMIME: -- cli_dbgmsg("No mime headers found in multipart part %d\n", i); -- if(mainMessage) { -- if(binhexBegin(aMessage)) { -- cli_dbgmsg("Found binhex message in multipart/mixed mainMessage\n"); -- -- if(exportBinhexMessage(mctx, mainMessage)) -- *rc = VIRUS; -- } -- if(mainMessage != messageIn) -- messageDestroy(mainMessage); -- mainMessage = NULL; -- } else if(aMessage) { -- if(binhexBegin(aMessage)) { -- cli_dbgmsg("Found binhex message in multipart/mixed non mime part\n"); -- if(exportBinhexMessage(mctx, aMessage)) -- *rc = VIRUS; -- assert(aMessage == messages[i]); -- messageReset(messages[i]); -- } -- } -- addToText = TRUE; -- if(messageGetBody(aMessage) == NULL) -- /* -+ switch (messageGetMimeType(aMessage)) { -+ case APPLICATION: -+ case AUDIO: -+ case IMAGE: -+ case VIDEO: -+ break; -+ case NOMIME: -+ cli_dbgmsg("No mime headers found in multipart part %d\n", i); -+ if (mainMessage) { -+ if (binhexBegin(aMessage)) { -+ cli_dbgmsg("Found binhex message in multipart/mixed mainMessage\n"); -+ -+ if (exportBinhexMessage(mctx, mainMessage)) -+ *rc = VIRUS; -+ } -+ if (mainMessage != messageIn) -+ messageDestroy(mainMessage); -+ mainMessage = NULL; -+ } else if (aMessage) { -+ if (binhexBegin(aMessage)) { -+ cli_dbgmsg("Found binhex message in multipart/mixed non mime part\n"); -+ if (exportBinhexMessage(mctx, aMessage)) -+ *rc = VIRUS; -+ assert(aMessage == messages[i]); -+ messageReset(messages[i]); -+ } -+ } -+ addToText = TRUE; -+ if (messageGetBody(aMessage) == NULL) -+ /* - * No plain text version - */ -- cli_dbgmsg("No plain text alternative\n"); -- break; -- case TEXT: -- dtype = messageGetDispositionType(aMessage); -- cli_dbgmsg("Mixed message text part disposition \"%s\"\n", -- dtype); -- if(strcasecmp(dtype, "attachment") == 0) -- break; -- if((*dtype == '\0') || (strcasecmp(dtype, "inline") == 0)) { -- const char *cptr; -- -- if(mainMessage && (mainMessage != messageIn)) -- messageDestroy(mainMessage); -- mainMessage = NULL; -- cptr = messageGetMimeSubtype(aMessage); -- cli_dbgmsg("Mime subtype \"%s\"\n", cptr); -- if((tableFind(mctx->subtypeTable, cptr) == PLAIN) && -- (messageGetEncoding(aMessage) == NOENCODING)) { -- /* -+ cli_dbgmsg("No plain text alternative\n"); -+ break; -+ case TEXT: -+ dtype = messageGetDispositionType(aMessage); -+ cli_dbgmsg("Mixed message text part disposition \"%s\"\n", -+ dtype); -+ if (strcasecmp(dtype, "attachment") == 0) -+ break; -+ if ((*dtype == '\0') || (strcasecmp(dtype, "inline") == 0)) { -+ const char *cptr; -+ -+ if (mainMessage && (mainMessage != messageIn)) -+ messageDestroy(mainMessage); -+ mainMessage = NULL; -+ cptr = messageGetMimeSubtype(aMessage); -+ cli_dbgmsg("Mime subtype \"%s\"\n", cptr); -+ if ((tableFind(mctx->subtypeTable, cptr) == PLAIN) && -+ (messageGetEncoding(aMessage) == NOENCODING)) { -+ /* - * Strictly speaking, a text/plain part - * is not an attachment. We pretend it - * is so that we can decode and scan it - */ -- if(!messageHasFilename(aMessage)) { -- cli_dbgmsg("Adding part to main message\n"); -- addToText = TRUE; -- } else -- cli_dbgmsg("Treating inline as attachment\n"); -- } else { -- const int is_html = (tableFind(mctx->subtypeTable, cptr) == HTML); -- if(doPhishingScan) -- checkURLs(aMessage, mctx, rc, is_html); -- messageAddArgument(aMessage, -- "filename=mixedtextportion"); -- } -- break; -- } -- cli_dbgmsg("Text type %s is not supported\n", dtype); -- return mainMessage; -- case MESSAGE: -- /* Content-Type: message/rfc822 */ -- cli_dbgmsg("Found message inside multipart (encoding type %d)\n", -- messageGetEncoding(aMessage)); --#ifndef SCAN_UNENCODED_BOUNCES -- switch(messageGetEncoding(aMessage)) { -- case NOENCODING: -- case EIGHTBIT: -- case BINARY: -- if(encodingLine(aMessage) == NULL) { -- /* -+ if (!messageHasFilename(aMessage)) { -+ cli_dbgmsg("Adding part to main message\n"); -+ addToText = TRUE; -+ } else -+ cli_dbgmsg("Treating inline as attachment\n"); -+ } else { -+ const int is_html = (tableFind(mctx->subtypeTable, cptr) == HTML); -+ if (doPhishingScan) -+ checkURLs(aMessage, mctx, rc, is_html); -+ messageAddArgument(aMessage, -+ "filename=mixedtextportion"); -+ } -+ break; -+ } -+ cli_dbgmsg("Text type %s is not supported\n", dtype); -+ return mainMessage; -+ case MESSAGE: -+ /* Content-Type: message/rfc822 */ -+ cli_dbgmsg("Found message inside multipart (encoding type %d)\n", -+ messageGetEncoding(aMessage)); -+#ifndef SCAN_UNENCODED_BOUNCES -+ switch (messageGetEncoding(aMessage)) { -+ case NOENCODING: -+ case EIGHTBIT: -+ case BINARY: -+ if (encodingLine(aMessage) == NULL) { -+ /* - * This means that the message - * has no attachments - * -@@ -4001,39 +3978,39 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m - * been set if the message - * itself has been encoded - */ -- cli_dbgmsg("Unencoded multipart/message will not be scanned\n"); -- assert(aMessage == messages[i]); -- messageDestroy(messages[i]); -- messages[i] = NULL; -- return mainMessage; -- } -- /* FALLTHROUGH */ -- default: -- cli_dbgmsg("Encoded multipart/message will be scanned\n"); -- } -+ cli_dbgmsg("Unencoded multipart/message will not be scanned\n"); -+ assert(aMessage == messages[i]); -+ messageDestroy(messages[i]); -+ messages[i] = NULL; -+ return mainMessage; -+ } -+ /* FALLTHROUGH */ -+ default: -+ cli_dbgmsg("Encoded multipart/message will be scanned\n"); -+ } - #endif --#if 0 -+#if 0 - messageAddStrAtTop(aMessage, - "Received: by clamd (message/rfc822)"); - #endif --#ifdef SAVE_TO_DISC -- /* -+#ifdef SAVE_TO_DISC -+ /* - * Save this embedded message - * to a temporary file - */ -- if(saveTextPart(mctx, aMessage, 1) == CL_VIRUS) -- *rc = VIRUS; -- assert(aMessage == messages[i]); -- messageDestroy(messages[i]); -- messages[i] = NULL; -+ if (saveTextPart(mctx, aMessage, 1) == CL_VIRUS) -+ *rc = VIRUS; -+ assert(aMessage == messages[i]); -+ messageDestroy(messages[i]); -+ messages[i] = NULL; - #else -- /* -+ /* - * Scan in memory, faster but is open to DoS attacks - * when many nested levels are involved. - */ -- body = parseEmailHeaders(aMessage, mctx->rfc821Table); -+ body = parseEmailHeaders(aMessage, mctx->rfc821Table); - -- /* -+ /* - * We've finished with the - * original copy of the message, - * so throw that away and -@@ -4041,106 +4018,106 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m - * message as a message. - * This can save a lot of memory - */ -- assert(aMessage == messages[i]); -- messageDestroy(messages[i]); -- messages[i] = NULL; -+ assert(aMessage == messages[i]); -+ messageDestroy(messages[i]); -+ messages[i] = NULL; - #if HAVE_JSON -- mctx->wrkobj = thisobj; -+ mctx->wrkobj = thisobj; - #endif -- if(body) { -- messageSetCTX(body, mctx->ctx); -- *rc = parseEmailBody(body, NULL, mctx, recursion_level + 1); -- if((*rc == OK) && messageContainsVirus(body)) -- *rc = VIRUS; -- messageDestroy(body); -- } -+ if (body) { -+ messageSetCTX(body, mctx->ctx); -+ *rc = parseEmailBody(body, NULL, mctx, recursion_level + 1); -+ if ((*rc == OK) && messageContainsVirus(body)) -+ *rc = VIRUS; -+ messageDestroy(body); -+ } - #if HAVE_JSON -- mctx->wrkobj = saveobj; -+ mctx->wrkobj = saveobj; - #endif - #endif -- return mainMessage; -- case MULTIPART: -- /* -+ return mainMessage; -+ case MULTIPART: -+ /* - * It's a multi part within a multi part - * Run the message parser on this bit, it won't - * be an attachment - */ -- cli_dbgmsg("Found multipart inside multipart\n"); -+ cli_dbgmsg("Found multipart inside multipart\n"); - #if HAVE_JSON -- mctx->wrkobj = thisobj; -+ mctx->wrkobj = thisobj; - #endif -- if(aMessage) { -- /* -+ if (aMessage) { -+ /* - * The headers were parsed when reading in the - * whole multipart section - */ -- *rc = parseEmailBody(aMessage, *tptr, mctx, recursion_level + 1); -- cli_dbgmsg("Finished recursion, rc = %d\n", (int)*rc); -- assert(aMessage == messages[i]); -- messageDestroy(messages[i]); -- messages[i] = NULL; -- } else { -- *rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); -- if(mainMessage && (mainMessage != messageIn)) -- messageDestroy(mainMessage); -- mainMessage = NULL; -- } -+ *rc = parseEmailBody(aMessage, *tptr, mctx, recursion_level + 1); -+ cli_dbgmsg("Finished recursion, rc = %d\n", (int)*rc); -+ assert(aMessage == messages[i]); -+ messageDestroy(messages[i]); -+ messages[i] = NULL; -+ } else { -+ *rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); -+ if (mainMessage && (mainMessage != messageIn)) -+ messageDestroy(mainMessage); -+ mainMessage = NULL; -+ } - #if HAVE_JSON -- mctx->wrkobj = saveobj; -+ mctx->wrkobj = saveobj; - #endif -- return mainMessage; -- default: -- cli_dbgmsg("Only text and application attachments are fully supported, type = %d\n", -- messageGetMimeType(aMessage)); -- /* fall through - we may be able to salvage something */ -- } -- -- if(*rc != VIRUS) { -- fileblob *fb = messageToFileblob(aMessage, mctx->dir, 1); -+ return mainMessage; -+ default: -+ cli_dbgmsg("Only text and application attachments are fully supported, type = %d\n", -+ messageGetMimeType(aMessage)); -+ /* fall through - we may be able to salvage something */ -+ } -+ -+ if (*rc != VIRUS) { -+ fileblob *fb = messageToFileblob(aMessage, mctx->dir, 1); - #if HAVE_JSON -- json_object *arrobj; -- int arrlen = 0; -+ json_object *arrobj; -+ int arrlen = 0; - -- if (thisobj != NULL) { -- /* attempt to determine container size - prevents incorrect type reporting */ -- if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) -- arrlen = json_object_array_length(arrobj); -- } -+ if (thisobj != NULL) { -+ /* attempt to determine container size - prevents incorrect type reporting */ -+ if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) -+ arrlen = json_object_array_length(arrobj); -+ } - - #endif -- if(fb) { -- /* aMessage doesn't always have a ctx set */ -- fileblobSetCTX(fb, mctx->ctx); -- if(fileblobScanAndDestroy(fb) == CL_VIRUS) -- *rc = VIRUS; -- if (!addToText) -- mctx->files++; -- } -+ if (fb) { -+ /* aMessage doesn't always have a ctx set */ -+ fileblobSetCTX(fb, mctx->ctx); -+ if (fileblobScanAndDestroy(fb) == CL_VIRUS) -+ *rc = VIRUS; -+ if (!addToText) -+ mctx->files++; -+ } - #if HAVE_JSON -- if (thisobj != NULL) { -- json_object *entry = NULL; -- const char *dtype = NULL; -- -- /* attempt to acquire container type */ -- if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) -- if (json_object_array_length(arrobj) > arrlen) -- entry = json_object_array_get_idx(arrobj, arrlen); -- if (entry) { -- json_object_object_get_ex(entry, "FileType", &entry); -- if (entry) -- dtype = json_object_get_string(entry); -- } -- cli_jsonint(thisobj, "ContainedObjectsIndex", arrlen); -- cli_jsonstr(thisobj, "ClamAVFileType", dtype ? dtype : "UNKNOWN"); -- } -+ if (thisobj != NULL) { -+ json_object *entry = NULL; -+ const char *dtype = NULL; -+ -+ /* attempt to acquire container type */ -+ if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) -+ if (json_object_array_length(arrobj) > arrlen) -+ entry = json_object_array_get_idx(arrobj, arrlen); -+ if (entry) { -+ json_object_object_get_ex(entry, "FileType", &entry); -+ if (entry) -+ dtype = json_object_get_string(entry); -+ } -+ cli_jsonint(thisobj, "ContainedObjectsIndex", arrlen); -+ cli_jsonstr(thisobj, "ClamAVFileType", dtype ? dtype : "UNKNOWN"); -+ } - #endif -- if(messageContainsVirus(aMessage)) -- *rc = VIRUS; -- } -- messageDestroy(aMessage); -- messages[i] = NULL; -+ if (messageContainsVirus(aMessage)) -+ *rc = VIRUS; -+ } -+ messageDestroy(aMessage); -+ messages[i] = NULL; - -- return mainMessage; -+ return mainMessage; - } - - /* -@@ -4149,13 +4126,13 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m - static int - count_quotes(const char *buf) - { -- int quotes = 0; -+ int quotes = 0; - -- while(*buf) -- if(*buf++ == '\"') -- quotes++; -+ while (*buf) -+ if (*buf++ == '\"') -+ quotes++; - -- return quotes; -+ return quotes; - } - - /* -@@ -4164,33 +4141,33 @@ count_quotes(const char *buf) - static bool - next_is_folded_header(const text *t) - { -- const text *next = t->t_next; -- const char *data, *ptr; -+ const text *next = t->t_next; -+ const char *data, *ptr; - -- if(next == NULL) -- return FALSE; -+ if (next == NULL) -+ return FALSE; - -- if(next->t_line == NULL) -- return FALSE; -+ if (next->t_line == NULL) -+ return FALSE; - -- data = lineGetData(next->t_line); -+ data = lineGetData(next->t_line); - -- /* -+ /* - * Section B.2 of RFC822 says TAB or SPACE means a continuation of the - * previous entry. - */ -- if(isblank(data[0])) -- return TRUE; -+ if (isblank(data[0])) -+ return TRUE; - -- if(strchr(data, '=') == NULL) -- /* -+ if (strchr(data, '=') == NULL) -+ /* - * Avoid false positives with - * Content-Type: text/html; - * Content-Transfer-Encoding: quoted-printable - */ -- return FALSE; -+ return FALSE; - -- /* -+ /* - * Some are broken and don't fold headers lines - * correctly as per section 2.2.3 of RFC2822. - * Generally they miss the white space at -@@ -4205,23 +4182,23 @@ next_is_folded_header(const text *t) - * Since we're a virus checker not an RFC - * verifier we need to handle these - */ -- data = lineGetData(t->t_line); -- -- ptr = strchr(data, '\0'); -- -- while(--ptr > data) -- switch(*ptr) { -- case ';': -- return TRUE; -- case '\n': -- case ' ': -- case '\r': -- case '\t': -- continue; /* white space at end of line */ -- default: -- return FALSE; -- } -- return FALSE; -+ data = lineGetData(t->t_line); -+ -+ ptr = strchr(data, '\0'); -+ -+ while (--ptr > data) -+ switch (*ptr) { -+ case ';': -+ return TRUE; -+ case '\n': -+ case ' ': -+ case '\r': -+ case '\t': -+ continue; /* white space at end of line */ -+ default: -+ return FALSE; -+ } -+ return FALSE; - } - - /* -@@ -4232,12 +4209,12 @@ next_is_folded_header(const text *t) - static bool - newline_in_header(const char *line) - { -- cli_dbgmsg("newline_in_header, check \"%s\"\n", line); -+ cli_dbgmsg("newline_in_header, check \"%s\"\n", line); - -- if(strncmp(line, "Message-Id: ", 12) == 0) -- return TRUE; -- if(strncmp(line, "Date: ", 6) == 0) -- return TRUE; -+ if (strncmp(line, "Message-Id: ", 12) == 0) -+ return TRUE; -+ if (strncmp(line, "Date: ", 6) == 0) -+ return TRUE; - -- return FALSE; -+ return FALSE; - } diff --git a/clamav-0.100.0-stats-deprecation.patch b/clamav-0.100.0-stats-deprecation.patch deleted file mode 100644 index 3ba6aa7b0888894cf0163394139d5ca8e46d1aef..0000000000000000000000000000000000000000 --- a/clamav-0.100.0-stats-deprecation.patch +++ /dev/null @@ -1,18 +0,0 @@ -https://bugzilla.clamav.net/show_bug.cgi?id=12097 - ---- a/shared/optparser.c -+++ b/shared/optparser.c -@@ -505,6 +505,13 @@ const struct clam_option __clam_options[ - { "ClamukoExcludeUID", NULL, 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, -1, NULL, FLAG_MULTIPLE, OPT_CLAMD | OPT_DEPRECATED, "", "" }, - { "ClamukoMaxFileSize", NULL, 0, CLOPT_TYPE_SIZE, MATCH_SIZE, 5242880, NULL, 0, OPT_CLAMD | OPT_DEPRECATED, "", "" }, - { "AllowSupplementaryGroups", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM | OPT_MILTER | OPT_DEPRECATED, "Initialize a supplementary group access (the process must be started by root).", "no" }, -+ { "StatsHostID", "stats-host-id", 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM | OPT_CLAMD | OPT_CLAMSCAN | OPT_DEPRECATED, "", "" }, -+ { "StatsEnabled", "enable-stats", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM | OPT_CLAMSCAN | OPT_DEPRECATED, "", "" }, -+ { "StatsPEDisabled", "disable-pe-stats", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN | OPT_DEPRECATED, "", "" }, -+ { "StatsTimeout", "stats-timeout", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, -1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN | OPT_FRESHCLAM | OPT_DEPRECATED, "", "" }, -+ { "SubmitDetectionStats", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM | OPT_DEPRECATED, "", "" }, -+ { "DetectionStatsCountry", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM | OPT_DEPRECATED, "", "" }, -+ { "DetectionStatsHostID", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM | OPT_DEPRECATED, "", "" }, - - /* Milter specific options */ - diff --git a/clamav-0.100.0-umask.patch b/clamav-0.100.0-umask.patch deleted file mode 100644 index 0e7c6ec2065e525a4a6ef8593910a514c8f27f84..0000000000000000000000000000000000000000 --- a/clamav-0.100.0-umask.patch +++ /dev/null @@ -1,33 +0,0 @@ ---- clamav-0.100.0/clamav-milter/clamav-milter.c 2018-04-04 02:13:58.000000000 +0200 -+++ clamav-0.100.0/clamav-milter/clamav-milter.c.umask 2018-05-28 23:25:12.374047156 +0200 -@@ -432,7 +432,7 @@ - - if((opt = optget(opts, "PidFile"))->enabled) { - FILE *fd; -- mode_t old_umask = umask(0002); -+ mode_t old_umask = umask(0022); - - if((fd = fopen(opt->strarg, "w")) == NULL) { - logg("!Can't save PID in file %s\n", opt->strarg); ---- clamav-0.100.0/shared/output.c 2018-04-04 02:13:58.000000000 +0200 -+++ clamav-0.100.0/shared/output.c.umask 2018-05-28 23:24:41.968851516 +0200 -@@ -379,7 +379,7 @@ - - if (!logg_fp && logg_file) - { -- old_umask = umask(0037); -+ old_umask = umask(0077); - if ((logg_fp = fopen(logg_file, "at")) == NULL) - { - umask(old_umask); ---- clamav-0.100.0/freshclam/freshclam.c 2018-04-04 02:13:58.000000000 +0200 -+++ clamav-0.100.0/freshclam/freshclam.c.umask 2018-05-28 23:25:30.675164850 +0200 -@@ -127,7 +127,7 @@ - { - FILE *fd; - int old_umask; -- old_umask = umask (0006); -+ old_umask = umask (0022); - if ((fd = fopen (pidfile, "w")) == NULL) - { - logg ("!Can't save PID to file %s: %s\n", pidfile, strerror (errno)); diff --git a/clamav-0.101.4.tar.gz b/clamav-0.103.2.tar.gz similarity index 50% rename from clamav-0.101.4.tar.gz rename to clamav-0.103.2.tar.gz index 1c1cec2be5f81f45ae5a056bc7689382ecc8f934..5e4e2c1f69a4576b2a7ec2faff835de62933922e 100644 Binary files a/clamav-0.101.4.tar.gz and b/clamav-0.103.2.tar.gz differ diff --git a/clamav-Fix-int64-overflow-check.patch b/clamav-Fix-int64-overflow-check.patch deleted file mode 100644 index a27f398357a370307503de88ca35d7b70f90f4ae..0000000000000000000000000000000000000000 --- a/clamav-Fix-int64-overflow-check.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 38622da97fb6fcb2d43d5676ac75cb5ac7896359 Mon Sep 17 00:00:00 2001 -From: lutianxiong -Date: Tue, 16 Jun 2020 11:15:10 +0800 -Subject: [PATCH] Fix int64 overflow check - -Overflow check "(value >> 32) * 10 < INT32_MAX" may not work in -certain conditions, e.g. value is 0xcccccccdbcdc9cc - -Note: This fixes oss-fuzz bug 16117. ---- - libclamav/htmlnorm.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/libclamav/htmlnorm.c b/libclamav/htmlnorm.c -index d0be15b..4ac4948 100644 ---- a/libclamav/htmlnorm.c -+++ b/libclamav/htmlnorm.c -@@ -1459,9 +1459,9 @@ static int cli_html_normalise(int fd, m_area_t *m_area, const char *dirname, tag - next_state = HTML_BAD_STATE; - ptr++; - } else if (isdigit(*ptr) || (hex && isxdigit(*ptr))) { -- if (hex && (value >> 32) * 16 < INT32_MAX) { -+ if (hex && value < INT64_MAX / 16) { - value *= 16; -- } else if ((value >> 32) * 10 < INT32_MAX) { -+ } else if (value < INT64_MAX / 10) { - value *= 10; - } else { - html_output_c(file_buff_o2, value); -@@ -1727,7 +1727,7 @@ static int cli_html_normalise(int fd, m_area_t *m_area, const char *dirname, tag - state = HTML_RFC2397_DATA; - break; - case HTML_ESCAPE_CHAR: -- if ((value >> 32) * 16 < INT32_MAX) { -+ if (value < INT64_MAX / 16) { - value *= 16; - } else { - state = next_state; --- -2.23.0 - diff --git a/clamav-check.patch b/clamav-check.patch new file mode 100644 index 0000000000000000000000000000000000000000..e3d37a4d3ee89abf167f06aa74eca5aa8b497d7a --- /dev/null +++ b/clamav-check.patch @@ -0,0 +1,12 @@ +diff -up clamav-0.103.0/unit_tests/check_jsnorm.c.check clamav-0.103.0/unit_tests/check_jsnorm.c +--- clamav-0.103.0/unit_tests/check_jsnorm.c.check 2020-09-12 18:27:10.000000000 -0600 ++++ clamav-0.103.0/unit_tests/check_jsnorm.c 2020-09-17 22:15:26.199957518 -0600 +@@ -247,7 +247,7 @@ static void tokenizer_test(const char *i + fd = open(filename, O_RDONLY); + if (fd < 0) { + jstest_teardown(); +- ck_assert_msg("failed to open output file: %s", filename); ++ ck_assert_msg(0, "failed to open output file: %s", filename); + } + + diff_file_mem(fd, expected, len); diff --git a/clamav-clamonacc-service.patch b/clamav-clamonacc-service.patch new file mode 100644 index 0000000000000000000000000000000000000000..bdac52d9150e211e1956dfd5e7b9c1c561f7c4fe --- /dev/null +++ b/clamav-clamonacc-service.patch @@ -0,0 +1,20 @@ +diff -up clamav-0.103.0/clamonacc/clamav-clamonacc.service.in.clamonacc-service clamav-0.103.0/clamonacc/clamav-clamonacc.service.in +--- clamav-0.103.0/clamonacc/clamav-clamonacc.service.in.clamonacc-service 2020-09-12 18:27:09.000000000 -0600 ++++ clamav-0.103.0/clamonacc/clamav-clamonacc.service.in 2020-09-18 19:49:35.400152760 -0600 +@@ -4,14 +4,12 @@ + [Unit] + Description=ClamAV On-Access Scanner + Documentation=man:clamonacc(8) man:clamd.conf(5) https://www.clamav.net/documents +-Requires=clamav-daemon.service +-After=clamav-daemon.service syslog.target network.target ++After=clamd@scan.service syslog.target network.target + + [Service] + Type=simple + User=root +-ExecStartPre=/bin/bash -c "while [ ! -S /run/clamav/clamd.ctl ]; do sleep 1; done" +-ExecStart=@prefix@/sbin/clamonacc -F --config-file=@APP_CONFIG_DIRECTORY@/clamd.conf --log=/var/log/clamav/clamonacc.log --move=/root/quarantine ++ExecStart=@prefix@/sbin/clamonacc -F --config-file=/etc/clamd.d/scan.conf + + [Install] + WantedBy=multi-user.target diff --git a/clamav-0.100.1-defaults_locations.patch b/clamav-default_confs.patch similarity index 51% rename from clamav-0.100.1-defaults_locations.patch rename to clamav-default_confs.patch index 5bcfa2d30d596eccbadecda3b40c469624ee6b30..97bbc1028d465bec76d40766f8f6b63828c58d1b 100644 --- a/clamav-0.100.1-defaults_locations.patch +++ b/clamav-default_confs.patch @@ -1,35 +1,23 @@ ---- ./clamconf/clamconf.c.orig 2018-07-30 05:28:40.199759145 +0100 -+++ ./clamconf/clamconf.c 2018-07-30 05:30:12.083760295 +0100 -@@ -58,9 +58,9 @@ static struct _cfgfile { +diff -up clamav-0.103.0/clamconf/clamconf.c.default_confs clamav-0.103.0/clamconf/clamconf.c +--- clamav-0.103.0/clamconf/clamconf.c.default_confs 2020-09-12 18:27:09.000000000 -0600 ++++ clamav-0.103.0/clamconf/clamconf.c 2020-09-17 22:00:20.792879792 -0600 +@@ -63,9 +63,9 @@ static struct _cfgfile { const char *name; int tool; } cfgfile[] = { -- { "clamd.conf", OPT_CLAMD }, -+ { "clamd.d/scan.conf", OPT_CLAMD }, - { "freshclam.conf", OPT_FRESHCLAM }, -- { "clamav-milter.conf", OPT_MILTER }, -+ { "mail/clamav-milter.conf", OPT_MILTER }, - { NULL, 0 } - }; +- {"clamd.conf", OPT_CLAMD}, ++ {"clamd.d/scan.conf", OPT_CLAMD}, + {"freshclam.conf", OPT_FRESHCLAM}, +- {"clamav-milter.conf", OPT_MILTER}, ++ {"mail/clamav-milter.conf", OPT_MILTER}, + {NULL, 0}}; ---- ./platform.h.in.orig 2018-07-30 06:27:54.437257754 +0100 -+++ ./platform.h.in 2018-07-30 06:29:18.920124404 +0100 -@@ -34,9 +34,9 @@ typedef unsigned int in_addr_t; - #define PATHSEP "/" - #endif - --#define CONFDIR_CLAMD CONFDIR PATHSEP "clamd.conf" -+#define CONFDIR_CLAMD CONFDIR PATHSEP "clamd.d/scan.conf" - #define CONFDIR_FRESHCLAM CONFDIR PATHSEP "freshclam.conf" --#define CONFDIR_MILTER CONFDIR PATHSEP "clamav-milter.conf" -+#define CONFDIR_MILTER CONFDIR PATHSEP "mail/clamav-milter.conf" - - #define cli_to_utf8_maybe_alloc(x) (x) - #define cli_strdup_to_utf8(x) strdup(x) ---- ./docs/man/clamav-milter.conf.5.in.orig 2018-07-31 02:47:52.768212114 +0100 -+++ ./docs/man/clamav-milter.conf.5.in 2018-07-31 02:48:57.295032444 +0100 -@@ -239,7 +239,7 @@ Default: no - All options expressing a size are limited to max 4GB. Values in excess will be reset to the maximum. + static void printopts(struct optstruct *opts, int nondef) +diff -up clamav-0.103.0/docs/man/clamav-milter.8.in.default_confs clamav-0.103.0/docs/man/clamav-milter.8.in +--- clamav-0.103.0/docs/man/clamav-milter.8.in.default_confs 2020-09-12 18:27:09.000000000 -0600 ++++ clamav-0.103.0/docs/man/clamav-milter.8.in 2020-09-17 22:00:20.793879800 -0600 +@@ -27,7 +27,7 @@ Print the version number and exit. + Read configuration from FILE. .SH "FILES" .LP -@CFGDIR@/clamav-milter.conf @@ -37,10 +25,11 @@ .SH "AUTHOR" .LP aCaB ---- ./docs/man/clamav-milter.8.in.orig 2018-07-31 02:47:45.154130364 +0100 -+++ ./docs/man/clamav-milter.8.in 2018-07-31 02:48:39.484792893 +0100 -@@ -27,7 +27,7 @@ Print the version number and exit. - Read configuration from FILE. +diff -up clamav-0.103.0/docs/man/clamav-milter.conf.5.in.default_confs clamav-0.103.0/docs/man/clamav-milter.conf.5.in +--- clamav-0.103.0/docs/man/clamav-milter.conf.5.in.default_confs 2020-09-12 18:27:09.000000000 -0600 ++++ clamav-0.103.0/docs/man/clamav-milter.conf.5.in 2020-09-17 22:00:20.794879808 -0600 +@@ -239,7 +239,7 @@ Default: no + All options expressing a size are limited to max 4GB. Values in excess will be reset to the maximum. .SH "FILES" .LP -@CFGDIR@/clamav-milter.conf @@ -48,19 +37,9 @@ .SH "AUTHOR" .LP aCaB ---- ./docs/man/clamd.conf.5.in.orig 2018-07-31 02:52:12.607659460 +0100 -+++ ./docs/man/clamd.conf.5.in 2018-07-31 02:52:37.396992885 +0100 -@@ -703,7 +703,7 @@ Default: no - All options expressing a size are limited to max 4GB. Values in excess will be reset to the maximum. - .SH "FILES" - .LP --@CFGDIR@/clamd.conf -+@CFGDIR@/clamd.d/scan.conf - .SH "AUTHORS" - .LP - Tomasz Kojm , Kevin Lin ---- ./docs/man/clamd.8.in.orig 2018-07-31 02:51:22.897990849 +0100 -+++ ./docs/man/clamd.8.in 2018-07-31 02:53:22.170595103 +0100 +diff -up clamav-0.103.0/docs/man/clamd.8.in.default_confs clamav-0.103.0/docs/man/clamd.8.in +--- clamav-0.103.0/docs/man/clamd.8.in.default_confs 2020-09-12 18:27:09.000000000 -0600 ++++ clamav-0.103.0/docs/man/clamd.8.in 2020-09-17 22:00:20.794879808 -0600 @@ -7,7 +7,7 @@ clamd \- an anti\-virus daemon clamd [options] .SH "DESCRIPTION" @@ -70,7 +49,7 @@ .SH "COMMANDS" .LP It's recommended to prefix clamd commands with the letter \fBz\fR (eg. zSCAN) to indicate that the command will be delimited by a NULL character and that clamd should continue reading command data until a NULL character is read. The null delimiter assures that the complete command and its entire argument will be processed as a single command. Alternatively commands may be prefixed with the letter \fBn\fR (e.g. nSCAN) to use a newline character as the delimiter. Clamd replies will honour the requested terminator in turn. -@@ -119,7 +119,7 @@ Reload the signature databases. +@@ -125,7 +125,7 @@ Reload the signature databases. Perform a clean exit. .SH "FILES" .LP @@ -79,3 +58,30 @@ .SH "CREDITS" Please check the full documentation for credits. .SH "AUTHOR" +diff -up clamav-0.103.0/docs/man/clamd.conf.5.in.default_confs clamav-0.103.0/docs/man/clamd.conf.5.in +--- clamav-0.103.0/docs/man/clamd.conf.5.in.default_confs 2020-09-17 22:00:20.795879816 -0600 ++++ clamav-0.103.0/docs/man/clamd.conf.5.in 2020-09-17 22:01:21.414353121 -0600 +@@ -759,7 +759,7 @@ Default: no + All options expressing a size are limited to max 4GB. Values in excess will be reset to the maximum. + .SH "FILES" + .LP +-@CFGDIR@/clamd.conf ++@CFGDIR@/clamd.d/scan.conf + .SH "AUTHORS" + .LP + Tomasz Kojm , Kevin Lin +diff -up clamav-0.103.0/platform.h.in.default_confs clamav-0.103.0/platform.h.in +--- clamav-0.103.0/platform.h.in.default_confs 2020-09-17 22:00:20.796879824 -0600 ++++ clamav-0.103.0/platform.h.in 2020-09-17 22:01:56.842629739 -0600 +@@ -112,9 +112,9 @@ typedef unsigned int in_addr_t; + #endif + + #ifndef _WIN32 +-#define CONFDIR_CLAMD CONFDIR PATHSEP "clamd.conf" ++#define CONFDIR_CLAMD CONFDIR PATHSEP "clamd.d/scan.conf" + #define CONFDIR_FRESHCLAM CONFDIR PATHSEP "freshclam.conf" +-#define CONFDIR_MILTER CONFDIR PATHSEP "clamav-milter.conf" ++#define CONFDIR_MILTER CONFDIR PATHSEP "mail/clamav-milter.conf" + #endif + + #ifndef WORDS_BIGENDIAN diff --git a/clamav-freshclam.service.patch b/clamav-freshclam.service.patch new file mode 100644 index 0000000000000000000000000000000000000000..c1ff9bc0922a1892c466263d8f0ad98b17386fac --- /dev/null +++ b/clamav-freshclam.service.patch @@ -0,0 +1,10 @@ +--- ./freshclam/clamav-freshclam.service.in.orig 2021-02-01 20:49:25.000000000 +0000 ++++ ./freshclam/clamav-freshclam.service.in 2021-03-07 22:59:34.476455890 +0000 +@@ -8,7 +8,6 @@ After=network-online.target + + [Service] + ExecStart=@prefix@/bin/freshclam -d --foreground=true +-StandardOutput=syslog + + [Install] + WantedBy=multi-user.target diff --git a/clamav-stats-deprecation.patch b/clamav-stats-deprecation.patch new file mode 100644 index 0000000000000000000000000000000000000000..a12f138e00ca19f43ef95dffbdbde374b1929e3a --- /dev/null +++ b/clamav-stats-deprecation.patch @@ -0,0 +1,17 @@ +diff -up clamav-0.102.0/shared/optparser.c.stats-deprecation clamav-0.102.0/shared/optparser.c +--- clamav-0.102.0/shared/optparser.c.stats-deprecation 2019-10-10 21:55:31.245995091 -0600 ++++ clamav-0.102.0/shared/optparser.c 2019-10-11 20:40:04.580067432 -0600 +@@ -524,6 +524,13 @@ const struct clam_option __clam_options[ + {"ArchiveLimitMemoryUsage", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, 0, OPT_CLAMD | OPT_DEPRECATED, "", ""}, + {"MailFollowURLs", "mail-follow-urls", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN | OPT_DEPRECATED, "", ""}, + {"AllowSupplementaryGroups", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM | OPT_MILTER | OPT_DEPRECATED, "Initialize a supplementary group access (the process must be started by root).", "no"}, ++ {"StatsHostID", "stats-host-id", 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM | OPT_CLAMD | OPT_CLAMSCAN | OPT_DEPRECATED, "", "" }, ++ {"StatsEnabled", "enable-stats", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM | OPT_CLAMSCAN | OPT_DEPRECATED, "", ""}, ++ {"StatsPEDisabled", "disable-pe-stats", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN | OPT_DEPRECATED, "", ""}, ++ {"StatsTimeout", "stats-timeout", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, -1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN | OPT_FRESHCLAM | OPT_DEPRECATED, "", ""}, ++ {"SubmitDetectionStats", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM | OPT_DEPRECATED, "", ""}, ++ {"DetectionStatsCountry", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM | OPT_DEPRECATED, "", ""}, ++ {"DetectionStatsHostID", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM | OPT_DEPRECATED, "", ""}, + {"ScanOnAccess", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, 0, OPT_CLAMD | OPT_DEPRECATED, "", ""}, + + /* Milter specific options */ diff --git a/clamav.spec b/clamav.spec index 3146bbf62bc91f28138b2dde4553e58b84b95a02..f7f6b004a21e7c096b6c2cf479bd4db736e2d1fb 100644 --- a/clamav.spec +++ b/clamav.spec @@ -1,7 +1,7 @@ Name: clamav Summary: End-user tools for the Clam Antivirus scanner -Version: 0.101.4 -Release: 9 +Version: 0.103.2 +Release: 1 License: GPLv2 and Public Domain and bzip2-1.0.6 and Zlib and Apache-2.0 URL: https://www.clamav.net/ Source0: https://www.clamav.net/downloads/production/clamav-%{version}.tar.gz @@ -20,16 +20,12 @@ Source13: clamd.scan.upstart Source14: clamd@scan.service Source15: clamd@.service -Patch0001: clamav-0.100.0-stats-deprecation.patch -Patch0002: clamav-0.100.1-defaults_locations.patch +Patch0001: clamav-stats-deprecation.patch +Patch0002: clamav-default_confs.patch Patch0003: clamav-0.99-private.patch -Patch0004: clamav-0.100.0-umask.patch -Patch0005: llvm-glibc.patch -Patch0006: clamav-Fix-int64-overflow-check.patch -Patch0007: CVE-2019-15961-pre-1.patch -Patch0008: CVE-2019-15961-pre-2.patch -Patch0009: CVE-2019-15961-1.patch -Patch0010: CVE-2019-15961-2.patch +Patch0004: clamav-check.patch +Patch0005: clamav-clamonacc-service.patch +Patch0006: clamav-freshclam.service.patch BuildRequires: autoconf automake gettext-devel libtool libtool-ltdl-devel BuildRequires: gcc-c++ zlib-devel bzip2-devel gmp-devel curl-devel json-c-devel @@ -349,6 +345,9 @@ test -e %_var/log/clamav-milter.log || { %_bindir/{clambc,clamconf,clamdscan,clamdtop,clamscan,clamsubmit,sigtool} %_libdir/libclamav.so.9* %_libdir/libclammspack.so.0* +%_sbindir/clamonacc +%_unitdir/clamav-clamonacc.service + %files devel %_includedir/* @@ -361,6 +360,7 @@ test -e %_var/log/clamav-milter.log || { %_mandir/man[15]/* %_mandir/man8/clamd.8* %_mandir/man8/clamav-milter* +%_mandir/man8/clamonacc.8* %_mandir/*/freshclam* %files filesystem @@ -377,6 +377,7 @@ test -e %_var/log/clamav-milter.log || { %files update %_bindir/freshclam +%_libdir/libfreshclam.so.2* %_datadir/%name/freshclam-sleep %config(noreplace) %verify(not mtime) %_sysconfdir/freshclam.conf %config(noreplace) %verify(not mtime) %_sysconfdir/logrotate.d/* @@ -409,17 +410,20 @@ test -e %_var/log/clamav-milter.log || { %changelog +* Fri Apr 16 2021 wangyue - 0.103.2-1 +- Upgrade to 0.103.2 + * Thu Feb 18 2021 zhanghua - 0.101.4-9 - fix CVE-2019-15961 * Thu Nov 26 2020 wutao - 0.101.4-8 - drop clamd@scan.service file, change /var/run to /run -* Sat Oct 10 2020 lingsheng - 0.101.4-7 -- Fix int64 overflow check +* Mon Oct 12 2020 baizhonggui - 0.101.4-7 +- Modify source0 wrote format -* Wed Sep 16 2020 Ge Wang - 0.101.4-6 -- Modify Source0 Url +* Thu Sep 17 2020 lingsheng - 0.101.4-6 +- Fix int64 overflow check * Thu Mar 12 2020 wutao - 0.101.4-5 - Type:N/A diff --git a/llvm-glibc.patch b/llvm-glibc.patch deleted file mode 100644 index 67dbdd7d8bc2938b1d68baadcc773f393108fdd9..0000000000000000000000000000000000000000 --- a/llvm-glibc.patch +++ /dev/null @@ -1,12 +0,0 @@ -Index: clamav-0.97.3/libclamav/c++/llvm/lib/ExecutionEngine/JIT/Intercept.cpp -=================================================================== ---- clamav-0.97.3.orig/libclamav/c++/llvm/lib/ExecutionEngine/JIT/Intercept.cpp -+++ clamav-0.97.3/libclamav/c++/llvm/lib/ExecutionEngine/JIT/Intercept.cpp -@@ -52,6 +52,7 @@ static void runAtExitHandlers() { - #include - #endif - #include -+#include - /* stat functions are redirecting to __xstat with a version number. On x86-64 - * linking with libc_nonshared.a and -Wl,--export-dynamic doesn't make 'stat' - * available as an exported symbol, so we have to add it explicitly.