imap-fetch-body.c revision ca63a68b1dbeeb44c1e71ddf129054f21edc0cb8
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
#include "common.h"
#include "buffer.h"
#include "str.h"
#include "strescape.h"
#include "istream.h"
#include "ostream.h"
#include "istream-header-filter.h"
#include "message-parser.h"
#include "message-send.h"
#include "mail-storage.h"
#include "imap-parser.h"
#include "imap-fetch.h"
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
struct imap_fetch_body_data {
struct imap_fetch_body_data *next;
struct mailbox_header_lookup_ctx *header_ctx;
const char *section; /* NOTE: always uppercased */
set it to (uoff_t)-1 */
const char *const *fields;
unsigned int skip_set:1;
unsigned int peek:1;
};
struct partial_cache {
unsigned int select_counter;
unsigned int uid;
bool cr_skipped;
struct message_size pos;
};
static struct partial_cache last_partial = { 0, 0, 0, 0, { 0, 0, 0 } };
{
i_error("FETCH for mailbox %s UID %u "
"failed to read message input: %m",
}
{
/* we can use the cache */
} else {
}
return -1;
return 0;
}
{
return 0;
}
{
}
const struct imap_fetch_body_data *body,
{
else
else
return str;
}
bool add_missing_eoh, bool *last_cr)
{
const unsigned char *msg;
unsigned char add;
/* go through the message data and insert CRs where needed. */
while (vsize_left > 0 && !blocks &&
add = '\0';
for (i = 0; i < size && vsize_left > 0; i++) {
vsize_left--;
if (msg[i] == '\n') {
(i == 0 && !cr_skipped)) {
/* missing CR */
add = '\r';
break;
}
} else if (msg[i] == '\0') {
add = 128;
break;
}
}
return -1;
add = '\0';
}
if (ret > 0)
if (add != '\0') {
return -1;
if (ret == 0)
else {
sent++;
if (add == 128)
}
}
}
if (input->stream_errno != 0) {
return -1;
}
/* Netscape missing EOH workaround. */
return -1;
sent += 2;
}
/* Input stream gave less data than we expected. Two choices
here: either we fill the missing data with spaces or we
disconnect the client.
We shouldn't really ever get here. One reason is if mail
was deleted from NFS server while we were reading it.
Another is some temporary disk error.
If we filled the missing data the client could cache it,
and if it was just a temporary error the message would be
permanently left corrupted in client's local cache. So, we
disconnect the client and hope that next try works. */
i_error("FETCH %s for mailbox %s UID %u got too little data: "
return -1;
}
*last_cr = cr_skipped;
return sent;
}
{
if (ret < 0)
return -1;
if (ctx->update_partial) {
}
}
{
if (ret < 0)
return -1;
/* Netscape missing EOH workaround. */
return -1;
}
/* unfinished */
/* Input stream gave less data than expected */
i_error("FETCH %s for mailbox %s UID %u "
"got too little data (copying): "
return -1;
}
return 0;
}
return 1;
}
const struct message_size *size)
{
/* no need to kludge with CRs, we can use sendfile() */
} else {
}
}
const struct imap_fetch_body_data *body,
const struct message_size *size)
{
return -1;
if (!ctx->update_partial) {
return -1;
}
} else {
return -1;
}
}
}
const struct imap_fetch_body_data *body)
{
const struct message_size *fetch_size;
return -1;
} else {
&input) < 0)
return -1;
}
case '\0':
/* BODY[] - fetch everything */
fetch_size = &body_size;
break;
case 'H':
/* BODY[HEADER] - fetch only header */
fetch_size = &hdr_size;
break;
case 'T':
/* BODY[TEXT] - skip header */
fetch_size = &body_size;
break;
default:
i_unreached();
}
}
bool *matched ATTR_UNUSED,
struct imap_fetch_context *ctx)
{
}
const struct imap_fetch_body_data *body,
const char *header_section)
{
struct message_size msg_size;
/* MIME, HEADER.FIELDS (list), HEADER.FIELDS.NOT (list) */
} else {
i_error("BUG: Accepted invalid section from user: '%s'",
return -1;
}
return -1;
}
if (!ctx->cur_have_eoh &&
(client_workarounds & WORKAROUND_NETSCAPE_EOH) != 0) {
/* Netscape 4.x doesn't like if end of headers line is
missing. */
}
ctx->cur_size_field = 0;
}
static int
const struct imap_fetch_body_data *body)
{
return -1;
}
static int
struct imap_fetch_body_data *body)
{
struct message_size size;
/* deinit */
return 0;
}
return -1;
return -1;
}
/* FIXME: We'll just always add the end of headers line now.
ideally mail-storage would have a way to tell us if it exists. */
ctx->cur_size_field = 0;
}
/* Find message_part for section (eg. 1.3.4) */
{
const struct message_part *part;
const char *path;
unsigned int num;
return -1;
/* 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 */
if (num != 1)
}
body part */
}
}
return 0;
}
const struct imap_fetch_body_data *body)
{
const struct message_part *part;
const char *section;
return -1;
/* part doesn't exist */
return -1;
return 1;
}
return -1;
if (*section == '\0') {
/* fetch the whole section */
}
/* fetch section's MIME header */
}
return -1;
return 1;
}
}
/* all headers */
}
}
return 1;
}
static bool fetch_body_header_fields_check(const char *section)
{
if (*section++ != '(')
return FALSE;
if (*section == ')')
return FALSE; /* has to be at least one field */
if (*section == '(')
return FALSE;
section++;
}
if (*section++ != ')')
return FALSE;
if (*section != '\0')
return FALSE;
return TRUE;
}
struct imap_fetch_body_data *body,
const char *section)
{
return FALSE;
MAIL_FETCH_STREAM_BODY)) != 0) {
/* we'll need to open the file anyway, don't try to get the
headers from cache. */
return TRUE;
}
}
return TRUE;
}
struct imap_fetch_body_data *body)
{
if (*section == '\0') {
fetch_body, body);
return TRUE;
}
fetch_body, body);
return TRUE;
}
/* exact header matches could be cached */
fetch_body, body);
return TRUE;
}
return TRUE;
return TRUE;
}
if (*section == '\0' ||
return TRUE;
}
}
"Invalid BODY[..] parameter: Unknown or broken section");
return FALSE;
}
/* Parse next digits in string into integer. Returns FALSE if the integer
becomes too big and wraps. */
{
*value = 0;
while (**p >= '0' && **p <= '9') {
return FALSE;
(*p)++;
}
return TRUE;
}
struct imap_fetch_body_data *body,
const char *prefix,
unsigned int args_count)
{
const char **arr;
size_t i;
/* @UNSAFE: NULL-terminated list of headers */
for (i = 0; i < args_count; i++) {
"Invalid BODY[..] parameter: "
"Header list contains non-strings");
return FALSE;
}
if (i != 0)
else {
}
}
return TRUE;
}
{
struct imap_fetch_body_data *body;
const char *partial;
const char *p = name + 4;
p += 5;
} else {
}
if (*p != '[') {
"Invalid BODY[..] parameter: Missing '['");
return FALSE;
}
/* BODY[HEADER.FIELDS.. (headers list)] */
"Invalid BODY[..] parameter: Missing ']'");
return FALSE;
}
IMAP_ARG_LIST_ARGS(&(*args)[0]),
IMAP_ARG_LIST_COUNT(&(*args)[0])))
return FALSE;
*args += 2;
} else {
/* no headers list */
if (p == NULL) {
"Invalid BODY[..] parameter: Missing ']'");
return FALSE;
}
}
if (*++p == '<') {
/* <start.end> */
partial = p;
p++;
/* wrapped */
"Invalid BODY[..] parameter: "
"Too big partial start");
return FALSE;
}
if (*p == '.') {
p++;
/* wrapped */
"Invalid BODY[..] parameter: "
"Too big partial end");
return FALSE;
}
}
if (*p != '>') {
t_strdup_printf("Invalid BODY[..] parameter: "
"Missing '>' in '%s'",
partial));
return FALSE;
}
}
}
void *context ATTR_UNUSED)
{
return -1;
return 1;
}
void *context ATTR_UNUSED)
{
struct message_size size;
const char *str;
return -1;
if (ctx->cur_offset == 0) {
}
return -1;
}
}
{
struct message_size hdr_size;
const char *str;
return -1;
}
return -1;
}
void *context ATTR_UNUSED)
{
const char *str;
return -1;
}
return -1;
}
{
fetch_rfc822, NULL);
return TRUE;
}
return TRUE;
}
return TRUE;
}
return TRUE;
}
return FALSE;
}