1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "istream.h"
5 #include "message-parser-private.h"
6
7 static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
8 struct message_block *block_r);
9 static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
10 struct message_block *block_r);
11
preparsed_parse_eof(struct message_parser_ctx * ctx ATTR_UNUSED,struct message_block * block_r ATTR_UNUSED)12 static int preparsed_parse_eof(struct message_parser_ctx *ctx ATTR_UNUSED,
13 struct message_block *block_r ATTR_UNUSED)
14 {
15 return -1;
16 }
17
preparsed_skip_to_next(struct message_parser_ctx * ctx)18 static void preparsed_skip_to_next(struct message_parser_ctx *ctx)
19 {
20 ctx->parse_next_block = preparsed_parse_next_header_init;
21 while (ctx->part != NULL) {
22 if (ctx->part->next != NULL) {
23 ctx->part = ctx->part->next;
24 break;
25 }
26
27 /* parse epilogue of multipart parent if requested */
28 if (ctx->part->parent != NULL &&
29 (ctx->part->parent->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
30 (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) {
31 /* check for presence of epilogue */
32 uoff_t part_end = ctx->part->physical_pos +
33 ctx->part->header_size.physical_size +
34 ctx->part->body_size.physical_size;
35 uoff_t parent_end = ctx->part->parent->physical_pos +
36 ctx->part->parent->header_size.physical_size +
37 ctx->part->parent->body_size.physical_size;
38
39 if (parent_end > part_end) {
40 ctx->parse_next_block = preparsed_parse_epilogue_init;
41 break;
42 }
43 }
44 ctx->part = ctx->part->parent;
45 }
46 if (ctx->part == NULL)
47 ctx->parse_next_block = preparsed_parse_eof;
48 }
49
preparsed_parse_body_finish(struct message_parser_ctx * ctx,struct message_block * block_r)50 static int preparsed_parse_body_finish(struct message_parser_ctx *ctx,
51 struct message_block *block_r)
52 {
53 i_stream_skip(ctx->input, ctx->skip);
54 ctx->skip = 0;
55
56 preparsed_skip_to_next(ctx);
57 return ctx->parse_next_block(ctx, block_r);
58 }
59
preparsed_parse_prologue_finish(struct message_parser_ctx * ctx,struct message_block * block_r)60 static int preparsed_parse_prologue_finish(struct message_parser_ctx *ctx,
61 struct message_block *block_r)
62 {
63 i_stream_skip(ctx->input, ctx->skip);
64 ctx->skip = 0;
65
66 ctx->parse_next_block = preparsed_parse_next_header_init;
67 ctx->part = ctx->part->children;
68 return ctx->parse_next_block(ctx, block_r);
69 }
70
preparsed_parse_body_more(struct message_parser_ctx * ctx,struct message_block * block_r)71 static int preparsed_parse_body_more(struct message_parser_ctx *ctx,
72 struct message_block *block_r)
73 {
74 uoff_t end_offset = ctx->part->physical_pos +
75 ctx->part->header_size.physical_size +
76 ctx->part->body_size.physical_size;
77 bool full;
78 int ret;
79
80 if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
81 return ret;
82
83 if (ctx->input->v_offset + block_r->size >= end_offset) {
84 block_r->size = end_offset - ctx->input->v_offset;
85 ctx->parse_next_block = preparsed_parse_body_finish;
86 }
87 ctx->skip = block_r->size;
88 return 1;
89 }
90
preparsed_parse_prologue_more(struct message_parser_ctx * ctx,struct message_block * block_r)91 static int preparsed_parse_prologue_more(struct message_parser_ctx *ctx,
92 struct message_block *block_r)
93 {
94 uoff_t boundary_min_start, end_offset;
95 const unsigned char *cur;
96 bool full;
97 int ret;
98
99 i_assert(ctx->part->children != NULL);
100 end_offset = ctx->part->children->physical_pos;
101
102 if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
103 return ret;
104
105 if (ctx->input->v_offset + block_r->size >= end_offset) {
106 /* we've got the full prologue: clip off the initial boundary */
107 block_r->size = end_offset - ctx->input->v_offset;
108 cur = block_r->data + block_r->size - 1;
109
110 /* [\r]\n--boundary[\r]\n */
111 if (block_r->size < 5 || *cur != '\n') {
112 ctx->broken_reason = "Prologue boundary end not at expected position";
113 return -1;
114 }
115
116 cur--;
117 if (*cur == '\r') cur--;
118
119 /* find newline just before boundary */
120 for (; cur >= block_r->data; cur--) {
121 if (*cur == '\n') break;
122 }
123
124 if (cur[0] != '\n' || cur[1] != '-' || cur[2] != '-') {
125 ctx->broken_reason = "Prologue boundary beginning not at expected position";
126 return -1;
127 }
128
129 if (cur != block_r->data && cur[-1] == '\r') cur--;
130
131 /* clip boundary */
132 block_r->size = cur - block_r->data;
133
134 ctx->parse_next_block = preparsed_parse_prologue_finish;
135 ctx->skip = block_r->size;
136 return 1;
137 }
138
139 /* retain enough data in the stream buffer to contain initial boundary */
140 if (end_offset > BOUNDARY_END_MAX_LEN)
141 boundary_min_start = end_offset - BOUNDARY_END_MAX_LEN;
142 else
143 boundary_min_start = 0;
144
145 if (ctx->input->v_offset + block_r->size >= boundary_min_start) {
146 if (boundary_min_start <= ctx->input->v_offset)
147 return 0;
148 block_r->size = boundary_min_start - ctx->input->v_offset;
149 }
150 ctx->skip = block_r->size;
151 return 1;
152 }
153
preparsed_parse_epilogue_more(struct message_parser_ctx * ctx,struct message_block * block_r)154 static int preparsed_parse_epilogue_more(struct message_parser_ctx *ctx,
155 struct message_block *block_r)
156 {
157 uoff_t end_offset = ctx->part->physical_pos +
158 ctx->part->header_size.physical_size +
159 ctx->part->body_size.physical_size;
160 bool full;
161 int ret;
162
163 if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
164 return ret;
165
166 if (ctx->input->v_offset + block_r->size >= end_offset) {
167 block_r->size = end_offset - ctx->input->v_offset;
168 ctx->parse_next_block = preparsed_parse_body_finish;
169 }
170 ctx->skip = block_r->size;
171 return 1;
172 }
173
preparsed_parse_epilogue_boundary(struct message_parser_ctx * ctx,struct message_block * block_r)174 static int preparsed_parse_epilogue_boundary(struct message_parser_ctx *ctx,
175 struct message_block *block_r)
176 {
177 uoff_t end_offset = ctx->part->physical_pos +
178 ctx->part->header_size.physical_size +
179 ctx->part->body_size.physical_size;
180 const unsigned char *data, *cur;
181 size_t size;
182 bool full;
183 int ret;
184
185 if (end_offset - ctx->input->v_offset < 7) {
186 ctx->broken_reason = "Epilogue position is wrong";
187 return -1;
188 }
189
190 if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
191 return ret;
192
193 /* [\r]\n--boundary--[\r]\n */
194 if (block_r->size < 7) {
195 ctx->want_count = 7;
196 return 0;
197 }
198
199 data = block_r->data;
200 size = block_r->size;
201 cur = data;
202
203 if (*cur == '\r') cur++;
204
205 if (cur[0] != '\n' || cur[1] != '-' || data[2] != '-') {
206 ctx->broken_reason = "Epilogue boundary start not at expected position";
207 return -1;
208 }
209
210 /* find the end of the line */
211 cur += 3;
212 if ((cur = memchr(cur, '\n', size - (cur-data))) == NULL) {
213 if (end_offset < ctx->input->v_offset + size) {
214 ctx->broken_reason = "Epilogue boundary end not at expected position";
215 return -1;
216 } else if (ctx->input->v_offset + size < end_offset &&
217 size < BOUNDARY_END_MAX_LEN &&
218 !ctx->input->eof && !full) {
219 ctx->want_count = BOUNDARY_END_MAX_LEN;
220 return 0;
221 }
222 }
223
224 block_r->size = 0;
225 ctx->parse_next_block = preparsed_parse_epilogue_more;
226 ctx->skip = cur - data + 1;
227 return 0;
228 }
229
preparsed_parse_body_init(struct message_parser_ctx * ctx,struct message_block * block_r)230 static int preparsed_parse_body_init(struct message_parser_ctx *ctx,
231 struct message_block *block_r)
232 {
233 uoff_t offset = ctx->part->physical_pos +
234 ctx->part->header_size.physical_size;
235
236 if (offset < ctx->input->v_offset) {
237 /* header was actually larger than the cached size suggested */
238 ctx->broken_reason = "Header larger than its cached size";
239 return -1;
240 }
241 i_stream_skip(ctx->input, offset - ctx->input->v_offset);
242
243 /* multipart messages may begin with --boundary--, which makes them
244 not have any children. */
245 if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
246 ctx->part->children == NULL)
247 ctx->parse_next_block = preparsed_parse_body_more;
248 else
249 ctx->parse_next_block = preparsed_parse_prologue_more;
250 return ctx->parse_next_block(ctx, block_r);
251 }
252
preparsed_parse_epilogue_init(struct message_parser_ctx * ctx,struct message_block * block_r)253 static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx,
254 struct message_block *block_r)
255 {
256 uoff_t offset = ctx->part->physical_pos +
257 ctx->part->header_size.physical_size +
258 ctx->part->body_size.physical_size;
259
260 ctx->part = ctx->part->parent;
261
262 if (offset < ctx->input->v_offset) {
263 /* last child was actually larger than the cached size
264 suggested */
265 ctx->broken_reason = "Part larger than its cached size";
266 return -1;
267 }
268 i_stream_skip(ctx->input, offset - ctx->input->v_offset);
269
270 ctx->parse_next_block = preparsed_parse_epilogue_boundary;
271 return ctx->parse_next_block(ctx, block_r);
272 }
273
preparsed_parse_finish_header(struct message_parser_ctx * ctx,struct message_block * block_r)274 static int preparsed_parse_finish_header(struct message_parser_ctx *ctx,
275 struct message_block *block_r)
276 {
277 if (ctx->part->children != NULL) {
278 if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
279 (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0)
280 ctx->parse_next_block = preparsed_parse_body_init;
281 else {
282 ctx->parse_next_block = preparsed_parse_next_header_init;
283 ctx->part = ctx->part->children;
284 }
285 } else if ((ctx->flags & MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK) == 0) {
286 ctx->parse_next_block = preparsed_parse_body_init;
287 } else {
288 preparsed_skip_to_next(ctx);
289 }
290 return ctx->parse_next_block(ctx, block_r);
291 }
292
preparsed_parse_next_header(struct message_parser_ctx * ctx,struct message_block * block_r)293 static int preparsed_parse_next_header(struct message_parser_ctx *ctx,
294 struct message_block *block_r)
295 {
296 struct message_header_line *hdr;
297 int ret;
298
299 ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr);
300 if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) {
301 ctx->want_count = i_stream_get_data_size(ctx->input) + 1;
302 return ret;
303 }
304
305 if (hdr != NULL) {
306 block_r->hdr = hdr;
307 block_r->size = 0;
308 return 1;
309 }
310 message_parse_header_deinit(&ctx->hdr_parser_ctx);
311
312 ctx->parse_next_block = preparsed_parse_finish_header;
313
314 /* return empty block as end of headers */
315 block_r->hdr = NULL;
316 block_r->size = 0;
317
318 i_assert(ctx->skip == 0);
319 if (ctx->input->v_offset != ctx->part->physical_pos +
320 ctx->part->header_size.physical_size) {
321 ctx->broken_reason = "Cached header size mismatch";
322 return -1;
323 }
324 return 1;
325 }
326
preparsed_parse_next_header_init(struct message_parser_ctx * ctx,struct message_block * block_r)327 static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
328 struct message_block *block_r)
329 {
330 struct istream *hdr_input;
331
332 i_assert(ctx->hdr_parser_ctx == NULL);
333
334 i_assert(ctx->part->physical_pos >= ctx->input->v_offset);
335 i_stream_skip(ctx->input, ctx->part->physical_pos -
336 ctx->input->v_offset);
337
338 /* the header may become truncated by --boundaries. limit the header
339 stream's size to what it's supposed to be to avoid duplicating (and
340 keeping in sync!) all the same complicated logic as in
341 parse_next_header(). */
342 hdr_input = i_stream_create_limit(ctx->input, ctx->part->header_size.physical_size);
343 ctx->hdr_parser_ctx =
344 message_parse_header_init(hdr_input, NULL, ctx->hdr_flags);
345 i_stream_unref(&hdr_input);
346
347 ctx->parse_next_block = preparsed_parse_next_header;
348 return preparsed_parse_next_header(ctx, block_r);
349 }
350
351 struct message_parser_ctx *
message_parser_init_from_parts(struct message_part * parts,struct istream * input,const struct message_parser_settings * set)352 message_parser_init_from_parts(struct message_part *parts,
353 struct istream *input,
354 const struct message_parser_settings *set)
355 {
356 struct message_parser_ctx *ctx;
357
358 i_assert(parts != NULL);
359
360 ctx = message_parser_init_int(input, set);
361 ctx->preparsed = TRUE;
362 ctx->parts = ctx->part = parts;
363 ctx->parse_next_block = preparsed_parse_next_header_init;
364 return ctx;
365 }
366