/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "str.h"
#include "array.h"
#include "istream.h"
#include "istream-crlf.h"
#include "istream-nonuls.h"
#include "istream-base64.h"
#include "istream-header-filter.h"
#include "istream-qp.h"
#include "ostream.h"
#include "message-parser.h"
#include "message-decoder.h"
#include "mail-storage-private.h"
#include "mail-namespace.h"
#include "imap-bodystructure.h"
#include "imap-parser.h"
#include "imap-msgpart.h"
enum fetch_type {
};
struct imap_msgpart {
/* "" for root, otherwise e.g. "1.2.3". the .MIME, .HEADER, etc.
suffix not included */
const char *section_number;
/* HEADER.FIELDS[.NOT] (list of headers) */
const char *const *headers;
/* which part of the message part to fetch (default: 0..(uoff_t)-1) */
};
struct imap_msgpart_open_ctx {
/* from matching message_part, set after opening: */
};
{
return msgpart;
}
{
return imap_msgpart_type(FETCH_FULL);
}
{
return imap_msgpart_type(FETCH_HEADER);
}
{
return imap_msgpart_type(FETCH_BODY);
}
static struct message_part *
{
const char *path;
unsigned int num;
/* get part number, we have already verified its validity */
num = 0;
path++;
}
if (*path == '.')
path++;
/* find the part */
} else {
/* only 1 allowed with non-multipart messages.
finished after this. */
if (num != 1)
else if (*path != '\0' &&
}
body part */
}
}
return part;
}
static int
{
unsigned int list_count;
unsigned int i;
int result = 0;
list_count > 0) {
const char *value;
for (i = 0; i < list_count; i++) {
result = -1;
break;
}
}
/* istream-header-filter requires headers to be sorted */
} else {
result = -1;
}
return result;
}
static int
const char *header_list)
{
if (header_list[0] == ' ')
header_list++;
/* HEADER.FIELDS (list), HEADER.FIELDS.NOT (list) */
&fields) < 0)
return -1;
return 0;
}
{
unsigned int i;
bool next_digit;
int ret;
/* get the section number */
next_digit = TRUE;
for (i = 0; section[i] != '\0'; i++) {
next_digit = FALSE;
next_digit = TRUE;
} else {
break;
}
}
if (i == 0) {
/* [], [HEADER], etc. */
} else if (section[i] == '\0') {
/* [1.2.3] */
pool_unref(&pool);
return -1;
}
section = "";
} else {
/* [1.2.3.MIME], [1.2.3.HEADER], etc */
pool_unref(&pool);
return -1;
}
section += i;
}
if (*section == '\0') {
/* BODY[] - header+body */
} else {
/* BODY[1] - body only */
}
return 0;
}
return -1;
ret = 0;
section+17);
section+13);
} else {
ret = -1;
}
if (ret < 0) {
return -1;
}
/* we may be able to get this from cache, don't give a
wanted_fields hint */
else
} else {
return -1;
}
return 0;
}
{
}
{
switch (msgpart->fetch_type) {
case FETCH_HEADER:
case FETCH_HEADER_FIELDS:
case FETCH_HEADER_FIELDS_NOT:
return FALSE;
case FETCH_FULL:
case FETCH_MIME:
case FETCH_MIME_BODY:
case FETCH_BODY:
break;
}
return TRUE;
}
{
}
{
}
{
return msgpart->partial_offset;
}
{
return msgpart->partial_size;
}
{
return msgpart->wanted_fields;
}
{
unsigned int i;
return;
}
static int
const struct imap_msgpart *msgpart,
struct imap_msgpart_open_result *result_r)
{
bool has_nuls;
NULL);
NULL);
} else {
/* mail_get_header_stream() already filtered out the
unwanted headers. */
input = mail_input;
}
return -1;
}
i_stream_seek(input, 0);
result_r->size_field = 0;
return 0;
}
static struct istream *
const struct imap_msgpart *msgpart)
{
bool cr_skipped;
if (virtual_skip == 0) {
/* no need to seek */
/* use cache */
}
return errinput;
}
(msgpart->partial_offset != 0 ||
/* update cache */
if (cr_skipped) {
/* the physical_pos points to virtual CRLF, but
virtual_pos already skipped CR. that can't work,
so seek back the virtual CR */
cache->virtual_pos--;
}
}
if (cr_skipped)
return crlf_input;
}
static void
bool convert_nuls, bool use_partial_cache,
struct imap_msgpart_open_result *result)
{
/* input is already seeked to the beginning of the wanted data */
/* can't seek past the MIME part */
return;
}
if (have_crlfs) {
/* input has CRLF linefeeds, we can quickly seek to
wanted position */
} else {
/* input has LF linefeeds. it can be slow to seek to wanted
position, so try to do caching whenever possible */
msgpart);
}
/* limit output to specified number of bytes */
} else {
/* send all bytes */
}
/* IMAP literals must not contain NULs. change them to
0x80 characters. */
}
}
static int
struct message_part **part_r)
{
return 1;
}
return -1;
/* MIME part not found. */
return 0;
}
switch (msgpart->fetch_type) {
case FETCH_MIME:
MIME headers or not? Possibilities are: a) no, return
empty string (UW-IMAP does this), b) return the same as
HEADER. Dovecot has done b) for a long time and it's not
very clear which one is correct, so we'll just continue
with b) */
case FETCH_FULL:
case FETCH_MIME_BODY:
break;
case FETCH_HEADER:
case FETCH_HEADER_FIELDS:
case FETCH_HEADER_FIELDS_NOT:
case FETCH_BODY:
return 0;
}
break;
}
return 1;
}
static int
const struct message_part *part,
struct imap_msgpart_open_result *result_r)
{
/* find the MIME part */
return -1;
} else switch (msgpart->fetch_type) {
case FETCH_FULL:
/* fetch the whole message */
return -1;
break;
case FETCH_MIME:
case FETCH_MIME_BODY:
i_unreached();
case FETCH_HEADER:
case FETCH_HEADER_FIELDS_NOT:
/* fetch the message's header */
return -1;
break;
case FETCH_HEADER_FIELDS:
/* try to lookup the headers from cache */
}
&input) < 0)
return -1;
result_r->size_field = 0;
break;
case FETCH_BODY:
/* fetch the message's body */
"mail body", &input) < 0)
return -1;
break;
}
/* return specific headers */
}
switch (msgpart->fetch_type) {
case FETCH_FULL:
/* fall through */
case FETCH_MIME:
case FETCH_HEADER:
break;
case FETCH_HEADER_FIELDS:
case FETCH_HEADER_FIELDS_NOT:
i_unreached();
case FETCH_BODY:
case FETCH_MIME_BODY:
break;
}
*have_crlfs_r = !unknown_crlfs &&
return 0;
}
struct imap_msgpart_open_result *result_r)
{
int ret;
return -1;
if (ret == 0) {
/* MIME part not found. return an empty part. */
return 0;
}
if (msgpart->decode_cte_to_binary &&
/* binary fetch */
return -1;
}
&virtual_size, &binary,
return -1;
have_crlfs = TRUE;
} else {
&have_crlfs, result_r) < 0)
return -1;
}
return 0;
}
{
bool include_hdr;
unsigned int lines;
int ret;
if (!msgpart->decode_cte_to_binary ||
/* generic implementation */
return -1;
return 0;
}
/* binary-optimized implementation: */
return -1;
if (ret == 0) {
/* MIME part not found. return an empty part. */
*size_r = 0;
return 0;
}
return -1;
}
}
static int
struct message_part *all_parts)
{
&bodystructure) < 0)
return -1;
/* we just parsed the bodystructure */
return 0;
}
"Invalid message_part/BODYSTRUCTURE %s: %s",
bodystructure, error));
return -1;
}
return 0;
}
static int
struct message_part **binpart_r)
{
unsigned int lines;
return -1;
return -1;
}
return 0;
}
struct imap_msgpart *msgpart,
const char **bpstruct_r)
{
int ret;
/* if we start parsing the body in here, make sure we also parse the
BODYSTRUCTURE */
return -1;
if (ret == 0) {
/* MIME part not found. */
*bpstruct_r = NULL;
return 0;
}
return -1;
return -1;
}
if (msgpart->decode_cte_to_binary)
if (ret >= 0) {
}
}
{
}