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