Line data Source code
1 : /* gpgparsemail.c - Standalone crypto mail parser
2 : * Copyright (C) 2004, 2007 Free Software Foundation, Inc.
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 <http://www.gnu.org/licenses/>.
18 : */
19 :
20 :
21 : /* This utility prints an RFC822, possible MIME structured, message
22 : in an annotated format with the first column having an indicator
23 : for the content of the line. Several options are available to
24 : scrutinize the message. S/MIME and OpenPGP support is included. */
25 :
26 : #ifdef HAVE_CONFIG_H
27 : #include <config.h>
28 : #endif
29 :
30 : #include <stdio.h>
31 : #include <stdlib.h>
32 : #include <stddef.h>
33 : #include <string.h>
34 : #include <errno.h>
35 : #include <stdarg.h>
36 : #include <assert.h>
37 : #include <time.h>
38 : #include <signal.h>
39 : #include <unistd.h>
40 : #include <fcntl.h>
41 : #include <sys/wait.h>
42 :
43 : #include "rfc822parse.h"
44 :
45 :
46 : #define PGM "gpgparsemail"
47 :
48 : /* Option flags. */
49 : static int verbose;
50 : static int debug;
51 : static int opt_crypto; /* Decrypt or verify messages. */
52 : static int opt_no_header; /* Don't output the header lines. */
53 :
54 : /* Structure used to communicate with the parser callback. */
55 : struct parse_info_s {
56 : int show_header; /* Show the header lines. */
57 : int show_data; /* Show the data lines. */
58 : unsigned int skip_show; /* Temporary disable above for these
59 : number of lines. */
60 : int show_data_as_note; /* The next data line should be shown
61 : as a note. */
62 : int show_boundary;
63 : int nesting_level;
64 :
65 : int is_pkcs7; /* Old style S/MIME message. */
66 :
67 : int smfm_state; /* State of PGP/MIME or S/MIME parsing. */
68 : int is_smime; /* This is S/MIME and not PGP/MIME. */
69 :
70 : const char *signing_protocol;
71 : const char *signing_protocol_2; /* there are two ways to present
72 : PKCS7 */
73 : int hashing_level; /* The nesting level we are hashing. */
74 : int hashing;
75 : FILE *hash_file;
76 :
77 : FILE *sig_file; /* Signature part with MIME or full
78 : pkcs7 data if IS_PCKS7 is set. */
79 : int verify_now; /* Flag set when all signature data is
80 : available. */
81 : };
82 :
83 :
84 : /* Print diagnostic message and exit with failure. */
85 : static void
86 0 : die (const char *format, ...)
87 : {
88 : va_list arg_ptr;
89 :
90 0 : fflush (stdout);
91 0 : fprintf (stderr, "%s: ", PGM);
92 :
93 0 : va_start (arg_ptr, format);
94 0 : vfprintf (stderr, format, arg_ptr);
95 0 : va_end (arg_ptr);
96 0 : putc ('\n', stderr);
97 :
98 0 : exit (1);
99 : }
100 :
101 :
102 : /* Print diagnostic message. */
103 : static void
104 0 : err (const char *format, ...)
105 : {
106 : va_list arg_ptr;
107 :
108 0 : fflush (stdout);
109 0 : fprintf (stderr, "%s: ", PGM);
110 :
111 0 : va_start (arg_ptr, format);
112 0 : vfprintf (stderr, format, arg_ptr);
113 0 : va_end (arg_ptr);
114 0 : putc ('\n', stderr);
115 0 : }
116 :
117 : static void *
118 0 : xmalloc (size_t n)
119 : {
120 0 : void *p = malloc (n);
121 0 : if (!p)
122 0 : die ("out of core: %s", strerror (errno));
123 0 : return p;
124 : }
125 :
126 : /* static void * */
127 : /* xcalloc (size_t n, size_t m) */
128 : /* { */
129 : /* void *p = calloc (n, m); */
130 : /* if (!p) */
131 : /* die ("out of core: %s", strerror (errno)); */
132 : /* return p; */
133 : /* } */
134 :
135 : /* static void * */
136 : /* xrealloc (void *old, size_t n) */
137 : /* { */
138 : /* void *p = realloc (old, n); */
139 : /* if (!p) */
140 : /* die ("out of core: %s", strerror (errno)); */
141 : /* return p; */
142 : /* } */
143 :
144 : /* static char * */
145 : /* xstrdup (const char *string) */
146 : /* { */
147 : /* void *p = malloc (strlen (string)+1); */
148 : /* if (!p) */
149 : /* die ("out of core: %s", strerror (errno)); */
150 : /* strcpy (p, string); */
151 : /* return p; */
152 : /* } */
153 :
154 : #ifndef HAVE_STPCPY
155 : static char *
156 : stpcpy (char *a,const char *b)
157 : {
158 : while (*b)
159 : *a++ = *b++;
160 : *a = 0;
161 :
162 : return (char*)a;
163 : }
164 : #endif
165 :
166 : static int
167 0 : run_gnupg (int smime, int sig_fd, int data_fd, int *close_list)
168 : {
169 : int rp[2];
170 : pid_t pid;
171 : int i, c, is_status;
172 : unsigned int pos;
173 : char status_buf[10];
174 : FILE *fp;
175 :
176 0 : if (pipe (rp) == -1)
177 0 : die ("error creating a pipe: %s", strerror (errno));
178 :
179 0 : pid = fork ();
180 0 : if (pid == -1)
181 0 : die ("error forking process: %s", strerror (errno));
182 :
183 0 : if (!pid)
184 : { /* Child. */
185 : char data_fd_buf[50];
186 : int fd;
187 :
188 : /* Connect our signature fd to stdin. */
189 0 : if (sig_fd != 0)
190 : {
191 0 : if (dup2 (sig_fd, 0) == -1)
192 0 : die ("dup2 stdin failed: %s", strerror (errno));
193 : }
194 :
195 : /* Keep our data fd and format it for gpg/gpgsm use. */
196 0 : if (data_fd == -1)
197 0 : *data_fd_buf = 0;
198 : else
199 0 : sprintf (data_fd_buf, "-&%d", data_fd);
200 :
201 : /* Send stdout to the bit bucket. */
202 0 : fd = open ("/dev/null", O_WRONLY);
203 0 : if (fd == -1)
204 0 : die ("can't open '/dev/null': %s", strerror (errno));
205 0 : if (fd != 1)
206 : {
207 0 : if (dup2 (fd, 1) == -1)
208 0 : die ("dup2 stderr failed: %s", strerror (errno));
209 : }
210 :
211 : /* Connect stderr to our pipe. */
212 0 : if (rp[1] != 2)
213 : {
214 0 : if (dup2 (rp[1], 2) == -1)
215 0 : die ("dup2 stderr failed: %s", strerror (errno));
216 : }
217 :
218 : /* Close other files. */
219 0 : for (i=0; (fd=close_list[i]) != -1; i++)
220 0 : if (fd > 2 && fd != data_fd)
221 0 : close (fd);
222 0 : errno = 0;
223 :
224 0 : if (smime)
225 0 : execlp ("gpgsm", "gpgsm",
226 : "--enable-special-filenames",
227 : "--status-fd", "2",
228 : "--assume-base64",
229 : "--verify",
230 : "--",
231 : "-", data_fd == -1? NULL : data_fd_buf,
232 : NULL);
233 : else
234 0 : execlp ("gpg", "gpg",
235 : "--enable-special-filenames",
236 : "--status-fd", "2",
237 : "--verify",
238 : "--debug=512",
239 : "--",
240 : "-", data_fd == -1? NULL : data_fd_buf,
241 : NULL);
242 :
243 0 : die ("failed to exec the crypto command: %s", strerror (errno));
244 : }
245 :
246 : /* Parent. */
247 0 : close (rp[1]);
248 :
249 0 : fp = fdopen (rp[0], "r");
250 0 : if (!fp)
251 0 : die ("can't fdopen pipe for reading: %s", strerror (errno));
252 :
253 0 : pos = 0;
254 0 : is_status = 0;
255 : assert (sizeof status_buf > 9);
256 0 : while ((c=getc (fp)) != EOF)
257 : {
258 0 : if (pos < 9)
259 0 : status_buf[pos] = c;
260 : else
261 : {
262 0 : if (pos == 9)
263 : {
264 0 : is_status = !memcmp (status_buf, "[GNUPG:] ", 9);
265 0 : if (is_status)
266 0 : fputs ( "c ", stdout);
267 0 : else if (verbose)
268 0 : fputs ( "# ", stdout);
269 0 : fwrite (status_buf, 9, 1, stdout);
270 : }
271 0 : putchar (c);
272 : }
273 0 : if (c == '\n')
274 : {
275 0 : if (verbose && pos < 9)
276 : {
277 0 : fputs ( "# ", stdout);
278 0 : fwrite (status_buf, pos+1, 1, stdout);
279 : }
280 0 : pos = 0;
281 : }
282 : else
283 0 : pos++;
284 : }
285 0 : if (pos)
286 : {
287 0 : if (verbose && pos < 9)
288 : {
289 0 : fputs ( "# ", stdout);
290 0 : fwrite (status_buf, pos+1, 1, stdout);
291 : }
292 0 : putchar ('\n');
293 : }
294 0 : fclose (fp);
295 :
296 0 : while ( (i=waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
297 : ;
298 0 : if (i == -1)
299 0 : die ("waiting for child failed: %s", strerror (errno));
300 :
301 0 : return 0;
302 : }
303 :
304 :
305 :
306 :
307 : /* Verify the signature in the current temp files. */
308 : static void
309 0 : verify_signature (struct parse_info_s *info)
310 : {
311 : int close_list[10];
312 :
313 0 : if (info->is_pkcs7)
314 : {
315 0 : assert (!info->hash_file);
316 0 : assert (info->sig_file);
317 0 : rewind (info->sig_file);
318 : }
319 : else
320 : {
321 0 : assert (info->hash_file);
322 0 : assert (info->sig_file);
323 0 : rewind (info->hash_file);
324 0 : rewind (info->sig_file);
325 : }
326 :
327 : /* printf ("# Begin hashed data\n"); */
328 : /* while ( (c=getc (info->hash_file)) != EOF) */
329 : /* putchar (c); */
330 : /* printf ("# End hashed data signature\n"); */
331 : /* printf ("# Begin signature\n"); */
332 : /* while ( (c=getc (info->sig_file)) != EOF) */
333 : /* putchar (c); */
334 : /* printf ("# End signature\n"); */
335 : /* rewind (info->hash_file); */
336 : /* rewind (info->sig_file); */
337 :
338 0 : close_list[0] = -1;
339 0 : run_gnupg (info->is_smime, fileno (info->sig_file),
340 0 : info->hash_file ? fileno (info->hash_file) : -1, close_list);
341 0 : }
342 :
343 :
344 :
345 :
346 :
347 : /* Prepare for a multipart/signed.
348 : FIELD_CTX is the parsed context of the content-type header.*/
349 : static void
350 0 : mime_signed_begin (struct parse_info_s *info, rfc822parse_t msg,
351 : rfc822parse_field_t field_ctx)
352 : {
353 : const char *s;
354 :
355 : (void)msg;
356 :
357 0 : s = rfc822parse_query_parameter (field_ctx, "protocol", 1);
358 0 : if (s)
359 : {
360 0 : printf ("h signed.protocol: %s\n", s);
361 0 : if (!strcmp (s, "application/pgp-signature"))
362 : {
363 0 : if (info->smfm_state)
364 0 : err ("note: ignoring nested PGP/MIME or S/MIME signature");
365 : else
366 : {
367 0 : info->smfm_state = 1;
368 0 : info->is_smime = 0;
369 0 : info->signing_protocol = "application/pgp-signature";
370 0 : info->signing_protocol_2 = NULL;
371 : }
372 : }
373 0 : else if (!strcmp (s, "application/pkcs7-signature")
374 0 : || !strcmp (s, "application/x-pkcs7-signature"))
375 : {
376 0 : if (info->smfm_state)
377 0 : err ("note: ignoring nested PGP/MIME or S/MIME signature");
378 : else
379 : {
380 0 : info->smfm_state = 1;
381 0 : info->is_smime = 1;
382 0 : info->signing_protocol = "application/pkcs7-signature";
383 0 : info->signing_protocol_2 = "application/x-pkcs7-signature";
384 : }
385 : }
386 0 : else if (verbose)
387 0 : printf ("# this protocol is not supported\n");
388 : }
389 0 : }
390 :
391 :
392 : /* Prepare for a multipart/encrypted.
393 : FIELD_CTX is the parsed context of the content-type header.*/
394 : static void
395 0 : mime_encrypted_begin (struct parse_info_s *info, rfc822parse_t msg,
396 : rfc822parse_field_t field_ctx)
397 : {
398 : const char *s;
399 :
400 : (void)info;
401 : (void)msg;
402 :
403 0 : s = rfc822parse_query_parameter (field_ctx, "protocol", 0);
404 0 : if (s)
405 0 : printf ("h encrypted.protocol: %s\n", s);
406 0 : }
407 :
408 :
409 : /* Prepare for old-style pkcs7 messages. */
410 : static void
411 0 : pkcs7_begin (struct parse_info_s *info, rfc822parse_t msg,
412 : rfc822parse_field_t field_ctx)
413 : {
414 : const char *s;
415 :
416 : (void)msg;
417 :
418 0 : s = rfc822parse_query_parameter (field_ctx, "name", 0);
419 0 : if (s)
420 0 : printf ("h pkcs7.name: %s\n", s);
421 0 : if (info->is_pkcs7)
422 0 : err ("note: ignoring nested pkcs7 data");
423 : else
424 : {
425 0 : info->is_pkcs7 = 1;
426 0 : if (opt_crypto)
427 : {
428 0 : assert (!info->sig_file);
429 0 : info->sig_file = tmpfile ();
430 0 : if (!info->sig_file)
431 0 : die ("error creating temp file: %s", strerror (errno));
432 : }
433 : }
434 0 : }
435 :
436 :
437 : /* Print the event received by the parser for debugging as comment
438 : line. */
439 : static void
440 0 : show_event (rfc822parse_event_t event)
441 : {
442 : const char *s;
443 :
444 0 : switch (event)
445 : {
446 0 : case RFC822PARSE_OPEN: s= "Open"; break;
447 0 : case RFC822PARSE_CLOSE: s= "Close"; break;
448 0 : case RFC822PARSE_CANCEL: s= "Cancel"; break;
449 0 : case RFC822PARSE_T2BODY: s= "T2Body"; break;
450 0 : case RFC822PARSE_FINISH: s= "Finish"; break;
451 0 : case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
452 0 : case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
453 0 : case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
454 0 : case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
455 0 : case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
456 0 : case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
457 0 : case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
458 0 : case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
459 0 : default: s= "[unknown event]"; break;
460 : }
461 0 : printf ("# *** got RFC822 event %s\n", s);
462 0 : }
463 :
464 : /* This function is called by the parser to communicate events. This
465 : callback comminucates with the main program using a structure
466 : passed in OPAQUE. Should retrun 0 or set errno and return -1. */
467 : static int
468 0 : message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
469 : {
470 0 : struct parse_info_s *info = opaque;
471 :
472 0 : if (debug)
473 0 : show_event (event);
474 :
475 0 : if (event == RFC822PARSE_BEGIN_HEADER || event == RFC822PARSE_T2BODY)
476 : {
477 : /* We need to check here whether to start collecting signed data
478 : because attachments might come without header lines and thus
479 : we won't see the BEGIN_HEADER event. */
480 0 : if (info->smfm_state == 1)
481 : {
482 0 : printf ("c begin_hash\n");
483 0 : info->hashing = 1;
484 0 : info->hashing_level = info->nesting_level;
485 0 : info->smfm_state++;
486 :
487 0 : if (opt_crypto)
488 : {
489 0 : assert (!info->hash_file);
490 0 : info->hash_file = tmpfile ();
491 0 : if (!info->hash_file)
492 0 : die ("failed to create temporary file: %s", strerror (errno));
493 : }
494 : }
495 : }
496 :
497 :
498 0 : if (event == RFC822PARSE_OPEN)
499 : {
500 : /* Initialize for a new message. */
501 0 : info->show_header = 1;
502 : }
503 0 : else if (event == RFC822PARSE_T2BODY)
504 : {
505 : rfc822parse_field_t ctx;
506 :
507 0 : ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
508 0 : if (ctx)
509 : {
510 : const char *s1, *s2;
511 0 : s1 = rfc822parse_query_media_type (ctx, &s2);
512 0 : if (s1)
513 : {
514 0 : printf ("h media: %*s%s %s\n",
515 0 : info->nesting_level*2, "", s1, s2);
516 0 : if (info->smfm_state == 3)
517 : {
518 0 : char *buf = xmalloc (strlen (s1) + strlen (s2) + 2);
519 0 : strcpy (stpcpy (stpcpy (buf, s1), "/"), s2);
520 0 : assert (info->signing_protocol);
521 0 : if (strcmp (buf, info->signing_protocol) &&
522 0 : (!info->signing_protocol_2
523 0 : || strcmp (buf,info->signing_protocol_2)))
524 0 : err ("invalid %s structure; expected %s%s%s, found '%s'",
525 0 : info->is_smime? "S/MIME":"PGP/MIME",
526 : info->signing_protocol,
527 0 : info->signing_protocol_2 ? " or " : "",
528 0 : info->signing_protocol_2 ? info->signing_protocol_2:"",
529 : buf);
530 : else
531 : {
532 0 : printf ("c begin_signature\n");
533 0 : info->smfm_state++;
534 0 : if (opt_crypto)
535 : {
536 0 : assert (!info->sig_file);
537 0 : info->sig_file = tmpfile ();
538 0 : if (!info->sig_file)
539 0 : die ("error creating temp file: %s",
540 0 : strerror (errno));
541 : }
542 : }
543 0 : free (buf);
544 : }
545 0 : else if (!strcmp (s1, "multipart"))
546 : {
547 0 : if (!strcmp (s2, "signed"))
548 0 : mime_signed_begin (info, msg, ctx);
549 0 : else if (!strcmp (s2, "encrypted"))
550 0 : mime_encrypted_begin (info, msg, ctx);
551 : }
552 0 : else if (!strcmp (s1, "application")
553 0 : && (!strcmp (s2, "pkcs7-mime")
554 0 : || !strcmp (s2, "x-pkcs7-mime")))
555 0 : pkcs7_begin (info, msg, ctx);
556 : }
557 : else
558 0 : printf ("h media: %*s none\n", info->nesting_level*2, "");
559 :
560 0 : rfc822parse_release_field (ctx);
561 : }
562 : else
563 0 : printf ("h media: %*stext plain [assumed]\n",
564 0 : info->nesting_level*2, "");
565 :
566 :
567 0 : info->show_header = 0;
568 0 : info->show_data = 1;
569 0 : info->skip_show = 1;
570 : }
571 0 : else if (event == RFC822PARSE_PREAMBLE)
572 0 : info->show_data_as_note = 1;
573 0 : else if (event == RFC822PARSE_LEVEL_DOWN)
574 : {
575 0 : printf ("b down\n");
576 0 : info->nesting_level++;
577 : }
578 0 : else if (event == RFC822PARSE_LEVEL_UP)
579 : {
580 0 : printf ("b up\n");
581 0 : if (info->nesting_level)
582 0 : info->nesting_level--;
583 : else
584 0 : err ("invalid structure (bad nesting level)");
585 : }
586 0 : else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY)
587 : {
588 0 : info->show_data = 0;
589 0 : info->show_boundary = 1;
590 0 : if (event == RFC822PARSE_BOUNDARY)
591 : {
592 0 : info->show_header = 1;
593 0 : info->skip_show = 1;
594 0 : printf ("b part\n");
595 : }
596 : else
597 0 : printf ("b last\n");
598 :
599 0 : if (info->smfm_state == 2 && info->nesting_level == info->hashing_level)
600 : {
601 0 : printf ("c end_hash\n");
602 0 : info->smfm_state++;
603 0 : info->hashing = 0;
604 : }
605 0 : else if (info->smfm_state == 4)
606 : {
607 0 : printf ("c end_signature\n");
608 0 : info->verify_now = 1;
609 : }
610 : }
611 :
612 0 : return 0;
613 : }
614 :
615 :
616 : /* Read a message from FP and process it according to the global
617 : options. */
618 : static void
619 0 : parse_message (FILE *fp)
620 : {
621 : char line[5000];
622 : size_t length;
623 : rfc822parse_t msg;
624 0 : unsigned int lineno = 0;
625 0 : int no_cr_reported = 0;
626 : struct parse_info_s info;
627 :
628 0 : memset (&info, 0, sizeof info);
629 :
630 0 : msg = rfc822parse_open (message_cb, &info);
631 0 : if (!msg)
632 0 : die ("can't open parser: %s", strerror (errno));
633 :
634 : /* Fixme: We should not use fgets because it can't cope with
635 : embedded nul characters. */
636 0 : while (fgets (line, sizeof (line), fp))
637 : {
638 0 : lineno++;
639 0 : if (lineno == 1 && !strncmp (line, "From ", 5))
640 0 : continue; /* We better ignore a leading From line. */
641 :
642 0 : length = strlen (line);
643 0 : if (length && line[length - 1] == '\n')
644 0 : line[--length] = 0;
645 : else
646 0 : err ("line number %u too long or last line not terminated", lineno);
647 0 : if (length && line[length - 1] == '\r')
648 0 : line[--length] = 0;
649 0 : else if (verbose && !no_cr_reported)
650 : {
651 0 : err ("non canonical ended line detected (line %u)", lineno);
652 0 : no_cr_reported = 1;
653 : }
654 :
655 :
656 0 : if (rfc822parse_insert (msg, line, length))
657 0 : die ("parser failed: %s", strerror (errno));
658 :
659 0 : if (info.hashing)
660 : {
661 : /* Delay hashing of the CR/LF because the last line ending
662 : belongs to the next boundary. */
663 0 : if (debug)
664 0 : printf ("# hashing %s'%s'\n", info.hashing==2?"CR,LF+":"", line);
665 0 : if (opt_crypto)
666 : {
667 0 : if (info.hashing == 2)
668 0 : fputs ("\r\n", info.hash_file);
669 0 : fputs (line, info.hash_file);
670 0 : if (ferror (info.hash_file))
671 0 : die ("error writing to temporary file: %s", strerror (errno));
672 : }
673 :
674 0 : info.hashing = 2;
675 : }
676 :
677 0 : if (info.sig_file && opt_crypto)
678 : {
679 0 : if (info.verify_now)
680 : {
681 0 : verify_signature (&info);
682 0 : if (info.hash_file)
683 0 : fclose (info.hash_file);
684 0 : info.hash_file = NULL;
685 0 : fclose (info.sig_file);
686 0 : info.sig_file = NULL;
687 0 : info.smfm_state = 0;
688 0 : info.is_smime = 0;
689 0 : info.is_pkcs7 = 0;
690 : }
691 : else
692 : {
693 0 : fputs (line, info.sig_file);
694 0 : fputs ("\r\n", info.sig_file);
695 0 : if (ferror (info.sig_file))
696 0 : die ("error writing to temporary file: %s", strerror (errno));
697 : }
698 : }
699 :
700 0 : if (info.show_boundary)
701 : {
702 0 : if (!opt_no_header)
703 0 : printf (":%s\n", line);
704 0 : info.show_boundary = 0;
705 : }
706 :
707 0 : if (info.skip_show)
708 0 : info.skip_show--;
709 0 : else if (info.show_data)
710 : {
711 0 : if (info.show_data_as_note)
712 : {
713 0 : if (verbose)
714 0 : printf ("# DATA: %s\n", line);
715 0 : info.show_data_as_note = 0;
716 : }
717 : else
718 0 : printf (" %s\n", line);
719 : }
720 0 : else if (info.show_header && !opt_no_header)
721 0 : printf (".%s\n", line);
722 :
723 : }
724 :
725 0 : if (info.sig_file && opt_crypto && info.is_pkcs7)
726 : {
727 0 : verify_signature (&info);
728 0 : fclose (info.sig_file);
729 0 : info.sig_file = NULL;
730 0 : info.is_pkcs7 = 0;
731 : }
732 :
733 0 : rfc822parse_close (msg);
734 0 : }
735 :
736 :
737 : int
738 0 : main (int argc, char **argv)
739 : {
740 0 : int last_argc = -1;
741 :
742 0 : if (argc)
743 : {
744 0 : argc--; argv++;
745 : }
746 0 : while (argc && last_argc != argc )
747 : {
748 0 : last_argc = argc;
749 0 : if (!strcmp (*argv, "--"))
750 : {
751 0 : argc--; argv++;
752 0 : break;
753 : }
754 0 : else if (!strcmp (*argv, "--help"))
755 : {
756 0 : puts (
757 : "Usage: " PGM " [OPTION] [FILE]\n"
758 : "Parse a mail message into an annotated format.\n\n"
759 : " --crypto decrypt or verify messages\n"
760 : " --no-header don't output the header lines\n"
761 : " --verbose enable extra informational output\n"
762 : " --debug enable additional debug output\n"
763 : " --help display this help and exit\n\n"
764 : "With no FILE, or when FILE is -, read standard input.\n\n"
765 : "WARNING: This tool is under development.\n"
766 : " The semantics may change without notice\n\n"
767 : "Report bugs to <bug-gnupg@gnu.org>.");
768 0 : exit (0);
769 : }
770 0 : else if (!strcmp (*argv, "--verbose"))
771 : {
772 0 : verbose = 1;
773 0 : argc--; argv++;
774 : }
775 0 : else if (!strcmp (*argv, "--debug"))
776 : {
777 0 : verbose = debug = 1;
778 0 : argc--; argv++;
779 : }
780 0 : else if (!strcmp (*argv, "--crypto"))
781 : {
782 0 : opt_crypto = 1;
783 0 : argc--; argv++;
784 : }
785 0 : else if (!strcmp (*argv, "--no-header"))
786 : {
787 0 : opt_no_header = 1;
788 0 : argc--; argv++;
789 : }
790 : }
791 :
792 0 : if (argc > 1)
793 0 : die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
794 :
795 0 : signal (SIGPIPE, SIG_IGN);
796 :
797 0 : if (argc && strcmp (*argv, "-"))
798 0 : {
799 0 : FILE *fp = fopen (*argv, "rb");
800 0 : if (!fp)
801 0 : die ("can't open '%s': %s", *argv, strerror (errno));
802 0 : parse_message (fp);
803 0 : fclose (fp);
804 : }
805 : else
806 0 : parse_message (stdin);
807 :
808 0 : return 0;
809 : }
810 :
811 :
812 : /*
813 : Local Variables:
814 : compile-command: "gcc -Wall -Wno-pointer-sign -g -o gpgparsemail rfc822parse.c gpgparsemail.c"
815 : End:
816 : */
|