wks: Partly implement draft-koch-openpgp-webkey-service-02.
authorWerner Koch <wk@gnupg.org>
Thu, 29 Sep 2016 15:55:32 +0000 (17:55 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 29 Sep 2016 15:59:41 +0000 (17:59 +0200)
* tools/gpg-wks.h (WKS_RECEIVE_DRAFT2): New.
* tools/wks-receive.c: Include rfc822parse.h.
(struct receive_ctx_s): Add fields PARSER, DRAFT_VERSION_2, and
MULTIPART_MIXED_SEEN.
(decrypt_data): Add --no-options.
(verify_signature): Ditto.
(new_part): Check for Wks-Draft-Version header.  Take care of text
parts.
(wks_receive): Set Parser and pass a flag value to RESULT_CB.
* tools/gpg-wks-client.c (read_confirmation_request): New.
(main) <aRead>: Call read_confirmation_request instead of
process_confirmation_request.
(command_receive_cb): Ditto.  Add arg FLAGS..
(decrypt_stream_status_cb, decrypt_stream): New.
(command_send): Set header Wks-Draft-Version.
* tools/gpg-wks-server.c (struct server_ctx_s): Add field
DRAFT_VERSION_2.
(sign_stream_status_cb, sign_stream): New.
(command_receive_cb): Set draft flag.
(send_confirmation_request): Rework to implement protocol draft
version 2.

* tools/gpg-wks.h (DBG_MIME_VALUE, DBG_PARSER_VALUE): New.
(DBG_MIME, DBG_PARSER, DBG_CRYPTO): New.  Use instead of a plain
opt.debug where useful.
* tools/gpg-wks-client.c (debug_flags): Add "mime" and "parser".
* tools/gpg-wks-server.c (debug_flags): Ditto.
--

If a client supporting the version 2 of the protocol is used, it will
tell this the server using a mail header.  An old server will ignore
that but a recent server will use the new protocol.  Next task is to
actually write draft-02.

There are still a lot of FIXMEs - take care.

Signed-off-by: Werner Koch <wk@gnupg.org>
tools/gpg-wks-client.c
tools/gpg-wks-server.c
tools/gpg-wks.h
tools/wks-receive.c

index 143dbc8..f4257ec 100644 (file)
@@ -91,6 +91,8 @@ static ARGPARSE_OPTS opts[] = {
 /* The list of supported debug flags.  */
 static struct debug_flags_s debug_flags [] =
   {
+    { DBG_MIME_VALUE   , "mime"    },
+    { DBG_PARSER_VALUE , "parser"  },
     { DBG_CRYPTO_VALUE , "crypto"  },
     { DBG_MEMORY_VALUE , "memory"  },
     { DBG_MEMSTAT_VALUE, "memstat" },
@@ -103,9 +105,10 @@ static struct debug_flags_s debug_flags [] =
 static void wrong_args (const char *text) GPGRT_ATTR_NORETURN;
 static gpg_error_t command_supported (char *userid);
 static gpg_error_t command_send (const char *fingerprint, char *userid);
-static gpg_error_t process_confirmation_request (estream_t msg);
+static gpg_error_t read_confirmation_request (estream_t msg);
 static gpg_error_t command_receive_cb (void *opaque,
-                                       const char *mediatype, estream_t fp);
+                                       const char *mediatype, estream_t fp,
+                                       unsigned int flags);
 
 
 \f
@@ -269,7 +272,7 @@ main (int argc, char **argv)
     case aRead:
       if (argc)
         wrong_args ("--read < WKS-DATA");
-      err = process_confirmation_request (es_stdin);
+      err = read_confirmation_request (es_stdin);
       if (err)
         log_error ("processing mail failed: %s\n", gpg_strerror (err));
       break;
@@ -394,6 +397,83 @@ get_key (estream_t *r_key, const char *fingerprint, const char *addrspec)
 
 
 \f
+static void
+decrypt_stream_status_cb (void *opaque, const char *keyword, char *args)
+{
+  (void)opaque;
+
+  if (DBG_CRYPTO)
+    log_debug ("gpg status: %s %s\n", keyword, args);
+}
+
+
+/* Decrypt the INPUT stream to a new stream which is stored at success
+ * at R_OUTPUT.  */
+static gpg_error_t
+decrypt_stream (estream_t *r_output, estream_t input)
+{
+  gpg_error_t err;
+  ccparray_t ccp;
+  const char **argv;
+  estream_t output;
+
+  *r_output = NULL;
+
+  output = es_fopenmem (0, "w+b");
+  if (!output)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
+      return err;
+    }
+
+  ccparray_init (&ccp, 0);
+
+  ccparray_put (&ccp, "--no-options");
+  /* We limit the output to 64 KiB to avoid DoS using compression
+   * tricks.  A regular client will anyway only send a minimal key;
+   * that is one w/o key signatures and attribute packets.  */
+  ccparray_put (&ccp, "--max-output=0x10000");
+  if (!opt.verbose)
+    ccparray_put (&ccp, "--quiet");
+  else if (opt.verbose > 1)
+    ccparray_put (&ccp, "--verbose");
+  ccparray_put (&ccp, "--batch");
+  ccparray_put (&ccp, "--status-fd=2");
+  ccparray_put (&ccp, "--decrypt");
+  ccparray_put (&ccp, "--");
+
+  ccparray_put (&ccp, NULL);
+  argv = ccparray_get (&ccp, NULL);
+  if (!argv)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  err = gnupg_exec_tool_stream (opt.gpg_program, argv, input,
+                                NULL, output,
+                                decrypt_stream_status_cb, NULL);
+  if (err)
+    {
+      log_error ("decryption failed: %s\n", gpg_strerror (err));
+      goto leave;
+    }
+  else if (opt.verbose)
+    log_info ("decryption succeeded\n");
+
+  es_rewind (output);
+  *r_output = output;
+  output = NULL;
+
+ leave:
+  es_fclose (output);
+  xfree (argv);
+  return err;
+}
+
+
+
+\f
 /* Check whether the  provider supports the WKS protocol.  */
 static gpg_error_t
 command_supported (char *userid)
@@ -517,6 +597,11 @@ command_send (const char *fingerprint, char *userid)
   if (err)
     goto leave;
 
+  /* Tell server that we support draft version 3.  */
+  err = mime_maker_add_header (mime, "Wks-Draft-Version", "3");
+  if (err)
+    goto leave;
+
   err = mime_maker_add_stream (mime, &key);
   if (err)
     goto leave;
@@ -539,8 +624,8 @@ encrypt_response_status_cb (void *opaque, const char *keyword, char *args)
   gpg_error_t *failure = opaque;
   char *fields[2];
 
-  if (opt.debug)
-    log_debug ("%s: %s\n", keyword, args);
+  if (DBG_CRYPTO)
+    log_debug ("gpg status: %s %s\n", keyword, args);
 
   if (!strcmp (keyword, "FAILURE"))
     {
@@ -747,7 +832,7 @@ process_confirmation_request (estream_t msg)
       goto leave;
     }
 
-  if (opt.debug)
+  if (DBG_MIME)
     {
       log_debug ("request follows:\n");
       nvc_write (nvc, log_get_stream ());
@@ -822,16 +907,62 @@ process_confirmation_request (estream_t msg)
 }
 
 
+/* Read a confirmation request and decrypt it if needed.  This
+ * function may not be used with a mail or MIME message but only with
+ * the actual encrypted or plaintext WKS data.  */
+static gpg_error_t
+read_confirmation_request (estream_t msg)
+{
+  gpg_error_t err;
+  int c;
+  estream_t plaintext = NULL;
+
+  /* We take a really simple approach to check whether MSG is
+   * encrypted: We know that an encrypted message is always armored
+   * and thus starts with a few dashes.  It is even sufficient to
+   * check for a single dash, because that can never be a proper first
+   * WKS data octet.  We need to skip leading spaces, though. */
+  while ((c = es_fgetc (msg)) == ' ' || c == '\t' || c == '\r' || c == '\n')
+    ;
+  if (c == EOF)
+    {
+      log_error ("can't process an empty message\n");
+      return gpg_error (GPG_ERR_INV_DATA);
+    }
+  if (es_ungetc (c, msg) != c)
+    {
+      log_error ("error ungetting octet from message\n");
+      return gpg_error (GPG_ERR_INTERNAL);
+    }
+
+  if (c != '-')
+    err = process_confirmation_request (msg);
+  else
+    {
+      err = decrypt_stream (&plaintext, msg);
+      if (err)
+        log_error ("decryption failed: %s\n", gpg_strerror (err));
+      else
+        err = process_confirmation_request (plaintext);
+    }
+
+  es_fclose (plaintext);
+  return err;
+}
+
+
 /* Called from the MIME receiver to process the plain text data in MSG.  */
 static gpg_error_t
-command_receive_cb (void *opaque, const char *mediatype, estream_t msg)
+command_receive_cb (void *opaque, const char *mediatype,
+                    estream_t msg, unsigned int flags)
 {
   gpg_error_t err;
 
   (void)opaque;
+  (void)flags;
 
   if (!strcmp (mediatype, "application/vnd.gnupg.wks"))
-    err = process_confirmation_request (msg);
+    err = read_confirmation_request (msg);
   else
     {
       log_info ("ignoring unexpected message of type '%s'\n", mediatype);
index 96e5e05..408e3f5 100644 (file)
@@ -102,6 +102,8 @@ static ARGPARSE_OPTS opts[] = {
 /* The list of supported debug flags.  */
 static struct debug_flags_s debug_flags [] =
   {
+    { DBG_MIME_VALUE   , "mime"    },
+    { DBG_PARSER_VALUE , "parser"  },
     { DBG_CRYPTO_VALUE , "crypto"  },
     { DBG_MEMORY_VALUE , "memory"  },
     { DBG_MEMSTAT_VALUE, "memstat" },
@@ -116,6 +118,7 @@ struct server_ctx_s
 {
   char *fpr;
   strlist_t mboxes;  /* List of addr-specs taken from the UIDs.  */
+  unsigned int draft_version_2:1; /* Client supports the draft 2.  */
 };
 typedef struct server_ctx_s *server_ctx_t;
 
@@ -123,7 +126,8 @@ typedef struct server_ctx_s *server_ctx_t;
 static gpg_error_t get_domain_list (strlist_t *r_list);
 
 static gpg_error_t command_receive_cb (void *opaque,
-                                       const char *mediatype, estream_t fp);
+                                       const char *mediatype, estream_t fp,
+                                       unsigned int flags);
 static gpg_error_t command_list_domains (void);
 static gpg_error_t command_cron (void);
 
@@ -350,8 +354,8 @@ list_key_status_cb (void *opaque, const char *keyword, char *args)
 {
   server_ctx_t ctx = opaque;
   (void)ctx;
-  if (opt.debug)
-    log_debug ("%s: %s\n", keyword, args);
+  if (DBG_CRYPTO)
+    log_debug ("gpg status: %s %s\n", keyword, args);
 }
 
 
@@ -629,8 +633,8 @@ encrypt_stream_status_cb (void *opaque, const char *keyword, char *args)
 {
   (void)opaque;
 
-  if (opt.debug)
-    log_debug ("%s: %s\n", keyword, args);
+  if (DBG_CRYPTO)
+    log_debug ("gpg status: %s %s\n", keyword, args);
 }
 
 
@@ -698,6 +702,78 @@ encrypt_stream (estream_t *r_output, estream_t input, const char *keyfile)
 }
 
 
+static void
+sign_stream_status_cb (void *opaque, const char *keyword, char *args)
+{
+  (void)opaque;
+
+  if (DBG_CRYPTO)
+    log_debug ("gpg status: %s %s\n", keyword, args);
+}
+
+/* Sign the INPUT stream to a new stream which is stored at success at
+ * R_OUTPUT.  A detached signature is created using the key specified
+ * by USERID.  */
+static gpg_error_t
+sign_stream (estream_t *r_output, estream_t input, const char *userid)
+{
+  gpg_error_t err;
+  ccparray_t ccp;
+  const char **argv;
+  estream_t output;
+
+  *r_output = NULL;
+
+  output = es_fopenmem (0, "w+b");
+  if (!output)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
+      return err;
+    }
+
+  ccparray_init (&ccp, 0);
+
+  ccparray_put (&ccp, "--no-options");
+  if (!opt.verbose)
+    ccparray_put (&ccp, "--quiet");
+  else if (opt.verbose > 1)
+    ccparray_put (&ccp, "--verbose");
+  ccparray_put (&ccp, "--batch");
+  ccparray_put (&ccp, "--status-fd=2");
+  ccparray_put (&ccp, "--armor");
+  ccparray_put (&ccp, "--local-user");
+  ccparray_put (&ccp, userid);
+  ccparray_put (&ccp, "--detach-sign");
+  ccparray_put (&ccp, "--");
+
+  ccparray_put (&ccp, NULL);
+  argv = ccparray_get (&ccp, NULL);
+  if (!argv)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  err = gnupg_exec_tool_stream (opt.gpg_program, argv, input,
+                                NULL, output,
+                                sign_stream_status_cb, NULL);
+  if (err)
+    {
+      log_error ("signing failed: %s\n", gpg_strerror (err));
+      goto leave;
+    }
+
+  es_rewind (output);
+  *r_output = output;
+  output = NULL;
+
+ leave:
+  es_fclose (output);
+  xfree (argv);
+  return err;
+}
+
+
 /* Get the submission address for address MBOX.  Caller must free the
  * value.  If no address can be found NULL is returned.  */
 static char *
@@ -933,6 +1009,8 @@ send_confirmation_request (server_ctx_t ctx,
   gpg_error_t err;
   estream_t body = NULL;
   estream_t bodyenc = NULL;
+  estream_t signeddata = NULL;
+  estream_t signature = NULL;
   mime_maker_t mime = NULL;
   char *from_buffer = NULL;
   const char *from;
@@ -958,12 +1036,16 @@ send_confirmation_request (server_ctx_t ctx,
       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
       goto leave;
     }
-  /* It is fine to use 8 bit encoding because that is encrypted and
-   * only our client will see it.  */
-  es_fputs ("Content-Type: application/vnd.gnupg.wks\n"
-            "Content-Transfer-Encoding: 8bit\n"
-            "\n",
-            body);
+
+  if (!ctx->draft_version_2)
+    {
+      /* It is fine to use 8 bit encoding because that is encrypted and
+       * only our client will see it.  */
+      es_fputs ("Content-Type: application/vnd.gnupg.wks\n"
+                "Content-Transfer-Encoding: 8bit\n"
+                "\n",
+                body);
+    }
 
   es_fprintf (body, ("type: confirmation-request\n"
                      "sender: %s\n"
@@ -1002,35 +1084,117 @@ send_confirmation_request (server_ctx_t ctx,
         goto leave;
     }
 
-  err = mime_maker_add_header (mime, "Content-Type",
-                               "multipart/encrypted; "
-                               "protocol=\"application/pgp-encrypted\"");
-  if (err)
-    goto leave;
-  err = mime_maker_add_container (mime);
-  if (err)
-    goto leave;
+  if (!ctx->draft_version_2)
+    {
+      err = mime_maker_add_header (mime, "Content-Type",
+                                   "multipart/encrypted; "
+                                   "protocol=\"application/pgp-encrypted\"");
+      if (err)
+        goto leave;
+      err = mime_maker_add_container (mime);
+      if (err)
+        goto leave;
 
-  err = mime_maker_add_header (mime, "Content-Type",
-                               "application/pgp-encrypted");
-  if (err)
-    goto leave;
-  err = mime_maker_add_body (mime, "Version: 1\n");
-  if (err)
-    goto leave;
-  err = mime_maker_add_header (mime, "Content-Type",
-                               "application/octet-stream");
-  if (err)
-    goto leave;
+      err = mime_maker_add_header (mime, "Content-Type",
+                                   "application/pgp-encrypted");
+      if (err)
+        goto leave;
+      err = mime_maker_add_body (mime, "Version: 1\n");
+      if (err)
+        goto leave;
+      err = mime_maker_add_header (mime, "Content-Type",
+                                   "application/octet-stream");
+      if (err)
+        goto leave;
 
-  err = mime_maker_add_stream (mime, &bodyenc);
-  if (err)
-    goto leave;
+      err = mime_maker_add_stream (mime, &bodyenc);
+      if (err)
+        goto leave;
+
+    }
+  else
+    {
+      unsigned int partid;
+
+      /* FIXME: Add micalg.  */
+      err = mime_maker_add_header (mime, "Content-Type",
+                                   "multipart/signed; "
+                                   "protocol=\"application/pgp-signature\"");
+      if (err)
+        goto leave;
+      err = mime_maker_add_container (mime);
+      if (err)
+        goto leave;
+
+      err = mime_maker_add_header (mime, "Content-Type", "multipart/mixed");
+      if (err)
+        goto leave;
+
+      err = mime_maker_add_container (mime);
+      if (err)
+        goto leave;
+      partid = mime_maker_get_partid (mime);
+
+      err = mime_maker_add_header (mime, "Content-Type", "text/plain");
+      if (err)
+        goto leave;
+
+      err = mime_maker_add_body
+        (mime,
+         "This message has been send to confirm your request\n"
+         "to publish your key.  If you did not request a key\n"
+         "publication, simply ignore this message.\n"
+         "\n"
+         "Most mail software can handle this kind of message\n"
+         "automatically and thus you would not have seen this\n"
+         "message.  It seems that your client does not fully\n"
+         "support this service.  The web page\n"
+         "\n"
+         "       https://gnupg.org/faq/wkd.html\n"
+         "\n"
+         "explains how you can process this message anyway in\n"
+         "a few manual steps.\n");
+      if (err)
+        goto leave;
+
+      err = mime_maker_add_header (mime, "Content-Type",
+                                   "application/vnd.gnupg.wks");
+      if (err)
+        goto leave;
+
+      err = mime_maker_add_stream (mime, &bodyenc);
+      if (err)
+        goto leave;
+
+      err = mime_maker_end_container (mime);
+      if (err)
+        goto leave;
+
+      mime_maker_dump_tree (mime);
+      err = mime_maker_get_part (mime, partid, &signeddata);
+      if (err)
+        goto leave;
+
+      err = sign_stream (&signature, signeddata, from);
+      if (err)
+        goto leave;
+
+      err = mime_maker_add_header (mime, "Content-Type",
+                                   "application/pgp-signature");
+      if (err)
+        goto leave;
+
+      err = mime_maker_add_stream (mime, &signature);
+      if (err)
+        goto leave;
+    }
 
   err = wks_send_mime (mime);
 
  leave:
   mime_maker_release (mime);
+  es_fclose (signature);
+  es_fclose (signeddata);
   es_fclose (bodyenc);
   es_fclose (body);
   xfree (from_buffer);
@@ -1478,15 +1642,18 @@ process_confirmation_response (server_ctx_t ctx, estream_t msg)
 \f
 /* Called from the MIME receiver to process the plain text data in MSG .  */
 static gpg_error_t
-command_receive_cb (void *opaque, const char *mediatype, estream_t msg)
+command_receive_cb (void *opaque, const char *mediatype,
+                    estream_t msg, unsigned int flags)
 {
   gpg_error_t err;
   struct server_ctx_s ctx;
 
-  memset (&ctx, 0, sizeof ctx);
-
   (void)opaque;
 
+  memset (&ctx, 0, sizeof ctx);
+  if ((flags & WKS_RECEIVE_DRAFT2))
+    ctx.draft_version_2 = 1;
+
   if (!strcmp (mediatype, "application/pgp-keys"))
     err = process_new_key (&ctx, msg);
   else if (!strcmp (mediatype, "application/vnd.gnupg.wks"))
index 85000cc..f8b6cfd 100644 (file)
@@ -39,12 +39,18 @@ struct
 } opt;
 
 /* Debug values and macros.  */
+#define DBG_MIME_VALUE        1 /* Debug the MIME structure.  */
+#define DBG_PARSER_VALUE      2 /* Debug the Mail parser.  */
 #define DBG_CRYPTO_VALUE      4        /* Debug low level crypto.  */
 #define DBG_MEMORY_VALUE     32        /* Debug memory allocation stuff.  */
 #define DBG_MEMSTAT_VALUE   128        /* Show memory statistics.  */
 #define DBG_IPC_VALUE      1024 /* Debug assuan communication.  */
 #define DBG_EXTPROG_VALUE 16384 /* debug external program calls */
 
+#define DBG_MIME     (opt.debug & DBG_MIME_VALUE)
+#define DBG_PARSER   (opt.debug & DBG_PARSER_VALUE)
+#define DBG_CRYPTO   (opt.debug & DBG_CRYPTO_VALUE)
+
 
 /* The parsed policy flags. */
 struct policy_flags_s
@@ -64,10 +70,15 @@ gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream,
                               int ignore_unknown);
 
 /*-- wks-receive.c --*/
+
+/* Flag values for the receive callback.  */
+#define WKS_RECEIVE_DRAFT2 1
+
 gpg_error_t wks_receive (estream_t fp,
                          gpg_error_t (*result_cb)(void *opaque,
                                                   const char *mediatype,
-                                                  estream_t data),
+                                                  estream_t data,
+                                                  unsigned int flags),
                          void *cb_data);
 
 
index 59141fc..0deca9b 100644 (file)
@@ -26,6 +26,7 @@
 #include "ccparray.h"
 #include "exectool.h"
 #include "gpg-wks.h"
+#include "rfc822parse.h"
 #include "mime-parser.h"
 
 
@@ -41,6 +42,7 @@
 /* Data for a received object.  */
 struct receive_ctx_s
 {
+  mime_parser_t parser;
   estream_t encrypted;
   estream_t plaintext;
   estream_t signeddata;
@@ -49,6 +51,8 @@ struct receive_ctx_s
   estream_t wkd_data;
   unsigned int collect_key_data:1;
   unsigned int collect_wkd_data:1;
+  unsigned int draft_version_2:1;  /* This is a draft version 2 request.  */
+  unsigned int multipart_mixed_seen:1;
 };
 typedef struct receive_ctx_s *receive_ctx_t;
 
@@ -59,7 +63,8 @@ decrypt_data_status_cb (void *opaque, const char *keyword, char *args)
 {
   receive_ctx_t ctx = opaque;
   (void)ctx;
-  log_debug ("%s: %s\n", keyword, args);
+  if (DBG_CRYPTO)
+    log_debug ("gpg status: %s %s\n", keyword, args);
 }
 
 
@@ -86,6 +91,7 @@ decrypt_data (receive_ctx_t ctx)
 
   ccparray_init (&ccp, 0);
 
+  ccparray_put (&ccp, "--no-options");
   /* We limit the output to 64 KiB to avoid DoS using compression
    * tricks.  A regular client will anyway only send a minimal key;
    * that is one w/o key signatures and attribute packets.  */
@@ -113,7 +119,7 @@ decrypt_data (receive_ctx_t ctx)
       goto leave;
     }
 
-  if (opt.debug)
+  if (DBG_CRYPTO)
     {
       es_rewind (ctx->plaintext);
       log_debug ("plaintext: '");
@@ -133,7 +139,8 @@ verify_signature_status_cb (void *opaque, const char *keyword, char *args)
 {
   receive_ctx_t ctx = opaque;
   (void)ctx;
-  log_debug ("%s: %s\n", keyword, args);
+  if (DBG_CRYPTO)
+    log_debug ("gpg status: %s %s\n", keyword, args);
 }
 
 /* Verify the signed data.  */
@@ -151,6 +158,7 @@ verify_signature (receive_ctx_t ctx)
 
   ccparray_init (&ccp, 0);
 
+  ccparray_put (&ccp, "--no-options");
   ccparray_put (&ccp, "--batch");
   if (opt.verbose)
     ccparray_put (&ccp, "--verbose");
@@ -177,6 +185,8 @@ verify_signature (receive_ctx_t ctx)
       goto leave;
     }
 
+  log_debug ("Fixme: Verification result is not used\n");
+
  leave:
   xfree (argv);
 }
@@ -264,6 +274,22 @@ new_part (void *cookie, const char *mediatype, const char *mediasubtype)
         }
       else
         {
+          rfc822parse_t msg = mime_parser_rfc822parser (ctx->parser);
+          if (msg)
+            {
+              char *value;
+              size_t valueoff;
+
+              value = rfc822parse_get_field (msg, "Wks-Draft-Version",
+                                             -1, &valueoff);
+              if (value)
+                {
+                  if (atoi(value+valueoff) >= 2 )
+                    ctx->draft_version_2 = 1;
+                  free (value);
+                }
+            }
+
           ctx->key_data = es_fopenmem (0, "w+b");
           if (!ctx->key_data)
             {
@@ -303,6 +329,19 @@ new_part (void *cookie, const char *mediatype, const char *mediasubtype)
             }
         }
     }
+  else if (!strcmp (mediatype, "multipart")
+           && !strcmp (mediasubtype, "mixed"))
+    {
+      ctx->multipart_mixed_seen = 1;
+    }
+  else if (!strcmp (mediatype, "text"))
+    {
+      /* Check that we receive a text part only after a
+       * application/mixed.  This is actually a too simple test and we
+       * should eventually employ a strict MIME structure check.  */
+      if (!ctx->multipart_mixed_seen)
+        err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
+    }
   else
     {
       log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype);
@@ -320,7 +359,7 @@ part_data (void *cookie, const void *data, size_t datalen)
 
   if (data)
     {
-      if (opt.debug)
+      if (DBG_MIME)
         log_debug ("part_data: '%.*s'\n", (int)datalen, (const char*)data);
       if (ctx->collect_key_data)
         {
@@ -337,7 +376,7 @@ part_data (void *cookie, const void *data, size_t datalen)
     }
   else
     {
-      if (opt.debug)
+      if (DBG_MIME)
         log_debug ("part_data: finished\n");
       ctx->collect_key_data = 0;
       ctx->collect_wkd_data = 0;
@@ -353,7 +392,8 @@ gpg_error_t
 wks_receive (estream_t fp,
              gpg_error_t (*result_cb)(void *opaque,
                                       const char *mediatype,
-                                      estream_t data),
+                                      estream_t data,
+                                      unsigned int flags),
              void *cb_data)
 {
   gpg_error_t err;
@@ -361,6 +401,7 @@ wks_receive (estream_t fp,
   mime_parser_t parser;
   estream_t plaintext = NULL;
   int c;
+  unsigned int flags = 0;
 
   ctx = xtrycalloc (1, sizeof *ctx);
   if (!ctx)
@@ -369,14 +410,16 @@ wks_receive (estream_t fp,
   err = mime_parser_new (&parser, ctx);
   if (err)
     goto leave;
-  if (opt.verbose > 1 || opt.debug)
-    mime_parser_set_verbose (parser, opt.debug? 10: 1);
+  if (DBG_PARSER)
+    mime_parser_set_verbose (parser, 1);
   mime_parser_set_new_part (parser, new_part);
   mime_parser_set_part_data (parser, part_data);
   mime_parser_set_collect_encrypted (parser, collect_encrypted);
   mime_parser_set_collect_signeddata (parser, collect_signeddata);
   mime_parser_set_collect_signature (parser, collect_signature);
 
+  ctx->parser = parser;
+
   err = mime_parser_parse (parser, fp);
   if (err)
     goto leave;
@@ -385,6 +428,11 @@ wks_receive (estream_t fp,
     log_info ("key data found\n");
   if (ctx->wkd_data)
     log_info ("wkd data found\n");
+  if (ctx->draft_version_2)
+    {
+      log_info ("draft version 2 requested\n");
+      flags |= WKS_RECEIVE_DRAFT2;
+    }
 
   if (ctx->plaintext)
     {
@@ -412,7 +460,7 @@ wks_receive (estream_t fp,
 
   if (ctx->key_data)
     {
-      if (opt.debug)
+      if (DBG_MIME)
         {
           es_rewind (ctx->key_data);
           log_debug ("Key: '");
@@ -424,14 +472,15 @@ wks_receive (estream_t fp,
       if (result_cb)
         {
           es_rewind (ctx->key_data);
-          err = result_cb (cb_data, "application/pgp-keys", ctx->key_data);
+          err = result_cb (cb_data, "application/pgp-keys",
+                           ctx->key_data, flags);
           if (err)
             goto leave;
         }
     }
   if (ctx->wkd_data)
     {
-      if (opt.debug)
+      if (DBG_MIME)
         {
           es_rewind (ctx->wkd_data);
           log_debug ("WKD: '");
@@ -443,7 +492,8 @@ wks_receive (estream_t fp,
       if (result_cb)
         {
           es_rewind (ctx->wkd_data);
-          err = result_cb (cb_data, "application/vnd.gnupg.wks", ctx->wkd_data);
+          err = result_cb (cb_data, "application/vnd.gnupg.wks",
+                           ctx->wkd_data, flags);
           if (err)
             goto leave;
         }
@@ -453,6 +503,7 @@ wks_receive (estream_t fp,
  leave:
   es_fclose (plaintext);
   mime_parser_release (parser);
+  ctx->parser = NULL;
   es_fclose (ctx->encrypted);
   es_fclose (ctx->plaintext);
   es_fclose (ctx->signeddata);