1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
5
#include "hex-binary.h"
6
#include "istream-private.h"
7
#include "quoted-printable.h"
8
#include "istream-qp.h"
10
struct qp_decoder_istream {
11
struct istream_private istream;
15
i_stream_read_parent(struct istream_private *stream, size_t *prev_size)
20
size = i_stream_get_data_size(stream->parent);
21
if (size >= 4 && size != *prev_size) {
26
ret = i_stream_read(stream->parent);
28
stream->istream.stream_errno = stream->parent->stream_errno;
29
stream->istream.eof = stream->parent->eof;
32
*prev_size = i_stream_get_data_size(stream->parent);
37
i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof)
39
struct istream_private *stream = &bstream->istream;
40
const unsigned char *data;
41
size_t size, avail, buffer_avail, pos;
45
data = i_stream_get_data(stream->parent, &size);
49
/* normally the decoded quoted-printable content can't be larger than
50
the encoded content, but because we always use CRLFs, it may use
51
twice as much space by only converting LFs to CRLFs. */
52
i_stream_try_alloc(stream, size, &avail);
53
buffer_avail = stream->buffer_size - stream->pos;
55
if (size > buffer_avail/2) {
56
/* can't fit everything to destination buffer.
57
write as much as we can. */
58
size = buffer_avail/2;
63
buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
65
ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) :
66
quoted_printable_decode_final(data, size, &pos, &buf);
68
io_stream_set_error(&stream->iostream,
69
"Invalid quoted-printable data: 0x%s",
70
binary_to_hex(data+pos, I_MAX(size-pos, 8)));
71
stream->istream.stream_errno = EINVAL;
75
stream->pos += buf.used;
76
i_stream_skip(stream->parent, pos);
77
return pos > 0 ? 1 : 0;
80
static ssize_t i_stream_qp_decoder_read(struct istream_private *stream)
82
struct qp_decoder_istream *bstream =
83
(struct qp_decoder_istream *)stream;
84
const unsigned char *data;
85
size_t pre_count, post_count, size;
90
ret = i_stream_read_parent(stream, &prev_size);
92
if (ret != -1 || stream->istream.stream_errno != 0)
95
ret = i_stream_qp_try_decode_input(bstream, TRUE);
97
/* ended with =[whitespace] but without LF */
98
stream->istream.eof = TRUE;
101
/* partial qp input */
103
data = i_stream_get_data(stream->parent, &size);
104
io_stream_set_error(&stream->iostream,
105
"quoted-printable input ends with a partial block: 0x%s",
106
binary_to_hex(data, size));
107
stream->istream.stream_errno = EINVAL;
111
/* encode as much data as fits into destination buffer */
112
pre_count = stream->pos - stream->skip;
113
while ((ret = i_stream_qp_try_decode_input(bstream, FALSE)) > 0) ;
114
post_count = stream->pos - stream->skip;
115
} while (ret == 0 && pre_count == post_count);
120
i_assert(post_count > pre_count);
121
return post_count - pre_count;
125
i_stream_qp_decoder_seek(struct istream_private *stream,
126
uoff_t v_offset, bool mark)
128
if (v_offset < stream->istream.v_offset) {
129
/* seeking backwards - go back to beginning and seek
130
forward from there. */
131
stream->parent_expected_offset = stream->parent_start_offset;
132
stream->skip = stream->pos = 0;
133
stream->istream.v_offset = 0;
134
i_stream_seek(stream->parent, 0);
136
i_stream_default_seek_nonseekable(stream, v_offset, mark);
139
struct istream *i_stream_create_qp_decoder(struct istream *input)
141
struct qp_decoder_istream *bstream;
143
bstream = i_new(struct qp_decoder_istream, 1);
144
bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
146
bstream->istream.read = i_stream_qp_decoder_read;
147
bstream->istream.seek = i_stream_qp_decoder_seek;
149
bstream->istream.istream.readable_fd = FALSE;
150
bstream->istream.istream.blocking = input->blocking;
151
bstream->istream.istream.seekable = input->seekable;
152
return i_stream_create(&bstream->istream, input,
153
i_stream_get_fd(input));