gpg: When comparing keyids, use the keyid, not the fingerprint's suffix.
[gnupg.git] / tools / gpgparsemail.c
index 30759f9..98bbad0 100644 (file)
@@ -1,11 +1,11 @@
 /* gpgparsemail.c - Standalone crypto mail parser
- *     Copyright (C) 2004 Free Software Foundation, Inc.
+ *     Copyright (C) 2004, 2007 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
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 
-/* This utility prints an RFC8222, possible MIME structured, message
+/* This utility prints an RFC822, 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 support is included. */
@@ -66,10 +64,12 @@ struct parse_info_s {
 
   int is_pkcs7;                /* Old style S/MIME message. */
 
-  int gpgsm_mime;              /* gpgsm shall be used from S/MIME. */
+  int smfm_state;              /* State of PGP/MIME or S/MIME parsing.  */
+  int is_smime;                /* This is S/MIME and not PGP/MIME. */
+
   char *signing_protocol;
   int hashing_level;           /* The nesting level we are hashing. */
-  int hashing;                 
+  int hashing;
   FILE *hash_file;
 
   FILE *sig_file;              /* Signature part with MIME or full
@@ -156,7 +156,7 @@ stpcpy (char *a,const char *b)
   while (*b)
     *a++ = *b++;
   *a = 0;
-  
+
   return (char*)a;
 }
 #endif
@@ -169,7 +169,6 @@ run_gnupg (int smime, int sig_fd, int data_fd, int *close_list)
   int i, c, is_status;
   unsigned int pos;
   char status_buf[10];
-  const char *cmd = smime? "gpgsm":"gpg";
   FILE *fp;
 
   if (pipe (rp) == -1)
@@ -190,7 +189,7 @@ run_gnupg (int smime, int sig_fd, int data_fd, int *close_list)
           if (dup2 (sig_fd, 0) == -1)
             die ("dup2 stdin failed: %s", strerror (errno));
         }
-      
+
       /* Keep our data fd and format it for gpg/gpgsm use. */
       if (data_fd == -1)
         *data_fd_buf = 0;
@@ -200,13 +199,13 @@ run_gnupg (int smime, int sig_fd, int data_fd, int *close_list)
       /* Send stdout to the bit bucket. */
       fd = open ("/dev/null", O_WRONLY);
       if (fd == -1)
-        die ("can't open `/dev/null': %s", strerror (errno));
+        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)
        {
@@ -220,19 +219,29 @@ run_gnupg (int smime, int sig_fd, int data_fd, int *close_list)
           close (fd);
       errno = 0;
 
-      execlp (cmd, cmd,
-              "--enable-special-filenames",
-              "--status-fd", "2",
-              "--assume-base64",
-              "--verify",
-              "--",
-              "-", data_fd == -1? NULL : data_fd_buf,
-              NULL);
+      if (smime)
+        execlp ("gpgsm", "gpgsm",
+                "--enable-special-filenames",
+                "--status-fd", "2",
+                "--assume-base64",
+                "--verify",
+                "--",
+                "-", data_fd == -1? NULL : data_fd_buf,
+                NULL);
+      else
+        execlp ("gpg", "gpg",
+                "--enable-special-filenames",
+                "--status-fd", "2",
+                "--verify",
+                "--debug=512",
+                "--",
+                "-", data_fd == -1? NULL : data_fd_buf,
+                NULL);
 
       die ("failed to exec the crypto command: %s", strerror (errno));
     }
 
-  /* Parent. */ 
+  /* Parent. */
   close (rp[1]);
 
   fp = fdopen (rp[0], "r");
@@ -246,7 +255,7 @@ run_gnupg (int smime, int sig_fd, int data_fd, int *close_list)
     {
       if (pos < 9)
         status_buf[pos] = c;
-      else 
+      else
         {
           if (pos == 9)
             {
@@ -325,7 +334,7 @@ verify_signature (struct parse_info_s *info)
 /*   rewind (info->sig_file); */
 
   close_list[0] = -1;
-  run_gnupg (1, fileno (info->sig_file),
+  run_gnupg (info->is_smime, fileno (info->sig_file),
              info->hash_file ? fileno (info->hash_file) : -1, close_list);
 }
 
@@ -333,25 +342,41 @@ verify_signature (struct parse_info_s *info)
 
 
 
-/* Prepare for a multipart/signed. 
+/* 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;
+
+  (void)msg;
+
   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 (!strcmp (s, "application/pgp-signature"))
         {
-          if (info->gpgsm_mime)
-            err ("note: ignoring nested pkcs7-signature");
+          if (info->smfm_state)
+            err ("note: ignoring nested PGP/MIME or S/MIME signature");
           else
             {
-              info->gpgsm_mime = 1;
+              info->smfm_state = 1;
+              info->is_smime = 0;
+              free (info->signing_protocol);
+              info->signing_protocol = xstrdup (s);
+            }
+        }
+      else if (!strcmp (s, "application/pkcs7-signature")
+               || !strcmp (s, "application/x-pkcs7-signature"))
+        {
+          if (info->smfm_state)
+            err ("note: ignoring nested PGP/MIME or S/MIME signature");
+          else
+            {
+              info->smfm_state = 1;
+              info->is_smime = 1;
               free (info->signing_protocol);
               info->signing_protocol = xstrdup (s);
             }
@@ -362,13 +387,17 @@ mime_signed_begin (struct parse_info_s *info, rfc822parse_t msg,
 }
 
 
-/* Prepare for a multipart/encrypted. 
+/* 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;
+
+  (void)info;
+  (void)msg;
+
   s = rfc822parse_query_parameter (field_ctx, "protocol", 0);
   if (s)
     printf ("h encrypted.protocol: %s\n", s);
@@ -381,6 +410,9 @@ pkcs7_begin (struct parse_info_s *info, rfc822parse_t msg,
              rfc822parse_field_t field_ctx)
 {
   const char *s;
+
+  (void)msg;
+
   s = rfc822parse_query_parameter (field_ctx, "name", 0);
   if (s)
     printf ("h pkcs7.name: %s\n", s);
@@ -437,6 +469,30 @@ message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
 
   if (debug)
     show_event (event);
+
+  if (event == RFC822PARSE_BEGIN_HEADER || event == RFC822PARSE_T2BODY)
+    {
+      /* We need to check here whether to start collecting signed data
+         because attachments might come without header lines and thus
+         we won't see the BEGIN_HEADER event.  */
+      if (info->smfm_state == 1)
+        {
+          printf ("c begin_hash\n");
+          info->hashing = 1;
+          info->hashing_level = info->nesting_level;
+          info->smfm_state++;
+
+          if (opt_crypto)
+            {
+              assert (!info->hash_file);
+              info->hash_file = tmpfile ();
+              if (!info->hash_file)
+                die ("failed to create temporary file: %s", strerror (errno));
+            }
+        }
+    }
+
+
   if (event == RFC822PARSE_OPEN)
     {
       /* Initialize for a new message. */
@@ -453,20 +509,21 @@ message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
           s1 = rfc822parse_query_media_type (ctx, &s2);
           if (s1)
             {
-              printf ("h media: %*s%s %s\n", 
+              printf ("h media: %*s%s %s\n",
                       info->nesting_level*2, "", s1, s2);
-              if (info->gpgsm_mime == 3)
+              if (info->smfm_state == 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'",
+                    err ("invalid %s structure; expected '%s', found '%s'",
+                         info->is_smime? "S/MIME":"PGP/MIME",
                          info->signing_protocol, buf);
                   else
                     {
                       printf ("c begin_signature\n");
-                      info->gpgsm_mime++;
+                      info->smfm_state++;
                       if (opt_crypto)
                         {
                           assert (!info->sig_file);
@@ -498,6 +555,8 @@ message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
       else
         printf ("h media: %*stext plain [assumed]\n",
                 info->nesting_level*2, "");
+
+
       info->show_header = 0;
       info->show_data = 1;
       info->skip_show = 1;
@@ -514,7 +573,7 @@ message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
       printf ("b up\n");
       if (info->nesting_level)
         info->nesting_level--;
-      else 
+      else
         err ("invalid structure (bad nesting level)");
     }
   else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY)
@@ -527,39 +586,21 @@ message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
           info->skip_show = 1;
           printf ("b part\n");
         }
-      else 
+      else
         printf ("b last\n");
 
-      if (info->gpgsm_mime == 2 && info->nesting_level == info->hashing_level)
+      if (info->smfm_state == 2 && info->nesting_level == info->hashing_level)
         {
           printf ("c end_hash\n");
-          info->gpgsm_mime++;
+          info->smfm_state++;
           info->hashing = 0;
         }
-      else if (info->gpgsm_mime == 4)
+      else if (info->smfm_state == 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;
 }
@@ -583,7 +624,7 @@ parse_message (FILE *fp)
   if (!msg)
     die ("can't open parser: %s", strerror (errno));
 
-  /* Fixme: We should not use fgets becuase it can't cope with
+  /* Fixme: We should not use fgets because it can't cope with
      embedded nul characters. */
   while (fgets (line, sizeof (line), fp))
     {
@@ -607,13 +648,13 @@ parse_message (FILE *fp)
 
       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);
+            printf ("# hashing %s'%s'\n", info.hashing==2?"CR,LF+":"", line);
           if (opt_crypto)
             {
               if (info.hashing == 2)
@@ -636,7 +677,8 @@ parse_message (FILE *fp)
               info.hash_file = NULL;
               fclose (info.sig_file);
               info.sig_file = NULL;
-              info.gpgsm_mime = 0;
+              info.smfm_state = 0;
+              info.is_smime = 0;
               info.is_pkcs7 = 0;
             }
           else
@@ -647,7 +689,7 @@ parse_message (FILE *fp)
                 die ("error writing to temporary file: %s", strerror (errno));
             }
         }
-      
+
       if (info.show_boundary)
         {
           if (!opt_no_header)
@@ -685,11 +727,11 @@ parse_message (FILE *fp)
 }
 
 
-int 
+int
 main (int argc, char **argv)
 {
   int last_argc = -1;
+
   if (argc)
     {
       argc--; argv++;
@@ -738,8 +780,8 @@ main (int argc, char **argv)
           opt_no_header = 1;
           argc--; argv++;
         }
-    }          
+    }
+
   if (argc > 1)
     die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
 
@@ -749,7 +791,7 @@ main (int argc, char **argv)
     {
       FILE *fp = fopen (*argv, "rb");
       if (!fp)
-        die ("can't open `%s': %s", *argv, strerror (errno));
+        die ("can't open '%s': %s", *argv, strerror (errno));
       parse_message (fp);
       fclose (fp);
     }
@@ -762,6 +804,6 @@ main (int argc, char **argv)
 
 /*
 Local Variables:
-compile-command: "gcc -Wall -g -o gpgparsemail rfc822parse.c gpgparsemail.c"
+compile-command: "gcc -Wall -Wno-pointer-sign -g -o gpgparsemail rfc822parse.c gpgparsemail.c"
 End:
 */