Creating PGP/MIME signed mails works now - kind of:
authorWerner Koch <wk@gnupg.org>
Thu, 13 Sep 2007 11:53:15 +0000 (11:53 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 13 Sep 2007 11:53:15 +0000 (11:53 +0000)
becuase the passphrase dialog runs in a second thread, the windows are not
properly cleaned up lated.

po/de.po
po/sv.po
src/ChangeLog
src/common.c
src/engine-gpgme.c
src/engine.c
src/engine.h
src/mimemaker.c
src/xmalloc.h

index f35a58f..7e0c736 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: GPGol 0.9.4\n"
 "Report-Msgid-Bugs-To: bug-gpgol@g10code.com\n"
-"POT-Creation-Date: 2007-09-06 21:27+0200\n"
+"POT-Creation-Date: 2007-09-11 20:30+0200\n"
 "PO-Revision-Date: 2007-04-13 12:55+0200\n"
 "Last-Translator: Werner Koch <wk@gnupg.org>\n"
 "Language-Team: de\n"
@@ -23,62 +23,62 @@ msgstr "GPG - Sichern der entschlüsselten Anlage"
 msgid "Select GPG Key Manager"
 msgstr "Das Schlüsselverwaltungsprogramm festlegen"
 
-#: src/engine-gpgme.c:1004
+#: src/engine-gpgme.c:1099
 msgid "Fingerprint: "
 msgstr "Fingerabdruck: "
 
-#: src/engine-gpgme.c:1061
+#: src/engine-gpgme.c:1156
 msgid "This signature is valid\n"
 msgstr "Diese Unterschrift ist korrekt\n"
 
-#: src/engine-gpgme.c:1063
+#: src/engine-gpgme.c:1158
 msgid "signature state is \"green\"\n"
 msgstr "Status der Unterschrift ist \"grün\"\n"
 
-#: src/engine-gpgme.c:1065
+#: src/engine-gpgme.c:1160
 msgid "signature state is \"red\"\n"
 msgstr "Status der Unterschrift ist \"rot\"\n"
 
-#: src/engine-gpgme.c:1069
+#: src/engine-gpgme.c:1164
 msgid "Warning: One of the keys has been revoked\n"
 msgstr "Warnung: Einer der Schlüssel wurde widerrufen\n"
 
-#: src/engine-gpgme.c:1079
+#: src/engine-gpgme.c:1174
 msgid "Warning: The key used to create the signature expired at: "
 msgstr ""
 "Warnung: Der Schlüssel mit der diese Unterschrift erzeugt wurde verfiel am: "
 
-#: src/engine-gpgme.c:1085
+#: src/engine-gpgme.c:1180
 msgid "Warning: At least one certification key has expired\n"
 msgstr ""
 "Warnung: Mindestens einer der Zertifizierungsschlüssel ist abgelaufen\n"
 
-#: src/engine-gpgme.c:1091
+#: src/engine-gpgme.c:1186
 msgid "Warning: The signature expired at: "
 msgstr "Die Unterschrift verfiel am: "
 
-#: src/engine-gpgme.c:1097
+#: src/engine-gpgme.c:1192
 msgid "Can't verify due to a missing key or certificate\n"
 msgstr ""
 "Aufrund eines fehlenden Schlüssels ist eine Überprüfung nicht möglich\n"
 
-#: src/engine-gpgme.c:1101
+#: src/engine-gpgme.c:1196
 msgid "The CRL is not available\n"
 msgstr "Die CRL ist nicht verfügbar\n"
 
-#: src/engine-gpgme.c:1107
+#: src/engine-gpgme.c:1202
 msgid "Available CRL is too old\n"
 msgstr "Die vorhandene CRL ist zu alt\n"
 
-#: src/engine-gpgme.c:1112
+#: src/engine-gpgme.c:1207
 msgid "A policy requirement was not met\n"
 msgstr "Eine Richtlinie wurde nicht erfüllt\n"
 
-#: src/engine-gpgme.c:1118
+#: src/engine-gpgme.c:1213
 msgid "A system error occured"
 msgstr "Ein Systemfehler ist aufgetreten"
 
-#: src/engine-gpgme.c:1155
+#: src/engine-gpgme.c:1250
 msgid ""
 "WARNING: We have NO indication whether the key belongs to the person named "
 "as shown above\n"
@@ -86,12 +86,12 @@ msgstr ""
 "WARNUNG: Es gibt keinen Hinweis darauf, ob der Schlüssel wirklich der Person "
 "gehört, die oben angezeigt ist\n"
 
-#: src/engine-gpgme.c:1162
+#: src/engine-gpgme.c:1257
 msgid "WARNING: The key does NOT BELONG to the person named as shown above\n"
 msgstr ""
 "WARNUNG: Der Schlüssel gehört NICHT der Person die oben angezeigt ist\n"
 
-#: src/engine-gpgme.c:1166
+#: src/engine-gpgme.c:1261
 msgid ""
 "WARNING: It is NOT certain that the key belongs to the person named as shown "
 "above\n"
@@ -99,43 +99,43 @@ msgstr ""
 "WARNING: Es ist nicht sicher, daß der Schlüssel der Person gehört, die oben "
 "angezeigt ist\n"
 
-#: src/engine-gpgme.c:1199
+#: src/engine-gpgme.c:1294
 msgid "Verification started at: "
 msgstr "Überprüfung begann am: "
 
-#: src/engine-gpgme.c:1204
+#: src/engine-gpgme.c:1299
 msgid "Verification result for: "
 msgstr "Prüfungsresultat für: "
 
-#: src/engine-gpgme.c:1205
+#: src/engine-gpgme.c:1300
 msgid "[unnamed part]"
 msgstr "[Unbenannter Teil]"
 
-#: src/engine-gpgme.c:1223 src/engine-gpgme.c:1253
+#: src/engine-gpgme.c:1318 src/engine-gpgme.c:1348
 msgid "Good signature from: "
 msgstr "Korrekte Unterschrift von: "
 
-#: src/engine-gpgme.c:1230
+#: src/engine-gpgme.c:1325
 msgid "                aka: "
 msgstr "                    alias: "
 
-#: src/engine-gpgme.c:1234 src/engine-gpgme.c:1256
+#: src/engine-gpgme.c:1329 src/engine-gpgme.c:1351
 msgid "            created: "
 msgstr "                  erzeugt: "
 
-#: src/engine-gpgme.c:1243
+#: src/engine-gpgme.c:1338
 msgid "*BAD* signature claimed to be from: "
 msgstr "*FALSCHE* Unterschrift, vorgeblich von: "
 
-#: src/engine-gpgme.c:1266
+#: src/engine-gpgme.c:1361
 msgid "Error checking signature"
 msgstr "Fehler beim Prüfen der Unterschrift"
 
-#: src/engine-gpgme.c:1282
+#: src/engine-gpgme.c:1377
 msgid "*** Begin Notation (signature by: "
 msgstr "*** Anfang Notation (Unterschrift von: "
 
-#: src/engine-gpgme.c:1302
+#: src/engine-gpgme.c:1397
 msgid "*** End Notation ***\n"
 msgstr "*** Ende Notation ***\n"
 
index 1b4a342..aad4059 100644 (file)
--- a/po/sv.po
+++ b/po/sv.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: GPGol\n"
 "Report-Msgid-Bugs-To: bug-gpgol@g10code.com\n"
-"POT-Creation-Date: 2007-09-06 21:27+0200\n"
+"POT-Creation-Date: 2007-09-11 20:30+0200\n"
 "PO-Revision-Date: 2006-12-12 23:52+0100\n"
 "Last-Translator: Daniel Nylander <po@danielnylander.se>\n"
 "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
@@ -23,59 +23,59 @@ msgstr "GPG - Spara dekrypterad bilaga"
 msgid "Select GPG Key Manager"
 msgstr "Välj GPG-nyckelhanterare"
 
-#: src/engine-gpgme.c:1004
+#: src/engine-gpgme.c:1099
 msgid "Fingerprint: "
 msgstr "Fingeravtryck: "
 
-#: src/engine-gpgme.c:1061
+#: src/engine-gpgme.c:1156
 msgid "This signature is valid\n"
 msgstr "Den här signaturen är giltig\n"
 
-#: src/engine-gpgme.c:1063
+#: src/engine-gpgme.c:1158
 msgid "signature state is \"green\"\n"
 msgstr "signaturens tillstånd är \"grön\"\n"
 
-#: src/engine-gpgme.c:1065
+#: src/engine-gpgme.c:1160
 msgid "signature state is \"red\"\n"
 msgstr "signaturens tillstånd är \"röd\"\n"
 
-#: src/engine-gpgme.c:1069
+#: src/engine-gpgme.c:1164
 msgid "Warning: One of the keys has been revoked\n"
 msgstr "Varning: En av nycklarna har spärrats\n"
 
-#: src/engine-gpgme.c:1079
+#: src/engine-gpgme.c:1174
 msgid "Warning: The key used to create the signature expired at: "
 msgstr "Varning: Nyckeln som användes för att skapa signaturen gick ut den: "
 
-#: src/engine-gpgme.c:1085
+#: src/engine-gpgme.c:1180
 msgid "Warning: At least one certification key has expired\n"
 msgstr "Varning: Åtminstone en certifieringsnyckel har gått ut\n"
 
-#: src/engine-gpgme.c:1091
+#: src/engine-gpgme.c:1186
 msgid "Warning: The signature expired at: "
 msgstr "Varning: Signaturen gick ut den: "
 
-#: src/engine-gpgme.c:1097
+#: src/engine-gpgme.c:1192
 msgid "Can't verify due to a missing key or certificate\n"
 msgstr "Kan inte validera på grund av en saknad nyckel eller certifikat\n"
 
-#: src/engine-gpgme.c:1101
+#: src/engine-gpgme.c:1196
 msgid "The CRL is not available\n"
 msgstr "Spärrlistan är inte tillgänglig\n"
 
-#: src/engine-gpgme.c:1107
+#: src/engine-gpgme.c:1202
 msgid "Available CRL is too old\n"
 msgstr "Tillgänglig spärrlista är för gammal\n"
 
-#: src/engine-gpgme.c:1112
+#: src/engine-gpgme.c:1207
 msgid "A policy requirement was not met\n"
 msgstr "Ett policykrav matchades inte\n"
 
-#: src/engine-gpgme.c:1118
+#: src/engine-gpgme.c:1213
 msgid "A system error occured"
 msgstr "Ett systemfel inträffade"
 
-#: src/engine-gpgme.c:1155
+#: src/engine-gpgme.c:1250
 msgid ""
 "WARNING: We have NO indication whether the key belongs to the person named "
 "as shown above\n"
@@ -83,11 +83,11 @@ msgstr ""
 "VARNING: Vi har INGA indikationer på huruvida nyckeln tillhör personen vars "
 "namn visas ovanför\n"
 
-#: src/engine-gpgme.c:1162
+#: src/engine-gpgme.c:1257
 msgid "WARNING: The key does NOT BELONG to the person named as shown above\n"
 msgstr "VARNING: Nyckeln TILLHÖR INTE personen vars namn visas ovanför\n"
 
-#: src/engine-gpgme.c:1166
+#: src/engine-gpgme.c:1261
 msgid ""
 "WARNING: It is NOT certain that the key belongs to the person named as shown "
 "above\n"
@@ -95,43 +95,43 @@ msgstr ""
 "VARNING: Det är INTE säkert att nyckeln tillhör den person vars namn visas "
 "ovanför\n"
 
-#: src/engine-gpgme.c:1199
+#: src/engine-gpgme.c:1294
 msgid "Verification started at: "
 msgstr "Validering startad: "
 
-#: src/engine-gpgme.c:1204
+#: src/engine-gpgme.c:1299
 msgid "Verification result for: "
 msgstr "Valideringsresultat för: "
 
-#: src/engine-gpgme.c:1205
+#: src/engine-gpgme.c:1300
 msgid "[unnamed part]"
 msgstr "[ej namngiven del]"
 
-#: src/engine-gpgme.c:1223 src/engine-gpgme.c:1253
+#: src/engine-gpgme.c:1318 src/engine-gpgme.c:1348
 msgid "Good signature from: "
 msgstr "Korrekt signatur från: "
 
-#: src/engine-gpgme.c:1230
+#: src/engine-gpgme.c:1325
 msgid "                aka: "
 msgstr "även känd som:"
 
-#: src/engine-gpgme.c:1234 src/engine-gpgme.c:1256
+#: src/engine-gpgme.c:1329 src/engine-gpgme.c:1351
 msgid "            created: "
 msgstr "             skapad: "
 
-#: src/engine-gpgme.c:1243
+#: src/engine-gpgme.c:1338
 msgid "*BAD* signature claimed to be from: "
 msgstr "*FELAKTIG* signatur hävdades komma från: "
 
-#: src/engine-gpgme.c:1266
+#: src/engine-gpgme.c:1361
 msgid "Error checking signature"
 msgstr "Fel vid kontroll av signatur"
 
-#: src/engine-gpgme.c:1282
+#: src/engine-gpgme.c:1377
 msgid "*** Begin Notation (signature by: "
 msgstr "*** Notation start (signatur av: "
 
-#: src/engine-gpgme.c:1302
+#: src/engine-gpgme.c:1397
 msgid "*** End Notation ***\n"
 msgstr "*** Notation slut ***\n"
 
index a3a5a9b..85d0f94 100644 (file)
@@ -1,3 +1,7 @@
+2007-09-13  Werner Koch  <wk@g10code.com>
+
+       * common.c (xrealloc): New.
+
 2007-09-11  Werner Koch  <wk@g10code.com>
 
        * engine-gpgme.c (op_encrypt_data): New.
index b12b230..991377f 100644 (file)
@@ -167,6 +167,15 @@ xcalloc (size_t m, size_t n)
     return p;
 }
 
+void *
+xrealloc (void *a, size_t n)
+{
+  void *p = realloc (a, n);
+  if (!p)
+    out_of_core ();
+  return p;
+}
+
 char*
 xstrdup (const char *s)
 {
index a01f313..3104626 100644 (file)
@@ -50,6 +50,8 @@ static int init_done = 0;
 
 
 static DWORD WINAPI waiter_thread (void *dummy);
+static void update_passphrase_cache (int err, 
+                                     struct passphrase_cb_s *pass_cb_value);
 static void add_verify_attestation (gpgme_data_t at, 
                                     gpgme_ctx_t ctx, 
                                     gpgme_verify_result_t res,
@@ -70,7 +72,7 @@ cleanup (void)
 
 /* Enable or disable GPGME debug mode. */
 void
-op_set_debug_mode (int val, const char *file)
+op_gpgme_set_debug_mode (int val, const char *file)
 {
   const char *s= "GPGME_DEBUG";
 
@@ -88,7 +90,7 @@ op_set_debug_mode (int val, const char *file)
 
 /* Cleanup static resources. */
 void
-op_deinit (void)
+op_gpgme_deinit (void)
 {
   cleanup ();
 }
@@ -96,7 +98,7 @@ op_deinit (void)
 
 /* Initialize the operation system. */
 int
-op_init (void)
+op_gpgme_init (void)
 {
   gpgme_error_t err;
 
@@ -148,6 +150,7 @@ waiter_thread (void *dummy)
   gpgme_ctx_t ctx;
   gpg_error_t err;
   void *filter;
+  void *pass_cb;
 
   (void)dummy;
 
@@ -164,6 +167,10 @@ waiter_thread (void *dummy)
         {
           gpgme_get_progress_cb (ctx, NULL, &filter);
           engine_gpgme_finished (filter, err);
+          gpgme_get_passphrase_cb (ctx, NULL, &pass_cb);
+          if (pass_cb)
+            update_passphrase_cache (err, (struct passphrase_cb_s *)pass_cb);
+          xfree (pass_cb);
           gpgme_release (ctx);
         }
       else if (err)
@@ -294,7 +301,7 @@ op_encrypt (const char *inbuf, char **outbuf, gpgme_key_t *keys,
 
   *outbuf = NULL;
 
-  op_init ();
+  op_gpgme_init ();
   err = gpgme_new (&ctx);
   if (err)
     goto leave;
@@ -549,9 +556,10 @@ prepare_recipient_keys (gpgme_key_t **r_keys, char **recipients, HWND hwnd)
    just for this notification.  We abuse the gpgme_set_progress_cb
    value for storing the pointer with the gpgme context.  */
 int
-op_encrypt_data (gpgme_data_t indata, gpgme_data_t outdata,
-                 void *notify_data, /* FIXME: Add hwnd */
-                 char **recipients, gpgme_key_t sign_key, int ttl)
+op_gpgme_encrypt_data (protocol_t protocol, 
+                       gpgme_data_t indata, gpgme_data_t outdata,
+                       void *notify_data, /* FIXME: Add hwnd */
+                       char **recipients, gpgme_key_t sign_key, int ttl)
 {
   gpg_error_t err;
   struct passphrase_cb_s cb;
@@ -569,6 +577,12 @@ op_encrypt_data (gpgme_data_t indata, gpgme_data_t outdata,
   err = gpgme_new (&ctx);
   if (err)
     goto leave;
+  if (protocol == PROTOCOL_SMIME)
+    {
+      err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
+      if (err)
+        goto leave;
+    }
   gpgme_set_progress_cb (ctx, NULL, notify_data);
 
   gpgme_set_armor (ctx, 1);
@@ -599,6 +613,61 @@ op_encrypt_data (gpgme_data_t indata, gpgme_data_t outdata,
 }
 
 
+/* Created a detached signature for INDATA and write it to OUTDATA.
+   On termination of the signing command engine_gpgme_finished() is
+   called with NOTIFY_DATA as the first argument.  */
+int
+op_gpgme_sign_data (protocol_t protocol, 
+                    gpgme_data_t indata, gpgme_data_t outdata,
+                    void *notify_data /* FIXME: Add hwnd */)
+{
+  gpg_error_t err;
+  struct passphrase_cb_s *cb;
+  gpgme_ctx_t ctx = NULL;
+  gpgme_key_t sign_key = NULL;
+
+  if (signer_dialog_box (&sign_key, NULL, 0) == -1)
+    {
+      log_debug ("%s:%s: leave (dialog failed)\n", SRCNAME, __func__);
+      return gpg_error (GPG_ERR_CANCELED);  
+    }
+
+  cb = xcalloc (1, sizeof *cb);
+  cb->ttl = 0 /*FIXME: ttl*/;
+
+  err = gpgme_new (&ctx);
+  if (err)
+    goto leave;
+  if (protocol == PROTOCOL_SMIME)
+    {
+      err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
+      if (err)
+        goto leave;
+    }
+
+  gpgme_set_progress_cb (ctx, NULL, notify_data);
+
+  gpgme_set_armor (ctx, 1);
+  gpgme_set_passphrase_cb (ctx, passphrase_callback_box, cb);
+  cb->ctx = ctx;
+  err = gpgme_signers_add (ctx, sign_key);
+  if (!err)
+    err = gpgme_op_sign_start (ctx, indata, outdata, GPGME_SIG_MODE_DETACH);
+
+ leave:
+  if (ctx && err)
+    {
+      xfree (cb);
+      gpgme_release (ctx);
+    }
+  gpgme_key_unref (sign_key);
+  return err;
+}
+
+
+
+
+
 \f
 /* Sign and encrypt the data in INBUF into a newly allocated buffer at
    OUTBUF. Caller needs to free the returned buffer using gpgme_free. */
@@ -617,7 +686,7 @@ op_sign (const char *inbuf, char **outbuf, int mode,
   cb.decrypt_cmd = 0;
 
   *outbuf = NULL;
-  op_init ();
+  op_gpgme_init ();
   
   err = gpgme_new (&ctx);
   if (err) 
@@ -740,7 +809,7 @@ op_decrypt (const char *inbuf, char **outbuf, int ttl, const char *filename,
   gpgme_error_t err;
   
   *outbuf = NULL;
-  op_init ();
+  op_gpgme_init ();
 
   memset (&cb, 0, sizeof cb);
   cb.ttl = ttl;
@@ -1028,7 +1097,7 @@ op_verify (const char *inbuf, char **outbuf, const char *filename,
   if (outbuf)
     *outbuf = NULL;
 
-  op_init ();
+  op_gpgme_init ();
 
   err = gpgme_new (&ctx);
   if (err)
@@ -1092,7 +1161,7 @@ op_verify_detached_sig (LPSTREAM data_stream,
   cbs.read = stream_read_cb;
   cbs.write = stream_write_cb;
 
-  op_init ();
+  op_gpgme_init ();
 
   err = gpgme_new (&ctx);
   if (err)
@@ -1142,7 +1211,7 @@ op_verify_detached_sig_mem (const char *data_string,
   gpgme_error_t err;
   gpgme_verify_result_t res = NULL;
 
-  op_init ();
+  op_gpgme_init ();
 
   err = gpgme_new (&ctx);
   if (err)
@@ -1191,7 +1260,7 @@ op_verify_detached_sig_gpgme (gpgme_protocol_t protocol,
   gpgme_error_t err;
   gpgme_verify_result_t res = NULL;
 
-  op_init ();
+  op_gpgme_init ();
 
   err = gpgme_new (&ctx);
   if (err)
index f195a6f..2ad5e8c 100644 (file)
@@ -267,7 +267,7 @@ engine_gpgme_finished (engine_filter_t filter, gpg_error_t status)
 int
 engine_init (void)
 {
-  op_init ();
+  op_gpgme_init ();
   return 0;
 }
 
@@ -276,7 +276,7 @@ engine_init (void)
 void
 engine_deinit (void)
 {
-  op_deinit ();
+  op_gpgme_deinit ();
 
 }
 
@@ -509,58 +509,41 @@ engine_cancel (engine_filter_t filter)
 
 /* Start an encryption operation to all RECIPEINTS using PROTOCOL
    RECIPIENTS is a NULL terminated array of rfc2822 addresses.  FILTER
-   is an object create by engine_create_filter.  The caller needs to
+   is an object created by engine_create_filter.  The caller needs to
    call engine_wait to finish the operation.  A filter object may not
    be reused after having been used through this function.  However,
    the lifetime of the filter object lasts until the final engine_wait
-   or engine_cabcel.  */
+   or engine_cancel.  */
 int
 engine_encrypt_start (engine_filter_t filter,
                       protocol_t protocol, char **recipients)
 {
   gpg_error_t err;
 
-  err = op_encrypt_data (filter->indata, filter->outdata,
-                         filter, recipients, NULL, 0);
+  err = op_gpgme_encrypt_data (protocol, filter->indata, filter->outdata,
+                               filter, recipients, NULL, 0);
   return err;
 }
 
 
-
-\f
-/* Release a KEY.  Do nothing if KEY is NULL.  */
-void
-engine_release_key (engine_keyinfo_t key)
+/* Start an detached signing operation.
+   FILTER
+   is an object created by engine_create_filter.  The caller needs to
+   call engine_wait to finish the operation.  A filter object may not
+   be reused after having been used through this function.  However,
+   the lifetime of the filter object lasts until the final engine_wait
+   or engine_cancel.  */
+int
+engine_sign_start (engine_filter_t filter, protocol_t protocol)
 {
-  if (!key)
-    return;
-  gpgme_key_release (key->gpgme.key);
-  key->gpgme.key = NULL;
-}
+  gpg_error_t err;
 
+  err = op_gpgme_sign_data (protocol, filter->indata, filter->outdata,
+                            filter);
+  return err;
+}
 
-/* Return a signing key and store it at the R_KEY.  the caller sahll
-   set ENCRYPTING to true if the message will also be encrypted.  On
-   error true is returned and NULL stored at R_KEY.  Not ethat
-   depending on the actual engine use the retruned key might be NULL -
-   thus this shall not be used as an error indication. */
-int 
-engine_get_signer_key (engine_keyinfo_t *r_key, int encrypting)
-{
-  engine_keyinfo_t key;
-  gpgme_key_t sign_key = NULL;
 
-  *r_key = NULL;
-  if (signer_dialog_box (&sign_key, NULL, encrypting) == -1)
-    {
-      log_debug ("%s:%s: failed to get key from dialog\n", SRCNAME, __func__);
-      return gpg_error (GPG_ERR_CANCELED);  
-    }
-  key = xcalloc (1, sizeof **r_key);
-  key->gpgme.key = sign_key;
 
-  *r_key = key;
-  return 0;
-}
 
 
index d2a992d..a4daa11 100644 (file)
@@ -66,12 +66,10 @@ void engine_cancel (engine_filter_t filter);
 
 int engine_encrypt_start (engine_filter_t filter, 
                           protocol_t protocol, char **recipients);
+int engine_sign_start (engine_filter_t filter, protocol_t protocol);
 
 
 
-void engine_release_key (engine_keyinfo_t key);
-int  engine_get_signer_key (engine_keyinfo_t *r_key, int encrypting);
-
 #ifdef __cplusplus
 }
 #endif
index 7ce2e00..7ebe427 100644 (file)
@@ -62,10 +62,20 @@ typedef struct sink_s *sink_t;
 struct sink_s
 {
   void *cb_data;
+  sink_t extrasink;
   int (*writefnc)(sink_t sink, const void *data, size_t datalen);
 };
 
 
+/* Object used to collect data in a memory buffer.  */
+struct databuf_s
+{
+  size_t len;      /* Used length.  */
+  size_t size;     /* Allocated length of BUF.  */
+  char *buf;       /* Malloced buffer.  */
+};
+
+
 /*** local prototypes  ***/
 static int write_multistring (sink_t sink, const char *text1,
                               ...) GPGOL_GCC_A_SENTINEL(0);
@@ -419,6 +429,14 @@ write_qp (sink_t sink, const void *data, size_t datalen)
           outbuf[outidx++] = tohex ((*p>>4)&15);
           outbuf[outidx++] = tohex (*p&15);
         }
+      else if (!outidx && datalen >= 5 && !memcmp (p, "From ", 5))
+        {
+          /* Protect the 'F' so that MTAs won't prefix the "From "
+             with an '>' */
+          outbuf[outidx++] = '=';
+          outbuf[outidx++] = tohex ((*p>>4)&15);
+          outbuf[outidx++] = tohex (*p&15);
+        }
       else if (*p >= '!' && *p <= '~' && *p != '=')
         {
           do_softlf (1);
@@ -713,6 +731,12 @@ infer_content_encoding (const void *data, size_t datalen)
              We better protect it by forcing QP encoding.  */
           need_qp = 1;
         }
+      else if (len == 1 && datalen >= 5 && !memcmp (p, "From ", 5))
+        {
+          /* The usual From hack is required so that MTAs do not
+             prefix it with an '>'.  */
+          need_qp = 1;
+        }
     }
   if (len > maxlen)
     maxlen = len;
@@ -1012,7 +1036,7 @@ finalize_message (LPMESSAGE message, mapi_attach_item_t *att_table)
     proparray.aulPropTag[0] = PR_BODY_HTML;
     IMessage_DeleteProps (message, &proparray, NULL);
   }
-  
+
   /* Save the Changes.  */
   hr = IMessage_SaveChanges (message, KEEP_OPEN_READWRITE|FORCE_SAVE);
   if (hr)
@@ -1026,6 +1050,64 @@ finalize_message (LPMESSAGE message, mapi_attach_item_t *att_table)
 }
 
 
+\f
+/* Sink write method used by mime_sign.  We write the data to the
+   filter and also to the EXTRASINK but we don't pass a flush request
+   to EXTRASINK. */
+static int
+sink_hashing_write (sink_t hashsink, const void *data, size_t datalen)
+{
+  int rc;
+  engine_filter_t filter = hashsink->cb_data;
+
+  if (!filter || !hashsink->extrasink)
+    {
+      log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
+      return -1;
+    }
+  
+  rc = engine_filter (filter, data, datalen);
+  if (!rc && data && datalen)
+    write_buffer (hashsink->extrasink, data, datalen);
+  return rc;
+}
+
+/* This function is called by the filter to collect the output which
+   is a detached signature.  */
+static int
+collect_signature (void *opaque, const void *data, size_t datalen)
+{
+  struct databuf_s *db = opaque;
+
+  if (db->len + datalen >= db->size)
+    {
+      db->size += datalen + 1024;
+      db->buf = xrealloc (db->buf, db->size);
+    }
+  memcpy (db->buf + db->len, data, datalen);
+  db->len += datalen;
+
+  return 0;
+}
+
+
+/* Helper to create the signing header.  This includes enough space
+   for later fixup of the micalg parameter.  */
+ static void
+create_top_signing_header (char *buffer, size_t buflen, protocol_t protocol,
+                           const char *boundary, const char *micalg)
+{
+  snprintf (buffer, buflen,
+            "MIME-Version: 1.0\r\n"
+            "Content-Type: multipart/signed;\r\n"
+            "\tprotocol=\"application/%s\";\r\n"
+            "\tmicalg=%-15.15s;\r\n"
+            "\tboundary=\"%s\"\r\n"
+            "\r\n",
+            (protocol==PROTOCOL_OPENPGP? "pgp-signature":"pkcs7-signature"),
+            micalg, boundary);
+}
+
 /* Sign the MESSAGE using PROTOCOL.  If PROTOCOL is PROTOCOL_UNKNOWN
    the engine decides what protocol to use.  On return MESSAGE is
    modified so that sending it will result in a properly MOSS (that is
@@ -1040,13 +1122,20 @@ mime_sign (LPMESSAGE message, protocol_t protocol)
   LPATTACH attach;
   struct sink_s sinkmem;
   sink_t sink = &sinkmem;
+  struct sink_s hashsinkmem;
+  sink_t hashsink = &hashsinkmem;
   char boundary[BOUNDARYSIZE+1];
   char inner_boundary[BOUNDARYSIZE+1];
   mapi_attach_item_t *att_table = NULL;
   char *body = NULL;
   int n_att_usable;
+  char top_header[BOUNDARYSIZE+200];
+  engine_filter_t filter;
+  struct databuf_s sigbuffer;
 
   memset (sink, 0, sizeof *sink);
+  memset (hashsink, 0, sizeof *hashsink);
+  memset (&sigbuffer, 0, sizeof sigbuffer);
 
   protocol = check_protocol (protocol);
   if (protocol == PROTOCOL_UNKNOWN)
@@ -1056,6 +1145,12 @@ mime_sign (LPMESSAGE message, protocol_t protocol)
   if (!attach)
     return -1;
 
+  /* Prepare the signing.  */
+  if (engine_create_filter (&filter, collect_signature, &sigbuffer))
+    goto failure;
+  if (engine_sign_start (filter, protocol))
+    goto failure;
+
   /* Get the attachment info and the body.  */
   body = mapi_get_body (message, NULL);
   if (body && !*body)
@@ -1073,60 +1168,66 @@ mime_sign (LPMESSAGE message, protocol_t protocol)
 
   /* Write the top header.  */
   generate_boundary (boundary);
-  rc = write_multistring (sink,
-                          "MIME-Version: 1.0\r\n"
-                          "Content-Type: multipart/signed;\r\n"
-                          "\tprotocol=\"application/",
-                          (protocol == PROTOCOL_OPENPGP
-                           ? "pgp-signature"
-                           : "pkcs7-signature"),
-                          "\";\r\n\tboundary=\"",
-                          boundary,
-                          "\"\r\n",
-                          NULL);
-  if (rc)
+  create_top_signing_header (top_header, sizeof top_header,
+                             protocol, boundary, "xxx");
+  if ((rc = write_string (sink, top_header)))
     goto failure;
-  
+
+  /* Create the inner boundary if we have a body and at least one
+     attachment or more than one attachment.  */
   if ((body && n_att_usable) || n_att_usable > 1)
-    {
-      /* A body and at least one attachment or more than one attachment  */
-      generate_boundary (inner_boundary);
-      if ((rc = write_boundary (sink, boundary, 0)))
-        goto failure;
-      if ((rc=write_multistring (sink, 
-                                 "Content-Type: multipart/mixed;\r\n",
-                                 "\tboundary=\"", inner_boundary, "\"\r\n",
-                                 NULL)))
-          goto failure;
-    }
-  else /* Only one part.  */
+    generate_boundary (inner_boundary);
+  else 
     *inner_boundary = 0;
 
+  /* Write the boundary so that it is not included in the hashing.  */
+  if ((rc = write_boundary (sink, boundary, 0)))
+    goto failure;
+
+  /* Create a new sink for hashing and wire/hash our content.  */
+  hashsink->cb_data = filter;
+  hashsink->extrasink = sink;
+  hashsink->writefnc = sink_hashing_write;
+
+  /* Note that OL2003 will add an extra line after the multipart
+     header, thus we do the same to avoid running all through an
+     IConverterSession first. */
+  if (*inner_boundary
+      && (rc=write_multistring (hashsink, 
+                                "Content-Type: multipart/mixed;\r\n",
+                                "\tboundary=\"", inner_boundary, "\"\r\n",
+                                "\r\n",  /* <-- extra line */
+                                NULL)))
+        goto failure;
+
 
   if (body)
-    rc = write_part (sink, body, strlen (body),
-                     *inner_boundary? inner_boundary : boundary, NULL, 1);
+    rc = write_part (hashsink, body, strlen (body),
+                     *inner_boundary? inner_boundary : NULL, NULL, 1);
   if (!rc && n_att_usable)
-    rc = write_attachments (sink, message, att_table,
-                            *inner_boundary? inner_boundary : boundary);
+    rc = write_attachments (hashsink, message, att_table,
+                            *inner_boundary? inner_boundary : NULL);
   if (rc)
     goto failure;
 
-
   xfree (body);
   body = NULL;
 
   /* Finish the possible multipart/mixed. */
-  if (*inner_boundary)
-    {
-      rc = write_boundary (sink, inner_boundary, 1);
-      if (rc)
-        goto failure;
-    }
+  if (*inner_boundary && (rc = write_boundary (hashsink, inner_boundary, 1)))
+    goto failure;
 
+  /* Here we are ready with the hashing.  Flush the filter and wait
+     for the signing process to finish.  */
+  if ((rc = write_buffer (hashsink, NULL, 0)))
+    goto failure;
+  if ((rc = engine_wait (filter)))
+    goto failure;
+  filter = NULL; /* Not valid anymore.  */
+  hashsink->cb_data = NULL; /* Not needed anymore.  */
+  
 
-  /* Write signature attachment.  We don't write it directly but use a
-     placeholder.  */
+  /* Write signature attachment.  */
   if ((rc = write_boundary (sink, boundary, 0)))
     goto failure;
 
@@ -1137,43 +1238,88 @@ mime_sign (LPMESSAGE message, protocol_t protocol)
     goto failure;
 
   /* If we would add "Content-Transfer-Encoding: 7bit\r\n" to this
-     attachment, Outlooks does not processed with sending and even
-     does not return any error.  A wild guess is that while OL adds
-     this header itself, it detects that it already exists and somehow
-     gets into a problem.  It is not a problem with the other parts,
+     attachment, Outlooks does not proceed with sending and even does
+     not return any error.  A wild guess is that while OL adds this
+     header itself, it detects that it already exists and somehow gets
+     into a problem.  It is not a problem with the other parts,
      though.  Hmmm, triggered by the top levels CT protocol parameter?
-     Any way, it is not required that we add it as we won't hash
+     Anyway, it is not required that we add it as we won't hash
      it.  */
 
   if ((rc = write_string (sink, "\r\n")))
     goto failure;
 
-  /* Let the placeholder start with the prefix of a boundary so that
-     it won't accidently occur in the actual content. */
-  if ((rc = write_string (sink, "--=-=@SIGNATURE@\r\n\r\n")))
+  /* Write the signature.  We add an extra CR,LF which should not harm
+     and a terminating 0. */
+  collect_signature (&sigbuffer, "\r\n", 3); 
+  if ((rc = write_string (sink, sigbuffer.buf)))
     goto failure;
 
+
   /* Write the final boundary and finish the attachment.  */
   if ((rc = write_boundary (sink, boundary, 1)))
     goto failure;
 
+  /* Fixup the micalg parameter.  */
+  {
+    HRESULT hr;
+    LARGE_INTEGER off;
+    LPSTREAM stream = sink->cb_data;
+
+    off.QuadPart = 0;
+    hr = IStream_Seek (stream, off, STREAM_SEEK_SET, NULL);
+    if (hr)
+      {
+        log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
+                   SRCNAME, __func__, hr);
+        goto failure;
+      }
+
+    create_top_signing_header (top_header, sizeof top_header,
+                               protocol, boundary, "pgp-sha1");
+
+    hr = IStream_Write (stream, top_header, strlen (top_header), NULL);
+    if (hr)
+      {
+        log_error ("%s:%s: writing fixed micalg failed: hr=%#lx",
+                   SRCNAME, __func__, hr);
+        goto failure;
+      }
+
+    /* Better seek again to the end. */
+    off.QuadPart = 0;
+    hr = IStream_Seek (stream, off, STREAM_SEEK_END, NULL);
+    if (hr)
+      {
+        log_error ("%s:%s: seeking back to the end failed: hr=%#lx",
+                   SRCNAME, __func__, hr);
+        goto failure;
+      }
+  }
+
+
   if (close_mapi_attachment (&attach, sink))
     goto failure;
 
   if (finalize_message (message, att_table))
     goto failure;
 
+  mapi_to_mime (message, "c:\\tmp\\x.msg");
+
   result = 0;  /* Everything is fine, fall through the cleanup now.  */
 
  failure:
+  engine_cancel (filter);
   cancel_mapi_attachment (&attach, sink);
   xfree (body);
   mapi_release_attach_table (att_table);
+  xfree (sigbuffer.buf);
   return result;
 }
 
 
 
+\f
 /* Sink write method used by mime_encrypt.  */
 static int
 sink_encryption_write (sink_t encsink, const void *data, size_t datalen)
index 69f026f..03fdfc1 100644 (file)
@@ -32,6 +32,7 @@ extern "C" {
 /*-- common.c --*/
 void* xmalloc (size_t n);
 void* xcalloc (size_t m, size_t n);
+void *xrealloc (void *a, size_t n);
 char* xstrdup (const char *s);
 void  xfree (void *p);
 void out_of_core (void);