[Gpa-commits] r835 - trunk/src

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Wed Mar 5 11:12:58 CET 2008


Author: werner
Date: 2008-03-05 11:12:57 +0100 (Wed, 05 Mar 2008)
New Revision: 835

Modified:
   trunk/src/ChangeLog
   trunk/src/gpastreamencryptop.c
   trunk/src/gpastreamencryptop.h
   trunk/src/recipientdlg.c
   trunk/src/recipientdlg.h
   trunk/src/server.c
Log:
Various rewrites for the server mode.


Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog	2008-03-04 16:46:03 UTC (rev 834)
+++ trunk/src/ChangeLog	2008-03-05 10:12:57 UTC (rev 835)
@@ -1,3 +1,9 @@
+2008-03-05  Werner Koch  <wk at g10code.com>
+
+	* server.c (struct conn_ctrl_s): Add field GPA_OP.
+	* gpastreamencryptop.c: Major rewrite.
+	* recipientdlg.c, recipientdlg.h: Add more stuff.
+
 2008-03-04  Marcus Brinkmann  <marcus at g10code.de>
 
 	* gpafileverifyop.c (gpa_file_verify_operation_done_cb): Use

Modified: trunk/src/gpastreamencryptop.c
===================================================================
--- trunk/src/gpastreamencryptop.c	2008-03-04 16:46:03 UTC (rev 834)
+++ trunk/src/gpastreamencryptop.c	2008-03-05 10:12:57 UTC (rev 835)
@@ -28,11 +28,32 @@
 #include "gpastreamencryptop.h"
 
 
+
+struct _GpaStreamEncryptOperation 
+{
+  GpaStreamOperation parent;
+  
+  RecipientDlg *recp_dialog;
+  GSList *recipients;
+  gpgme_key_t *keys;
+  gpgme_protocol_t selected_protocol;
+};
+
+
+struct _GpaStreamEncryptOperationClass 
+{
+  GpaStreamOperationClass parent_class;
+};
+
+
+
 /* Indentifiers for our properties. */
 enum 
   {
     PROP_0,
-    PROP_RECIPIENTS
+    PROP_RECIPIENTS,
+    PROP_RECIPIENT_KEYS,
+    PROP_PROTOCOL
   };
 
 
@@ -40,6 +61,7 @@
 static void response_cb (GtkDialog *dialog,
                          gint response,
                          gpointer user_data);
+static gboolean start_encryption_cb (gpointer data);
 static void done_error_cb (GpaContext *context, gpg_error_t err,
                            GpaStreamEncryptOperation *op);
 static void done_cb (GpaContext *context, gpg_error_t err,
@@ -48,6 +70,7 @@
 static GObjectClass *parent_class;
 
 
+
 /* Helper to be used as a GFunc for free. */
 static void
 free_func (void *p, void *dummy)
@@ -57,8 +80,72 @@
 }
 
 
+static void
+release_recipients (GSList *recipients)
+{
+  if (recipients)
+    {
+      g_slist_foreach (recipients, free_func, NULL);
+      g_slist_free (recipients);
+    }
+}
+
+/* Return a deep copy of the recipients list.  */
+static GSList *
+copy_recipients (GSList *recipients)
+{
+  GSList *recp, *newlist;
+  
+  newlist= NULL;
+  for (recp = recipients; recp; recp = g_slist_next (recp))
+    newlist = g_slist_append (newlist, g_strdup (recp->data));
+
+  return newlist;
+}
+
+
 
 static void
+release_keys (gpgme_key_t *keys)
+{
+  if (keys)
+    {
+      int idx;
+      
+      for (idx=0; keys[idx]; idx++)
+        gpgme_key_unref (keys[idx]);
+      g_free (keys);
+    }
+}
+
+
+/* Return a copy of the key array.  */
+static gpgme_key_t *
+copy_keys (gpgme_key_t *keys)
+{
+  gpgme_key_t *newarray;
+  int idx;
+
+  if (!keys)
+    return NULL;
+
+  for (idx=0; keys[idx]; idx++)
+    ;
+  idx++;
+  newarray = g_new (gpgme_key_t, idx); 
+  for (idx=0; keys[idx]; idx++)
+    {
+      gpgme_key_ref (keys[idx]);
+      newarray[idx] = keys[idx];
+    }
+  newarray[idx] = NULL;
+
+  return newarray;
+}
+
+
+
+static void
 gpa_stream_encrypt_operation_get_property (GObject *object, guint prop_id,
                                            GValue *value, GParamSpec *pspec)
 {
@@ -69,6 +156,12 @@
     case PROP_RECIPIENTS:
       g_value_set_pointer (value, op->recipients);
       break;
+    case PROP_RECIPIENT_KEYS:
+      g_value_set_pointer (value, op->keys);
+      break;
+    case PROP_PROTOCOL:
+      g_value_set_int (value, op->selected_protocol);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -88,6 +181,12 @@
     case PROP_RECIPIENTS:
       op->recipients = (GSList*)g_value_get_pointer (value);
       break;
+    case PROP_RECIPIENT_KEYS:
+      op->keys = (gpgme_key_t*)g_value_get_pointer (value);
+      break;
+    case PROP_PROTOCOL:
+      op->selected_protocol = g_value_get_int (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -100,12 +199,10 @@
 {  
   GpaStreamEncryptOperation *op = GPA_STREAM_ENCRYPT_OPERATION (object);
 
-  if (op->recipients)
-    {
-      g_slist_foreach (op->recipients, free_func, NULL);
-      g_slist_free (op->recipients);
-      op->recipients = NULL;
-    }
+  release_recipients (op->recipients);
+  op->recipients = NULL;
+  release_keys(op->keys);
+  op->keys = NULL;
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -114,8 +211,9 @@
 static void
 gpa_stream_encrypt_operation_init (GpaStreamEncryptOperation *op)
 {
-  op->encrypt_dialog = NULL;
+  op->recp_dialog = NULL;
   op->recipients = NULL;
+  op->keys = NULL;
   op->selected_protocol = GPGME_PROTOCOL_UNKNOWN;
 }
 
@@ -134,13 +232,21 @@
 				      construct_properties);
   op = GPA_STREAM_ENCRYPT_OPERATION (object);
 
-  /* Create the "Encrypt" dialog */
-  op->encrypt_dialog = recipient_dlg_new (GPA_OPERATION (op)->window);
+  /* Create the recipient key selection dialog if we don't know the
+     keys yet. */
+  if (!op->keys)
+    {
+      op->recp_dialog = recipient_dlg_new (GPA_OPERATION (op)->window);
+      recipient_dlg_set_recipients (op->recp_dialog,
+                                    op->recipients,
+                                    op->selected_protocol);
+      g_signal_connect (G_OBJECT (op->recp_dialog), "response",
+                        G_CALLBACK (response_cb), op);
+    }
+  else
+    g_idle_add (start_encryption_cb, op);
 
-  recipient_dlg_set_recipients (op->encrypt_dialog, op->recipients);
 
-  g_signal_connect (G_OBJECT (op->encrypt_dialog), "response",
-		    G_CALLBACK (response_cb), op);
   /* We connect the done signal to two handles.  The error handler is
      called first.  */
   g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done",
@@ -152,7 +258,8 @@
     (GTK_WINDOW (GPA_STREAM_OPERATION (op)->progress_dialog),
 			_("Encrypting message ..."));
 
-  gtk_widget_show_all (op->encrypt_dialog);
+  if (op->recp_dialog)
+    gtk_widget_show_all (GTK_WIDGET (op->recp_dialog));
 
   return object;
 }
@@ -176,6 +283,19 @@
      ("recipients", "Recipients",
       "A list of recipients in rfc-822 mailbox format.",
       G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property 
+    (object_class, PROP_RECIPIENT_KEYS,
+     g_param_spec_pointer 
+     ("recipient-keys", "Recipient-keys",
+      "An array of gpgme_key_t with the selected keys.",
+      G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property 
+    (object_class, PROP_PROTOCOL,
+     g_param_spec_int 
+     ("protocol", "Protocol",
+      "The gpgme protocol currently selected.",
+      GPGME_PROTOCOL_OpenPGP, GPGME_PROTOCOL_UNKNOWN, GPGME_PROTOCOL_UNKNOWN,
+      G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
 
 }
 
@@ -209,184 +329,106 @@
 }
 
 
-static gpg_error_t
-start_encryption (GpaStreamEncryptOperation *op, gpgme_key_t *keys)
+/* Return true if all keys are matching the protocol. */
+static int
+keys_match_protocol_p (gpgme_key_t *keys, gpgme_protocol_t protocol)
 {
-  gpg_error_t err;
-  
-  /* Start the operation */
-  /* Always trust keys, because any untrusted keys were already confirmed
-   * by the user.
-   */
-/*   if (gpa_file_encrypt_dialog_sign  */
-/*       (GPA_FILE_ENCRYPT_DIALOG (op->encrypt_dialog))) */
-/*     { */
-/*       /\* Signing was request as well.  *\/ */
-/*       err = gpgme_op_encrypt_sign_start (GPA_OPERATION (op)->context->ctx, */
-/* 					 op->rset, GPGME_ENCRYPT_ALWAYS_TRUST, */
-/* 					 op->plain, op->cipher); */
-/*     } */
-/*   else */
-    {
-      /* Plain encryption.  */
-      err = gpgme_op_encrypt_start (GPA_OPERATION (op)->context->ctx,
-				    keys, GPGME_ENCRYPT_ALWAYS_TRUST,
-				    GPA_STREAM_OPERATION (op)->input_stream,
-				    GPA_STREAM_OPERATION (op)->output_stream);
-    }
-  if (err)
-    {
-      gpa_gpgme_warning (err);
-      return err;
-    }
+  int idx;
 
-  /* Show and update the progress dialog */
-  gtk_widget_show_all (GPA_STREAM_OPERATION (op)->progress_dialog);
-  gpa_progress_dialog_set_label (GPA_PROGRESS_DIALOG 
-				 (GPA_STREAM_OPERATION (op)->progress_dialog),
-				 _("Message encryption"));
-  return 0;
+  if (!keys)
+    return 1; /* No keys: assume match.  */
+  for (idx = 0; keys[idx]; idx++)
+    if (keys[idx]->protocol != protocol)
+      return 0;
+  return 1;
 }
 
 
-
-/* Given a list of strings, each describing one recipient, parse them
-   to detect duplicates, check the validity of each key and ask the
-   user whether he wants to use an invalid key. 
-
-   Try to find a key for each item in RECIPIENT.  Items not found are
-   stored in the newly allocated list R_UNKNOWN.  Found keys are
-   stored in the newly allocated and NULL terminated gpgme_key_t array
-   R_FOUND.  Caller needs to release R_FOUND and R_UNKNOWN.
+/*
+ * Fire up the encryption.
  */
-static gpg_error_t
-parse_recipients (GpaStreamEncryptOperation *op, GSList *recipients,
-                  gpgme_key_t **r_found, GSList **r_unknown)
+static void
+start_encryption (GpaStreamEncryptOperation *op)
 {
-  gpgme_error_t err;
-  GSList *recp;
-  gpgme_ctx_t ctx;
-  size_t n;
-  gpgme_key_t *found;
-  GSList *unknown = NULL;
-  int found_idx;
-  gpgme_key_t key, key2;
-  const char *name;
-  gpgme_protocol_t used_proto = GPGME_PROTOCOL_UNKNOWN;
+  gpg_error_t err;
+  int prep_only = 0;
 
-  *r_found = NULL;
-  *r_unknown = NULL;
+  if (!op->keys || !op->keys[0])
+    {
+      err = gpg_error (GPG_ERR_NO_PUBKEY);
+      goto leave;
+    }
 
-  err = gpgme_new (&ctx);
+  if (op->selected_protocol == GPGME_PROTOCOL_OpenPGP)
+    err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
+                                      "OpenPGP", NULL);
+  else if (op->selected_protocol == GPGME_PROTOCOL_CMS)
+    err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
+                                      "CMS", NULL);
+  else
+    err = gpg_error (GPG_ERR_NO_PUBKEY);
   if (err)
-    return err; 
+    goto leave;
 
-  /* Fixme: Add rfc-822 mailbox parsing.  */
+  /* Set the output encoding.  */
+  if (GPA_STREAM_OPERATION (op)->input_stream 
+      && GPA_STREAM_OPERATION (op)->output_stream)
+    {
+      if (op->selected_protocol == GPGME_PROTOCOL_CMS)
+        gpgme_data_set_encoding (GPA_STREAM_OPERATION (op)->output_stream,
+                                 GPGME_DATA_ENCODING_BASE64);
+      else
+        gpgme_set_armor (GPA_OPERATION (op)->context->ctx, 1);
 
-  for (n=0, recp = recipients; recp; recp = g_slist_next (recp))
-    n++;
-
-  found = xcalloc (n+1, sizeof *found);
-  found_idx = 0;
-  for (recp = recipients; recp; recp = g_slist_next (recp))
-    {
-      name = recp->data;
-      if (!name)
-        continue;
-      key = NULL;
-      err = gpgme_op_keylist_start (ctx, name, 0);
-      if (!err)
+      if (!keys_match_protocol_p (op->keys, op->selected_protocol))
         {
-          err = gpgme_op_keylist_next (ctx, &key);
-          if (!err && !gpgme_op_keylist_next (ctx, &key2))
-            {
-              /* Not found or ambiguous key specification.  */
-              gpgme_key_unref (key);
-              gpgme_key_unref (key2);
-              key = key2 = NULL;
-            }
+          g_debug ("the selected keys do not match the protocol");
+          err = gpg_error (GPG_ERR_CONFLICT);
+          goto leave;
         }
-      gpgme_op_keylist_end (ctx);
+                                   
+      gpgme_set_protocol (GPA_OPERATION (op)->context->ctx, 
+                          op->selected_protocol);
 
-      /* If a usable key has been found, put it into the list of good
-         keys.  All other keys end up in the list of unknown keys.  We
-         select the protocol to use from the frist found key.  Fixme:
-         We might want to have a different logic to select the
-         protocol.  */
-      if (key 
-          && used_proto != GPGME_PROTOCOL_UNKNOWN
-          && used_proto != key->protocol)
+      /* We always trust the keys because the recipient selection
+         dialog has already sorted unusable out.  */
+      err = gpgme_op_encrypt_start (GPA_OPERATION (op)->context->ctx,
+                                    op->keys, GPGME_ENCRYPT_ALWAYS_TRUST,
+                                    GPA_STREAM_OPERATION (op)->input_stream,
+                                    GPA_STREAM_OPERATION (op)->output_stream);
+      if (err)
         {
-          /* We can't use this key becuase it does not match the
-             selected protocol.  */
-	  char *p;
-          const char *warn = _("wrong protocol");
-
-	  n = strlen (name) + 3 + strlen (warn);
-          p = xmalloc (n+1);
-	  snprintf (p, n, "%s (%s)", name, warn);
-          unknown = g_slist_append (unknown, p);
+          gpa_gpgme_warning (err);
+          goto leave;
         }
-      else if (key && !key->revoked && !key->disabled && !key->expired)
-        {
-          gpgme_key_ref (key);
-          found[found_idx++] = key;
-          if (used_proto == GPGME_PROTOCOL_UNKNOWN)
-            used_proto = key->protocol;
-        }
-      else if (key)
-	{
-	  char *p;
-	  const char *warn = (key->revoked? "revoked" :
-                              key->expired? "expired" : "disabled");
-	  
-	  n = strlen (name) + 3 + strlen (warn);
-          p = xmalloc (n+1);
-	  snprintf (p, n, "%s (%s)", name, warn);
-          unknown = g_slist_append (unknown, p);
-	}
-      else /* Not found or ambiguous.  */
-        unknown = g_slist_append (unknown, xstrdup (name));
 
-      gpgme_key_unref (key);
+      /* Show and update the progress dialog.  */
+      gtk_widget_show_all (GPA_STREAM_OPERATION (op)->progress_dialog);
+      gpa_progress_dialog_set_label 
+        (GPA_PROGRESS_DIALOG (GPA_STREAM_OPERATION (op)->progress_dialog),
+         _("Message encryption"));
     }
-  gpgme_release (ctx);
-
-  if (err)
-    ;
-  else if (used_proto == GPGME_PROTOCOL_OpenPGP)
+  else
     {
-      op->selected_protocol = used_proto;
-      err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
-                                        "OpenPGP", NULL);
+      /* We are just preparing an encryption. */
+      prep_only = 1;
+      err = 0;
     }
-  else if (used_proto == GPGME_PROTOCOL_CMS)
+
+ leave:
+  if (err || prep_only)
     {
-      op->selected_protocol = used_proto;
-      err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
-                                        "CMS", NULL);
+      gpa_operation_server_finish (GPA_OPERATION (op), err);
+      g_signal_emit_by_name (GPA_OPERATION (op), "completed");
     }
-  else 
-    err = 0;
-
-  *r_found = found;
-  *r_unknown = unknown;
-  return err;
 }
 
 
-/*
- * The key selection dialog has returned.
- */
+/* The recipient key selection dialog has returned.  */
 static void 
 response_cb (GtkDialog *dialog, int response, void *user_data)
 {
-  gpg_error_t err;
-  GSList *recipients;
   GpaStreamEncryptOperation *op = user_data;
-  gpgme_key_t *keys;
-  GSList *unknown_recp;
-  int prep_only = 0;
 
   gtk_widget_hide (GTK_WIDGET (dialog));
   
@@ -400,59 +442,28 @@
       return;
     }
 
-  recipients = g_slist_append (NULL, xstrdup ("wk at gnupg.org"));
+  /* Get the keys.  */
+  release_keys (op->keys);
+  op->keys = NULL;
+  op->keys = recipient_dlg_get_keys (op->recp_dialog, &op->selected_protocol);
 
-/* gpa_stream_encrypt_dialog_recipients */
-/*     (GPA_STREAM_ENCRYPT_DIALOG (op->encrypt_dialog)); */
-      
-  err = parse_recipients (op, recipients, &keys, &unknown_recp);
-  if (err)
-    goto leave;
+  start_encryption (op);
+}
 
-  /* Set the output encoding.  */
-  if (GPA_STREAM_OPERATION (op)->input_stream 
-      && GPA_STREAM_OPERATION (op)->output_stream)
-    {
-      if (op->selected_protocol == GPGME_PROTOCOL_CMS)
-        {
-          gpgme_data_set_encoding (GPA_STREAM_OPERATION (op)->output_stream,
-                                   GPGME_DATA_ENCODING_BASE64);
-        }
-      else
-        {
-          gpgme_set_armor (GPA_OPERATION (op)->context->ctx, 1);
-        }
-      err = start_encryption (op, keys);
-    }
-  else
-    {
-      /* We are just preparing an encryption. */
-      prep_only = 1;
-      err = 0;
-    }
 
- leave:
-  if (keys)
-    {
-      int idx;
-      
-      for (idx=0; keys[idx]; idx++)
-        gpgme_key_unref (keys[idx]);
-      xfree (keys);
-    }
-  g_slist_foreach (unknown_recp, free_func, NULL);
-  g_slist_free (unknown_recp);
-  g_slist_foreach (recipients, free_func, NULL);
-  g_slist_free (recipients);
-  if (err || prep_only)
-    {
-      gpa_operation_server_finish (GPA_OPERATION (op), err);
-      g_signal_emit_by_name (GPA_OPERATION (op), "completed");
-    }
+/* This is the idle function used to start the encryption if no
+   recipient key selection dialog has been requested.  */
+static gboolean
+start_encryption_cb (void *user_data)
+{
+  GpaStreamEncryptOperation *op = user_data;
+
+  start_encryption (op);
+
+  return FALSE;  /* Remove this callback from the event loop.  */
 }
 
 
-
 /*Show an error message. */
 static void
 done_error_cb (GpaContext *context, gpg_error_t err,
@@ -477,7 +488,6 @@
 static void
 done_cb (GpaContext *context, gpg_error_t err, GpaStreamEncryptOperation *op)
 {
-
   gtk_widget_hide (GPA_STREAM_OPERATION (op)->progress_dialog);
 
   /* Tell the server that we finished and delete ourself.  */
@@ -487,44 +497,60 @@
 
 
 
+
+/************************************************************ 
+ **********************  Public API  ************************
+ ************************************************************/
 
-/* API */
-
 /* Start encrypting INPUT_STREAM to OUTPUT_STREAM using SERVER_CTX and
    WINDOW.  RECIPIENTS gives a list of recipients and the function
-   matches them with existing keys and selects appropriate keys.  The
-   ownership of RECIPIENTS is taken by this function.  If it is not
-   possible to unambigiously select keys and SILENT is not given, a
-   key selection dialog offers the user a way to manually select keys.
-   INPUT_STREAM and OUTPUT_STREAM may be given as NULL in which case
-   the function skips the actual encryption step and just verifies the
-   recipients.  */
-/* FIXME: We need to offer a way to return the actual selected list of
-   recipients so that repeating this command with that list instantly
-   starts the decryption.  */
+   matches them with existing keys and selects appropriate keys.
+   RECP_KEYS is either NULL or an array with gpgme keys which will
+   then immediatley be used and suppresses the recipient key selection
+   dialog.
+
+   If it is not possible to unambigiously select keys and SILENT is
+   not given, a key selection dialog offers the user a way to manually
+   select keys.  INPUT_STREAM and OUTPUT_STREAM may be given as NULL
+   in which case the function skips the actual encryption step and
+   just verifies the recipients.  */
 GpaStreamEncryptOperation*
 gpa_stream_encrypt_operation_new (GtkWidget *window,
                                   gpgme_data_t input_stream,
                                   gpgme_data_t output_stream,
                                   GSList *recipients,
+                                  gpgme_key_t *recp_keys,
+                                  gpgme_protocol_t protocol,
                                   int silent,
                                   void *server_ctx)
 {
   GpaStreamEncryptOperation *op;
-  
+
+  /* Fixme: SILENT is not yet implemented.  */
+
   op = g_object_new (GPA_STREAM_ENCRYPT_OPERATION_TYPE,
 		     "window", window,
 		     "input_stream", input_stream,
 		     "output_stream", output_stream,
-                     "recipients", recipients,
+                     "recipients", copy_recipients (recipients),
+                     "recipient-keys", copy_keys (recp_keys),
+                     "protocol", (int)protocol,
                      "server-ctx", server_ctx,
 		     NULL);
-  if (!op)
-    {
-      g_slist_foreach (recipients, free_func, NULL);
-      g_slist_free (recipients);
-    }
 
   return op;
 }
 
+
+/* Return an array of keys for the set of recipients of this object.
+   The function also returns the selected protocol.  */
+gpgme_key_t *
+gpa_stream_encrypt_operation_get_keys (GpaStreamEncryptOperation *op,
+                                       gpgme_protocol_t *r_protocol)
+{
+  g_return_val_if_fail (op, NULL);
+  
+  if (r_protocol)
+    *r_protocol = op->selected_protocol;
+  return copy_keys (op->keys);
+}

Modified: trunk/src/gpastreamencryptop.h
===================================================================
--- trunk/src/gpastreamencryptop.h	2008-03-04 16:46:03 UTC (rev 834)
+++ trunk/src/gpastreamencryptop.h	2008-03-05 10:12:57 UTC (rev 835)
@@ -55,25 +55,11 @@
 typedef struct _GpaStreamEncryptOperationClass GpaStreamEncryptOperationClass;
 
 
-struct _GpaStreamEncryptOperation 
-{
-  GpaStreamOperation parent;
-  
-  GtkWidget *encrypt_dialog;
-  GSList *recipients;
-  gpgme_protocol_t selected_protocol;
-};
-
-
-struct _GpaStreamEncryptOperationClass 
-{
-  GpaStreamOperationClass parent_class;
-};
-
-
 GType gpa_stream_encrypt_operation_get_type (void) G_GNUC_CONST;
 
-/* API */
+/************************************
+ ************ Public API ************
+ ************************************/
 
 /* Creates a new encryption operation. */
 GpaStreamEncryptOperation *
@@ -81,9 +67,14 @@
                                   gpgme_data_t input_stream,
                                   gpgme_data_t output_stream,
                                   GSList *recipients,
+                                  gpgme_key_t *recp_keys,
+                                  gpgme_protocol_t protocol,
                                   int silent,
                                   void *server_ctx);
 
+gpgme_key_t *gpa_stream_encrypt_operation_get_keys 
+(GpaStreamEncryptOperation *op, gpgme_protocol_t *r_protocol);
 
 
+
 #endif /*GPA_STREAM_ENCRYPT_OP_H*/

Modified: trunk/src/recipientdlg.c
===================================================================
--- trunk/src/recipientdlg.c	2008-03-04 16:46:03 UTC (rev 834)
+++ trunk/src/recipientdlg.c	2008-03-05 10:12:57 UTC (rev 835)
@@ -32,6 +32,21 @@
   GtkDialog parent;
   
   GtkWidget *clist_keys;
+  GtkWidget *statushint;
+  GtkWidget *radio_pgp;
+  GtkWidget *radio_x509;
+  GtkWidget *radio_auto;
+
+  /* Flag to disable updates of the status hint.  This is actual a
+     counter with updates allowed if it is zero. */
+  int  disable_update_statushint;
+
+  /* Set if this dialog has usable key to be passed back to the
+     caller.  You need to call update_statushint to set it.  */
+  int usable;
+
+  /* The selected protocol.  This is also set by update_statushint.  */
+  gpgme_protocol_t selected_protocol;
 };
 
 
@@ -55,6 +70,43 @@
   };
 
 
+/* For performance reasons we truncate the listing of ambiguous keys
+   at a reasonable value.  */
+#define TRUNCATE_KEYSEARCH_AT 40
+
+
+/* Na object to keep information about keys.  */
+struct keyinfo_s 
+{
+  /* An array with associated key(s) or NULL if none found/selected.  */
+  gpgme_key_t *keys;
+  
+  /* The allocated size of the KEYS array.  This included the
+     terminating NULL entry.  */
+  unsigned int dimof_keys;
+  
+  /* If set, indicates that the KEYS array has been truncated.  */
+  int truncated:1;
+};
+
+
+/* Management information for each recipient.  This data is used per
+   recipient.  */
+struct userdata_s
+{
+  /* Information about PGP keys.  */
+  struct keyinfo_s pgp;
+
+  /* Information about X.509 keys.  */
+  struct keyinfo_s x509;
+
+  /* If the user has set this field, no encryption key will be
+     required for the recipient.  */
+  int ignore_recipient;
+  
+};
+
+
 /* Identifiers for the columns of the RECPLIST.  */
 enum
   {
@@ -64,6 +116,9 @@
     RECPLIST_HAS_X509,  /* An X.509 certificate is available.  */
     RECPLIST_KEYID,     /* The key ID of the associated key. */
 
+    RECPLIST_USERDATA,  /* Pointer to management information (struct
+                           userdata_s *).  */
+
     RECPLIST_N_COLUMNS
   };
 
@@ -107,7 +162,9 @@
 			      G_TYPE_STRING,
                               G_TYPE_BOOLEAN,
                               G_TYPE_BOOLEAN,
-			      G_TYPE_STRING);
+			      G_TYPE_STRING,
+                              G_TYPE_POINTER);
+
   list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE);
 
@@ -123,15 +180,16 @@
   renderer = gtk_cell_renderer_toggle_new ();
   column = gtk_tree_view_column_new_with_attributes
     (NULL, renderer, "active", RECPLIST_HAS_PGP, NULL);
-  set_column_title (column, "P", _("Checked if at least one matching"
-                                   " OpenPGP certificate has been found.")); 
+  set_column_title (column, "PGP", _("Checked if at least one matching"
+                                     " OpenPGP certificate has been found.")); 
   gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
 
   renderer = gtk_cell_renderer_toggle_new ();
   column = gtk_tree_view_column_new_with_attributes
     (NULL, renderer, "active", RECPLIST_HAS_X509, NULL);
-  set_column_title (column, "X", _("Checked if at least one matching"
-                                   " X.509 certificate has been found.")); 
+  set_column_title (column, "X.509", _("Checked if at least one matching"
+                                       " X.509 certificate for use with S/MIME"
+                                       " has been found.")); 
   gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
 
   renderer = gtk_cell_renderer_text_new ();
@@ -147,86 +205,248 @@
 }
 
 
+/* Compute and display a new help text for the statushint.  */
+static void
+update_statushint (RecipientDlg *dialog)
+{
+  gpgme_protocol_t req_protocol, sel_protocol;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  int missing_keys = 0;
+  int ambiguous_pgp_keys = 0;
+  int ambiguous_x509_keys = 0;
+  int n_pgp_keys = 0;
+  int n_x509_keys = 0;
+  int n_keys = 0;
+  const char *hint;
+  int okay = 0;
+
+  if (dialog->disable_update_statushint)
+    return;
+
+  g_debug ("update_statushint called");
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->clist_keys));
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radio_pgp)))
+    req_protocol = GPGME_PROTOCOL_OpenPGP;
+  else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
+                                         (dialog->radio_x509)))
+    req_protocol = GPGME_PROTOCOL_CMS;
+  else
+    req_protocol = GPGME_PROTOCOL_UNKNOWN;
+  
+  sel_protocol = GPGME_PROTOCOL_UNKNOWN;
+
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    {
+      do
+        {
+          gboolean has_pgp, has_x509;
+          struct userdata_s *info;
+
+          gtk_tree_model_get (model, &iter, 
+                              RECPLIST_HAS_PGP, &has_pgp,
+                              RECPLIST_HAS_X509, &has_x509,
+                              RECPLIST_USERDATA, &info,
+                              -1);
+          if (!info)
+            missing_keys++;  /* Oops */
+          else if (info->ignore_recipient)
+            ;
+          else if (!info->pgp.keys && !info->x509.keys)
+            missing_keys++;
+          else if ((req_protocol == GPGME_PROTOCOL_OpenPGP && !has_pgp)
+                   ||(req_protocol == GPGME_PROTOCOL_CMS && !has_x509))
+            ; /* Not of the requested protocol.  */
+          else 
+            {
+              n_keys++;
+              if (info->pgp.keys && info->pgp.keys[0])
+                {
+                  n_pgp_keys++;
+                  if (info->pgp.keys[1])
+                    ambiguous_pgp_keys++;
+                }
+              if (info->x509.keys && info->x509.keys[0])
+                {
+                  n_x509_keys++;
+                  if (info->x509.keys[1])
+                    ambiguous_x509_keys++;
+                }
+            }
+        }
+      while (gtk_tree_model_iter_next (model, &iter));
+    }
+
+  if (req_protocol == GPGME_PROTOCOL_UNKNOWN)
+    {
+      /* We select the protocol with the most available keys,
+         preferring PGP.  */
+      if (n_pgp_keys >= n_x509_keys)
+        sel_protocol = GPGME_PROTOCOL_OpenPGP;
+      else if (n_x509_keys)
+        sel_protocol = GPGME_PROTOCOL_CMS;
+    }
+  else 
+    sel_protocol = req_protocol;
+
+
+  if (missing_keys)
+    hint = _("You need to select a key for each recipient.\n"
+             "To select a key you click on the respective line.");
+  else if ((sel_protocol == GPGME_PROTOCOL_OpenPGP
+            && ambiguous_pgp_keys)
+           || (sel_protocol == GPGME_PROTOCOL_CMS
+               && ambiguous_x509_keys)
+           || (sel_protocol == GPGME_PROTOCOL_UNKNOWN
+               && (ambiguous_pgp_keys || ambiguous_x509_keys )))
+    hint = _("You need to select exactly one key for each recipient.\n"
+             "To select a key you click on the respective line.");
+  else if ((sel_protocol == GPGME_PROTOCOL_OpenPGP
+            && n_keys != n_pgp_keys)
+           || (sel_protocol == GPGME_PROTOCOL_CMS
+               && n_keys != n_x509_keys)
+           || (sel_protocol == GPGME_PROTOCOL_UNKNOWN))
+    hint = _("Although you selected keys for all recipients "
+             "a common encryption protocol can't be used. "
+             "Please decide on one protocol by clicking one "
+             "of the above radio buttons.");
+  else if (sel_protocol == GPGME_PROTOCOL_OpenPGP)
+    {
+      hint = _("Using OpenPGP for encryption.");
+      okay = 1;
+    }
+  else if (sel_protocol == GPGME_PROTOCOL_CMS)
+    {
+      hint = _("Using S/MIME for encryption.");
+      okay = 1;
+    }
+  else
+    hint = _("No recipients - encryption is not possible");
+
+  gtk_label_set_text (GTK_LABEL (dialog->statushint), hint);
+  gtk_label_set_line_wrap (GTK_LABEL (dialog->statushint), TRUE);
+
+  dialog->usable = okay;
+  dialog->selected_protocol = sel_protocol;
+  gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, 
+				     okay);
+}
+
+
+/* Add KEY to the keyarray of KEYINFO.  Owvership of KEY is moved to
+   KEYARRAY.  Returns the number of keys in KEYINFO.  */
+static unsigned int
+append_key_to_keyinfo (struct keyinfo_s *keyinfo, gpgme_key_t key)
+{
+  unsigned int nkeys;
+
+  if (!keyinfo->keys)
+    {
+      keyinfo->dimof_keys = 5; /* Space for 4 keys.  */
+      keyinfo->keys = g_new (gpgme_key_t, keyinfo->dimof_keys);
+      keyinfo->keys[0] = NULL;
+    }
+  for (nkeys=0; keyinfo->keys[nkeys]; nkeys++)
+    ;
+  /* Note that we silently skip KEY of NULL becuase we can't store
+     them in the array.  */
+  if (key)
+    {
+      if (nkeys+1 >= keyinfo->dimof_keys)
+        {
+          keyinfo->dimof_keys += 10;
+          keyinfo->keys = g_renew (gpgme_key_t, keyinfo->keys, 
+                                   keyinfo->dimof_keys);
+        }
+      keyinfo->keys[nkeys++] = key;
+      keyinfo->keys[nkeys] = NULL;
+    }
+  return nkeys;
+}
+
+
 /* Parse one recipient, this is the working horse of parse_recipeints. */
 static void
 parse_one_recipient (gpgme_ctx_t ctx, GtkListStore *store, GtkTreeIter *iter,
                      const char *mailbox)
 {
-  gpgme_key_t key, key2;
-  int any_pgp = 0, any_x509 = 0, any_ambiguous = 0, any_unusable = 0;
+  gpgme_key_t key;
+  int any_pgp = 0, any_x509 = 0;
   char *infostr = NULL;
+  struct userdata_s *info;
 
   key = NULL;
+  info = g_malloc0 (sizeof *info);
 
   gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP);
   if (!gpgme_op_keylist_start (ctx, mailbox, 0))
     {
-      if (!gpgme_op_keylist_next (ctx, &key))
+      while (!gpgme_op_keylist_next (ctx, &key))
         {
-          any_pgp++;
-          if (!gpgme_op_keylist_next (ctx, &key2))
+          if (key->revoked || key->disabled || key->expired)
+            gpgme_key_unref (key);
+          else if (append_key_to_keyinfo (&info->pgp, key)
+                   >= TRUNCATE_KEYSEARCH_AT)
             {
-              any_ambiguous++;
-              gpgme_key_unref (key);
-              gpgme_key_unref (key2);
-              key = key2 = NULL;
+              /* Note that the truncation flag is not 100 correct.  In
+                 case the next iteration would not yield a new key we
+                 have not actually truncated the search.  */ 
+              info->pgp.truncated = 1;
+              break;
             }
         }
     }
   gpgme_op_keylist_end (ctx);
-  if (key)
-    {
-      /* fixme: We should put the key into a list.  It would be best
-         to use a hidden columnin the store.  Need to figure out how
-         to do that.  */
-      if (key->revoked || key->disabled || key->expired)
-        any_unusable++;
-      else if (!infostr)
-        infostr = gpa_gpgme_key_get_userid (key->uids);
-    }
-  gpgme_key_unref (key);
-  key = NULL;
-
   gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
   if (!gpgme_op_keylist_start (ctx, mailbox, 0))
     {
-      if (!gpgme_op_keylist_next (ctx, &key))
+      while (!gpgme_op_keylist_next (ctx, &key))
         {
-          any_x509++;
-          if (!gpgme_op_keylist_next (ctx, &key2))
+          if (key->revoked || key->disabled || key->expired)
+            gpgme_key_unref (key);
+          else if (append_key_to_keyinfo (&info->x509,key) 
+                   >= TRUNCATE_KEYSEARCH_AT)
             {
-              any_ambiguous++;
-              gpgme_key_unref (key);
-              gpgme_key_unref (key2);
-              key = key2 = NULL;
+              info->x509.truncated = 1;
+              break;
             }
         }
     }
   gpgme_op_keylist_end (ctx);
-  if (key)
+
+
+  if (info->pgp.keys && info->pgp.keys[0])
+    any_pgp = 1;
+  if (info->x509.keys && info->x509.keys[0])
+    any_x509 = 1;
+
+  if (any_pgp && any_x509 && info->pgp.keys[1] && info->x509.keys[1])
+    infostr = g_strdup (_("[ambiguous keys - click to select]"));
+  else if (any_pgp && info->pgp.keys[1])
+    infostr = g_strdup (_("[ambiguous PGP key - click to select]"));
+  else if (any_x509 && info->x509.keys[1])
+    infostr = g_strdup (_("[ambiguous X.509 key - click to select]"));
+  else if (any_pgp && !info->pgp.keys[1])
     {
-      /* fixme: We should put the key into a list.  It would be best
-         to use a hidden column in the store.  Need to figure out how
-         to do that.  */
-      if (key->revoked || key->disabled || key->expired)
-        any_unusable++;
-      else if (!infostr)
-        infostr = gpa_gpgme_key_get_userid (key->uids);
+      /* Exactly one key found.  */
+      key = info->pgp.keys[0];
+      infostr = gpa_gpgme_key_get_userid (key->uids);
     }
-  gpgme_key_unref (key);
+  else if (any_x509 && !info->x509.keys[1])
+    {
+      key = info->x509.keys[0];
+      infostr = gpa_gpgme_key_get_userid (key->uids);
+    }
+  else
+    infostr = g_strdup (_("[click to select]"));
   
-  if (!infostr)
-    infostr = g_strdup (any_ambiguous? 
-                        _("[ambiguous key - click to select]"):
-                        any_unusable?
-                        _("[unusable key - click to select another one]"):
-                        _("[click to select]"));
-  
   g_print ("   pgp=%d x509=%d infostr=`%s'\n", any_pgp, any_x509, infostr);
   gtk_list_store_set (store, iter,
                       RECPLIST_HAS_PGP, any_pgp,
                       RECPLIST_HAS_X509, any_x509,
                       RECPLIST_KEYID, infostr,
+                      RECPLIST_USERDATA, info,
                       -1);
   g_free (infostr);
 }
@@ -246,9 +466,10 @@
   if (err)
     gpa_gpgme_error (err);
 
-
+  
   model = GTK_TREE_MODEL (store);
   /* Walk through the list, reading each row. */
+  
   if (gtk_tree_model_get_iter_first (model, &iter))
     do
       {
@@ -259,7 +480,7 @@
                             -1);
         
         /* Do something with the data */
-        g_print ("mailbox `%s'\n", mailbox);
+        g_print ("parsing mailbox `%s'\n", mailbox);
         parse_one_recipient (ctx, store, &iter, mailbox);
         g_free (mailbox);
       }
@@ -268,15 +489,13 @@
   gpgme_release (ctx);
 }
 
-
-
 static void
 recplist_row_activated_cb (GtkTreeView       *tree_view,
                            GtkTreePath       *path,
                            GtkTreeViewColumn *column,
                            gpointer           user_data)    
 {
-  RecipientDlg *dialog = user_data;
+  /*RecipientDlg *dialog = user_data;*/
   GtkTreeIter iter;
   GtkTreeModel *model;
   char *mailbox;
@@ -289,12 +508,57 @@
   gtk_tree_model_get (model, &iter, 
                       RECPLIST_MAILBOX, &mailbox,
                       -1);
-        
   g_print ("  mailbox is `%s'\n", mailbox);
   g_free (mailbox);
 }
 
 
+static void
+recplist_row_changed_cb (GtkTreeModel *model, GtkTreePath *path,
+                         GtkTreeIter *changediter, gpointer user_data)
+{
+  RecipientDlg *dialog = user_data;
+
+  g_print ("row changed signal received\n");
+  g_return_if_fail (dialog);
+  update_statushint (dialog);
+}
+
+
+static void
+rbutton_toggled_cb (GtkToggleButton *button, gpointer user_data)
+{
+  RecipientDlg *dialog = user_data;
+  GtkTreeViewColumn *column;
+  int pgp = FALSE;
+  int x509 = FALSE;
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radio_pgp)))
+    {
+      pgp = TRUE;
+    }
+  else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
+                                         (dialog->radio_x509)))
+    {
+      x509 = TRUE;
+    }
+  else
+    {
+      pgp = TRUE;
+      x509 = TRUE;
+    }
+
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->clist_keys),
+                                     RECPLIST_HAS_PGP);
+  gtk_tree_view_column_set_visible (column, pgp);
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->clist_keys),
+                                     RECPLIST_HAS_X509);
+  gtk_tree_view_column_set_visible (column, x509);
+  update_statushint (dialog);
+}
+
+
+
 
 /************************************************************ 
  ******************   Object Management  ********************
@@ -348,7 +612,7 @@
 static void
 recipient_dlg_init (RecipientDlg *dialog)
 {
-
+  dialog->disable_update_statushint = 0;
 }
 
 
@@ -358,8 +622,10 @@
 {
   GObject *object;
   RecipientDlg *dialog;
-  GtkAccelGroup *accelGroup;
-  GtkWidget *vboxEncrypt;
+  GtkAccelGroup *accel_group;
+  GtkWidget *vbox;
+  GtkWidget *hbox;
+  GtkWidget *widget;
   GtkWidget *labelKeys;
   GtkWidget *scrollerKeys;
   GtkWidget *clistKeys;
@@ -380,34 +646,73 @@
 				     FALSE);
   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
 
-  accelGroup = gtk_accel_group_new ();
-  gtk_window_add_accel_group (GTK_WINDOW (dialog), accelGroup);
+  accel_group = gtk_accel_group_new ();
+  gtk_window_add_accel_group (GTK_WINDOW (dialog), accel_group);
 
-  vboxEncrypt = GTK_DIALOG (dialog)->vbox;
-  gtk_container_set_border_width (GTK_CONTAINER (vboxEncrypt), 5);
+  vbox = GTK_DIALOG (dialog)->vbox;
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
 
   labelKeys = gtk_label_new ("");
   gtk_misc_set_alignment (GTK_MISC (labelKeys), 0.0, 0.5);
-  gtk_box_pack_start (GTK_BOX (vboxEncrypt), labelKeys, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), labelKeys, FALSE, FALSE, 0);
 
   scrollerKeys = gtk_scrolled_window_new (NULL, NULL);
   gtk_scrolled_window_set_policy  (GTK_SCROLLED_WINDOW (scrollerKeys),
 				   GTK_POLICY_AUTOMATIC,
 				   GTK_POLICY_AUTOMATIC);
-  gtk_box_pack_start (GTK_BOX (vboxEncrypt), scrollerKeys, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), scrollerKeys, TRUE, TRUE, 0);
   gtk_widget_set_size_request (scrollerKeys, 400, 200);
 
   clistKeys = recplist_window_new ();
-  g_signal_connect (G_OBJECT (GTK_TREE_VIEW (clistKeys)),
-                    "row-activated",
-                    G_CALLBACK (recplist_row_activated_cb), dialog);
   dialog->clist_keys = clistKeys;
   gtk_container_add (GTK_CONTAINER (scrollerKeys), clistKeys);
-  gpa_connect_by_accelerator (GTK_LABEL (labelKeys), clistKeys, accelGroup,
+  gpa_connect_by_accelerator (GTK_LABEL (labelKeys), clistKeys, accel_group,
 			      _("_Recipient list"));
 
+  dialog->radio_pgp  = gtk_radio_button_new_with_mnemonic
+    (NULL, _("Use _PGP"));
+  dialog->radio_x509 = gtk_radio_button_new_with_mnemonic 
+    (gtk_radio_button_get_group (GTK_RADIO_BUTTON (dialog->radio_pgp)),
+     _("Use _X.509"));
+  dialog->radio_auto = gtk_radio_button_new_with_mnemonic 
+    (gtk_radio_button_get_group (GTK_RADIO_BUTTON (dialog->radio_pgp)),
+     _("_Auto selection"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->radio_auto), TRUE);
+
+  hbox = gtk_hbox_new (FALSE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_pgp,  FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_x509, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_auto, FALSE, FALSE, 0);
+
+  widget = gtk_hseparator_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+  dialog->statushint = gtk_label_new (NULL);
+  gtk_box_pack_start (GTK_BOX (vbox), dialog->statushint, FALSE, FALSE, 0);
+
+
+  g_signal_connect (G_OBJECT (GTK_TREE_VIEW (dialog->clist_keys)),
+                    "row-activated",
+                    G_CALLBACK (recplist_row_activated_cb), dialog);
  
+  g_signal_connect (G_OBJECT (gtk_tree_view_get_model 
+                              (GTK_TREE_VIEW (dialog->clist_keys))),
+                    "row-changed",
+                    G_CALLBACK (recplist_row_changed_cb), dialog);
 
+  g_signal_connect (G_OBJECT (dialog->radio_pgp),
+                    "toggled",
+                    G_CALLBACK (rbutton_toggled_cb), dialog);
+  g_signal_connect (G_OBJECT (dialog->radio_x509),
+                    "toggled",
+                    G_CALLBACK (rbutton_toggled_cb), dialog);
+  g_signal_connect (G_OBJECT (dialog->radio_auto),
+                    "toggled",
+                    G_CALLBACK (rbutton_toggled_cb), dialog);
+
+
   return object;
 }
 
@@ -470,7 +775,7 @@
  **********************  Public API  ************************
  ************************************************************/
 
-GtkWidget *
+RecipientDlg *
 recipient_dlg_new (GtkWidget *parent)
 {
   RecipientDlg *dialog;
@@ -479,23 +784,40 @@
 			 "window", parent,
 			 NULL);
 
-  return GTK_WIDGET(dialog);
+  return dialog;
 }
 
 
-/* Put the recipients into the list.  */
+/* Put RECIPIENTS into the list.  PROTOCOL select the defualt protocol. */
 void 
-recipient_dlg_set_recipients (GtkWidget *object, GSList *recipients)
+recipient_dlg_set_recipients (RecipientDlg *dialog, GSList *recipients,
+                              gpgme_protocol_t protocol)
 {
-  RecipientDlg *dialog;
   GtkListStore *store;
   GSList *recp;
   GtkTreeIter iter;
   const char *name;
+  GtkWidget *widget;
 
-  g_return_if_fail (object);
-  dialog = RECIPIENT_DLG (object);
+  g_return_if_fail (dialog);
 
+  dialog->disable_update_statushint++;
+
+  if (protocol == GPGME_PROTOCOL_OpenPGP)
+    widget = dialog->radio_pgp;
+  else if (protocol == GPGME_PROTOCOL_CMS)
+    widget = dialog->radio_x509;
+  else
+    widget = dialog->radio_auto;
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+
+  if (widget != dialog->radio_auto)
+    {
+      gtk_widget_set_sensitive (GTK_WIDGET (dialog->radio_pgp), FALSE);  
+      gtk_widget_set_sensitive (GTK_WIDGET (dialog->radio_x509), FALSE);  
+      gtk_widget_set_sensitive (GTK_WIDGET (dialog->radio_auto), FALSE);  
+    }
+  
   store = GTK_LIST_STORE (gtk_tree_view_get_model
                           (GTK_TREE_VIEW (dialog->clist_keys)));
 
@@ -511,11 +833,89 @@
                               RECPLIST_HAS_PGP, FALSE,
                               RECPLIST_HAS_X509, FALSE,
                               RECPLIST_KEYID,  "",
+                              RECPLIST_USERDATA, NULL,
                               -1);
         }    
     }
+
   parse_recipients (store);
+  dialog->disable_update_statushint--;
+  update_statushint (dialog);
 }
 
 
+/* Return the selected keys as well as the selected protocol.  */
+gpgme_key_t *
+recipient_dlg_get_keys (RecipientDlg *dialog, gpgme_protocol_t *r_protocol)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  size_t idx, nkeys;
+  gpgme_key_t key, *keyarray;
+  gpgme_protocol_t protocol;
+  
+  g_return_val_if_fail (dialog, NULL);
 
+  if (!dialog->usable)
+    return NULL;  /* No valid keys available.  */
+  protocol = dialog->selected_protocol;
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->clist_keys));
+
+  /* Count the number of possible keys.  */
+  nkeys = 0;
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    {
+      do
+        nkeys++;
+      while (gtk_tree_model_iter_next (model, &iter));
+    }
+  keyarray = g_new (gpgme_key_t, nkeys+1); 
+  idx = 0;
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    {
+      do
+        {
+          char *mailbox;
+          struct userdata_s *info;
+
+          if (idx >= nkeys)
+            {
+              g_debug ("key list grew unexpectedly\n");
+              break;
+            }
+          
+          gtk_tree_model_get (model, &iter, 
+                              RECPLIST_MAILBOX, &mailbox,
+                              RECPLIST_USERDATA, &info,
+                              -1);
+          if (info && !info->ignore_recipient)
+            {
+              if (protocol == GPGME_PROTOCOL_OpenPGP && info->pgp.keys)
+                key = info->pgp.keys[0];
+              else if (protocol == GPGME_PROTOCOL_CMS && info->x509.keys)
+                key = info->x509.keys[0];
+              else
+                key = NULL;
+              if (key)
+                {
+                  g_print ("returning key for recipient '%s'\n", mailbox);
+                  gpgme_key_ref (key);
+                  keyarray[idx++] = key;
+                }
+            }
+          g_free (mailbox);
+        }
+      while (gtk_tree_model_iter_next (model, &iter));
+    }
+  g_assert (idx < nkeys+1);
+  keyarray[idx] = NULL;
+
+  if (r_protocol)
+    *r_protocol = protocol;
+
+  return keyarray;
+}
+
+
+

Modified: trunk/src/recipientdlg.h
===================================================================
--- trunk/src/recipientdlg.h	2008-03-04 16:46:03 UTC (rev 834)
+++ trunk/src/recipientdlg.h	2008-03-05 10:12:57 UTC (rev 835)
@@ -59,10 +59,12 @@
  ************ Public API ************
  ************************************/
 
-GtkWidget *recipient_dlg_new (GtkWidget *parent);
-void recipient_dlg_set_recipients (GtkWidget *list, GSList *recipients);
+RecipientDlg *recipient_dlg_new (GtkWidget *parent);
+void recipient_dlg_set_recipients (RecipientDlg *dialog, GSList *recipients,
+                                   gpgme_protocol_t protocol);
+gpgme_key_t *recipient_dlg_get_keys (RecipientDlg *dialog,
+                                     gpgme_protocol_t *r_protocol);
 
 
 
-
 #endif /*RECIPIENTDLG_H*/

Modified: trunk/src/server.c
===================================================================
--- trunk/src/server.c	2008-03-04 16:46:03 UTC (rev 834)
+++ trunk/src/server.c	2008-03-05 10:12:57 UTC (rev 835)
@@ -59,6 +59,9 @@
      comes from our command handler.  */
   int is_unfinished;
 
+  /* An GPAOperation object.  */
+  GpaOperation *gpa_op;
+
   /* File descriptors used by the gpgme callbacks.  */
   int input_fd;
   int output_fd;
@@ -70,6 +73,12 @@
   /* List of collected recipients.  */
   GSList *recipients;
 
+  /* Array of keys already prepared for RECIPIENTS.  */
+  gpgme_key_t *recipient_keys;
+
+  /* The protocol as selected by the user.  */
+  gpgme_protocol_t selected_protocol;
+
   /* The current sender address (malloced). */
   char *sender;
 };
@@ -227,21 +236,30 @@
     }
 }
 
-/* Return a deep copy of the recipients list.  */
-static GSList *
-copy_recipients (conn_ctrl_t ctrl)
+static void
+release_keys (gpgme_key_t *keys)
 {
-  GSList *recp, *newlist;
-  
-  newlist= NULL;
-  for (recp = ctrl->recipients; recp; recp = g_slist_next (recp))
-    newlist = g_slist_append (newlist, xstrdup (recp->data));
+  if (keys)
+    {
+      int idx;
+      
+      for (idx=0; keys[idx]; idx++)
+        gpgme_key_unref (keys[idx]);
+      g_free (keys);
+    }
+}
 
-  return newlist;
+
+/* Reset already prepared keys.  */
+static void
+reset_prepared_keys (conn_ctrl_t ctrl)
+{
+  release_keys (ctrl->recipient_keys);
+  ctrl->recipient_keys = NULL;
+  ctrl->selected_protocol = GPGME_PROTOCOL_UNKNOWN;
 }
 
 
-
 
 /*  RECIPIENT <recipient>
 
@@ -256,6 +274,8 @@
   conn_ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err = 0;
 
+  reset_prepared_keys (ctrl);
+
   if (*line)
     ctrl->recipients = g_slist_append (ctrl->recipients, xstrdup (line));
 
@@ -320,6 +340,13 @@
       goto leave;
     }
 
+  if (protocol != ctrl->selected_protocol)
+    {
+      if (ctrl->selected_protocol != GPGME_PROTOCOL_UNKNOWN)
+        g_debug ("note: protocol does not macth the one from PREP_ENCRYPT");
+      reset_prepared_keys (ctrl);
+    }
+
   line = skip_options (line);
   if (*line)
     {
@@ -378,7 +405,10 @@
 
   ctrl->cont_cmd = cont_encrypt;
   op = gpa_stream_encrypt_operation_new (NULL, input_data, output_data,
-                                         copy_recipients (ctrl), 0, ctx);
+                                         ctrl->recipients, 
+                                         ctrl->recipient_keys,
+                                         protocol,
+                                         0, ctx);
   input_data = output_data = NULL;
   g_signal_connect (G_OBJECT (op), "completed",
                     G_CALLBACK (g_object_unref), NULL);
@@ -405,6 +435,36 @@
 
 
 
+/* Continuation for cmd_prep_encrypt.  */
+static void
+cont_prep_encrypt (assuan_context_t ctx, gpg_error_t err)
+{
+  conn_ctrl_t ctrl = assuan_get_pointer (ctx);
+
+  g_debug ("cont_prep_encrypt called with with ERR=%s <%s>",
+           gpg_strerror (err), gpg_strsource (err));
+
+  if (!err)
+    {
+      release_keys (ctrl->recipient_keys);
+      ctrl->recipient_keys = gpa_stream_encrypt_operation_get_keys
+        (GPA_STREAM_ENCRYPT_OPERATION (ctrl->gpa_op),
+         &ctrl->selected_protocol);
+
+      if (ctrl->recipient_keys)
+        g_print ("received some keys\n");
+      else
+        g_print ("received no keys\n");
+    }
+
+  if (ctrl->gpa_op)
+    {
+      g_object_unref (ctrl->gpa_op);
+      ctrl->gpa_op = NULL;
+    }
+  assuan_process_done (ctx, err);
+}
+
 /* PREP_ENCRYPT [--protocol=OpenPGP|CMS]
 
    Dummy encryption command used to check whether the given recipients
@@ -414,11 +474,11 @@
 {
   conn_ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err;
-  gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
+  gpgme_protocol_t protocol = GPGME_PROTOCOL_UNKNOWN;
   GpaStreamEncryptOperation *op;
 
   if (has_option (line, "--protocol=OpenPGP"))
-    ; /* This is the default.  */
+    protocol = GPGME_PROTOCOL_OpenPGP;
   else if (has_option (line, "--protocol=CMS"))
     protocol = GPGME_PROTOCOL_CMS;
   else if (has_option_name (line, "--protocol"))
@@ -434,11 +494,27 @@
       goto leave;
     }
 
-  ctrl->cont_cmd = NULL;
+  reset_prepared_keys (ctrl);
+
+  if (ctrl->gpa_op)
+    {
+      g_debug ("Oops: there is still an GPA_OP active\n");
+      g_object_unref (ctrl->gpa_op);
+      ctrl->gpa_op = NULL;
+    }
+  ctrl->cont_cmd = cont_prep_encrypt;
   op = gpa_stream_encrypt_operation_new (NULL, NULL, NULL,
-                                         copy_recipients (ctrl), 0, ctx);
+                                         ctrl->recipients,
+                                         ctrl->recipient_keys,
+                                         protocol,
+                                         0, ctx);
+  /* Store that instance for later use but also install a signal
+     handler to unref it.  */
+  g_object_ref (op);
+  ctrl->gpa_op = GPA_OPERATION (op);
   g_signal_connect (G_OBJECT (op), "completed",
                     G_CALLBACK (g_object_unref), NULL);
+
   return not_finished (ctrl);
 
  leave:
@@ -670,6 +746,7 @@
 {
   conn_ctrl_t ctrl = assuan_get_pointer (ctx);
 
+  reset_prepared_keys (ctrl);
   release_recipients (ctrl);
   xfree (ctrl->sender);
   ctrl->sender = NULL;
@@ -685,6 +762,11 @@
     }
   assuan_close_input_fd (ctx);
   assuan_close_output_fd (ctx);
+  if (ctrl->gpa_op)
+    {
+      g_object_unref (ctrl->gpa_op);
+      ctrl->gpa_op = NULL;
+    }
 }
 
 
@@ -792,7 +874,7 @@
       g_debug ("no continuation defined; using default");
       assuan_process_done (ctx, err);
     }
-  else if (!ctrl->client_died)
+  else if (ctrl->client_died)
     {
       g_debug ("not running continuation as client has disconnected");
       connection_finish (ctx);



More information about the Gpa-commits mailing list