From fd62a171b65353c038cd93455c6493c78acebe26 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 19 Nov 2019 07:33:18 -0800 Subject: [PATCH] clang-formatted mbox.c --- libclamav/mbox.c | 5547 +++++++++++++++++++++++----------------------- 1 file changed, 2762 insertions(+), 2785 deletions(-) diff --git a/libclamav/mbox.c b/libclamav/mbox.c index 7fee0cab4c..d4afa78cd2 100644 --- a/libclamav/mbox.c +++ b/libclamav/mbox.c @@ -29,8 +29,8 @@ #endif #ifdef CL_THREAD_SAFE -#ifndef _REENTRANT -#define _REENTRANT /* for Solaris 2.8 */ +#ifndef _REENTRANT +#define _REENTRANT /* for Solaris 2.8 */ #endif #endif @@ -39,23 +39,23 @@ #include #include #include -#ifdef HAVE_STRINGS_H +#ifdef HAVE_STRINGS_H #include #endif -#ifdef HAVE_STRING_H +#ifdef HAVE_STRING_H #include #endif #include #include #include -#ifdef HAVE_SYS_PARAM_H +#ifdef HAVE_SYS_PARAM_H #include #endif #include #include #include -#ifdef HAVE_UNISTD_H +#ifdef HAVE_UNISTD_H #include #endif @@ -63,7 +63,7 @@ #include #endif -#ifdef CL_THREAD_SAFE +#ifdef CL_THREAD_SAFE #include #endif @@ -86,9 +86,9 @@ #define DCONF_PHISHING mctx->ctx->dconf->phishing -#ifdef CL_DEBUG +#ifdef CL_DEBUG -#if defined(C_LINUX) +#if defined(C_LINUX) #include #endif @@ -101,52 +101,53 @@ #include #include -static void sigsegv(int sig); -static void print_trace(int use_syslog); +static void sigsegv(int sig); +static void print_trace(int use_syslog); -/*#define SAVE_TMP */ /* Save the file being worked on in tmp */ +/*#define SAVE_TMP */ /* Save the file being worked on in tmp */ #endif -#if defined(NO_STRTOK_R) || !defined(CL_THREAD_SAFE) +#if defined(NO_STRTOK_R) || !defined(CL_THREAD_SAFE) #undef strtok_r #undef __strtok_r -#define strtok_r(a,b,c) strtok(a,b) +#define strtok_r(a, b, c) strtok(a, b) #endif -#ifdef HAVE_STDBOOL_H -#ifdef C_BEOS +#ifdef HAVE_STDBOOL_H +#ifdef C_BEOS #include "SupportDefs.h" #else #include #endif #else -#ifdef FALSE -typedef unsigned char bool; +#ifdef FALSE +typedef unsigned char bool; #else -typedef enum { FALSE = 0, TRUE = 1 } bool; +typedef enum { FALSE = 0, + TRUE = 1 } bool; #endif #endif -typedef enum { - FAIL, - OK, - OK_ATTACHMENTS_NOT_SAVED, - VIRUS, - MAXREC, - MAXFILES +typedef enum { + FAIL, + OK, + OK_ATTACHMENTS_NOT_SAVED, + VIRUS, + MAXREC, + MAXFILES } mbox_status; #ifndef isblank -#define isblank(c) (((c) == ' ') || ((c) == '\t')) +#define isblank(c) (((c) == ' ') || ((c) == '\t')) #endif -#define SAVE_TO_DISC /* multipart/message are saved in a temporary file */ +#define SAVE_TO_DISC /* multipart/message are saved in a temporary file */ #include "htmlnorm.h" #include "phishcheck.h" -#ifndef _WIN32 +#ifndef _WIN32 #include #include #include @@ -170,19 +171,19 @@ typedef enum { */ /*#define NEW_WORLD*/ -/*#define SCAN_UNENCODED_BOUNCES *//* +/*#define SCAN_UNENCODED_BOUNCES */ /* * Slows things down a lot and only catches unencoded copies * of EICAR within bounces, which don't matter */ -typedef struct mbox_ctx { - const char *dir; - const table_t *rfc821Table; - const table_t *subtypeTable; - cli_ctx *ctx; - unsigned int files; /* number of files extracted */ +typedef struct mbox_ctx { + const char *dir; + const table_t *rfc821Table; + const table_t *subtypeTable; + cli_ctx *ctx; + unsigned int files; /* number of files extracted */ #if HAVE_JSON - json_object *wrkobj; + json_object *wrkobj; #endif } mbox_ctx; @@ -199,157 +200,134 @@ typedef struct mbox_ctx { #define UNLOCKFILE(fp) #endif -static int cli_parse_mbox(const char *dir, cli_ctx *ctx); -static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir); -static message *parseEmailHeaders(message *m, const table_t *rfc821Table); -static int parseEmailHeader(message *m, const char *line, const table_t *rfc821Table); -static int parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata); -static mbox_status parseRootMHTML(mbox_ctx *mctx, message *m, text *t); -static mbox_status parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level); -static int boundaryStart(const char *line, const char *boundary); -static int boundaryEnd(const char *line, const char *boundary); -static int initialiseTables(table_t **rfc821Table, table_t **subtypeTable); -static int getTextPart(message *const messages[], size_t size); -static size_t strip(char *buf, int len); -static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg); -static int saveTextPart(mbox_ctx *mctx, message *m, int destroy_text); -static char *rfc2047(const char *in); -static char *rfc822comments(const char *in, char *out); -static int rfc1341(message *m, const char *dir); -static bool usefulHeader(int commandNumber, const char *cmd); -static char *getline_from_mbox(char *buffer, size_t len, fmap_t *map, size_t *at); -static bool isBounceStart(mbox_ctx *mctx, const char *line); -static bool exportBinhexMessage(mbox_ctx *mctx, message *m); -static int exportBounceMessage(mbox_ctx *ctx, text *start); -static const char *getMimeTypeStr(mime_type mimetype); -static const char *getEncTypeStr(encoding_type enctype); -static message *do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, mbox_ctx *mctx, message *messageIn, text **tptr, unsigned int recursion_level); -static int count_quotes(const char *buf); -static bool next_is_folded_header(const text *t); -static bool newline_in_header(const char *line); - -static blob *getHrefs(message *m, tag_arguments_t *hrefs); -static void hrefs_done(blob *b, tag_arguments_t *hrefs); -static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html); +static int cli_parse_mbox(const char *dir, cli_ctx *ctx); +static message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir); +static message *parseEmailHeaders(message *m, const table_t *rfc821Table); +static int parseEmailHeader(message *m, const char *line, const table_t *rfc821Table); +static int parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata); +static mbox_status parseRootMHTML(mbox_ctx *mctx, message *m, text *t); +static mbox_status parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level); +static int boundaryStart(const char *line, const char *boundary); +static int boundaryEnd(const char *line, const char *boundary); +static int initialiseTables(table_t **rfc821Table, table_t **subtypeTable); +static int getTextPart(message *const messages[], size_t size); +static size_t strip(char *buf, int len); +static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg); +static int saveTextPart(mbox_ctx *mctx, message *m, int destroy_text); +static char *rfc2047(const char *in); +static char *rfc822comments(const char *in, char *out); +static int rfc1341(message *m, const char *dir); +static bool usefulHeader(int commandNumber, const char *cmd); +static char *getline_from_mbox(char *buffer, size_t len, fmap_t *map, size_t *at); +static bool isBounceStart(mbox_ctx *mctx, const char *line); +static bool exportBinhexMessage(mbox_ctx *mctx, message *m); +static int exportBounceMessage(mbox_ctx *ctx, text *start); +static const char *getMimeTypeStr(mime_type mimetype); +static const char *getEncTypeStr(encoding_type enctype); +static message *do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, mbox_ctx *mctx, message *messageIn, text **tptr, unsigned int recursion_level); +static int count_quotes(const char *buf); +static bool next_is_folded_header(const text *t); +static bool newline_in_header(const char *line); + +static blob *getHrefs(message *m, tag_arguments_t *hrefs); +static void hrefs_done(blob *b, tag_arguments_t *hrefs); +static void checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html); /* Maximum line length according to RFC2821 */ -#define RFC2821LENGTH 1000 +#define RFC2821LENGTH 1000 /* Hashcodes for our hash tables */ -#define CONTENT_TYPE 1 -#define CONTENT_TRANSFER_ENCODING 2 -#define CONTENT_DISPOSITION 3 +#define CONTENT_TYPE 1 +#define CONTENT_TRANSFER_ENCODING 2 +#define CONTENT_DISPOSITION 3 /* Mime sub types */ -#define PLAIN 1 -#define ENRICHED 2 -#define HTML 3 -#define RICHTEXT 4 -#define MIXED 5 -#define ALTERNATIVE 6 /* RFC1521*/ -#define DIGEST 7 -#define SIGNED 8 -#define PARALLEL 9 -#define RELATED 10 /* RFC2387 */ -#define REPORT 11 /* RFC1892 */ -#define APPLEDOUBLE 12 /* Handling of this in only noddy for now */ -#define FAX MIXED /* - * RFC3458 - * Drafts stated to treat is as mixed if it is - * not known. This disappeared in the final - * version (except when talking about - * voice-message), but it is good enough for us - * since we do no validation of coversheet - * presence etc. (which also has disappeared - * in the final version) - */ -#define ENCRYPTED 13 /* - * e.g. RFC2015 - * Content-Type: multipart/encrypted; - * boundary="nextPart1383049.XCRrrar2yq"; - * protocol="application/pgp-encrypted" - */ -#define X_BFILE RELATED /* - * BeOS, expert two parts: the file and it's - * attributes. The attributes part comes as - * Content-Type: application/x-be_attribute - * name="foo" - * I can't find where it is defined, any - * pointers would be appreciated. For now - * we treat it as multipart/related - */ -#define KNOWBOT 14 /* Unknown and undocumented format? */ - -static const struct tableinit { - const char *key; - int value; +#define PLAIN 1 +#define ENRICHED 2 +#define HTML 3 +#define RICHTEXT 4 +#define MIXED 5 +#define ALTERNATIVE 6 /* RFC1521*/ +#define DIGEST 7 +#define SIGNED 8 +#define PARALLEL 9 +#define RELATED 10 /* RFC2387 */ +#define REPORT 11 /* RFC1892 */ +#define APPLEDOUBLE 12 /* Handling of this in only noddy for now */ +#define FAX MIXED /* \ + * RFC3458 \ + * Drafts stated to treat is as mixed if it is \ + * not known. This disappeared in the final \ + * version (except when talking about \ + * voice-message), but it is good enough for us \ + * since we do no validation of coversheet \ + * presence etc. (which also has disappeared \ + * in the final version) \ + */ +#define ENCRYPTED 13 /* \ + * e.g. RFC2015 \ + * Content-Type: multipart/encrypted; \ + * boundary="nextPart1383049.XCRrrar2yq"; \ + * protocol="application/pgp-encrypted" \ + */ +#define X_BFILE RELATED /* \ + * BeOS, expert two parts: the file and it's \ + * attributes. The attributes part comes as \ + * Content-Type: application/x-be_attribute \ + * name="foo" \ + * I can't find where it is defined, any \ + * pointers would be appreciated. For now \ + * we treat it as multipart/related \ + */ +#define KNOWBOT 14 /* Unknown and undocumented format? */ + +static const struct tableinit { + const char *key; + int value; } rfc821headers[] = { - /* TODO: make these regular expressions */ - { "Content-Type", CONTENT_TYPE }, - { "Content-Transfer-Encoding", CONTENT_TRANSFER_ENCODING }, - { "Content-Disposition", CONTENT_DISPOSITION }, - { NULL, 0 } -}, mimeSubtypes[] = { /* see RFC2045 */ - /* subtypes of Text */ - { "plain", PLAIN }, - { "enriched", ENRICHED }, - { "html", HTML }, - { "richtext", RICHTEXT }, - /* subtypes of Multipart */ - { "mixed", MIXED }, - { "alternative", ALTERNATIVE }, - { "digest", DIGEST }, - { "signed", SIGNED }, - { "parallel", PARALLEL }, - { "related", RELATED }, - { "report", REPORT }, - { "appledouble", APPLEDOUBLE }, - { "fax-message", FAX }, - { "encrypted", ENCRYPTED }, - { "x-bfile", X_BFILE }, /* BeOS */ - { "knowbot", KNOWBOT }, /* ??? */ - { "knowbot-metadata", KNOWBOT }, /* ??? */ - { "knowbot-code", KNOWBOT }, /* ??? */ - { "knowbot-state", KNOWBOT }, /* ??? */ - { NULL, 0 } -}, mimeTypeStr[] = { - { "NOMIME", NOMIME }, - { "APPLICATION", APPLICATION }, - { "AUDIO", AUDIO }, - { "IMAGE", IMAGE }, - { "MESSAGE", MESSAGE }, - { "MULTIPART", MULTIPART }, - { "TEXT", TEXT }, - { "VIDEO", VIDEO }, - { "MEXTENSION", MEXTENSION }, - { NULL, 0 } -}, encTypeStr[] = { - { "NOENCODING", NOENCODING }, - { "QUOTEDPRINTABLE", QUOTEDPRINTABLE }, - { "BASE64", BASE64 }, - { "EIGHTBIT", EIGHTBIT }, - { "BINARY", BINARY }, - { "UUENCODE", UUENCODE }, - { "YENCODE", YENCODE }, - { "EEXTENSION", EEXTENSION }, - { "BINHEX", BINHEX }, - { NULL, 0 } -}; - -#ifdef CL_THREAD_SAFE -static pthread_mutex_t tables_mutex = PTHREAD_MUTEX_INITIALIZER; + /* TODO: make these regular expressions */ + {"Content-Type", CONTENT_TYPE}, + {"Content-Transfer-Encoding", CONTENT_TRANSFER_ENCODING}, + {"Content-Disposition", CONTENT_DISPOSITION}, + {NULL, 0}}, + mimeSubtypes[] = {/* see RFC2045 */ + /* subtypes of Text */ + {"plain", PLAIN}, + {"enriched", ENRICHED}, + {"html", HTML}, + {"richtext", RICHTEXT}, + /* subtypes of Multipart */ + {"mixed", MIXED}, + {"alternative", ALTERNATIVE}, + {"digest", DIGEST}, + {"signed", SIGNED}, + {"parallel", PARALLEL}, + {"related", RELATED}, + {"report", REPORT}, + {"appledouble", APPLEDOUBLE}, + {"fax-message", FAX}, + {"encrypted", ENCRYPTED}, + {"x-bfile", X_BFILE}, /* BeOS */ + {"knowbot", KNOWBOT}, /* ??? */ + {"knowbot-metadata", KNOWBOT}, /* ??? */ + {"knowbot-code", KNOWBOT}, /* ??? */ + {"knowbot-state", KNOWBOT}, /* ??? */ + {NULL, 0}}, + mimeTypeStr[] = {{"NOMIME", NOMIME}, {"APPLICATION", APPLICATION}, {"AUDIO", AUDIO}, {"IMAGE", IMAGE}, {"MESSAGE", MESSAGE}, {"MULTIPART", MULTIPART}, {"TEXT", TEXT}, {"VIDEO", VIDEO}, {"MEXTENSION", MEXTENSION}, {NULL, 0}}, encTypeStr[] = {{"NOENCODING", NOENCODING}, {"QUOTEDPRINTABLE", QUOTEDPRINTABLE}, {"BASE64", BASE64}, {"EIGHTBIT", EIGHTBIT}, {"BINARY", BINARY}, {"UUENCODE", UUENCODE}, {"YENCODE", YENCODE}, {"EEXTENSION", EEXTENSION}, {"BINHEX", BINHEX}, {NULL, 0}}; + +#ifdef CL_THREAD_SAFE +static pthread_mutex_t tables_mutex = PTHREAD_MUTEX_INITIALIZER; #endif -static table_t *rfc821 = NULL; -static table_t *subtype = NULL; +static table_t *rfc821 = NULL; +static table_t *subtype = NULL; -int -cli_mbox(const char *dir, cli_ctx *ctx) +int cli_mbox(const char *dir, cli_ctx *ctx) { - if(dir == NULL) { - cli_dbgmsg("cli_mbox called with NULL dir\n"); - return CL_ENULLARG; - } - return cli_parse_mbox(dir, ctx); + if (dir == NULL) { + cli_dbgmsg("cli_mbox called with NULL dir\n"); + return CL_ENULLARG; + } + return cli_parse_mbox(dir, ctx); } /* @@ -370,51 +348,51 @@ cli_mbox(const char *dir, cli_ctx *ctx) static int cli_parse_mbox(const char *dir, cli_ctx *ctx) { - int retcode; - message *body; - char buffer[RFC2821LENGTH + 1]; - mbox_ctx mctx; - size_t at = 0; - fmap_t *map = *ctx->fmap; - - cli_dbgmsg("in mbox()\n"); - - if(!fmap_gets(map, buffer, &at, sizeof(buffer) - 1)) { - /* empty message */ - return CL_CLEAN; - } -#ifdef CL_THREAD_SAFE - pthread_mutex_lock(&tables_mutex); + int retcode; + message *body; + char buffer[RFC2821LENGTH + 1]; + mbox_ctx mctx; + size_t at = 0; + fmap_t *map = *ctx->fmap; + + cli_dbgmsg("in mbox()\n"); + + if (!fmap_gets(map, buffer, &at, sizeof(buffer) - 1)) { + /* empty message */ + return CL_CLEAN; + } +#ifdef CL_THREAD_SAFE + pthread_mutex_lock(&tables_mutex); #endif - if(rfc821 == NULL) { - assert(subtype == NULL); - - if(initialiseTables(&rfc821, &subtype) < 0) { - rfc821 = NULL; - subtype = NULL; -#ifdef CL_THREAD_SAFE - pthread_mutex_unlock(&tables_mutex); + if (rfc821 == NULL) { + assert(subtype == NULL); + + if (initialiseTables(&rfc821, &subtype) < 0) { + rfc821 = NULL; + subtype = NULL; +#ifdef CL_THREAD_SAFE + pthread_mutex_unlock(&tables_mutex); #endif - return CL_EMEM; - } - } -#ifdef CL_THREAD_SAFE - pthread_mutex_unlock(&tables_mutex); + return CL_EMEM; + } + } +#ifdef CL_THREAD_SAFE + pthread_mutex_unlock(&tables_mutex); #endif - retcode = CL_SUCCESS; - body = NULL; + retcode = CL_SUCCESS; + body = NULL; - mctx.dir = dir; - mctx.rfc821Table = rfc821; - mctx.subtypeTable = subtype; - mctx.ctx = ctx; - mctx.files = 0; + mctx.dir = dir; + mctx.rfc821Table = rfc821; + mctx.subtypeTable = subtype; + mctx.ctx = ctx; + mctx.files = 0; #if HAVE_JSON - mctx.wrkobj = ctx->wrkproperty; + mctx.wrkobj = ctx->wrkproperty; #endif - /* + /* * Is it a UNIX style mbox with more than one * mail message, or just a single mail message? * @@ -423,9 +401,9 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) * than one message is handled, e.g. giving a better indication of * which message within the mailbox is infected */ - /*if((strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ - if(strncmp(buffer, "From ", 5) == 0) { - /* + /*if((strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ + if (strncmp(buffer, "From ", 5) == 0) { + /* * Have been asked to check a UNIX style mbox file, which * may contain more than one e-mail message to decode * @@ -443,47 +421,47 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) * This would remove a problem with this code that it can * fill up the tmp directory before it starts scanning */ - bool lastLineWasEmpty; - int messagenumber; - message *m = messageCreate(); - - if(m == NULL) - return CL_EMEM; - - lastLineWasEmpty = FALSE; - messagenumber = 1; - messageSetCTX(m, ctx); - - do { - cli_chomp(buffer); - /*if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ - if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0)) { - cli_dbgmsg("Deal with message number %d\n", messagenumber++); - /* + bool lastLineWasEmpty; + int messagenumber; + message *m = messageCreate(); + + if (m == NULL) + return CL_EMEM; + + lastLineWasEmpty = FALSE; + messagenumber = 1; + messageSetCTX(m, ctx); + + do { + cli_chomp(buffer); + /*if(lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0) && isalnum(buffer[5])) {*/ + if (lastLineWasEmpty && (strncmp(buffer, "From ", 5) == 0)) { + cli_dbgmsg("Deal with message number %d\n", messagenumber++); + /* * End of a message in the mail box */ - body = parseEmailHeaders(m, rfc821); - if(body == NULL) { - messageReset(m); - continue; - } - messageSetCTX(body, ctx); - messageDestroy(m); - if(messageGetBody(body)) { - mbox_status rc = parseEmailBody(body, NULL, &mctx, 0); - if(rc == FAIL) { - messageReset(body); - m = body; - continue; - } else if(rc == VIRUS) { - cli_dbgmsg("Message number %d is infected\n", - messagenumber-1); - retcode = CL_VIRUS; - m = NULL; - break; - } - } - /* + body = parseEmailHeaders(m, rfc821); + if (body == NULL) { + messageReset(m); + continue; + } + messageSetCTX(body, ctx); + messageDestroy(m); + if (messageGetBody(body)) { + mbox_status rc = parseEmailBody(body, NULL, &mctx, 0); + if (rc == FAIL) { + messageReset(body); + m = body; + continue; + } else if (rc == VIRUS) { + cli_dbgmsg("Message number %d is infected\n", + messagenumber - 1); + retcode = CL_VIRUS; + m = NULL; + break; + } + } + /* * Starting a new message, throw away all the * information about the old one. It would * be best to be able to scan this message @@ -491,72 +469,72 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) * that haven't been passed here so it can't be * called */ - m = body; - messageReset(body); - messageSetCTX(body, ctx); + m = body; + messageReset(body); + messageSetCTX(body, ctx); - cli_dbgmsg("Finished processing message\n"); - } else - lastLineWasEmpty = (bool)(buffer[0] == '\0'); + cli_dbgmsg("Finished processing message\n"); + } else + lastLineWasEmpty = (bool)(buffer[0] == '\0'); - if(isuuencodebegin(buffer)) { - /* + if (isuuencodebegin(buffer)) { + /* * Fast track visa to uudecode. * TODO: binhex, yenc */ - if(uudecodeFile(m, buffer, dir, map, &at) < 0) - if(messageAddStr(m, buffer) < 0) - break; - } else - /* at this point, the \n has been removed */ - if(messageAddStr(m, buffer) < 0) - break; - } while(fmap_gets(map, buffer, &at, sizeof(buffer) - 1)); - - if(retcode == CL_SUCCESS) { - cli_dbgmsg("Extract attachments from email %d\n", messagenumber); - body = parseEmailHeaders(m, rfc821); - } - if(m) - messageDestroy(m); - } else { - /* + if (uudecodeFile(m, buffer, dir, map, &at) < 0) + if (messageAddStr(m, buffer) < 0) + break; + } else + /* at this point, the \n has been removed */ + if (messageAddStr(m, buffer) < 0) + break; + } while (fmap_gets(map, buffer, &at, sizeof(buffer) - 1)); + + if (retcode == CL_SUCCESS) { + cli_dbgmsg("Extract attachments from email %d\n", messagenumber); + body = parseEmailHeaders(m, rfc821); + } + if (m) + messageDestroy(m); + } else { + /* * It's a single message, parse the headers then the body */ - if(strncmp(buffer, "P I ", 4) == 0) - /* + if (strncmp(buffer, "P I ", 4) == 0) + /* * CommuniGate Pro format: ignore headers until * blank line */ - while(fmap_gets(map, buffer, &at, sizeof(buffer) - 1) && - (strchr("\r\n", buffer[0]) == NULL)) - ; - /* getline_from_mbox could be using unlocked_stdio(3), + while (fmap_gets(map, buffer, &at, sizeof(buffer) - 1) && + (strchr("\r\n", buffer[0]) == NULL)) + ; + /* getline_from_mbox could be using unlocked_stdio(3), * so lock file here */ - /* + /* * Ignore any blank lines at the top of the message */ - while(strchr("\r\n", buffer[0]) && - (getline_from_mbox(buffer, sizeof(buffer) - 1, map, &at) != NULL)) - ; + while (strchr("\r\n", buffer[0]) && + (getline_from_mbox(buffer, sizeof(buffer) - 1, map, &at) != NULL)) + ; - buffer[sizeof(buffer) - 1] = '\0'; + buffer[sizeof(buffer) - 1] = '\0'; - body = parseEmailFile(map, &at, rfc821, buffer, dir); - } + body = parseEmailFile(map, &at, rfc821, buffer, dir); + } - if(body) { - /* + if (body) { + /* * Write out the last entry in the mailbox */ - if((retcode == CL_SUCCESS) && messageGetBody(body)) { - messageSetCTX(body, ctx); - switch(parseEmailBody(body, NULL, &mctx, 0)) { - case OK: - case OK_ATTACHMENTS_NOT_SAVED: - break; - case FAIL: - /* + if ((retcode == CL_SUCCESS) && messageGetBody(body)) { + messageSetCTX(body, ctx); + switch (parseEmailBody(body, NULL, &mctx, 0)) { + case OK: + case OK_ATTACHMENTS_NOT_SAVED: + break; + case FAIL: + /* * beware: cli_magic_scandesc(), * changes this into CL_CLEAN, so only * use it to inform the higher levels @@ -565,37 +543,37 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) * decoding errors on what *is* a valid * mbox */ - retcode = CL_EFORMAT; - break; - case MAXREC: - retcode = CL_EMAXREC; - break; - case MAXFILES: - retcode = CL_EMAXFILES; - break; - case VIRUS: - retcode = CL_VIRUS; - break; - } - } - - if(body->isTruncated && retcode == CL_SUCCESS) - retcode = CL_EMEM; - /* + retcode = CL_EFORMAT; + break; + case MAXREC: + retcode = CL_EMAXREC; + break; + case MAXFILES: + retcode = CL_EMAXFILES; + break; + case VIRUS: + retcode = CL_VIRUS; + break; + } + } + + if (body->isTruncated && retcode == CL_SUCCESS) + retcode = CL_EMEM; + /* * Tidy up and quit */ - messageDestroy(body); - } + messageDestroy(body); + } - if((retcode == CL_CLEAN) && ctx->found_possibly_unwanted && - (*ctx->virname == NULL || SCAN_ALLMATCHES)) { - retcode = cli_append_virus(ctx, "Heuristics.Phishing.Email"); - ctx->found_possibly_unwanted = 0; - } + if ((retcode == CL_CLEAN) && ctx->found_possibly_unwanted && + (*ctx->virname == NULL || SCAN_ALLMATCHES)) { + retcode = cli_append_virus(ctx, "Heuristics.Phishing.Email"); + ctx->found_possibly_unwanted = 0; + } - cli_dbgmsg("cli_mbox returning %d\n", retcode); + cli_dbgmsg("cli_mbox returning %d\n", retcode); - return retcode; + return retcode; } /* @@ -607,58 +585,58 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx) static message * parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *firstLine, const char *dir) { - bool inHeader = TRUE; - bool bodyIsEmpty = TRUE; - bool lastWasBlank = FALSE, lastBodyLineWasBlank = FALSE; - message *ret; - bool anyHeadersFound = FALSE; - int commandNumber = -1; - char *fullline = NULL, *boundary = NULL; - size_t fulllinelength = 0; - char buffer[RFC2821LENGTH + 1]; - - cli_dbgmsg("parseEmailFile\n"); - - ret = messageCreate(); - if(ret == NULL) - return NULL; - - strncpy(buffer, firstLine, sizeof(buffer)-1); - do { - const char *line; - - (void)cli_chomp(buffer); - - if(buffer[0] == '\0') - line = NULL; - else - line = buffer; - - /* + bool inHeader = TRUE; + bool bodyIsEmpty = TRUE; + bool lastWasBlank = FALSE, lastBodyLineWasBlank = FALSE; + message *ret; + bool anyHeadersFound = FALSE; + int commandNumber = -1; + char *fullline = NULL, *boundary = NULL; + size_t fulllinelength = 0; + char buffer[RFC2821LENGTH + 1]; + + cli_dbgmsg("parseEmailFile\n"); + + ret = messageCreate(); + if (ret == NULL) + return NULL; + + strncpy(buffer, firstLine, sizeof(buffer) - 1); + do { + const char *line; + + (void)cli_chomp(buffer); + + if (buffer[0] == '\0') + line = NULL; + else + line = buffer; + + /* * Don't blank lines which are only spaces from headers, * otherwise they'll be treated as the end of header marker */ - if(lastWasBlank) { - lastWasBlank = FALSE; - if(boundaryStart(buffer, boundary)) { - cli_dbgmsg("Found a header line with space that should be blank\n"); - inHeader = FALSE; - } - } - if(inHeader) { - cli_dbgmsg("parseEmailFile: check '%s' fullline %p\n", - buffer, fullline); - /* + if (lastWasBlank) { + lastWasBlank = FALSE; + if (boundaryStart(buffer, boundary)) { + cli_dbgmsg("Found a header line with space that should be blank\n"); + inHeader = FALSE; + } + } + if (inHeader) { + cli_dbgmsg("parseEmailFile: check '%s' fullline %p\n", + buffer, fullline); + /* * Ensure wide characters are handled where * sizeof(char) > 1 */ - if(line && isspace(line[0] & 0xFF)) { - char copy[sizeof(buffer)]; + if (line && isspace(line[0] & 0xFF)) { + char copy[sizeof(buffer)]; - strcpy(copy, buffer); - strstrip(copy); - if(copy[0] == '\0') { - /* + strcpy(copy, buffer); + strstrip(copy); + if (copy[0] == '\0') { + /* * The header line contains only white * space. This is not the end of the * headers according to RFC2822, but @@ -671,189 +649,189 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first * content-type line. So we just have * to make a best guess. Sigh. */ - if(fullline) { - if(parseEmailHeader(ret, fullline, rfc821) < 0) - continue; - - free(fullline); - fullline = NULL; - } - if(boundary || - ((boundary = (char *)messageFindArgument(ret, "boundary")) != NULL)) { - lastWasBlank = TRUE; - continue; - } - } - } - if((line == NULL) && (fullline == NULL)) { /* empty line */ - /* + if (fullline) { + if (parseEmailHeader(ret, fullline, rfc821) < 0) + continue; + + free(fullline); + fullline = NULL; + } + if (boundary || + ((boundary = (char *)messageFindArgument(ret, "boundary")) != NULL)) { + lastWasBlank = TRUE; + continue; + } + } + } + if ((line == NULL) && (fullline == NULL)) { /* empty line */ + /* * A blank line signifies the end of * the header and the start of the text */ - if(!anyHeadersFound) - /* Ignore the junk at the top */ - continue; + if (!anyHeadersFound) + /* Ignore the junk at the top */ + continue; - cli_dbgmsg("End of header information\n"); - inHeader = FALSE; - bodyIsEmpty = TRUE; - } else { - char *ptr; - const char *lookahead; + cli_dbgmsg("End of header information\n"); + inHeader = FALSE; + bodyIsEmpty = TRUE; + } else { + char *ptr; + const char *lookahead; - if(fullline == NULL) { - char cmd[RFC2821LENGTH + 1], out[RFC2821LENGTH + 1]; + if (fullline == NULL) { + char cmd[RFC2821LENGTH + 1], out[RFC2821LENGTH + 1]; - /* + /* * Continuation of line we're ignoring? */ - if(isblank(line[0])) - continue; + if (isblank(line[0])) + continue; - /* + /* * Is this a header we're interested in? */ - if((strchr(line, ':') == NULL) || - (cli_strtokbuf(line, 0, ":", cmd) == NULL)) { - if(strncmp(line, "From ", 5) == 0) - anyHeadersFound = TRUE; - continue; - } - - ptr = rfc822comments(cmd, out); - commandNumber = tableFind(rfc821, ptr ? ptr : cmd); - - switch(commandNumber) { - case CONTENT_TRANSFER_ENCODING: - case CONTENT_DISPOSITION: - case CONTENT_TYPE: - anyHeadersFound = TRUE; - break; - default: - if(!anyHeadersFound) - anyHeadersFound = usefulHeader(commandNumber, cmd); - continue; - } - fullline = cli_strdup(line); - fulllinelength = strlen(line) + 1; - if(!fullline) { - if(ret) - ret->isTruncated = TRUE; - break; - } - } else if(line != NULL) { - fulllinelength += strlen(line) + 1; - ptr = cli_realloc(fullline, fulllinelength); - if(ptr == NULL) - continue; - fullline = ptr; - cli_strlcat(fullline, line, fulllinelength); - } - - assert(fullline != NULL); - - if((lookahead = fmap_need_off_once(map, *at, 1))) { - /* + if ((strchr(line, ':') == NULL) || + (cli_strtokbuf(line, 0, ":", cmd) == NULL)) { + if (strncmp(line, "From ", 5) == 0) + anyHeadersFound = TRUE; + continue; + } + + ptr = rfc822comments(cmd, out); + commandNumber = tableFind(rfc821, ptr ? ptr : cmd); + + switch (commandNumber) { + case CONTENT_TRANSFER_ENCODING: + case CONTENT_DISPOSITION: + case CONTENT_TYPE: + anyHeadersFound = TRUE; + break; + default: + if (!anyHeadersFound) + anyHeadersFound = usefulHeader(commandNumber, cmd); + continue; + } + fullline = cli_strdup(line); + fulllinelength = strlen(line) + 1; + if (!fullline) { + if (ret) + ret->isTruncated = TRUE; + break; + } + } else if (line != NULL) { + fulllinelength += strlen(line) + 1; + ptr = cli_realloc(fullline, fulllinelength); + if (ptr == NULL) + continue; + fullline = ptr; + cli_strlcat(fullline, line, fulllinelength); + } + + assert(fullline != NULL); + + if ((lookahead = fmap_need_off_once(map, *at, 1))) { + /* * Section B.2 of RFC822 says TAB or * SPACE means a continuation of the * previous entry. * * Add all the arguments on the line */ - if(isblank(*lookahead)) - continue; - } + if (isblank(*lookahead)) + continue; + } - /* + /* * Handle broken headers, where the next * line isn't indented by whitespace */ - if(fullline[strlen(fullline) - 1] == ';') - /* Add arguments to this line */ - continue; - - if(line && (count_quotes(fullline) & 1)) - continue; - - ptr = rfc822comments(fullline, NULL); - if(ptr) { - free(fullline); - fullline = ptr; - } - - if(parseEmailHeader(ret, fullline, rfc821) < 0) - continue; - - free(fullline); - fullline = NULL; - } - } else if(line && isuuencodebegin(line)) { - /* + if (fullline[strlen(fullline) - 1] == ';') + /* Add arguments to this line */ + continue; + + if (line && (count_quotes(fullline) & 1)) + continue; + + ptr = rfc822comments(fullline, NULL); + if (ptr) { + free(fullline); + fullline = ptr; + } + + if (parseEmailHeader(ret, fullline, rfc821) < 0) + continue; + + free(fullline); + fullline = NULL; + } + } else if (line && isuuencodebegin(line)) { + /* * Fast track visa to uudecode. * TODO: binhex, yenc */ - bodyIsEmpty = FALSE; - if(uudecodeFile(ret, line, dir, map, at) < 0) - if(messageAddStr(ret, line) < 0) - break; - } else { - if(line == NULL) { - /* + bodyIsEmpty = FALSE; + if (uudecodeFile(ret, line, dir, map, at) < 0) + if (messageAddStr(ret, line) < 0) + break; + } else { + if (line == NULL) { + /* * Although this would save time and RAM, some * phish signatures have been built which need * the blank lines */ - if(lastBodyLineWasBlank && - (messageGetMimeType(ret) != TEXT)) { - cli_dbgmsg("Ignoring consecutive blank lines in the body\n"); - continue; - } - lastBodyLineWasBlank = TRUE; - } else { - if(bodyIsEmpty) { - /* + if (lastBodyLineWasBlank && + (messageGetMimeType(ret) != TEXT)) { + cli_dbgmsg("Ignoring consecutive blank lines in the body\n"); + continue; + } + lastBodyLineWasBlank = TRUE; + } else { + if (bodyIsEmpty) { + /* * Broken message: new line in the * middle of the headers, so the first * line of the body is in fact * the last lines of the header */ - if(newline_in_header(line)) - continue; - bodyIsEmpty = FALSE; - } - lastBodyLineWasBlank = FALSE; - } - - if(messageAddStr(ret, line) < 0) - break; - } - } while(getline_from_mbox(buffer, sizeof(buffer) - 1, map, at) != NULL); - - if(boundary) - free(boundary); - - if(fullline) { - if(*fullline) switch(commandNumber) { - case CONTENT_TRANSFER_ENCODING: - case CONTENT_DISPOSITION: - case CONTENT_TYPE: - cli_dbgmsg("parseEmailFile: Fullline unparsed '%s'\n", fullline); - } - free(fullline); - } - - if(!anyHeadersFound) { - /* + if (newline_in_header(line)) + continue; + bodyIsEmpty = FALSE; + } + lastBodyLineWasBlank = FALSE; + } + + if (messageAddStr(ret, line) < 0) + break; + } + } while (getline_from_mbox(buffer, sizeof(buffer) - 1, map, at) != NULL); + + if (boundary) + free(boundary); + + if (fullline) { + if (*fullline) switch (commandNumber) { + case CONTENT_TRANSFER_ENCODING: + case CONTENT_DISPOSITION: + case CONTENT_TYPE: + cli_dbgmsg("parseEmailFile: Fullline unparsed '%s'\n", fullline); + } + free(fullline); + } + + if (!anyHeadersFound) { + /* * False positive in believing we have an e-mail when we don't */ - messageDestroy(ret); - cli_dbgmsg("parseEmailFile: no headers found, assuming it isn't an email\n"); - return NULL; - } + messageDestroy(ret); + cli_dbgmsg("parseEmailFile: no headers found, assuming it isn't an email\n"); + return NULL; + } - cli_dbgmsg("parseEmailFile: return\n"); + cli_dbgmsg("parseEmailFile: return\n"); - return ret; + return ret; } /* @@ -867,162 +845,162 @@ parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *first static message * parseEmailHeaders(message *m, const table_t *rfc821) { - bool inHeader = TRUE; - bool bodyIsEmpty = TRUE; - text *t; - message *ret; - bool anyHeadersFound = FALSE; - int commandNumber = -1; - char *fullline = NULL; - size_t fulllinelength = 0; - - cli_dbgmsg("parseEmailHeaders\n"); - - if(m == NULL) - return NULL; - - ret = messageCreate(); - - for(t = messageGetBody(m); t; t = t->t_next) { - const char *line; - - if(t->t_line) - line = lineGetData(t->t_line); - else - line = NULL; - - if(inHeader) { - cli_dbgmsg("parseEmailHeaders: check '%s'\n", - line ? line : ""); - if(line == NULL) { - /* + bool inHeader = TRUE; + bool bodyIsEmpty = TRUE; + text *t; + message *ret; + bool anyHeadersFound = FALSE; + int commandNumber = -1; + char *fullline = NULL; + size_t fulllinelength = 0; + + cli_dbgmsg("parseEmailHeaders\n"); + + if (m == NULL) + return NULL; + + ret = messageCreate(); + + for (t = messageGetBody(m); t; t = t->t_next) { + const char *line; + + if (t->t_line) + line = lineGetData(t->t_line); + else + line = NULL; + + if (inHeader) { + cli_dbgmsg("parseEmailHeaders: check '%s'\n", + line ? line : ""); + if (line == NULL) { + /* * A blank line signifies the end of * the header and the start of the text */ - cli_dbgmsg("End of header information\n"); - if(!anyHeadersFound) { - cli_dbgmsg("Nothing interesting in the header\n"); - break; - } - inHeader = FALSE; - bodyIsEmpty = TRUE; - } else { - char *ptr; - - if(fullline == NULL) { - char cmd[RFC2821LENGTH + 1]; - - /* + cli_dbgmsg("End of header information\n"); + if (!anyHeadersFound) { + cli_dbgmsg("Nothing interesting in the header\n"); + break; + } + inHeader = FALSE; + bodyIsEmpty = TRUE; + } else { + char *ptr; + + if (fullline == NULL) { + char cmd[RFC2821LENGTH + 1]; + + /* * Continuation of line we're ignoring? */ - if(isblank(line[0])) - continue; + if (isblank(line[0])) + continue; - /* + /* * Is this a header we're interested in? */ - if((strchr(line, ':') == NULL) || - (cli_strtokbuf(line, 0, ":", cmd) == NULL)) { - if(strncmp(line, "From ", 5) == 0) - anyHeadersFound = TRUE; - continue; - } - - ptr = rfc822comments(cmd, NULL); - commandNumber = tableFind(rfc821, ptr ? ptr : cmd); - if(ptr) - free(ptr); - - switch(commandNumber) { - case CONTENT_TRANSFER_ENCODING: - case CONTENT_DISPOSITION: - case CONTENT_TYPE: - anyHeadersFound = TRUE; - break; - default: - if(!anyHeadersFound) - anyHeadersFound = usefulHeader(commandNumber, cmd); - continue; - } - fullline = cli_strdup(line); - fulllinelength = strlen(line) + 1; - } else if(line) { - fulllinelength += strlen(line) + 1; - ptr = cli_realloc(fullline, fulllinelength); - if(ptr == NULL) - continue; - fullline = ptr; - cli_strlcat(fullline, line, fulllinelength); - } - assert(fullline != NULL); - - if(next_is_folded_header(t)) - /* Add arguments to this line */ - continue; - - lineUnlink(t->t_line); - t->t_line = NULL; - - if(count_quotes(fullline) & 1) - continue; - - ptr = rfc822comments(fullline, NULL); - if(ptr) { - free(fullline); - fullline = ptr; - } - - if(parseEmailHeader(ret, fullline, rfc821) < 0) - continue; - - free(fullline); - fullline = NULL; - } - } else { - if(bodyIsEmpty) { - if(line == NULL) - /* throw away leading blank lines */ - continue; - /* + if ((strchr(line, ':') == NULL) || + (cli_strtokbuf(line, 0, ":", cmd) == NULL)) { + if (strncmp(line, "From ", 5) == 0) + anyHeadersFound = TRUE; + continue; + } + + ptr = rfc822comments(cmd, NULL); + commandNumber = tableFind(rfc821, ptr ? ptr : cmd); + if (ptr) + free(ptr); + + switch (commandNumber) { + case CONTENT_TRANSFER_ENCODING: + case CONTENT_DISPOSITION: + case CONTENT_TYPE: + anyHeadersFound = TRUE; + break; + default: + if (!anyHeadersFound) + anyHeadersFound = usefulHeader(commandNumber, cmd); + continue; + } + fullline = cli_strdup(line); + fulllinelength = strlen(line) + 1; + } else if (line) { + fulllinelength += strlen(line) + 1; + ptr = cli_realloc(fullline, fulllinelength); + if (ptr == NULL) + continue; + fullline = ptr; + cli_strlcat(fullline, line, fulllinelength); + } + assert(fullline != NULL); + + if (next_is_folded_header(t)) + /* Add arguments to this line */ + continue; + + lineUnlink(t->t_line); + t->t_line = NULL; + + if (count_quotes(fullline) & 1) + continue; + + ptr = rfc822comments(fullline, NULL); + if (ptr) { + free(fullline); + fullline = ptr; + } + + if (parseEmailHeader(ret, fullline, rfc821) < 0) + continue; + + free(fullline); + fullline = NULL; + } + } else { + if (bodyIsEmpty) { + if (line == NULL) + /* throw away leading blank lines */ + continue; + /* * Broken message: new line in the * middle of the headers, so the first * line of the body is in fact * the last lines of the header */ - if(newline_in_header(line)) - continue; - bodyIsEmpty = FALSE; - } - /*if(t->t_line && isuuencodebegin(t->t_line)) + if (newline_in_header(line)) + continue; + bodyIsEmpty = FALSE; + } + /*if(t->t_line && isuuencodebegin(t->t_line)) puts("FIXME: add fast visa here");*/ - cli_dbgmsg("parseEmailHeaders: finished with headers, moving body\n"); - messageMoveText(ret, t, m); - break; - } - } - - if(fullline) { - if(*fullline) switch(commandNumber) { - case CONTENT_TRANSFER_ENCODING: - case CONTENT_DISPOSITION: - case CONTENT_TYPE: - cli_dbgmsg("parseEmailHeaders: Fullline unparsed '%s'\n", fullline); - } - free(fullline); - } - - if(!anyHeadersFound) { - /* + cli_dbgmsg("parseEmailHeaders: finished with headers, moving body\n"); + messageMoveText(ret, t, m); + break; + } + } + + if (fullline) { + if (*fullline) switch (commandNumber) { + case CONTENT_TRANSFER_ENCODING: + case CONTENT_DISPOSITION: + case CONTENT_TYPE: + cli_dbgmsg("parseEmailHeaders: Fullline unparsed '%s'\n", fullline); + } + free(fullline); + } + + if (!anyHeadersFound) { + /* * False positive in believing we have an e-mail when we don't */ - messageDestroy(ret); - cli_dbgmsg("parseEmailHeaders: no headers found, assuming it isn't an email\n"); - return NULL; - } + messageDestroy(ret); + cli_dbgmsg("parseEmailHeaders: no headers found, assuming it isn't an email\n"); + return NULL; + } - cli_dbgmsg("parseEmailHeaders: return\n"); + cli_dbgmsg("parseEmailHeaders: return\n"); - return ret; + return ret; } /* @@ -1031,99 +1009,97 @@ parseEmailHeaders(message *m, const table_t *rfc821) static int parseEmailHeader(message *m, const char *line, const table_t *rfc821) { - int ret; + int ret; #ifdef CL_THREAD_SAFE - char *strptr; + char *strptr; #endif - const char *separator; - char *cmd, *copy, tokenseparator[2]; + const char *separator; + char *cmd, *copy, tokenseparator[2]; - cli_dbgmsg("parseEmailHeader '%s'\n", line); + cli_dbgmsg("parseEmailHeader '%s'\n", line); - /* + /* * In RFC822 the separator between the key a value is a colon, * e.g. Content-Transfer-Encoding: base64 * However some MUA's are lapse about this and virus writers exploit * this hole, so we need to check all known possibilities */ - for(separator = ":= "; *separator; separator++) - if(strchr(line, *separator) != NULL) - break; + for (separator = ":= "; *separator; separator++) + if (strchr(line, *separator) != NULL) + break; - if(*separator == '\0') - return -1; + if (*separator == '\0') + return -1; - copy = rfc2047(line); - if(copy == NULL) - /* an RFC checker would return -1 here */ - copy = cli_strdup(line); + copy = rfc2047(line); + if (copy == NULL) + /* an RFC checker would return -1 here */ + copy = cli_strdup(line); - tokenseparator[0] = *separator; - tokenseparator[1] = '\0'; + tokenseparator[0] = *separator; + tokenseparator[1] = '\0'; - ret = -1; + ret = -1; -#ifdef CL_THREAD_SAFE - cmd = strtok_r(copy, tokenseparator, &strptr); +#ifdef CL_THREAD_SAFE + cmd = strtok_r(copy, tokenseparator, &strptr); #else - cmd = strtok(copy, tokenseparator); + cmd = strtok(copy, tokenseparator); #endif - if(cmd && (strstrip(cmd) > 0)) { -#ifdef CL_THREAD_SAFE - char *arg = strtok_r(NULL, "", &strptr); + if (cmd && (strstrip(cmd) > 0)) { +#ifdef CL_THREAD_SAFE + char *arg = strtok_r(NULL, "", &strptr); #else - char *arg = strtok(NULL, ""); + char *arg = strtok(NULL, ""); #endif - if(arg) - /* + if (arg) + /* * Found a header such as * Content-Type: multipart/mixed; * set arg to be * "multipart/mixed" and cmd to * be "Content-Type" */ - ret = parseMimeHeader(m, cmd, rfc821, arg); - } - free(copy); - return ret; + ret = parseMimeHeader(m, cmd, rfc821, arg); + } + free(copy); + return ret; } #if HAVE_LIBXML2 static const struct key_entry mhtml_keys[] = { - /* root html tags for microsoft office document */ - { "html", "RootHTML", MSXML_JSON_ROOT | MSXML_JSON_ATTRIB }, - - { "head", "Head", MSXML_JSON_WRKPTR | MSXML_COMMENT_CB }, - { "meta", "Meta", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_ATTRIB }, - { "link", "Link", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_ATTRIB }, - { "script", "Script", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_VALUE } -}; + /* root html tags for microsoft office document */ + {"html", "RootHTML", MSXML_JSON_ROOT | MSXML_JSON_ATTRIB}, + + {"head", "Head", MSXML_JSON_WRKPTR | MSXML_COMMENT_CB}, + {"meta", "Meta", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_ATTRIB}, + {"link", "Link", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_ATTRIB}, + {"script", "Script", MSXML_JSON_WRKPTR | MSXML_JSON_MULTI | MSXML_JSON_VALUE}}; static size_t num_mhtml_keys = sizeof(mhtml_keys) / sizeof(struct key_entry); static const struct key_entry mhtml_comment_keys[] = { - /* embedded xml tags (comment) for microsoft office document */ - { "o:documentproperties", "DocumentProperties", MSXML_JSON_ROOT | MSXML_JSON_ATTRIB }, - { "o:author", "Author", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:lastauthor", "LastAuthor", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:revision", "Revision", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:totaltime", "TotalTime", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:created", "Created", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:lastsaved", "LastSaved", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:pages", "Pages", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:words", "Words", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:characters", "Characters", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:company", "Company", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:lines", "Lines", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:paragraphs", "Paragraphs", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:characterswithspaces", "CharactersWithSpaces", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - { "o:version", "Version", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE }, - - { "o:officedocumentsettings", "DocumentSettings", MSXML_IGNORE_ELEM }, - { "w:worddocument", "WordDocument", MSXML_IGNORE_ELEM }, - { "w:latentstyles", "LatentStyles", MSXML_IGNORE_ELEM } -}; + /* embedded xml tags (comment) for microsoft office document */ + {"o:documentproperties", "DocumentProperties", MSXML_JSON_ROOT | MSXML_JSON_ATTRIB}, + {"o:author", "Author", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:lastauthor", "LastAuthor", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:revision", "Revision", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:totaltime", "TotalTime", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:created", "Created", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:lastsaved", "LastSaved", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:pages", "Pages", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:words", "Words", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:characters", "Characters", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:company", "Company", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:lines", "Lines", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:paragraphs", "Paragraphs", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:characterswithspaces", "CharactersWithSpaces", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + {"o:version", "Version", MSXML_JSON_WRKPTR | MSXML_JSON_VALUE}, + + {"o:officedocumentsettings", "DocumentSettings", MSXML_IGNORE_ELEM}, + {"w:worddocument", "WordDocument", MSXML_IGNORE_ELEM}, + {"w:latentstyles", "LatentStyles", MSXML_IGNORE_ELEM}}; static size_t num_mhtml_comment_keys = sizeof(mhtml_comment_keys) / sizeof(struct key_entry); #endif @@ -1137,55 +1113,55 @@ static int parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata) { #if HAVE_LIBXML2 - const char *xmlsrt, *xmlend; - xmlTextReaderPtr reader; + const char *xmlsrt, *xmlend; + xmlTextReaderPtr reader; #if HAVE_JSON - json_object *thisjobj = (json_object *)wrkjobj; + json_object *thisjobj = (json_object *)wrkjobj; #endif - int ret = CL_SUCCESS; + int ret = CL_SUCCESS; - UNUSEDPARAM(cbdata); - UNUSEDPARAM(wrkjobj); + UNUSEDPARAM(cbdata); + UNUSEDPARAM(wrkjobj); - xmlend = comment; - while ((xmlsrt = strstr(xmlend, ""))) { - xmlend = strstr(xmlsrt, ""); - if (xmlend == NULL) { - cli_dbgmsg("parseMHTMLComment: unbounded xml tag\n"); - break; - } + xmlend = comment; + while ((xmlsrt = strstr(xmlend, ""))) { + xmlend = strstr(xmlsrt, ""); + if (xmlend == NULL) { + cli_dbgmsg("parseMHTMLComment: unbounded xml tag\n"); + break; + } - reader = xmlReaderForMemory(xmlsrt, xmlend-xmlsrt+6, "comment.xml", NULL, CLAMAV_MIN_XMLREADER_FLAGS); - if (!reader) { - cli_dbgmsg("parseMHTMLComment: cannot initialize xmlReader\n"); + reader = xmlReaderForMemory(xmlsrt, xmlend - xmlsrt + 6, "comment.xml", NULL, CLAMAV_MIN_XMLREADER_FLAGS); + if (!reader) { + cli_dbgmsg("parseMHTMLComment: cannot initialize xmlReader\n"); #if HAVE_JSON - if (ctx->wrkproperty != NULL) - ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_XML_READER_MEM"); + if (ctx->wrkproperty != NULL) + ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_XML_READER_MEM"); #endif - return ret; // libxml2 failed! - } - - /* comment callback is not set to prevent recursion */ - /* TODO: should we separate the key dictionaries? */ - /* TODO: should we use the json object pointer? */ - ret = cli_msxml_parse_document(ctx, reader, mhtml_comment_keys, num_mhtml_comment_keys, MSXML_FLAG_JSON, NULL); - - xmlTextReaderClose(reader); - xmlFreeTextReader(reader); - if (ret != CL_SUCCESS) - return ret; - } + return ret; // libxml2 failed! + } + + /* comment callback is not set to prevent recursion */ + /* TODO: should we separate the key dictionaries? */ + /* TODO: should we use the json object pointer? */ + ret = cli_msxml_parse_document(ctx, reader, mhtml_comment_keys, num_mhtml_comment_keys, MSXML_FLAG_JSON, NULL); + + xmlTextReaderClose(reader); + xmlFreeTextReader(reader); + if (ret != CL_SUCCESS) + return ret; + } #else - UNUSEDPARAM(comment); - UNUSEDPARAM(ctx); - UNUSEDPARAM(wrkjobj); - UNUSEDPARAM(cbdata); + UNUSEDPARAM(comment); + UNUSEDPARAM(ctx); + UNUSEDPARAM(wrkjobj); + UNUSEDPARAM(cbdata); - cli_dbgmsg("in parseMHTMLComment\n"); - cli_dbgmsg("parseMHTMLComment: parsing html xml-comments requires libxml2!\n"); + cli_dbgmsg("in parseMHTMLComment\n"); + cli_dbgmsg("parseMHTMLComment: parsing html xml-comments requires libxml2!\n"); #endif - return CL_SUCCESS; + return CL_SUCCESS; } /* @@ -1197,117 +1173,117 @@ parseMHTMLComment(const char *comment, cli_ctx *ctx, void *wrkjobj, void *cbdata static mbox_status parseRootMHTML(mbox_ctx *mctx, message *m, text *t) { - cli_ctx *ctx = mctx->ctx; + cli_ctx *ctx = mctx->ctx; #if HAVE_LIBXML2 #ifdef LIBXML_HTML_ENABLED - struct msxml_ctx mxctx; - blob *input = NULL; - htmlDocPtr htmlDoc; - xmlTextReaderPtr reader; - int ret = CL_SUCCESS; - mbox_status rc = OK; + struct msxml_ctx mxctx; + blob *input = NULL; + htmlDocPtr htmlDoc; + xmlTextReaderPtr reader; + int ret = CL_SUCCESS; + mbox_status rc = OK; #if HAVE_JSON - json_object *rhtml; + json_object *rhtml; #endif - cli_dbgmsg("in parseRootMHTML\n"); + cli_dbgmsg("in parseRootMHTML\n"); - if (ctx == NULL) - return OK; + if (ctx == NULL) + return OK; - if (m == NULL && t == NULL) - return OK; + if (m == NULL && t == NULL) + return OK; - if (m != NULL) - input = messageToBlob(m, 0); - else /* t != NULL */ - input = textToBlob(t, NULL, 0); + if (m != NULL) + input = messageToBlob(m, 0); + else /* t != NULL */ + input = textToBlob(t, NULL, 0); - if (input == NULL) - return OK; + if (input == NULL) + return OK; - htmlDoc = htmlReadMemory((char*)input->data, input->len, "mhtml.html", NULL, CLAMAV_MIN_XMLREADER_FLAGS); - if (htmlDoc == NULL) { - cli_dbgmsg("parseRootMHTML: cannot initialize read html document\n"); + htmlDoc = htmlReadMemory((char *)input->data, input->len, "mhtml.html", NULL, CLAMAV_MIN_XMLREADER_FLAGS); + if (htmlDoc == NULL) { + cli_dbgmsg("parseRootMHTML: cannot initialize read html document\n"); #if HAVE_JSON - if (ctx->wrkproperty != NULL) - ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_HTML_READ"); - if (ret != CL_SUCCESS) - rc = FAIL; + if (ctx->wrkproperty != NULL) + ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_HTML_READ"); + if (ret != CL_SUCCESS) + rc = FAIL; #endif - blobDestroy(input); - return rc; - } + blobDestroy(input); + return rc; + } #if HAVE_JSON - if (mctx->wrkobj) { - rhtml = cli_jsonobj(mctx->wrkobj, "RootHTML"); - if (rhtml != NULL) { - /* MHTML-specific properties */ - cli_jsonstr(rhtml, "Encoding", (const char*)htmlGetMetaEncoding(htmlDoc)); - cli_jsonint(rhtml, "CompressMode", xmlGetDocCompressMode(htmlDoc)); - } - } + if (mctx->wrkobj) { + rhtml = cli_jsonobj(mctx->wrkobj, "RootHTML"); + if (rhtml != NULL) { + /* MHTML-specific properties */ + cli_jsonstr(rhtml, "Encoding", (const char *)htmlGetMetaEncoding(htmlDoc)); + cli_jsonint(rhtml, "CompressMode", xmlGetDocCompressMode(htmlDoc)); + } + } #endif - reader = xmlReaderWalker(htmlDoc); - if (reader == NULL) { - cli_dbgmsg("parseRootMHTML: cannot initialize xmlTextReader\n"); + reader = xmlReaderWalker(htmlDoc); + if (reader == NULL) { + cli_dbgmsg("parseRootMHTML: cannot initialize xmlTextReader\n"); #if HAVE_JSON - if (ctx->wrkproperty != NULL) - ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_XML_READER_IO"); - if (ret != CL_SUCCESS) - rc = FAIL; + if (ctx->wrkproperty != NULL) + ret = cli_json_parse_error(ctx->wrkproperty, "MHTML_ERROR_XML_READER_IO"); + if (ret != CL_SUCCESS) + rc = FAIL; #endif - blobDestroy(input); - return rc; - } - - memset(&mxctx, 0, sizeof(mxctx)); - /* no scanning callback set */ - mxctx.comment_cb = parseMHTMLComment; - ret = cli_msxml_parse_document(ctx, reader, mhtml_keys, num_mhtml_keys, MSXML_FLAG_JSON | MSXML_FLAG_WALK, &mxctx); - switch (ret) { - case CL_SUCCESS: - case CL_ETIMEOUT: - case CL_BREAK: - rc = OK; - break; - - case CL_EMAXREC: - rc = MAXREC; - break; - - case CL_EMAXFILES: - rc = MAXFILES; - break; - - case CL_VIRUS: - rc = VIRUS; - break; - - default: - rc = FAIL; - } - - xmlTextReaderClose(reader); - xmlFreeTextReader(reader); - xmlFreeDoc(htmlDoc); - blobDestroy(input); - return rc; + blobDestroy(input); + return rc; + } + + memset(&mxctx, 0, sizeof(mxctx)); + /* no scanning callback set */ + mxctx.comment_cb = parseMHTMLComment; + ret = cli_msxml_parse_document(ctx, reader, mhtml_keys, num_mhtml_keys, MSXML_FLAG_JSON | MSXML_FLAG_WALK, &mxctx); + switch (ret) { + case CL_SUCCESS: + case CL_ETIMEOUT: + case CL_BREAK: + rc = OK; + break; + + case CL_EMAXREC: + rc = MAXREC; + break; + + case CL_EMAXFILES: + rc = MAXFILES; + break; + + case CL_VIRUS: + rc = VIRUS; + break; + + default: + rc = FAIL; + } + + xmlTextReaderClose(reader); + xmlFreeTextReader(reader); + xmlFreeDoc(htmlDoc); + blobDestroy(input); + return rc; #else /* LIBXML_HTML_ENABLED */ - UNUSEDPARAM(m); - UNUSEDPARAM(t); - cli_dbgmsg("in parseRootMHTML\n"); - cli_dbgmsg("parseRootMHTML: parsing html documents disabled in libxml2!\n"); + UNUSEDPARAM(m); + UNUSEDPARAM(t); + cli_dbgmsg("in parseRootMHTML\n"); + cli_dbgmsg("parseRootMHTML: parsing html documents disabled in libxml2!\n"); #endif /* LIBXML_HTML_ENABLED */ #else /* HAVE_LIBXML2 */ - UNUSEDPARAM(m); - UNUSEDPARAM(t); - cli_dbgmsg("in parseRootMHTML\n"); - cli_dbgmsg("parseRootMHTML: parsing html documents requires libxml2!\n"); + UNUSEDPARAM(m); + UNUSEDPARAM(t); + cli_dbgmsg("in parseRootMHTML\n"); + cli_dbgmsg("parseRootMHTML: parsing html documents requires libxml2!\n"); - return OK; + return OK; #endif /* HAVE_LIBXML2 */ } @@ -1324,182 +1300,181 @@ parseRootMHTML(mbox_ctx *mctx, message *m, text *t) static mbox_status parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level) { - mbox_status rc; - text *aText = textIn; - message *mainMessage = messageIn; - fileblob *fb; - bool infected = FALSE; - const struct cl_engine *engine = mctx->ctx->engine; - const int doPhishingScan = engine->dboptions&CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); + mbox_status rc; + text *aText = textIn; + message *mainMessage = messageIn; + fileblob *fb; + bool infected = FALSE; + const struct cl_engine *engine = mctx->ctx->engine; + const int doPhishingScan = engine->dboptions & CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); #if HAVE_JSON - json_object *saveobj = mctx->wrkobj; + json_object *saveobj = mctx->wrkobj; #endif - cli_dbgmsg("in parseEmailBody, %u files saved so far\n", - mctx->files); + cli_dbgmsg("in parseEmailBody, %u files saved so far\n", + mctx->files); - /* FIXMELIMITS: this should be better integrated */ - if(engine->maxreclevel) - /* + /* FIXMELIMITS: this should be better integrated */ + if (engine->maxreclevel) + /* * This is approximate */ - if(recursion_level > engine->maxreclevel) { + if (recursion_level > engine->maxreclevel) { - cli_dbgmsg("parseEmailBody: hit maximum recursion level (%u)\n", recursion_level); - return MAXREC; - } - if(engine->maxfiles && (mctx->files >= engine->maxfiles)) { - /* + cli_dbgmsg("parseEmailBody: hit maximum recursion level (%u)\n", recursion_level); + return MAXREC; + } + if (engine->maxfiles && (mctx->files >= engine->maxfiles)) { + /* * FIXME: This is only approx - it may have already * been exceeded */ - cli_dbgmsg("parseEmailBody: number of files exceeded %u\n", engine->maxfiles); - return MAXFILES; - } - - rc = OK; - - /* Anything left to be parsed? */ - if(mainMessage && (messageGetBody(mainMessage) != NULL)) { - mime_type mimeType; - int subtype, inhead, htmltextPart, inMimeHead, i; - const char *mimeSubtype; - char *boundary; - const text *t_line; - /*bool isAlternative;*/ - message *aMessage; - int multiparts = 0; - message **messages = NULL; /* parts of a multipart message */ - - cli_dbgmsg("Parsing mail file\n"); - - mimeType = messageGetMimeType(mainMessage); - mimeSubtype = messageGetMimeSubtype(mainMessage); + cli_dbgmsg("parseEmailBody: number of files exceeded %u\n", engine->maxfiles); + return MAXFILES; + } + + rc = OK; + + /* Anything left to be parsed? */ + if (mainMessage && (messageGetBody(mainMessage) != NULL)) { + mime_type mimeType; + int subtype, inhead, htmltextPart, inMimeHead, i; + const char *mimeSubtype; + char *boundary; + const text *t_line; + /*bool isAlternative;*/ + message *aMessage; + int multiparts = 0; + message **messages = NULL; /* parts of a multipart message */ + + cli_dbgmsg("Parsing mail file\n"); + + mimeType = messageGetMimeType(mainMessage); + mimeSubtype = messageGetMimeSubtype(mainMessage); #if HAVE_JSON - if (mctx->wrkobj != NULL) { - mctx->wrkobj = cli_jsonobj(mctx->wrkobj, "Body"); - cli_jsonstr(mctx->wrkobj, "MimeType", getMimeTypeStr(mimeType)); - cli_jsonstr(mctx->wrkobj, "MimeSubtype", mimeSubtype); - cli_jsonstr(mctx->wrkobj, "EncodingType", getEncTypeStr(messageGetEncoding(mainMessage))); - cli_jsonstr(mctx->wrkobj, "Disposition", messageGetDispositionType(mainMessage)); - cli_jsonstr(mctx->wrkobj, "Filename", messageHasFilename(mainMessage) ? - messageGetFilename(mainMessage): "(inline)"); - } + if (mctx->wrkobj != NULL) { + mctx->wrkobj = cli_jsonobj(mctx->wrkobj, "Body"); + cli_jsonstr(mctx->wrkobj, "MimeType", getMimeTypeStr(mimeType)); + cli_jsonstr(mctx->wrkobj, "MimeSubtype", mimeSubtype); + cli_jsonstr(mctx->wrkobj, "EncodingType", getEncTypeStr(messageGetEncoding(mainMessage))); + cli_jsonstr(mctx->wrkobj, "Disposition", messageGetDispositionType(mainMessage)); + cli_jsonstr(mctx->wrkobj, "Filename", messageHasFilename(mainMessage) ? messageGetFilename(mainMessage) : "(inline)"); + } #endif - /* pre-process */ - subtype = tableFind(mctx->subtypeTable, mimeSubtype); - if((mimeType == TEXT) && (subtype == PLAIN)) { - /* + /* pre-process */ + subtype = tableFind(mctx->subtypeTable, mimeSubtype); + if ((mimeType == TEXT) && (subtype == PLAIN)) { + /* * This is effectively no encoding, notice that we * don't check that charset is us-ascii */ - cli_dbgmsg("text/plain: Assume no attachments\n"); - mimeType = NOMIME; - messageSetMimeSubtype(mainMessage, ""); - } else if((mimeType == MESSAGE) && - (strcasecmp(mimeSubtype, "rfc822-headers") == 0)) { - /* + cli_dbgmsg("text/plain: Assume no attachments\n"); + mimeType = NOMIME; + messageSetMimeSubtype(mainMessage, ""); + } else if ((mimeType == MESSAGE) && + (strcasecmp(mimeSubtype, "rfc822-headers") == 0)) { + /* * RFC1892/RFC3462: section 2 text/rfc822-headers * incorrectly sent as message/rfc822-headers * * Parse as text/plain, i.e. no mime */ - cli_dbgmsg("Changing message/rfc822-headers to text/rfc822-headers\n"); - mimeType = NOMIME; - messageSetMimeSubtype(mainMessage, ""); - } else - cli_dbgmsg("mimeType = %d\n", (int)mimeType); - - switch(mimeType) { - case NOMIME: - cli_dbgmsg("Not a mime encoded message\n"); - aText = textAddMessage(aText, mainMessage); - - if(!doPhishingScan) - break; - /* + cli_dbgmsg("Changing message/rfc822-headers to text/rfc822-headers\n"); + mimeType = NOMIME; + messageSetMimeSubtype(mainMessage, ""); + } else + cli_dbgmsg("mimeType = %d\n", (int)mimeType); + + switch (mimeType) { + case NOMIME: + cli_dbgmsg("Not a mime encoded message\n"); + aText = textAddMessage(aText, mainMessage); + + if (!doPhishingScan) + break; + /* * Fall through: some phishing mails claim they are * text/plain, when they are in fact html */ - case TEXT: - /* text/plain has been preprocessed as no encoding */ - if(doPhishingScan) { - /* + case TEXT: + /* text/plain has been preprocessed as no encoding */ + if (doPhishingScan) { + /* * It would be better to save and scan the * file and only checkURLs if it's found to be * clean */ - checkURLs(mainMessage, mctx, &rc, (subtype == HTML)); - /* + checkURLs(mainMessage, mctx, &rc, (subtype == HTML)); + /* * There might be html sent without subtype * html too, so scan them for phishing */ - if(rc == VIRUS) - infected = TRUE; - } - break; - case MULTIPART: - cli_dbgmsg("Content-type 'multipart' handler\n"); - boundary = messageFindArgument(mainMessage, "boundary"); + if (rc == VIRUS) + infected = TRUE; + } + break; + case MULTIPART: + cli_dbgmsg("Content-type 'multipart' handler\n"); + boundary = messageFindArgument(mainMessage, "boundary"); #if HAVE_JSON - if (mctx->wrkobj != NULL) - cli_jsonstr(mctx->wrkobj, "Boundary", boundary); + if (mctx->wrkobj != NULL) + cli_jsonstr(mctx->wrkobj, "Boundary", boundary); #endif - if(boundary == NULL) { - cli_dbgmsg("Multipart/%s MIME message contains no boundary header\n", - mimeSubtype); - /* Broken e-mail message */ - mimeType = NOMIME; - /* + if (boundary == NULL) { + cli_dbgmsg("Multipart/%s MIME message contains no boundary header\n", + mimeSubtype); + /* Broken e-mail message */ + mimeType = NOMIME; + /* * The break means that we will still * check if the file contains a uuencoded file */ - break; - } + break; + } - cli_chomp(boundary); + cli_chomp(boundary); - /* Perhaps it should assume mixed? */ - if(mimeSubtype[0] == '\0') { - cli_dbgmsg("Multipart has no subtype assuming alternative\n"); - mimeSubtype = "alternative"; - messageSetMimeSubtype(mainMessage, "alternative"); - } + /* Perhaps it should assume mixed? */ + if (mimeSubtype[0] == '\0') { + cli_dbgmsg("Multipart has no subtype assuming alternative\n"); + mimeSubtype = "alternative"; + messageSetMimeSubtype(mainMessage, "alternative"); + } - /* + /* * Get to the start of the first message */ - t_line = messageGetBody(mainMessage); - - if(t_line == NULL) { - cli_dbgmsg("Multipart MIME message has no body\n"); - free((char *)boundary); - mimeType = NOMIME; - break; - } - - do - if(t_line->t_line) { - if(boundaryStart(lineGetData(t_line->t_line), boundary)) - break; - /* + t_line = messageGetBody(mainMessage); + + if (t_line == NULL) { + cli_dbgmsg("Multipart MIME message has no body\n"); + free((char *)boundary); + mimeType = NOMIME; + break; + } + + do + if (t_line->t_line) { + if (boundaryStart(lineGetData(t_line->t_line), boundary)) + break; + /* * Found a binhex file before * the first multipart * TODO: check yEnc */ - if(binhexBegin(mainMessage) == t_line) { - if(exportBinhexMessage(mctx, mainMessage)) { - /* virus found */ - rc = VIRUS; - infected = TRUE; - break; - } - } else if(t_line->t_next && - (encodingLine(mainMessage) == t_line->t_next)) { - /* + if (binhexBegin(mainMessage) == t_line) { + if (exportBinhexMessage(mctx, mainMessage)) { + /* virus found */ + rc = VIRUS; + infected = TRUE; + break; + } + } else if (t_line->t_next && + (encodingLine(mainMessage) == t_line->t_next)) { + /* * We look for the next line * since later on we'll skip * over the important line when @@ -1508,39 +1483,39 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * which it would have been in * an RFC compliant world */ - cli_dbgmsg("Found MIME attachment before the first MIME section \"%s\"\n", - lineGetData(t_line->t_next->t_line)); - if(messageGetEncoding(mainMessage) == NOENCODING) - break; - } - } - while((t_line = t_line->t_next) != NULL); - - if(t_line == NULL) { - cli_dbgmsg("Multipart MIME message contains no boundary lines (%s)\n", - boundary); - free((char *)boundary); - mimeType = NOMIME; - /* + cli_dbgmsg("Found MIME attachment before the first MIME section \"%s\"\n", + lineGetData(t_line->t_next->t_line)); + if (messageGetEncoding(mainMessage) == NOENCODING) + break; + } + } + while ((t_line = t_line->t_next) != NULL); + + if (t_line == NULL) { + cli_dbgmsg("Multipart MIME message contains no boundary lines (%s)\n", + boundary); + free((char *)boundary); + mimeType = NOMIME; + /* * The break means that we will still * check if the file contains a yEnc/binhex file */ - break; - } - /* + break; + } + /* * Build up a table of all of the parts of this * multipart message. Remember, each part may itself * be a multipart message. */ - inhead = 1; - inMimeHead = 0; + inhead = 1; + inMimeHead = 0; - /* + /* * Re-read this variable in case mimeSubtype has changed */ - subtype = tableFind(mctx->subtypeTable, mimeSubtype); + subtype = tableFind(mctx->subtypeTable, mimeSubtype); - /* + /* * Parse the mainMessage object and create an array * of objects called messages, one for each of the * multiparts that mainMessage contains. @@ -1554,99 +1529,99 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * which of those elements it will be, except in * the case of mixed, when all parts need to be scanned. */ - for(multiparts = 0; t_line && !infected; multiparts++) { - int lines = 0; - message **m; - mbox_status old_rc; - - m = cli_realloc(messages, ((multiparts + 1) * sizeof(message *))); - if(m == NULL) - break; - messages = m; - - aMessage = messages[multiparts] = messageCreate(); - if(aMessage == NULL) { - multiparts--; - /* if allocation failed the first time, + for (multiparts = 0; t_line && !infected; multiparts++) { + int lines = 0; + message **m; + mbox_status old_rc; + + m = cli_realloc(messages, ((multiparts + 1) * sizeof(message *))); + if (m == NULL) + break; + messages = m; + + aMessage = messages[multiparts] = messageCreate(); + if (aMessage == NULL) { + multiparts--; + /* if allocation failed the first time, * there's no point in retrying, just * break out */ - break; - } - messageSetCTX(aMessage, mctx->ctx); + break; + } + messageSetCTX(aMessage, mctx->ctx); - cli_dbgmsg("Now read in part %d\n", multiparts); + cli_dbgmsg("Now read in part %d\n", multiparts); - /* + /* * Ignore blank lines. There shouldn't be ANY * but some viruses insert them */ - while((t_line = t_line->t_next) != NULL) - if(t_line->t_line && - /*(cli_chomp(t_line->t_text) > 0))*/ - (strlen(lineGetData(t_line->t_line)) > 0)) - break; - - if(t_line == NULL) { - cli_dbgmsg("Empty part\n"); - /* + while ((t_line = t_line->t_next) != NULL) + if (t_line->t_line && + /*(cli_chomp(t_line->t_text) > 0))*/ + (strlen(lineGetData(t_line->t_line)) > 0)) + break; + + if (t_line == NULL) { + cli_dbgmsg("Empty part\n"); + /* * Remove this part unless there's * a binhex portion somewhere in * the complete message that we may * throw away by mistake if the MIME * encoding information is incorrect */ - if(mainMessage && - (binhexBegin(mainMessage) == NULL)) { - messageDestroy(aMessage); - --multiparts; - } - continue; - } - - do { - const char *line = lineGetData(t_line->t_line); - - /*cli_dbgmsg("multipart %d: inMimeHead %d inhead %d boundary '%s' line '%s' next '%s'\n", + if (mainMessage && + (binhexBegin(mainMessage) == NULL)) { + messageDestroy(aMessage); + --multiparts; + } + continue; + } + + do { + const char *line = lineGetData(t_line->t_line); + + /*cli_dbgmsg("multipart %d: inMimeHead %d inhead %d boundary '%s' line '%s' next '%s'\n", multiparts, inMimeHead, inhead, boundary, line, t_line->t_next && t_line->t_next->t_line ? lineGetData(t_line->t_next->t_line) : "(null)");*/ - if(inMimeHead) { /* continuation line */ - if(line == NULL) { - /*inhead =*/ inMimeHead = 0; - continue; - } - /* + if (inMimeHead) { /* continuation line */ + if (line == NULL) { + /*inhead =*/inMimeHead = 0; + continue; + } + /* * Handle continuation lines * because the previous line * ended with a ; or this line * starts with a white space */ - cli_dbgmsg("Multipart %d: About to add mime Argument '%s'\n", - multiparts, line); - /* + cli_dbgmsg("Multipart %d: About to add mime Argument '%s'\n", + multiparts, line); + /* * Handle the case when it * isn't really a continuation * line: * Content-Type: application/octet-stream; * Content-Transfer-Encoding: base64 */ - parseEmailHeader(aMessage, line, mctx->rfc821Table); - - while(isspace((int)*line)) - line++; - - if(*line == '\0') { - inhead = inMimeHead = 0; - continue; - } - inMimeHead = FALSE; - messageAddArgument(aMessage, line); - } else if(inhead) { /* handling normal headers */ - /*int quotes;*/ - char *fullline, *ptr; - - if(line == NULL) { - /* + parseEmailHeader(aMessage, line, mctx->rfc821Table); + + while (isspace((int)*line)) + line++; + + if (*line == '\0') { + inhead = inMimeHead = 0; + continue; + } + inMimeHead = FALSE; + messageAddArgument(aMessage, line); + } else if (inhead) { /* handling normal headers */ + /*int quotes;*/ + char *fullline, *ptr; + + if (line == NULL) { + /* * empty line, should the end of the headers, * but some base64 decoders, e.g. uudeview, are broken * and will handle this type of entry, decoding the @@ -1663,15 +1638,15 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * * UEsDBAoAAAAAAACgPjJ2RHw676gAAO+oAABEAAAAbWFpbF90ZXh0LWluZm8udHh0ICAgICAgICAg */ - const text *next = t_line->t_next; + const text *next = t_line->t_next; - if(next && next->t_line) { - const char *data = lineGetData(next->t_line); + if (next && next->t_line) { + const char *data = lineGetData(next->t_line); - if((messageGetEncoding(aMessage) == NOENCODING) && - (messageGetMimeType(aMessage) == APPLICATION) && - data && strstr(data, "base64")) { - /* + if ((messageGetEncoding(aMessage) == NOENCODING) && + (messageGetMimeType(aMessage) == APPLICATION) && + data && strstr(data, "base64")) { + /* * Handle this nightmare (note the blank * line in the header and the incorrect * content-transfer-encoding header) @@ -1681,23 +1656,23 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * r-Encoding: base64 * Content-Disposition: attachment; filename="zipped_files.EXE" */ - messageSetEncoding(aMessage, "base64"); - cli_dbgmsg("Ignoring fake end of headers\n"); - continue; - } - if((strncmp(data, "Content", 7) == 0) || - (strncmp(data, "filename=", 9) == 0)) { - cli_dbgmsg("Ignoring fake end of headers\n"); - continue; - } - } - cli_dbgmsg("Multipart %d: End of header information\n", - multiparts); - inhead = 0; - continue; - } - if(isspace((int)*line)) { - /* + messageSetEncoding(aMessage, "base64"); + cli_dbgmsg("Ignoring fake end of headers\n"); + continue; + } + if ((strncmp(data, "Content", 7) == 0) || + (strncmp(data, "filename=", 9) == 0)) { + cli_dbgmsg("Ignoring fake end of headers\n"); + continue; + } + } + cli_dbgmsg("Multipart %d: End of header information\n", + multiparts); + inhead = 0; + continue; + } + if (isspace((int)*line)) { + /* * The first line is * continuation line. * This is tricky @@ -1705,10 +1680,10 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * all we can do is our * best */ - cli_dbgmsg("Part %d starts with a continuation line\n", - multiparts); - messageAddArgument(aMessage, line); - /* + cli_dbgmsg("Part %d starts with a continuation line\n", + multiparts); + messageAddArgument(aMessage, line); + /* * Give it a default * MIME type since * that may be the @@ -1717,68 +1692,68 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * Choose application to * force a save */ - if(messageGetMimeType(aMessage) == NOMIME) - messageSetMimeType(aMessage, "application"); - continue; - } + if (messageGetMimeType(aMessage) == NOMIME) + messageSetMimeType(aMessage, "application"); + continue; + } - inMimeHead = FALSE; + inMimeHead = FALSE; - assert(strlen(line) <= RFC2821LENGTH); + assert(strlen(line) <= RFC2821LENGTH); - fullline = rfc822comments(line, NULL); - if(fullline == NULL) - fullline = cli_strdup(line); + fullline = rfc822comments(line, NULL); + if (fullline == NULL) + fullline = cli_strdup(line); - /*quotes = count_quotes(fullline);*/ + /*quotes = count_quotes(fullline);*/ - /* + /* * Fold next lines to the end of this * if they start with a white space * or if this line has an odd number of quotes: * Content-Type: application/octet-stream; name="foo * " */ - while(t_line && next_is_folded_header(t_line)) { - const char *data; - size_t datasz; + while (t_line && next_is_folded_header(t_line)) { + const char *data; + size_t datasz; - t_line = t_line->t_next; + t_line = t_line->t_next; - data = lineGetData(t_line->t_line); + data = lineGetData(t_line->t_line); - if(data[1] == '\0') { - /* + if (data[1] == '\0') { + /* * Broken message: the * blank line at the end * of the headers isn't blank - * it contains a space */ - cli_dbgmsg("Multipart %d: headers not terminated by blank line\n", - multiparts); - inhead = FALSE; - break; - } + cli_dbgmsg("Multipart %d: headers not terminated by blank line\n", + multiparts); + inhead = FALSE; + break; + } - datasz = strlen(fullline) + strlen(data) + 1; - ptr = cli_realloc(fullline, datasz); + datasz = strlen(fullline) + strlen(data) + 1; + ptr = cli_realloc(fullline, datasz); - if(ptr == NULL) - break; + if (ptr == NULL) + break; - fullline = ptr; - cli_strlcat(fullline, data, datasz); + fullline = ptr; + cli_strlcat(fullline, data, datasz); - /*quotes = count_quotes(data);*/ - } + /*quotes = count_quotes(data);*/ + } - cli_dbgmsg("Multipart %d: About to parse folded header '%s'\n", - multiparts, fullline); + cli_dbgmsg("Multipart %d: About to parse folded header '%s'\n", + multiparts, fullline); - parseEmailHeader(aMessage, fullline, mctx->rfc821Table); - free(fullline); - } else if(boundaryEnd(line, boundary)) { - /* + parseEmailHeader(aMessage, fullline, mctx->rfc821Table); + free(fullline); + } else if (boundaryEnd(line, boundary)) { + /* * Some viruses put information * *after* the end of message, * which presumably some broken @@ -1786,184 +1761,187 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * can't assume that this * is the end of the message */ - /* t_line = NULL;*/ - break; - } else if(boundaryStart(line, boundary)) { - inhead = 1; - break; - } else { - if(messageAddLine(aMessage, t_line->t_line) < 0) - break; - lines++; - } - } while((t_line = t_line->t_next) != NULL); - - cli_dbgmsg("Part %d has %d lines, rc = %d\n", - multiparts, lines, (int)rc); - - /* + /* t_line = NULL;*/ + break; + } else if (boundaryStart(line, boundary)) { + inhead = 1; + break; + } else { + if (messageAddLine(aMessage, t_line->t_line) < 0) + break; + lines++; + } + } while ((t_line = t_line->t_next) != NULL); + + cli_dbgmsg("Part %d has %d lines, rc = %d\n", + multiparts, lines, (int)rc); + + /* * Only save in the array of messages if some * decision will be taken on whether to scan. * If all parts will be scanned then save to * file straight away */ - switch(subtype) { - case MIXED: - case ALTERNATIVE: - case REPORT: - case DIGEST: - case APPLEDOUBLE: - case KNOWBOT: - case -1: - old_rc = rc; - mainMessage = do_multipart(mainMessage, - messages, multiparts, - &rc, mctx, messageIn, - &aText, recursion_level); - if((rc == OK_ATTACHMENTS_NOT_SAVED) && (old_rc == OK)) - rc = OK; - if(messages[multiparts]) { - messageDestroy(messages[multiparts]); - messages[multiparts] = NULL; - } - --multiparts; - if(rc == VIRUS) - infected = TRUE; - break; - - case RELATED: - case ENCRYPTED: - case SIGNED: - case PARALLEL: - /* all the subtypes that we handle + switch (subtype) { + case MIXED: + case ALTERNATIVE: + case REPORT: + case DIGEST: + case APPLEDOUBLE: + case KNOWBOT: + case -1: + old_rc = rc; + mainMessage = do_multipart(mainMessage, + messages, multiparts, + &rc, mctx, messageIn, + &aText, recursion_level); + if ((rc == OK_ATTACHMENTS_NOT_SAVED) && (old_rc == OK)) + rc = OK; + if (messages[multiparts]) { + messageDestroy(messages[multiparts]); + messages[multiparts] = NULL; + } + --multiparts; + if (rc == VIRUS) + infected = TRUE; + break; + + case RELATED: + case ENCRYPTED: + case SIGNED: + case PARALLEL: + /* all the subtypes that we handle * (all from the switch(tableFind...) below) * must be listed here */ - break; - default: - /* this is a subtype that we + break; + default: + /* this is a subtype that we * don't handle anyway, * don't store */ - if(messages[multiparts]) { - messageDestroy(messages[multiparts]); - messages[multiparts] = NULL; - } - --multiparts; - } - } + if (messages[multiparts]) { + messageDestroy(messages[multiparts]); + messages[multiparts] = NULL; + } + --multiparts; + } + } - free((char *)boundary); + free((char *)boundary); - /* + /* * Preprocess. Anything special to be done before * we handle the multiparts? */ - switch(subtype) { - case KNOWBOT: - /* TODO */ - cli_dbgmsg("multipart/knowbot parsed as multipart/mixed for now\n"); - mimeSubtype = "mixed"; - break; - case -1: - /* + switch (subtype) { + case KNOWBOT: + /* TODO */ + cli_dbgmsg("multipart/knowbot parsed as multipart/mixed for now\n"); + mimeSubtype = "mixed"; + break; + case -1: + /* * According to section 7.2.6 of * RFC1521, unrecognized multiparts * should be treated as multipart/mixed. */ - cli_dbgmsg("Unsupported multipart format `%s', parsed as mixed\n", mimeSubtype); - mimeSubtype = "mixed"; - break; - } + cli_dbgmsg("Unsupported multipart format `%s', parsed as mixed\n", mimeSubtype); + mimeSubtype = "mixed"; + break; + } - /* + /* * We've finished message we're parsing */ - if(mainMessage && (mainMessage != messageIn)) { - messageDestroy(mainMessage); - mainMessage = NULL; - } - - cli_dbgmsg("The message has %d parts\n", multiparts); - - if(infected || ((multiparts == 0) && (aText == NULL))) { - if(messages) { - for(i = 0; i < multiparts; i++) - if(messages[i]) - messageDestroy(messages[i]); - free(messages); - } - if(aText && (textIn == NULL)) - textDestroy(aText); + if (mainMessage && (mainMessage != messageIn)) { + messageDestroy(mainMessage); + mainMessage = NULL; + } + + cli_dbgmsg("The message has %d parts\n", multiparts); + + if (infected || ((multiparts == 0) && (aText == NULL))) { + if (messages) { + for (i = 0; i < multiparts; i++) + if (messages[i]) + messageDestroy(messages[i]); + free(messages); + } + if (aText && (textIn == NULL)) + textDestroy(aText); #if HAVE_JSON - mctx->wrkobj = saveobj; + mctx->wrkobj = saveobj; #endif - /* + /* * Nothing to do */ - switch(rc) { - case VIRUS: return VIRUS; - case MAXREC: return MAXREC; - default: return OK_ATTACHMENTS_NOT_SAVED; - } - } + switch (rc) { + case VIRUS: + return VIRUS; + case MAXREC: + return MAXREC; + default: + return OK_ATTACHMENTS_NOT_SAVED; + } + } - cli_dbgmsg("Find out the multipart type (%s)\n", mimeSubtype); + cli_dbgmsg("Find out the multipart type (%s)\n", mimeSubtype); - /* + /* * We now have all the parts of the multipart message * in the messages array: * message *messages[multiparts] * Let's decide what to do with them all */ - switch(tableFind(mctx->subtypeTable, mimeSubtype)) { - case RELATED: - cli_dbgmsg("Multipart related handler\n"); - /* + switch (tableFind(mctx->subtypeTable, mimeSubtype)) { + case RELATED: + cli_dbgmsg("Multipart related handler\n"); + /* * Have a look to see if there's HTML code * which will need scanning */ - aMessage = NULL; - assert(multiparts > 0); + aMessage = NULL; + assert(multiparts > 0); - htmltextPart = getTextPart(messages, multiparts); + htmltextPart = getTextPart(messages, multiparts); - if(htmltextPart >= 0 && messages) { - if(messageGetBody(messages[htmltextPart])) + if (htmltextPart >= 0 && messages) { + if (messageGetBody(messages[htmltextPart])) - aText = textAddMessage(aText, messages[htmltextPart]); - } else - /* + aText = textAddMessage(aText, messages[htmltextPart]); + } else + /* * There isn't an HTML bit. If there's a * multipart bit, it'll may be in there * somewhere */ - for(i = 0; i < multiparts; i++) - if(messageGetMimeType(messages[i]) == MULTIPART) { - aMessage = messages[i]; - htmltextPart = i; - break; - } - - if(htmltextPart == -1) - cli_dbgmsg("No HTML code found to be scanned\n"); - else { + for (i = 0; i < multiparts; i++) + if (messageGetMimeType(messages[i]) == MULTIPART) { + aMessage = messages[i]; + htmltextPart = i; + break; + } + + if (htmltextPart == -1) + cli_dbgmsg("No HTML code found to be scanned\n"); + else { #if HAVE_JSON - /* Send root HTML file for preclassification */ - if (mctx->ctx->wrkproperty) - parseRootMHTML(mctx, aMessage, aText); + /* Send root HTML file for preclassification */ + if (mctx->ctx->wrkproperty) + parseRootMHTML(mctx, aMessage, aText); #endif - rc = parseEmailBody(aMessage, aText, mctx, recursion_level + 1); - if((rc == OK) && aMessage) { - assert(aMessage == messages[htmltextPart]); - messageDestroy(aMessage); - messages[htmltextPart] = NULL; - } else if(rc == VIRUS) { - infected = TRUE; - break; - } - } - - /* + rc = parseEmailBody(aMessage, aText, mctx, recursion_level + 1); + if ((rc == OK) && aMessage) { + assert(aMessage == messages[htmltextPart]); + messageDestroy(aMessage); + messages[htmltextPart] = NULL; + } else if (rc == VIRUS) { + infected = TRUE; + break; + } + } + + /* * The message is confused about the difference * between alternative and related. Badtrans.B * suffers from this problem. @@ -1972,8 +1950,8 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * Content-Type: multipart/related; * type="multipart/alternative" */ - case DIGEST: - /* + case DIGEST: + /* * According to section 5.1.5 RFC2046, the * default mime type of multipart/digest parts * is message/rfc822 @@ -1984,29 +1962,29 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * OK for our needs since it means each part * will be scanned */ - case ALTERNATIVE: - cli_dbgmsg("Multipart alternative handler\n"); + case ALTERNATIVE: + cli_dbgmsg("Multipart alternative handler\n"); - /* + /* * Fall through - some clients are broken and * say alternative instead of mixed. The Klez * virus is broken that way, and anyway we * wish to scan all of the alternatives */ - case REPORT: - /* + case REPORT: + /* * According to section 1 of RFC1892, the * syntax of multipart/report is the same * as multipart/mixed. There are some required * parameters, but there's no need for us to * verify that they exist */ - case ENCRYPTED: - /* MUAs without encryption plugins can display as multipart/mixed, + case ENCRYPTED: + /* MUAs without encryption plugins can display as multipart/mixed, * just scan it*/ - case MIXED: - case APPLEDOUBLE: /* not really supported */ - /* + case MIXED: + case APPLEDOUBLE: /* not really supported */ + /* * Look for attachments * * Not all formats are supported. If an @@ -2014,32 +1992,32 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * common enough to implement, it is a simple * matter to add it */ - if(aText) { - if(mainMessage && (mainMessage != messageIn)) - messageDestroy(mainMessage); - mainMessage = NULL; - } - - cli_dbgmsg("Mixed message with %d parts\n", multiparts); - for(i = 0; i < multiparts; i++) { - mainMessage = do_multipart(mainMessage, - messages, i, &rc, mctx, - messageIn, &aText, recursion_level + 1); - if(rc == VIRUS) { - infected = TRUE; - break; - } - if(rc == MAXREC) - break; - if (rc == OK_ATTACHMENTS_NOT_SAVED) - rc = OK; - } - - /* rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); */ - break; - case SIGNED: - case PARALLEL: - /* + if (aText) { + if (mainMessage && (mainMessage != messageIn)) + messageDestroy(mainMessage); + mainMessage = NULL; + } + + cli_dbgmsg("Mixed message with %d parts\n", multiparts); + for (i = 0; i < multiparts; i++) { + mainMessage = do_multipart(mainMessage, + messages, i, &rc, mctx, + messageIn, &aText, recursion_level + 1); + if (rc == VIRUS) { + infected = TRUE; + break; + } + if (rc == MAXREC) + break; + if (rc == OK_ATTACHMENTS_NOT_SAVED) + rc = OK; + } + + /* rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); */ + break; + case SIGNED: + case PARALLEL: + /* * If we're here it could be because we have a * multipart/mixed message, consisting of a * message followed by an attachment. That @@ -2047,226 +2025,225 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * message and we need to dig out the plain * text part of that alternative */ - if(messages) { - htmltextPart = getTextPart(messages, multiparts); - if(htmltextPart == -1) - htmltextPart = 0; - rc = parseEmailBody(messages[htmltextPart], aText, mctx, recursion_level + 1); - } - break; - default: - assert(0); - } - - if(mainMessage && (mainMessage != messageIn)) - messageDestroy(mainMessage); - - if(aText && (textIn == NULL)) { - if((!infected) && (fb = fileblobCreate()) != NULL) { - cli_dbgmsg("Save non mime and/or text/plain part\n"); - fileblobSetFilename(fb, mctx->dir, "textpart"); - /*fileblobAddData(fb, "Received: by clamd (textpart)\n", 30);*/ - fileblobSetCTX(fb, mctx->ctx); - (void)textToFileblob(aText, fb, 1); - - fileblobDestroy(fb); - mctx->files++; - } - textDestroy(aText); - } - - for(i = 0; i < multiparts; i++) - if(messages[i]) - messageDestroy(messages[i]); - - if(messages) - free(messages); + if (messages) { + htmltextPart = getTextPart(messages, multiparts); + if (htmltextPart == -1) + htmltextPart = 0; + rc = parseEmailBody(messages[htmltextPart], aText, mctx, recursion_level + 1); + } + break; + default: + assert(0); + } + + if (mainMessage && (mainMessage != messageIn)) + messageDestroy(mainMessage); + + if (aText && (textIn == NULL)) { + if ((!infected) && (fb = fileblobCreate()) != NULL) { + cli_dbgmsg("Save non mime and/or text/plain part\n"); + fileblobSetFilename(fb, mctx->dir, "textpart"); + /*fileblobAddData(fb, "Received: by clamd (textpart)\n", 30);*/ + fileblobSetCTX(fb, mctx->ctx); + (void)textToFileblob(aText, fb, 1); + + fileblobDestroy(fb); + mctx->files++; + } + textDestroy(aText); + } + + for (i = 0; i < multiparts; i++) + if (messages[i]) + messageDestroy(messages[i]); + + if (messages) + free(messages); #if HAVE_JSON - mctx->wrkobj = saveobj; + mctx->wrkobj = saveobj; #endif - return rc; + return rc; - case MESSAGE: - /* + case MESSAGE: + /* * Check for forbidden encodings */ - switch(messageGetEncoding(mainMessage)) { - case NOENCODING: - case EIGHTBIT: - case BINARY: - break; - default: - cli_dbgmsg("MIME type 'message' cannot be decoded\n"); - break; - } - rc = FAIL; - if((strcasecmp(mimeSubtype, "rfc822") == 0) || - (strcasecmp(mimeSubtype, "delivery-status") == 0)) { - message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table); - if(m) { - cli_dbgmsg("Decode rfc822\n"); - - messageSetCTX(m, mctx->ctx); - - if(mainMessage && (mainMessage != messageIn)) { - messageDestroy(mainMessage); - mainMessage = NULL; - } else - messageReset(mainMessage); - if(messageGetBody(m)) - rc = parseEmailBody(m, NULL, mctx, recursion_level + 1); - - messageDestroy(m); - } - break; - } else if(strcasecmp(mimeSubtype, "disposition-notification") == 0) { - /* RFC 2298 - handle like a normal email */ - rc = OK; - break; - } else if(strcasecmp(mimeSubtype, "partial") == 0) { - if(mctx->ctx->options->mail & CL_SCAN_MAIL_PARTIAL_MESSAGE) { - /* RFC1341 message split over many emails */ - if(rfc1341(mainMessage, mctx->dir) >= 0) - rc = OK; - } else { - cli_warnmsg("Partial message received from MUA/MTA - message cannot be scanned\n"); - } - } else if(strcasecmp(mimeSubtype, "external-body") == 0) - /* TODO */ - cli_warnmsg("Attempt to send Content-type message/external-body trapped\n"); - else - cli_warnmsg("Unsupported message format `%s' - if you believe this file contains a virus, submit it to www.clamav.net\n", mimeSubtype); - - - if(mainMessage && (mainMessage != messageIn)) - messageDestroy(mainMessage); - if(messages) - free(messages); + switch (messageGetEncoding(mainMessage)) { + case NOENCODING: + case EIGHTBIT: + case BINARY: + break; + default: + cli_dbgmsg("MIME type 'message' cannot be decoded\n"); + break; + } + rc = FAIL; + if ((strcasecmp(mimeSubtype, "rfc822") == 0) || + (strcasecmp(mimeSubtype, "delivery-status") == 0)) { + message *m = parseEmailHeaders(mainMessage, mctx->rfc821Table); + if (m) { + cli_dbgmsg("Decode rfc822\n"); + + messageSetCTX(m, mctx->ctx); + + if (mainMessage && (mainMessage != messageIn)) { + messageDestroy(mainMessage); + mainMessage = NULL; + } else + messageReset(mainMessage); + if (messageGetBody(m)) + rc = parseEmailBody(m, NULL, mctx, recursion_level + 1); + + messageDestroy(m); + } + break; + } else if (strcasecmp(mimeSubtype, "disposition-notification") == 0) { + /* RFC 2298 - handle like a normal email */ + rc = OK; + break; + } else if (strcasecmp(mimeSubtype, "partial") == 0) { + if (mctx->ctx->options->mail & CL_SCAN_MAIL_PARTIAL_MESSAGE) { + /* RFC1341 message split over many emails */ + if (rfc1341(mainMessage, mctx->dir) >= 0) + rc = OK; + } else { + cli_warnmsg("Partial message received from MUA/MTA - message cannot be scanned\n"); + } + } else if (strcasecmp(mimeSubtype, "external-body") == 0) + /* TODO */ + cli_warnmsg("Attempt to send Content-type message/external-body trapped\n"); + else + cli_warnmsg("Unsupported message format `%s' - if you believe this file contains a virus, submit it to www.clamav.net\n", mimeSubtype); + + if (mainMessage && (mainMessage != messageIn)) + messageDestroy(mainMessage); + if (messages) + free(messages); #if HAVE_JSON - mctx->wrkobj = saveobj; + mctx->wrkobj = saveobj; #endif - return rc; + return rc; - default: - cli_dbgmsg("Message received with unknown mime encoding - assume application\n"); - /* + default: + cli_dbgmsg("Message received with unknown mime encoding - assume application\n"); + /* * Some Yahoo emails attach as * Content-Type: X-unknown/unknown; * instead of * Content-Type: application/unknown; * so let's try our best to salvage something */ - case APPLICATION: - /*cptr = messageGetMimeSubtype(mainMessage); + case APPLICATION: + /*cptr = messageGetMimeSubtype(mainMessage); if((strcasecmp(cptr, "octet-stream") == 0) || (strcasecmp(cptr, "x-msdownload") == 0)) {*/ - { - fb = messageToFileblob(mainMessage, mctx->dir, 1); - - if(fb) { - cli_dbgmsg("Saving main message as attachment\n"); - if(fileblobScanAndDestroy(fb) == CL_VIRUS) - rc = VIRUS; - mctx->files++; - if(mainMessage != messageIn) { - messageDestroy(mainMessage); - mainMessage = NULL; - } else - messageReset(mainMessage); - } - } /*else + { + fb = messageToFileblob(mainMessage, mctx->dir, 1); + + if (fb) { + cli_dbgmsg("Saving main message as attachment\n"); + if (fileblobScanAndDestroy(fb) == CL_VIRUS) + rc = VIRUS; + mctx->files++; + if (mainMessage != messageIn) { + messageDestroy(mainMessage); + mainMessage = NULL; + } else + messageReset(mainMessage); + } + } /*else cli_warnmsg("Discarded application not sent as attachment\n");*/ - break; - - case AUDIO: - case VIDEO: - case IMAGE: - break; - } - - if(messages) { - /* "can't happen" */ - cli_warnmsg("messages != NULL\n"); - free(messages); - } - } - - if(aText && (textIn == NULL)) { - /* Look for a bounce in the text (non mime encoded) portion */ - const text *t; - /* isBounceStart() is expensive, reduce the number of calls */ - bool lookahead_definately_is_bounce = FALSE; - - for(t = aText; t && (rc != VIRUS); t = t->t_next) { - const line_t *l = t->t_line; - const text *lookahead, *topofbounce; - const char *s; - bool inheader; - - if(l == NULL) { - /* assert(lookahead_definately_is_bounce == FALSE) */ - continue; - } - - if(lookahead_definately_is_bounce) - lookahead_definately_is_bounce = FALSE; - else if(!isBounceStart(mctx, lineGetData(l))) - continue; - - lookahead = t->t_next; - if(lookahead) { - if(isBounceStart(mctx, lineGetData(lookahead->t_line))) { - lookahead_definately_is_bounce = TRUE; - /* don't save worthless header lines */ - continue; - } - } else /* don't save a single liner */ - break; - - /* + break; + + case AUDIO: + case VIDEO: + case IMAGE: + break; + } + + if (messages) { + /* "can't happen" */ + cli_warnmsg("messages != NULL\n"); + free(messages); + } + } + + if (aText && (textIn == NULL)) { + /* Look for a bounce in the text (non mime encoded) portion */ + const text *t; + /* isBounceStart() is expensive, reduce the number of calls */ + bool lookahead_definately_is_bounce = FALSE; + + for (t = aText; t && (rc != VIRUS); t = t->t_next) { + const line_t *l = t->t_line; + const text *lookahead, *topofbounce; + const char *s; + bool inheader; + + if (l == NULL) { + /* assert(lookahead_definately_is_bounce == FALSE) */ + continue; + } + + if (lookahead_definately_is_bounce) + lookahead_definately_is_bounce = FALSE; + else if (!isBounceStart(mctx, lineGetData(l))) + continue; + + lookahead = t->t_next; + if (lookahead) { + if (isBounceStart(mctx, lineGetData(lookahead->t_line))) { + lookahead_definately_is_bounce = TRUE; + /* don't save worthless header lines */ + continue; + } + } else /* don't save a single liner */ + break; + + /* * We've found what looks like the start of a bounce * message. Only bother saving if it really is a bounce * message, this helps to speed up scanning of ping-pong * messages that have lots of bounces within bounces in * them */ - for(; lookahead; lookahead = lookahead->t_next) { - l = lookahead->t_line; - - if(l == NULL) - break; - s = lineGetData(l); - if(strncasecmp(s, "Content-Type:", 13) == 0) { - /* + for (; lookahead; lookahead = lookahead->t_next) { + l = lookahead->t_line; + + if (l == NULL) + break; + s = lineGetData(l); + if (strncasecmp(s, "Content-Type:", 13) == 0) { + /* * Don't bother with text/plain or * text/html */ - if(cli_strcasestr(s, "text/plain") != NULL) - /* + if (cli_strcasestr(s, "text/plain") != NULL) + /* * Don't bother to save the * unuseful part, read past * the headers then we'll go * on to look for the next * bounce message */ - continue; - if((!doPhishingScan) && - (cli_strcasestr(s, "text/html") != NULL)) - continue; - break; - } - } - - if(lookahead && (lookahead->t_line == NULL)) { - cli_dbgmsg("Non mime part bounce message is not mime encoded, so it will not be scanned\n"); - t = lookahead; - /* look for next bounce message */ - continue; - } - - /* + continue; + if ((!doPhishingScan) && + (cli_strcasestr(s, "text/html") != NULL)) + continue; + break; + } + } + + if (lookahead && (lookahead->t_line == NULL)) { + cli_dbgmsg("Non mime part bounce message is not mime encoded, so it will not be scanned\n"); + t = lookahead; + /* look for next bounce message */ + continue; + } + + /* * Prescan the bounce message to see if there's likely * to be anything nasty. * This algorithm is hand crafted and may be breakable @@ -2275,155 +2252,156 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re * significantly speeds up the scanning of multiple * bounces (i.e. bounces within many bounces) */ - for(; lookahead; lookahead = lookahead->t_next) { - l = lookahead->t_line; - - if(l) { - s = lineGetData(l); - if((strncasecmp(s, "Content-Type:", 13) == 0) && - (strstr(s, "multipart/") == NULL) && - (strstr(s, "message/rfc822") == NULL) && - (strstr(s, "text/plain") == NULL)) - break; - } - } - if(lookahead == NULL) { - cli_dbgmsg("cli_mbox: I believe it's plain text which must be clean\n"); - /* nothing here, move along please */ - break; - } - if((fb = fileblobCreate()) == NULL) - break; - cli_dbgmsg("Save non mime part bounce message\n"); - fileblobSetFilename(fb, mctx->dir, "bounce"); - fileblobAddData(fb, (const unsigned char *)"Received: by clamd (bounce)\n", 28); - fileblobSetCTX(fb, mctx->ctx); - - inheader = TRUE; - topofbounce = NULL; - do { - l = t->t_line; - - if(l == NULL) { - if(inheader) { - inheader = FALSE; - topofbounce = t; - } - } else { - s = lineGetData(l); - fileblobAddData(fb, (const unsigned char *)s, strlen(s)); - } - fileblobAddData(fb, (const unsigned char *)"\n", 1); - lookahead = t->t_next; - if(lookahead == NULL) - break; - t = lookahead; - l = t->t_line; - if((!inheader) && l) { - s = lineGetData(l); - if(isBounceStart(mctx, s)) { - cli_dbgmsg("Found the start of another bounce candidate (%s)\n", s); - lookahead_definately_is_bounce = TRUE; - break; - } - } - } while(!fileblobInfected(fb)); - - if(fileblobScanAndDestroy(fb) == CL_VIRUS) - rc = VIRUS; - mctx->files++; - - if(topofbounce) - t = topofbounce; - } - textDestroy(aText); - aText = NULL; - } - - /* + for (; lookahead; lookahead = lookahead->t_next) { + l = lookahead->t_line; + + if (l) { + s = lineGetData(l); + if ((strncasecmp(s, "Content-Type:", 13) == 0) && + (strstr(s, "multipart/") == NULL) && + (strstr(s, "message/rfc822") == NULL) && + (strstr(s, "text/plain") == NULL)) + break; + } + } + if (lookahead == NULL) { + cli_dbgmsg("cli_mbox: I believe it's plain text which must be clean\n"); + /* nothing here, move along please */ + break; + } + if ((fb = fileblobCreate()) == NULL) + break; + cli_dbgmsg("Save non mime part bounce message\n"); + fileblobSetFilename(fb, mctx->dir, "bounce"); + fileblobAddData(fb, (const unsigned char *)"Received: by clamd (bounce)\n", 28); + fileblobSetCTX(fb, mctx->ctx); + + inheader = TRUE; + topofbounce = NULL; + do { + l = t->t_line; + + if (l == NULL) { + if (inheader) { + inheader = FALSE; + topofbounce = t; + } + } else { + s = lineGetData(l); + fileblobAddData(fb, (const unsigned char *)s, strlen(s)); + } + fileblobAddData(fb, (const unsigned char *)"\n", 1); + lookahead = t->t_next; + if (lookahead == NULL) + break; + t = lookahead; + l = t->t_line; + if ((!inheader) && l) { + s = lineGetData(l); + if (isBounceStart(mctx, s)) { + cli_dbgmsg("Found the start of another bounce candidate (%s)\n", s); + lookahead_definately_is_bounce = TRUE; + break; + } + } + } while (!fileblobInfected(fb)); + + if (fileblobScanAndDestroy(fb) == CL_VIRUS) + rc = VIRUS; + mctx->files++; + + if (topofbounce) + t = topofbounce; + } + textDestroy(aText); + aText = NULL; + } + + /* * No attachments - scan the text portions, often files * are hidden in HTML code */ - if(mainMessage && (rc != VIRUS)) { - text *t_line; + if (mainMessage && (rc != VIRUS)) { + text *t_line; - /* + /* * Look for uu-encoded main file */ - if(mainMessage->body_first != NULL && - (encodingLine(mainMessage) != NULL) && - ((t_line = bounceBegin(mainMessage)) != NULL)) - rc = (exportBounceMessage(mctx, t_line) == CL_VIRUS) ? VIRUS : OK; - else { - bool saveIt; - - if(messageGetMimeType(mainMessage) == MESSAGE) - /* + if (mainMessage->body_first != NULL && + (encodingLine(mainMessage) != NULL) && + ((t_line = bounceBegin(mainMessage)) != NULL)) + rc = (exportBounceMessage(mctx, t_line) == CL_VIRUS) ? VIRUS : OK; + else { + bool saveIt; + + if (messageGetMimeType(mainMessage) == MESSAGE) + /* * Quick peek, if the encapsulated * message has no * content encoding statement don't * bother saving to scan, it's safe */ - saveIt = (bool)(encodingLine(mainMessage) != NULL); - else if(mainMessage->body_last != NULL && (t_line = encodingLine(mainMessage)) != NULL) { - /* + saveIt = (bool)(encodingLine(mainMessage) != NULL); + else if (mainMessage->body_last != NULL && (t_line = encodingLine(mainMessage)) != NULL) { + /* * Some bounces include the message * body without the headers. * FIXME: Unfortunately this generates a * lot of false positives that a bounce * has been found when it hasn't. */ - if((fb = fileblobCreate()) != NULL) { - cli_dbgmsg("Found a bounce message with no header at '%s'\n", - lineGetData(t_line->t_line)); - fileblobSetFilename(fb, mctx->dir, "bounce"); - fileblobAddData(fb, - (const unsigned char *)"Received: by clamd (bounce)\n", - 28); - - fileblobSetCTX(fb, mctx->ctx); - if(fileblobScanAndDestroy(textToFileblob(t_line, fb, 1)) == CL_VIRUS) - rc = VIRUS; - mctx->files++; - } - saveIt = FALSE; - } else - /* + if ((fb = fileblobCreate()) != NULL) { + cli_dbgmsg("Found a bounce message with no header at '%s'\n", + lineGetData(t_line->t_line)); + fileblobSetFilename(fb, mctx->dir, "bounce"); + fileblobAddData(fb, + (const unsigned char *)"Received: by clamd (bounce)\n", + 28); + + fileblobSetCTX(fb, mctx->ctx); + if (fileblobScanAndDestroy(textToFileblob(t_line, fb, 1)) == CL_VIRUS) + rc = VIRUS; + mctx->files++; + } + saveIt = FALSE; + } else + /* * Save the entire text portion, * since it it may be an HTML file with * a JavaScript virus or a phish */ - saveIt = TRUE; - - if(saveIt) { - cli_dbgmsg("Saving text part to scan, rc = %d\n", - (int)rc); - if(saveTextPart(mctx, mainMessage, 1) == CL_VIRUS) - rc = VIRUS; - - if(mainMessage != messageIn) { - messageDestroy(mainMessage); - mainMessage = NULL; - } else - messageReset(mainMessage); - } - } - } /*else - rc = OK_ATTACHMENTS_NOT_SAVED; */ /* nothing saved */ - - if(mainMessage && (mainMessage != messageIn)) - messageDestroy(mainMessage); - - if((rc != FAIL) && infected) - rc = VIRUS; + saveIt = TRUE; + + if (saveIt) { + cli_dbgmsg("Saving text part to scan, rc = %d\n", + (int)rc); + if (saveTextPart(mctx, mainMessage, 1) == CL_VIRUS) + rc = VIRUS; + + if (mainMessage != messageIn) { + messageDestroy(mainMessage); + mainMessage = NULL; + } else + messageReset(mainMessage); + } + } + } /*else + rc = OK_ATTACHMENTS_NOT_SAVED; */ + /* nothing saved */ + + if (mainMessage && (mainMessage != messageIn)) + messageDestroy(mainMessage); + + if ((rc != FAIL) && infected) + rc = VIRUS; #if HAVE_JSON - mctx->wrkobj = saveobj; + mctx->wrkobj = saveobj; #endif - cli_dbgmsg("parseEmailBody() returning %d\n", (int)rc); + cli_dbgmsg("parseEmailBody() returning %d\n", (int)rc); - return rc; + return rc; } /* @@ -2434,16 +2412,16 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re static int boundaryStart(const char *line, const char *boundary) { - const char *ptr; - char *out; - int rc; - char buf[RFC2821LENGTH + 1]; + const char *ptr; + char *out; + int rc; + char buf[RFC2821LENGTH + 1]; char *newline; - if(line == NULL || *line == '\0') - return 0; /* empty line */ - if(boundary == NULL) - return 0; + if (line == NULL || *line == '\0') + return 0; /* empty line */ + if (boundary == NULL) + return 0; newline = strdup(line); if (!(newline)) @@ -2460,39 +2438,39 @@ boundaryStart(const char *line, const char *boundary) if (newline != line) cli_chomp(newline); - /* cli_dbgmsg("boundaryStart: line = '%s' boundary = '%s'\n", line, boundary); */ + /* cli_dbgmsg("boundaryStart: line = '%s' boundary = '%s'\n", line, boundary); */ - if((*newline != '-') && (*newline != '(')) { + if ((*newline != '-') && (*newline != '(')) { if (newline != line) free(newline); - return 0; + return 0; } - if(strchr(newline, '-') == NULL) { + if (strchr(newline, '-') == NULL) { if (newline != line) free(newline); - return 0; + return 0; } - if(strlen(newline) <= sizeof(buf)) { - out = NULL; - ptr = rfc822comments(newline, buf); - } else - ptr = out = rfc822comments(newline, NULL); + if (strlen(newline) <= sizeof(buf)) { + out = NULL; + ptr = rfc822comments(newline, buf); + } else + ptr = out = rfc822comments(newline, NULL); - if(ptr == NULL) - ptr = newline; + if (ptr == NULL) + ptr = newline; - if((*ptr++ != '-') || (*ptr == '\0')) { - if(out) - free(out); + if ((*ptr++ != '-') || (*ptr == '\0')) { + if (out) + free(out); if (newline != line) free(newline); - return 0; - } + return 0; + } - /* + /* * Gibe.B3 is broken, it has: * boundary="---- =_NextPart_000_01C31177.9DC7C000" * but it's boundaries look like @@ -2512,45 +2490,45 @@ boundaryStart(const char *line, const char *boundary) * they're not. Irrespective of whatever RFC2822 says, we need to find * viruses in both types of mails. */ - if((strstr(&ptr[1], boundary) != NULL) || (strstr(newline, boundary) != NULL)) { - const char *k = ptr; + if ((strstr(&ptr[1], boundary) != NULL) || (strstr(newline, boundary) != NULL)) { + const char *k = ptr; - /* + /* * We need to ensure that we don't match --11=-=-=11 when * looking for --1=-=-=1 in well behaved headers, that's a * false positive problem mentioned above */ - rc = 0; - do - if(strcmp(++k, boundary) == 0) { - rc = 1; - break; - } - while(*k == '-'); - if(rc == 0) { - k = &line[1]; - do - if(strcmp(++k, boundary) == 0) { - rc = 1; - break; - } - while(*k == '-'); - } - } else if(*ptr++ != '-') - rc = 0; - else - rc = (strcasecmp(ptr, boundary) == 0); - - if(out) - free(out); - - if(rc == 1) - cli_dbgmsg("boundaryStart: found %s in %s\n", boundary, line); + rc = 0; + do + if (strcmp(++k, boundary) == 0) { + rc = 1; + break; + } + while (*k == '-'); + if (rc == 0) { + k = &line[1]; + do + if (strcmp(++k, boundary) == 0) { + rc = 1; + break; + } + while (*k == '-'); + } + } else if (*ptr++ != '-') + rc = 0; + else + rc = (strcasecmp(ptr, boundary) == 0); + + if (out) + free(out); + + if (rc == 1) + cli_dbgmsg("boundaryStart: found %s in %s\n", boundary, line); if (newline != line) free(newline); - return rc; + return rc; } /* @@ -2561,15 +2539,15 @@ boundaryStart(const char *line, const char *boundary) static int boundaryEnd(const char *line, const char *boundary) { - size_t len; + size_t len; char *newline, *p, *p2; - if(line == NULL || *line == '\0') - return 0; + if (line == NULL || *line == '\0') + return 0; p = newline = strdup(line); if (!(newline)) { - p = (char *)line; + p = (char *)line; newline = (char *)line; } @@ -2580,59 +2558,59 @@ boundaryEnd(const char *line, const char *boundary) *(p2--) = '\0'; } - /* cli_dbgmsg("boundaryEnd: line = '%s' boundary = '%s'\n", newline, boundary); */ + /* cli_dbgmsg("boundaryEnd: line = '%s' boundary = '%s'\n", newline, boundary); */ - if(*p++ != '-') { + if (*p++ != '-') { if (newline != line) free(newline); - return 0; + return 0; } - if(*p++ != '-') { + if (*p++ != '-') { if (newline != line) free(newline); - return 0; + return 0; } - len = strlen(boundary); - if(strncasecmp(p, boundary, len) != 0) { + len = strlen(boundary); + if (strncasecmp(p, boundary, len) != 0) { if (newline != line) free(newline); - return 0; + return 0; } - /* + /* * Use < rather than == because some broken mails have white * space after the boundary */ - if(strlen(p) < (len + 2)) { + if (strlen(p) < (len + 2)) { if (newline != line) free(newline); - return 0; + return 0; } - p = &p[len]; - if(*p++ != '-') { + p = &p[len]; + if (*p++ != '-') { if (newline != line) free(newline); - return 0; + return 0; } - if(*p == '-') { - /* cli_dbgmsg("boundaryEnd: found %s in %s\n", boundary, p); */ + if (*p == '-') { + /* cli_dbgmsg("boundaryEnd: found %s in %s\n", boundary, p); */ if (newline != line) free(newline); - return 1; - } + return 1; + } if (newline != line) free(newline); - return 0; + return 0; } /* @@ -2641,34 +2619,34 @@ boundaryEnd(const char *line, const char *boundary) static int initialiseTables(table_t **rfc821Table, table_t **subtypeTable) { - const struct tableinit *tableinit; + const struct tableinit *tableinit; - /* + /* * Initialise the various look up tables */ - *rfc821Table = tableCreate(); - assert(*rfc821Table != NULL); - - for(tableinit = rfc821headers; tableinit->key; tableinit++) - if(tableInsert(*rfc821Table, tableinit->key, tableinit->value) < 0) { - tableDestroy(*rfc821Table); - *rfc821Table = NULL; - return -1; - } - - *subtypeTable = tableCreate(); - assert(*subtypeTable != NULL); - - for(tableinit = mimeSubtypes; tableinit->key; tableinit++) - if(tableInsert(*subtypeTable, tableinit->key, tableinit->value) < 0) { - tableDestroy(*rfc821Table); - tableDestroy(*subtypeTable); - *rfc821Table = NULL; - *subtypeTable = NULL; - return -1; - } - - return 0; + *rfc821Table = tableCreate(); + assert(*rfc821Table != NULL); + + for (tableinit = rfc821headers; tableinit->key; tableinit++) + if (tableInsert(*rfc821Table, tableinit->key, tableinit->value) < 0) { + tableDestroy(*rfc821Table); + *rfc821Table = NULL; + return -1; + } + + *subtypeTable = tableCreate(); + assert(*subtypeTable != NULL); + + for (tableinit = mimeSubtypes; tableinit->key; tableinit++) + if (tableInsert(*subtypeTable, tableinit->key, tableinit->value) < 0) { + tableDestroy(*rfc821Table); + tableDestroy(*subtypeTable); + *rfc821Table = NULL; + *subtypeTable = NULL; + return -1; + } + + return 0; } /* @@ -2682,17 +2660,17 @@ initialiseTables(table_t **rfc821Table, table_t **subtypeTable) static int getTextPart(message *const messages[], size_t size) { - size_t i; - int textpart = -1; + size_t i; + int textpart = -1; - for(i = 0; i < size; i++) - if(messages[i] && (messageGetMimeType(messages[i]) == TEXT)) { - if(strcasecmp(messageGetMimeSubtype(messages[i]), "html") == 0) - return (int)i; - textpart = (int)i; - } + for (i = 0; i < size; i++) + if (messages[i] && (messageGetMimeType(messages[i]) == TEXT)) { + if (strcasecmp(messageGetMimeSubtype(messages[i]), "html") == 0) + return (int)i; + textpart = (int)i; + } - return textpart; + return textpart; } /* @@ -2712,31 +2690,31 @@ getTextPart(message *const messages[], size_t size) static size_t strip(char *buf, int len) { - register char *ptr; - register size_t i; - - if((buf == NULL) || (len <= 0)) - return 0; - - i = strlen(buf); - if(len > (int)(i + 1)) - return i; - ptr = &buf[--len]; - -#if defined(UNIX) || defined(C_LINUX) || defined(C_DARWIN) /* watch - it may be in shared text area */ - do - if(*ptr) - *ptr = '\0'; - while((--len >= 0) && (!isgraph(*--ptr)) && (*ptr != '\n') && (*ptr != '\r')); -#else /* more characters can be displayed on DOS */ - do -#ifndef REAL_MODE_DOS - if(*ptr) /* C8.0 puts into a text area */ + register char *ptr; + register size_t i; + + if ((buf == NULL) || (len <= 0)) + return 0; + + i = strlen(buf); + if (len > (int)(i + 1)) + return i; + ptr = &buf[--len]; + +#if defined(UNIX) || defined(C_LINUX) || defined(C_DARWIN) /* watch - it may be in shared text area */ + do + if (*ptr) + *ptr = '\0'; + while ((--len >= 0) && (!isgraph(*--ptr)) && (*ptr != '\n') && (*ptr != '\r')); +#else /* more characters can be displayed on DOS */ + do +#ifndef REAL_MODE_DOS + if (*ptr) /* C8.0 puts into a text area */ #endif - *ptr = '\0'; - while((--len >= 0) && ((*--ptr == '\0') || isspace((int)(*ptr & 0xFF)))); + *ptr = '\0'; + while ((--len >= 0) && ((*--ptr == '\0') || isspace((int)(*ptr & 0xFF)))); #endif - return((size_t)(len + 1)); + return ((size_t)(len + 1)); } /* @@ -2746,10 +2724,10 @@ strip(char *buf, int len) size_t strstrip(char *s) { - if(s == (char *)NULL) - return(0); + if (s == (char *)NULL) + return (0); - return(strip(s, strlen(s) + 1)); + return (strip(s, strlen(s) + 1)); } /* @@ -2758,38 +2736,38 @@ strstrip(char *s) static int parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const char *arg) { - char *copy, *p, *buf; - const char *ptr; - int commandNumber; + char *copy, *p, *buf; + const char *ptr; + int commandNumber; - cli_dbgmsg("parseMimeHeader: cmd='%s', arg='%s'\n", cmd, arg); + cli_dbgmsg("parseMimeHeader: cmd='%s', arg='%s'\n", cmd, arg); - copy = rfc822comments(cmd, NULL); - if(copy) { - commandNumber = tableFind(rfc821Table, copy); - free(copy); - } else - commandNumber = tableFind(rfc821Table, cmd); + copy = rfc822comments(cmd, NULL); + if (copy) { + commandNumber = tableFind(rfc821Table, copy); + free(copy); + } else + commandNumber = tableFind(rfc821Table, cmd); - copy = rfc822comments(arg, NULL); + copy = rfc822comments(arg, NULL); - if(copy) - ptr = copy; - else - ptr = arg; + if (copy) + ptr = copy; + else + ptr = arg; - buf = NULL; + buf = NULL; - switch(commandNumber) { - case CONTENT_TYPE: - /* + switch (commandNumber) { + case CONTENT_TYPE: + /* * Fix for non RFC1521 compliant mailers * that send content-type: Text instead * of content-type: Text/Plain, or * just simply "Content-Type:" */ - if(arg == NULL) - /* + if (arg == NULL) + /* * According to section 4 of RFC1521: * "Note also that a subtype specification is * MANDATORY. There are no default subtypes" @@ -2798,155 +2776,155 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c * for the subtype because virus writers and * email client writers don't get it right */ - cli_dbgmsg("Empty content-type received, no subtype specified, assuming text/plain; charset=us-ascii\n"); - else if(strchr(ptr, '/') == NULL) - /* + cli_dbgmsg("Empty content-type received, no subtype specified, assuming text/plain; charset=us-ascii\n"); + else if (strchr(ptr, '/') == NULL) + /* * Empty field, such as * Content-Type: * which I believe is illegal according to * RFC1521 */ - cli_dbgmsg("Invalid content-type '%s' received, no subtype specified, assuming text/plain; charset=us-ascii\n", ptr); - else { - int i; + cli_dbgmsg("Invalid content-type '%s' received, no subtype specified, assuming text/plain; charset=us-ascii\n", ptr); + else { + int i; - buf = cli_malloc(strlen(ptr) + 1); - if(buf == NULL) { + buf = cli_malloc(strlen(ptr) + 1); + if (buf == NULL) { cli_errmsg("parseMimeHeader: Unable to allocate memory for buf %llu\n", (long long unsigned)(strlen(ptr) + 1)); - if(copy) - free(copy); - return -1; - } - /* + if (copy) + free(copy); + return -1; + } + /* * Some clients are broken and * put white space after the ; */ - if(*arg == '/') { - cli_dbgmsg("Content-type '/' received, assuming application/octet-stream\n"); - messageSetMimeType(m, "application"); - messageSetMimeSubtype(m, "octet-stream"); - } else { - /* + if (*arg == '/') { + cli_dbgmsg("Content-type '/' received, assuming application/octet-stream\n"); + messageSetMimeType(m, "application"); + messageSetMimeSubtype(m, "octet-stream"); + } else { + /* * The content type could be in quotes: * Content-Type: "multipart/mixed" * FIXME: this is a hack in that ignores * the quotes, it doesn't handle * them properly */ - while(isspace(*ptr)) - ptr++; - if(ptr[0] == '\"') - ptr++; + while (isspace(*ptr)) + ptr++; + if (ptr[0] == '\"') + ptr++; - if(ptr[0] != '/') { - char *s; + if (ptr[0] != '/') { + char *s; #ifdef CL_THREAD_SAFE - char *strptr = NULL; + char *strptr = NULL; #endif - s = cli_strtokbuf(ptr, 0, ";", buf); - /* + s = cli_strtokbuf(ptr, 0, ";", buf); + /* * Handle * Content-Type: foo/bar multipart/mixed * and * Content-Type: multipart/mixed foo/bar */ - if(s && *s) { - char *buf2 = cli_strdup(buf); - - if(buf2 == NULL) { - if(copy) - free(copy); - free(buf); - return -1; - } - for(;;) { -#ifdef CL_THREAD_SAFE - int set = messageSetMimeType(m, strtok_r(s, "/", &strptr)); + if (s && *s) { + char *buf2 = cli_strdup(buf); + + if (buf2 == NULL) { + if (copy) + free(copy); + free(buf); + return -1; + } + for (;;) { +#ifdef CL_THREAD_SAFE + int set = messageSetMimeType(m, strtok_r(s, "/", &strptr)); #else - int set = messageSetMimeType(m, strtok(s, "/")); + int set = messageSetMimeType(m, strtok(s, "/")); #endif -#ifdef CL_THREAD_SAFE - s = strtok_r(NULL, ";", &strptr); +#ifdef CL_THREAD_SAFE + s = strtok_r(NULL, ";", &strptr); #else - s = strtok(NULL, ";"); + s = strtok(NULL, ";"); #endif - if(s == NULL) - break; - if(set) { - size_t len = strstrip(s) - 1; - if(s[len] == '\"') { - s[len] = '\0'; - len = strstrip(s); - } - if(len) { - if(strchr(s, ' ')) - messageSetMimeSubtype(m, - cli_strtokbuf(s, 0, " ", buf2)); - else - messageSetMimeSubtype(m, s); - } - } - - while(*s && !isspace(*s)) - s++; - if(*s++ == '\0') - break; - if(*s == '\0') - break; - } - free(buf2); - } - } - } - - /* + if (s == NULL) + break; + if (set) { + size_t len = strstrip(s) - 1; + if (s[len] == '\"') { + s[len] = '\0'; + len = strstrip(s); + } + if (len) { + if (strchr(s, ' ')) + messageSetMimeSubtype(m, + cli_strtokbuf(s, 0, " ", buf2)); + else + messageSetMimeSubtype(m, s); + } + } + + while (*s && !isspace(*s)) + s++; + if (*s++ == '\0') + break; + if (*s == '\0') + break; + } + free(buf2); + } + } + } + + /* * Add in all rest of the the arguments. * e.g. if the header is this: * Content-Type:', arg='multipart/mixed; boundary=foo * we find the boundary argument set it */ - i = 1; - while(cli_strtokbuf(ptr, i++, ";", buf) != NULL) { - cli_dbgmsg("mimeArgs = '%s'\n", buf); - - messageAddArguments(m, buf); - } - } - break; - case CONTENT_TRANSFER_ENCODING: - messageSetEncoding(m, ptr); - break; - case CONTENT_DISPOSITION: - buf = cli_malloc(strlen(ptr) + 1); - if(buf == NULL) { + i = 1; + while (cli_strtokbuf(ptr, i++, ";", buf) != NULL) { + cli_dbgmsg("mimeArgs = '%s'\n", buf); + + messageAddArguments(m, buf); + } + } + break; + case CONTENT_TRANSFER_ENCODING: + messageSetEncoding(m, ptr); + break; + case CONTENT_DISPOSITION: + buf = cli_malloc(strlen(ptr) + 1); + if (buf == NULL) { cli_errmsg("parseMimeHeader: Unable to allocate memory for buf %llu\n", (long long unsigned)(strlen(ptr) + 1)); - if(copy) - free(copy); - return -1; - } - p = cli_strtokbuf(ptr, 0, ";", buf); - if(p && *p) { - messageSetDispositionType(m, p); - messageAddArgument(m, cli_strtokbuf(ptr, 1, ";", buf)); - } - if(!messageHasFilename(m)) - /* + if (copy) + free(copy); + return -1; + } + p = cli_strtokbuf(ptr, 0, ";", buf); + if (p && *p) { + messageSetDispositionType(m, p); + messageAddArgument(m, cli_strtokbuf(ptr, 1, ";", buf)); + } + if (!messageHasFilename(m)) + /* * Handle this type of header, without * a filename (e.g. some Worm.Torvil.D) * Content-ID: * Content-Transfer-Encoding: base64 * Content-Disposition: attachment */ - messageAddArgument(m, "filename=unknown"); - } - if(copy) - free(copy); - if(buf) - free(buf); - - return 0; + messageAddArgument(m, "filename=unknown"); + } + if (copy) + free(copy); + if (buf) + free(buf); + + return 0; } /* @@ -2955,19 +2933,19 @@ parseMimeHeader(message *m, const char *cmd, const table_t *rfc821Table, const c static int saveTextPart(mbox_ctx *mctx, message *m, int destroy_text) { - fileblob *fb; + fileblob *fb; - messageAddArgument(m, "filename=textportion"); - if((fb = messageToFileblob(m, mctx->dir, destroy_text)) != NULL) { - /* + messageAddArgument(m, "filename=textportion"); + if ((fb = messageToFileblob(m, mctx->dir, destroy_text)) != NULL) { + /* * Save main part to scan that */ - cli_dbgmsg("Saving main message\n"); + cli_dbgmsg("Saving main message\n"); - mctx->files++; - return fileblobScanAndDestroy(fb); - } - return CL_ETMPFILE; + mctx->files++; + return fileblobScanAndDestroy(fb); + } + return CL_ETMPFILE; } /* @@ -2981,73 +2959,74 @@ saveTextPart(mbox_ctx *mctx, message *m, int destroy_text) static char * rfc822comments(const char *in, char *out) { - const char *iptr; - char *optr; - int backslash, inquote, commentlevel; + const char *iptr; + char *optr; + int backslash, inquote, commentlevel; - if(in == NULL) - return NULL; + if (in == NULL) + return NULL; - if(strchr(in, '(') == NULL) - return NULL; + if (strchr(in, '(') == NULL) + return NULL; - assert(out != in); + assert(out != in); - while(isspace(*in)) - in++; + while (isspace(*in)) + in++; - if(out == NULL) { - out = cli_malloc(strlen(in) + 1); - if(out == NULL) { + if (out == NULL) { + out = cli_malloc(strlen(in) + 1); + if (out == NULL) { cli_errmsg("rfc822comments: Unable to allocate memory for out %llu\n", (long long unsigned)(strlen(in) + 1)); - return NULL; + return NULL; } - } - - backslash = commentlevel = inquote = 0; - optr = out; - - cli_dbgmsg("rfc822comments: contains a comment\n"); - - for(iptr = in; *iptr; iptr++) - if(backslash) { - if(commentlevel == 0) - *optr++ = *iptr; - backslash = 0; - } else switch(*iptr) { - case '\\': - backslash = 1; - break; - case '\"': - *optr++ = '\"'; - inquote = !inquote; - break; - case '(': - if(inquote) - *optr++ = '('; - else - commentlevel++; - break; - case ')': - if(inquote) - *optr++ = ')'; - else if(commentlevel > 0) - commentlevel--; - break; - default: - if(commentlevel == 0) - *optr++ = *iptr; - } - - if(backslash) /* last character was a single backslash */ - *optr++ = '\\'; - *optr = '\0'; - - /*strstrip(out);*/ - - cli_dbgmsg("rfc822comments '%s'=>'%s'\n", in, out); - - return out; + } + + backslash = commentlevel = inquote = 0; + optr = out; + + cli_dbgmsg("rfc822comments: contains a comment\n"); + + for (iptr = in; *iptr; iptr++) + if (backslash) { + if (commentlevel == 0) + *optr++ = *iptr; + backslash = 0; + } else + switch (*iptr) { + case '\\': + backslash = 1; + break; + case '\"': + *optr++ = '\"'; + inquote = !inquote; + break; + case '(': + if (inquote) + *optr++ = '('; + else + commentlevel++; + break; + case ')': + if (inquote) + *optr++ = ')'; + else if (commentlevel > 0) + commentlevel--; + break; + default: + if (commentlevel == 0) + *optr++ = *iptr; + } + + if (backslash) /* last character was a single backslash */ + *optr++ = '\\'; + *optr = '\0'; + + /*strstrip(out);*/ + + cli_dbgmsg("rfc822comments '%s'=>'%s'\n", in, out); + + return out; } /* @@ -3057,110 +3036,109 @@ rfc822comments(const char *in, char *out) static char * rfc2047(const char *in) { - char *out, *pout; - size_t len; + char *out, *pout; + size_t len; - if((strstr(in, "=?") == NULL) || (strstr(in, "?=") == NULL)) - return cli_strdup(in); + if ((strstr(in, "=?") == NULL) || (strstr(in, "?=") == NULL)) + return cli_strdup(in); - cli_dbgmsg("rfc2047 '%s'\n", in); - out = cli_malloc(strlen(in) + 1); + cli_dbgmsg("rfc2047 '%s'\n", in); + out = cli_malloc(strlen(in) + 1); - if(out == NULL) { + if (out == NULL) { cli_errmsg("rfc2047: Unable to allocate memory for out %llu\n", (long long unsigned)(strlen(in) + 1)); - return NULL; + return NULL; } - pout = out; - - /* For each RFC2047 string */ - while(*in) { - char encoding, *ptr, *enctext; - message *m; - blob *b; - - /* Find next RFC2047 string */ - while(*in) { - if((*in == '=') && (in[1] == '?')) { - in += 2; - break; - } - *pout++ = *in++; - } - /* Skip over charset, find encoding */ - while((*in != '?') && *in) - in++; - if(*in == '\0') - break; - encoding = *++in; - encoding = (char)tolower(encoding); - - if((encoding != 'q') && (encoding != 'b')) { - cli_warnmsg("Unsupported RFC2047 encoding type '%c' - if you believe this file contains a virus, submit it to www.clamav.net\n", encoding); - free(out); - out = NULL; - break; - } - /* Skip to encoded text */ - if(*++in != '?') - break; - if(*++in == '\0') - break; - - enctext = cli_strdup(in); - if(enctext == NULL) { - free(out); - out = NULL; - break; - } - in = strstr(in, "?="); - if(in == NULL) { - free(enctext); - break; - } - in += 2; - ptr = strstr(enctext, "?="); - assert(ptr != NULL); - *ptr = '\0'; - /*cli_dbgmsg("Need to decode '%s' with method '%c'\n", enctext, encoding);*/ - - m = messageCreate(); - if(m == NULL) - break; - messageAddStr(m, enctext); - free(enctext); - switch(encoding) { - case 'q': - messageSetEncoding(m, "quoted-printable"); - break; - case 'b': - messageSetEncoding(m, "base64"); - break; - } - b = messageToBlob(m, 1); - if (b == NULL) { - messageDestroy(m); - break; - } - len = blobGetDataSize(b); - cli_dbgmsg("Decoded as '%*.*s'\n", (int)len, (int)len, - (const char *)blobGetData(b)); - memcpy(pout, blobGetData(b), len); - blobDestroy(b); - messageDestroy(m); - if(len > 0 && pout[len - 1] == '\n') - pout += len - 1; - else - pout += len; - - } - if(out == NULL) - return NULL; - - *pout = '\0'; - - cli_dbgmsg("rfc2047 returns '%s'\n", out); - return out; + pout = out; + + /* For each RFC2047 string */ + while (*in) { + char encoding, *ptr, *enctext; + message *m; + blob *b; + + /* Find next RFC2047 string */ + while (*in) { + if ((*in == '=') && (in[1] == '?')) { + in += 2; + break; + } + *pout++ = *in++; + } + /* Skip over charset, find encoding */ + while ((*in != '?') && *in) + in++; + if (*in == '\0') + break; + encoding = *++in; + encoding = (char)tolower(encoding); + + if ((encoding != 'q') && (encoding != 'b')) { + cli_warnmsg("Unsupported RFC2047 encoding type '%c' - if you believe this file contains a virus, submit it to www.clamav.net\n", encoding); + free(out); + out = NULL; + break; + } + /* Skip to encoded text */ + if (*++in != '?') + break; + if (*++in == '\0') + break; + + enctext = cli_strdup(in); + if (enctext == NULL) { + free(out); + out = NULL; + break; + } + in = strstr(in, "?="); + if (in == NULL) { + free(enctext); + break; + } + in += 2; + ptr = strstr(enctext, "?="); + assert(ptr != NULL); + *ptr = '\0'; + /*cli_dbgmsg("Need to decode '%s' with method '%c'\n", enctext, encoding);*/ + + m = messageCreate(); + if (m == NULL) + break; + messageAddStr(m, enctext); + free(enctext); + switch (encoding) { + case 'q': + messageSetEncoding(m, "quoted-printable"); + break; + case 'b': + messageSetEncoding(m, "base64"); + break; + } + b = messageToBlob(m, 1); + if (b == NULL) { + messageDestroy(m); + break; + } + len = blobGetDataSize(b); + cli_dbgmsg("Decoded as '%*.*s'\n", (int)len, (int)len, + (const char *)blobGetData(b)); + memcpy(pout, blobGetData(b), len); + blobDestroy(b); + messageDestroy(m); + if (len > 0 && pout[len - 1] == '\n') + pout += len - 1; + else + pout += len; + } + if (out == NULL) + return NULL; + + *pout = '\0'; + + cli_dbgmsg("rfc2047 returns '%s'\n", out); + return out; } /* @@ -3169,252 +3147,252 @@ rfc2047(const char *in) static int rfc1341(message *m, const char *dir) { - char *arg, *id, *number, *total, *oldfilename; - const char *tmpdir; - int n; - char pdir[NAME_MAX + 1]; - unsigned char md5_val[16]; - char *md5_hex; - - id = (char *)messageFindArgument(m, "id"); - if(id == NULL) - return -1; - - tmpdir = cli_gettmpdir(); - - snprintf(pdir, sizeof(pdir) - 1, "%s"PATHSEP"clamav-partial", tmpdir); - - if((mkdir(pdir, S_IRWXU) < 0) && (errno != EEXIST)) { - cli_errmsg("Can't create the directory '%s'\n", pdir); - free(id); - return -1; - } else if(errno == EEXIST) { - STATBUF statb; - - if(CLAMSTAT(pdir, &statb) < 0) { - char err[128]; - cli_errmsg("Partial directory %s: %s\n", pdir, - cli_strerror(errno, err, sizeof(err))); - free(id); - return -1; - } - if(statb.st_mode & 077) - cli_warnmsg("Insecure partial directory %s (mode 0%o)\n", - pdir, -#ifdef ACCESSPERMS - (int)(statb.st_mode&ACCESSPERMS) + char *arg, *id, *number, *total, *oldfilename; + const char *tmpdir; + int n; + char pdir[NAME_MAX + 1]; + unsigned char md5_val[16]; + char *md5_hex; + + id = (char *)messageFindArgument(m, "id"); + if (id == NULL) + return -1; + + tmpdir = cli_gettmpdir(); + + snprintf(pdir, sizeof(pdir) - 1, "%s" PATHSEP "clamav-partial", tmpdir); + + if ((mkdir(pdir, S_IRWXU) < 0) && (errno != EEXIST)) { + cli_errmsg("Can't create the directory '%s'\n", pdir); + free(id); + return -1; + } else if (errno == EEXIST) { + STATBUF statb; + + if (CLAMSTAT(pdir, &statb) < 0) { + char err[128]; + cli_errmsg("Partial directory %s: %s\n", pdir, + cli_strerror(errno, err, sizeof(err))); + free(id); + return -1; + } + if (statb.st_mode & 077) + cli_warnmsg("Insecure partial directory %s (mode 0%o)\n", + pdir, +#ifdef ACCESSPERMS + (int)(statb.st_mode & ACCESSPERMS) #else - (int)(statb.st_mode & 0777) + (int)(statb.st_mode & 0777) #endif - ); - } - - number = (char *)messageFindArgument(m, "number"); - if(number == NULL) { - free(id); - return -1; - } - - oldfilename = messageGetFilename(m); - - arg = cli_malloc(10 + strlen(id) + strlen(number)); - if(arg) { - sprintf(arg, "filename=%s%s", id, number); - messageAddArgument(m, arg); - free(arg); - } - - if(oldfilename) { - cli_dbgmsg("Must reset to %s\n", oldfilename); - free(oldfilename); - } - - n = atoi(number); + ); + } + + number = (char *)messageFindArgument(m, "number"); + if (number == NULL) { + free(id); + return -1; + } + + oldfilename = messageGetFilename(m); + + arg = cli_malloc(10 + strlen(id) + strlen(number)); + if (arg) { + sprintf(arg, "filename=%s%s", id, number); + messageAddArgument(m, arg); + free(arg); + } + + if (oldfilename) { + cli_dbgmsg("Must reset to %s\n", oldfilename); + free(oldfilename); + } + + n = atoi(number); cl_hash_data("md5", id, strlen(id), md5_val, NULL); - md5_hex = cli_str2hex((const char*)md5_val, 16); - - if(!md5_hex) { - free(id); - free(number); - return CL_EMEM; - } - - if(messageSavePartial(m, pdir, md5_hex, n) < 0) { - free(md5_hex); - free(id); - free(number); - return -1; - } - - total = (char *)messageFindArgument(m, "total"); - cli_dbgmsg("rfc1341: %s, %s of %s\n", id, number, (total) ? total : "?"); - if(total) { - int t = atoi(total); - DIR *dd = NULL; - - free(total); - /* + md5_hex = cli_str2hex((const char *)md5_val, 16); + + if (!md5_hex) { + free(id); + free(number); + return CL_EMEM; + } + + if (messageSavePartial(m, pdir, md5_hex, n) < 0) { + free(md5_hex); + free(id); + free(number); + return -1; + } + + total = (char *)messageFindArgument(m, "total"); + cli_dbgmsg("rfc1341: %s, %s of %s\n", id, number, (total) ? total : "?"); + if (total) { + int t = atoi(total); + DIR *dd = NULL; + + free(total); + /* * If it's the last one - reassemble it * FIXME: this assumes that we receive the parts in order */ - if((n == t) && ((dd = opendir(pdir)) != NULL)) { - FILE *fout; - char outname[NAME_MAX + 1]; - time_t now; - - sanitiseName(id); - - snprintf(outname, sizeof(outname) - 1, "%s"PATHSEP"%s", dir, id); - - cli_dbgmsg("outname: %s\n", outname); - - fout = fopen(outname, "wb"); - if(fout == NULL) { - cli_errmsg("Can't open '%s' for writing", outname); - free(id); - free(number); - free(md5_hex); - closedir(dd); - return -1; - } - - time(&now); - for(n = 1; n <= t; n++) { - char filename[NAME_MAX + 1]; - struct dirent *dent; + if ((n == t) && ((dd = opendir(pdir)) != NULL)) { + FILE *fout; + char outname[NAME_MAX + 1]; + time_t now; + + sanitiseName(id); + + snprintf(outname, sizeof(outname) - 1, "%s" PATHSEP "%s", dir, id); + + cli_dbgmsg("outname: %s\n", outname); + + fout = fopen(outname, "wb"); + if (fout == NULL) { + cli_errmsg("Can't open '%s' for writing", outname); + free(id); + free(number); + free(md5_hex); + closedir(dd); + return -1; + } + + time(&now); + for (n = 1; n <= t; n++) { + char filename[NAME_MAX + 1]; + struct dirent *dent; #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2) - union { - struct dirent d; - char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; - } result; + union { + struct dirent d; + char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; + } result; #endif - snprintf(filename, sizeof(filename), "_%s-%u", md5_hex, n); + snprintf(filename, sizeof(filename), "_%s-%u", md5_hex, n); #ifdef HAVE_READDIR_R_3 - while((readdir_r(dd, &result.d, &dent) == 0) && dent) { + while ((readdir_r(dd, &result.d, &dent) == 0) && dent) { #elif defined(HAVE_READDIR_R_2) - while((dent = (struct dirent *)readdir_r(dd, &result.d))) { -#else /*!HAVE_READDIR_R*/ - while((dent = readdir(dd))) { + while ((dent = (struct dirent *)readdir_r(dd, &result.d))) { +#else /*!HAVE_READDIR_R*/ + while ((dent = readdir(dd))) { #endif - FILE *fin; - char buffer[BUFSIZ], fullname[NAME_MAX + 1]; - int nblanks; - STATBUF statb; - const char *dentry_idpart; + FILE *fin; + char buffer[BUFSIZ], fullname[NAME_MAX + 1]; + int nblanks; + STATBUF statb; + const char *dentry_idpart; int test_fd; - if(dent->d_ino == 0) - continue; + if (dent->d_ino == 0) + continue; - if(!strcmp(".",dent->d_name) || - !strcmp("..", dent->d_name)) - continue; - snprintf(fullname, sizeof(fullname) - 1, - "%s"PATHSEP"%s", pdir, dent->d_name); - dentry_idpart = strchr(dent->d_name, '_'); + if (!strcmp(".", dent->d_name) || + !strcmp("..", dent->d_name)) + continue; + snprintf(fullname, sizeof(fullname) - 1, + "%s" PATHSEP "%s", pdir, dent->d_name); + dentry_idpart = strchr(dent->d_name, '_'); - if(!dentry_idpart || - strcmp(filename, dentry_idpart) != 0) { - if(!m->ctx->engine->keeptmp) - continue; + if (!dentry_idpart || + strcmp(filename, dentry_idpart) != 0) { + if (!m->ctx->engine->keeptmp) + continue; if ((test_fd = open(fullname, O_RDONLY)) < 0) continue; - if(FSTAT(test_fd, &statb) < 0) { + if (FSTAT(test_fd, &statb) < 0) { close(test_fd); - continue; + continue; } - if(now - statb.st_mtime > (time_t)(7 * 24 * 3600)) { - if (cli_unlink(fullname)) { - cli_unlink(outname); - fclose(fout); - free(md5_hex); - free(id); - free(number); - closedir(dd); + if (now - statb.st_mtime > (time_t)(7 * 24 * 3600)) { + if (cli_unlink(fullname)) { + cli_unlink(outname); + fclose(fout); + free(md5_hex); + free(id); + free(number); + closedir(dd); close(test_fd); - return -1; - } - } + return -1; + } + } close(test_fd); - continue; - } - - fin = fopen(fullname, "rb"); - if(fin == NULL) { - cli_errmsg("Can't open '%s' for reading", fullname); - fclose(fout); - cli_unlink(outname); - free(md5_hex); - free(id); - free(number); - closedir(dd); - return -1; - } - nblanks = 0; - while(fgets(buffer, sizeof(buffer) - 1, fin) != NULL) - /* + continue; + } + + fin = fopen(fullname, "rb"); + if (fin == NULL) { + cli_errmsg("Can't open '%s' for reading", fullname); + fclose(fout); + cli_unlink(outname); + free(md5_hex); + free(id); + free(number); + closedir(dd); + return -1; + } + nblanks = 0; + while (fgets(buffer, sizeof(buffer) - 1, fin) != NULL) + /* * Ensure that trailing newlines * aren't copied */ - if(buffer[0] == '\n') - nblanks++; - else { - if(nblanks) - do { - if (putc('\n', fout)==EOF) break; - } while(--nblanks > 0); - if (nblanks || fputs(buffer, fout)==EOF) { - fclose(fin); - fclose(fout); - cli_unlink(outname); - free(md5_hex); - free(id); - free(number); - closedir(dd); - return -1; - } - } - fclose(fin); - - /* don't unlink if leave temps */ - if(!m->ctx->engine->keeptmp) { - if(cli_unlink(fullname)) { - fclose(fout); - cli_unlink(outname); - free(md5_hex); - free(id); - free(number); - closedir(dd); - return -1; - } - } - break; - } - rewinddir(dd); - } - closedir(dd); - fclose(fout); - } - } - free(number); - free(id); - free(md5_hex); - - return 0; + if (buffer[0] == '\n') + nblanks++; + else { + if (nblanks) + do { + if (putc('\n', fout) == EOF) break; + } while (--nblanks > 0); + if (nblanks || fputs(buffer, fout) == EOF) { + fclose(fin); + fclose(fout); + cli_unlink(outname); + free(md5_hex); + free(id); + free(number); + closedir(dd); + return -1; + } + } + fclose(fin); + + /* don't unlink if leave temps */ + if (!m->ctx->engine->keeptmp) { + if (cli_unlink(fullname)) { + fclose(fout); + cli_unlink(outname); + free(md5_hex); + free(id); + free(number); + closedir(dd); + return -1; + } + } + break; + } + rewinddir(dd); + } + closedir(dd); + fclose(fout); + } + } + free(number); + free(id); + free(md5_hex); + + return 0; } static void hrefs_done(blob *b, tag_arguments_t *hrefs) { - if(b) - blobDestroy(b); - html_tag_arg_free(hrefs); + if (b) + blobDestroy(b); + html_tag_arg_free(hrefs); } /* extract URLs from static text */ @@ -3422,28 +3400,28 @@ static void extract_text_urls(const unsigned char *mem, size_t len, tag_argument { char url[1024]; size_t off; - for (off=0;off + 10 < len;off++) { - /* check whether this is the start of a URL */ - int32_t proto = cli_readint32(mem + off); - /* convert to lowercase */ - proto |= 0x20202020; - /* 'http:', 'https:', or 'ftp:' in little-endian */ - if ((proto == 0x70747468 && - (mem[off+4] == ':' || (mem[off+5] == 's' && mem[off+6] == ':'))) - || proto == 0x3a707466) { - size_t url_len; - for (url_len=4; off + url_len < len && url_len < (sizeof(url)-1); url_len++) { - unsigned char c = mem[off + url_len]; - /* smart compilers will compile this if into + for (off = 0; off + 10 < len; off++) { + /* check whether this is the start of a URL */ + int32_t proto = cli_readint32(mem + off); + /* convert to lowercase */ + proto |= 0x20202020; + /* 'http:', 'https:', or 'ftp:' in little-endian */ + if ((proto == 0x70747468 && + (mem[off + 4] == ':' || (mem[off + 5] == 's' && mem[off + 6] == ':'))) || + proto == 0x3a707466) { + size_t url_len; + for (url_len = 4; off + url_len < len && url_len < (sizeof(url) - 1); url_len++) { + unsigned char c = mem[off + url_len]; + /* smart compilers will compile this if into * a single bt + jb instruction */ - if (c == ' ' || c == '\n' || c == '\t') - break; - } - memcpy(url, mem + off, url_len); - url[url_len] = '\0'; - html_tag_arg_add(hrefs, "href", url); - off += url_len; - } + if (c == ' ' || c == '\n' || c == '\t') + break; + } + memcpy(url, mem + off, url_len); + url[url_len] = '\0'; + html_tag_arg_add(hrefs, "href", url); + off += url_len; + } } } @@ -3455,44 +3433,44 @@ static void extract_text_urls(const unsigned char *mem, size_t len, tag_argument static blob * getHrefs(message *m, tag_arguments_t *hrefs) { - unsigned char *mem; - blob *b = messageToBlob(m, 0); - size_t len; - - if(b == NULL) - return NULL; - - len = blobGetDataSize(b); - - if(len == 0) { - blobDestroy(b); - return NULL; - } - - /* TODO: make this size customisable */ - if(len > 100*1024) { - cli_dbgmsg("Viruses pointed to by URLs not scanned in large message\n"); - blobDestroy(b); - return NULL; - } - - hrefs->count = 0; - hrefs->tag = hrefs->value = NULL; - hrefs->contents = NULL; - - cli_dbgmsg("getHrefs: calling html_normalise_mem\n"); - mem = blobGetData(b); - if(!html_normalise_mem(mem, (off_t)len, NULL, hrefs,m->ctx->dconf)) { - blobDestroy(b); - return NULL; - } - cli_dbgmsg("getHrefs: html_normalise_mem returned\n"); - if (!hrefs->count && hrefs->scanContents) { - extract_text_urls(mem, len, hrefs); - } - - /* TODO: Do we need to call remove_html_comments? */ - return b; + unsigned char *mem; + blob *b = messageToBlob(m, 0); + size_t len; + + if (b == NULL) + return NULL; + + len = blobGetDataSize(b); + + if (len == 0) { + blobDestroy(b); + return NULL; + } + + /* TODO: make this size customisable */ + if (len > 100 * 1024) { + cli_dbgmsg("Viruses pointed to by URLs not scanned in large message\n"); + blobDestroy(b); + return NULL; + } + + hrefs->count = 0; + hrefs->tag = hrefs->value = NULL; + hrefs->contents = NULL; + + cli_dbgmsg("getHrefs: calling html_normalise_mem\n"); + mem = blobGetData(b); + if (!html_normalise_mem(mem, (off_t)len, NULL, hrefs, m->ctx->dconf)) { + blobDestroy(b); + return NULL; + } + cli_dbgmsg("getHrefs: html_normalise_mem returned\n"); + if (!hrefs->count && hrefs->scanContents) { + extract_text_urls(mem, len, hrefs); + } + + /* TODO: Do we need to call remove_html_comments? */ + return b; } /* @@ -3502,84 +3480,84 @@ getHrefs(message *m, tag_arguments_t *hrefs) static void checkURLs(message *mainMessage, mbox_ctx *mctx, mbox_status *rc, int is_html) { - blob *b; - tag_arguments_t hrefs; + blob *b; + tag_arguments_t hrefs; UNUSEDPARAM(is_html); - if(*rc == VIRUS) - return; + if (*rc == VIRUS) + return; - hrefs.scanContents = mctx->ctx->engine->dboptions&CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); + hrefs.scanContents = mctx->ctx->engine->dboptions & CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); - if(!hrefs.scanContents) - /* + if (!hrefs.scanContents) + /* * Don't waste time extracting hrefs (parsing html), nobody * will need it */ - return; + return; - hrefs.count = 0; - hrefs.tag = hrefs.value = NULL; - hrefs.contents = NULL; + hrefs.count = 0; + hrefs.tag = hrefs.value = NULL; + hrefs.contents = NULL; - b = getHrefs(mainMessage, &hrefs); - if(b) { - if(hrefs.scanContents) { - if(phishingScan(mctx->ctx, &hrefs) == CL_VIRUS) { - /* + b = getHrefs(mainMessage, &hrefs); + if (b) { + if (hrefs.scanContents) { + if (phishingScan(mctx->ctx, &hrefs) == CL_VIRUS) { + /* * FIXME: message objects' contents are * encapsulated so we should not access * the members directly */ - mainMessage->isInfected = TRUE; - *rc = VIRUS; - cli_dbgmsg("PH:Phishing found\n"); - } - } - } - hrefs_done(b,&hrefs); + mainMessage->isInfected = TRUE; + *rc = VIRUS; + cli_dbgmsg("PH:Phishing found\n"); + } + } + } + hrefs_done(b, &hrefs); } #ifdef HAVE_BACKTRACE static void sigsegv(int sig) { - signal(SIGSEGV, SIG_DFL); - print_trace(1); - exit(SIGSEGV); + signal(SIGSEGV, SIG_DFL); + print_trace(1); + exit(SIGSEGV); } static void print_trace(int use_syslog) { - void *array[10]; - size_t size; - char **strings; - size_t i; - pid_t pid = getpid(); + void *array[10]; + size_t size; + char **strings; + size_t i; + pid_t pid = getpid(); - cli_errmsg("Segmentation fault, attempting to print backtrace\n"); + cli_errmsg("Segmentation fault, attempting to print backtrace\n"); - size = backtrace(array, 10); - strings = backtrace_symbols(array, size); + size = backtrace(array, 10); + strings = backtrace_symbols(array, size); - cli_errmsg("Backtrace of pid %d:\n", pid); - if(use_syslog) - syslog(LOG_ERR, "Backtrace of pid %d:", pid); + cli_errmsg("Backtrace of pid %d:\n", pid); + if (use_syslog) + syslog(LOG_ERR, "Backtrace of pid %d:", pid); - for(i = 0; i < size; i++) { - cli_errmsg("%s\n", strings[i]); - if(use_syslog) - syslog(LOG_ERR, "bt[%llu]: %s", (unsigned long long)i, strings[i]); - } + for (i = 0; i < size; i++) { + cli_errmsg("%s\n", strings[i]); + if (use_syslog) + syslog(LOG_ERR, "bt[%llu]: %s", (unsigned long long)i, strings[i]); + } -#ifdef SAVE_TMP - cli_errmsg("The errant mail file has been saved\n"); +#ifdef SAVE_TMP + cli_errmsg("The errant mail file has been saved\n"); #endif - /* #else TODO: dump the current email */ + /* #else TODO: dump the current email */ - free(strings); + free(strings); } #endif @@ -3587,21 +3565,21 @@ print_trace(int use_syslog) static bool usefulHeader(int commandNumber, const char *cmd) { - switch(commandNumber) { - case CONTENT_TRANSFER_ENCODING: - case CONTENT_DISPOSITION: - case CONTENT_TYPE: - return TRUE; - default: - if(strcasecmp(cmd, "From") == 0) - return TRUE; - if(strcasecmp(cmd, "Received") == 0) - return TRUE; - if(strcasecmp(cmd, "De") == 0) - return TRUE; - } - - return FALSE; + switch (commandNumber) { + case CONTENT_TRANSFER_ENCODING: + case CONTENT_DISPOSITION: + case CONTENT_TYPE: + return TRUE; + default: + if (strcasecmp(cmd, "From") == 0) + return TRUE; + if (strcasecmp(cmd, "Received") == 0) + return TRUE; + if (strcasecmp(cmd, "De") == 0) + return TRUE; + } + + return FALSE; } /* @@ -3616,53 +3594,53 @@ getline_from_mbox(char *buffer, size_t buffer_len, fmap_t *map, size_t *at) size_t input_len = MIN(map->len - *at, buffer_len + 1); src = cursrc = fmap_need_off_once(map, *at, input_len); -/* we check for eof from the result of GETC() + /* we check for eof from the result of GETC() * if(feof(fin)) return NULL;*/ - if(!src) { - cli_dbgmsg("getline_from_mbox: fmap need failed\n"); - return NULL; + if (!src) { + cli_dbgmsg("getline_from_mbox: fmap need failed\n"); + return NULL; } - if((buffer_len == 0) || (buffer == NULL)) { - cli_errmsg("Invalid call to getline_from_mbox(). Refer to https://www.clamav.net/documents/installing-clamav\n"); - return NULL; + if ((buffer_len == 0) || (buffer == NULL)) { + cli_errmsg("Invalid call to getline_from_mbox(). Refer to https://www.clamav.net/documents/installing-clamav\n"); + return NULL; } curbuf = buffer; - for(i=0; iFrom ", 6) == 0) && !isalnum(line[6])) return FALSE;*/ - len = strlen(line); - if((len < 6) || (len >= 72)) - return FALSE; + len = strlen(line); + if ((len < 6) || (len >= 72)) + return FALSE; - if((memcmp(line, "From ", 5) == 0) || - (memcmp(line, ">From ", 6) == 0)) { - int numSpaces = 0, numDigits = 0; - - line += 4; - - do - if(*line == ' ') - numSpaces++; - else if(isdigit((*line) & 0xFF)) - numDigits++; - while(*++line != '\0'); - - if(numSpaces < 6) - return FALSE; - if(numDigits < 11) - return FALSE; - return TRUE; - } - return (bool)(cli_filetype((const unsigned char *)line, len, mctx->ctx->engine) == CL_TYPE_MAIL); + if ((memcmp(line, "From ", 5) == 0) || + (memcmp(line, ">From ", 6) == 0)) { + int numSpaces = 0, numDigits = 0; + + line += 4; + + do + if (*line == ' ') + numSpaces++; + else if (isdigit((*line) & 0xFF)) + numDigits++; + while (*++line != '\0'); + + if (numSpaces < 6) + return FALSE; + if (numDigits < 11) + return FALSE; + return TRUE; + } + return (bool)(cli_filetype((const unsigned char *)line, len, mctx->ctx->engine) == CL_TYPE_MAIL); } /* @@ -3720,25 +3698,25 @@ isBounceStart(mbox_ctx *mctx, const char *line) static bool exportBinhexMessage(mbox_ctx *mctx, message *m) { - bool infected = FALSE; - fileblob *fb; + bool infected = FALSE; + fileblob *fb; - if(messageGetEncoding(m) == NOENCODING) - messageSetEncoding(m, "x-binhex"); + if (messageGetEncoding(m) == NOENCODING) + messageSetEncoding(m, "x-binhex"); - fb = messageToFileblob(m, mctx->dir, 0); + fb = messageToFileblob(m, mctx->dir, 0); - if(fb) { - cli_dbgmsg("Binhex file decoded to %s\n", - fileblobGetFilename(fb)); + if (fb) { + cli_dbgmsg("Binhex file decoded to %s\n", + fileblobGetFilename(fb)); - if(fileblobScanAndDestroy(fb) == CL_VIRUS) - infected = TRUE; - mctx->files++; - } else - cli_errmsg("Couldn't decode binhex file to %s\n", mctx->dir); + if (fileblobScanAndDestroy(fb) == CL_VIRUS) + infected = TRUE; + mctx->files++; + } else + cli_errmsg("Couldn't decode binhex file to %s\n", mctx->dir); - return infected; + return infected; } /* @@ -3747,11 +3725,11 @@ exportBinhexMessage(mbox_ctx *mctx, message *m) static int exportBounceMessage(mbox_ctx *mctx, text *start) { - int rc = CL_CLEAN; - text *t; - fileblob *fb; + int rc = CL_CLEAN; + text *t; + fileblob *fb; - /* + /* * Attempt to save the original (unbounced) * message - clamscan will find that in the * directory and call us again (with any luck) @@ -3770,80 +3748,80 @@ exportBounceMessage(mbox_ctx *mctx, text *start) * must remain otherwise non bounce messages * won't be scanned */ - for(t = start; t; t = t->t_next) { - const char *txt = lineGetData(t->t_line); - char cmd[RFC2821LENGTH + 1]; - - if(txt == NULL) - continue; - if(cli_strtokbuf(txt, 0, ":", cmd) == NULL) - continue; - - switch(tableFind(mctx->rfc821Table, cmd)) { - case CONTENT_TRANSFER_ENCODING: - if((strstr(txt, "7bit") == NULL) && - (strstr(txt, "8bit") == NULL)) - break; - continue; - case CONTENT_DISPOSITION: - break; - case CONTENT_TYPE: - if(strstr(txt, "text/plain") != NULL) - t = NULL; - break; - default: - if(strcasecmp(cmd, "From") == 0) - start = t; - else if(strcasecmp(cmd, "Received") == 0) - start = t; - continue; - } - break; - } - if(t && ((fb = fileblobCreate()) != NULL)) { - cli_dbgmsg("Found a bounce message\n"); - fileblobSetFilename(fb, mctx->dir, "bounce"); - fileblobSetCTX(fb, mctx->ctx); - if(textToFileblob(start, fb, 1) == NULL) { - cli_dbgmsg("Nothing new to save in the bounce message\n"); - fileblobDestroy(fb); - } else - rc = fileblobScanAndDestroy(fb); - mctx->files++; - } else - cli_dbgmsg("Not found a bounce message\n"); - - return rc; + for (t = start; t; t = t->t_next) { + const char *txt = lineGetData(t->t_line); + char cmd[RFC2821LENGTH + 1]; + + if (txt == NULL) + continue; + if (cli_strtokbuf(txt, 0, ":", cmd) == NULL) + continue; + + switch (tableFind(mctx->rfc821Table, cmd)) { + case CONTENT_TRANSFER_ENCODING: + if ((strstr(txt, "7bit") == NULL) && + (strstr(txt, "8bit") == NULL)) + break; + continue; + case CONTENT_DISPOSITION: + break; + case CONTENT_TYPE: + if (strstr(txt, "text/plain") != NULL) + t = NULL; + break; + default: + if (strcasecmp(cmd, "From") == 0) + start = t; + else if (strcasecmp(cmd, "Received") == 0) + start = t; + continue; + } + break; + } + if (t && ((fb = fileblobCreate()) != NULL)) { + cli_dbgmsg("Found a bounce message\n"); + fileblobSetFilename(fb, mctx->dir, "bounce"); + fileblobSetCTX(fb, mctx->ctx); + if (textToFileblob(start, fb, 1) == NULL) { + cli_dbgmsg("Nothing new to save in the bounce message\n"); + fileblobDestroy(fb); + } else + rc = fileblobScanAndDestroy(fb); + mctx->files++; + } else + cli_dbgmsg("Not found a bounce message\n"); + + return rc; } /* * Get string representation of mimetype */ -static const char *getMimeTypeStr(mime_type mimetype) +static const char *getMimeTypeStr(mime_type mimetype) { - const struct tableinit *entry = mimeTypeStr; - - while (entry->key) { - if (mimetype == entry->value) - return entry->key; - entry++; - } - return "UNKNOWN"; + const struct tableinit *entry = mimeTypeStr; + + while (entry->key) { + if (mimetype == entry->value) + return entry->key; + entry++; + } + return "UNKNOWN"; } /* * Get string representation of encoding type */ -static const char *getEncTypeStr(encoding_type enctype) +static const char *getEncTypeStr(encoding_type enctype) { - const struct tableinit *entry = encTypeStr; - - while (entry->key) { - if (enctype == entry->value) - return entry->key; - entry++; - } - return "UNKNOWN"; + const struct tableinit *entry = encTypeStr; + + while (entry->key) { + if (enctype == entry->value) + return entry->key; + entry++; + } + return "UNKNOWN"; } /* @@ -3852,146 +3830,145 @@ static const char *getEncTypeStr(encoding_type enctype) static message * do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, mbox_ctx *mctx, message *messageIn, text **tptr, unsigned int recursion_level) { - bool addToText = FALSE; - const char *dtype; -#ifndef SAVE_TO_DISC - message *body; + bool addToText = FALSE; + const char *dtype; +#ifndef SAVE_TO_DISC + message *body; #endif - message *aMessage = messages[i]; - const int doPhishingScan = mctx->ctx->engine->dboptions&CL_DB_PHISHING_URLS && (DCONF_PHISHING&PHISHING_CONF_ENGINE); + message *aMessage = messages[i]; + const int doPhishingScan = mctx->ctx->engine->dboptions & CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE); #if HAVE_JSON - const char *mtype = NULL; - json_object *thisobj = NULL, *saveobj = mctx->wrkobj; - - if (mctx->wrkobj != NULL) { - json_object *multiobj = cli_jsonarray(mctx->wrkobj, "Multipart"); - if (multiobj == NULL) { - cli_errmsg("Cannot get multipart preclass array\n"); - *rc = -1; - return mainMessage; - } - - thisobj = messageGetJObj(aMessage); - if (thisobj == NULL) { - cli_dbgmsg("Cannot get message preclass object\n"); - *rc = -1; - return mainMessage; - } - if (cli_json_addowner(multiobj, thisobj, NULL, -1) != CL_SUCCESS) { - cli_errmsg("Cannot assign message preclass object to multipart preclass array\n"); - *rc = -1; - return mainMessage; - } - } + const char *mtype = NULL; + json_object *thisobj = NULL, *saveobj = mctx->wrkobj; + + if (mctx->wrkobj != NULL) { + json_object *multiobj = cli_jsonarray(mctx->wrkobj, "Multipart"); + if (multiobj == NULL) { + cli_errmsg("Cannot get multipart preclass array\n"); + *rc = -1; + return mainMessage; + } + + thisobj = messageGetJObj(aMessage); + if (thisobj == NULL) { + cli_dbgmsg("Cannot get message preclass object\n"); + *rc = -1; + return mainMessage; + } + if (cli_json_addowner(multiobj, thisobj, NULL, -1) != CL_SUCCESS) { + cli_errmsg("Cannot assign message preclass object to multipart preclass array\n"); + *rc = -1; + return mainMessage; + } + } #endif - if(aMessage == NULL) { + if (aMessage == NULL) { #if HAVE_JSON - if (thisobj != NULL) - cli_jsonstr(thisobj, "MimeType", "NULL"); + if (thisobj != NULL) + cli_jsonstr(thisobj, "MimeType", "NULL"); #endif - return mainMessage; - } + return mainMessage; + } - if(*rc != OK) - return mainMessage; + if (*rc != OK) + return mainMessage; - cli_dbgmsg("Mixed message part %d is of type %d\n", - i, messageGetMimeType(aMessage)); + cli_dbgmsg("Mixed message part %d is of type %d\n", + i, messageGetMimeType(aMessage)); #if HAVE_JSON - if (thisobj != NULL) { - cli_jsonstr(thisobj, "MimeType", getMimeTypeStr(messageGetMimeType(aMessage))); - cli_jsonstr(thisobj, "MimeSubtype", messageGetMimeSubtype(aMessage)); - cli_jsonstr(thisobj, "EncodingType", getEncTypeStr(messageGetEncoding(aMessage))); - cli_jsonstr(thisobj, "Disposition", messageGetDispositionType(aMessage)); - cli_jsonstr(thisobj, "Filename", messageHasFilename(aMessage) ? - messageGetFilename(aMessage): "(inline)"); - } + if (thisobj != NULL) { + cli_jsonstr(thisobj, "MimeType", getMimeTypeStr(messageGetMimeType(aMessage))); + cli_jsonstr(thisobj, "MimeSubtype", messageGetMimeSubtype(aMessage)); + cli_jsonstr(thisobj, "EncodingType", getEncTypeStr(messageGetEncoding(aMessage))); + cli_jsonstr(thisobj, "Disposition", messageGetDispositionType(aMessage)); + cli_jsonstr(thisobj, "Filename", messageHasFilename(aMessage) ? messageGetFilename(aMessage) : "(inline)"); + } #endif - switch(messageGetMimeType(aMessage)) { - case APPLICATION: - case AUDIO: - case IMAGE: - case VIDEO: - break; - case NOMIME: - cli_dbgmsg("No mime headers found in multipart part %d\n", i); - if(mainMessage) { - if(binhexBegin(aMessage)) { - cli_dbgmsg("Found binhex message in multipart/mixed mainMessage\n"); - - if(exportBinhexMessage(mctx, mainMessage)) - *rc = VIRUS; - } - if(mainMessage != messageIn) - messageDestroy(mainMessage); - mainMessage = NULL; - } else if(aMessage) { - if(binhexBegin(aMessage)) { - cli_dbgmsg("Found binhex message in multipart/mixed non mime part\n"); - if(exportBinhexMessage(mctx, aMessage)) - *rc = VIRUS; - assert(aMessage == messages[i]); - messageReset(messages[i]); - } - } - addToText = TRUE; - if(messageGetBody(aMessage) == NULL) - /* + switch (messageGetMimeType(aMessage)) { + case APPLICATION: + case AUDIO: + case IMAGE: + case VIDEO: + break; + case NOMIME: + cli_dbgmsg("No mime headers found in multipart part %d\n", i); + if (mainMessage) { + if (binhexBegin(aMessage)) { + cli_dbgmsg("Found binhex message in multipart/mixed mainMessage\n"); + + if (exportBinhexMessage(mctx, mainMessage)) + *rc = VIRUS; + } + if (mainMessage != messageIn) + messageDestroy(mainMessage); + mainMessage = NULL; + } else if (aMessage) { + if (binhexBegin(aMessage)) { + cli_dbgmsg("Found binhex message in multipart/mixed non mime part\n"); + if (exportBinhexMessage(mctx, aMessage)) + *rc = VIRUS; + assert(aMessage == messages[i]); + messageReset(messages[i]); + } + } + addToText = TRUE; + if (messageGetBody(aMessage) == NULL) + /* * No plain text version */ - cli_dbgmsg("No plain text alternative\n"); - break; - case TEXT: - dtype = messageGetDispositionType(aMessage); - cli_dbgmsg("Mixed message text part disposition \"%s\"\n", - dtype); - if(strcasecmp(dtype, "attachment") == 0) - break; - if((*dtype == '\0') || (strcasecmp(dtype, "inline") == 0)) { - const char *cptr; - - if(mainMessage && (mainMessage != messageIn)) - messageDestroy(mainMessage); - mainMessage = NULL; - cptr = messageGetMimeSubtype(aMessage); - cli_dbgmsg("Mime subtype \"%s\"\n", cptr); - if((tableFind(mctx->subtypeTable, cptr) == PLAIN) && - (messageGetEncoding(aMessage) == NOENCODING)) { - /* + cli_dbgmsg("No plain text alternative\n"); + break; + case TEXT: + dtype = messageGetDispositionType(aMessage); + cli_dbgmsg("Mixed message text part disposition \"%s\"\n", + dtype); + if (strcasecmp(dtype, "attachment") == 0) + break; + if ((*dtype == '\0') || (strcasecmp(dtype, "inline") == 0)) { + const char *cptr; + + if (mainMessage && (mainMessage != messageIn)) + messageDestroy(mainMessage); + mainMessage = NULL; + cptr = messageGetMimeSubtype(aMessage); + cli_dbgmsg("Mime subtype \"%s\"\n", cptr); + if ((tableFind(mctx->subtypeTable, cptr) == PLAIN) && + (messageGetEncoding(aMessage) == NOENCODING)) { + /* * Strictly speaking, a text/plain part * is not an attachment. We pretend it * is so that we can decode and scan it */ - if(!messageHasFilename(aMessage)) { - cli_dbgmsg("Adding part to main message\n"); - addToText = TRUE; - } else - cli_dbgmsg("Treating inline as attachment\n"); - } else { - const int is_html = (tableFind(mctx->subtypeTable, cptr) == HTML); - if(doPhishingScan) - checkURLs(aMessage, mctx, rc, is_html); - messageAddArgument(aMessage, - "filename=mixedtextportion"); - } - break; - } - cli_dbgmsg("Text type %s is not supported\n", dtype); - return mainMessage; - case MESSAGE: - /* Content-Type: message/rfc822 */ - cli_dbgmsg("Found message inside multipart (encoding type %d)\n", - messageGetEncoding(aMessage)); -#ifndef SCAN_UNENCODED_BOUNCES - switch(messageGetEncoding(aMessage)) { - case NOENCODING: - case EIGHTBIT: - case BINARY: - if(encodingLine(aMessage) == NULL) { - /* + if (!messageHasFilename(aMessage)) { + cli_dbgmsg("Adding part to main message\n"); + addToText = TRUE; + } else + cli_dbgmsg("Treating inline as attachment\n"); + } else { + const int is_html = (tableFind(mctx->subtypeTable, cptr) == HTML); + if (doPhishingScan) + checkURLs(aMessage, mctx, rc, is_html); + messageAddArgument(aMessage, + "filename=mixedtextportion"); + } + break; + } + cli_dbgmsg("Text type %s is not supported\n", dtype); + return mainMessage; + case MESSAGE: + /* Content-Type: message/rfc822 */ + cli_dbgmsg("Found message inside multipart (encoding type %d)\n", + messageGetEncoding(aMessage)); +#ifndef SCAN_UNENCODED_BOUNCES + switch (messageGetEncoding(aMessage)) { + case NOENCODING: + case EIGHTBIT: + case BINARY: + if (encodingLine(aMessage) == NULL) { + /* * This means that the message * has no attachments * @@ -4001,39 +3978,39 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m * been set if the message * itself has been encoded */ - cli_dbgmsg("Unencoded multipart/message will not be scanned\n"); - assert(aMessage == messages[i]); - messageDestroy(messages[i]); - messages[i] = NULL; - return mainMessage; - } - /* FALLTHROUGH */ - default: - cli_dbgmsg("Encoded multipart/message will be scanned\n"); - } + cli_dbgmsg("Unencoded multipart/message will not be scanned\n"); + assert(aMessage == messages[i]); + messageDestroy(messages[i]); + messages[i] = NULL; + return mainMessage; + } + /* FALLTHROUGH */ + default: + cli_dbgmsg("Encoded multipart/message will be scanned\n"); + } #endif -#if 0 +#if 0 messageAddStrAtTop(aMessage, "Received: by clamd (message/rfc822)"); #endif -#ifdef SAVE_TO_DISC - /* +#ifdef SAVE_TO_DISC + /* * Save this embedded message * to a temporary file */ - if(saveTextPart(mctx, aMessage, 1) == CL_VIRUS) - *rc = VIRUS; - assert(aMessage == messages[i]); - messageDestroy(messages[i]); - messages[i] = NULL; + if (saveTextPart(mctx, aMessage, 1) == CL_VIRUS) + *rc = VIRUS; + assert(aMessage == messages[i]); + messageDestroy(messages[i]); + messages[i] = NULL; #else - /* + /* * Scan in memory, faster but is open to DoS attacks * when many nested levels are involved. */ - body = parseEmailHeaders(aMessage, mctx->rfc821Table); + body = parseEmailHeaders(aMessage, mctx->rfc821Table); - /* + /* * We've finished with the * original copy of the message, * so throw that away and @@ -4041,106 +4018,106 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m * message as a message. * This can save a lot of memory */ - assert(aMessage == messages[i]); - messageDestroy(messages[i]); - messages[i] = NULL; + assert(aMessage == messages[i]); + messageDestroy(messages[i]); + messages[i] = NULL; #if HAVE_JSON - mctx->wrkobj = thisobj; + mctx->wrkobj = thisobj; #endif - if(body) { - messageSetCTX(body, mctx->ctx); - *rc = parseEmailBody(body, NULL, mctx, recursion_level + 1); - if((*rc == OK) && messageContainsVirus(body)) - *rc = VIRUS; - messageDestroy(body); - } + if (body) { + messageSetCTX(body, mctx->ctx); + *rc = parseEmailBody(body, NULL, mctx, recursion_level + 1); + if ((*rc == OK) && messageContainsVirus(body)) + *rc = VIRUS; + messageDestroy(body); + } #if HAVE_JSON - mctx->wrkobj = saveobj; + mctx->wrkobj = saveobj; #endif #endif - return mainMessage; - case MULTIPART: - /* + return mainMessage; + case MULTIPART: + /* * It's a multi part within a multi part * Run the message parser on this bit, it won't * be an attachment */ - cli_dbgmsg("Found multipart inside multipart\n"); + cli_dbgmsg("Found multipart inside multipart\n"); #if HAVE_JSON - mctx->wrkobj = thisobj; + mctx->wrkobj = thisobj; #endif - if(aMessage) { - /* + if (aMessage) { + /* * The headers were parsed when reading in the * whole multipart section */ - *rc = parseEmailBody(aMessage, *tptr, mctx, recursion_level + 1); - cli_dbgmsg("Finished recursion, rc = %d\n", (int)*rc); - assert(aMessage == messages[i]); - messageDestroy(messages[i]); - messages[i] = NULL; - } else { - *rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); - if(mainMessage && (mainMessage != messageIn)) - messageDestroy(mainMessage); - mainMessage = NULL; - } + *rc = parseEmailBody(aMessage, *tptr, mctx, recursion_level + 1); + cli_dbgmsg("Finished recursion, rc = %d\n", (int)*rc); + assert(aMessage == messages[i]); + messageDestroy(messages[i]); + messages[i] = NULL; + } else { + *rc = parseEmailBody(NULL, NULL, mctx, recursion_level + 1); + if (mainMessage && (mainMessage != messageIn)) + messageDestroy(mainMessage); + mainMessage = NULL; + } #if HAVE_JSON - mctx->wrkobj = saveobj; + mctx->wrkobj = saveobj; #endif - return mainMessage; - default: - cli_dbgmsg("Only text and application attachments are fully supported, type = %d\n", - messageGetMimeType(aMessage)); - /* fall through - we may be able to salvage something */ - } - - if(*rc != VIRUS) { - fileblob *fb = messageToFileblob(aMessage, mctx->dir, 1); + return mainMessage; + default: + cli_dbgmsg("Only text and application attachments are fully supported, type = %d\n", + messageGetMimeType(aMessage)); + /* fall through - we may be able to salvage something */ + } + + if (*rc != VIRUS) { + fileblob *fb = messageToFileblob(aMessage, mctx->dir, 1); #if HAVE_JSON - json_object *arrobj; - int arrlen = 0; + json_object *arrobj; + int arrlen = 0; - if (thisobj != NULL) { - /* attempt to determine container size - prevents incorrect type reporting */ - if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) - arrlen = json_object_array_length(arrobj); - } + if (thisobj != NULL) { + /* attempt to determine container size - prevents incorrect type reporting */ + if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) + arrlen = json_object_array_length(arrobj); + } #endif - if(fb) { - /* aMessage doesn't always have a ctx set */ - fileblobSetCTX(fb, mctx->ctx); - if(fileblobScanAndDestroy(fb) == CL_VIRUS) - *rc = VIRUS; - if (!addToText) - mctx->files++; - } + if (fb) { + /* aMessage doesn't always have a ctx set */ + fileblobSetCTX(fb, mctx->ctx); + if (fileblobScanAndDestroy(fb) == CL_VIRUS) + *rc = VIRUS; + if (!addToText) + mctx->files++; + } #if HAVE_JSON - if (thisobj != NULL) { - json_object *entry = NULL; - const char *dtype = NULL; - - /* attempt to acquire container type */ - if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) - if (json_object_array_length(arrobj) > arrlen) - entry = json_object_array_get_idx(arrobj, arrlen); - if (entry) { - json_object_object_get_ex(entry, "FileType", &entry); - if (entry) - dtype = json_object_get_string(entry); - } - cli_jsonint(thisobj, "ContainedObjectsIndex", arrlen); - cli_jsonstr(thisobj, "ClamAVFileType", dtype ? dtype : "UNKNOWN"); - } + if (thisobj != NULL) { + json_object *entry = NULL; + const char *dtype = NULL; + + /* attempt to acquire container type */ + if (json_object_object_get_ex(mctx->ctx->wrkproperty, "ContainedObjects", &arrobj)) + if (json_object_array_length(arrobj) > arrlen) + entry = json_object_array_get_idx(arrobj, arrlen); + if (entry) { + json_object_object_get_ex(entry, "FileType", &entry); + if (entry) + dtype = json_object_get_string(entry); + } + cli_jsonint(thisobj, "ContainedObjectsIndex", arrlen); + cli_jsonstr(thisobj, "ClamAVFileType", dtype ? dtype : "UNKNOWN"); + } #endif - if(messageContainsVirus(aMessage)) - *rc = VIRUS; - } - messageDestroy(aMessage); - messages[i] = NULL; + if (messageContainsVirus(aMessage)) + *rc = VIRUS; + } + messageDestroy(aMessage); + messages[i] = NULL; - return mainMessage; + return mainMessage; } /* @@ -4149,13 +4126,13 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m static int count_quotes(const char *buf) { - int quotes = 0; + int quotes = 0; - while(*buf) - if(*buf++ == '\"') - quotes++; + while (*buf) + if (*buf++ == '\"') + quotes++; - return quotes; + return quotes; } /* @@ -4164,33 +4141,33 @@ count_quotes(const char *buf) static bool next_is_folded_header(const text *t) { - const text *next = t->t_next; - const char *data, *ptr; + const text *next = t->t_next; + const char *data, *ptr; - if(next == NULL) - return FALSE; + if (next == NULL) + return FALSE; - if(next->t_line == NULL) - return FALSE; + if (next->t_line == NULL) + return FALSE; - data = lineGetData(next->t_line); + data = lineGetData(next->t_line); - /* + /* * Section B.2 of RFC822 says TAB or SPACE means a continuation of the * previous entry. */ - if(isblank(data[0])) - return TRUE; + if (isblank(data[0])) + return TRUE; - if(strchr(data, '=') == NULL) - /* + if (strchr(data, '=') == NULL) + /* * Avoid false positives with * Content-Type: text/html; * Content-Transfer-Encoding: quoted-printable */ - return FALSE; + return FALSE; - /* + /* * Some are broken and don't fold headers lines * correctly as per section 2.2.3 of RFC2822. * Generally they miss the white space at @@ -4205,23 +4182,23 @@ next_is_folded_header(const text *t) * Since we're a virus checker not an RFC * verifier we need to handle these */ - data = lineGetData(t->t_line); - - ptr = strchr(data, '\0'); - - while(--ptr > data) - switch(*ptr) { - case ';': - return TRUE; - case '\n': - case ' ': - case '\r': - case '\t': - continue; /* white space at end of line */ - default: - return FALSE; - } - return FALSE; + data = lineGetData(t->t_line); + + ptr = strchr(data, '\0'); + + while (--ptr > data) + switch (*ptr) { + case ';': + return TRUE; + case '\n': + case ' ': + case '\r': + case '\t': + continue; /* white space at end of line */ + default: + return FALSE; + } + return FALSE; } /* @@ -4232,12 +4209,12 @@ next_is_folded_header(const text *t) static bool newline_in_header(const char *line) { - cli_dbgmsg("newline_in_header, check \"%s\"\n", line); + cli_dbgmsg("newline_in_header, check \"%s\"\n", line); - if(strncmp(line, "Message-Id: ", 12) == 0) - return TRUE; - if(strncmp(line, "Date: ", 6) == 0) - return TRUE; + if (strncmp(line, "Message-Id: ", 12) == 0) + return TRUE; + if (strncmp(line, "Date: ", 6) == 0) + return TRUE; - return FALSE; + return FALSE; }