1
/* gpgparsemail.c - Standalone crypto mail parser
2
* Copyright (C) 2004 Free Software Foundation, Inc.
4
* This file is part of GnuPG.
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 2 of the License, or
9
* (at your option) any later version.
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.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
22
/* This utility prints an RFC8222, possible MIME structured, message
23
in an annotated format with the first column having an indicator
24
for the content of the line.. Several options are available to
25
scrutinize the message. S/MIME and OpenPGP suuport is included. */
41
#include "rfc822parse.h"
44
#define PGM "gpgparsemail"
49
static int opt_crypto; /* Decrypt or verify messages. */
50
static int opt_no_header; /* Don't output the header lines. */
52
/* Structure used to communicate with the parser callback. */
54
int show_header; /* Show the header lines. */
55
int show_data; /* Show the data lines. */
56
unsigned int skip_show; /* Temporary disable above for these
58
int show_data_as_note; /* The next data line should be shown
63
int gpgsm_mime; /* gpgsm shall be used from S/MIME. */
64
char *signing_protocol;
65
int hashing_level; /* The nesting level we are hashing. */
69
int verify_now; /* Falg set when all signature data is
74
/* Print diagnostic message and exit with failure. */
76
die (const char *format, ...)
81
fprintf (stderr, "%s: ", PGM);
83
va_start (arg_ptr, format);
84
vfprintf (stderr, format, arg_ptr);
92
/* Print diagnostic message. */
94
err (const char *format, ...)
99
fprintf (stderr, "%s: ", PGM);
101
va_start (arg_ptr, format);
102
vfprintf (stderr, format, arg_ptr);
110
void *p = malloc (n);
112
die ("out of core: %s", strerror (errno));
117
/* xcalloc (size_t n, size_t m) */
119
/* void *p = calloc (n, m); */
121
/* die ("out of core: %s", strerror (errno)); */
126
/* xrealloc (void *old, size_t n) */
128
/* void *p = realloc (old, n); */
130
/* die ("out of core: %s", strerror (errno)); */
135
xstrdup (const char *string)
137
void *p = malloc (strlen (string)+1);
139
die ("out of core: %s", strerror (errno));
145
stpcpy (char *a,const char *b)
156
run_gnupg (int smime, int sig_fd, int data_fd, int *close_list)
163
const char *cmd = smime? "gpgsm":"gpg";
167
die ("error creating a pipe: %s", strerror (errno));
171
die ("error forking process: %s", strerror (errno));
175
char data_fd_buf[50];
178
/* Connect our signature fd to stdin. */
181
if (dup2 (sig_fd, 0) == -1)
182
die ("dup2 stdin failed: %s", strerror (errno));
185
/* Keep our data fd and format it for gpg/gpgsm use. */
186
sprintf (data_fd_buf, "-&%d", data_fd);
188
/* Send stdout to the bit bucket. */
189
fd = open ("/dev/null", O_WRONLY);
191
die ("can't open `/dev/null': %s", strerror (errno));
194
if (dup2 (fd, 1) == -1)
195
die ("dup2 stderr failed: %s", strerror (errno));
198
/* Connect stderr to our pipe. */
201
if (dup2 (rp[1], 2) == -1)
202
die ("dup2 stderr failed: %s", strerror (errno));
205
/* Close other files. */
206
for (i=0; (fd=close_list[i]) != -1; i++)
207
if (fd > 2 && fd != data_fd)
212
"--enable-special-filenames",
220
die ("failed to exec the crypto command: %s", strerror (errno));
226
fp = fdopen (rp[0], "r");
228
die ("can't fdopen pipe for reading: %s", strerror (errno));
232
assert (sizeof status_buf > 9);
233
while ((c=getc (fp)) != EOF)
241
is_status = !memcmp (status_buf, "[GNUPG:] ", 9);
243
fputs ( "c ", stdout);
245
fputs ( "# ", stdout);
246
fwrite (status_buf, 9, 1, stdout);
252
if (verbose && pos < 9)
254
fputs ( "# ", stdout);
255
fwrite (status_buf, pos+1, 1, stdout);
264
if (verbose && pos < 9)
266
fputs ( "# ", stdout);
267
fwrite (status_buf, pos+1, 1, stdout);
273
while ( (i=waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
276
die ("waiting for child failed: %s", strerror (errno));
284
/* Verify the signature in the current temp files. */
286
verify_signature (struct parse_info_s *info)
290
assert (info->hash_file);
291
assert (info->sig_file);
292
rewind (info->hash_file);
293
rewind (info->sig_file);
295
/* printf ("# Begin hashed data\n"); */
296
/* while ( (c=getc (info->hash_file)) != EOF) */
298
/* printf ("# End hashed data signature\n"); */
299
/* printf ("# Begin signature\n"); */
300
/* while ( (c=getc (info->sig_file)) != EOF) */
302
/* printf ("# End signature\n"); */
303
/* rewind (info->hash_file); */
304
/* rewind (info->sig_file); */
307
run_gnupg (1, fileno (info->sig_file), fileno (info->hash_file), close_list);
314
/* Prepare for a multipart/signed.
315
FIELD_CTX is the parsed context of the content-type header.*/
317
mime_signed_begin (struct parse_info_s *info, rfc822parse_t msg,
318
rfc822parse_field_t field_ctx)
321
s = rfc822parse_query_parameter (field_ctx, "protocol", 1);
324
printf ("h signed.protocol: %s\n", s);
325
if (!strcmp (s, "application/pkcs7-signature")
326
|| !strcmp (s, "application/x-pkcs7-signature"))
328
if (info->gpgsm_mime)
329
err ("note: ignoring nested pkcs7-signature");
332
info->gpgsm_mime = 1;
333
free (info->signing_protocol);
334
info->signing_protocol = xstrdup (s);
338
printf ("# this protocol is not supported\n");
343
/* Prepare for a multipart/encrypted.
344
FIELD_CTX is the parsed context of the content-type header.*/
346
mime_encrypted_begin (struct parse_info_s *info, rfc822parse_t msg,
347
rfc822parse_field_t field_ctx)
350
s = rfc822parse_query_parameter (field_ctx, "protocol", 0);
352
printf ("h encrypted.protocol: %s\n", s);
357
/* Print the event received by the parser for debugging as comment
360
show_event (rfc822parse_event_t event)
366
case RFC822PARSE_OPEN: s= "Open"; break;
367
case RFC822PARSE_CLOSE: s= "Close"; break;
368
case RFC822PARSE_CANCEL: s= "Cancel"; break;
369
case RFC822PARSE_T2BODY: s= "T2Body"; break;
370
case RFC822PARSE_FINISH: s= "Finish"; break;
371
case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
372
case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
373
case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
374
case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
375
case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
376
case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
377
case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
378
case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
379
default: s= "[unknown event]"; break;
381
printf ("# *** got RFC822 event %s\n", s);
384
/* This function is called by the parser to communicate events. This
385
callback comminucates with the main program using a structure
386
passed in OPAQUE. Should retrun 0 or set errno and return -1. */
388
message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
390
struct parse_info_s *info = opaque;
394
if (event == RFC822PARSE_OPEN)
396
/* Initialize for a new message. */
397
info->show_header = 1;
399
else if (event == RFC822PARSE_T2BODY)
401
rfc822parse_field_t ctx;
403
ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
407
s1 = rfc822parse_query_media_type (ctx, &s2);
410
printf ("h media: %*s%s %s\n",
411
info->nesting_level*2, "", s1, s2);
412
if (info->gpgsm_mime == 3)
414
char *buf = xmalloc (strlen (s1) + strlen (s2) + 2);
415
strcpy (stpcpy (stpcpy (buf, s1), "/"), s2);
416
assert (info->signing_protocol);
417
if (strcmp (buf, info->signing_protocol))
418
err ("invalid S/MIME structure; expected `%s', found `%s'",
419
info->signing_protocol, buf);
422
printf ("c begin_signature\n");
426
assert (!info->sig_file);
427
info->sig_file = tmpfile ();
429
die ("error creating temp file: %s",
435
else if (!strcmp (s1, "multipart"))
437
if (!strcmp (s2, "signed"))
438
mime_signed_begin (info, msg, ctx);
439
else if (!strcmp (s2, "encrypted"))
440
mime_encrypted_begin (info, msg, ctx);
444
printf ("h media: %*s none\n", info->nesting_level*2, "");
446
rfc822parse_release_field (ctx);
449
printf ("h media: %*stext plain [assumed]\n",
450
info->nesting_level*2, "");
451
info->show_header = 0;
455
else if (event == RFC822PARSE_PREAMBLE)
456
info->show_data_as_note = 1;
457
else if (event == RFC822PARSE_LEVEL_DOWN)
460
info->nesting_level++;
462
else if (event == RFC822PARSE_LEVEL_UP)
465
if (info->nesting_level)
466
info->nesting_level--;
468
err ("invalid structure (bad nesting level)");
470
else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY)
473
info->show_boundary = 1;
474
if (event == RFC822PARSE_BOUNDARY)
476
info->show_header = 1;
483
if (info->gpgsm_mime == 2 && info->nesting_level == info->hashing_level)
485
printf ("c end_hash\n");
489
else if (info->gpgsm_mime == 4)
491
printf ("c end_signature\n");
492
info->verify_now = 1;
495
else if (event == RFC822PARSE_BEGIN_HEADER)
497
if (info->gpgsm_mime == 1)
499
printf ("c begin_hash\n");
501
info->hashing_level = info->nesting_level;
506
assert (!info->hash_file);
507
info->hash_file = tmpfile ();
508
if (!info->hash_file)
509
die ("failed to create temporary file: %s", strerror (errno));
518
/* Read a message from FP and process it according to the global
521
parse_message (FILE *fp)
526
unsigned int lineno = 0;
527
int no_cr_reported = 0;
528
struct parse_info_s info;
530
memset (&info, 0, sizeof info);
532
msg = rfc822parse_open (message_cb, &info);
534
die ("can't open parser: %s", strerror (errno));
536
/* Fixme: We should not use fgets becuase it can't cope with
537
embedded nul characters. */
538
while (fgets (line, sizeof (line), fp))
541
if (lineno == 1 && !strncmp (line, "From ", 5))
542
continue; /* We better ignore a leading From line. */
544
length = strlen (line);
545
if (length && line[length - 1] == '\n')
548
err ("line number %u too long or last line not terminated", lineno);
549
if (length && line[length - 1] == '\r')
551
else if (verbose && !no_cr_reported)
553
err ("non canonical ended line detected (line %u)", lineno);
558
if (rfc822parse_insert (msg, line, length))
559
die ("parser failed: %s", strerror (errno));
563
/* Delay hashing of the CR/LF because the last line ending
564
belongs to the next boundary. */
566
printf ("# hashing %s`%s'\n", info.hashing==2?"CR,LF+":"", line);
569
if (info.hashing == 2)
570
fputs ("\r\n", info.hash_file);
571
fputs (line, info.hash_file);
572
if (ferror (info.hash_file))
573
die ("error writing to temporary file: %s", strerror (errno));
579
if (info.sig_file && opt_crypto)
583
verify_signature (&info);
584
fclose (info.hash_file);
585
info.hash_file = NULL;
586
fclose (info.sig_file);
587
info.sig_file = NULL;
592
fputs (line, info.sig_file);
593
fputs ("\r\n", info.sig_file);
594
if (ferror (info.sig_file))
595
die ("error writing to temporary file: %s", strerror (errno));
599
if (info.show_boundary)
602
printf (":%s\n", line);
603
info.show_boundary = 0;
608
else if (info.show_data)
610
if (info.show_data_as_note)
613
printf ("# DATA: %s\n", line);
614
info.show_data_as_note = 0;
617
printf (" %s\n", line);
619
else if (info.show_header && !opt_no_header)
620
printf (".%s\n", line);
624
rfc822parse_close (msg);
629
main (int argc, char **argv)
637
while (argc && last_argc != argc )
640
if (!strcmp (*argv, "--"))
645
else if (!strcmp (*argv, "--help"))
648
"Usage: " PGM " [OPTION] [FILE]\n"
649
"Parse a mail message into an annotated format.\n\n"
650
" --crypto decrypt or verify messages\n"
651
" --no-header don't output the header lines\n"
652
" --verbose enable extra informational output\n"
653
" --debug enable additional debug output\n"
654
" --help display this help and exit\n\n"
655
"With no FILE, or when FILE is -, read standard input.\n\n"
656
"Report bugs to <bug-gnupg@gnu.org>.");
659
else if (!strcmp (*argv, "--verbose"))
664
else if (!strcmp (*argv, "--debug"))
669
else if (!strcmp (*argv, "--crypto"))
674
else if (!strcmp (*argv, "--no-header"))
682
die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
684
signal (SIGPIPE, SIG_IGN);
686
if (argc && strcmp (*argv, "-"))
688
FILE *fp = fopen (*argv, "rb");
690
die ("can't open `%s': %s", *argv, strerror (errno));
695
parse_message (stdin);
703
compile-command: "gcc -Wall -g -o gpgparsemail rfc822parse.c gpgparsemail.c"