~ubuntu-branches/ubuntu/utopic/dovecot/utopic-proposed

« back to all changes in this revision

Viewing changes to src/lib-mail/istream-binary-converter.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (4.1.35 sid)
  • Revision ID: package-import@ubuntu.com-20140108093549-i72o93pux8p0dlaf
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "buffer.h"
 
5
#include "base64.h"
 
6
#include "istream-private.h"
 
7
#include "message-parser.h"
 
8
#include "istream-binary-converter.h"
 
9
 
 
10
#define BASE64_BLOCK_INPUT_SIZE 3
 
11
#define BASE64_BLOCK_SIZE 4
 
12
#define BASE64_BLOCKS_PER_LINE (76/BASE64_BLOCK_SIZE)
 
13
#define MAX_HDR_BUFFER_SIZE (1024*32)
 
14
 
 
15
struct binary_converter_istream {
 
16
        struct istream_private istream;
 
17
 
 
18
        pool_t pool;
 
19
        struct message_parser_ctx *parser;
 
20
        struct message_part *convert_part;
 
21
        char base64_delayed[BASE64_BLOCK_INPUT_SIZE-1];
 
22
        unsigned int base64_delayed_len;
 
23
        unsigned int base64_block_pos;
 
24
 
 
25
        buffer_t *hdr_buf;
 
26
        unsigned int cte_header_len;
 
27
        unsigned int content_type_seen:1;
 
28
};
 
29
 
 
30
static void stream_add_data(struct binary_converter_istream *bstream,
 
31
                            const void *data, size_t size);
 
32
 
 
33
static bool part_can_convert(const struct message_part *part)
 
34
{
 
35
        /* some MUAs use "c-t-e: binary" for multiparts.
 
36
           we don't want to convert them. */
 
37
        return (part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0;
 
38
}
 
39
 
 
40
static void
 
41
stream_finish_convert_decision(struct binary_converter_istream *bstream)
 
42
{
 
43
        buffer_t *buf = bstream->hdr_buf;
 
44
        const unsigned char *data;
 
45
 
 
46
        bstream->hdr_buf = NULL;
 
47
        if (!part_can_convert(bstream->convert_part)) {
 
48
                bstream->convert_part = NULL;
 
49
                stream_add_data(bstream, buf->data, buf->used);
 
50
        } else {
 
51
                stream_add_data(bstream,
 
52
                        "Content-Transfer-Encoding: base64\r\n", 35);
 
53
 
 
54
                data = CONST_PTR_OFFSET(buf->data, bstream->cte_header_len);
 
55
                stream_add_data(bstream, data,
 
56
                                buf->used - bstream->cte_header_len);
 
57
        }
 
58
        buffer_free(&buf);
 
59
}
 
60
 
 
61
static void stream_add_data(struct binary_converter_istream *bstream,
 
62
                            const void *data, size_t size)
 
63
{
 
64
        if (size == 0)
 
65
                return;
 
66
 
 
67
        if (bstream->hdr_buf != NULL) {
 
68
                if (bstream->hdr_buf->used + size <= MAX_HDR_BUFFER_SIZE) {
 
69
                        buffer_append(bstream->hdr_buf, data, size);
 
70
                        return;
 
71
                }
 
72
                /* buffer is getting too large. just finish the decision. */
 
73
                stream_finish_convert_decision(bstream);
 
74
        }
 
75
 
 
76
        memcpy(i_stream_alloc(&bstream->istream, size), data, size);
 
77
        bstream->istream.pos += size;
 
78
}
 
79
 
 
80
static void stream_encode_base64(struct binary_converter_istream *bstream,
 
81
                                 const void *_data, size_t size)
 
82
{
 
83
        struct istream_private *stream = &bstream->istream;
 
84
        const unsigned char *data = _data;
 
85
        buffer_t buf;
 
86
        void *dest;
 
87
        size_t encode_size, max_encoded_size;
 
88
        unsigned char base64_block[BASE64_BLOCK_INPUT_SIZE];
 
89
        unsigned int base64_block_len, missing_len, encode_blocks;
 
90
 
 
91
        if (bstream->base64_delayed_len > 0) {
 
92
                if (bstream->base64_delayed_len == 1 && size == 1) {
 
93
                        bstream->base64_delayed[1] = data[0];
 
94
                        bstream->base64_delayed_len++;
 
95
                        return;
 
96
                }
 
97
                memcpy(base64_block, bstream->base64_delayed,
 
98
                       bstream->base64_delayed_len);
 
99
                base64_block_len = bstream->base64_delayed_len;
 
100
                if (size == 0) {
 
101
                        /* finish base64 */
 
102
                } else {
 
103
                        missing_len = BASE64_BLOCK_INPUT_SIZE - base64_block_len;
 
104
                        i_assert(size >= missing_len);
 
105
                        memcpy(base64_block + base64_block_len,
 
106
                               data, missing_len);
 
107
                        data += missing_len;
 
108
                        size -= missing_len;
 
109
                        base64_block_len = BASE64_BLOCK_INPUT_SIZE;
 
110
                }
 
111
 
 
112
                if (bstream->base64_block_pos == BASE64_BLOCKS_PER_LINE) {
 
113
                        memcpy(i_stream_alloc(stream, 2), "\r\n", 2);
 
114
                        stream->pos += 2;
 
115
                        bstream->base64_block_pos = 0;
 
116
                }
 
117
 
 
118
                dest = i_stream_alloc(stream, BASE64_BLOCK_SIZE);
 
119
                buffer_create_from_data(&buf, dest, BASE64_BLOCK_SIZE);
 
120
                base64_encode(base64_block, base64_block_len, &buf);
 
121
                stream->pos += buf.used;
 
122
                bstream->base64_block_pos++;
 
123
                bstream->base64_delayed_len = 0;
 
124
        }
 
125
 
 
126
        while (size >= BASE64_BLOCK_INPUT_SIZE) {
 
127
                if (bstream->base64_block_pos == BASE64_BLOCKS_PER_LINE) {
 
128
                        memcpy(i_stream_alloc(stream, 2), "\r\n", 2);
 
129
                        stream->pos += 2;
 
130
                        bstream->base64_block_pos = 0;
 
131
                }
 
132
 
 
133
                /* try to encode one full line of base64 blocks */
 
134
                encode_size = I_MIN(size, BASE64_BLOCKS_PER_LINE*BASE64_BLOCK_SIZE);
 
135
                if (encode_size % BASE64_BLOCK_INPUT_SIZE != 0)
 
136
                        encode_size -= encode_size % BASE64_BLOCK_INPUT_SIZE;
 
137
                encode_blocks = encode_size/BASE64_BLOCK_INPUT_SIZE;
 
138
                if (bstream->base64_block_pos + encode_blocks > BASE64_BLOCKS_PER_LINE) {
 
139
                        encode_blocks = BASE64_BLOCKS_PER_LINE -
 
140
                                bstream->base64_block_pos;
 
141
                        encode_size = encode_blocks * BASE64_BLOCK_INPUT_SIZE;
 
142
                }
 
143
 
 
144
                max_encoded_size = MAX_BASE64_ENCODED_SIZE(encode_size);
 
145
                dest = i_stream_alloc(stream, max_encoded_size);
 
146
                buffer_create_from_data(&buf, dest, max_encoded_size);
 
147
                base64_encode(data, encode_size, &buf);
 
148
                stream->pos += buf.used;
 
149
                bstream->base64_block_pos += encode_blocks;
 
150
 
 
151
                data += encode_size;
 
152
                size -= encode_size;
 
153
        }
 
154
        if (size > 0) {
 
155
                /* encode these when more data is available */
 
156
                i_assert(size < BASE64_BLOCK_INPUT_SIZE);
 
157
                memcpy(bstream->base64_delayed, data, size);
 
158
                bstream->base64_delayed_len = size;
 
159
        }
 
160
}
 
161
 
 
162
static void stream_add_hdr(struct binary_converter_istream *bstream,
 
163
                           const struct message_header_line *hdr)
 
164
{
 
165
        if (!hdr->continued) {
 
166
                stream_add_data(bstream, hdr->name, hdr->name_len);
 
167
                stream_add_data(bstream, hdr->middle, hdr->middle_len);
 
168
        }
 
169
 
 
170
        stream_add_data(bstream, hdr->value, hdr->value_len);
 
171
        if (!hdr->no_newline)
 
172
                stream_add_data(bstream, "\r\n", 2);
 
173
}
 
174
 
 
175
static ssize_t i_stream_binary_converter_read(struct istream_private *stream)
 
176
{
 
177
        /* @UNSAFE */
 
178
        struct binary_converter_istream *bstream =
 
179
                (struct binary_converter_istream *)stream;
 
180
        struct message_block block;
 
181
        size_t old_size, new_size;
 
182
 
 
183
        if (stream->pos - stream->skip >= stream->max_buffer_size)
 
184
                return -2;
 
185
 
 
186
        switch (message_parser_parse_next_block(bstream->parser, &block)) {
 
187
        case -1:
 
188
                /* done / error */
 
189
                stream->istream.eof = TRUE;
 
190
                stream->istream.stream_errno = stream->parent->stream_errno;
 
191
                return -1;
 
192
        case 0:
 
193
                /* need more data */
 
194
                return 0;
 
195
        default:
 
196
                break;
 
197
        }
 
198
 
 
199
        old_size = stream->pos - stream->skip;
 
200
 
 
201
        if (block.part != bstream->convert_part &&
 
202
            bstream->convert_part != NULL) {
 
203
                /* end of base64 encoded part */
 
204
                stream_encode_base64(bstream, "", 0);
 
205
        }
 
206
 
 
207
        if (block.hdr != NULL) {
 
208
                /* parsing a header */
 
209
                if (strcasecmp(block.hdr->name, "Content-Type") == 0)
 
210
                        bstream->content_type_seen = TRUE;
 
211
 
 
212
                if (strcasecmp(block.hdr->name, "Content-Transfer-Encoding") == 0 &&
 
213
                         !block.hdr->continued && !block.hdr->continues &&
 
214
                         block.hdr->value_len == 6 &&
 
215
                         i_memcasecmp(block.hdr->value, "binary", 6) == 0 &&
 
216
                         part_can_convert(block.part) &&
 
217
                         bstream->convert_part != block.part) {
 
218
                        /* looks like we want to convert this body part to
 
219
                           base64, but if we haven't seen Content-Type yet
 
220
                           delay the decision until we've read the rest of
 
221
                           the header */
 
222
                        i_assert(block.part != NULL);
 
223
                        bstream->convert_part = block.part;
 
224
                        bstream->base64_block_pos = 0;
 
225
                        if (!bstream->content_type_seen) {
 
226
                                i_assert(bstream->hdr_buf == NULL);
 
227
                                bstream->hdr_buf = buffer_create_dynamic(default_pool, 512);
 
228
                                stream_add_hdr(bstream, block.hdr);
 
229
                                bstream->cte_header_len = bstream->hdr_buf->used;
 
230
                        } else {
 
231
                                stream_add_data(bstream,
 
232
                                        "Content-Transfer-Encoding: base64\r\n", 35);
 
233
                        }
 
234
                } else if (block.hdr->eoh && bstream->hdr_buf != NULL) {
 
235
                        /* finish the decision about decoding */
 
236
                        stream_finish_convert_decision(bstream);
 
237
                        stream_add_data(bstream, "\r\n", 2);
 
238
                } else {
 
239
                        stream_add_hdr(bstream, block.hdr);
 
240
                }
 
241
        } else if (block.size == 0) {
 
242
                /* end of header */
 
243
                if (bstream->hdr_buf != NULL) {
 
244
                        /* message has no body */
 
245
                        bstream->convert_part = NULL;
 
246
                        stream_add_data(bstream, bstream->hdr_buf->data,
 
247
                                        bstream->hdr_buf->used);
 
248
                        buffer_free(&bstream->hdr_buf);
 
249
                }
 
250
                bstream->content_type_seen = FALSE;
 
251
        } else if (block.part == bstream->convert_part) {
 
252
                /* convert body part to base64 */
 
253
                stream_encode_base64(bstream, block.data, block.size);
 
254
        } else {
 
255
                stream_add_data(bstream, block.data, block.size);
 
256
        }
 
257
        new_size = stream->pos - stream->skip;
 
258
        if (new_size == old_size)
 
259
                return i_stream_binary_converter_read(stream);
 
260
        return new_size - old_size;
 
261
}
 
262
 
 
263
static void i_stream_binary_converter_close(struct iostream_private *stream,
 
264
                                            bool close_parent)
 
265
{
 
266
        struct binary_converter_istream *bstream =
 
267
                (struct binary_converter_istream *)stream;
 
268
        struct message_part *parts;
 
269
 
 
270
        if (bstream->parser != NULL)
 
271
                (void)message_parser_deinit(&bstream->parser, &parts);
 
272
        if (bstream->pool != NULL)
 
273
                pool_unref(&bstream->pool);
 
274
        if (close_parent)
 
275
                i_stream_close(bstream->istream.parent);
 
276
}
 
277
 
 
278
struct istream *i_stream_create_binary_converter(struct istream *input)
 
279
{
 
280
        struct binary_converter_istream *bstream;
 
281
 
 
282
        bstream = i_new(struct binary_converter_istream, 1);
 
283
        bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
 
284
 
 
285
        bstream->istream.read = i_stream_binary_converter_read;
 
286
        bstream->istream.iostream.close = i_stream_binary_converter_close;
 
287
 
 
288
        bstream->istream.istream.readable_fd = FALSE;
 
289
        bstream->istream.istream.blocking = input->blocking;
 
290
        bstream->istream.istream.seekable = FALSE;
 
291
 
 
292
        bstream->pool = pool_alloconly_create("istream binary converter", 128);
 
293
        bstream->parser = message_parser_init(bstream->pool, input, 0,
 
294
                                MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
 
295
                                MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
 
296
        return i_stream_create(&bstream->istream, input,
 
297
                               i_stream_get_fd(input));
 
298
}