static void test_quoted_printable_decode(void) { static struct test_quoted_printable_decode_data data[] = { { "foo \r\nbar=", "foo\r\nbar", 1, 0 }, { "foo\t=\nbar", "foo\tbar", 0, 0 }, { "foo = \n=01", "foo \001", 0, 0 }, { "foo =\t\r\nbar", "foo bar", 0, 0 }, { "foo =\r\n=01", "foo \001", 0, 0 }, { "foo \nbar=", "foo\nbar", 1, 0 }, { "=0A=0D ", "\n\r", 2, 0 }, { "foo_bar", "foo_bar", 0, 0 }, { "foo=", "foo", 1, 0 }, { "foo= ", "foo", 3, 0 }, { "foo=A", "foo", 2, 0 }, { "foo=Ax", "foo=Ax", 0, -1 }, { "foo=Ax=xy", "foo=Ax=xy", 0, -1 } }; buffer_t *buf; unsigned int i, start, end, len; size_t src_pos; int ret; test_begin("quoted printable decode"); buf = buffer_create_dynamic(pool_datastack_create(), 128); for (i = 0; i < N_ELEMENTS(data); i++) { len = strlen(data[i].input); ret = quoted_printable_decode((const void *)data[i].input, len, &src_pos, buf); test_assert(ret == data[i].ret); test_assert(src_pos + data[i].end_skip == len); test_assert(strcmp(data[i].output, str_c(buf)) == 0); buffer_set_used_size(buf, 0); for (start = 0, end = 1; end <= len; ) { quoted_printable_decode(CONST_PTR_OFFSET(data[i].input, start), end - start, &src_pos, buf); src_pos += start; start = src_pos; if (src_pos <= end) end++; else end = src_pos + 1; } test_assert(src_pos + data[i].end_skip == len); test_assert(strcmp(data[i].output, str_c(buf)) == 0); buffer_set_used_size(buf, 0); } test_end(); }
static int i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; int ret; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; /* normally the decoded quoted-printable content can't be larger than the encoded content, but because we always use CRLFs, it may use twice as much space by only converting LFs to CRLFs. */ i_stream_try_alloc(stream, size, &avail); buffer_avail = stream->buffer_size - stream->pos; if (size > buffer_avail/2) { /* can't fit everything to destination buffer. write as much as we can. */ size = buffer_avail/2; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) : quoted_printable_decode_final(data, size, &pos, &buf); if (ret < 0) { io_stream_set_error(&stream->iostream, "Invalid quoted-printable data: 0x%s", binary_to_hex(data+pos, I_MAX(size-pos, 8))); stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static int i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; int ret; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; /* the decoded quoted-printable content can never be larger than the encoded content. at worst they are equal. */ i_stream_try_alloc(stream, size, &avail); buffer_avail = stream->buffer_size - stream->pos; if (size > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = buffer_avail; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) : quoted_printable_decode_final(data, size, &pos, &buf); if (ret < 0) { stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static bool message_decode_body(struct message_decoder_context *ctx, struct message_block *input, struct message_block *output) { const unsigned char *data = NULL; size_t pos = 0, size = 0; int ret; if (ctx->encoding_buf->used != 0) { /* @UNSAFE */ buffer_append(ctx->encoding_buf, input->data, input->size); } switch (ctx->message_cte) { case MESSAGE_CTE_UNKNOWN: /* just skip this body */ return FALSE; case MESSAGE_CTE_78BIT: case MESSAGE_CTE_BINARY: data = input->data; size = pos = input->size; break; case MESSAGE_CTE_QP: buffer_set_used_size(ctx->buf, 0); if (ctx->encoding_buf->used != 0) { (void)quoted_printable_decode(ctx->encoding_buf->data, ctx->encoding_buf->used, &pos, ctx->buf); } else { (void)quoted_printable_decode(input->data, input->size, &pos, ctx->buf); } data = ctx->buf->data; size = ctx->buf->used; break; case MESSAGE_CTE_BASE64: buffer_set_used_size(ctx->buf, 0); if (ctx->encoding_buf->used != 0) { ret = base64_decode(ctx->encoding_buf->data, ctx->encoding_buf->used, &pos, ctx->buf); } else { ret = base64_decode(input->data, input->size, &pos, ctx->buf); } if (ret < 0) { /* corrupted base64 data, don't bother with the rest of it */ return FALSE; } if (ret == 0) { /* end of base64 input */ pos = input->size; buffer_set_used_size(ctx->encoding_buf, 0); } data = ctx->buf->data; size = ctx->buf->used; break; } if (ctx->encoding_buf->used != 0) buffer_delete(ctx->encoding_buf, 0, pos); else if (pos != input->size) { buffer_append(ctx->encoding_buf, input->data + pos, input->size - pos); } if (ctx->binary_input) { output->data = data; output->size = size; } else if (ctx->charset_utf8 || ctx->charset_trans == NULL) { /* handle unknown charsets the same as UTF-8. it might find usable ASCII text. */ buffer_set_used_size(ctx->buf2, 0); if (ctx->normalizer != NULL) { (void)ctx->normalizer(data, size, ctx->buf2); output->data = ctx->buf2->data; output->size = ctx->buf2->used; } else if (uni_utf8_get_valid_data(data, size, ctx->buf2)) { output->data = data; output->size = size; } else { output->data = ctx->buf2->data; output->size = ctx->buf2->used; } } else { buffer_set_used_size(ctx->buf2, 0); if (ctx->translation_size != 0) translation_buf_decode(ctx, &data, &size); pos = size; (void)charset_to_utf8(ctx->charset_trans, data, &pos, ctx->buf2); if (pos != size) { ctx->translation_size = size - pos; i_assert(ctx->translation_size <= sizeof(ctx->translation_buf)); memcpy(ctx->translation_buf, data + pos, ctx->translation_size); } output->data = ctx->buf2->data; output->size = ctx->buf2->used; } output->hdr = NULL; return TRUE; }