message-parser.c revision 12fd7a30563f3256642070ef9528dda0d089cb41
/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "buffer.h"
#include "str.h"
#include "istream.h"
#include "rfc822-parser.h"
#include "rfc2231-parser.h"
#include "message-parser.h"
/* RFC-2046 requires boundaries are max. 70 chars + "--" prefix + "--" suffix.
We'll add a bit more just in case. */
struct message_boundary {
struct message_boundary *next;
struct message_part *part;
const char *boundary;
unsigned int epilogue_found:1;
};
struct message_parser_ctx {
const char *broken_reason;
enum message_parser_flags flags;
const char *last_boundary;
struct message_boundary *boundaries;
char last_chr;
unsigned int want_count;
struct message_header_parser_ctx *hdr_parser_ctx;
unsigned int prev_hdr_newline_size;
struct message_block *block_r);
unsigned int part_seen_content_type:1;
unsigned int multipart:1;
unsigned int preparsed:1;
unsigned int eof:1;
};
struct message_block *block_r);
struct message_block *block_r);
struct message_block *block_r);
struct message_block *block_r);
struct message_block *block_r);
static struct message_boundary *
{
/* As MIME spec says: search from latest one to oldest one so that we
don't break if the same boundary is used in nested parts. Also the
full message line doesn't have to match the boundary, only the
beginning. However, if there are multiple prefixes whose beginning
matches, use the longest matching one. */
while (boundaries != NULL) {
best = boundaries;
}
return best;
}
struct message_block *block)
{
unsigned int missing_cr_count = 0;
/* check if we have NULs */
/* count number of lines and missing CRs */
if (*data == '\n') {
}
}
}
{
int ret;
}
if (ret <= 0) {
switch (ret) {
case 0:
return 0;
}
break;
case -1:
/* EOF, but we still have some data.
return it. */
return 1;
}
return -1;
case -2:
break;
default:
i_unreached();
}
}
if (!*full_r) {
/* reset number of wanted characters if we actually got them */
}
return 1;
}
static struct message_part *
{
MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0);
p->children_count++;
/* set child position */
part->physical_pos =
return part;
}
{
struct message_boundary *b;
ctx->boundaries = b;
}
struct message_block *block_r)
{
}
static int
struct message_boundary **boundary_r)
{
*boundary_r = NULL;
if (size < 2) {
return -1;
return 0;
}
/* not a boundary, just skip this line */
return -1;
}
/* need to find the end of line */
size < BOUNDARY_END_MAX_LEN &&
/* no LF found */
return 0;
}
data += 2;
size -= 2;
if (*boundary_r == NULL)
return -1;
(*boundary_r)->epilogue_found =
return 1;
}
struct message_block *block_r)
{
}
struct message_block *block_r)
{
const unsigned char *ptr;
int ret;
bool full;
return ret;
return 1;
return 0;
}
/* found the LF */
/* epilogue */
else
} else {
/* a new MIME part begins */
}
return 1;
}
struct message_boundary *boundary,
{
struct message_part *part;
/* get back to parent MIME part, summing the child MIME part sizes
into parent's body sizes */
}
if (boundary->epilogue_found) {
/* this boundary isn't needed anymore */
} else {
/* forget about the boundaries we possibly skipped */
}
/* the boundary itself should already be in buffer. add that. */
/* [[\r]\n]--<boundary>[--] */
if (first_line)
line_size = 0;
line_size = 2;
} else {
line_size = 1;
}
return 1;
}
struct message_block *block_r)
{
int ret;
bool full;
return ret;
/* handle boundary in first line of message. alternatively
it's an empty line. */
if (ret >= 0) {
return ret == 0 ? 0 :
}
}
boundary_start = 0;
/* skip to beginning of the next line. the first line was
handled already. */
if (boundary_start != 0) {
/* we can at least skip data until the first [CR]LF.
input buffer can't be full anymore. */
}
if (ret >= 0) {
/* found / need more data */
if (ret == 0 && boundary_start == 0)
break;
}
}
/* found / need more data */
} else if (boundary_start == 0) {
/* no linefeeds in this block. we can just skip it. */
ret = 0;
/* this may be the beginning of the \r\n--boundary */
}
} else {
/* the boundary wasn't found from this data block,
we'll need more data. */
ret = 0;
}
/* a) we found the boundary
b) we need more data and haven't reached EOF yet
so leave CR+LF + last line to buffer */
}
return 0;
return 1;
}
}
struct message_block *block_r)
{
bool full;
int ret;
return ret;
return 0;
return 1;
}
struct message_header_line *hdr)
{
struct rfc822_parser_context parser;
const char *const *results;
if (ctx->part_seen_content_type)
return;
return;
}
return;
ctx->last_boundary =
break;
}
}
}
{
return FALSE;
return TRUE;
return FALSE;
return TRUE;
}
return FALSE;
}
#define MUTEX_FLAGS \
struct message_block *block_r)
{
struct message_header_line *hdr;
struct message_boundary *boundary;
bool full;
int ret;
return ret;
/* we are at the end of headers and we've determined that we're
going to start a multipart. add the boundary already here
at this point so we can reliably determine whether the
"\n--boundary" belongs to us or to a previous boundary.
this is a problem if the boundary prefixes are identical,
because MIME requires only the prefix to match. */
}
/* before parsing the header see if we can find a --boundary from here.
we're guaranteed to be at the beginning of the line here. */
if (ret > 0) {
/* our own body begins with our own --boundary.
we don't want to handle that yet. */
ret = -1;
}
}
if (ret < 0) {
/* no boundary */
return ret;
}
} else if (ret == 0) {
/* need more data */
return 0;
} else {
/* boundary found. stop parsing headers here. The previous
[CR]LF belongs to the MIME boundary though. */
if (ctx->prev_hdr_newline_size > 0) {
/* remove the newline size from the MIME header */
/* add the newline size to the parent's body */
}
}
;
/* it's MIME. Content-* headers are valid */
else T_BEGIN {
} T_END;
}
return 1;
}
/* end of headers */
/* It's not MIME. Reset everything we found from
Content-Type. */
}
if (!ctx->part_seen_content_type ||
MESSAGE_PART_FLAG_MULTIPART_DIGEST) != 0) {
/* when there's no content-type specified and we're
content-type */
} else {
}
}
else
/* return empty block as end of headers */
return 1;
}
struct message_block *block_r)
{
ctx->prev_hdr_newline_size = 0;
}
{
return -1;
}
{
break;
}
/* parse epilogue of multipart parent if requested */
/* check for presence of epilogue */
if (parent_end > part_end) {
break;
}
}
}
}
struct message_block *block_r)
{
}
struct message_block *block_r)
{
}
struct message_block *block_r)
{
bool full;
int ret;
return ret;
}
return 1;
}
struct message_block *block_r)
{
const unsigned char *cur;
bool full;
int ret;
return ret;
/* we've got the full prologue: clip off the initial boundary */
/* [\r]\n--boundary[\r]\n */
return -1;
}
cur--;
/* find newline just before boundary */
if (*cur == '\n') break;
}
return -1;
}
/* clip boundary */
return 1;
}
/* retain enough data in the stream buffer to contain initial boundary */
if (end_offset > BOUNDARY_END_MAX_LEN)
else
boundary_min_start = 0;
return 0;
}
return 1;
}
struct message_block *block_r)
{
bool full;
int ret;
return ret;
}
return 1;
}
struct message_block *block_r)
{
bool full;
int ret;
return -1;
}
return ret;
/* [\r]\n--boundary--[\r]\n */
return 0;
}
return -1;
}
/* find the end of the line */
cur += 3;
return -1;
size < BOUNDARY_END_MAX_LEN &&
return 0;
}
}
return 0;
}
struct message_block *block_r)
{
/* header was actually larger than the cached size suggested */
return -1;
}
/* multipart messages may begin with --boundary--, which makes them
not have any children. */
else
}
struct message_block *block_r)
{
/* last child was actually larger than the cached size
suggested */
return -1;
}
}
struct message_block *block_r)
{
else {
}
} else {
}
}
struct message_block *block_r)
{
struct message_header_line *hdr;
int ret;
return ret;
}
return 1;
}
/* return empty block as end of headers */
return -1;
}
return 1;
}
struct message_block *block_r)
{
}
static struct message_parser_ctx *
enum message_parser_flags flags)
{
struct message_parser_ctx *ctx;
return ctx;
}
struct message_parser_ctx *
enum message_parser_flags flags)
{
struct message_parser_ctx *ctx;
return ctx;
}
struct message_parser_ctx *
enum message_parser_flags flags)
{
struct message_parser_ctx *ctx;
return ctx;
}
struct message_part **parts_r)
{
const char *error;
}
struct message_part **parts_r,
const char **error_r)
{
return ret;
}
struct message_block *block_r)
{
int ret;
if (ret == 0) {
return 0;
}
if (ret == -1) {
}
}
/* Successful EOF or unexpected failure */
}
}
/* data isn't supposed to be read, so make sure it's NULL */
}
return ret;
}
struct message_size *hdr_size,
void *context)
{
struct message_block block;
int ret;
break;
}
if (ret < 0) {
/* well, can't return error so fake end of headers */
}
}
void *context)
{
struct message_block block;
int ret;
}
}