Line data Source code
1 : /* mime-parser.c - Parse MIME structures (high level rfc822 parser).
2 : * Copyright (C) 2016 g10 Code GmbH
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * GnuPG is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * GnuPG is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include <config.h>
21 : #include <stdio.h>
22 : #include <stdlib.h>
23 : #include <string.h>
24 :
25 : #include "util.h"
26 : #include "rfc822parse.h"
27 : #include "mime-parser.h"
28 :
29 :
30 : enum pgpmime_states
31 : {
32 : PGPMIME_NONE = 0,
33 : PGPMIME_WAIT_ENCVERSION,
34 : PGPMIME_IN_ENCVERSION,
35 : PGPMIME_WAIT_ENCDATA,
36 : PGPMIME_IN_ENCDATA,
37 : PGPMIME_GOT_ENCDATA,
38 : PGPMIME_WAIT_SIGNEDDATA,
39 : PGPMIME_IN_SIGNEDDATA,
40 : PGPMIME_WAIT_SIGNATURE,
41 : PGPMIME_IN_SIGNATURE,
42 : PGPMIME_GOT_SIGNATURE,
43 : PGPMIME_INVALID
44 : };
45 :
46 :
47 : /* Definition of the mime parser object. */
48 : struct mime_parser_context_s
49 : {
50 : void *cookie; /* Cookie passed to all callbacks. */
51 :
52 : /* The callback to announce a new part. */
53 : gpg_error_t (*new_part) (void *cookie,
54 : const char *mediatype,
55 : const char *mediasubtype);
56 : /* The callback to return data of a part. */
57 : gpg_error_t (*part_data) (void *cookie,
58 : const void *data,
59 : size_t datalen);
60 : /* The callback to collect encrypted data. */
61 : gpg_error_t (*collect_encrypted) (void *cookie, const char *data);
62 : /* The callback to collect signed data. */
63 : gpg_error_t (*collect_signeddata) (void *cookie, const char *data);
64 : /* The callback to collect a signature. */
65 : gpg_error_t (*collect_signature) (void *cookie, const char *data);
66 :
67 : /* The RFC822 parser context is stored here during callbacks. */
68 : rfc822parse_t msg;
69 :
70 : /* Helper to convey error codes from user callbacks. */
71 : gpg_error_t err;
72 :
73 : int nesting_level; /* The current nesting level. */
74 : int hashing_at_level; /* The nesting level at which we are hashing. */
75 : enum pgpmime_states pgpmime; /* Current PGP/MIME state. */
76 : unsigned int delay_hashing:1;/* Helper for PGPMIME_IN_SIGNEDDATA. */
77 : unsigned int want_part:1; /* Return the current part. */
78 : unsigned int decode_part:2; /* Decode the part. 1 = QP, 2 = Base64. */
79 :
80 : unsigned int verbose:1; /* Enable verbose mode. */
81 : unsigned int debug:1; /* Enable debug mode. */
82 :
83 : /* Flags to help with debug output. */
84 : struct {
85 : unsigned int n_skip; /* Skip showing these number of lines. */
86 : unsigned int header:1; /* Show the header lines. */
87 : unsigned int data:1; /* Show the data lines. */
88 : unsigned int as_note:1; /* Show the next data line as a note. */
89 : unsigned int boundary : 1;
90 : } show;
91 :
92 : struct b64state *b64state; /* NULL or malloced Base64 decoder state. */
93 :
94 : /* A buffer for reading a mail line, */
95 : char line[5000];
96 : };
97 :
98 :
99 : /* Print the event received by the parser for debugging. */
100 : static void
101 0 : show_message_parser_event (rfc822parse_event_t event)
102 : {
103 : const char *s;
104 :
105 0 : switch (event)
106 : {
107 0 : case RFC822PARSE_OPEN: s= "Open"; break;
108 0 : case RFC822PARSE_CLOSE: s= "Close"; break;
109 0 : case RFC822PARSE_CANCEL: s= "Cancel"; break;
110 0 : case RFC822PARSE_T2BODY: s= "T2Body"; break;
111 0 : case RFC822PARSE_FINISH: s= "Finish"; break;
112 0 : case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
113 0 : case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
114 0 : case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
115 0 : case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
116 0 : case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
117 0 : case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
118 0 : case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
119 0 : case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
120 0 : default: s= "[unknown event]"; break;
121 : }
122 0 : log_debug ("*** RFC822 event %s\n", s);
123 0 : }
124 :
125 :
126 : /* Do in-place decoding of quoted-printable data of LENGTH in BUFFER.
127 : Returns the new length of the buffer and stores true at R_SLBRK if
128 : the line ended with a soft line break; false is stored if not.
129 : This function asssumes that a complete line is passed in
130 : buffer. */
131 : static size_t
132 0 : qp_decode (char *buffer, size_t length, int *r_slbrk)
133 : {
134 : char *d, *s;
135 :
136 0 : if (r_slbrk)
137 0 : *r_slbrk = 0;
138 :
139 : /* Fixme: We should remove trailing white space first. */
140 0 : for (s=d=buffer; length; length--)
141 : {
142 0 : if (*s == '=')
143 : {
144 0 : if (length > 2 && hexdigitp (s+1) && hexdigitp (s+2))
145 : {
146 0 : s++;
147 0 : *(unsigned char*)d++ = xtoi_2 (s);
148 0 : s += 2;
149 0 : length -= 2;
150 : }
151 0 : else if (length > 2 && s[1] == '\r' && s[2] == '\n')
152 : {
153 : /* Soft line break. */
154 0 : s += 3;
155 0 : length -= 2;
156 0 : if (r_slbrk && length == 1)
157 0 : *r_slbrk = 1;
158 : }
159 0 : else if (length > 1 && s[1] == '\n')
160 : {
161 : /* Soft line break with only a Unix line terminator. */
162 0 : s += 2;
163 0 : length -= 1;
164 0 : if (r_slbrk && length == 1)
165 0 : *r_slbrk = 1;
166 : }
167 0 : else if (length == 1)
168 : {
169 : /* Soft line break at the end of the line. */
170 0 : s += 1;
171 0 : if (r_slbrk)
172 0 : *r_slbrk = 1;
173 : }
174 : else
175 0 : *d++ = *s++;
176 : }
177 : else
178 0 : *d++ = *s++;
179 : }
180 :
181 0 : return d - buffer;
182 : }
183 :
184 :
185 : /* This function is called by parse_mail to communicate events. This
186 : * callback communicates with the caller using a structure passed in
187 : * OPAQUE. Should return 0 on success or set ERRNO and return -1. */
188 : static int
189 0 : parse_message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
190 : {
191 0 : mime_parser_t ctx = opaque;
192 : const char *s;
193 0 : int rc = 0;
194 :
195 : /* Make the RFC822 parser context availabale for callbacks. */
196 0 : ctx->msg = msg;
197 :
198 0 : if (ctx->debug)
199 0 : show_message_parser_event (event);
200 :
201 0 : if (event == RFC822PARSE_BEGIN_HEADER || event == RFC822PARSE_T2BODY)
202 : {
203 : /* We need to check here whether to start collecting signed data
204 : * because attachments might come without header lines and thus
205 : * we won't see the BEGIN_HEADER event. */
206 0 : if (ctx->pgpmime == PGPMIME_WAIT_SIGNEDDATA)
207 : {
208 0 : if (ctx->debug)
209 0 : log_debug ("begin_hash\n");
210 0 : ctx->hashing_at_level = ctx->nesting_level;
211 0 : ctx->pgpmime = PGPMIME_IN_SIGNEDDATA;
212 0 : ctx->delay_hashing = 0;
213 : }
214 : }
215 :
216 0 : if (event == RFC822PARSE_OPEN)
217 : {
218 : /* Initialize for a new message. */
219 0 : ctx->show.header = 1;
220 : }
221 0 : else if (event == RFC822PARSE_T2BODY)
222 : {
223 : rfc822parse_field_t field;
224 :
225 0 : ctx->want_part = 0;
226 0 : ctx->decode_part = 0;
227 0 : field = rfc822parse_parse_field (msg, "Content-Type", -1);
228 0 : if (field)
229 : {
230 : const char *s1, *s2;
231 :
232 0 : s1 = rfc822parse_query_media_type (field, &s2);
233 0 : if (s1)
234 : {
235 0 : if (ctx->verbose)
236 0 : log_debug ("h media: %*s%s %s\n",
237 0 : ctx->nesting_level*2, "", s1, s2);
238 0 : if (ctx->pgpmime == PGPMIME_WAIT_ENCVERSION)
239 : {
240 0 : if (!strcmp (s1, "application")
241 0 : && !strcmp (s2, "pgp-encrypted"))
242 : {
243 0 : if (ctx->debug)
244 0 : log_debug ("c begin_encversion\n");
245 0 : ctx->pgpmime = PGPMIME_IN_ENCVERSION;
246 : }
247 : else
248 : {
249 0 : log_error ("invalid PGP/MIME structure;"
250 : " expected '%s', got '%s/%s'\n",
251 : "application/pgp-encrypted", s1, s2);
252 0 : ctx->pgpmime = PGPMIME_INVALID;
253 : }
254 : }
255 0 : else if (ctx->pgpmime == PGPMIME_WAIT_ENCDATA)
256 : {
257 0 : if (!strcmp (s1, "application")
258 0 : && !strcmp (s2, "octet-stream"))
259 : {
260 0 : if (ctx->debug)
261 0 : log_debug ("c begin_encdata\n");
262 0 : ctx->pgpmime = PGPMIME_IN_ENCDATA;
263 : }
264 : else
265 : {
266 0 : log_error ("invalid PGP/MIME structure;"
267 : " expected '%s', got '%s/%s'\n",
268 : "application/octet-stream", s1, s2);
269 0 : ctx->pgpmime = PGPMIME_INVALID;
270 : }
271 : }
272 0 : else if (ctx->pgpmime == PGPMIME_WAIT_SIGNATURE)
273 : {
274 0 : if (!strcmp (s1, "application")
275 0 : && !strcmp (s2, "pgp-signature"))
276 : {
277 0 : if (ctx->debug)
278 0 : log_debug ("c begin_signature\n");
279 0 : ctx->pgpmime = PGPMIME_IN_SIGNATURE;
280 : }
281 : else
282 : {
283 0 : log_error ("invalid PGP/MIME structure;"
284 : " expected '%s', got '%s/%s'\n",
285 : "application/pgp-signature", s1, s2);
286 0 : ctx->pgpmime = PGPMIME_INVALID;
287 : }
288 : }
289 0 : else if (!strcmp (s1, "multipart")
290 0 : && !strcmp (s2, "encrypted"))
291 : {
292 0 : s = rfc822parse_query_parameter (field, "protocol", 0);
293 0 : if (s)
294 : {
295 0 : if (ctx->debug)
296 0 : log_debug ("h encrypted.protocol: %s\n", s);
297 0 : if (!strcmp (s, "application/pgp-encrypted"))
298 : {
299 0 : if (ctx->pgpmime)
300 0 : log_error ("note: "
301 : "ignoring nested PGP/MIME signature\n");
302 : else
303 0 : ctx->pgpmime = PGPMIME_WAIT_ENCVERSION;
304 : }
305 0 : else if (ctx->verbose)
306 0 : log_debug ("# this protocol is not supported\n");
307 : }
308 : }
309 0 : else if (!strcmp (s1, "multipart")
310 0 : && !strcmp (s2, "signed"))
311 : {
312 0 : s = rfc822parse_query_parameter (field, "protocol", 1);
313 0 : if (s)
314 : {
315 0 : if (ctx->debug)
316 0 : log_debug ("h signed.protocol: %s\n", s);
317 0 : if (!strcmp (s, "application/pgp-signature"))
318 : {
319 0 : if (ctx->pgpmime)
320 0 : log_error ("note: "
321 : "ignoring nested PGP/MIME signature\n");
322 : else
323 0 : ctx->pgpmime = PGPMIME_WAIT_SIGNEDDATA;
324 : }
325 0 : else if (ctx->verbose)
326 0 : log_debug ("# this protocol is not supported\n");
327 : }
328 : }
329 0 : else if (ctx->new_part)
330 : {
331 0 : ctx->err = ctx->new_part (ctx->cookie, s1, s2);
332 0 : if (!ctx->err)
333 0 : ctx->want_part = 1;
334 0 : else if (gpg_err_code (ctx->err) == GPG_ERR_FALSE)
335 0 : ctx->err = 0;
336 0 : else if (gpg_err_code (ctx->err) == GPG_ERR_TRUE)
337 : {
338 0 : ctx->want_part = ctx->decode_part = 1;
339 0 : ctx->err = 0;
340 : }
341 : }
342 : }
343 : else
344 : {
345 0 : if (ctx->debug)
346 0 : log_debug ("h media: %*s none\n", ctx->nesting_level*2, "");
347 0 : if (ctx->new_part)
348 : {
349 0 : ctx->err = ctx->new_part (ctx->cookie, "", "");
350 0 : if (!ctx->err)
351 0 : ctx->want_part = 1;
352 0 : else if (gpg_err_code (ctx->err) == GPG_ERR_FALSE)
353 0 : ctx->err = 0;
354 0 : else if (gpg_err_code (ctx->err) == GPG_ERR_TRUE)
355 : {
356 0 : ctx->want_part = ctx->decode_part = 1;
357 0 : ctx->err = 0;
358 : }
359 : }
360 : }
361 :
362 0 : rfc822parse_release_field (field);
363 : }
364 : else
365 : {
366 0 : if (ctx->verbose)
367 0 : log_debug ("h media: %*stext plain [assumed]\n",
368 0 : ctx->nesting_level*2, "");
369 0 : if (ctx->new_part)
370 : {
371 0 : ctx->err = ctx->new_part (ctx->cookie, "text", "plain");
372 0 : if (!ctx->err)
373 0 : ctx->want_part = 1;
374 0 : else if (gpg_err_code (ctx->err) == GPG_ERR_FALSE)
375 0 : ctx->err = 0;
376 0 : else if (gpg_err_code (ctx->err) == GPG_ERR_TRUE)
377 : {
378 0 : ctx->want_part = ctx->decode_part = 1;
379 0 : ctx->err = 0;
380 : }
381 : }
382 : }
383 :
384 : /* Figure out the encoding if needed. */
385 0 : if (ctx->decode_part)
386 : {
387 : char *value;
388 : size_t valueoff;
389 :
390 0 : ctx->decode_part = 0; /* Fallback for unknown encoding. */
391 0 : value = rfc822parse_get_field (msg, "Content-Transfer-Encoding", -1,
392 : &valueoff);
393 0 : if (value)
394 : {
395 0 : if (!stricmp (value+valueoff, "quoted-printable"))
396 0 : ctx->decode_part = 1;
397 0 : else if (!stricmp (value+valueoff, "base64"))
398 : {
399 0 : ctx->decode_part = 2;
400 0 : if (ctx->b64state)
401 0 : b64dec_finish (ctx->b64state); /* Reuse state. */
402 : else
403 : {
404 0 : ctx->b64state = xtrymalloc (sizeof *ctx->b64state);
405 0 : if (!ctx->b64state)
406 0 : rc = gpg_error_from_syserror ();
407 : }
408 0 : if (!rc)
409 0 : rc = b64dec_start (ctx->b64state, NULL);
410 : }
411 0 : free (value); /* Right, we need a plain free. */
412 : }
413 : }
414 :
415 0 : ctx->show.header = 0;
416 0 : ctx->show.data = 1;
417 0 : ctx->show.n_skip = 1;
418 : }
419 0 : else if (event == RFC822PARSE_PREAMBLE)
420 0 : ctx->show.as_note = 1;
421 0 : else if (event == RFC822PARSE_LEVEL_DOWN)
422 : {
423 0 : if (ctx->debug)
424 0 : log_debug ("b down\n");
425 0 : ctx->nesting_level++;
426 : }
427 0 : else if (event == RFC822PARSE_LEVEL_UP)
428 : {
429 0 : if (ctx->debug)
430 0 : log_debug ("b up\n");
431 0 : if (ctx->nesting_level)
432 0 : ctx->nesting_level--;
433 : else
434 0 : log_error ("invalid structure (bad nesting level)\n");
435 : }
436 0 : else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY)
437 : {
438 0 : ctx->show.data = 0;
439 0 : ctx->show.boundary = 1;
440 0 : if (event == RFC822PARSE_BOUNDARY)
441 : {
442 0 : ctx->show.header = 1;
443 0 : ctx->show.n_skip = 1;
444 0 : if (ctx->debug)
445 0 : log_debug ("b part\n");
446 : }
447 0 : else if (ctx->debug)
448 0 : log_debug ("b last\n");
449 :
450 0 : if (ctx->pgpmime == PGPMIME_IN_ENCDATA)
451 : {
452 0 : if (ctx->debug)
453 0 : log_debug ("c end_encdata\n");
454 0 : ctx->pgpmime = PGPMIME_GOT_ENCDATA;
455 : /* FIXME: We should assert (event == LAST_BOUNDARY). */
456 : }
457 0 : else if (ctx->pgpmime == PGPMIME_IN_SIGNEDDATA
458 0 : && ctx->nesting_level == ctx->hashing_at_level)
459 : {
460 0 : if (ctx->debug)
461 0 : log_debug ("c end_hash\n");
462 0 : ctx->pgpmime = PGPMIME_WAIT_SIGNATURE;
463 0 : if (ctx->collect_signeddata)
464 0 : ctx->err = ctx->collect_signeddata (ctx->cookie, NULL);
465 : }
466 0 : else if (ctx->pgpmime == PGPMIME_IN_SIGNATURE)
467 : {
468 0 : if (ctx->debug)
469 0 : log_debug ("c end_signature\n");
470 0 : ctx->pgpmime = PGPMIME_GOT_SIGNATURE;
471 : /* FIXME: We should assert (event == LAST_BOUNDARY). */
472 : }
473 0 : else if (ctx->want_part)
474 : {
475 0 : if (ctx->part_data)
476 : {
477 : /* FIXME: We may need to flush things. */
478 0 : ctx->err = ctx->part_data (ctx->cookie, NULL, 0);
479 : }
480 0 : ctx->want_part = 0;
481 : }
482 : }
483 :
484 0 : ctx->msg = NULL;
485 :
486 0 : return rc;
487 : }
488 :
489 :
490 : /* Create a new mime parser object. COOKIE is a values which will be
491 : * used as first argument for all callbacks registered with this
492 : * parser object. */
493 : gpg_error_t
494 0 : mime_parser_new (mime_parser_t *r_parser, void *cookie)
495 : {
496 : mime_parser_t ctx;
497 :
498 0 : *r_parser = NULL;
499 :
500 0 : ctx = xtrycalloc (1, sizeof *ctx);
501 0 : if (!ctx)
502 0 : return gpg_error_from_syserror ();
503 0 : ctx->cookie = cookie;
504 :
505 0 : *r_parser = ctx;
506 0 : return 0;
507 : }
508 :
509 :
510 : /* Release a mime parser object. */
511 : void
512 0 : mime_parser_release (mime_parser_t ctx)
513 : {
514 0 : if (!ctx)
515 0 : return;
516 :
517 0 : if (ctx->b64state)
518 : {
519 0 : b64dec_finish (ctx->b64state);
520 0 : xfree (ctx->b64state);
521 : }
522 0 : xfree (ctx);
523 : }
524 :
525 :
526 : /* Set verbose and debug mode. */
527 : void
528 0 : mime_parser_set_verbose (mime_parser_t ctx, int level)
529 : {
530 0 : if (!level)
531 : {
532 0 : ctx->verbose = 0;
533 0 : ctx->debug = 0;
534 : }
535 : else
536 : {
537 0 : ctx->verbose = 1;
538 0 : if (level > 10)
539 0 : ctx->debug = 1;
540 : }
541 0 : }
542 :
543 :
544 : /* Set the callback used to announce a new part. It will be called
545 : * with the media type and media subtype of the part. If no
546 : * Content-type header was given both values are the empty string.
547 : * The callback should return 0 on success or an error code. The
548 : * error code GPG_ERR_FALSE indicates that the caller is not
549 : * interested in the part and data shall not be returned via a
550 : * registered part_data callback. The error code GPG_ERR_TRUE
551 : * indicates that the parts shall be redurned in decoded format
552 : * (i.e. base64 or QP encoding is removed). */
553 : void
554 0 : mime_parser_set_new_part (mime_parser_t ctx,
555 : gpg_error_t (*fnc) (void *cookie,
556 : const char *mediatype,
557 : const char *mediasubtype))
558 : {
559 0 : ctx->new_part = fnc;
560 0 : }
561 :
562 :
563 : /* Set the callback used to return the data of a part to the caller.
564 : * The end of the part is indicated by passing NUL for DATA. */
565 : void
566 0 : mime_parser_set_part_data (mime_parser_t ctx,
567 : gpg_error_t (*fnc) (void *cookie,
568 : const void *data,
569 : size_t datalen))
570 : {
571 0 : ctx->part_data = fnc;
572 0 : }
573 :
574 :
575 : /* Set the callback to collect encrypted data. A NULL passed to the
576 : * callback indicates the end of the encrypted data; the callback may
577 : * then decrypt the collected data. */
578 : void
579 0 : mime_parser_set_collect_encrypted (mime_parser_t ctx,
580 : gpg_error_t (*fnc) (void *cookie,
581 : const char *data))
582 : {
583 0 : ctx->collect_encrypted = fnc;
584 0 : }
585 :
586 :
587 : /* Set the callback to collect signed data. A NULL passed to the
588 : * callback indicates the end of the signed data. */
589 : void
590 0 : mime_parser_set_collect_signeddata (mime_parser_t ctx,
591 : gpg_error_t (*fnc) (void *cookie,
592 : const char *data))
593 : {
594 0 : ctx->collect_signeddata = fnc;
595 0 : }
596 :
597 :
598 : /* Set the callback to collect the signature. A NULL passed to the
599 : * callback indicates the end of the signature; the callback may the
600 : * verify the signature. */
601 : void
602 0 : mime_parser_set_collect_signature (mime_parser_t ctx,
603 : gpg_error_t (*fnc) (void *cookie,
604 : const char *data))
605 : {
606 0 : ctx->collect_signature = fnc;
607 0 : }
608 :
609 :
610 : /* Return the RFC888 parser context. This is only available inside a
611 : * callback. */
612 : rfc822parse_t
613 0 : mime_parser_rfc822parser (mime_parser_t ctx)
614 : {
615 0 : return ctx->msg;
616 : }
617 :
618 :
619 : /* Helper for mime_parser_parse. */
620 : static gpg_error_t
621 0 : process_part_data (mime_parser_t ctx, char *line, size_t *length)
622 : {
623 : gpg_error_t err;
624 : size_t nbytes;
625 :
626 0 : if (!ctx->want_part)
627 0 : return 0;
628 0 : if (!ctx->part_data)
629 0 : return 0;
630 :
631 0 : if (ctx->decode_part == 1)
632 : {
633 0 : *length = qp_decode (line, *length, NULL);
634 : }
635 0 : else if (ctx->decode_part == 2)
636 : {
637 0 : log_assert (ctx->b64state);
638 0 : err = b64dec_proc (ctx->b64state, line, *length, &nbytes);
639 0 : if (err)
640 0 : return err;
641 0 : *length = nbytes;
642 : }
643 :
644 0 : return ctx->part_data (ctx->cookie, line, *length);
645 : }
646 :
647 :
648 : /* Read and parse a message from FP and call the appropriate
649 : * callbacks. */
650 : gpg_error_t
651 0 : mime_parser_parse (mime_parser_t ctx, estream_t fp)
652 : {
653 : gpg_error_t err;
654 0 : rfc822parse_t msg = NULL;
655 0 : unsigned int lineno = 0;
656 : size_t length;
657 : char *line;
658 :
659 0 : line = ctx->line;
660 :
661 0 : msg = rfc822parse_open (parse_message_cb, ctx);
662 0 : if (!msg)
663 : {
664 0 : err = gpg_error_from_syserror ();
665 0 : log_error ("can't open mail parser: %s", gpg_strerror (err));
666 0 : goto leave;
667 : }
668 :
669 : /* Fixme: We should not use fgets because it can't cope with
670 : embedded nul characters. */
671 0 : while (es_fgets (ctx->line, sizeof (ctx->line), fp))
672 : {
673 0 : lineno++;
674 0 : if (lineno == 1 && !strncmp (line, "From ", 5))
675 0 : continue; /* We better ignore a leading From line. */
676 :
677 0 : length = strlen (line);
678 0 : if (length && line[length - 1] == '\n')
679 0 : line[--length] = 0;
680 : else
681 0 : log_error ("mail parser detected too long or"
682 : " non terminated last line (lnr=%u)\n", lineno);
683 0 : if (length && line[length - 1] == '\r')
684 0 : line[--length] = 0;
685 :
686 0 : ctx->err = 0;
687 0 : if (rfc822parse_insert (msg, line, length))
688 : {
689 0 : err = gpg_error_from_syserror ();
690 0 : log_error ("mail parser failed: %s", gpg_strerror (err));
691 0 : goto leave;
692 : }
693 0 : if (ctx->err)
694 : {
695 : /* Error from a callback detected. */
696 0 : err = ctx->err;
697 0 : goto leave;
698 : }
699 :
700 :
701 : /* Debug output. Note that the boundary is shown before n_skip
702 : * is evaluated. */
703 0 : if (ctx->show.boundary)
704 : {
705 0 : if (ctx->debug)
706 0 : log_debug ("# Boundary: %s\n", line);
707 0 : ctx->show.boundary = 0;
708 : }
709 0 : if (ctx->show.n_skip)
710 0 : ctx->show.n_skip--;
711 0 : else if (ctx->show.data)
712 : {
713 0 : if (ctx->show.as_note)
714 : {
715 0 : if (ctx->verbose)
716 0 : log_debug ("# Note: %s\n", line);
717 0 : ctx->show.as_note = 0;
718 : }
719 0 : else if (ctx->debug)
720 0 : log_debug ("# Data: %s\n", line);
721 : }
722 0 : else if (ctx->show.header && ctx->verbose)
723 0 : log_debug ("# Header: %s\n", line);
724 :
725 0 : if (ctx->pgpmime == PGPMIME_IN_ENCVERSION)
726 : {
727 0 : trim_trailing_spaces (line);
728 0 : if (!*line)
729 : ; /* Skip empty lines. */
730 0 : else if (!strcmp (line, "Version: 1"))
731 0 : ctx->pgpmime = PGPMIME_WAIT_ENCDATA;
732 : else
733 : {
734 0 : log_error ("invalid PGP/MIME structure;"
735 : " garbage in pgp-encrypted part ('%s')\n", line);
736 0 : ctx->pgpmime = PGPMIME_INVALID;
737 : }
738 : }
739 0 : else if (ctx->pgpmime == PGPMIME_IN_ENCDATA)
740 : {
741 0 : if (ctx->collect_encrypted)
742 : {
743 0 : err = ctx->collect_encrypted (ctx->cookie, line);
744 0 : if (!err)
745 0 : err = ctx->collect_encrypted (ctx->cookie, "\r\n");
746 0 : if (err)
747 0 : goto leave;
748 : }
749 : }
750 0 : else if (ctx->pgpmime == PGPMIME_GOT_ENCDATA)
751 : {
752 0 : ctx->pgpmime = PGPMIME_NONE;
753 0 : if (ctx->collect_encrypted)
754 0 : ctx->collect_encrypted (ctx->cookie, NULL);
755 : }
756 0 : else if (ctx->pgpmime == PGPMIME_IN_SIGNEDDATA)
757 : {
758 : /* If we are processing signed data, store the signed data.
759 : * We need to delay the hashing of the CR/LF because the
760 : * last line ending belongs to the next boundary. This is
761 : * the reason why we can't use the PGPMIME state as a
762 : * condition. */
763 0 : if (ctx->debug)
764 0 : log_debug ("# hashing %s'%s'\n",
765 0 : ctx->delay_hashing? "CR,LF+":"", line);
766 0 : if (ctx->collect_signeddata)
767 : {
768 0 : if (ctx->delay_hashing)
769 0 : ctx->collect_signeddata (ctx->cookie, "\r\n");
770 0 : ctx->collect_signeddata (ctx->cookie, line);
771 : }
772 0 : ctx->delay_hashing = 1;
773 :
774 0 : err = process_part_data (ctx, line, &length);
775 0 : if (err)
776 0 : goto leave;
777 : }
778 0 : else if (ctx->pgpmime == PGPMIME_IN_SIGNATURE)
779 : {
780 0 : if (ctx->collect_signeddata)
781 : {
782 0 : ctx->collect_signature (ctx->cookie, line);
783 0 : ctx->collect_signature (ctx->cookie, "\r\n");
784 : }
785 : }
786 0 : else if (ctx->pgpmime == PGPMIME_GOT_SIGNATURE)
787 : {
788 0 : ctx->pgpmime = PGPMIME_NONE;
789 0 : if (ctx->collect_signeddata)
790 0 : ctx->collect_signature (ctx->cookie, NULL);
791 : }
792 : else
793 : {
794 0 : err = process_part_data (ctx, line, &length);
795 0 : if (err)
796 0 : goto leave;
797 : }
798 : }
799 :
800 0 : rfc822parse_close (msg);
801 0 : msg = NULL;
802 0 : err = 0;
803 :
804 : leave:
805 0 : rfc822parse_cancel (msg);
806 0 : return err;
807 : }
|