2007-01-17 [colin] 2.7.1cvs12
[claws.git] / src / plugins / pgpcore / select-keys.c
index be80dca83241c821f4c6f6e4b904a0596df38609..4ba1f3d205a11805d16c9961406a74e1db436516 100644 (file)
@@ -1,5 +1,5 @@
 /* select-keys.c - GTK+ based key selection
- *      Copyright (C) 2001 Werner Koch (dd9jn)
+ *      Copyright (C) 2001-2007 Werner Koch (dd9jn) and the Claws Mail team
  *
  * This program is free software; you can redistribute it and/or modify        
  * it under the terms of the GNU General Public License as published by
@@ -13,7 +13,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -70,15 +70,16 @@ struct select_keys_s {
     unsigned int num_keys;
     gpgme_key_t *kset;
     gpgme_ctx_t select_ctx;
-
+    gpgme_protocol_t proto;
     GtkSortType sort_type;
     enum col_titles sort_column;
     SelectionResult result;
 };
 
 
-static void set_row (GtkCList *clist, gpgme_key_t key);
-static void fill_clist (struct select_keys_s *sk, const char *pattern);
+static void set_row (GtkCList *clist, gpgme_key_t key, gpgme_protocol_t proto);
+static gpgme_key_t fill_clist (struct select_keys_s *sk, const char *pattern,
+                       gpgme_protocol_t proto);
 static void create_dialog (struct select_keys_s *sk);
 static void open_dialog (struct select_keys_s *sk);
 static void close_dialog (struct select_keys_s *sk);
@@ -94,7 +95,7 @@ static void sort_keys (struct select_keys_s *sk, enum col_titles column);
 static void sort_keys_name (GtkWidget *widget, gpointer data);
 static void sort_keys_email (GtkWidget *widget, gpointer data);
 
-static gboolean use_untrusted (gpgme_key_t);
+static gboolean use_untrusted (gpgme_key_t, gpgme_protocol_t proto);
 
 static void
 update_progress (struct select_keys_s *sk, int running, const char *pattern)
@@ -103,7 +104,7 @@ update_progress (struct select_keys_s *sk, int running, const char *pattern)
     char *buf;
 
     if (!running)
-        buf = g_strdup_printf (_("Please select key for '%s'"),
+        buf = g_strdup_printf (_("No exact match for '%s'; please select the key."),
                                pattern);
     else 
         buf = g_strdup_printf (_("Collecting info for '%s' ... %c"),
@@ -125,20 +126,38 @@ update_progress (struct select_keys_s *sk, int running, const char *pattern)
  * Return value: NULL on error or a list of list of recipients.
  **/
 gpgme_key_t *
-gpgmegtk_recipient_selection (GSList *recp_names, SelectionResult *result)
+gpgmegtk_recipient_selection (GSList *recp_names, SelectionResult *result,
+                               gpgme_protocol_t proto)
 {
     struct select_keys_s sk;
-
+    gpgme_key_t key = NULL;
     memset (&sk, 0, sizeof sk);
 
     open_dialog (&sk);
 
     do {
         sk.pattern = recp_names? recp_names->data:NULL;
+       sk.proto = proto;
         gtk_clist_clear (sk.clist);
-        fill_clist (&sk, sk.pattern);
+        key = fill_clist (&sk, sk.pattern, proto);
         update_progress (&sk, 0, sk.pattern);
-        gtk_main ();
+       if (!key) {
+               gtk_widget_show_all (sk.window);
+               gtk_main ();
+       } else {
+               gtk_widget_hide (sk.window);
+               sk.kset = g_realloc(sk.kset,
+                       sizeof(gpgme_key_t) * (sk.num_keys + 1));
+               gpgme_key_ref(key);
+               sk.kset[sk.num_keys] = key;
+               sk.num_keys++;
+               sk.okay = 1;
+               sk.result = KEY_SELECTION_OK;
+               gpgme_release (sk.select_ctx);
+               sk.select_ctx = NULL;
+               debug_print("used %s\n", key->uids->email);
+       }
+       key = NULL;
         if (recp_names)
             recp_names = recp_names->next;
     } while (sk.okay && recp_names);
@@ -165,14 +184,14 @@ destroy_key (gpointer data)
 }
 
 static void
-set_row (GtkCList *clist, gpgme_key_t key)
+set_row (GtkCList *clist, gpgme_key_t key, gpgme_protocol_t proto)
 {
     const char *s;
     const char *text[N_COL_TITLES];
     char *algo_buf;
     int row;
-    gssize by_read = 0, by_written = 0;
-    gchar *ret_str;
+    gsize by_read = 0, by_written = 0;
+    gchar *ret_str = NULL;
 
     /* first check whether the key is capable of encryption which is not
      * the case for revoked, expired or sign-only keys */
@@ -188,15 +207,38 @@ set_row (GtkCList *clist, gpgme_key_t key)
         s += 8; /* show only the short keyID */
     text[COL_KEYID] = s;
 
+
     s = key->uids->name;
-    ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
+    if (!s || !*s)
+        s = key->uids->uid;
+    if (proto == GPGME_PROTOCOL_CMS) {
+       if (strstr(s, ",CN="))
+               s = strstr(s, ",CN=")+4;
+       else if (strstr(s, "CN="))
+               s = strstr(s, "CN=")+3;
+    } 
+    
+    ret_str = NULL;
+    if (!g_utf8_validate(s, -1, NULL))
+           ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
     if (ret_str && by_written) {
         s = ret_str;
     }
     text[COL_NAME] = s;
 
-    s = key->uids->email;
-    ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
+    if (proto == GPGME_PROTOCOL_CMS && (!key->uids->email || !*key->uids->email)) {
+       gpgme_user_id_t uid = key->uids->next;
+       if (uid)
+               s = uid->email;
+       else
+               s = key->uids->email;
+    } else {
+        s = key->uids->email;
+    }
+    
+    ret_str = NULL;
+    if (!g_utf8_validate(s, -1, NULL))
+           ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
     if (ret_str && by_written) {
         s = ret_str;
     }
@@ -232,25 +274,28 @@ set_row (GtkCList *clist, gpgme_key_t key)
     gtk_clist_set_row_data_full (clist, row, key, destroy_key);
 }
 
-static void 
-fill_clist (struct select_keys_s *sk, const char *pattern)
+static gpgme_key_t 
+fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t proto)
 {
     GtkCList *clist;
     gpgme_ctx_t ctx;
     gpgme_error_t err;
     gpgme_key_t key;
     int running=0;
-
-    g_return_if_fail (sk);
+    int num_results = 0;
+    gboolean exact_match = FALSE;
+    gpgme_key_t last_key = NULL;
+    g_return_val_if_fail (sk, NULL);
     clist = sk->clist;
-    g_return_if_fail (clist);
+    g_return_val_if_fail (clist, NULL);
 
-    debug_print ("select_keys:fill_clist:  pattern '%s'\n", pattern);
+    debug_print ("select_keys:fill_clist:  pattern '%s' proto %d\n", pattern, proto);
 
     /*gtk_clist_freeze (select_keys.clist);*/
     err = gpgme_new (&ctx);
     g_assert (!err);
 
+    gpgme_set_protocol(ctx, proto);
     sk->select_ctx = ctx;
 
     update_progress (sk, ++running, pattern);
@@ -263,25 +308,52 @@ fill_clist (struct select_keys_s *sk, const char *pattern)
                      pattern, gpgme_strerror (err));
         sk->select_ctx = NULL;
         gpgme_release(ctx);
-        return;
+        return NULL;
     }
     update_progress (sk, ++running, pattern);
     while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
+       gpgme_user_id_t uid = key->uids;
         debug_print ("%% %s:%d:  insert\n", __FILE__ ,__LINE__ );
-        set_row (clist, key ); key = NULL;
+        set_row (clist, key, proto ); 
+       for (; uid; uid = uid->next) {
+               gchar *raw_mail = NULL;
+               if (!uid->email)
+                       continue;
+               raw_mail = g_strdup(uid->email);
+               extract_address(raw_mail);
+               if (!strcmp(pattern, raw_mail)) {
+                       exact_match = TRUE;
+                       g_free(raw_mail);
+                       break;
+               }
+               g_free(raw_mail);
+       }
+       num_results++;
+       last_key = key;
+       key = NULL;
         update_progress (sk, ++running, pattern);
         while (gtk_events_pending ())
             gtk_main_iteration ();
     }
+    if (exact_match == TRUE && num_results == 1) {
+           if (last_key->uids->validity < GPGME_VALIDITY_FULL && 
+               !use_untrusted(last_key, proto))
+                   exact_match = FALSE;
+    }
+
     debug_print ("%% %s:%d:  ready\n", __FILE__ ,__LINE__ );
     if (gpgme_err_code(err) != GPG_ERR_EOF) {
         debug_print ("** gpgme_op_keylist_next failed: %s",
                      gpgme_strerror (err));
         gpgme_op_keylist_end(ctx);
     }
-    sk->select_ctx = NULL;
-    gpgme_release (ctx);
+    if (!exact_match || num_results != 1) {
+           sk->select_ctx = NULL;
+           gpgme_release (ctx);
+    }
     /*gtk_clist_thaw (select_keys.clist);*/
+    return (exact_match == TRUE && num_results == 1 ? last_key:NULL);
 }
 
 
@@ -353,13 +425,13 @@ create_dialog (struct select_keys_s *sk)
 
     gtkut_stock_button_set_create (&bbox, 
                                    &select_btn, _("Select"),
-                                   &cancel_btn, GTK_STOCK_CANCEL,
+                                  &other_btn, _("Other"),
                                   &dont_encrypt_btn, _("Don't encrypt"));
     
-    other_btn = gtk_button_new_from_stock(_("Other"));
-    GTK_WIDGET_SET_FLAGS(other_btn, GTK_CAN_DEFAULT);
-    gtk_box_pack_start(GTK_BOX(bbox), other_btn, TRUE, TRUE, 0);
-    gtk_widget_show(other_btn);
+    cancel_btn = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+    GTK_WIDGET_SET_FLAGS(cancel_btn, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(bbox), cancel_btn, TRUE, TRUE, 0);
+    gtk_widget_show(cancel_btn);
     gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
     gtk_widget_grab_default (select_btn);
 
@@ -375,8 +447,6 @@ create_dialog (struct select_keys_s *sk)
     vbox2 = gtk_vbox_new (FALSE, 4);
     gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
 
-    gtk_widget_show_all (window);
-
     sk->window = window;
     sk->toplabel = GTK_LABEL (label);
     sk->clist  = GTK_CLIST (clist);
@@ -392,7 +462,6 @@ open_dialog (struct select_keys_s *sk)
     sk->okay = 0;
     sk->sort_column = N_COL_TITLES; /* use an invalid value */
     sk->sort_type = GTK_SORT_ASCENDING;
-    gtk_widget_show (sk->window);
 }
 
 
@@ -448,7 +517,7 @@ select_btn_cb (GtkWidget *widget, gpointer data)
     key = gtk_clist_get_row_data(sk->clist, row);
     if (key) {
         if ( key->uids->validity < GPGME_VALIDITY_FULL ) {
-            use_key = use_untrusted(key);
+            use_key = use_untrusted(key, sk->proto);
             if (!use_key) {
                 debug_print ("** Key untrusted, will not encrypt");
                 return;
@@ -504,25 +573,34 @@ other_btn_cb (GtkWidget *widget, gpointer data)
                          NULL );
     if (!uid)
         return;
-    fill_clist (sk, uid);
+    if (fill_clist (sk, uid, sk->proto) != NULL) {
+           gpgme_release(sk->select_ctx);
+           sk->select_ctx = NULL;
+    }
     update_progress (sk, 0, sk->pattern);
     g_free (uid);
 }
 
 
 static gboolean
-use_untrusted (gpgme_key_t key)
+use_untrusted (gpgme_key_t key, gpgme_protocol_t proto)
 {
     AlertValue aval;
+    gchar *buf = NULL;
+    
+    if (proto != GPGME_PROTOCOL_OpenPGP)
+       return TRUE;
 
-    aval = alertpanel
-           (_("Trust key"),
-            _("The selected key is not fully trusted.\n"
+    buf = g_strdup_printf(_("The key of '%s' is not fully trusted.\n"
               "If you choose to encrypt the message with this key you don't\n"
               "know for sure that it will go to the person you mean it to.\n"
-              "Do you trust it enough to use it anyway?"),
-            GTK_STOCK_YES, GTK_STOCK_NO, NULL);
-    if (aval == G_ALERTDEFAULT)
+              "Do you trust it enough to use it anyway?"), key->uids->email);
+    aval = alertpanel
+           (_("Trust key"),
+            buf,
+            GTK_STOCK_NO, GTK_STOCK_YES, NULL);
+    g_free(buf);
+    if (aval == G_ALERTALTERNATE)
        return TRUE;
     else
        return FALSE;