/* Copyright (c) 2013-2018 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"
enum mail_attachment_state {
};
enum base64_state {
BASE64_STATE_0 = 0,
};
struct attachment_istream_part {
/* start offset of the message part in the original input stream */
/* for saving attachments base64-decoded: */
bool base64_failed;
int temp_fd;
};
struct attachment_istream {
void *context;
bool retry_read;
};
{
if (size > 0) {
}
}
const struct message_header_line *hdr)
{
return;
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)
{
/* multiparts may contain attachments as children,
but they're never themselves */
return FALSE;
}
return TRUE;
}
{
/* mixed LF vs CRLFs */
return -1;
}
/* 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 == '=') {
part->base64_line_blocks > 0) {
/* too many blocks */
return -1;
}
return 0;
} else {
return -1;
}
break;
case BASE64_STATE_CR:
if (chr != '\n')
return -1;
if (!part->base64_have_crlf) {
if (part->base64_line_blocks != 0) {
/* mixed LF vs CRLFs */
return -1;
}
}
return astream_base64_decode_lf(part);
case BASE64_STATE_EOB:
if (chr != '=')
return -1;
part->base64_line_blocks > 0) {
/* too many blocks */
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 - write the new data to temp file */
break;
}
}
{
const unsigned char *data;
int outfd;
*extra_buf_r = NULL;
/* 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;
bytes_needed)) > 0) {
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: %s",
}
if (o_stream_finish(output) < 0) {
i_error("istream-attachment: write(%s) failed: %s",
}
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: %s",
}
}
if (failed) {
i_close_fd(&outfd);
return -1;
}
/* successfully wrote it. switch to using it. */
return 0;
}
static int
{
const unsigned char *data;
int ret = 0;
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. */
} else {
}
}
/* open attachment output file */
if (!part->base64_failed) {
/* base64-decoder updated the hash, use it */
str_truncate(digest_str, 0);
} 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) {
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;
ret = -1;
else {
/* finished base64 may have added a few more trailing
bytes to the stream */
}
break;
}
return ret;
}
{
const char *error;
int ret;
return -2;
case -1:
/* done / error */
if (ret > 0) {
/* final data */
}
if (ret < 0) {
}
return -1;
case 0:
/* need more data */
return 0;
default:
break;
}
/* end of a MIME part */
return -1;
}
}
/* parsing a header */
/* end of headers */
}
} else {
}
}
static ssize_t
{
(struct attachment_istream *)stream;
bool retry;
do {
return ret;
}
bool close_parent)
{
(struct attachment_istream *)stream;
}
if (close_parent)
}
struct istream *
struct istream_attachment_settings *set,
void *context)
{
/* make sure the caller doesn't try to double-free this */
i_stream_get_fd(input), 0);
}
{
return astream->retry_read;
}