Add some new features - not fully working yet.
authorWerner Koch <wk@gnupg.org>
Tue, 13 Sep 2005 17:21:29 +0000 (17:21 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 13 Sep 2005 17:21:29 +0000 (17:21 +0000)
src/ChangeLog
src/engine-gpgme.c
src/engine.h
src/gpgmsg.cpp
src/pgpmime.c
src/pgpmime.h
src/util.h

index afb9995..114f6a3 100644 (file)
@@ -1,4 +1,22 @@
-2004-09-08  Timo Schulz  <ts@g10code.com>
+2005-09-13  Werner Koch  <wk@g10code.com>
+
+       * pgpmime.c (pgpmime_decrypt): New arg ATTESTATION.
+       * engine-gpgme.c (add_verify_attestation): New.
+       (op_decrypt, op_verify_detached_sig, op_verify_detached_sig)
+       (op_verify_detached_sig): Add new arg ATTESTATION.  Changed all
+       callers.
+       (at_sig_summary, at_sig_validity, add_verify_attestation): New.
+       The code has been taken and modified from Mutt's crypt-gpgme.c and
+       entirely been writen by g10 Code.
+       (at_fingerprint): Ditto.
+
+       * gpgmsg.cpp (class GpgMsgImpl): New member ATTESTATION.  Use it
+       in all calls to the functions above.
+
+       * gpgmsg.cpp (decryptAttachment, decrypt): Save plaintext back
+       into the MAPI if desired.
+
+g2004-09-08  Timo Schulz  <ts@g10code.com>
 
        * passphrase-dialog.c (lod_recipbox): Use gpgme directly
        to resolve the keyids to userids.
index 87f8440..afcdf6d 100644 (file)
 static char *debug_file = NULL;
 static int init_done = 0;
 
+
+static void add_verify_attestation (gpgme_data_t at, 
+                                    gpgme_ctx_t ctx, 
+                                    gpgme_verify_result_t res,
+                                    const char *filename);
+
+
+
 static void
 cleanup (void)
 {
@@ -474,9 +482,11 @@ op_sign_stream (LPSTREAM instream, LPSTREAM outstream, int mode,
 /* Run the decryption.  Decrypts INBUF to OUTBUF, caller must xfree
    the result at OUTBUF.  TTL is the time in seconds to cache a
    passphrase.  If FILENAME is not NULL it will be displayed along
-   with status outputs. */
+   with status outputs. If ATTESTATION is not NULL a text with the
+   result of the signature verification will get printed to it. */
 int 
-op_decrypt (const char *inbuf, char **outbuf, int ttl, const char *filename)
+op_decrypt (const char *inbuf, char **outbuf, int ttl, const char *filename,
+            gpgme_data_t attestation)
 {
   struct decrypt_key_s dk;
   gpgme_data_t in = NULL;
@@ -524,6 +534,8 @@ op_decrypt (const char *inbuf, char **outbuf, int ttl, const char *filename)
       res = gpgme_op_verify_result (ctx);
       if (res && res->signatures)
         verify_dialog_box (res, filename);
+      if (res && res->signatures && attestation)
+        add_verify_attestation (attestation, ctx, res, filename);
     }
   else if (gpgme_err_code (err) == GPG_ERR_DECRYPT_FAILED)
     {
@@ -560,10 +572,12 @@ leave:
 
 /* Decrypt the GPGME data object IN into the data object OUT.  Returns
    0 on success or an gpgme error code on failure.  If FILENAME is not
-   NULL it will be displayed along with status outputs. */
+   NULL it will be displayed along with status outputs. If ATTESTATION
+   is not NULL a text with the result of the signature verification
+   will get printed to it. */
 static int
 decrypt_stream (gpgme_data_t in, gpgme_data_t out, int ttl,
-                const char *filename)
+                const char *filename, gpgme_data_t attestation)
 {    
   struct decrypt_key_s dk;
   gpgme_ctx_t ctx = NULL;
@@ -590,6 +604,8 @@ decrypt_stream (gpgme_data_t in, gpgme_data_t out, int ttl,
       res = gpgme_op_verify_result (ctx);
       if (res && res->signatures)
         verify_dialog_box (res, filename);
+      if (res && res->signatures && attestation)
+        add_verify_attestation (attestation, ctx, res, filename);
     }
   else if (gpgme_err_code (err) == GPG_ERR_DECRYPT_FAILED)
     {
@@ -625,7 +641,7 @@ decrypt_stream (gpgme_data_t in, gpgme_data_t out, int ttl,
    outputs. */
 int
 op_decrypt_stream (LPSTREAM instream, LPSTREAM outstream, int ttl,
-                   const char *filename)
+                   const char *filename, gpgme_data_t attestation)
 {
   struct gpgme_data_cbs cbs;
   gpgme_data_t in = NULL;
@@ -640,7 +656,7 @@ op_decrypt_stream (LPSTREAM instream, LPSTREAM outstream, int ttl,
   if (!err)
     err = gpgme_data_new_from_cbs (&out, &cbs, outstream);
   if (!err)
-    err = decrypt_stream (in, out, ttl, filename);
+    err = decrypt_stream (in, out, ttl, filename, attestation);
 
   if (in)
     gpgme_data_release (in);
@@ -656,7 +672,7 @@ op_decrypt_stream (LPSTREAM instream, LPSTREAM outstream, int ttl,
    outputs. */
 int
 op_decrypt_stream_to_buffer (LPSTREAM instream, char **outbuf, int ttl,
-                             const char *filename)
+                             const char *filename, gpgme_data_t attestation)
 {    
   struct gpgme_data_cbs cbs;
   gpgme_data_t in = NULL;
@@ -672,7 +688,7 @@ op_decrypt_stream_to_buffer (LPSTREAM instream, char **outbuf, int ttl,
   if (!err)
     err = gpgme_data_new (&out);
   if (!err)
-    err = decrypt_stream (in, out, ttl, filename);
+    err = decrypt_stream (in, out, ttl, filename, attestation);
   if (!err)
     {
       /* Return the buffer but first make sure it is a string. */
@@ -697,7 +713,7 @@ op_decrypt_stream_to_buffer (LPSTREAM instream, char **outbuf, int ttl,
    outputs. */
 int
 op_decrypt_stream_to_gpgme (LPSTREAM instream, gpgme_data_t out, int ttl,
-                            const char *filename)
+                            const char *filename, gpgme_data_t attestation)
 {
   struct gpgme_data_cbs cbs;
   gpgme_data_t in = NULL;
@@ -708,7 +724,7 @@ op_decrypt_stream_to_gpgme (LPSTREAM instream, gpgme_data_t out, int ttl,
 
   err = gpgme_data_new_from_cbs (&in, &cbs, instream);
   if (!err)
-    err = decrypt_stream (in, out, ttl, filename);
+    err = decrypt_stream (in, out, ttl, filename, attestation);
 
   if (in)
     gpgme_data_release (in);
@@ -722,9 +738,12 @@ op_decrypt_stream_to_gpgme (LPSTREAM instream, gpgme_data_t out, int ttl,
    OUTBUF. If OUTBUF is NULL only the verification result will be
    displayed (this is suitable for PGP/MIME messages).  A dialog box
    will show the result of the verification.  If FILENAME is not NULL
-   it will be displayed along with status outputs. */
+   it will be displayed along with status outputs.  If ATTESTATION is
+   not NULL a text with the result of the signature verification will
+   get printed to it. */
 int
-op_verify (const char *inbuf, char **outbuf, const char *filename)
+op_verify (const char *inbuf, char **outbuf, const char *filename,
+           gpgme_data_t attestation)
 {
   gpgme_data_t in = NULL;
   gpgme_data_t out = NULL;
@@ -765,6 +784,8 @@ op_verify (const char *inbuf, char **outbuf, const char *filename)
     }
   if (res) 
     verify_dialog_box (res, filename);
+  if (res && attestation)
+    add_verify_attestation (attestation, ctx, res, filename);
 
  leave:
   if (out)
@@ -779,10 +800,12 @@ op_verify (const char *inbuf, char **outbuf, const char *filename)
 /* Verify a detached message where the data is to be read from the
    DATA_STREAM and the signature itself is expected to be the string
    SIG_STRING.  FILENAME will be shown by the verification status
-   dialog box. */ 
+   dialog box.  If ATTESTATION is not NULL a text with the result of
+   the signature verification will get printed to it.  */ 
 int
 op_verify_detached_sig (LPSTREAM data_stream,
-                        const char *sig_string, const char *filename)
+                        const char *sig_string, const char *filename,
+                        gpgme_data_t attestation)
 {
   struct gpgme_data_cbs cbs;
   gpgme_data_t data = NULL;
@@ -815,6 +838,8 @@ op_verify_detached_sig (LPSTREAM data_stream,
       res = gpgme_op_verify_result (ctx);
       if (res) 
         verify_dialog_box (res, filename);
+      if (res && attestation)
+        add_verify_attestation (attestation, ctx, res, filename);
     }
 
  leave:
@@ -828,6 +853,334 @@ op_verify_detached_sig (LPSTREAM data_stream,
 }
 
 
+\f
+static void
+at_puts (gpgme_data_t a, const char *s)
+{
+  gpgme_data_write (a, s, strlen (s));
+}
+
+static void 
+at_print_time (gpgme_data_t a, time_t t)
+{
+  char buf[200];
+
+  strftime (buf, sizeof (buf)-1, "%c", localtime (&t));
+  at_puts (a, buf);
+}
+
+static void 
+at_fingerprint (gpgme_data_t a, gpgme_key_t key)
+{
+  const char *s;
+  int i, is_pgp;
+  char *buf, *p;
+  const char *prefix = _("Fingerprint: ");
+
+  if (!key)
+    return;
+  s = key->subkeys ? key->subkeys->fpr : NULL;
+  if (!s)
+    return;
+  is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
+
+  buf = xmalloc ( strlen (prefix) + strlen(s) * 4 + 2 );
+  p = stpcpy (buf, prefix);
+  if (is_pgp && strlen (s) == 40)
+    { 
+      /* v4 style formatted. */
+      for (i=0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++)
+        {
+          *p++ = s[0];
+          *p++ = s[1];
+          *p++ = s[2];
+          *p++ = s[3];
+          *p++ = ' ';
+          if (i == 4)
+            *p++ = ' ';
+        }
+    }
+  else
+    { 
+      /* v3 style or X.509 formatted. */
+      for (i=0; *s && s[1] && s[2]; s += 2, i++)
+        {
+          *p++ = s[0];
+          *p++ = s[1];
+          *p++ = is_pgp? ' ':':';
+          if (is_pgp && i == 7)
+            *p++ = ' ';
+        }
+    }
+
+  /* Just in case print remaining odd digits */
+  for (; *s; s++)
+    *p++ = *s;
+  *p++ = '\n';
+  *p = 0;
+  at_puts (a, buf);
+  xfree (buf);
+}
+
+
+/* Print common attributes of the signature summary SUM.  Returns
+   trues if a severe warning has been encountered. */
+static int 
+at_sig_summary (gpgme_data_t a,  
+                unsigned long sum, gpgme_signature_t sig, gpgme_key_t key)
+{
+  int severe = 0;
+
+  if ((sum & GPGME_SIGSUM_VALID))
+    at_puts (a, _("This signature is valid\n"));
+  if ((sum & GPGME_SIGSUM_GREEN))
+    at_puts (a, _("signature state is \"green\"\n"));
+  if ((sum & GPGME_SIGSUM_RED))
+    at_puts (a, _("signature state is \"red\"\n"));
+
+  if ((sum & GPGME_SIGSUM_KEY_REVOKED))
+    {
+      at_puts (a, _("Warning: One of the keys has been revoked\n"));
+      severe = 1;
+    }
+  
+  if ((sum & GPGME_SIGSUM_KEY_EXPIRED))
+    {
+      time_t t = key->subkeys->expires ? key->subkeys->expires : 0;
+
+      if (t)
+        {
+          at_puts (a, _("Warning: The key used to create the "
+                        "signature expired at: "));
+          at_print_time (a, t);
+          at_puts (a, "\n");
+        }
+      else
+        at_puts (a, _("Warning: At least one certification key "
+                      "has expired\n"));
+    }
+
+  if ((sum & GPGME_SIGSUM_SIG_EXPIRED))
+    {
+      at_puts (a, _("Warning: The signature expired at: "));
+      at_print_time (a, sig ? sig->exp_timestamp : 0);
+      at_puts (a, "\n");
+    }
+
+  if ((sum & GPGME_SIGSUM_KEY_MISSING))
+    at_puts (a, _("Can't verify due to a missing key or certificate\n"));
+
+  if ((sum & GPGME_SIGSUM_CRL_MISSING))
+    {
+      at_puts (a, _("The CRL is not available\n"));
+      severe = 1;
+    }
+
+  if ((sum & GPGME_SIGSUM_CRL_TOO_OLD))
+    {
+      at_puts (a, _("Available CRL is too old\n"));
+      severe = 1;
+    }
+
+  if ((sum & GPGME_SIGSUM_BAD_POLICY))
+    at_puts (a, _("A policy requirement was not met\n"));
+
+  if ((sum & GPGME_SIGSUM_SYS_ERROR))
+    {
+      const char *t0 = NULL, *t1 = NULL;
+
+      at_puts (a, _("A system error occured"));
+
+      /* Try to figure out some more detailed system error information. */
+      if (sig)
+       {
+         t0 = "";
+         t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
+       }
+
+      if (t0 || t1)
+        {
+          at_puts (a, ": ");
+          if (t0)
+            at_puts (a, t0);
+          if (t1 && !(t0 && !strcmp (t0, t1)))
+            {
+              if (t0)
+                at_puts (a, ",");
+              at_puts (a, t1);
+            }
+        }
+      at_puts (a, "\n");
+    }
+
+  return severe;
+}
+
+
+/* Print the validity of a key used for one signature. */
+static void 
+at_sig_validity (gpgme_data_t a, gpgme_signature_t sig)
+{
+  const char *txt = NULL;
+
+  switch (sig ? sig->validity : 0)
+    {
+    case GPGME_VALIDITY_UNKNOWN:
+      txt = _("WARNING: We have NO indication whether "
+              "the key belongs to the person named "
+              "as shown above\n");
+      break;
+    case GPGME_VALIDITY_UNDEFINED:
+      break;
+    case GPGME_VALIDITY_NEVER:
+      txt = _("WARNING: The key does NOT BELONG to "
+              "the person named as shown above\n");
+      break;
+    case GPGME_VALIDITY_MARGINAL:
+      txt = _("WARNING: It is NOT certain that the key "
+              "belongs to the person named as shown above\n");
+      break;
+    case GPGME_VALIDITY_FULL:
+    case GPGME_VALIDITY_ULTIMATE:
+      txt = NULL;
+      break;
+    }
+
+  if (txt)
+    at_puts (a, txt);
+}
+
+
+/* Print a text with the attestation of the signature verification
+   (which is in RES) to A.  FILENAME may also be used in the
+   attestation. */
+static void
+add_verify_attestation (gpgme_data_t a, gpgme_ctx_t ctx,
+                        gpgme_verify_result_t res, const char *filename)
+{
+  time_t created;
+  const char *fpr, *uid;
+  gpgme_key_t key = NULL;
+  int i, anybad = 0, anywarn = 0;
+  unsigned int sum;
+  gpgme_user_id_t uids = NULL;
+  gpgme_signature_t sig;
+  gpgme_error_t err;
+
+  if (!gpgme_data_seek (a, 0, SEEK_CUR))
+    {
+      /* Nothing yet written to the stream.  Insert the curretn time. */
+      at_puts (a, _("Verification started at: "));
+      at_print_time (a, time (NULL));
+      at_puts (a, "\n\n");
+    }
+
+  at_puts (a, _("Verification result for: "));
+  at_puts (a, filename ? filename : _("[unnamed part]"));
+  at_puts (a, "\n");
+  if (res)
+    {
+      for (sig = res->signatures; sig; sig = sig->next)
+        {
+          created = sig->timestamp;
+          fpr = sig->fpr;
+          sum = sig->summary;
+
+          if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
+            anybad = 1;
+
+          err = gpgme_get_key (ctx, fpr, &key, 0);
+          uid = !err && key->uids && key->uids->uid ? key->uids->uid : "[?]";
+
+          if ((sum & GPGME_SIGSUM_GREEN))
+            {
+              at_puts (a, _("Good signature from: "));
+              at_puts (a, uid);
+              at_puts (a, "\n");
+              for (i = 1, uids = key->uids; uids; i++, uids = uids->next)
+                {
+                  if (uids->revoked)
+                    continue;
+                  at_puts (a, _("                aka: "));
+                  at_puts (a, uids->uid);
+                  at_puts (a, "\n");
+                }
+              at_puts (a, _("            created: "));
+              at_print_time (a, created);
+              at_puts (a, "\n");
+              if (at_sig_summary (a, sum, sig, key))
+                anywarn = 1;
+              at_sig_validity (a, sig);
+            }
+          else if ((sum & GPGME_SIGSUM_RED))
+            {
+              at_puts (a, _("*BAD* signature claimed to be from: "));
+              at_puts (a, uid);
+              at_puts (a, "\n");
+              at_sig_summary (a, sum, sig, key);
+            }
+          else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP))
+            { /* We can't decide (yellow) but this is a PGP key with a
+                 good signature, so we display what a PGP user
+                 expects: The name, fingerprint and the key validity
+                 (which is neither fully or ultimate). */
+              at_puts (a, _("Good signature from: "));
+              at_puts (a, uid);
+              at_puts (a, "\n");
+              at_puts (a, _("            created: "));
+              at_print_time (a, created);
+              at_puts (a, "\n");
+              at_sig_validity (a, sig);
+              at_fingerprint (a, key);
+              if (at_sig_summary (a, sum, sig, key))
+                anywarn = 1;
+            }
+          else /* can't decide (yellow) */
+            {
+              at_puts (a, _("Error checking signature"));
+              at_puts (a, "\n");
+              at_sig_summary (a, sum, sig, key);
+            }
+          
+          gpgme_key_release (key);
+        }
+
+      if (!anybad )
+        {
+          gpgme_sig_notation_t notation;
+          
+          for (sig = res->signatures; sig; sig = sig->next)
+            {
+              if (!sig->notations)
+                continue;
+              at_puts (a, _("*** Begin Notation (signature by: "));
+              at_puts (a, sig->fpr);
+              at_puts (a, ") ***\n");
+              for (notation = sig->notations; notation;
+                   notation = notation->next)
+                {
+                  if (notation->name)
+                    {
+                      at_puts (a, notation->name);
+                      at_puts (a, "=");
+                    }
+                  if (notation->value)
+                    {
+                      at_puts (a, notation->value);
+                      if (!(*notation->value
+                            && (notation->value[strlen (notation->value)-1]
+                                =='\n')))
+                        at_puts (a, "\n");
+                    }
+                }
+              at_puts (a, _("*** End Notation ***\n"));
+            }
+       }
+    }
+  at_puts (a, "\n");
+}
+
+
 
 \f
 /* Try to find a key for each item in array NAMES. If one ore more
index 943c54b..1368ee8 100644 (file)
@@ -57,17 +57,20 @@ int op_sign_stream (LPSTREAM instream, LPSTREAM outstream, int mode,
                     gpgme_key_t sign_key, int ttl);
 
 int op_decrypt (const char *inbuf, char **outbuf, int ttl,
-                const char *filename);
+                const char *filename, gpgme_data_t attestation);
 int op_decrypt_stream (LPSTREAM instream, LPSTREAM outstream, int ttl,
-                       const char *filename);
+                       const char *filename, gpgme_data_t attestation);
 int op_decrypt_stream_to_buffer (LPSTREAM instream, char **outbuf, int ttl,
-                                 const char *filename);
+                                 const char *filename,
+                                 gpgme_data_t attestation);
 int op_decrypt_stream_to_gpgme (LPSTREAM instream, gpgme_data_t out, int ttl,
-                                const char *filename);
+                                const char *filename,
+                                gpgme_data_t attestation);
 
-int op_verify (const char *inbuf, char **outbuf, const char *filename);
+int op_verify (const char *inbuf, char **outbuf, const char *filename,
+               gpgme_data_t attestation);
 int op_verify_detached_sig (LPSTREAM data, const char *sig,
-                            const char *filename);
+                            const char *filename, gpgme_data_t attestation);
 
 
 int op_export_keys (const char *pattern[], const char *outfile);
index 3a16aba..a2429ca 100644 (file)
@@ -101,6 +101,8 @@ public:
     is_pgpmime = false;
     silent = false;
 
+    attestation = NULL;
+
     attach.att_table = NULL;
     attach.rows = NULL;
   }
@@ -112,6 +114,9 @@ public:
     xfree (body);
     xfree (body_plain);
 
+    if (attestation)
+      gpgme_data_release (attestation);
+
     if (attach.att_table)
       {
         attach.att_table->Release ();
@@ -196,7 +201,11 @@ private:
   char *body_plain;   /* Plaintext version of BODY or NULL. */
   bool is_pgpmime;    /* True if the message is a PGP/MIME encrypted one. */
   bool silent;        /* Don't pop up message boxes.  Currently this
-                         is only used with decryption. g*/
+                         is only used with decryption.  */
+
+  /* If not NULL, collect attestation information here. */
+  gpgme_data_t attestation;
+  
 
   /* This structure collects the information about attachments. */
   struct 
@@ -207,6 +216,7 @@ private:
   
   void loadBody (void);
   bool isPgpmimeVersionPart (int pos);
+  void writeAttestation (void);
   attach_info_t gatherAttachmentInfo (void);
   int encrypt_and_sign (HWND hwnd, bool sign);
 };
@@ -666,6 +676,108 @@ GpgMsgImpl::getRecipients ()
 }
 
 
+/* Write an Attestation to the current message. */
+void
+GpgMsgImpl::writeAttestation (void)
+{
+  HRESULT hr;
+  ULONG newpos;
+  SPropValue prop;
+  LPATTACH newatt = NULL;
+  LPSTREAM to = NULL;
+  char *buffer = NULL;
+  ULONG nwritten;
+
+  if (!message || !attestation)
+    return;
+
+  hr = message->CreateAttach (NULL, 0, &newpos, &newatt);
+  if (hr != S_OK)
+    {
+      log_error ("%s:%s: can't create attachment: hr=%#lx\n",
+                 __FILE__, __func__, hr); 
+      goto leave;
+    }
+          
+  prop.ulPropTag = PR_ATTACH_METHOD;
+  prop.Value.ul = ATTACH_BY_VALUE;
+  hr = HrSetOneProp (newatt, &prop);
+  if (hr != S_OK)
+    {
+      log_error ("%s:%s: can't set attach method: hr=%#lx\n",
+                 __FILE__, __func__, hr); 
+      goto leave;
+    }
+  
+  prop.ulPropTag = PR_ATTACH_LONG_FILENAME_A;
+  prop.Value.lpszA = "GPGol-Attestation.txt";
+  hr = HrSetOneProp (newatt, &prop);
+  if (hr != S_OK)
+    {
+      log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
+                 __FILE__, __func__, hr); 
+      goto leave;
+    }
+  
+  hr = newatt->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 0,
+                             MAPI_CREATE|MAPI_MODIFY, (LPUNKNOWN*)&to);
+  if (FAILED (hr)) 
+    {
+      log_error ("%s:%s: can't create output stream: hr=%#lx\n",
+                 __FILE__, __func__, hr); 
+      goto leave;
+    }
+  
+
+  if (gpgme_data_write (attestation, "", 1) != 1
+      || !(buffer = gpgme_data_release_and_get_mem (attestation, NULL)))
+    {
+      attestation = NULL;
+      log_error ("%s:%s: gpgme_data_write failed\n", __FILE__, __func__); 
+      goto leave;
+    }
+  attestation = NULL;
+
+  log_debug ("writing attestation `%s'\n", buffer);
+
+  hr = to->Write (buffer, strlen (buffer), &nwritten);
+  if (hr != S_OK)
+    {
+      log_debug ("%s:%s: Write failed: hr=%#lx", __FILE__, __func__, hr);
+      goto leave;
+    }
+  
+  to->Commit (0);
+  to->Release ();
+  to = NULL;
+  
+  hr = newatt->SaveChanges (0);
+  if (hr != S_OK)
+    {
+      log_error ("%s:%s: SaveChanges(attachment) failed: hr=%#lx\n",
+                 __FILE__, __func__, hr); 
+      goto leave;
+    }
+  hr = message->SaveChanges (KEEP_OPEN_READWRITE|FORCE_SAVE);
+  if (hr != S_OK)
+    {
+      log_error ("%s:%s: SaveChanges(message) failed: hr=%#lx\n",
+                 __FILE__, __func__, hr); 
+      goto leave;
+    }
+
+
+ leave:
+  if (to)
+    {
+      to->Revert ();
+      to->Release ();
+    }
+  if (newatt)
+    newatt->Release ();
+  xfree (buffer);
+}
+
 
 
 /* Decrypt the message MSG and update the window.  HWND identifies the
@@ -711,8 +823,8 @@ GpgMsgImpl::decrypt (HWND hwnd)
          means of update_display and the SetWindowText API).  Thus it
          happens sometimes that the ciphertext is still displayed
          although the MAPI calls in loadBody returned the plaintext
-         (becuase we once used set_message_body).  The effect is that
-         when cliching the decrypt button, we won't have any
+         (because we once used set_message_body).  The effect is that
+         when clicking the decrypt button, we won't have any
          ciphertext to decrypt and thus get to here.  We try solving
          this by updating the window if we also have a cached entry.
 
@@ -744,6 +856,14 @@ GpgMsgImpl::decrypt (HWND hwnd)
       return 0;
     }
 
+  /* We always want an attestation.  Note that we ignore any error
+     because that would anyway be a out of core situation and thus we
+     can't do much about it. */
+  if (!attestation)
+    gpgme_data_new (&attestation);
+  
+
+  /* Process according to type of message. */
   if (is_pgpmime)
     {
       LPATTACH att;
@@ -789,7 +909,7 @@ GpgMsgImpl::decrypt (HWND hwnd)
           return gpg_error (GPG_ERR_GENERAL);
         }
 
-      err = pgpmime_decrypt (from, opt.passwd_ttl, &plaintext);
+      err = pgpmime_decrypt (from, opt.passwd_ttl, &plaintext, attestation);
       
       from->Release ();
       att->Release ();
@@ -797,9 +917,10 @@ GpgMsgImpl::decrypt (HWND hwnd)
         pgpmime_succeeded = 1;
     }
   else if (mtype == OPENPGP_CLEARSIG)
-    err = op_verify (getOrigText (), NULL, NULL);
+    err = op_verify (getOrigText (), NULL, NULL, attestation);
   else if (*getOrigText())
-    err = op_decrypt (getOrigText (), &plaintext, opt.passwd_ttl, NULL);
+    err = op_decrypt (getOrigText (), &plaintext, opt.passwd_ttl,
+                      NULL, attestation);
   else
     err = gpg_error (GPG_ERR_NO_DATA);
   if (err)
@@ -836,10 +957,18 @@ GpgMsgImpl::decrypt (HWND hwnd)
       plaintext = NULL;
       msgcache_put (body_plain, 0, message);
 
-      /* XXX: find a way to handle text/html message in a better way! */
-      /* I have disabled the kludge to see what happens to a html
-         message. */
-      if (!silent && /*is_html ||*/ update_display (hwnd, this, exchange_cb)) 
+      if (opt.save_decrypted_attach)
+        {
+          /* User wants us to replace the encrypted message with the
+             plaintext version. */
+          hr = message->SaveChanges (KEEP_OPEN_READWRITE|FORCE_SAVE);
+          if (FAILED (hr))
+            log_debug ("%s:%s: SaveChanges failed: hr=%#lx",
+                       __FILE__, __func__, hr);
+          update_display (hwnd, this, exchange_cb);
+          
+        }
+      else if (!silent && update_display (hwnd, this, exchange_cb)) 
         {
           const char s[] = 
             "The message text cannot be displayed.\n"
@@ -912,6 +1041,8 @@ GpgMsgImpl::decrypt (HWND hwnd)
         }
     }
 
+  writeAttestation ();
+
   release_attach_info (table);
   log_debug ("%s:%s: leave (rc=%d)\n", __FILE__, __func__, err);
   return err;
@@ -1793,7 +1924,7 @@ GpgMsgImpl::verifyAttachment (HWND hwnd, attach_info_t table,
           goto leave;
         }
       err = op_verify_detached_sig (stream, sig_data,
-                                    table[pos_data].filename);
+                                    table[pos_data].filename, attestation);
       if (err)
         {
           log_debug ("%s:%s: verify detached signature failed: %s",
@@ -1827,6 +1958,9 @@ GpgMsgImpl::decryptAttachment (HWND hwnd, int pos, bool save_plaintext,
   HRESULT hr;
   LPATTACH att;
   int method, err;
+  LPATTACH newatt = NULL;
+  char *outname = NULL;
+  
 
   log_debug ("%s:%s: processing attachment %d", __FILE__, __func__, pos);
 
@@ -1885,14 +2019,13 @@ GpgMsgImpl::decryptAttachment (HWND hwnd, int pos, bool save_plaintext,
   else if (method == ATTACH_BY_VALUE)
     {
       char *s;
-      char *outname;
       char *suggested_name;
       LPSTREAM from, to;
 
       suggested_name = get_attach_filename (att);
       if (suggested_name)
         log_debug ("%s:%s: attachment %d, filename `%s'", 
-                  __FILE__, __func__, pos, suggested_name);
+                   __FILE__, __func__, pos, suggested_name);
       /* Strip of know extensions or use a default name. */
       if (!suggested_name)
         {
@@ -1907,58 +2040,138 @@ GpgMsgImpl::decryptAttachment (HWND hwnd, int pos, bool save_plaintext,
         {
           *s = 0;
         }
-      outname = get_save_filename (hwnd, suggested_name);
-      xfree (suggested_name);
-
+      if (opt.save_decrypted_attach)
+        outname = suggested_name;
+      else
+        {
+          outname = get_save_filename (hwnd, suggested_name);
+          xfree (suggested_name);
+        }
+      
       hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
                               0, 0, (LPUNKNOWN*) &from);
       if (FAILED (hr))
         {
           log_error ("%s:%s: can't open data of attachment %d: hr=%#lx",
                      __FILE__, __func__, pos, hr);
-          xfree (outname);
           goto leave;
         }
 
-      hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
-                             (STGM_CREATE | STGM_READWRITE),
-                             outname, NULL, &to); 
-      if (FAILED (hr)) 
+
+      if (opt.save_decrypted_attach) /* Decrypt and save in the MAPI. */
         {
-          log_error ("%s:%s: can't create stream for `%s': hr=%#lx\n",
-                     __FILE__, __func__, outname, hr); 
+          ULONG newpos;
+          SPropValue prop;
+
+          hr = message->CreateAttach (NULL, 0, &newpos, &newatt);
+          if (hr != S_OK)
+            {
+              log_error ("%s:%s: can't create attachment: hr=%#lx\n",
+                         __FILE__, __func__, hr); 
+              goto leave;
+            }
+          
+          prop.ulPropTag = PR_ATTACH_METHOD;
+          prop.Value.ul = ATTACH_BY_VALUE;
+          hr = HrSetOneProp (newatt, &prop);
+          if (hr != S_OK)
+            {
+              log_error ("%s:%s: can't set attach method: hr=%#lx\n",
+                         __FILE__, __func__, hr); 
+              goto leave;
+            }
+          
+          prop.ulPropTag = PR_ATTACH_LONG_FILENAME_A;
+          prop.Value.lpszA = outname;   
+          hr = HrSetOneProp (newatt, &prop);
+          if (hr != S_OK)
+            {
+              log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
+                         __FILE__, __func__, hr); 
+              goto leave;
+            }
+          log_debug ("%s:%s: setting filename of attachment %d/%ld to `%s'",
+                     __FILE__, __func__, pos, newpos, outname);
+          
+
+          hr = newatt->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 0,
+                                     MAPI_CREATE|MAPI_MODIFY, (LPUNKNOWN*)&to);
+          if (FAILED (hr)) 
+            {
+              log_error ("%s:%s: can't create output stream: hr=%#lx\n",
+                         __FILE__, __func__, hr); 
+              goto leave;
+            }
+      
+          err = op_decrypt_stream (from, to, ttl, filename, attestation);
+          if (err)
+            {
+              log_debug ("%s:%s: decrypt stream failed: %s",
+                         __FILE__, __func__, op_strerror (err)); 
+              to->Revert ();
+              to->Release ();
+              from->Release ();
+              MessageBox (hwnd, op_strerror (err),
+                          "GPG Attachment Decryption", MB_ICONERROR|MB_OK);
+              goto leave;
+            }
+        
+          to->Commit (0);
+          to->Release ();
           from->Release ();
-          xfree (outname);
-          goto leave;
+
+          hr = newatt->SaveChanges (0);
+          if (hr != S_OK)
+            {
+              log_error ("%s:%s: SaveChanges failed: hr=%#lx\n",
+                         __FILE__, __func__, hr); 
+              goto leave;
+            }
+
+          /* Delete the orginal attachment. FIXME: Should we really do
+             that or better just mark it in the table and delete
+             later? */
+          att->Release ();
+          att = NULL;
+          if (message->DeleteAttach (pos, 0, NULL, 0) == S_OK)
+            log_error ("%s:%s: failed to delete attacghment %d: %s",
+                       __FILE__, __func__, pos, op_strerror (err)); 
+          
         }
-      
-      err = op_decrypt_stream (from, to, ttl, filename);
-      if (err)
+      else  /* Save attachment to a file. */
         {
-          log_debug ("%s:%s: decrypt stream failed: %s",
-                     __FILE__, __func__, op_strerror (err)); 
-          to->Revert ();
+          hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
+                                 (STGM_CREATE | STGM_READWRITE),
+                                 outname, NULL, &to); 
+          if (FAILED (hr)) 
+            {
+              log_error ("%s:%s: can't create stream for `%s': hr=%#lx\n",
+                         __FILE__, __func__, outname, hr); 
+              from->Release ();
+              goto leave;
+            }
+      
+          err = op_decrypt_stream (from, to, ttl, filename, attestation);
+          if (err)
+            {
+              log_debug ("%s:%s: decrypt stream failed: %s",
+                         __FILE__, __func__, op_strerror (err)); 
+              to->Revert ();
+              to->Release ();
+              from->Release ();
+              MessageBox (hwnd, op_strerror (err),
+                          "GPG Attachment Decryption", MB_ICONERROR|MB_OK);
+              /* FIXME: We might need to delete outname now.  However a
+                 sensible implementation of the stream object should have
+                 done it through the Revert call. */
+              goto leave;
+            }
+        
+          to->Commit (0);
           to->Release ();
           from->Release ();
-          MessageBox (hwnd, op_strerror (err),
-                      "GPG Attachment Decryption", MB_ICONERROR|MB_OK);
-          /* FIXME: We might need to delete outname now.  However a
-             sensible implementation of the stream object should have
-             done it trhough the Revert call. */
-          xfree (outname);
-          goto leave;
         }
-        
-      to->Commit (0);
-      to->Release ();
-      from->Release ();
-
-      /*  Hmmm: Why are we deleting the attachment now????? 
-          Disabled until clarified.   FIXME */
-      //if (message->DeleteAttach (pos, 0, NULL, 0) == S_OK)
-      //   show error;
-
-      xfree (outname);
+      
     }
   else
     {
@@ -1967,8 +2180,11 @@ GpgMsgImpl::decryptAttachment (HWND hwnd, int pos, bool save_plaintext,
     }
 
  leave:
-  /* Close this attachment. */
-  att->Release ();
+  xfree (outname);
+  if (newatt)
+    newatt->Release ();
+  if (att)
+    att->Release ();
 }
 
 
index 151e46e..f0ce13c 100644 (file)
@@ -273,9 +273,12 @@ plaintext_handler (void *handle, const void *buffer, size_t size)
 
 /* Decrypt the PGP/MIME INSTREAM (i.e the second part of the
    multipart/mixed) and allow saving of all attachments. On success a
-   newly allocated body will be stored at BODY. */
+   newly allocated body will be stored at BODY.  If ATTESTATION is not
+   NULL a text with the result of the signature verification will get
+   printed to it.  */
 int
-pgpmime_decrypt (LPSTREAM instream, int ttl, char **body)
+pgpmime_decrypt (LPSTREAM instream, int ttl, char **body,
+                 gpgme_data_t attestation)
 {
   gpg_error_t err;
   struct gpgme_data_cbs cbs;
@@ -302,7 +305,8 @@ pgpmime_decrypt (LPSTREAM instream, int ttl, char **body)
   if (err)
     goto leave;
 
-  err = op_decrypt_stream_to_gpgme (instream, plaintext, ttl, NULL);
+  err = op_decrypt_stream_to_gpgme (instream, plaintext, ttl,
+                                    NULL, NULL);
   if (!err && (ctx->parser_error || ctx->line_too_long))
     err = gpg_error (GPG_ERR_GENERAL);
 
index e36c1db..c2c1003 100644 (file)
@@ -28,7 +28,8 @@ extern "C" {
 #endif
 #endif
 
-int pgpmime_decrypt (LPSTREAM instream, int ttl, char **body);
+int pgpmime_decrypt (LPSTREAM instream, int ttl, char **body,
+                     gpgme_data_t attestation);
 
 
 
index 3778e27..d0ae465 100644 (file)
@@ -43,6 +43,10 @@ extern "C" {
                   } while(0)
 
 
+/* i18n stuff (dummies for now). */
+#define _(a) (a)
+
+
 
 /*-- common.c --*/
 void* xmalloc (size_t n);