diff options
author | Jeffrey Stedfast <jestedfa@microsoft.com> | 2020-04-24 11:01:01 -0400 |
---|---|---|
committer | Jeffrey Stedfast <jestedfa@microsoft.com> | 2020-04-24 11:01:01 -0400 |
commit | 53449a25fa46e6a0333d1919ee4f3778c1789d53 (patch) | |
tree | c6555d189deadefb0d84aec4c4f3d30a03c129f3 | |
parent | 9749a44bae4bacbb59c06d9a62e5e87cb8be3081 (diff) | |
download | gmime-53449a25fa46e6a0333d1919ee4f3778c1789d53.tar.gz |
Fixed the parser to handle MIME parts without headers -or- content of any kind
Added parser warnings for these cases.
-rw-r--r-- | examples/msgcheck.c | 46 | ||||
-rw-r--r-- | gmime/gmime-parser-options.h | 8 | ||||
-rw-r--r-- | gmime/gmime-parser.c | 382 |
3 files changed, 225 insertions, 211 deletions
diff --git a/examples/msgcheck.c b/examples/msgcheck.c index 5f00f95c..8b91e662 100644 --- a/examples/msgcheck.c +++ b/examples/msgcheck.c @@ -26,14 +26,6 @@ #include <stdlib.h> #include <gmime/gmime.h> - -typedef struct { - gint64 offset; - GMimeParserWarning errcode; - gchar *message; -} issue_log_elem_t; - - static const gchar * errcode2str(GMimeParserWarning errcode) { @@ -66,32 +58,36 @@ errcode2str(GMimeParserWarning errcode) return "conflicting header parameter"; case GMIME_CRIT_MULTIPART_WITHOUT_BOUNDARY: return "multipart without boundary"; + case GMIME_WARN_PART_WITHOUT_CONTENT: + return "MIME part without content encountered"; + case GMIME_CRIT_PART_WITHOUT_HEADERS_OR_CONTENT: + return "MIME part without headers or content encountered"; default: return "unknown"; } } +static int issues = 0; static void parser_issue (gint64 offset, GMimeParserWarning errcode, const gchar *item, gpointer user_data) { - GList **issues = (GList **) user_data; - issue_log_elem_t *new_issue; + char *message; - new_issue = g_new (issue_log_elem_t, 1U); - new_issue->offset = offset; - new_issue->errcode = errcode; if (item == NULL) { - new_issue->message = g_strdup (errcode2str (errcode)); + message = g_strdup (errcode2str (errcode)); } else { gchar *buf; buf = g_strdup (item); g_strstrip (buf); - new_issue->message = g_strdup_printf ("%s: '%s'", errcode2str (errcode), buf); + message = g_strdup_printf ("%s: '%s'", errcode2str (errcode), buf); g_free (buf); } - *issues = g_list_append (*issues, new_issue); + + g_printf ("offset %" G_GINT64_FORMAT ": [%u] %s\n", offset, errcode, message); + g_free (message); + issues++; } @@ -110,12 +106,11 @@ check_msg_file (const gchar *filename) GMimeParser *parser; GMimeParserOptions *options; GMimeMessage *message; - GList *issues = NULL; parser = g_mime_parser_new (); g_mime_parser_init_with_stream (parser, stream); options = g_mime_parser_options_new (); - g_mime_parser_options_set_warning_callback (options, parser_issue, &issues); + g_mime_parser_options_set_warning_callback (options, parser_issue, NULL); message = g_mime_parser_construct_message (parser, options); g_mime_parser_options_free (options); g_object_unref (parser); @@ -124,21 +119,10 @@ check_msg_file (const gchar *filename) g_object_unref (message); } - if (issues == NULL) { + if (issues == 0) { g_printf ("%s: message looks benign\n", filename); } else { - GList *this_issue; - - g_printf ("%s: message contains %u RFC violations:\n", filename, g_list_length (issues)); - for (this_issue = issues; this_issue != NULL; this_issue = this_issue->next) { - issue_log_elem_t *issue_data = (issue_log_elem_t *) this_issue->data; - - g_printf ("offset %" G_GINT64_FORMAT ": [%u] %s\n", - issue_data->offset, issue_data->errcode, issue_data->message); - g_free (issue_data->message); - g_free (issue_data); - } - g_list_free (issues); + g_printf ("%s: message contained %d RFC violations.\n", filename, issues); } } } diff --git a/gmime/gmime-parser-options.h b/gmime/gmime-parser-options.h index 06bf6c0e..2b8c57db 100644 --- a/gmime/gmime-parser-options.h +++ b/gmime/gmime-parser-options.h @@ -57,7 +57,9 @@ typedef enum { * @GMIME_CRIT_CONFLICTING_HEADER: Conflicting header. * @GMIME_CRIT_CONFLICTING_PARAMETER: Conflicting header parameter. * @GMIME_CRIT_MULTIPART_WITHOUT_BOUNDARY: A multipart lacks the required boundary parameter. - * @GMIME_CRIT_NESTING_OVERFLOW: The maximum MIME nesting level has been exceeded. + * @GMIME_CRIT_NESTING_OVERFLOW: The maximum MIME nesting level has been exceeded. This is very likely to be an attempt to exploit the MIME parser. + * @GMIME_WARN_PART_WITHOUT_CONTENT: A MIME part's headers were terminated by a boundary marker. + * @GMIME_CRIT_PART_WITHOUT_HEADERS_OR_CONTENT: A MIME part was encountered without any headers -or- content. This is very likely to be an attempt to exploit the MIME parser. * * Issues the @GMimeParser detects. Note that the `GMIME_CRIT_*` issues indicate that some parts of the @GMimeParser input may * be ignored or will be interpreted differently by other software products. @@ -77,7 +79,9 @@ typedef enum { GMIME_CRIT_MULTIPART_WITHOUT_BOUNDARY, GMIME_WARN_INVALID_PARAMETER, GMIME_WARN_INVALID_ADDRESS_LIST, - GMIME_CRIT_NESTING_OVERFLOW + GMIME_CRIT_NESTING_OVERFLOW, + GMIME_WARN_PART_WITHOUT_CONTENT, + GMIME_CRIT_PART_WITHOUT_HEADERS_OR_CONTENT, } GMimeParserWarning; /** diff --git a/gmime/gmime-parser.c b/gmime/gmime-parser.c index 159470f3..b8eb64e4 100644 --- a/gmime/gmime-parser.c +++ b/gmime/gmime-parser.c @@ -98,9 +98,9 @@ static void parser_init (GMimeParser *parser, GMimeStream *stream); static void parser_close (GMimeParser *parser); static GMimeObject *parser_construct_leaf_part (GMimeParser *parser, GMimeParserOptions *options, ContentType *content_type, - gboolean toplevel, BoundaryType *found, int depth); + gboolean toplevel, int depth); static GMimeObject *parser_construct_multipart (GMimeParser *parser, GMimeParserOptions *options, ContentType *content_type, - gboolean toplevel, BoundaryType *found, int depth); + gboolean toplevel, int depth); static GObjectClass *parent_class = NULL; @@ -122,6 +122,7 @@ typedef enum { GMIME_PARSER_STATE_HEADERS, GMIME_PARSER_STATE_HEADERS_END, GMIME_PARSER_STATE_CONTENT, + GMIME_PARSER_STATE_BOUNDARY, GMIME_PARSER_STATE_COMPLETE, } GMimeParserState; @@ -166,6 +167,7 @@ struct _GMimeParserPrivate { size_t headerleft; BoundaryStack *bounds; + BoundaryType boundary; GMimeOpenPGPState openpgp; short int state; @@ -382,6 +384,7 @@ parser_init (GMimeParser *parser, GMimeStream *stream) priv->header_offset = -1; priv->openpgp = GMIME_OPENPGP_NONE; + priv->boundary = BOUNDARY_NONE; priv->toplevel = FALSE; priv->seekable = offset != -1; @@ -824,6 +827,124 @@ parser_step_mmdf (GMimeParser *parser) return parser_step_marker (parser, MMDF_BOUNDARY, MMDF_BOUNDARY_LEN); } +#define possible_boundary(marker, mlen, start, len) \ + ((marker && len >= mlen && !strncmp (start, marker, mlen)) || \ + (len >= 2 && (start[0] == '-' && start[1] == '-'))) + +static gboolean +is_boundary (struct _GMimeParserPrivate *priv, const char *text, size_t len, const char *boundary, size_t boundary_len) +{ + const char *inptr = text + boundary_len; + const char *inend = text + len; + + if (boundary_len > len) + return FALSE; + + /* make sure that the text matches the boundary */ + if (strncmp (text, boundary, boundary_len) != 0) + return FALSE; + + if (priv->format == GMIME_FORMAT_MBOX) { + if (!strncmp (text, MBOX_BOUNDARY, MBOX_BOUNDARY_LEN)) + return TRUE; + } else if (priv->format == GMIME_FORMAT_MMDF) { + if (!strncmp (text, MMDF_BOUNDARY, MMDF_BOUNDARY_LEN)) + return TRUE; + } + + /* the boundary may be optionally followed by linear whitespace */ + while (inptr < inend) { + if (!is_lwsp (*inptr)) + return FALSE; + + inptr++; + } + + return TRUE; +} + +static BoundaryType +check_boundary (struct _GMimeParserPrivate *priv, const char *start, size_t len) +{ + gint64 offset = parser_offset (priv, start); + BoundaryStack *bounds; + const char *marker; + size_t mlen; + guint i; + + switch (priv->format) { + case GMIME_FORMAT_MBOX: marker = MBOX_BOUNDARY; mlen = MBOX_BOUNDARY_LEN; break; + case GMIME_FORMAT_MMDF: marker = MMDF_BOUNDARY; mlen = MMDF_BOUNDARY_LEN; break; + default: marker = NULL; mlen = 0; break; + } + + if (len > 0 && start[len - 1] == '\r') + len--; + + if (!possible_boundary (marker, mlen, start, len)) + return BOUNDARY_NONE; + + d(printf ("checking boundary '%.*s'\n", len, start)); + + bounds = priv->bounds; + while (bounds != NULL && (priv->content_end > 0 ? bounds->parent != NULL : TRUE)) { + if (is_boundary (priv, start, len, bounds->boundary, bounds->boundarylenfinal)) { + d(printf ("found end boundary\n")); + return bounds == priv->bounds ? BOUNDARY_IMMEDIATE_END : BOUNDARY_PARENT_END; + } + + if (is_boundary (priv, start, len, bounds->boundary, bounds->boundarylen)) { + d(printf ("found boundary\n")); + return bounds == priv->bounds ? BOUNDARY_IMMEDIATE : BOUNDARY_PARENT; + } + + bounds = bounds->parent; + } + + if (priv->content_end > 0 && bounds != NULL) { + /* now it is time to check the mbox From-marker for the Content-Length case */ + if (offset >= priv->content_end && is_boundary (priv, start, len, bounds->boundary, bounds->boundarylenfinal)) { + d(printf ("found end of content\n")); + return BOUNDARY_IMMEDIATE_END; + } + } + + d(printf ("'%.*s' not a boundary\n", len, start)); + + if (!strncmp (start, "--", 2)) { + start += 2; + len -= 2; + + /* check for OpenPGP markers... */ + for (i = 0; i < G_N_ELEMENTS (g_mime_openpgp_markers); i++) { + const char *pgp_marker = g_mime_openpgp_markers[i].marker + 2; + GMimeOpenPGPState state = g_mime_openpgp_markers[i].before; + size_t n = g_mime_openpgp_markers[i].len - 2; + + if (len == n && priv->openpgp == state && !strncmp (pgp_marker, start, len)) + priv->openpgp = g_mime_openpgp_markers[i].after; + } + } + + return BOUNDARY_NONE; +} + +static gboolean +found_immediate_boundary (struct _GMimeParserPrivate *priv, gboolean end) +{ + BoundaryStack *s = priv->bounds; + size_t boundary_len = end ? s->boundarylenfinal : s->boundarylen; + register char *inptr = priv->inptr; + char *inend = priv->inend; + + /* Note: see optimization comment [1] */ + *inend = '\n'; + while (*inptr != '\n') + inptr++; + + return is_boundary (priv, priv->inptr, inptr - priv->inptr, s->boundary, boundary_len); +} + static inline size_t next_alloc_size (size_t n) { @@ -1131,6 +1252,20 @@ step_headers (GMimeParser *parser, struct _StepHeadersState *state, GMimeParserO } len = (inptr + 1) - start; + + /* check if we've encountered a parent boundary (malformed message) */ + if ((priv->boundary = check_boundary (priv, start, len)) != BOUNDARY_NONE) { + if (can_warn) { + if (priv->headers->len == 0) + _g_mime_parser_options_warn (options, priv->header_offset, GMIME_CRIT_PART_WITHOUT_HEADERS_OR_CONTENT, NULL); + else + _g_mime_parser_options_warn (options, priv->header_offset, GMIME_WARN_PART_WITHOUT_CONTENT, NULL); + } + priv->headers_end = parser_offset (priv, start); + priv->state = GMIME_PARSER_STATE_BOUNDARY; + priv->inptr = start; + return FALSE; + } if (!state->valid && priv->headers->len == 0) { if (len > 0 && priv->preheader == NULL) { @@ -1180,6 +1315,7 @@ parser_step_headers (GMimeParser *parser, GMimeParserOptions *options) parser_free_headers (priv); priv->headers_begin = parser_offset (priv, NULL); priv->header_offset = priv->headers_begin; + priv->boundary = BOUNDARY_NONE; parser_fill (parser, SCAN_HEAD/*, 0*/); @@ -1352,6 +1488,8 @@ parser_step (GMimeParser *parser, GMimeParserOptions *options) break; case GMIME_PARSER_STATE_CONTENT: break; + case GMIME_PARSER_STATE_BOUNDARY: + break; case GMIME_PARSER_STATE_COMPLETE: break; default: @@ -1362,123 +1500,6 @@ parser_step (GMimeParser *parser, GMimeParserOptions *options) return priv->state; } -#define possible_boundary(marker, mlen, start, len) \ - ((marker && len >= mlen && !strncmp (start, marker, mlen)) || \ - (len >= 2 && (start[0] == '-' && start[1] == '-'))) - -static gboolean -is_boundary (struct _GMimeParserPrivate *priv, const char *text, size_t len, const char *boundary, size_t boundary_len) -{ - const char *inptr = text + boundary_len; - const char *inend = text + len; - - if (boundary_len > len) - return FALSE; - - /* make sure that the text matches the boundary */ - if (strncmp (text, boundary, boundary_len) != 0) - return FALSE; - - if (priv->format == GMIME_FORMAT_MBOX) { - if (!strncmp (text, MBOX_BOUNDARY, MBOX_BOUNDARY_LEN)) - return TRUE; - } else if (priv->format == GMIME_FORMAT_MMDF) { - if (!strncmp (text, MMDF_BOUNDARY, MMDF_BOUNDARY_LEN)) - return TRUE; - } - - /* the boundary may be optionally followed by linear whitespace */ - while (inptr < inend) { - if (!is_lwsp (*inptr)) - return FALSE; - - inptr++; - } - - return TRUE; -} - -static BoundaryType -check_boundary (struct _GMimeParserPrivate *priv, const char *start, size_t len) -{ - gint64 offset = parser_offset (priv, start); - BoundaryStack *bounds; - const char *marker; - size_t mlen; - guint i; - - switch (priv->format) { - case GMIME_FORMAT_MBOX: marker = MBOX_BOUNDARY; mlen = MBOX_BOUNDARY_LEN; break; - case GMIME_FORMAT_MMDF: marker = MMDF_BOUNDARY; mlen = MMDF_BOUNDARY_LEN; break; - default: marker = NULL; mlen = 0; break; - } - - if (len > 0 && start[len - 1] == '\r') - len--; - - if (!possible_boundary (marker, mlen, start, len)) - return BOUNDARY_NONE; - - d(printf ("checking boundary '%.*s'\n", len, start)); - - bounds = priv->bounds; - while (bounds != NULL && (priv->content_end > 0 ? bounds->parent != NULL : TRUE)) { - if (is_boundary (priv, start, len, bounds->boundary, bounds->boundarylenfinal)) { - d(printf ("found end boundary\n")); - return bounds == priv->bounds ? BOUNDARY_IMMEDIATE_END : BOUNDARY_PARENT_END; - } - - if (is_boundary (priv, start, len, bounds->boundary, bounds->boundarylen)) { - d(printf ("found boundary\n")); - return bounds == priv->bounds ? BOUNDARY_IMMEDIATE : BOUNDARY_PARENT; - } - - bounds = bounds->parent; - } - - if (priv->content_end > 0 && bounds != NULL) { - /* now it is time to check the mbox From-marker for the Content-Length case */ - if (offset >= priv->content_end && is_boundary (priv, start, len, bounds->boundary, bounds->boundarylenfinal)) { - d(printf ("found end of content\n")); - return BOUNDARY_IMMEDIATE_END; - } - } - - d(printf ("'%.*s' not a boundary\n", len, start)); - - if (!strncmp (start, "--", 2)) { - start += 2; - len -= 2; - - /* check for OpenPGP markers... */ - for (i = 0; i < G_N_ELEMENTS (g_mime_openpgp_markers); i++) { - const char *pgp_marker = g_mime_openpgp_markers[i].marker + 2; - GMimeOpenPGPState state = g_mime_openpgp_markers[i].before; - size_t n = g_mime_openpgp_markers[i].len - 2; - - if (len == n && priv->openpgp == state && !strncmp (pgp_marker, start, len)) - priv->openpgp = g_mime_openpgp_markers[i].after; - } - } - - return BOUNDARY_NONE; -} - -static gboolean -found_immediate_boundary (struct _GMimeParserPrivate *priv, gboolean end) -{ - BoundaryStack *s = priv->bounds; - size_t boundary_len = end ? s->boundarylenfinal : s->boundarylen; - register char *inptr = priv->inptr; - char *inend = priv->inend; - - /* Note: see optimization comment [1] */ - *inend = '\n'; - while (*inptr != '\n') - inptr++; - - return is_boundary (priv, priv->inptr, inptr - priv->inptr, s->boundary, boundary_len); -} /* Optimization Notes: * @@ -1494,11 +1515,10 @@ found_immediate_boundary (struct _GMimeParserPrivate *priv, gboolean end) /* we add 2 for \r\n */ #define MAX_BOUNDARY_LEN(bounds) (bounds ? bounds->boundarylenmax + 2 : 0) -static BoundaryType +static void parser_scan_content (GMimeParser *parser, GMimeStream *content, gboolean *empty) { struct _GMimeParserPrivate *priv = parser->priv; - BoundaryType found = BOUNDARY_NONE; char *aligned, *start, *inend; register unsigned int *dword; gboolean midline = FALSE; @@ -1512,6 +1532,7 @@ parser_scan_content (GMimeParser *parser, GMimeStream *content, gboolean *empty) d(printf ("scan-content\n")); priv->openpgp = GMIME_OPENPGP_NONE; + priv->boundary = BOUNDARY_NONE; g_assert (priv->inptr <= priv->inend); @@ -1524,8 +1545,8 @@ parser_scan_content (GMimeParser *parser, GMimeStream *content, gboolean *empty) refill: nleft = priv->inend - inptr; if (parser_fill (parser, atleast) <= 0) { + priv->boundary = BOUNDARY_EOS; start = priv->inptr; - found = BOUNDARY_EOS; break; } @@ -1536,7 +1557,7 @@ parser_scan_content (GMimeParser *parser, GMimeStream *content, gboolean *empty) len = (size_t) (inend - inptr); if (midline && len == nleft) - found = BOUNDARY_EOS; + priv->boundary = BOUNDARY_EOS; midline = FALSE; @@ -1569,7 +1590,7 @@ parser_scan_content (GMimeParser *parser, GMimeStream *content, gboolean *empty) len = (size_t) (inptr - start); if (inptr < inend) { - if ((found = check_boundary (priv, start, len))) + if ((priv->boundary = check_boundary (priv, start, len)) != BOUNDARY_NONE) goto boundary; inptr++; @@ -1578,7 +1599,7 @@ parser_scan_content (GMimeParser *parser, GMimeStream *content, gboolean *empty) /* didn't find an end-of-line */ midline = TRUE; - if (!found) { + if (priv->boundary == BOUNDARY_NONE) { /* not enough to tell if we found a boundary */ priv->inptr = start; inptr = start; @@ -1586,7 +1607,7 @@ parser_scan_content (GMimeParser *parser, GMimeStream *content, gboolean *empty) } /* check for a boundary not ending in a \n (EOF) */ - if ((found = check_boundary (priv, start, len))) + if ((priv->boundary = check_boundary (priv, start, len)) != BOUNDARY_NONE) goto boundary; } @@ -1594,7 +1615,7 @@ parser_scan_content (GMimeParser *parser, GMimeStream *content, gboolean *empty) } priv->inptr = inptr; - } while (!found); + } while (priv->boundary == BOUNDARY_NONE); boundary: @@ -1604,19 +1625,17 @@ parser_scan_content (GMimeParser *parser, GMimeStream *content, gboolean *empty) pos = g_mime_stream_tell (content); *empty = pos == 0; - if (found != BOUNDARY_EOS && pos > 0) { + if (priv->boundary != BOUNDARY_EOS && pos > 0) { /* the last \r\n belongs to the boundary */ if (inptr[-1] == '\r') g_mime_stream_seek (content, -2, GMIME_STREAM_SEEK_CUR); else g_mime_stream_seek (content, -1, GMIME_STREAM_SEEK_CUR); } - - return found; } static void -parser_scan_mime_part_content (GMimeParser *parser, GMimePart *mime_part, BoundaryType *found) +parser_scan_mime_part_content (GMimeParser *parser, GMimePart *mime_part) { struct _GMimeParserPrivate *priv = parser->priv; GMimeContentType *content_type; @@ -1637,7 +1656,7 @@ parser_scan_mime_part_content (GMimeParser *parser, GMimePart *mime_part, Bounda start = 0; } - *found = parser_scan_content (parser, stream, &empty); + parser_scan_content (parser, stream, &empty); len = g_mime_stream_tell (stream); if (priv->persist_stream && priv->seekable) { @@ -1714,7 +1733,7 @@ check_repeated_header (GMimeParserOptions *options, GMimeObject *object, const H } static void -parser_scan_message_part (GMimeParser *parser, GMimeParserOptions *options, GMimeMessagePart *mpart, BoundaryType *found, int depth) +parser_scan_message_part (GMimeParser *parser, GMimeParserOptions *options, GMimeMessagePart *mpart, int depth) { struct _GMimeParserPrivate *priv = parser->priv; ContentType *content_type; @@ -1736,7 +1755,7 @@ parser_scan_message_part (GMimeParser *parser, GMimeParserOptions *options, GMim atleast = MAX (SCAN_HEAD, MAX_BOUNDARY_LEN (priv->bounds)); if (parser_fill (parser, atleast) <= 0) { - *found = BOUNDARY_EOS; + priv->boundary = BOUNDARY_EOS; return; } @@ -1748,8 +1767,8 @@ parser_scan_message_part (GMimeParser *parser, GMimeParserOptions *options, GMim while (*inptr != '\n') inptr++; - *found = check_boundary (priv, priv->inptr, inptr - priv->inptr); - switch (*found) { + priv->boundary = check_boundary (priv, priv->inptr, inptr - priv->inptr); + switch (priv->boundary) { case BOUNDARY_IMMEDIATE_END: case BOUNDARY_IMMEDIATE: case BOUNDARY_PARENT: @@ -1768,7 +1787,7 @@ parser_scan_message_part (GMimeParser *parser, GMimeParserOptions *options, GMim /* get the headers */ priv->state = GMIME_PARSER_STATE_HEADERS; if (parser_step (parser, options) == GMIME_PARSER_STATE_ERROR) { - *found = BOUNDARY_EOS; + priv->boundary = BOUNDARY_EOS; return; } @@ -1792,9 +1811,9 @@ parser_scan_message_part (GMimeParser *parser, GMimeParserOptions *options, GMim content_type = parser_content_type (parser, NULL); if (content_type_is_type (content_type, "multipart", "*")) - object = parser_construct_multipart (parser, options, content_type, TRUE, found, depth + 1); + object = parser_construct_multipart (parser, options, content_type, TRUE, depth + 1); else - object = parser_construct_leaf_part (parser, options, content_type, TRUE, found, depth + 1); + object = parser_construct_leaf_part (parser, options, content_type, TRUE, depth + 1); content_type_destroy (content_type); message->mime_part = object; @@ -1818,7 +1837,7 @@ is_rfc822 (const char *subtype) } static GMimeObject * -parser_construct_leaf_part (GMimeParser *parser, GMimeParserOptions *options, ContentType *content_type, gboolean toplevel, BoundaryType *found, int depth) +parser_construct_leaf_part (GMimeParser *parser, GMimeParserOptions *options, ContentType *content_type, gboolean toplevel, int depth) { struct _GMimeParserPrivate *priv = parser->priv; const char *subtype = content_type->subtype; @@ -1891,15 +1910,17 @@ parser_construct_leaf_part (GMimeParser *parser, GMimeParserOptions *options, Co if (priv->state == GMIME_PARSER_STATE_HEADERS_END) { /* skip empty line after headers */ if (parser_step (parser, options) == GMIME_PARSER_STATE_ERROR) { - *found = BOUNDARY_EOS; + priv->boundary = BOUNDARY_EOS; return object; } } - if (GMIME_IS_MESSAGE_PART (object)) - parser_scan_message_part (parser, options, (GMimeMessagePart *) object, found, depth + 1); - else - parser_scan_mime_part_content (parser, (GMimePart *) object, found); + if (priv->state == GMIME_PARSER_STATE_CONTENT) { + if (GMIME_IS_MESSAGE_PART (object)) + parser_scan_message_part (parser, options, (GMimeMessagePart *) object, depth + 1); + else + parser_scan_mime_part_content (parser, (GMimePart *) object); + } return object; } @@ -1929,18 +1950,17 @@ crlf2lf (char *in) *outptr = '\0'; } -static BoundaryType +static void parser_scan_multipart_face (GMimeParser *parser, GMimeMultipart *multipart, gboolean prologue) { GMimeStream *stream; - BoundaryType found; GByteArray *buffer; gboolean empty; gint64 len; char *face; stream = g_mime_stream_mem_new (); - found = parser_scan_content (parser, stream, &empty); + parser_scan_content (parser, stream, &empty); if (!empty) { buffer = g_mime_stream_mem_get_byte_array ((GMimeStreamMem *) stream); @@ -1957,8 +1977,6 @@ parser_scan_multipart_face (GMimeParser *parser, GMimeMultipart *multipart, gboo } g_object_unref (stream); - - return found; } #define parser_scan_multipart_prologue(parser, multipart) parser_scan_multipart_face (parser, multipart, TRUE) @@ -1970,43 +1988,53 @@ parser_scan_multipart_subparts (GMimeParser *parser, GMimeParserOptions *options struct _GMimeParserPrivate *priv = parser->priv; ContentType *content_type; GMimeObject *subpart; - BoundaryType found; do { /* skip over the boundary marker */ if (parser_skip_line (parser) == -1) { - found = BOUNDARY_EOS; + priv->boundary = BOUNDARY_EOS; break; } /* get the headers */ priv->state = GMIME_PARSER_STATE_HEADERS; if (parser_step (parser, options) == GMIME_PARSER_STATE_ERROR) { - found = BOUNDARY_EOS; + priv->boundary = BOUNDARY_EOS; break; } + + if (priv->state == GMIME_PARSER_STATE_BOUNDARY) { + if (priv->headers->len == 0) { + if (priv->boundary == BOUNDARY_IMMEDIATE) + continue; + break; + } + + /* This part has no content, but that will be handled in in parser_construct_multipart() + * or parser_consruct_leaf_part(). */ + } if (priv->state == GMIME_PARSER_STATE_COMPLETE && priv->headers->len == 0) { - found = BOUNDARY_IMMEDIATE_END; + priv->boundary = BOUNDARY_IMMEDIATE_END; break; } content_type = parser_content_type (parser, ((GMimeObject *) multipart)->content_type); if (content_type_is_type (content_type, "multipart", "*")) - subpart = parser_construct_multipart (parser, options, content_type, FALSE, &found, depth + 1); + subpart = parser_construct_multipart (parser, options, content_type, FALSE, depth + 1); else - subpart = parser_construct_leaf_part (parser, options, content_type, FALSE, &found, depth + 1); + subpart = parser_construct_leaf_part (parser, options, content_type, FALSE, depth + 1); g_mime_multipart_add (multipart, subpart); content_type_destroy (content_type); g_object_unref (subpart); - } while (found == BOUNDARY_IMMEDIATE); + } while (priv->boundary == BOUNDARY_IMMEDIATE); - return found; + return priv->boundary; } static GMimeObject * -parser_construct_multipart (GMimeParser *parser, GMimeParserOptions *options, ContentType *content_type, gboolean toplevel, BoundaryType *found, int depth) +parser_construct_multipart (GMimeParser *parser, GMimeParserOptions *options, ContentType *content_type, gboolean toplevel, int depth) { struct _GMimeParserPrivate *priv = parser->priv; GMimeMultipart *multipart; @@ -2041,7 +2069,7 @@ parser_construct_multipart (GMimeParser *parser, GMimeParserOptions *options, Co if (priv->state == GMIME_PARSER_STATE_HEADERS_END) { /* skip empty line after headers */ if (parser_step (parser, options) == GMIME_PARSER_STATE_ERROR) { - *found = BOUNDARY_EOS; + priv->boundary = BOUNDARY_EOS; return object; } } @@ -2049,33 +2077,33 @@ parser_construct_multipart (GMimeParser *parser, GMimeParserOptions *options, Co if ((boundary = g_mime_object_get_content_type_parameter (object, "boundary")) && depth < MAX_LEVEL) { parser_push_boundary (parser, boundary); - *found = parser_scan_multipart_prologue (parser, multipart); + parser_scan_multipart_prologue (parser, multipart); - if (*found == BOUNDARY_IMMEDIATE) - *found = parser_scan_multipart_subparts (parser, options, multipart, depth); + if (priv->boundary == BOUNDARY_IMMEDIATE) + priv->boundary = parser_scan_multipart_subparts (parser, options, multipart, depth); - if (*found == BOUNDARY_IMMEDIATE_END) { + if (priv->boundary == BOUNDARY_IMMEDIATE_END) { /* eat end boundary */ multipart->write_end_boundary = TRUE; parser_skip_line (parser); parser_pop_boundary (parser); - *found = parser_scan_multipart_epilogue (parser, multipart); + parser_scan_multipart_epilogue (parser, multipart); return object; } - if ((*found == BOUNDARY_PARENT) || (*found == BOUNDARY_PARENT_END)) + if (priv->boundary == BOUNDARY_PARENT || priv->boundary == BOUNDARY_PARENT_END) _g_mime_parser_options_warn (options, ctype_offset, GMIME_WARN_MALFORMED_MULTIPART, content_type->subtype); - if (*found == BOUNDARY_EOS) + if (priv->boundary == BOUNDARY_EOS) _g_mime_parser_options_warn (options, -1, GMIME_WARN_TRUNCATED_MESSAGE, NULL); multipart->write_end_boundary = FALSE; parser_pop_boundary (parser); - if (*found == BOUNDARY_PARENT_END && found_immediate_boundary (priv, TRUE)) - *found = BOUNDARY_IMMEDIATE_END; - else if (*found == BOUNDARY_PARENT && found_immediate_boundary (priv, FALSE)) - *found = BOUNDARY_IMMEDIATE; + if (priv->boundary == BOUNDARY_PARENT_END && found_immediate_boundary (priv, TRUE)) + priv->boundary = BOUNDARY_IMMEDIATE_END; + else if (priv->boundary == BOUNDARY_PARENT && found_immediate_boundary (priv, FALSE)) + priv->boundary = BOUNDARY_IMMEDIATE; } else { if (depth >= MAX_LEVEL) { _g_mime_parser_options_warn (options, priv->headers_begin, GMIME_CRIT_NESTING_OVERFLOW, NULL); @@ -2086,7 +2114,7 @@ parser_construct_multipart (GMimeParser *parser, GMimeParserOptions *options, Co } /* this will scan everything into the prologue */ - *found = parser_scan_multipart_prologue (parser, multipart); + parser_scan_multipart_prologue (parser, multipart); } return object; @@ -2098,7 +2126,6 @@ parser_construct_part (GMimeParser *parser, GMimeParserOptions *options) struct _GMimeParserPrivate *priv = parser->priv; ContentType *content_type; GMimeObject *object; - BoundaryType found; /* get the headers */ priv->state = GMIME_PARSER_STATE_HEADERS; @@ -2111,9 +2138,9 @@ parser_construct_part (GMimeParser *parser, GMimeParserOptions *options) content_type = parser_content_type (parser, NULL); if (content_type_is_type (content_type, "multipart", "*")) - object = parser_construct_multipart (parser, options, content_type, FALSE, &found, 0); + object = parser_construct_multipart (parser, options, content_type, FALSE, 0); else - object = parser_construct_leaf_part (parser, options, content_type, FALSE, &found, 0); + object = parser_construct_leaf_part (parser, options, content_type, FALSE, 0); content_type_destroy (content_type); @@ -2148,7 +2175,6 @@ parser_construct_message (GMimeParser *parser, GMimeParserOptions *options) ContentType *content_type; GMimeMessage *message; GMimeObject *object; - BoundaryType found; const char *inptr; gboolean can_warn; Header *header; @@ -2206,9 +2232,9 @@ parser_construct_message (GMimeParser *parser, GMimeParserOptions *options) content_type = parser_content_type (parser, NULL); if (content_type_is_type (content_type, "multipart", "*")) - object = parser_construct_multipart (parser, options, content_type, TRUE, &found, 0); + object = parser_construct_multipart (parser, options, content_type, TRUE, 0); else - object = parser_construct_leaf_part (parser, options, content_type, TRUE, &found, 0); + object = parser_construct_leaf_part (parser, options, content_type, TRUE, 0); content_type_destroy (content_type); message->mime_part = object; |