526 lines
15 KiB
C
526 lines
15 KiB
C
/* select-keys.c - GTK+ based key selection
|
|
* Copyright (C) 2001 Werner Koch (dd9jn)
|
|
*
|
|
* 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
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#ifdef USE_GPGME
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <glib.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gtk/gtkmain.h>
|
|
#include <gtk/gtkwidget.h>
|
|
#include <gtk/gtkwindow.h>
|
|
#include <gtk/gtkscrolledwindow.h>
|
|
#include <gtk/gtkvbox.h>
|
|
#include <gtk/gtkhbox.h>
|
|
#include <gtk/gtkclist.h>
|
|
#include <gtk/gtklabel.h>
|
|
#include <gtk/gtkentry.h>
|
|
#include <gtk/gtkhbbox.h>
|
|
#include <gtk/gtkbutton.h>
|
|
#include <gtk/gtksignal.h>
|
|
|
|
#include "intl.h"
|
|
#include "select-keys.h"
|
|
#include "utils.h"
|
|
#include "gtkutils.h"
|
|
#include "inputdialog.h"
|
|
|
|
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
|
|
#define DIMof(type,member) DIM(((type *)0)->member)
|
|
|
|
|
|
enum col_titles {
|
|
COL_ALGO,
|
|
COL_KEYID,
|
|
COL_NAME,
|
|
COL_EMAIL,
|
|
COL_VALIDITY,
|
|
|
|
N_COL_TITLES
|
|
};
|
|
|
|
struct select_keys_s {
|
|
int okay;
|
|
GtkWidget *window;
|
|
GtkLabel *toplabel;
|
|
GtkCList *clist;
|
|
const char *pattern;
|
|
GpgmeRecipients rset;
|
|
GpgmeCtx select_ctx;
|
|
|
|
GtkSortType sort_type;
|
|
enum col_titles sort_column;
|
|
|
|
};
|
|
|
|
|
|
static void set_row (GtkCList *clist, GpgmeKey key);
|
|
static void fill_clist (struct select_keys_s *sk, const char *pattern);
|
|
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);
|
|
static gint delete_event_cb (GtkWidget *widget,
|
|
GdkEventAny *event, gpointer data);
|
|
static void key_pressed_cb (GtkWidget *widget,
|
|
GdkEventKey *event, gpointer data);
|
|
static void select_btn_cb (GtkWidget *widget, gpointer data);
|
|
static void cancel_btn_cb (GtkWidget *widget, gpointer data);
|
|
static void other_btn_cb (GtkWidget *widget, gpointer data);
|
|
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 void
|
|
update_progress (struct select_keys_s *sk, int running, const char *pattern)
|
|
{
|
|
static int windmill[] = { '-', '\\', '|', '/' };
|
|
char *buf;
|
|
|
|
if (!running)
|
|
buf = g_strdup_printf (_("Please select key for `%s'"),
|
|
pattern);
|
|
else
|
|
buf = g_strdup_printf (_("Collecting info for `%s' ... %c"),
|
|
pattern,
|
|
windmill[running%DIM(windmill)]);
|
|
gtk_label_set_text (sk->toplabel, buf);
|
|
g_free (buf);
|
|
}
|
|
|
|
|
|
/**
|
|
* select_keys_get_recipients:
|
|
* @recp_names: A list of email addresses
|
|
*
|
|
* Select a list of recipients from a given list of email addresses.
|
|
* This may pop up a window to present the user a choice, it will also
|
|
* check that the recipients key are all valid.
|
|
*
|
|
* Return value: NULL on error or a list of list of recipients.
|
|
**/
|
|
GpgmeRecipients
|
|
gpgmegtk_recipient_selection (GSList *recp_names)
|
|
{
|
|
struct select_keys_s sk;
|
|
GpgmeError err;
|
|
|
|
memset ( &sk, 0, sizeof sk);
|
|
|
|
err = gpgme_recipients_new (&sk.rset);
|
|
if (err) {
|
|
g_message ("** failed to allocate recipients set: %s",
|
|
gpgme_strerror (err));
|
|
return NULL;
|
|
}
|
|
|
|
open_dialog (&sk);
|
|
|
|
do {
|
|
sk.pattern = recp_names? recp_names->data:NULL;
|
|
gtk_clist_clear (sk.clist);
|
|
fill_clist (&sk, sk.pattern);
|
|
update_progress (&sk, 0, sk.pattern);
|
|
gtk_main ();
|
|
if (recp_names)
|
|
recp_names = recp_names->next;
|
|
} while (sk.okay && recp_names);
|
|
|
|
close_dialog (&sk);
|
|
|
|
if (!sk.okay) {
|
|
gpgme_recipients_release (sk.rset);
|
|
sk.rset = NULL;
|
|
}
|
|
return sk.rset;
|
|
}
|
|
|
|
static void
|
|
destroy_key (gpointer data)
|
|
{
|
|
GpgmeKey key = data;
|
|
gpgme_key_release (key);
|
|
}
|
|
|
|
static void
|
|
set_row (GtkCList *clist, GpgmeKey key)
|
|
{
|
|
const char *s;
|
|
const char *text[N_COL_TITLES];
|
|
char *algo_buf;
|
|
int row;
|
|
|
|
/* first check whether the key is capable of encryption which is not
|
|
* the case for revoked, expired or sign-only keys */
|
|
if ( !gpgme_key_get_ulong_attr (key, GPGME_ATTR_CAN_ENCRYPT, NULL, 0 ) )
|
|
return;
|
|
|
|
algo_buf = g_strdup_printf ("%lu/%s",
|
|
gpgme_key_get_ulong_attr (key, GPGME_ATTR_LEN, NULL, 0 ),
|
|
gpgme_key_get_string_attr (key, GPGME_ATTR_ALGO, NULL, 0 ) );
|
|
text[COL_ALGO] = algo_buf;
|
|
|
|
s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID, NULL, 0);
|
|
if (strlen (s) == 16)
|
|
s += 8; /* show only the short keyID */
|
|
text[COL_KEYID] = s;
|
|
|
|
s = gpgme_key_get_string_attr (key, GPGME_ATTR_NAME, NULL, 0);
|
|
text[COL_NAME] = s;
|
|
|
|
s = gpgme_key_get_string_attr (key, GPGME_ATTR_EMAIL, NULL, 0);
|
|
text[COL_EMAIL] = s;
|
|
|
|
s = gpgme_key_get_string_attr (key, GPGME_ATTR_VALIDITY, NULL, 0);
|
|
text[COL_VALIDITY] = s;
|
|
|
|
row = gtk_clist_append (clist, (gchar**)text);
|
|
g_free (algo_buf);
|
|
|
|
gtk_clist_set_row_data_full (clist, row, key, destroy_key);
|
|
}
|
|
|
|
|
|
static void
|
|
fill_clist (struct select_keys_s *sk, const char *pattern)
|
|
{
|
|
GtkCList *clist;
|
|
GpgmeCtx ctx;
|
|
GpgmeError err;
|
|
GpgmeKey key;
|
|
int running=0;
|
|
|
|
g_return_if_fail (sk);
|
|
clist = sk->clist;
|
|
g_return_if_fail (clist);
|
|
|
|
debug_print ("select_keys:fill_clist: pattern `%s'\n", pattern);
|
|
|
|
/*gtk_clist_freeze (select_keys.clist);*/
|
|
err = gpgme_new (&ctx);
|
|
g_assert (!err);
|
|
|
|
sk->select_ctx = ctx;
|
|
|
|
update_progress (sk, ++running, pattern);
|
|
while (gtk_events_pending ())
|
|
gtk_main_iteration ();
|
|
|
|
err = gpgme_op_keylist_start (ctx, pattern, 0);
|
|
if (err) {
|
|
g_message ("** gpgme_op_keylist_start(%s) failed: %s",
|
|
pattern, gpgme_strerror (err));
|
|
sk->select_ctx = NULL;
|
|
return;
|
|
}
|
|
update_progress (sk, ++running, pattern);
|
|
while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
|
|
debug_print ("%% %s:%d: insert\n", __FILE__ ,__LINE__ );
|
|
set_row (clist, key ); key = NULL;
|
|
update_progress (sk, ++running, pattern);
|
|
while (gtk_events_pending ())
|
|
gtk_main_iteration ();
|
|
}
|
|
debug_print ("%% %s:%d: ready\n", __FILE__ ,__LINE__ );
|
|
if (err != GPGME_EOF)
|
|
g_message ("** gpgme_op_keylist_next failed: %s",
|
|
gpgme_strerror (err));
|
|
sk->select_ctx = NULL;
|
|
gpgme_release (ctx);
|
|
/*gtk_clist_thaw (select_keys.clist);*/
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
create_dialog (struct select_keys_s *sk)
|
|
{
|
|
GtkWidget *window;
|
|
GtkWidget *vbox, *vbox2, *hbox;
|
|
GtkWidget *bbox;
|
|
GtkWidget *scrolledwin;
|
|
GtkWidget *clist;
|
|
GtkWidget *label;
|
|
GtkWidget *other_btn, *select_btn, *cancel_btn;
|
|
const char *titles[N_COL_TITLES];
|
|
|
|
g_assert (!sk->window);
|
|
window = gtk_window_new (GTK_WINDOW_DIALOG);
|
|
gtk_widget_set_usize (window, 500, 320);
|
|
gtk_container_set_border_width (GTK_CONTAINER (window), 8);
|
|
gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
|
|
gtk_window_set_modal (GTK_WINDOW (window), TRUE);
|
|
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
|
|
GTK_SIGNAL_FUNC (delete_event_cb), sk);
|
|
gtk_signal_connect (GTK_OBJECT (window), "key_press_event",
|
|
GTK_SIGNAL_FUNC (key_pressed_cb), sk);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 8);
|
|
gtk_container_add (GTK_CONTAINER (window), vbox);
|
|
|
|
hbox = gtk_hbox_new(FALSE, 4);
|
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
|
|
label = gtk_label_new ( "" );
|
|
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
|
|
|
|
|
|
hbox = gtk_hbox_new (FALSE, 8);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
|
|
|
|
|
|
scrolledwin = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
|
|
GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
|
|
titles[COL_ALGO] = _("Size");
|
|
titles[COL_KEYID] = _("Key ID");
|
|
titles[COL_NAME] = _("Name");
|
|
titles[COL_EMAIL] = _("Address");
|
|
titles[COL_VALIDITY] = _("Val");
|
|
|
|
clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
|
|
gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
|
|
gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO, 40);
|
|
gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID, 60);
|
|
gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME, 100);
|
|
gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL, 100);
|
|
gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY, 20);
|
|
gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
|
|
gtk_signal_connect (GTK_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
|
|
"clicked",
|
|
GTK_SIGNAL_FUNC(sort_keys_name), sk);
|
|
gtk_signal_connect (GTK_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
|
|
"clicked",
|
|
GTK_SIGNAL_FUNC(sort_keys_email), sk);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 8);
|
|
gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
|
|
gtkut_button_set_create (&bbox,
|
|
&other_btn, _("Other"),
|
|
&select_btn, _("Select"),
|
|
&cancel_btn, _("Cancel"));
|
|
gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
|
|
gtk_widget_grab_default (select_btn);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (other_btn), "clicked",
|
|
GTK_SIGNAL_FUNC (other_btn_cb), sk);
|
|
gtk_signal_connect (GTK_OBJECT (select_btn), "clicked",
|
|
GTK_SIGNAL_FUNC (select_btn_cb), sk);
|
|
gtk_signal_connect (GTK_OBJECT(cancel_btn), "clicked",
|
|
GTK_SIGNAL_FUNC (cancel_btn_cb), 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);
|
|
}
|
|
|
|
|
|
static void
|
|
open_dialog (struct select_keys_s *sk)
|
|
{
|
|
if ( !sk->window )
|
|
create_dialog (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);
|
|
}
|
|
|
|
|
|
static void
|
|
close_dialog (struct select_keys_s *sk)
|
|
{
|
|
g_return_if_fail (sk);
|
|
gtk_widget_destroy (sk->window);
|
|
sk->window = NULL;
|
|
}
|
|
|
|
|
|
static gint
|
|
delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
|
|
{
|
|
struct select_keys_s *sk = data;
|
|
|
|
sk->okay = 0;
|
|
gtk_main_quit ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
|
|
{
|
|
struct select_keys_s *sk = data;
|
|
|
|
g_return_if_fail (sk);
|
|
if (event && event->keyval == GDK_Escape) {
|
|
sk->okay = 0;
|
|
gtk_main_quit ();
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
select_btn_cb (GtkWidget *widget, gpointer data)
|
|
{
|
|
struct select_keys_s *sk = data;
|
|
int row;
|
|
GpgmeKey key;
|
|
|
|
g_return_if_fail (sk);
|
|
if (!sk->clist->selection) {
|
|
g_message ("** nothing selected");
|
|
return;
|
|
}
|
|
row = GPOINTER_TO_INT(sk->clist->selection->data);
|
|
key = gtk_clist_get_row_data(sk->clist, row);
|
|
if (key) {
|
|
const char *s = gpgme_key_get_string_attr (key,
|
|
GPGME_ATTR_FPR,
|
|
NULL, 0 );
|
|
if ( gpgme_key_get_ulong_attr (key, GPGME_ATTR_VALIDITY, NULL, 0 )
|
|
< GPGME_VALIDITY_FULL ) {
|
|
g_message ("** FIXME: we are faking the trust calculation");
|
|
}
|
|
if (!gpgme_recipients_add_name_with_validity (sk->rset, s,
|
|
GPGME_VALIDITY_FULL) ) {
|
|
sk->okay = 1;
|
|
gtk_main_quit ();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
cancel_btn_cb (GtkWidget *widget, gpointer data)
|
|
{
|
|
struct select_keys_s *sk = data;
|
|
|
|
g_return_if_fail (sk);
|
|
sk->okay = 0;
|
|
if (sk->select_ctx)
|
|
gpgme_cancel (sk->select_ctx);
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
|
|
static void
|
|
other_btn_cb (GtkWidget *widget, gpointer data)
|
|
{
|
|
struct select_keys_s *sk = data;
|
|
char *uid;
|
|
|
|
g_return_if_fail (sk);
|
|
uid = input_dialog ( _("Add key"),
|
|
_("Enter another user or key ID\n"),
|
|
NULL );
|
|
if (!uid)
|
|
return;
|
|
fill_clist (sk, uid);
|
|
update_progress (sk, 0, sk->pattern);
|
|
g_free (uid);
|
|
}
|
|
|
|
|
|
static gint
|
|
cmp_attr (gconstpointer pa, gconstpointer pb, GpgmeAttr attr)
|
|
{
|
|
GpgmeKey a = ((GtkCListRow *)pa)->data;
|
|
GpgmeKey b = ((GtkCListRow *)pb)->data;
|
|
const char *sa, *sb;
|
|
|
|
sa = a? gpgme_key_get_string_attr (a, attr, NULL, 0 ) : NULL;
|
|
sb = b? gpgme_key_get_string_attr (b, attr, NULL, 0 ) : NULL;
|
|
if (!sa)
|
|
return !!sb;
|
|
if (!sb)
|
|
return -1;
|
|
return strcasecmp(sa, sb);
|
|
}
|
|
|
|
static gint
|
|
cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
|
|
{
|
|
return cmp_attr (pa, pb, GPGME_ATTR_NAME);
|
|
}
|
|
|
|
static gint
|
|
cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
|
|
{
|
|
return cmp_attr (pa, pb, GPGME_ATTR_EMAIL);
|
|
}
|
|
|
|
static void
|
|
sort_keys ( struct select_keys_s *sk, enum col_titles column)
|
|
{
|
|
GtkCList *clist = sk->clist;
|
|
|
|
switch (column) {
|
|
case COL_NAME:
|
|
gtk_clist_set_compare_func (clist, cmp_name);
|
|
break;
|
|
case COL_EMAIL:
|
|
gtk_clist_set_compare_func (clist, cmp_email);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
/* column clicked again: toggle as-/decending */
|
|
if ( sk->sort_column == column) {
|
|
sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
|
|
GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
|
|
}
|
|
else
|
|
sk->sort_type = GTK_SORT_ASCENDING;
|
|
|
|
sk->sort_column = column;
|
|
gtk_clist_set_sort_type (clist, sk->sort_type);
|
|
gtk_clist_sort (clist);
|
|
}
|
|
|
|
static void
|
|
sort_keys_name (GtkWidget *widget, gpointer data)
|
|
{
|
|
sort_keys ((struct select_keys_s*)data, COL_NAME);
|
|
}
|
|
|
|
static void
|
|
sort_keys_email (GtkWidget *widget, gpointer data)
|
|
{
|
|
sort_keys ((struct select_keys_s*)data, COL_EMAIL);
|
|
}
|
|
|
|
|
|
#endif /*USE_GPGME*/
|