diff options
Diffstat (limited to 'tools/gpgparsemail.c')
-rw-r--r-- | tools/gpgparsemail.c | 705 |
1 files changed, 0 insertions, 705 deletions
diff --git a/tools/gpgparsemail.c b/tools/gpgparsemail.c deleted file mode 100644 index fa848c8f6..000000000 --- a/tools/gpgparsemail.c +++ /dev/null @@ -1,705 +0,0 @@ -/* gpgparsemail.c - Standalone crypto mail parser - * Copyright (C) 2004 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * GnuPG is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * GnuPG is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - - -/* This utility prints an RFC8222, possible MIME structured, message - in an annotated format with the first column having an indicator - for the content of the line.. Several options are available to - scrutinize the message. S/MIME and OpenPGP suuport is included. */ - - -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <errno.h> -#include <stdarg.h> -#include <assert.h> -#include <time.h> -#include <signal.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/wait.h> - -#include "rfc822parse.h" - - -#define PGM "gpgparsemail" - -/* Option flags. */ -static int verbose; -static int debug; -static int opt_crypto; /* Decrypt or verify messages. */ -static int opt_no_header; /* Don't output the header lines. */ - -/* Structure used to communicate with the parser callback. */ -struct parse_info_s { - int show_header; /* Show the header lines. */ - int show_data; /* Show the data lines. */ - unsigned int skip_show; /* Temporary disable above for these - number of lines. */ - int show_data_as_note; /* The next data line should be shown - as a note. */ - int show_boundary; - int nesting_level; - - int gpgsm_mime; /* gpgsm shall be used from S/MIME. */ - char *signing_protocol; - int hashing_level; /* The nesting level we are hashing. */ - int hashing; - FILE *hash_file; - FILE *sig_file; - int verify_now; /* Falg set when all signature data is - available. */ -}; - - -/* Print diagnostic message and exit with failure. */ -static void -die (const char *format, ...) -{ - va_list arg_ptr; - - fflush (stdout); - fprintf (stderr, "%s: ", PGM); - - va_start (arg_ptr, format); - vfprintf (stderr, format, arg_ptr); - va_end (arg_ptr); - putc ('\n', stderr); - - exit (1); -} - - -/* Print diagnostic message. */ -static void -err (const char *format, ...) -{ - va_list arg_ptr; - - fflush (stdout); - fprintf (stderr, "%s: ", PGM); - - va_start (arg_ptr, format); - vfprintf (stderr, format, arg_ptr); - va_end (arg_ptr); - putc ('\n', stderr); -} - -static void * -xmalloc (size_t n) -{ - void *p = malloc (n); - if (!p) - die ("out of core: %s", strerror (errno)); - return p; -} - -/* static void * */ -/* xcalloc (size_t n, size_t m) */ -/* { */ -/* void *p = calloc (n, m); */ -/* if (!p) */ -/* die ("out of core: %s", strerror (errno)); */ -/* return p; */ -/* } */ - -/* static void * */ -/* xrealloc (void *old, size_t n) */ -/* { */ -/* void *p = realloc (old, n); */ -/* if (!p) */ -/* die ("out of core: %s", strerror (errno)); */ -/* return p; */ -/* } */ - -static char * -xstrdup (const char *string) -{ - void *p = malloc (strlen (string)+1); - if (!p) - die ("out of core: %s", strerror (errno)); - strcpy (p, string); - return p; -} - -static char * -stpcpy (char *a,const char *b) -{ - while (*b) - *a++ = *b++; - *a = 0; - - return (char*)a; -} - - -static int -run_gnupg (int smime, int sig_fd, int data_fd, int *close_list) -{ - int rp[2]; - pid_t pid; - int i, c, is_status; - unsigned int pos; - char status_buf[10]; - const char *cmd = smime? "gpgsm":"gpg"; - FILE *fp; - - if (pipe (rp) == -1) - die ("error creating a pipe: %s", strerror (errno)); - - pid = fork (); - if (pid == -1) - die ("error forking process: %s", strerror (errno)); - - if (!pid) - { /* Child. */ - char data_fd_buf[50]; - int fd; - - /* Connect our signature fd to stdin. */ - if (sig_fd != 0) - { - if (dup2 (sig_fd, 0) == -1) - die ("dup2 stdin failed: %s", strerror (errno)); - } - - /* Keep our data fd and format it for gpg/gpgsm use. */ - sprintf (data_fd_buf, "-&%d", data_fd); - - /* Send stdout to the bit bucket. */ - fd = open ("/dev/null", O_WRONLY); - if (fd == -1) - die ("can't open `/dev/null': %s", strerror (errno)); - if (fd != 1) - { - if (dup2 (fd, 1) == -1) - die ("dup2 stderr failed: %s", strerror (errno)); - } - - /* Connect stderr to our pipe. */ - if (rp[1] != 2) - { - if (dup2 (rp[1], 2) == -1) - die ("dup2 stderr failed: %s", strerror (errno)); - } - - /* Close other files. */ - for (i=0; (fd=close_list[i]) != -1; i++) - if (fd > 2 && fd != data_fd) - close (fd); - errno = 0; - - execlp (cmd, cmd, - "--enable-special-filenames", - "--status-fd", "2", - "--assume-base64", - "--verify", - "--", - "-", data_fd_buf, - NULL); - - die ("failed to exec the crypto command: %s", strerror (errno)); - } - - /* Parent. */ - close (rp[1]); - - fp = fdopen (rp[0], "r"); - if (!fp) - die ("can't fdopen pipe for reading: %s", strerror (errno)); - - pos = 0; - is_status = 0; - assert (sizeof status_buf > 9); - while ((c=getc (fp)) != EOF) - { - if (pos < 9) - status_buf[pos] = c; - else - { - if (pos == 9) - { - is_status = !memcmp (status_buf, "[GNUPG:] ", 9); - if (is_status) - fputs ( "c ", stdout); - else if (verbose) - fputs ( "# ", stdout); - fwrite (status_buf, 9, 1, stdout); - } - putchar (c); - } - if (c == '\n') - { - if (verbose && pos < 9) - { - fputs ( "# ", stdout); - fwrite (status_buf, pos+1, 1, stdout); - } - pos = 0; - } - else - pos++; - } - if (pos) - { - if (verbose && pos < 9) - { - fputs ( "# ", stdout); - fwrite (status_buf, pos+1, 1, stdout); - } - putchar ('\n'); - } - fclose (fp); - - while ( (i=waitpid (pid, NULL, 0)) == -1 && errno == EINTR) - ; - if (i == -1) - die ("waiting for child failed: %s", strerror (errno)); - - return 0; -} - - - - -/* Verify the signature in the current temp files. */ -static void -verify_signature (struct parse_info_s *info) -{ - int close_list[10]; - - assert (info->hash_file); - assert (info->sig_file); - rewind (info->hash_file); - rewind (info->sig_file); - -/* printf ("# Begin hashed data\n"); */ -/* while ( (c=getc (info->hash_file)) != EOF) */ -/* putchar (c); */ -/* printf ("# End hashed data signature\n"); */ -/* printf ("# Begin signature\n"); */ -/* while ( (c=getc (info->sig_file)) != EOF) */ -/* putchar (c); */ -/* printf ("# End signature\n"); */ -/* rewind (info->hash_file); */ -/* rewind (info->sig_file); */ - - close_list[0] = -1; - run_gnupg (1, fileno (info->sig_file), fileno (info->hash_file), close_list); -} - - - - - -/* Prepare for a multipart/signed. - FIELD_CTX is the parsed context of the content-type header.*/ -static void -mime_signed_begin (struct parse_info_s *info, rfc822parse_t msg, - rfc822parse_field_t field_ctx) -{ - const char *s; - s = rfc822parse_query_parameter (field_ctx, "protocol", 1); - if (s) - { - printf ("h signed.protocol: %s\n", s); - if (!strcmp (s, "application/pkcs7-signature") - || !strcmp (s, "application/x-pkcs7-signature")) - { - if (info->gpgsm_mime) - err ("note: ignoring nested pkcs7-signature"); - else - { - info->gpgsm_mime = 1; - free (info->signing_protocol); - info->signing_protocol = xstrdup (s); - } - } - else if (verbose) - printf ("# this protocol is not supported\n"); - } -} - - -/* Prepare for a multipart/encrypted. - FIELD_CTX is the parsed context of the content-type header.*/ -static void -mime_encrypted_begin (struct parse_info_s *info, rfc822parse_t msg, - rfc822parse_field_t field_ctx) -{ - const char *s; - s = rfc822parse_query_parameter (field_ctx, "protocol", 0); - if (s) - printf ("h encrypted.protocol: %s\n", s); -} - - - -/* Print the event received by the parser for debugging as comment - line. */ -static void -show_event (rfc822parse_event_t event) -{ - const char *s; - - switch (event) - { - case RFC822PARSE_OPEN: s= "Open"; break; - case RFC822PARSE_CLOSE: s= "Close"; break; - case RFC822PARSE_CANCEL: s= "Cancel"; break; - case RFC822PARSE_T2BODY: s= "T2Body"; break; - case RFC822PARSE_FINISH: s= "Finish"; break; - case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break; - case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break; - case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break; - case RFC822PARSE_BOUNDARY: s= "Boundary"; break; - case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break; - case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break; - case RFC822PARSE_PREAMBLE: s= "Preamble"; break; - case RFC822PARSE_EPILOGUE: s= "Epilogue"; break; - default: s= "[unknown event]"; break; - } - printf ("# *** got RFC822 event %s\n", s); -} - -/* This function is called by the parser to communicate events. This - callback comminucates with the main program using a structure - passed in OPAQUE. Should retrun 0 or set errno and return -1. */ -static int -message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg) -{ - struct parse_info_s *info = opaque; - - if (debug) - show_event (event); - if (event == RFC822PARSE_OPEN) - { - /* Initialize for a new message. */ - info->show_header = 1; - } - else if (event == RFC822PARSE_T2BODY) - { - rfc822parse_field_t ctx; - - ctx = rfc822parse_parse_field (msg, "Content-Type", -1); - if (ctx) - { - const char *s1, *s2; - s1 = rfc822parse_query_media_type (ctx, &s2); - if (s1) - { - printf ("h media: %*s%s %s\n", - info->nesting_level*2, "", s1, s2); - if (info->gpgsm_mime == 3) - { - char *buf = xmalloc (strlen (s1) + strlen (s2) + 2); - strcpy (stpcpy (stpcpy (buf, s1), "/"), s2); - assert (info->signing_protocol); - if (strcmp (buf, info->signing_protocol)) - err ("invalid S/MIME structure; expected `%s', found `%s'", - info->signing_protocol, buf); - else - { - printf ("c begin_signature\n"); - info->gpgsm_mime++; - if (opt_crypto) - { - assert (!info->sig_file); - info->sig_file = tmpfile (); - if (!info->sig_file) - die ("error creating temp file: %s", - strerror (errno)); - } - } - free (buf); - } - else if (!strcmp (s1, "multipart")) - { - if (!strcmp (s2, "signed")) - mime_signed_begin (info, msg, ctx); - else if (!strcmp (s2, "encrypted")) - mime_encrypted_begin (info, msg, ctx); - } - } - else - printf ("h media: %*s none\n", info->nesting_level*2, ""); - - rfc822parse_release_field (ctx); - } - else - printf ("h media: %*stext plain [assumed]\n", - info->nesting_level*2, ""); - info->show_header = 0; - info->show_data = 1; - info->skip_show = 1; - } - else if (event == RFC822PARSE_PREAMBLE) - info->show_data_as_note = 1; - else if (event == RFC822PARSE_LEVEL_DOWN) - { - printf ("b down\n"); - info->nesting_level++; - } - else if (event == RFC822PARSE_LEVEL_UP) - { - printf ("b up\n"); - if (info->nesting_level) - info->nesting_level--; - else - err ("invalid structure (bad nesting level)"); - } - else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY) - { - info->show_data = 0; - info->show_boundary = 1; - if (event == RFC822PARSE_BOUNDARY) - { - info->show_header = 1; - info->skip_show = 1; - printf ("b part\n"); - } - else - printf ("b last\n"); - - if (info->gpgsm_mime == 2 && info->nesting_level == info->hashing_level) - { - printf ("c end_hash\n"); - info->gpgsm_mime++; - info->hashing = 0; - } - else if (info->gpgsm_mime == 4) - { - printf ("c end_signature\n"); - info->verify_now = 1; - } - } - else if (event == RFC822PARSE_BEGIN_HEADER) - { - if (info->gpgsm_mime == 1) - { - printf ("c begin_hash\n"); - info->hashing = 1; - info->hashing_level = info->nesting_level; - info->gpgsm_mime++; - - if (opt_crypto) - { - assert (!info->hash_file); - info->hash_file = tmpfile (); - if (!info->hash_file) - die ("failed to create temporary file: %s", strerror (errno)); - } - } - } - - return 0; -} - - -/* Read a message from FP and process it according to the global - options. */ -static void -parse_message (FILE *fp) -{ - char line[5000]; - size_t length; - rfc822parse_t msg; - unsigned int lineno = 0; - int no_cr_reported = 0; - struct parse_info_s info; - - memset (&info, 0, sizeof info); - - msg = rfc822parse_open (message_cb, &info); - if (!msg) - die ("can't open parser: %s", strerror (errno)); - - /* Fixme: We should not use fgets becuase it can't cope with - embedded nul characters. */ - while (fgets (line, sizeof (line), fp)) - { - lineno++; - if (lineno == 1 && !strncmp (line, "From ", 5)) - continue; /* We better ignore a leading From line. */ - - length = strlen (line); - if (length && line[length - 1] == '\n') - line[--length] = 0; - else - err ("line number %u too long or last line not terminated", lineno); - if (length && line[length - 1] == '\r') - line[--length] = 0; - else if (verbose && !no_cr_reported) - { - err ("non canonical ended line detected (line %u)", lineno); - no_cr_reported = 1; - } - - - if (rfc822parse_insert (msg, line, length)) - die ("parser failed: %s", strerror (errno)); - - if (info.hashing) - { - /* Delay hashing of the CR/LF because the last line ending - belongs to the next boundary. */ - if (debug) - printf ("# hashing %s`%s'\n", info.hashing==2?"CR,LF+":"", line); - if (opt_crypto) - { - if (info.hashing == 2) - fputs ("\r\n", info.hash_file); - fputs (line, info.hash_file); - if (ferror (info.hash_file)) - die ("error writing to temporary file: %s", strerror (errno)); - } - - info.hashing = 2; - } - - if (info.sig_file && opt_crypto) - { - if (info.verify_now) - { - verify_signature (&info); - fclose (info.hash_file); - info.hash_file = NULL; - fclose (info.sig_file); - info.sig_file = NULL; - info.gpgsm_mime = 0; - } - else - { - fputs (line, info.sig_file); - fputs ("\r\n", info.sig_file); - if (ferror (info.sig_file)) - die ("error writing to temporary file: %s", strerror (errno)); - } - } - - if (info.show_boundary) - { - if (!opt_no_header) - printf (":%s\n", line); - info.show_boundary = 0; - } - - if (info.skip_show) - info.skip_show--; - else if (info.show_data) - { - if (info.show_data_as_note) - { - if (verbose) - printf ("# DATA: %s\n", line); - info.show_data_as_note = 0; - } - else - printf (" %s\n", line); - } - else if (info.show_header && !opt_no_header) - printf (".%s\n", line); - - } - - rfc822parse_close (msg); -} - - -int -main (int argc, char **argv) -{ - int last_argc = -1; - - if (argc) - { - argc--; argv++; - } - while (argc && last_argc != argc ) - { - last_argc = argc; - if (!strcmp (*argv, "--")) - { - argc--; argv++; - break; - } - else if (!strcmp (*argv, "--help")) - { - puts ( - "Usage: " PGM " [OPTION] [FILE]\n" - "Parse a mail message into an annotated format.\n\n" - " --crypto decrypt or verify messages\n" - " --no-header don't output the header lines\n" - " --verbose enable extra informational output\n" - " --debug enable additional debug output\n" - " --help display this help and exit\n\n" - "With no FILE, or when FILE is -, read standard input.\n\n" - "Report bugs to <[email protected]>."); - exit (0); - } - else if (!strcmp (*argv, "--verbose")) - { - verbose = 1; - argc--; argv++; - } - else if (!strcmp (*argv, "--debug")) - { - verbose = debug = 1; - argc--; argv++; - } - else if (!strcmp (*argv, "--crypto")) - { - opt_crypto = 1; - argc--; argv++; - } - else if (!strcmp (*argv, "--no-header")) - { - opt_no_header = 1; - argc--; argv++; - } - } - - if (argc > 1) - die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n"); - - signal (SIGPIPE, SIG_IGN); - - if (argc && strcmp (*argv, "-")) - { - FILE *fp = fopen (*argv, "rb"); - if (!fp) - die ("can't open `%s': %s", *argv, strerror (errno)); - parse_message (fp); - fclose (fp); - } - else - parse_message (stdin); - - return 0; -} - - -/* -Local Variables: -compile-command: "gcc -Wall -g -o gpgparsemail rfc822parse.c gpgparsemail.c" -End: -*/ |