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