istream-attachment-extractor.c revision 8aeae03f9f447c8a792b215c9fb954468053c556
/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "istream-private.h"
#include "ostream.h"
#include "base64.h"
#include "buffer.h"
#include "str.h"
#include "hash-format.h"
#include "rfc822-parser.h"
#include "message-parser.h"
#include "istream-attachment-extractor.h"
#define BASE64_ATTACHMENT_MAX_EXTRA_BYTES 1024
enum mail_attachment_state {
};
enum base64_state {
BASE64_STATE_0 = 0,
};
struct attachment_istream_part {
char *content_type, *content_disposition;
enum mail_attachment_state state;
/* start offset of the message part in the original input stream */
/* for saving attachments base64-decoded: */
enum base64_state base64_state;
unsigned int base64_line_blocks, cur_base64_blocks;
bool base64_have_crlf; /* CRLF linefeeds */
bool base64_failed;
int temp_fd;
struct ostream *temp_output;
};
struct attachment_istream {
struct istream_private istream;
struct istream_attachment_settings set;
void *context;
struct message_parser_ctx *parser;
struct message_part *cur_part;
struct attachment_istream_part part;
bool retry_read;
};
{
if (size > 0) {
}
}
const struct message_header_line *hdr)
{
struct rfc822_parser_context parser;
T_BEGIN {
}
} T_END;
}
static void
const struct message_header_line *hdr)
{
/* just pass it without parsing to is_attachment() callback */
}
struct message_header_line *hdr)
{
}
if (!hdr->no_newline) {
if (hdr->crlf_newline)
else
}
return;
}
}
struct message_part *part)
{
struct istream_attachment_header ahdr;
/* multiparts may contain attachments as children,
but they're never themselves */
return FALSE;
}
return TRUE;
}
{
/* last line */
return 0;
} else if (part->base64_line_blocks == 0) {
/* first line */
if (part->cur_base64_blocks == 0)
return -1;
/* line is ok */
} else {
return -1;
}
part->cur_base64_blocks = 0;
return 1;
}
static int
{
switch (part->base64_state) {
case BASE64_STATE_0:
if (base64_is_valid_char(chr))
part->base64_state++;
else if (chr == '\r')
else if (chr == '\n') {
return astream_base64_decode_lf(part);
} else {
return -1;
}
break;
case BASE64_STATE_1:
if (!base64_is_valid_char(chr))
return -1;
part->base64_state++;
break;
case BASE64_STATE_2:
if (base64_is_valid_char(chr))
part->base64_state++;
else if (chr == '=')
else
return -1;
break;
case BASE64_STATE_3:
if (base64_is_valid_char(chr)) {
} else if (chr == '=') {
return 0;
} else {
return -1;
}
break;
case BASE64_STATE_CR:
if (chr != '\n')
return -1;
return astream_base64_decode_lf(part);
case BASE64_STATE_EOB:
if (chr != '=')
return -1;
return 0;
case BASE64_STATE_EOM:
i_unreached();
}
return 1;
}
static void
{
size_t i;
int ret;
return;
for (i = 0; i < size; i++) {
if (ret <= 0) {
if (ret < 0)
break;
}
}
}
{
int fd;
if (fd == -1)
return -1;
return 0;
}
const struct message_block *block)
{
case MAIL_ATTACHMENT_STATE_NO:
break;
/* we'll write data to in-memory buffer until we reach
attachment min_size */
}
break;
}
/* attachment is large enough. we'll first copy the buffered
data from memory to temp file */
if (astream_open_output(astream) < 0) {
/* failed, fallback to just saving it inline */
break;
}
/* fall through to write the new data to temp file */
break;
}
}
{
const unsigned char *data;
int outfd;
/* only a small part of the MIME part is base64-encoded. */
return -1;
}
if (part->base64_line_blocks == 0) {
/* only one line of base64 */
}
/* decode base64 data and write it to another temp file */
if (outfd == -1)
return -1;
buffer_set_used_size(buf, 0);
i_error("istream-attachment: BUG: "
"Attachment base64 data unexpectedly broke");
break;
}
}
if (ret != -1) {
} else if (base64_input->stream_errno != 0) {
i_error("istream-attachment: read(%s) failed: %m",
}
if (o_stream_nfinish(output) < 0) {
i_error("istream-attachment: write(%s) failed: %m",
}
buffer_free(&buf);
/* write the rest of the data to the message stream */
}
if (input->stream_errno != 0) {
i_error("istream-attachment: read(%s) failed: %m",
}
}
if (failed) {
i_close_fd(&outfd);
return -1;
}
/* successfully wrote it. switch to using it. */
}
return 0;
}
{
struct istream_attachment_info info;
const unsigned char *data;
int ret = 0;
i_error("istream-attachment: write(%s) failed: %m",
return -1;
}
/* base64_bytes contains how many valid base64 bytes there are so far.
if the base64 ends properly, it'll specify how much of the MIME part
is saved as an attachment. the rest of the data (typically
linefeeds) is added back to main stream */
/* get the hash before base64-decoder resets it */
/* if it looks like we can decode base64 without any data loss,
do it and write the decoded data to another temp file. */
if (!part->base64_failed) {
part->base64_bytes > 0) {
/* there is no trailing LF or '=' characters,
but it's not completely empty */
}
/* base64 data looks ok. */
if (astream_decode_base64(astream) < 0)
} else {
}
}
/* open attachment output file */
if (!part->base64_failed) {
/* base64-decoder updated the hash, use it */
} else {
/* couldn't decode base64, so write the entire MIME part
as attachment */
}
return -1;
/* copy data to attachment from temp file */
}
if (input->stream_errno != 0) {
i_error("istream-attachment: read(%s) failed: %m",
ret = -1;
}
ret = -1;
return ret;
}
{
}
static int
{
int ret = 0;
/* MIME part changed. we're now parsing the end of a boundary,
possibly followed by message epilogue */
case MAIL_ATTACHMENT_STATE_NO:
break;
/* MIME part wasn't large enough to be an attachment */
}
break;
if (astream_part_finish(astream) < 0)
ret = -1;
break;
}
return ret;
}
static int
{
struct message_block block;
return -2;
case -1:
/* done / error */
if (astream_end_of_part(astream) < 0)
return -1;
case 0:
/* need more data */
return 0;
default:
break;
}
/* end of a MIME part */
if (astream_end_of_part(astream) < 0) {
return -1;
}
}
/* parsing a header */
/* end of headers */
}
} else {
}
}
{
struct attachment_istream *astream =
(struct attachment_istream *)stream;
bool retry;
do {
return ret;
}
static const struct stat *
{
}
{
struct attachment_istream *astream =
(struct attachment_istream *)stream;
struct message_part *parts;
int ret;
}
}
struct istream *
struct istream_attachment_settings *set,
void *context)
{
struct attachment_istream *astream;
/* make sure the caller doesn't try to double-free this */
}
{
struct attachment_istream *astream =
return astream->retry_read;
}