clamav/CVE-2019-15961-pre-2.patch
2021-02-22 09:33:35 +08:00

6751 lines
206 KiB
Diff

From fd62a171b65353c038cd93455c6493c78acebe26 Mon Sep 17 00:00:00 2001
From: Andy Ragusa <aragusa@cisco.com>
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 <errno.h>
#include <assert.h>
#include <string.h>
-#ifdef HAVE_STRINGS_H
+#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
-#ifdef HAVE_STRING_H
+#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
-#ifdef HAVE_SYS_PARAM_H
+#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include <dirent.h>
#include <limits.h>
#include <signal.h>
-#ifdef HAVE_UNISTD_H
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -63,7 +63,7 @@
#include <stddef.h>
#endif
-#ifdef CL_THREAD_SAFE
+#ifdef CL_THREAD_SAFE
#include <pthread.h>
#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 <features.h>
#endif
@@ -101,52 +101,53 @@
#include <execinfo.h>
#include <syslog.h>
-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 <stdbool.h>
#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 <sys/time.h>
#include <netdb.h>
#include <sys/socket.h>
@@ -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, "<xml>"))) {
- xmlend = strstr(xmlsrt, "</xml>");
- if (xmlend == NULL) {
- cli_dbgmsg("parseMHTMLComment: unbounded xml tag\n");
- break;
- }
+ xmlend = comment;
+ while ((xmlsrt = strstr(xmlend, "<xml>"))) {
+ xmlend = strstr(xmlsrt, "</xml>");
+ 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: <nRfkHdrKsAxRU>
* 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; i<buffer_len-1; i++) {
- char c;
-
- if(!input_len--) {
- if(curbuf == buffer) {
- /* EOF on first char */
- return NULL;
- }
- break;
- }
-
- switch((c = *cursrc++)) {
- case '\0':
- continue;
- case '\n':
- *curbuf++ = '\n';
- if(input_len && *cursrc == '\r') {
- i++;
- cursrc++;
- }
- break;
- case '\r':
- *curbuf++ = '\r';
- if(input_len && *cursrc == '\n') {
- i++;
- cursrc++;
- }
- break;
- default:
- *curbuf++ = c;
- continue;
- }
- break;
+ for (i = 0; i < buffer_len - 1; i++) {
+ char c;
+
+ if (!input_len--) {
+ if (curbuf == buffer) {
+ /* EOF on first char */
+ return NULL;
+ }
+ break;
+ }
+
+ switch ((c = *cursrc++)) {
+ case '\0':
+ continue;
+ case '\n':
+ *curbuf++ = '\n';
+ if (input_len && *cursrc == '\r') {
+ i++;
+ cursrc++;
+ }
+ break;
+ case '\r':
+ *curbuf++ = '\r';
+ if (input_len && *cursrc == '\n') {
+ i++;
+ cursrc++;
+ }
+ break;
+ default:
+ *curbuf++ = c;
+ continue;
+ }
+ break;
}
*at += cursrc - src;
*curbuf = '\0';
@@ -3676,41 +3654,41 @@ getline_from_mbox(char *buffer, size_t buffer_len, fmap_t *map, size_t *at)
static bool
isBounceStart(mbox_ctx *mctx, const char *line)
{
- size_t len;
+ size_t len;
- if(line == NULL)
- return FALSE;
- if(*line == '\0')
- return FALSE;
- /*if((strncmp(line, "From ", 5) == 0) && !isalnum(line[5]))
+ if (line == NULL)
+ return FALSE;
+ if (*line == '\0')
+ return FALSE;
+ /*if((strncmp(line, "From ", 5) == 0) && !isalnum(line[5]))
return FALSE;
if((strncmp(line, ">From ", 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;
}