Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623191
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
121 KB
Subscribers
None
View Options
diff --git a/src/gpabackupop.c b/src/gpabackupop.c
index 8a48c03..4f5ddc7 100644
--- a/src/gpabackupop.c
+++ b/src/gpabackupop.c
@@ -1,323 +1,344 @@
/* gpabackupop.c - The GpaBackupOperation object.
Copyright (C) 2003 Miguel Coca.
Copyright (C) 2005, 2008 g10 Code GmbH.
This file is part of GPA.
GPA 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.
GPA 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 GPA; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */
#include <config.h>
#include <gpgme.h>
#include "gpa.h"
#include "i18n.h"
#include "gtktools.h"
#include "gpabackupop.h"
static GObjectClass *parent_class = NULL;
static gboolean gpa_backup_operation_idle_cb (gpointer data);
/* GObject boilerplate. */
/* Properties. */
enum
{
PROP_0,
PROP_KEY,
PROP_FINGERPRINT,
+ PROP_PROTOCOL
};
static void
gpa_backup_operation_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GpaBackupOperation *op = GPA_BACKUP_OPERATION (object);
switch (prop_id)
{
case PROP_KEY:
g_value_set_pointer (value, op->key);
break;
case PROP_FINGERPRINT:
g_value_set_string (value, op->fpr);
break;
+ case PROP_PROTOCOL:
+ g_value_set_int (value, op->protocol);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gpa_backup_operation_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GpaBackupOperation *op = GPA_BACKUP_OPERATION (object);
gchar *fpr;
gpgme_key_t key;
switch (prop_id)
{
case PROP_KEY:
key = (gpgme_key_t) g_value_get_pointer (value);
if (key)
{
op->key = key;
gpgme_key_ref (op->key);
op->fpr = g_strdup (op->key->subkeys->fpr);
op->key_id = g_strdup (gpa_gpgme_key_get_short_keyid (op->key));
}
break;
case PROP_FINGERPRINT:
fpr = (gchar*) g_value_get_pointer (value);
if (fpr)
{
op->key = NULL;
op->fpr = g_strdup (fpr);
op->key_id = g_strdup (fpr + strlen (fpr) - 8);
}
break;
+ case PROP_PROTOCOL:
+ op->protocol = g_value_get_int (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gpa_backup_operation_finalize (GObject *object)
{
GpaBackupOperation *op = GPA_BACKUP_OPERATION (object);
- if (op->key)
- {
- gpgme_key_unref (op->key);
- }
+ gpgme_key_unref (op->key);
g_free (op->fpr);
g_free (op->key_id);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gpa_backup_operation_init (GpaBackupOperation *op)
{
op->key = NULL;
op->fpr = NULL;
op->key_id = NULL;
+ op->protocol = GPGME_PROTOCOL_UNKNOWN;
}
static GObject*
gpa_backup_operation_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GObject *object;
GpaBackupOperation *op;
/* Invoke parent's constructor */
object = parent_class->constructor (type,
n_construct_properties,
construct_properties);
op = GPA_BACKUP_OPERATION (object);
/* Begin working when we are back into the main loop */
g_idle_add (gpa_backup_operation_idle_cb, op);
return object;
}
static void
gpa_backup_operation_class_init (GpaBackupOperationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->constructor = gpa_backup_operation_constructor;
object_class->finalize = gpa_backup_operation_finalize;
object_class->set_property = gpa_backup_operation_set_property;
object_class->get_property = gpa_backup_operation_get_property;
/* Properties */
g_object_class_install_property (object_class,
PROP_KEY,
g_param_spec_pointer
("key", "Key",
"Key",
G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class,
PROP_FINGERPRINT,
g_param_spec_pointer
("fpr", "fpr",
"Fingerprint",
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 used for FPR.",
+ GPGME_PROTOCOL_OpenPGP, GPGME_PROTOCOL_UNKNOWN, GPGME_PROTOCOL_UNKNOWN,
+ G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
}
GType
gpa_backup_operation_get_type (void)
{
static GType file_operation_type = 0;
if (!file_operation_type)
{
static const GTypeInfo file_operation_info =
{
sizeof (GpaBackupOperationClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gpa_backup_operation_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GpaBackupOperation),
0, /* n_preallocs */
(GInstanceInitFunc) gpa_backup_operation_init,
};
file_operation_type = g_type_register_static (GPA_OPERATION_TYPE,
"GpaBackupOperation",
&file_operation_info, 0);
}
return file_operation_type;
}
/* Private functions */
static void
gpa_backup_operation_do_backup (GpaBackupOperation *op, gchar *filename)
{
- if (gpa_backup_key (op->fpr, filename))
+ if (gpa_backup_key (op->fpr, filename, (op->protocol == GPGME_PROTOCOL_CMS)))
{
gchar *message;
message = g_strdup_printf (_("A copy of your secret key has "
"been made to the file:\n\n"
"\t\"%s\"\n\n"
"This is sensitive information, "
"and should be stored carefully\n"
"(for example, in a floppy disk "
"kept in a safe place)."),
filename);
gpa_window_message (message, GPA_OPERATION (op)->window);
g_free (message);
gpa_options_set_backup_generated (gpa_options_get_instance (),
TRUE);
}
else
{
gchar *message = g_strdup_printf (_("An error ocurred during the "
"backup operation."));
gpa_window_error (message, GPA_OPERATION (op)->window);
}
}
/* Return the filename in filename encoding. */
static gchar*
-gpa_backup_operation_dialog_run (GtkWidget *parent, const gchar *key_id)
+gpa_backup_operation_dialog_run (GtkWidget *parent, const gchar *key_id,
+ int is_x509)
{
static GtkWidget *dialog;
GtkResponseType response;
gchar *default_comp;
gchar *filename = NULL;
+ gchar *id_text;
+ GtkWidget *id_label;
if (! dialog)
{
- gchar *id_text;
- GtkWidget *id_label;
dialog = gtk_file_chooser_dialog_new
(_("Backup key to file"), GTK_WINDOW (parent),
GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
gtk_file_chooser_set_do_overwrite_confirmation
(GTK_FILE_CHOOSER (dialog), TRUE);
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
g_get_home_dir ());
- /* Set the label with more explanations. */
- id_text = g_strdup_printf (_("Generating backup of key: %s"), key_id);
- id_label = gtk_label_new (id_text);
- g_free (id_text);
- gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), id_label);
}
- /* Set the default file name. */
- default_comp = g_strdup_printf ("%s/secret-key-%s.asc",
- gnupg_homedir, key_id);
+ /* Set the label with more explanations. */
+ id_text = g_strdup_printf (_("Generating backup of key: 0x%s"), key_id);
+ id_label = gtk_label_new (id_text);
+ g_free (id_text);
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), id_label);
+
+ /* Set the default file name. I am not sure whether ".p12" or
+ ".pem" is better for an _armored_ pkcs#12. */
+ default_comp = g_strdup_printf ("%s/secret-key-%s.%s",
+ gnupg_homedir, key_id,
+ is_x509? "p12":"asc");
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), default_comp);
g_free (default_comp);
response = gtk_dialog_run (GTK_DIALOG (dialog));
if (response == GTK_RESPONSE_OK)
{
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
if (filename)
g_strdup (filename);
}
gtk_widget_hide (dialog);
return filename;
}
static gboolean
gpa_backup_operation_idle_cb (gpointer data)
{
GpaBackupOperation *op = data;
gchar *file;
file = gpa_backup_operation_dialog_run (GPA_OPERATION (op)->window,
- op->key_id);
+ op->key_id,
+ !!(op->protocol==GPGME_PROTOCOL_CMS));
if (file)
gpa_backup_operation_do_backup (op, file);
/* FIXME: Error handling. */
g_signal_emit_by_name (GPA_OPERATION (op), "completed", 0);
return FALSE; /* Remove us from the idle chain. */
}
/* API */
GpaBackupOperation*
gpa_backup_operation_new (GtkWidget *window, gpgme_key_t key)
{
GpaBackupOperation *op;
op = g_object_new (GPA_BACKUP_OPERATION_TYPE,
"window", window,
"key", key,
+ "protocol", key->protocol,
NULL);
return op;
}
GpaBackupOperation*
-gpa_backup_operation_new_from_fpr (GtkWidget *window, const gchar *fpr)
+gpa_backup_operation_new_from_fpr (GtkWidget *window,
+ const gchar *fpr, gpgme_protocol_t protocol)
{
GpaBackupOperation *op;
op = g_object_new (GPA_BACKUP_OPERATION_TYPE,
"window", window,
"fpr", fpr,
+ "protocol", protocol,
NULL);
return op;
}
diff --git a/src/gpabackupop.h b/src/gpabackupop.h
index 979d5b4..5002e89 100644
--- a/src/gpabackupop.h
+++ b/src/gpabackupop.h
@@ -1,61 +1,63 @@
/* gpabackupop.h - The GpaBackupOperation object.
* Copyright (C) 2003, Miguel Coca.
*
* This file is part of GPA
*
* GPA 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.
*
* GPA 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
*/
#ifndef GPA_BACKUP_OP_H
#define GPA_BACKUP_OP_H
#include "gpa.h"
#include <glib.h>
#include <glib-object.h>
#include "gpaoperation.h"
#include "gpaprogressdlg.h"
/* GObject stuff */
#define GPA_BACKUP_OPERATION_TYPE (gpa_backup_operation_get_type ())
#define GPA_BACKUP_OPERATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GPA_BACKUP_OPERATION_TYPE, GpaBackupOperation))
#define GPA_BACKUP_OPERATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GPA_BACKUP_OPERATION_TYPE, GpaBackupOperationClass))
#define GPA_IS_BACKUP_OPERATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GPA_BACKUP_OPERATION_TYPE))
#define GPA_IS_BACKUP_OPERATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GPA_BACKUP_OPERATION_TYPE))
#define GPA_BACKUP_OPERATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GPA_BACKUP_OPERATION_TYPE, GpaBackupOperationClass))
typedef struct _GpaBackupOperation GpaBackupOperation;
typedef struct _GpaBackupOperationClass GpaBackupOperationClass;
struct _GpaBackupOperation {
GpaOperation parent;
gpgme_key_t key;
gchar *fpr, *key_id;
+ gpgme_protocol_t protocol;
};
struct _GpaBackupOperationClass {
GpaOperationClass parent_class;
};
GType gpa_backup_operation_get_type (void) G_GNUC_CONST;
-/* API */
-GpaBackupOperation*
+/* API */
+GpaBackupOperation*
gpa_backup_operation_new (GtkWidget *window, gpgme_key_t key);
-GpaBackupOperation*
-gpa_backup_operation_new_from_fpr (GtkWidget *window, const gchar *fpr);
+GpaBackupOperation*
+gpa_backup_operation_new_from_fpr (GtkWidget *window, const gchar *fpr,
+ gpgme_protocol_t protocol);
#endif
diff --git a/src/gpagenkeysimpleop.c b/src/gpagenkeysimpleop.c
index fd6c21a..a298ac7 100644
--- a/src/gpagenkeysimpleop.c
+++ b/src/gpagenkeysimpleop.c
@@ -1,226 +1,227 @@
/* gpagenkeysimpleop.c - The GpaGenKeySimpleOperation object.
* Copyright (C) 2003, Miguel Coca.
*
* This file is part of GPA
*
* GPA 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.
*
* GPA 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
*/
#include <config.h>
#include <gpgme.h>
#include "gpa.h"
#include "i18n.h"
#include "gtktools.h"
#include "gpagenkeysimpleop.h"
#include "gpabackupop.h"
#include "keygenwizard.h"
static GObjectClass *parent_class = NULL;
static void gpa_gen_key_simple_operation_done_cb (GpaContext *context,
gpg_error_t err,
GpaGenKeySimpleOperation *op);
static void gpa_gen_key_simple_operation_done_error_cb (GpaContext *context,
gpg_error_t err,
GpaGenKeySimpleOperation *op);
static gboolean
gpa_gen_key_simple_operation_generate (gpa_keygen_para_t *params,
gboolean do_backup, gpointer data);
/* GObject boilerplate */
static void
gpa_gen_key_simple_operation_init (GpaGenKeySimpleOperation *op)
{
op->wizard = NULL;
op->do_backup = FALSE;
}
static void
gpa_gen_key_simple_operation_finalize (GObject *object)
{
GpaGenKeySimpleOperation *op = GPA_GEN_KEY_SIMPLE_OPERATION (object);
gtk_widget_destroy (op->wizard);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GObject*
gpa_gen_key_simple_operation_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *
construct_properties)
{
GObject *object;
GpaGenKeySimpleOperation *op;
/* Invoke parent's constructor */
object = parent_class->constructor (type,
n_construct_properties,
construct_properties);
op = GPA_GEN_KEY_SIMPLE_OPERATION (object);
/* Create wizard dialog. */
op->wizard = gpa_keygen_wizard_new (GPA_OPERATION (op)->window,
gpa_gen_key_simple_operation_generate,
op);
gtk_widget_show_all (op->wizard);
/* Connect to the "done" signal */
g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done",
G_CALLBACK (gpa_gen_key_simple_operation_done_error_cb), op);
g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done",
G_CALLBACK (gpa_gen_key_simple_operation_done_cb), op);
return object;
}
static void
gpa_gen_key_simple_operation_class_init (GpaGenKeySimpleOperationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->constructor = gpa_gen_key_simple_operation_constructor;
object_class->finalize = gpa_gen_key_simple_operation_finalize;
}
GType
gpa_gen_key_simple_operation_get_type (void)
{
static GType operation_type = 0;
if (!operation_type)
{
static const GTypeInfo operation_info =
{
sizeof (GpaGenKeySimpleOperationClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gpa_gen_key_simple_operation_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GpaGenKeySimpleOperation),
0, /* n_preallocs */
(GInstanceInitFunc) gpa_gen_key_simple_operation_init,
};
operation_type = g_type_register_static (GPA_GEN_KEY_OPERATION_TYPE,
"GpaGenKeySimpleOperation",
&operation_info, 0);
}
return operation_type;
}
/* API */
GpaGenKeySimpleOperation*
gpa_gen_key_simple_operation_new (GtkWidget *window)
{
GpaGenKeySimpleOperation *op;
op = g_object_new (GPA_GEN_KEY_SIMPLE_OPERATION_TYPE,
"window", window, NULL);
return op;
}
/* Internal */
static gboolean
gpa_gen_key_simple_operation_generate (gpa_keygen_para_t *params,
gboolean do_backup, gpointer data)
{
GpaGenKeySimpleOperation *op = data;
gpg_error_t err;
op->do_backup = do_backup;
err = gpa_generate_key_start (GPA_OPERATION (op)->context->ctx, params);
if (err)
{
gpa_gpgme_warning (err);
g_signal_emit_by_name (op, "completed", err);
return FALSE;
}
return TRUE;
}
static void
gpa_gen_key_simple_operation_backup_complete (GpaBackupOperation *backup,
gpg_error_t err,
GpaGenKeySimpleOperation *op)
{
gpgme_genkey_result_t result;
result = gpgme_op_genkey_result(GPA_OPERATION (op)->context->ctx);
g_signal_emit_by_name (op, "generated_key", result->fpr);
g_object_unref (backup);
g_signal_emit_by_name (op, "completed", 0);
}
static void
gpa_gen_key_simple_operation_done_cb (GpaContext *context,
gpg_error_t err,
GpaGenKeySimpleOperation *op)
{
if (! err)
{
gpgme_genkey_result_t result = gpgme_op_genkey_result (context->ctx);
if (op->do_backup)
{
GpaBackupOperation *backup;
- backup = gpa_backup_operation_new_from_fpr (op->wizard, result->fpr);
+ backup = gpa_backup_operation_new_from_fpr
+ (op->wizard, result->fpr, gpgme_get_protocol (context->ctx));
g_signal_connect (backup, "completed", G_CALLBACK
(gpa_gen_key_simple_operation_backup_complete),
op);
}
else
{
g_signal_emit_by_name (op, "generated_key", result->fpr);
g_signal_emit_by_name (op, "completed", err);
}
}
else
g_signal_emit_by_name (op, "completed", err);
}
static void
gpa_gen_key_simple_operation_done_error_cb (GpaContext *context,
gpg_error_t err,
GpaGenKeySimpleOperation *op)
{
switch (gpg_err_code (err))
{
case GPG_ERR_NO_ERROR:
case GPG_ERR_CANCELED:
/* Ignore these */
break;
default:
gpa_gpgme_warning (err);
break;
}
}
diff --git a/src/gpgmetools.c b/src/gpgmetools.c
index 8c16622..621ea23 100644
--- a/src/gpgmetools.c
+++ b/src/gpgmetools.c
@@ -1,1696 +1,1710 @@
/* gpgmetools.h - Additional gpgme support functions for GPA.
Copyright (C) 2002 Miguel Coca.
Copyright (C) 2005, 2008, 2009, 2012 g10 Code GmbH.
This file is part of GPA
GPA 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 3 of the License, or
(at your option) any later version.
GPA 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, see <http://www.gnu.org/licenses/>.
*/
/* A set of auxiliary functions for common tasks related to GPGME */
#include <config.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#include "gpa.h"
#include "gtktools.h"
#include "gpgmetools.h"
#include <fcntl.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#else
#include <io.h>
#endif
#ifdef G_OS_WIN32
#include <windows.h>
#endif
#include <glib/gstdio.h>
#ifndef O_BINARY
#ifdef _O_BINARY
#define O_BINARY _O_BINARY
#else
#define O_BINARY 0
#endif
#endif
/* Helper to strip the path from a source file name. This helps to
avoid showing long filenames in case of VPATH builds. */
static const char *
strip_path (const char *file)
{
const char *s = strrchr (file, '/');
return s? s+1:file;
}
/* Report an unexpected error in GPGME and quit the application. */
void
_gpa_gpgme_error (gpg_error_t err, const char *file, int line)
{
gchar *message = g_strdup_printf (_("Fatal Error in GPGME library\n"
"(invoked from file %s, line %i):\n\n"
"\t%s\n\n"
"The application will be terminated"),
strip_path (file), line,
gpgme_strerror (err));
gpa_window_error (message, NULL);
g_free (message);
exit (EXIT_FAILURE);
}
/* (Please use the gpa_gpgme_warning macros). */
void
_gpa_gpgme_warning (gpg_error_t err, const char *desc,
const char *file, int line)
{
char *argbuf = NULL;
const char *arg;
char *message;
if (desc && (!err || gpg_err_code (err) == GPG_ERR_GENERAL))
arg = desc;
else if (desc)
{
argbuf = g_strdup_printf ("%s (%s)", gpgme_strerror (err), desc);
arg = argbuf;
}
else
arg = gpgme_strerror (err);
message = g_strdup_printf
(_("The GPGME library returned an unexpected\n"
"error at %s:%d. The error was:\n\n"
"\t%s\n\n"
"This is either an installation problem or a bug in %s.\n"
"%s will now try to recover from this error."),
strip_path (file), line, arg, GPA_NAME, GPA_NAME);
g_free (argbuf);
gpa_window_error (message, NULL);
g_free (message);
}
/* Initialize a gpgme_ctx_t for use with GPA. */
gpgme_ctx_t
gpa_gpgme_new (void)
{
gpgme_ctx_t ctx;
gpg_error_t err;
/* g_assert (!"using gpa_gpgme_new"); */
err = gpgme_new (&ctx);
if (gpg_err_code (err) != GPG_ERR_NO_ERROR)
gpa_gpgme_error (err);
if (! cms_hack)
gpgme_set_passphrase_cb (ctx, gpa_passphrase_cb, NULL);
return ctx;
}
/* Write the contents of the gpgme_data_t object to the file.
Receives a filehandle instead of the filename, so that the caller
can make sure the file is accesible before putting anything into
data. This is only used for a TMP file, thus it is okay to
terminate the application on error. */
void
dump_data_to_file (gpgme_data_t data, FILE *file)
{
char buffer[128];
int nread;
nread = gpgme_data_seek (data, 0, SEEK_SET);
if (nread == -1)
{
gpa_window_error (strerror (errno), NULL);
exit (EXIT_FAILURE);
}
while ((nread = gpgme_data_read (data, buffer, sizeof (buffer))) > 0)
fwrite (buffer, nread, 1, file);
if (nread == -1)
{
gpa_window_error (strerror (errno), NULL);
exit (EXIT_FAILURE);
}
return;
}
static gboolean
check_overwriting (const char *filename, GtkWidget *parent)
{
/* If the file exists, ask before overwriting. */
if (g_file_test (filename, G_FILE_TEST_EXISTS))
{
GtkWidget *msgbox = gtk_message_dialog_new
(GTK_WINDOW(parent), GTK_DIALOG_MODAL,
GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
_("The file %s already exists.\n"
"Do you want to overwrite it?"), filename);
gtk_dialog_add_buttons (GTK_DIALOG (msgbox),
_("_Yes"), GTK_RESPONSE_YES,
_("_No"), GTK_RESPONSE_NO, NULL);
if (gtk_dialog_run (GTK_DIALOG (msgbox)) == GTK_RESPONSE_NO)
{
gtk_widget_destroy (msgbox);
return FALSE;
}
gtk_widget_destroy (msgbox);
}
return TRUE;
}
/* Not really a gpgme function, but needed in most places
dump_data_to_file is used. Opens a file for writing, asking the
user to overwrite if it exists and reporting any errors. Returns
NULL on failure, but you can assume the user has been informed of
the error (or maybe he just didn't want to overwrite!). */
FILE *
gpa_fopen (const char *filename, GtkWidget *parent)
{
FILE *target;
if (!check_overwriting (filename, parent))
return NULL;
target = g_fopen (filename, "w");
if (!target)
{
gchar *message;
message = g_strdup_printf ("%s: %s", filename, strerror(errno));
gpa_window_error (message, parent);
g_free (message);
return NULL;
}
return target;
}
int
gpa_open_output_direct (const char *filename, gpgme_data_t *data,
GtkWidget *parent)
{
int target = -1;
gpg_error_t err;
target = g_open (filename,
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
if (target == -1)
{
gchar *message;
message = g_strdup_printf ("%s: %s", filename, strerror(errno));
gpa_window_error (message, parent);
g_free (message);
}
err = gpgme_data_new_from_fd (data, target);
if (gpg_err_code (err) != GPG_ERR_NO_ERROR)
{
close (target);
target = -1;
}
return target;
}
int
gpa_open_output (const char *filename, gpgme_data_t *data, GtkWidget *parent)
{
if (! check_overwriting (filename, parent))
return -1;
return gpa_open_output_direct (filename, data, parent);
}
int
gpa_open_input (const char *filename, gpgme_data_t *data, GtkWidget *parent)
{
gpg_error_t err;
int target = -1;
target = g_open (filename, O_RDONLY | O_BINARY, 0);
if (target == -1)
{
gchar *message;
message = g_strdup_printf ("%s: %s", filename, strerror(errno));
gpa_window_error (message, parent);
g_free (message);
}
err = gpgme_data_new_from_fd (data, target);
if (gpg_err_code (err) != GPG_ERR_NO_ERROR)
{
close (target);
target = -1;
}
return target;
}
/* Do a gpgme_data_new_from_file and report any GPGME_File_Error to
the user. */
gpg_error_t
gpa_gpgme_data_new_from_file (gpgme_data_t *data,
const char *filename,
GtkWidget *parent)
{
gpg_error_t err;
err = gpgme_data_new_from_file (data, filename, 1);
if (gpg_err_code_to_errno (err) != 0)
{
gchar *message;
message = g_strdup_printf ("%s: %s", filename, strerror(errno));
gpa_window_error (message, NULL);
g_free (message);
}
return err;
}
/* Write the contents of the gpgme_data_t into the clipboard. Assumes
that the data is ASCII. Return 0 on success. */
int
dump_data_to_clipboard (gpgme_data_t data, GtkClipboard *clipboard)
{
char buffer[512];
int nread;
gchar *text = NULL;
size_t len = 0;
nread = gpgme_data_seek (data, 0, SEEK_SET);
if (nread == -1)
{
gpa_window_error (strerror (errno), NULL);
return -1;
}
while ((nread = gpgme_data_read (data, buffer, sizeof (buffer))) > 0)
{
text = g_realloc (text, len + nread + 1);
strncpy (text + len, buffer, nread);
len += nread;
}
if (nread == -1)
{
gpa_window_error (strerror (errno), NULL);
return -1;
}
gtk_clipboard_set_text (clipboard, text, (int)len);
g_free (text);
return 0;
}
/* Assemble the parameter string for gpgme_op_genkey for GnuPG. We
don't need worry about the user ID being UTF-8 as long as we are
using GTK+2, because all user input is UTF-8 in it. */
static gchar *
build_genkey_parms (gpa_keygen_para_t *params)
{
gchar *string;
const char *key_algo;
gchar *subkeys = NULL;
gchar *name = NULL;
gchar *email = NULL;
gchar *comment = NULL;
gchar *expire = NULL;
/* Choose which keys and subkeys to generate. */
switch (params->algo)
{
case GPA_KEYGEN_ALGO_RSA_RSA:
key_algo = "RSA";
subkeys = g_strdup_printf ("Subkey-Type: RSA\n"
"Subkey-Length: %d\n"
"Subkey-Usage: encrypt\n", params->keysize);
break;
case GPA_KEYGEN_ALGO_RSA_ELGAMAL:
key_algo = "RSA";
subkeys = g_strdup_printf ("Subkey-Type: ELG-E\n"
"Subkey-Length: %d\n"
"Subkey-Usage: encrypt\n", params->keysize);
break;
case GPA_KEYGEN_ALGO_RSA:
key_algo = "RSA";
break;
case GPA_KEYGEN_ALGO_DSA_ELGAMAL:
key_algo = "DSA";
subkeys = g_strdup_printf ("Subkey-Type: ELG-E\n"
"Subkey-Length: %i\n"
"Subkey-Usage: encrypt\n", params->keysize);
break;
case GPA_KEYGEN_ALGO_DSA:
key_algo = "DSA";
break;
default:
/* Can't happen */
return NULL;
}
/* Construct the user ID. */
if (params->name && params->name[0])
name = g_strdup_printf ("Name-Real: %s\n", params->name);
if (params->email && params->email[0])
email = g_strdup_printf ("Name-Email: %s\n", params->email);
if (params->comment && params->comment[0])
comment = g_strdup_printf ("Name-Comment: %s\n", params->comment);
/* Build the expiration date string if needed */
if (g_date_valid (¶ms->expire))
expire = g_strdup_printf ("Expire-Date: %04d-%02d-%02d\n",
g_date_get_year (¶ms->expire),
g_date_get_month (¶ms->expire),
g_date_get_day (¶ms->expire));
/* Assemble the final parameter string */
string = g_strdup_printf ("<GnupgKeyParms format=\"internal\">\n"
"Key-Type: %s\n"
"Key-Length: %i\n"
"Key-Usage: sign\n"
"%s" /* Subkeys */
"%s" /* Name */
"%s" /* Email */
"%s" /* Comment */
"%s" /* Expiration date */
"%%ask-passphrase\n"
"</GnupgKeyParms>\n",
key_algo,
params->keysize,
subkeys? subkeys : "",
name? name:"",
email? email : "",
comment? comment : "",
expire? expire : "");
/* Free auxiliary strings if they are not empty */
g_free (subkeys);
g_free (name);
g_free (email);
g_free (comment);
g_free (expire);
return string;
}
/* Begin generation of a key with the given parameters. It prepares
the parameters required by GPGME and returns whatever
gpgme_op_genkey_start returns. */
gpg_error_t
gpa_generate_key_start (gpgme_ctx_t ctx, gpa_keygen_para_t *params)
{
gchar *parm_string;
gpg_error_t err;
parm_string = build_genkey_parms (params);
err = gpgme_op_genkey_start (ctx, parm_string, NULL, NULL);
g_free (parm_string);
return err;
}
/* Retrieve the path to the GPG executable. */
static const gchar *
get_gpg_path (void)
{
gpgme_engine_info_t engine;
gpgme_get_engine_info (&engine);
while (engine)
{
if (engine->protocol == GPGME_PROTOCOL_OpenPGP)
return engine->file_name;
engine = engine->next;
}
return NULL;
}
/* Retrieve the path to the GPGSM executable. */
static const gchar *
get_gpgsm_path (void)
{
gpgme_engine_info_t engine;
gpgme_get_engine_info (&engine);
while (engine)
{
if (engine->protocol == GPGME_PROTOCOL_CMS)
return engine->file_name;
engine = engine->next;
}
return NULL;
}
/* Retrieve the path to the GPGCONF executable. */
static const gchar *
get_gpgconf_path (void)
{
gpgme_engine_info_t engine;
gpgme_get_engine_info (&engine);
while (engine)
{
if (engine->protocol == GPGME_PROTOCOL_GPGCONF)
return engine->file_name;
engine = engine->next;
}
return NULL;
}
/* Retrieve the path to the GPG-CONNECT-AGENT executable. Note that
the caller must free the returned string. */
static char *
get_gpg_connect_agent_path (void)
{
const char *gpgconf;
char *fname, *p;
gpgconf = get_gpgconf_path ();
if (!gpgconf)
return NULL;
#ifdef G_OS_WIN32
# define NEWNAME "gpg-connect-agent.exe"
#else
# define NEWNAME "gpg-connect-agent"
#endif
fname = g_malloc (strlen (gpgconf) + strlen (NEWNAME) + 1);
strcpy (fname, gpgconf);
#ifdef G_OS_WIN32
for (p=fname; *p; p++)
if (*p == '\\')
*p = '/';
#endif /*G_OS_WIN32*/
p = strrchr (fname, '/');
if (p)
p++;
else
p = fname;
strcpy (p, NEWNAME);
#undef NEWNAME
return fname;
}
/* Backup a key. It exports both the public and secret keys to a file.
- Returns TRUE on success and FALSE on error. It displays errors to
- the user. */
+ IS_X509 tells the function that the fingerprint is from an X.509
+ key. Returns TRUE on success and FALSE on error. It displays
+ errors to the user. */
gboolean
-gpa_backup_key (const gchar *fpr, const char *filename)
+gpa_backup_key (const gchar *fpr, const char *filename, int is_x509)
{
gchar *header, *pub_key, *sec_key;
gchar *err;
FILE *file;
gint ret_code;
gchar *header_argv[] =
{
NULL, "--batch", "--no-tty", "--fingerprint", (gchar*) fpr, NULL
};
gchar *pub_argv[] =
{
NULL, "--batch", "--no-tty", "--armor", "--export", (gchar*) fpr, NULL
};
gchar *sec_argv[] =
{
NULL, "--batch", "--no-tty", "--armor", "--export-secret-key",
(gchar*) fpr, NULL
};
+ gchar *seccms_argv[] =
+ {
+ NULL, "--batch", "--no-tty", "--armor", "--export-secret-key-p12",
+ (gchar*) fpr, NULL
+ };
const gchar *path;
mode_t mask;
/* Get the gpg path. */
- path = get_gpg_path ();
+ if (is_x509)
+ path = get_gpgsm_path ();
+ else
+ path = get_gpg_path ();
g_return_val_if_fail (path && *path, FALSE);
/* Add the executable to the arg arrays */
header_argv[0] = (gchar*) path;
pub_argv[0] = (gchar*) path;
sec_argv[0] = (gchar*) path;
+ seccms_argv[0] = (gchar*) path;
/* Open the file */
mask = umask (0077);
file = g_fopen (filename, "w");
umask (mask);
if (!file)
{
gchar message[256];
g_snprintf (message, sizeof(message), "%s: %s",
filename, strerror(errno));
gpa_window_error (message, NULL);
return FALSE;
}
/* Get the keys and write them into the file */
- fputs (_("************************************************************************\n"
- "* WARNING: This file is a backup of your secret key. Please keep it in *\n"
- "* a safe place. *\n"
- "************************************************************************\n\n"),
- file);
+ fputs (_(
+ "************************************************************************\n"
+ "* WARNING: This file is a backup of your secret key. Please keep it in *\n"
+ "* a safe place. *\n"
+ "************************************************************************\n"
+ "\n"), file);
+
fputs (_("The key backed up in this file is:\n\n"), file);
if( !g_spawn_sync (NULL, header_argv, NULL, 0, NULL, NULL, &header,
&err, &ret_code, NULL))
{
return FALSE;
}
fputs (header, file);
g_free (err);
g_free (header);
fputs ("\n", file);
if( !g_spawn_sync (NULL, pub_argv, NULL, 0, NULL, NULL, &pub_key,
- &err, &ret_code, NULL))
+ &err, &ret_code, NULL))
{
fclose (file);
return FALSE;
}
fputs (pub_key, file);
g_free (err);
g_free (pub_key);
fputs ("\n", file);
- if( !g_spawn_sync (NULL, sec_argv, NULL, 0, NULL, NULL, &sec_key,
+ if( !g_spawn_sync (NULL,
+ is_x509? seccms_argv : sec_argv,
+ NULL, 0, NULL, NULL, &sec_key,
&err, &ret_code, NULL))
{
fclose (file);
return FALSE;
}
fputs (sec_key, file);
g_free (err);
g_free (sec_key);
fclose (file);
return TRUE;
}
void
gpa_keygen_para_free (gpa_keygen_para_t *params)
{
if (params)
{
g_free (params->name);
g_free (params->email);
g_free (params->comment);
g_free (params->r_error_desc);
g_free (params);
}
}
gpa_keygen_para_t *
gpa_keygen_para_new (void)
{
gpa_keygen_para_t *params = xcalloc (1, sizeof *params);
g_date_clear (¶ms->expire, 1);
return params;
}
/* Ownertrust strings. */
const gchar *
gpa_key_ownertrust_string (gpgme_key_t key)
{
if (key->protocol == GPGME_PROTOCOL_CMS)
return "";
switch (key->owner_trust)
{
case GPGME_VALIDITY_UNKNOWN:
case GPGME_VALIDITY_UNDEFINED:
default:
return _("Unknown");
break;
case GPGME_VALIDITY_NEVER:
return _("Never");
break;
case GPGME_VALIDITY_MARGINAL:
return _("Marginal");
break;
case GPGME_VALIDITY_FULL:
return _("Full");
break;
case GPGME_VALIDITY_ULTIMATE:
return _("Ultimate");
break;
}
}
/* Key validity strings. */
const gchar *
gpa_key_validity_string (gpgme_key_t key)
{
if (!key->uids)
return _("Unknown");
switch (key->uids->validity)
{
case GPGME_VALIDITY_UNKNOWN:
case GPGME_VALIDITY_UNDEFINED:
case GPGME_VALIDITY_NEVER:
case GPGME_VALIDITY_MARGINAL:
default:
if (key->subkeys->revoked)
return _("Revoked");
else if (key->subkeys->expired)
return _("Expired");
else if (key->subkeys->disabled)
return _("Disabled");
else if (key->subkeys->invalid)
return _("Incomplete");
else
return _("Unknown");
break;
case GPGME_VALIDITY_FULL:
case GPGME_VALIDITY_ULTIMATE:
return _("Fully Valid");
}
}
static GtkWidget *
passphrase_question_label (const char *uid_hint,
const char *passphrase_info,
int prev_was_bad)
{
GtkWidget *label;
gchar *input;
gchar *text;
gchar *keyid, *userid;
gint i;
/* Just in case this is called without a user id hint we return a
simple text to avoid a crash. */
if (!uid_hint)
return gtk_label_new ("Passphrase?");
input = g_strdup (uid_hint);
/* The first word in the hint is the key ID */
keyid = input;
for (i = 0; input[i] != ' '; i++)
{
}
input[i++] = '\0';
/* The rest of the hint is the user ID */
userid = input+i;
/* Build the label widget */
if (!prev_was_bad)
{
text = g_strdup_printf ("%s\n\n%s %s\n%s %s",
_("Please enter the passphrase for"
" the following key:"),
_("User Name:"), userid,
_("Key ID:"), keyid);
}
else
{
text = g_strdup_printf ("%s\n\n%s %s\n%s %s",
_("Wrong passphrase, please try again:"),
_("User Name:"), userid,
_("Key ID:"), keyid);
}
label = gtk_label_new (text);
g_free (input);
g_free (text);
return label;
}
/* This is the function called by GPGME when it wants a passphrase. */
gpg_error_t
gpa_passphrase_cb (void *hook, const char *uid_hint,
const char *passphrase_info,
int prev_was_bad, int fd)
{
GtkWidget * dialog;
GtkWidget * hbox;
GtkWidget * vbox;
GtkWidget * entry;
GtkWidget * pixmap;
GtkResponseType response;
gchar *passphrase;
dialog = gtk_dialog_new_with_buttons (_("Enter Passphrase"),
NULL, GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
TRUE, FALSE, 10);
pixmap = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION,
GTK_ICON_SIZE_DIALOG);
gtk_box_pack_start (GTK_BOX (hbox), pixmap, TRUE, FALSE, 10);
vbox = gtk_vbox_new (TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, FALSE, 10);
/* The default button is OK */
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
/* Set the contents of the dialog */
gtk_box_pack_start_defaults (GTK_BOX (vbox),
passphrase_question_label (uid_hint,
passphrase_info,
prev_was_bad));
entry = gtk_entry_new ();
gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, FALSE, 10);
gtk_widget_grab_focus (entry);
/* Run the dialog */
gtk_widget_show_all (dialog);
response = gtk_dialog_run (GTK_DIALOG (dialog));
passphrase = g_strdup_printf ("%s\n",
gtk_entry_get_text (GTK_ENTRY (entry)));
gtk_widget_destroy (dialog);
if (response == GTK_RESPONSE_OK)
{
int passphrase_len = strlen (passphrase);
#ifdef G_OS_WIN32
DWORD res;
if (WriteFile ((HANDLE) _get_osfhandle (fd), passphrase,
passphrase_len, &res, NULL) == 0
|| res < passphrase_len)
{
g_free (passphrase);
return gpg_error (gpg_err_code_from_errno (EIO));
}
else
return gpg_error (GPG_ERR_NO_ERROR);
#else
int res;
res = write (fd, passphrase, passphrase_len);
g_free (passphrase);
if (res == -1)
return gpg_error (gpg_err_code_from_errno (errno));
else if (res < passphrase_len)
return gpg_error (gpg_err_code_from_errno (EIO));
else
return gpg_error (GPG_ERR_NO_ERROR);
#endif
}
else
{
g_free (passphrase);
return gpg_error (GPG_ERR_CANCELED);
}
}
/* Convenience functions to access key attributes, which need to be
filtered before being displayed to the user. */
/* Return the user ID, making sure it is properly UTF-8 encoded.
Allocates a new string, which must be freed with g_free (). */
static gchar *
string_to_utf8 (const gchar *string)
{
const char *s;
if (!string)
return NULL;
/* Due to a bug in old and not so old PGP versions user IDs have
been copied verbatim into the key. Thus many users with Umlauts
et al. in their name will see their names garbled. Although this
is not an issue for me (;-)), I have a couple of friends with
Umlauts in their name, so let's try to make their life easier by
detecting invalid encodings and convert that to Latin-1. We use
this even for X.509 because it may make things even better given
all the invalid encodings often found in X.509 certificates. */
for (s = string; *s && !(*s & 0x80); s++)
;
if (*s && ((s[1] & 0xc0) == 0x80) && ( ((*s & 0xe0) == 0xc0)
|| ((*s & 0xf0) == 0xe0)
|| ((*s & 0xf8) == 0xf0)
|| ((*s & 0xfc) == 0xf8)
|| ((*s & 0xfe) == 0xfc)) )
{
/* Possible utf-8 character followed by continuation byte.
Although this might still be Latin-1 we better assume that it
is valid utf-8. */
return g_strdup (string);
}
else if (*s && !strchr (string, 0xc3))
{
/* No 0xC3 character in the string; assume that it is Latin-1. */
return g_convert (string, -1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
}
else
{
/* Everything else is assumed to be UTF-8. We do this even that
we know the encoding is not valid. However as we only test
the first non-ascii character, valid encodings might
follow. */
return g_strdup (string);
}
}
gchar *
gpa_gpgme_key_get_userid (gpgme_user_id_t uid)
{
gchar *uid_utf8;
if (!uid)
return g_strdup (_("[None]"));
uid_utf8 = string_to_utf8 (uid->uid);
/* Tag revoked UID's*/
if (uid->revoked)
{
/* FIXME: I think, this code should be simply disabled. Even if
the uid U is revoked, it is "U", not "[Revoked] U". Side
note: adding this prefix obviously breaks sorting by
uid. -moritz */
gchar *tmp = g_strdup_printf ("[%s] %s", _("Revoked"), uid_utf8);
g_free (uid_utf8);
uid_utf8 = tmp;
}
return uid_utf8;
}
/* Return the key fingerprint, properly formatted according to the key
version. Allocates a new string, which must be freed with
g_free(). This is based on code from GPAPA's
extract_fingerprint. */
gchar *
gpa_gpgme_key_format_fingerprint (const char *fpraw)
{
if (strlen (fpraw) == 32 ) /* v3 */
{
char *fp = g_malloc (strlen (fpraw) + 16 + 1);
const char *r = fpraw;
char *q = fp;
gint c = 0;
while (*r)
{
*q++ = *r++;
c++;
if (c < 32)
{
if (c % 2 == 0)
*q++ = ' ';
if (c % 16 == 0)
*q++ = ' ';
}
}
*q = 0;
return fp;
}
else
{
char *fp = g_malloc (strlen (fpraw) + 10 + 1);
const char *r = fpraw;
char *q = fp;
gint c = 0;
while (*r)
{
*q++ = *r++;
c++;
if (c < 40)
{
if (c % 4 == 0)
*q++ = ' ';
if (c % 20 == 0)
*q++ = ' ';
}
}
*q = 0;
return fp;
}
}
/* Return the short key ID of the indicated key. The returned string
is valid as long as the key is valid. */
const gchar *
gpa_gpgme_key_get_short_keyid (gpgme_key_t key)
{
const char *keyid = key->subkeys->keyid;
if (!keyid)
return NULL;
else
return keyid + 8;
}
/* Convenience function to access key signature attibutes, much like
the previous ones. */
/* Return the user ID, making sure it is properly UTF-8 encoded.
Allocates a new string, which must be freed with g_free(). */
gchar *
gpa_gpgme_key_sig_get_userid (gpgme_key_sig_t sig)
{
if (!sig->uid || !*sig->uid)
/* Duplicate it to make sure it can be g_free'd. */
return g_strdup (_("[Unknown user ID]"));
else
return string_to_utf8 (sig->uid);
}
/* Return the short key ID of the indicated key. The returned string
is valid as long as the key is valid. */
const gchar *
gpa_gpgme_key_sig_get_short_keyid (gpgme_key_sig_t sig)
{
return sig->keyid + 8;
}
/* Return a string with the status of the key signature. */
const gchar *
gpa_gpgme_key_sig_get_sig_status (gpgme_key_sig_t sig,
GHashTable *revoked)
{
const gchar *status;
switch (sig->status)
{
case GPGME_SIG_STAT_GOOD:
status = _("Valid");
break;
case GPGME_SIG_STAT_BAD:
status = _("Bad");
default:
status = _("Unknown");
}
if (sig->expired)
{
status = _("Expired");
}
else if (g_hash_table_lookup (revoked, sig->keyid))
{
status = _("Revoked");
}
return status;
}
/* Return a string with the level of the key signature. */
const gchar *
gpa_gpgme_key_sig_get_level (gpgme_key_sig_t sig)
{
switch (sig->sig_class)
{
case 0x10:
return _("Generic");
break;
case 0x11:
return _("Persona");
break;
case 0x12:
return _("Casual");
break;
case 0x13:
return _("Positive");
break;
default:
return _("Unknown");
break;
}
}
/* Return a human readable string with the status of the signature
SIG. If R_KEYDESC is not NULL, the description of the key
(e.g.. the user ID) will be stored as a malloced string at that
address; if no key is known, NULL will be stored. If R_KEY is not
NULL, a key object will be stored at that address; NULL if no key
is known. CTX is used as helper to figure out the key
description. */
char *
gpa_gpgme_get_signature_desc (gpgme_ctx_t ctx, gpgme_signature_t sig,
char **r_keydesc, gpgme_key_t *r_key)
{
gpgme_key_t key = NULL;
char *keydesc = NULL;
char *sigdesc;
const char *sigstatus;
sigstatus = sig->status? gpg_strerror (sig->status) : "";
if (sig->fpr && ctx)
{
gpgme_get_key (ctx, sig->fpr, &key, 0);
if (key)
keydesc = gpa_gpgme_key_get_userid (key->uids);
}
if (sig->summary & GPGME_SIGSUM_RED)
{
if (keydesc && *sigstatus)
sigdesc = g_strdup_printf (_("Bad signature by %s: %s"),
keydesc, sigstatus);
else if (keydesc)
sigdesc = g_strdup_printf (_("Bad signature by %s"),
keydesc);
else if (sig->fpr && *sigstatus)
sigdesc = g_strdup_printf (_("Bad signature by unknown key "
"%s: %s"), sig->fpr, sigstatus);
else if (sig->fpr)
sigdesc = g_strdup_printf (_("Bad signature by unknown key "
"%s"), sig->fpr);
else if (*sigstatus)
sigdesc = g_strdup_printf (_("Bad signature by unknown key: "
"%s"), sigstatus);
else
sigdesc = g_strdup_printf (_("Bad signature by unknown key"));
}
else if (sig->summary & GPGME_SIGSUM_VALID)
{
if (keydesc && *sigstatus)
sigdesc = g_strdup_printf (_("Good signature by %s: %s"),
keydesc, sigstatus);
else if (keydesc)
sigdesc = g_strdup_printf (_("Good signature by %s"),
keydesc);
else if (sig->fpr && *sigstatus)
sigdesc = g_strdup_printf (_("Good signature by unknown key "
"%s: %s"), sig->fpr, sigstatus);
else if (sig->fpr)
sigdesc = g_strdup_printf (_("Good signature by unknown key "
"%s"), sig->fpr);
else if (*sigstatus)
sigdesc = g_strdup_printf (_("Good signature by unknown key: "
"%s"), sigstatus);
else
sigdesc = g_strdup_printf (_("Good signature by unknown key"));
}
else
{
if (keydesc && *sigstatus)
sigdesc = g_strdup_printf (_("Uncertain signature by %s: %s"),
keydesc, sigstatus);
else if (keydesc)
sigdesc = g_strdup_printf (_("Uncertain signature by %s"),
keydesc);
else if (sig->fpr && *sigstatus)
sigdesc = g_strdup_printf (_("Uncertain signature by unknown key "
"%s: %s"), sig->fpr, sigstatus);
else if (sig->fpr)
sigdesc = g_strdup_printf (_("Uncertain signature by unknown key "
"%s"), sig->fpr);
else if (*sigstatus)
sigdesc = g_strdup_printf (_("Uncertain signature by unknown "
"key: %s"), sigstatus);
else
sigdesc = g_strdup_printf (_("Uncertain signature by unknown "
"key"));
}
if (r_keydesc)
*r_keydesc = keydesc;
else
g_free (keydesc);
if (r_key)
*r_key = key;
else
gpgme_key_unref (key);
return sigdesc;
}
/* Return a string listing the capabilities of a key. */
const gchar *
gpa_get_key_capabilities_text (gpgme_key_t key)
{
if (key->can_certify)
{
if (key->can_sign)
{
if (key->can_encrypt)
return _("The key can be used for certification, signing "
"and encryption.");
else
return _("The key can be used for certification and "
"signing, but not for encryption.");
}
else
{
if (key->can_encrypt)
return _("The key can be used for certification and "
"encryption.");
else
return _("The key can be used only for certification.");
}
}
else
{
if (key->can_sign)
{
if (key->can_encrypt)
return _("The key can be used only for signing and "
"encryption, but not for certification.");
else
return _("The key can be used only for signing.");
}
else
{
if (key->can_encrypt)
return _("The key can be used only for encryption.");
else
/* Can't happen, even for subkeys. */
return _("This key is useless.");
}
}
}
/* Return a copy of the key array. */
gpgme_key_t *
gpa_gpgme_copy_keyarray (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;
}
/* Release all keys in the array KEYS as weel as ARRY itself. */
void
gpa_gpgme_release_keyarray (gpgme_key_t *keys)
{
if (keys)
{
int idx;
for (idx=0; keys[idx]; idx++)
gpgme_key_unref (keys[idx]);
g_free (keys);
}
}
/* Read the next number in the version string STR and return it in
*NUMBER. Return a pointer to the tail of STR after parsing, or
*NULL if the version string was invalid. */
static const char *
parse_version_number (const char *str, int *number)
{
#define MAXVAL ((INT_MAX - 10) / 10)
int val = 0;
/* Leading zeros are not allowed. */
if (*str == '0' && isascii (str[1]) && isdigit (str[1]))
return NULL;
while (isascii (*str) && isdigit (*str) && val <= MAXVAL)
{
val *= 10;
val += *(str++) - '0';
}
*number = val;
return val > MAXVAL ? NULL : str;
#undef MAXVAL
}
/* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
as integers. The function returns the tail of the string that
follows the version number. This might be te empty string if there
is nothing following the version number, or a patchlevel. The
function returns NULL if the version string is not valid. */
static const char *
parse_version_string (const char *str, int *major, int *minor, int *micro)
{
str = parse_version_number (str, major);
if (!str || *str != '.')
return NULL;
str++;
str = parse_version_number (str, minor);
if (!str || *str != '.')
return NULL;
str++;
str = parse_version_number (str, micro);
if (!str)
return NULL;
/* A patchlevel might follow. */
return str;
}
/* Return true if MY_VERSION is at least REQ_VERSION, and false
otherwise. */
static int
compare_version_strings (const char *my_version,
const char *rq_version)
{
int my_major, my_minor, my_micro;
int rq_major, rq_minor, rq_micro;
const char *my_plvl, *rq_plvl;
if (!rq_version)
return 1;
if (!my_version)
return 0;
my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
if (!my_plvl)
return 0;
rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
if (!rq_plvl)
return 0;
if (my_major > rq_major
|| (my_major == rq_major && my_minor > rq_minor)
|| (my_major == rq_major && my_minor == rq_minor
&& my_micro > rq_micro)
|| (my_major == rq_major && my_minor == rq_minor
&& my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
return 1;
return 0;
}
/* Try switching to the gpg2 backend or the one given by filename. */
/* Return 1 if the gpg engine has at least version NEED_VERSION,
otherwise 0. */
int
is_gpg_version_at_least (const char *need_version)
{
gpgme_engine_info_t engine;
gpgme_get_engine_info (&engine);
while (engine)
{
if (engine->protocol == GPGME_PROTOCOL_OpenPGP)
return !!compare_version_strings (engine->version, need_version);
engine = engine->next;
}
return 0; /* No gpg-engine available. */
}
/* Structure used to communicate with gpg_simple_stdio_cb. */
struct gpg_simple_stdio_parm_s
{
gboolean (*cb)(void *opaque, char *line);
void *cb_arg;
GString *string;
int only_status_lines;
};
/* Helper for gpa_start_simple_gpg_command. */
static gboolean
gpg_simple_stdio_cb (GIOChannel *channel, GIOCondition condition,
void *user_data)
{
struct gpg_simple_stdio_parm_s *parm = user_data;
GIOStatus status;
char *line, *p;
if ((condition & G_IO_IN))
{
/* We don't use a while but an if because that allows to update
progress bars nicely. A bit slower, but no real problem. */
if ((status = g_io_channel_read_line_string
(channel, parm->string, NULL, NULL)) == G_IO_STATUS_NORMAL)
{
line = parm->string->str;
/* Strip line terminator. */
p = strchr (line, '\n');
if (p)
{
if (p > line && p[-1] == '\r')
p[-1] = 0;
else
*p = 0;
}
/* We care only about status lines. */
if (parm->only_status_lines && !strncmp (line, "[GNUPG:] ", 9))
{
line += 9;
/* Call user callback. */
if (parm->cb && !parm->cb (parm->cb_arg, line))
{
/* User requested EOF. */
goto cleanup;
}
/* Return direct so that we do not run into the G_IO_HUP
check. This is required to read all buffered input. */
return TRUE; /* Keep on watching this channel. */
}
else if (!parm->only_status_lines)
{
/* Call user callback. */
if (parm->cb && !parm->cb (parm->cb_arg, line))
{
/* User requested EOF. */
goto cleanup;
}
return TRUE; /* Keep on watching this channel. */
}
}
if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN )
{
/* Error or EOF. */
goto cleanup;
}
}
if ((condition & G_IO_HUP))
{
goto cleanup;
}
return TRUE; /* Keep on watching this channel. */
cleanup:
if (parm->cb)
parm->cb (parm->cb_arg, NULL); /* Tell user about EOF. */
g_string_free (parm->string, TRUE);
xfree (parm);
g_io_channel_unref (channel); /* Close channel. */
return FALSE; /* Remove us from the watch list. */
}
/* Run gpg asynchronously with the given arguments and return a gpg
error code on error. The list of arguments are all of type (const
char*) and end with a NULL argument (FIRST_ARG may already be NULL,
but that does not make any sense). STDIN and STDOUT are connected
to /dev/null. No more than 20 arguments may be given.
Because the protocol GPGME_PROTOCOL_ASSUAN makes no sense here, it
is used to call gpg-connect-agent.
If the function returns success the provided callback CB is called
for each line received on stdout (respective stderr if USE_STADERR
is true). EOF is send to this callback by passing a LINE as NULL.
The callback may use this for cleanup. If the callback returns
FALSE, an EOF is forced so that the callback is called once more
with LINE set to NULL.
This function is used to run
gpgsm --learn-card
gpg-connect-agent NOP /bye
The problem is that under Windows g_spawn does not allow to specify
flags for the underlying CreateProcess. Thus it is not possible to
create a detached process (i.e. without a console); the result is
that a console window pops up. I can see two solutions: (1) Use a
wrapper process to start them detached, or (2) move the required
function into GPGME and use that new API.
*/
gpg_error_t
gpa_start_simple_gpg_command (gboolean (*cb)(void *opaque, char *line),
void *cb_arg, gpgme_protocol_t protocol,
int use_stderr,
const char *first_arg, ...)
{
char *argv[24];
int argc;
int fd_stdio;
GIOChannel *channel;
struct gpg_simple_stdio_parm_s *parm = NULL;
char *freeme = NULL;
if (protocol == GPGME_PROTOCOL_OpenPGP)
argv[0] = (char*)get_gpg_path ();
else if (protocol == GPGME_PROTOCOL_CMS)
argv[0] = (char*)get_gpgsm_path ();
else if (protocol == GPGME_PROTOCOL_GPGCONF)
argv[0] = (char*)get_gpgconf_path ();
else if (protocol == GPGME_PROTOCOL_ASSUAN)
argv[0] = freeme = get_gpg_connect_agent_path ();
else
argv[0] = NULL;
if (!argv[0])
{
gpa_window_error (_("A required engine component is not installed."),
NULL);
return gpg_error (GPG_ERR_INV_ARG);
}
argc = 1;
if (protocol != GPGME_PROTOCOL_GPGCONF
&& protocol != GPGME_PROTOCOL_ASSUAN)
{
argv[argc++] = (char*)"--status-fd";
argv[argc++] = (char*)"2";
}
argv[argc++] = (char*)first_arg;
if (first_arg)
{
va_list arg_ptr;
const char *s;
va_start (arg_ptr, first_arg);
while (argc < DIM (argv)-1 && (s=va_arg (arg_ptr, const char *)))
argv[argc++] = (char*)s;
va_end (arg_ptr);
argv[argc] = NULL;
g_return_val_if_fail (argc < DIM (argv), gpg_error (GPG_ERR_INV_ARG));
}
parm = g_try_malloc (sizeof *parm);
if (!parm)
return gpg_error_from_syserror ();
parm->cb = cb;
parm->cb_arg = cb_arg;
parm->string = g_string_sized_new (200);
parm->only_status_lines = use_stderr;
if (!g_spawn_async_with_pipes (NULL, argv, NULL,
(use_stderr
? G_SPAWN_STDOUT_TO_DEV_NULL
: G_SPAWN_STDERR_TO_DEV_NULL),
NULL, NULL, NULL,
NULL,
use_stderr? NULL : &fd_stdio,
use_stderr? &fd_stdio : NULL,
NULL))
{
gpa_window_error (_("Calling the crypto engine program failed."), NULL);
xfree (parm);
g_free (freeme);
return gpg_error (GPG_ERR_GENERAL);
}
g_free (freeme);
#ifdef G_OS_WIN32
channel = g_io_channel_win32_new_fd (fd_stdio);
#else
channel = g_io_channel_unix_new (fd_stdio);
#endif
g_io_channel_set_encoding (channel, NULL, NULL);
/* Note that we need a buffered channel, so that we can use the read
line function. */
g_io_channel_set_close_on_unref (channel, TRUE);
/* Create a watch for the channel. */
if (!g_io_add_watch (channel, (G_IO_IN|G_IO_HUP),
gpg_simple_stdio_cb, parm))
{
g_debug ("error creating watch for gpg command");
g_io_channel_unref (channel);
xfree (parm);
return gpg_error (GPG_ERR_GENERAL);
}
return 0;
}
/* Try to start the gpg-agent if it has not yet been started.
Starting the agent works in the background. Thus if the function
returns, it is not sure that the agent is now running. */
void
gpa_start_agent (void)
{
gpg_error_t err;
gpgme_ctx_t ctx;
char *pgm;
const char *argv[3];
pgm = get_gpg_connect_agent_path ();
if (!pgm)
{
g_message ("tool to start the agent is not available");
return;
}
ctx = gpa_gpgme_new ();
gpgme_set_protocol (ctx, GPGME_PROTOCOL_SPAWN);
argv[0] = ""; /* Auto-insert the basename. */
argv[1] = "NOP";
argv[2] = NULL;
err = gpgme_op_spawn (ctx, pgm, argv, NULL, NULL, NULL, GPGME_SPAWN_DETACHED);
if (err)
g_message ("error running '%s': %s", pgm, gpg_strerror (err));
g_free (pgm);
gpgme_release (ctx);
}
/* Fucntions matching the user id verification isn gpg's key generation. */
const char *
gpa_validate_gpg_name (const char *name)
{
const char *result = NULL;
if (!name || !*name)
result = _("You must enter a name.");
else if (strpbrk (name, "<>"))
result = _("Invalid character in name.");
else if (g_ascii_isdigit (*name))
result = _("Name may not start with a digit.");
else if (g_utf8_strlen (name, -1) < 5)
result = _("Name is too short.");
return result;
}
/* Check whether the string has characters not valid in an RFC-822
address. To cope with OpenPGP we allow non-ascii characters
so that for example umlauts are legal in an email address. An
OpenPGP user ID must be utf-8 encoded but there is no strict
requirement for RFC-822. Thus to avoid IDNA encoding we put the
address verbatim as utf-8 into the user ID under the assumption
that mail programs handle IDNA at a lower level and take OpenPGP
user IDs as utf-8. */
static int
has_invalid_email_chars (const char *s)
{
int at_seen = 0;
const char *valid_chars=
"01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for ( ; *s; s++ )
{
if ((*s & 0x80))
continue; /* We only care about ASCII. */
if (*s == '@')
at_seen=1;
else if (!at_seen && !( !!strchr( valid_chars, *s ) || *s == '+' ))
return 1;
else if (at_seen && !strchr (valid_chars, *s))
return 1;
}
return 0;
}
static int
string_count_chr (const char *string, int c)
{
int count;
for (count=0; *string; string++ )
if ( *string == c )
count++;
return count;
}
/* Check whether NAME represents a valid mailbox according to RFC822
except for non-ascii utf-8 characters. Returns true if so. */
static int
is_valid_mailbox (const char *name)
{
return !( !name
|| !*name
|| has_invalid_email_chars (name)
|| string_count_chr (name,'@') != 1
|| *name == '@'
|| name[strlen(name)-1] == '@'
|| name[strlen(name)-1] == '.'
|| strstr (name, "..") );
}
const char *
gpa_validate_gpg_email (const char *email)
{
const char *result = NULL;
if (!email || !*email)
;
else if (!is_valid_mailbox (email))
result = _("Email address is not valid.");
return result;
}
const char *
gpa_validate_gpg_comment (const char *comment)
{
const char *result = NULL;
if (!comment || !*comment)
;
else if (strpbrk (comment, "()"))
result = _("Invalid character in comments.");
return result;
}
diff --git a/src/gpgmetools.h b/src/gpgmetools.h
index 8f08ae8..c6d4885 100644
--- a/src/gpgmetools.h
+++ b/src/gpgmetools.h
@@ -1,224 +1,225 @@
/* gpgmetools.h - additional gpgme support functions for GPA.
Copyright (C) 2002, Miguel Coca.
Copyright (C) 2005, 2008 g10 Code GmbH.
This file is part of GPA
GPA 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 3 of the License, or
(at your option) any later version.
GPA 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, see <http://www.gnu.org/licenses/>.
*/
/* A set of auxiliary functions for common tasks related to GPGME. */
#ifndef GPGMETOOLS_H
#define GPGMETOOLS_H
#include <gtk/gtk.h>
#include <gpgme.h>
/* Internal algorithm identifiers, describing which keys to
create. */
typedef enum
{
GPA_KEYGEN_ALGO_RSA_RSA,
GPA_KEYGEN_ALGO_RSA_ELGAMAL,
GPA_KEYGEN_ALGO_RSA,
GPA_KEYGEN_ALGO_DSA_ELGAMAL,
GPA_KEYGEN_ALGO_DSA,
GPA_KEYGEN_ALGO_VIA_CARD
} gpa_keygen_algo_t;
typedef struct
{
/* User ID. */
gchar *name;
gchar *email;
gchar *comment;
/* Algorithm. */
gpa_keygen_algo_t algo;
/* Key size. */
gint keysize;
/* The password to use. */
gchar *password;
/* Epiration date. It is only used if it is valid. */
GDate expire;
/* True if the encryption key is created with an backup file. */
gboolean backup;
/* Used to return an error description. */
char *r_error_desc;
} gpa_keygen_para_t;
/* Report an unexpected error in GPGME and quit the application.
Better to use the macro instead of the function. */
#define gpa_gpgme_error(err) \
do { _gpa_gpgme_error (err, __FILE__, __LINE__); } while (0)
void _gpa_gpgme_error (gpg_error_t err,
const char *file, int line) G_GNUC_NORETURN;
/* The same as gpa_gpgme_error, without quitting. */
#define gpa_gpgme_warning_ext(err,desc) \
do { _gpa_gpgme_warning (err, desc, __FILE__, __LINE__); } while (0)
#define gpa_gpgme_warning(err) \
do { _gpa_gpgme_warning (err, NULL, __FILE__, __LINE__); } while (0)
void _gpa_gpgme_warning (gpg_error_t err, const char *desc,
const char *file, int line);
/* Initialize a gpgme_ctx_t for use with GPA. */
gpgme_ctx_t gpa_gpgme_new (void);
/* Write the contents of the gpgme_data_t object to the file. Receives
a filehandle instead of the filename, so that the caller can make
sure the file is accesible before putting anything into data. */
void dump_data_to_file (gpgme_data_t data, FILE *file);
/* Not really a gpgme function, but needed in most places
dump_data_to_file is used. Opens a file for writing, asking the
user to overwrite if it exists and reporting any errors. Returns
NULL on failure, but you can assume the user has been informed of
the error (or maybe he just didn't want to overwrite!). */
FILE *gpa_fopen (const char *filename, GtkWidget *parent);
/* Do a gpgme_data_new_from_file and report any GPG_ERR_File_Error to
the user. */
gpg_error_t gpa_gpgme_data_new_from_file (gpgme_data_t *data,
const char *filename,
GtkWidget *parent);
/* Create a new gpgme_data_t from a file for writing, and return the
file descriptor for the file. Always reports all errors to the
user. The _direct variant does not check for overwriting. */
int gpa_open_output_direct (const char *filename, gpgme_data_t *data,
GtkWidget *parent);
int gpa_open_output (const char *filename, gpgme_data_t *data,
GtkWidget *parent);
/* Create a new gpgme_data_t from a file for reading, and return the
file descriptor for the file. Always reports all errors to the user. */
int gpa_open_input (const char *filename, gpgme_data_t *data,
GtkWidget *parent);
/* Write the contents of the gpgme_data_t into the clipboard. */
int dump_data_to_clipboard (gpgme_data_t data, GtkClipboard *clipboard);
/* Begin generation of a key with the given parameters. It prepares
the parameters required by Gpgme and returns whatever
gpgme_op_genkey_start returns. */
gpg_error_t gpa_generate_key_start (gpgme_ctx_t ctx,
gpa_keygen_para_t *params);
/* Backup a key. It exports both the public and secret keys to a
- file. Returns TRUE on success and FALSE on error. It displays
- errors to the user. */
-gboolean gpa_backup_key (const gchar *fpr, const char *filename);
+ file. IS_X509 tells the function that the fingerprint is from an
+ X.509 key. Returns TRUE on success and FALSE on error. It
+ displays errors to the user. */
+gboolean gpa_backup_key (const gchar *fpr, const char *filename, int is_x509);
gpa_keygen_para_t *gpa_keygen_para_new (void);
void gpa_keygen_para_free (gpa_keygen_para_t *params);
/* Ownertrust strings. */
const gchar *gpa_key_ownertrust_string (gpgme_key_t key);
/* Key validity strings. */
const gchar *gpa_key_validity_string (gpgme_key_t key);
/* This is the function called by GPGME when it wants a
passphrase. */
gpg_error_t gpa_passphrase_cb (void *hook, const char *uid_hint,
const char *passphrase_info,
int prev_was_bad, int fd);
/* Convenience functions to access key attributes, which need to be
filtered before being displayed to the user. */
/* Return the user ID string, making sure it is properly UTF-8
encoded. Allocates a new string, which must be freed with
g_free(). */
gchar *gpa_gpgme_key_get_userid (gpgme_user_id_t key);
/* Return the key fingerprint, properly formatted according to the key
version. Allocates a new string, which must be freed with
g_free(). This is based on code from GPAPA's extract_fingerprint. */
gchar *gpa_gpgme_key_format_fingerprint (const char *fpraw);
/* Return the short key ID of the indicated key. The returned string
is valid as long as the key is valid. */
const gchar *gpa_gpgme_key_get_short_keyid (gpgme_key_t key);
/* Convenience function to access key signature attibutes, much like
the previous ones. */
/* Return the user ID, making sure it is properly UTF-8 encoded.
Allocates a new string, which must be freed with g_free(). */
gchar *gpa_gpgme_key_sig_get_userid (gpgme_key_sig_t sig);
/* Return the short key ID of the indicated key. The returned string
is valid as long as the key is valid. */
const gchar *gpa_gpgme_key_sig_get_short_keyid (gpgme_key_sig_t sig);
/* Return a string with the status of the key signature. */
const gchar *gpa_gpgme_key_sig_get_sig_status (gpgme_key_sig_t sig,
GHashTable *revoked);
/* Return a string with the level of the key signature. */
const gchar *gpa_gpgme_key_sig_get_level (gpgme_key_sig_t sig);
/* Return a human readable string with the status of the signature
SIG. */
char *gpa_gpgme_get_signature_desc (gpgme_ctx_t ctx, gpgme_signature_t sig,
char **r_keydesc, gpgme_key_t *r_key);
/* Return a string listing the capabilities of a key. */
const gchar *gpa_get_key_capabilities_text (gpgme_key_t key);
/* Return a copy of the key array. */
gpgme_key_t *gpa_gpgme_copy_keyarray (gpgme_key_t *keys);
/* Release all keys in the array KEYS as weel as ARRY itself. */
void gpa_gpgme_release_keyarray (gpgme_key_t *keys);
/* Try switching to the gpg2 backend. */
void gpa_switch_to_gpg2 (const char *gpg_binary, const char *gpgsm_binary);
/* Return true if the gpg engine has at least version NEED_VERSION. */
int is_gpg_version_at_least (const char *need_version);
/* Run a simple gpg command. */
gpg_error_t gpa_start_simple_gpg_command (gboolean (*cb)
(void *opaque, char *line),
void *cb_arg,
gpgme_protocol_t protocol,
int use_stderr,
const char *first_arg, ...
) G_GNUC_NULL_TERMINATED;
void gpa_start_agent (void);
/* Funtions to check user inputs in the same way gpg does. */
const char *gpa_validate_gpg_name (const char *name);
const char *gpa_validate_gpg_email (const char *email);
const char *gpa_validate_gpg_comment (const char *comment);
#endif /*GPGMETOOLS_H*/
diff --git a/src/keymanager.c b/src/keymanager.c
index b9a5dbc..2c73777 100644
--- a/src/keymanager.c
+++ b/src/keymanager.c
@@ -1,1501 +1,1506 @@
/* keymanager.c - The Key Manager.
* Copyright (C) 2000, 2001 G-N-U GmbH.
* Copyright (C) 2005, 2008, 2009 g10 Code GmbH.
*
* This file is part of GPA.
*
* GPA 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 3 of the License, or
* (at your option) any later version.
*
* GPA 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, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <gpgme.h>
#include "gpa.h"
#include "gtktools.h"
#include "icons.h"
#include "helpmenu.h"
#include "gpawidgets.h"
#include "ownertrustdlg.h"
#include "keysigndlg.h"
#include "keygendlg.h"
#include "keygenwizard.h"
#include "keyeditdlg.h"
#include "keydeletedlg.h"
#include "keylist.h"
#include "siglist.h"
#include "gpgmetools.h"
#include "gpgmeedit.h"
#include "keytable.h"
#include "server-access.h"
#include "options.h"
#include "convert.h"
#include "gpasubkeylist.h"
#include "gpakeydeleteop.h"
#include "gpakeysignop.h"
#include "gpakeytrustop.h"
#include "gpaexportfileop.h"
#include "gpaexportclipop.h"
#include "gpaexportserverop.h"
#include "gpaimportfileop.h"
#include "gpaimportclipop.h"
#include "gpaimportserverop.h"
#include "gpabackupop.h"
#include "gpagenkeyadvop.h"
#include "gpagenkeysimpleop.h"
#include "gpa-key-details.h"
#include "keymanager.h"
#if ! GTK_CHECK_VERSION (2, 10, 0)
#define GTK_STOCK_SELECT_ALL "gtk-select-all"
#endif
/* Object's class definition. */
struct _GpaKeyManagerClass
{
GtkWindowClass parent_class;
};
/* Object definition. */
struct _GpaKeyManager
{
GtkWindow parent_instance;
/* The central list of keys. */
GpaKeyList *keylist;
/* The "Show Ownertrust" toggle button. */
GtkWidget *toggle_show;
/* The details widget. */
GtkWidget *details;
/* Idle handler id for updates of the details widget. Will be
nonzero whenever a handler is currently set and zero
otherwise. */
guint details_idle_id;
/* Labels in the status bar. */
GtkWidget *status_label;
GtkWidget *status_key_user;
GtkWidget *status_key_id;
/* The popup menu. */
GtkWidget *popup_menu;
/* List of sensitive widgets. See below. */
GList *selection_sensitive_actions;
/* The currently selected key. */
gpgme_key_t current_key;
/* Context used for retrieving the current key. */
GpaContext *ctx;
/* Hack: warn the selection callback to ignore changes. Don't, ever,
assign a value directly. Raise and lower it with increments. */
int freeze_selection;
};
/* There is only one instance of the card manager class. Use a global
variable to keep track of it. */
static GpaKeyManager *this_instance;
/* Prototype of a sensitivity callback. Return TRUE if the widget
should be sensitive, FALSE otherwise. The parameter is a pointer
to the instance. */
typedef gboolean (*sensitivity_func_t) (gpointer);
/* Local prototypes */
static int idle_update_details (gpointer param);
static void keyring_update_details (GpaKeyManager *self);
static void gpa_key_manager_finalize (GObject *object);
/************************************************************
******************* Implementation *********************
************************************************************/
/* A simple sensitivity callback mechanism.
The basic idea is that buttons (and other widgets like menu items
as well) should know when they should be sensitive or not. The
implementation here is very simple and quite specific for the
key manager's needs.
We maintain a list of sensitive widgets each of which has a
sensitivity callback associated with them as the "gpa_sensitivity"
data. The callback returns TRUE when the widget should be
sensitive and FALSE otherwise.
Whenever the selection in the key list widget changes we call
update_selection_sensitive_actions which iterates through the
widgets in the list, calls the sensitivity callback and changes the
widget's sensitivity accordingly. */
/* Add widget to the list of sensitive widgets of editor. */
static void
add_selection_sensitive_action (GpaKeyManager *self,
GtkAction *action,
sensitivity_func_t callback)
{
g_object_set_data (G_OBJECT (action), "gpa_sensitivity", callback);
self->selection_sensitive_actions
= g_list_append (self->selection_sensitive_actions, action);
}
/* Update the sensitivity of the widget data and pass param through to
the sensitivity callback. Usable as iterator function in
g_list_foreach. */
static void
update_selection_sensitive_action (gpointer data, gpointer param)
{
sensitivity_func_t func;
func = g_object_get_data (G_OBJECT (data), "gpa_sensitivity");
gtk_action_set_sensitive (GTK_ACTION (data), func (param));
}
/* Call update_selection_sensitive_widget for all widgets in the list
of sensitive widgets and pass self through as the user data
parameter. */
static void
update_selection_sensitive_actions (GpaKeyManager *self)
{
g_list_foreach (self->selection_sensitive_actions,
update_selection_sensitive_action, self);
}
/* Disable all the widgets in the list of sensitive widgets. To be used while
the selection changes. */
static void
disable_selection_sensitive_actions (GpaKeyManager *self)
{
GList *cur;
cur = self->selection_sensitive_actions;
while (cur)
{
gtk_action_set_sensitive (GTK_ACTION (cur->data), FALSE);
cur = g_list_next (cur);
}
}
/* Helper functions to cope with selections. */
/* Return TRUE if the key list widget of the key manager has at
least one selected item. Usable as a sensitivity callback. */
static gboolean
key_manager_has_selection (gpointer param)
{
GpaKeyManager *self = param;
return gpa_keylist_has_selection (self->keylist);
}
/* Return TRUE if the key list widget of the key manager has
exactly one selected item. Usable as a sensitivity callback. */
static gboolean
key_manager_has_single_selection (gpointer param)
{
GpaKeyManager *self = param;
return gpa_keylist_has_single_selection (self->keylist);
}
/* Return TRUE if the key list widget of the key manager has
exactly one selected OpenPGP item. Usable as a sensitivity
callback. */
static gboolean
key_manager_has_single_selection_OpenPGP (gpointer param)
{
GpaKeyManager *self = param;
int result = 0;
if (gpa_keylist_has_single_selection (self->keylist))
{
gpgme_key_t key = gpa_keylist_get_selected_key (self->keylist);
if (key && key->protocol == GPGME_PROTOCOL_OpenPGP)
result = 1;
gpgme_key_unref (key);
}
return result;
}
/* Return TRUE if the key list widget of the key manager has
exactly one selected item and it is a private key. Usable as a
sensitivity callback. */
static gboolean
key_manager_has_private_selected (gpointer param)
{
GpaKeyManager *self = param;
return gpa_keylist_has_single_secret_selection
(GPA_KEYLIST(self->keylist));
}
/* Return the the currently selected key. NULL if no key is selected. */
static gpgme_key_t
key_manager_current_key (GpaKeyManager *self)
{
return self->current_key;
}
/* Action callbacks. */
static void
gpa_key_manager_changed_wot_cb (gpointer data)
{
GpaKeyManager *self = data;
gpa_keylist_start_reload (self->keylist);
}
static void
gpa_key_manager_changed_wot_secret_cb (gpointer data)
{
GpaKeyManager *self = data;
gpa_keylist_imported_secret_key (self->keylist);
gpa_keylist_start_reload (self->keylist);
}
static void
gpa_key_manager_key_modified (GpaKeyEditDialog *dialog, gpgme_key_t key,
gpointer data)
{
GpaKeyManager *self = data;
gpa_keylist_start_reload (self->keylist);
}
static void
gpa_key_manager_new_key_cb (gpointer data, const gchar *fpr)
{
GpaKeyManager *self = data;
gpa_keylist_new_key (GPA_KEYLIST (self->keylist), fpr);
gpa_options_update_default_key (gpa_options_get_instance ());
}
static void
register_key_operation (GpaKeyManager *self, GpaKeyOperation *op)
{
g_signal_connect_swapped (G_OBJECT (op), "changed_wot",
G_CALLBACK (gpa_key_manager_changed_wot_cb),
self);
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), self);
}
static void
register_import_operation (GpaKeyManager *self, GpaImportOperation *op)
{
g_signal_connect_swapped (G_OBJECT (op), "imported_keys",
G_CALLBACK (gpa_key_manager_changed_wot_cb),
self);
g_signal_connect_swapped
(G_OBJECT (op), "imported_secret_keys",
G_CALLBACK (gpa_key_manager_changed_wot_secret_cb),
self);
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), self);
}
static void
register_generate_operation (GpaKeyManager *self, GpaGenKeyOperation *op)
{
g_signal_connect_swapped (G_OBJECT (op), "generated_key",
G_CALLBACK (gpa_key_manager_new_key_cb),
self);
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), self);
}
static void
register_operation (GpaKeyManager *self, GpaOperation *op)
{
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), self);
}
/* delete the selected keys */
static void
key_manager_delete (GtkAction *action, GpaKeyManager *self)
{
GList *selection = gpa_keylist_get_selected_keys (self->keylist);
GpaKeyDeleteOperation *op = gpa_key_delete_operation_new (GTK_WIDGET (self),
selection);
register_key_operation (self, GPA_KEY_OPERATION (op));
}
/* Return true if the public key key has been signed by the key with
the id key_id, otherwise return FALSE. */
static gboolean
key_has_been_signed (const gpgme_key_t key,
const gpgme_key_t signer_key)
{
gboolean uid_signed, key_signed;
const char *signer_id;
gpgme_key_sig_t sig;
gpgme_user_id_t uid;
signer_id = signer_key->subkeys->keyid;
/* We consider the key signed if all user IDs have been signed. */
key_signed = TRUE;
for (uid = key->uids; key_signed && uid; uid = uid->next)
{
uid_signed = FALSE;
for (sig = uid->signatures; !uid_signed && sig; sig = sig->next)
if (g_str_equal (signer_id, sig->keyid))
uid_signed = TRUE;
key_signed = key_signed && uid_signed;
}
return key_signed;
}
/* Return true if the key sign button should be sensitive, i.e. if
there is at least one selected key and there is a default key. */
static gboolean
key_manager_can_sign (gpointer param)
{
gboolean result = FALSE;
gpgme_key_t default_key;
default_key = gpa_options_get_default_key (gpa_options_get_instance ());
if (default_key && key_manager_has_single_selection (param))
{
/* The most important requirements have been met, now check if
the selected key was already signed with the default key. */
GpaKeyManager *self = param;
gpgme_key_t key = key_manager_current_key (self);
result = ! key_has_been_signed (key, default_key);
}
else if (default_key && key_manager_has_selection (param))
/* Always allow signing many keys at once. */
result = TRUE;
return result;
}
/* sign the selected keys */
static void
key_manager_sign (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
GList *selection;
GpaKeySignOperation *op;
if (! gpa_keylist_has_selection (self->keylist))
{
/* This shouldn't happen because the button should be grayed out
in this case. */
gpa_window_error (_("No keys selected for signing."), GTK_WIDGET (self));
return;
}
selection = gpa_keylist_get_selected_keys (self->keylist);
op = gpa_key_sign_operation_new (GTK_WIDGET (self), selection);
register_key_operation (self, GPA_KEY_OPERATION (op));
}
/* Invoke the "edit key" dialog. */
static void
key_manager_edit (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
gpgme_key_t key;
GtkWidget *dialog;
if (! key_manager_has_private_selected (self))
return;
key = key_manager_current_key (self);
if (! key)
return;
dialog = gpa_key_edit_dialog_new (GTK_WIDGET (self), key);
gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
g_signal_connect (G_OBJECT (dialog), "key_modified",
G_CALLBACK (gpa_key_manager_key_modified), self);
gtk_widget_show_all (dialog);
}
static void
key_manager_trust (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
GList *selection;
GpaKeyTrustOperation *op;
/* FIXME: Key trust operation currently does not support more than
one key at a time. */
if (! key_manager_has_single_selection (self))
return;
selection = gpa_keylist_get_selected_keys (self->keylist);
op = gpa_key_trust_operation_new (GTK_WIDGET (self), selection);
register_key_operation (self, GPA_KEY_OPERATION (op));
}
/* Import keys. */
static void
key_manager_import (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
GpaImportFileOperation *op;
op = gpa_import_file_operation_new (GTK_WIDGET (self));
register_import_operation (self, GPA_IMPORT_OPERATION (op));
}
/* Export the selected keys to a file. */
static void
key_manager_export (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
GList *selection;
GpaExportFileOperation *op;
selection = gpa_keylist_get_selected_keys (self->keylist);
if (! selection)
return;
op = gpa_export_file_operation_new (GTK_WIDGET (self), selection);
register_operation (self, GPA_OPERATION (op));
}
/* Import a key from the keyserver. */
#ifdef ENABLE_KEYSERVER_SUPPORT
static void
key_manager_retrieve (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
GpaImportServerOperation *op;
op = gpa_import_server_operation_new (GTK_WIDGET (self));
register_import_operation (self, GPA_IMPORT_OPERATION (op));
}
#endif /*ENABLE_KEYSERVER_SUPPORT*/
/* Send a key to the keyserver. */
#ifdef ENABLE_KEYSERVER_SUPPORT
static void
key_manager_send (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
GList *selection;
GpaExportServerOperation *op;
/* FIXME: The export-to-server operation currently only supports
exporting one key at a time. */
if (! key_manager_has_single_selection (self))
return;
selection = gpa_keylist_get_selected_keys (self->keylist);
op = gpa_export_server_operation_new (GTK_WIDGET (self), selection);
register_operation (self, GPA_OPERATION (op));
}
#endif /*ENABLE_KEYSERVER_SUPPORT*/
/* Backup the default keys. */
static void
key_manager_backup (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
gpgme_key_t key;
GpaBackupOperation *op;
if (! key_manager_has_private_selected (self))
return;
key = key_manager_current_key (self);
if (! key)
return;
op = gpa_backup_operation_new (GTK_WIDGET (self), key);
register_operation (self, GPA_OPERATION (op));
}
/* Run the advanced key generation dialog and if the user clicked OK,
generate a new key pair and update the key list. */
static void
key_manager_generate_key_advanced (gpointer param)
{
GpaKeyManager *self = param;
GpaGenKeyAdvancedOperation *op;
op = gpa_gen_key_advanced_operation_new (GTK_WIDGET (self));
register_generate_operation (self, GPA_GEN_KEY_OPERATION (op));
}
/* Call the key generation wizard and update the key list if necessary */
static void
key_manager_generate_key_simple (gpointer param)
{
GpaKeyManager *self = param;
GpaGenKeySimpleOperation *op;
op = gpa_gen_key_simple_operation_new (GTK_WIDGET (self));
register_generate_operation (self, GPA_GEN_KEY_OPERATION (op));
}
/* Generate a key. */
static void
key_manager_generate_key (GtkAction *action, gpointer param)
{
if (gpa_options_get_simplified_ui (gpa_options_get_instance ()))
key_manager_generate_key_simple (param);
else
key_manager_generate_key_advanced (param);
}
/* Update everything that has to be updated when the selection in the
key list changes. */
static void
keyring_selection_update_actions (GpaKeyManager *self)
{
update_selection_sensitive_actions (self);
keyring_update_details (self);
}
/* Callback for key listings invoked with the "next_key" signal. Used
to receive and set the new current key. */
static void
key_manager_key_listed (GpaContext *ctx, gpgme_key_t key, gpointer param)
{
GpaKeyManager *self = param;
gpgme_key_unref (self->current_key);
self->current_key = key;
keyring_selection_update_actions (self);
}
/* FIXME: CHECK! Signal handler for selection changes. */
static void
key_manager_selection_changed (GtkTreeSelection *treeselection,
gpointer param)
{
GpaKeyManager *self = param;
/* Some other piece of the keyring wants us to ignore this signal. */
if (self->freeze_selection)
return;
/* Update the current key. */
if (self->current_key)
{
/* Remove the previous one. */
gpgme_key_unref (self->current_key);
self->current_key = NULL;
}
/* Abort retrieval of the current key. */
if (gpa_context_busy (self->ctx))
gpgme_op_keylist_end (self->ctx->ctx);
/* Load the new one. */
if (gpa_keylist_has_single_selection (self->keylist))
{
gpg_error_t err;
GList *selection;
gpgme_key_t key;
int old_mode;
selection = gpa_keylist_get_selected_keys (self->keylist);
key = (gpgme_key_t) selection->data;
old_mode = gpgme_get_keylist_mode (self->ctx->ctx);
/* With all the signatures and validating for the sake of X.509.
Note that we should not save and restore the old protocol
because the protocol should not be changed before the
gpgme_op_keylist_end. Saving and restoring the keylist mode
is okay. */
gpgme_set_keylist_mode (self->ctx->ctx,
(old_mode
| GPGME_KEYLIST_MODE_SIGS
| GPGME_KEYLIST_MODE_VALIDATE));
gpgme_set_protocol (self->ctx->ctx, key->protocol);
err = gpgme_op_keylist_start (self->ctx->ctx, key->subkeys->fpr,
FALSE);
if (gpg_err_code (err) != GPG_ERR_NO_ERROR)
gpa_gpgme_warning (err);
gpgme_set_keylist_mode (self->ctx->ctx, old_mode);
g_list_free (selection);
/* Make sure the actions that depend on a current key are
disabled. */
disable_selection_sensitive_actions (self);
}
else
keyring_selection_update_actions (self);
}
/* FIXME: CHECK THIS!
Signal handler for the map signal. If the simplified_ui flag is
set and there's no private key in the key ring, ask the user
whether he wants to generate a key. If so, call
key_manager_generate_key which runs the appropriate dialog.
Also, if the simplified_ui flag is set, remind the user if he has
not yet created a backup copy of his private key. */
static void
key_manager_mapped (gpointer param)
{
static gboolean asked_about_key_generation = FALSE;
static gboolean asked_about_key_backup = FALSE;
GpaKeyManager *self = param;
if (gpa_options_get_simplified_ui (gpa_options_get_instance ()))
{
/* FIXME: We assume that the only reason a user might not have a
default key is because he has no private keys. */
if (! asked_about_key_generation
&& ! gpa_options_get_default_key (gpa_options_get_instance()))
{
GtkWidget *dialog;
GtkResponseType response;
dialog = gtk_message_dialog_new (GTK_WINDOW (GTK_WIDGET (self)),
GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
_("You do not have a private key "
"yet. Do you want to generate "
"one now (recommended) or do it"
" later?"));
gtk_dialog_add_buttons (GTK_DIALOG (dialog), _("_Generate key now"),
GTK_RESPONSE_OK, _("Do it _later"),
GTK_RESPONSE_CANCEL, NULL);
response = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
if (response == GTK_RESPONSE_OK)
key_manager_generate_key (NULL, param);
asked_about_key_generation = TRUE;
}
else if (!asked_about_key_backup
&& !gpa_options_get_backup_generated
(gpa_options_get_instance ())
&& !gpa_options_get_default_key (gpa_options_get_instance()))
{
GtkWidget *dialog;
GtkResponseType response;
dialog = gtk_message_dialog_new (GTK_WINDOW (GTK_WIDGET (self)),
GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
_("You do not have a backup copy of"
" your private key yet."
" Do you want to backup your key "
"now (recommended) or do it "
"later?"));
gtk_dialog_add_buttons (GTK_DIALOG (dialog), _("_Backup key now"),
GTK_RESPONSE_OK, _("Do it _later"),
GTK_RESPONSE_CANCEL, NULL);
response = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
if (response == GTK_RESPONSE_OK)
{
- GpaBackupOperation *op = gpa_backup_operation_new
- (GTK_WIDGET (self), gpa_options_get_default_key
- (gpa_options_get_instance ()));
- register_operation (self, GPA_OPERATION (op));
+ gpgme_key_t key;
+ GpaBackupOperation *op;
+
+ key = gpa_options_get_default_key (gpa_options_get_instance ());
+ if (key)
+ {
+ op = gpa_backup_operation_new (GTK_WIDGET (self), key);
+ register_operation (self, GPA_OPERATION (op));
+ }
}
asked_about_key_backup = TRUE;
}
}
}
/* Close the key manager. */
static void
key_manager_close (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
gtk_widget_destroy (GTK_WIDGET (self));
}
/* select all keys in the keyring */
static void
key_manager_select_all (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->keylist));
gtk_tree_selection_select_all (selection);
}
/* Paste the clipboard into the keyring. */
static void
key_manager_paste (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
GpaImportClipboardOperation *op;
op = gpa_import_clipboard_operation_new (GTK_WIDGET (self));
register_import_operation (self, GPA_IMPORT_OPERATION (op));
}
/* Copy the keys into the clipboard. */
static void
key_manager_copy (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
GList *selection;
GpaExportClipboardOperation *op;
selection = gpa_keylist_get_selected_keys (self->keylist);
if (! selection)
return;
op = gpa_export_clipboard_operation_new (GTK_WIDGET (self), selection);
register_operation (self, GPA_OPERATION (op));
}
/* Reload the key list. */
static void
key_manager_refresh (GtkAction *action, gpointer param)
{
GpaKeyManager *self = param;
gpa_keylist_start_reload (self->keylist);
}
static void
keyring_set_listing_cb (GtkAction *action,
GtkRadioAction *current_action, gpointer param)
{
GpaKeyManager *self = param;
gint detailed;
detailed = gtk_radio_action_get_current_value
(GTK_RADIO_ACTION (current_action));
if (detailed)
{
gpa_keylist_set_detailed (self->keylist);
gpa_options_set_detailed_view (gpa_options_get_instance (), TRUE);
}
else
{
gpa_keylist_set_brief (self->keylist);
gpa_options_set_detailed_view (gpa_options_get_instance (), FALSE);
}
}
/* Create and return the menu bar for the key ring editor. */
static void
key_manager_action_new (GpaKeyManager *self,
GtkWidget **menu, GtkWidget **toolbar,
GtkWidget **popup)
{
static const GtkActionEntry entries[] =
{
/* Toplevel. */
{ "File", NULL, N_("_File"), NULL },
{ "Edit", NULL, N_("_Edit"), NULL },
{ "Keys", NULL, N_("_Keys"), NULL },
#ifdef ENABLE_KEYSERVER_SUPPORT
{ "Server", NULL, N_("_Server"), NULL },
#endif
/* File menu. */
{ "FileClose", GTK_STOCK_CLOSE, NULL, NULL,
"FIXME", G_CALLBACK (key_manager_close) },
{ "FileQuit", GTK_STOCK_QUIT, NULL, NULL,
N_("Quit the program"), G_CALLBACK (gtk_main_quit) },
/* Edit menu. */
{ "EditCopy", GTK_STOCK_COPY, NULL, NULL,
N_("Copy the selection"), G_CALLBACK (key_manager_copy) },
{ "EditPaste", GTK_STOCK_PASTE, NULL, NULL,
N_("Paste the clipboard"), G_CALLBACK (key_manager_paste) },
{ "EditSelectAll", GTK_STOCK_SELECT_ALL, NULL, "<control>A",
N_("Select all certificates"),
G_CALLBACK (key_manager_select_all) },
/* Keys menu. */
{ "KeysRefresh", GTK_STOCK_REFRESH, NULL, NULL,
N_("Refresh the keyring"), G_CALLBACK (key_manager_refresh) },
{ "KeysNew", GTK_STOCK_NEW, N_("_New key..."), NULL,
N_("Generate a new key"), G_CALLBACK (key_manager_generate_key) },
{ "KeysDelete", GTK_STOCK_DELETE, N_("_Delete keys"), NULL,
N_("Remove the selected key"), G_CALLBACK (key_manager_delete) },
{ "KeysSign", GPA_STOCK_SIGN, N_("_Sign Keys..."), NULL,
N_("Sign the selected key"), G_CALLBACK (key_manager_sign) },
{ "KeysSetOwnerTrust", NULL, N_("Set _Owner Trust..."), NULL,
N_("Set owner trust of the selected key"),
G_CALLBACK (key_manager_trust) },
{ "KeysEditPrivateKey", GPA_STOCK_EDIT, N_("_Edit Private Key..."), NULL,
N_("Edit the selected private key"),
G_CALLBACK (key_manager_edit) },
{ "KeysImport", GPA_STOCK_IMPORT, N_("_Import Keys..."), NULL,
N_("Import Keys"), G_CALLBACK (key_manager_import) },
{ "KeysExport", GPA_STOCK_EXPORT, N_("E_xport Keys..."), NULL,
N_("Export Keys"), G_CALLBACK (key_manager_export) },
{ "KeysBackup", NULL, N_("_Backup..."), NULL,
N_("Backup key"), G_CALLBACK (key_manager_backup) },
/* Server menu. */
#ifdef ENABLE_KEYSERVER_SUPPORT
{ "ServerRetrieve", NULL, N_("_Retrieve Keys..."), NULL,
N_("Retrieve keys from server"),
G_CALLBACK (key_manager_retrieve) },
{ "ServerSend", NULL, N_("_Send Keys..."), NULL,
N_("Send keys to server"), G_CALLBACK (key_manager_send) }
#endif /*ENABLE_KEYSERVER_SUPPORT*/
};
static const GtkRadioActionEntry radio_entries[] =
{
{ "DetailsBrief", GPA_STOCK_BRIEF, NULL, NULL,
N_("Show Brief Keylist"), 0 },
{ "DetailsDetailed", GPA_STOCK_DETAILED, NULL, NULL,
N_("Show Key Details"), 1 }
};
static const char *ui_description =
"<ui>"
" <menubar name='MainMenu'>"
" <menu action='File'>"
" <menuitem action='FileClose'/>"
" <menuitem action='FileQuit'/>"
" </menu>"
" <menu action='Edit'>"
" <menuitem action='EditCopy'/>"
" <menuitem action='EditPaste'/>"
" <separator/>"
" <menuitem action='EditSelectAll'/>"
" <separator/>"
" <menuitem action='EditPreferences'/>"
" <menuitem action='EditBackendPreferences'/>"
" </menu>"
" <menu action='Keys'>"
" <menuitem action='KeysRefresh'/>"
" <separator/>"
" <menuitem action='KeysNew'/>"
" <menuitem action='KeysDelete'/>"
" <separator/>"
" <menuitem action='KeysSign'/>"
" <menuitem action='KeysSetOwnerTrust'/>"
" <menuitem action='KeysEditPrivateKey'/>"
" <separator/>"
" <menuitem action='KeysImport'/>"
" <menuitem action='KeysExport'/>"
" <menuitem action='KeysBackup'/>"
" </menu>"
" <menu action='Windows'>"
" <menuitem action='WindowsKeyringEditor'/>"
" <menuitem action='WindowsFileManager'/>"
" <menuitem action='WindowsClipboard'/>"
" <menuitem action='WindowsCardManager'/>"
" </menu>"
#ifdef ENABLE_KEYSERVER_SUPPORT
" <menu action='Server'>"
" <menuitem action='ServerRetrieve'/>"
" <menuitem action='ServerSend'/>"
" </menu>"
#endif /*ENABLE_KEYSERVER_SUPPORT*/
" <menu action='Help'>"
#if 0
" <menuitem action='HelpContents'/>"
#endif
" <menuitem action='HelpAbout'/>"
" </menu>"
" </menubar>"
" <toolbar name='ToolBar'>"
" <toolitem action='KeysEditPrivateKey'/>"
" <toolitem action='KeysDelete'/>"
" <toolitem action='KeysSign'/>"
" <toolitem action='KeysImport'/>"
" <toolitem action='KeysExport'/>"
" <separator/>"
" <toolitem action='DetailsBrief'/>"
" <toolitem action='DetailsDetailed'/>"
" <separator/>"
" <toolitem action='EditPreferences'/>"
" <separator/>"
" <toolitem action='KeysRefresh'/>"
" <separator/>"
" <toolitem action='WindowsFileManager'/>"
" <toolitem action='WindowsClipboard'/>"
" <toolitem action='WindowsCardManager'/>"
#if 0
" <toolitem action='HelpContents'/>"
#endif
" </toolbar>"
" <popup name='PopupMenu'>"
" <menuitem action='EditCopy'/>"
" <menuitem action='EditPaste'/>"
" <menuitem action='KeysDelete'/>"
" <separator/>"
" <menuitem action='KeysSign'/>"
" <menuitem action='KeysSetOwnerTrust'/>"
" <menuitem action='KeysEditPrivateKey'/>"
" <separator/>"
" <menuitem action='KeysExport'/>"
#ifdef ENABLE_KEYSERVER_SUPPORT
" <menuitem action='ServerSend'/>"
#endif
" <menuitem action='KeysBackup'/>"
" </popup>"
"</ui>";
GtkAccelGroup *accel_group;
GtkActionGroup *action_group;
GtkAction *action;
GtkUIManager *ui_manager;
GError *error;
int detailed;
detailed = gpa_options_get_detailed_view (gpa_options_get_instance());
action_group = gtk_action_group_new ("MenuActions");
gtk_action_group_set_translation_domain (action_group, PACKAGE);
gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries),
self);
gtk_action_group_add_radio_actions (action_group, radio_entries,
G_N_ELEMENTS (radio_entries),
detailed ? 1 : 0,
G_CALLBACK (keyring_set_listing_cb),
self);
gtk_action_group_add_actions (action_group, gpa_help_menu_action_entries,
G_N_ELEMENTS (gpa_help_menu_action_entries),
GTK_WIDGET (self));
gtk_action_group_add_actions (action_group, gpa_windows_menu_action_entries,
G_N_ELEMENTS (gpa_windows_menu_action_entries),
GTK_WIDGET (self));
gtk_action_group_add_actions
(action_group, gpa_preferences_menu_action_entries,
G_N_ELEMENTS (gpa_preferences_menu_action_entries), GTK_WIDGET (self));
ui_manager = gtk_ui_manager_new ();
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
accel_group = gtk_ui_manager_get_accel_group (ui_manager);
gtk_window_add_accel_group (GTK_WINDOW (GTK_WIDGET (self)), accel_group);
if (! gtk_ui_manager_add_ui_from_string (ui_manager, ui_description,
-1, &error))
{
g_message ("building keyring menus failed: %s", error->message);
g_error_free (error);
exit (EXIT_FAILURE);
}
/* Fixup the icon theme labels which are too long for the toolbar. */
action = gtk_action_group_get_action (action_group, "KeysEditPrivateKey");
g_object_set (action, "short_label", _("Edit"), NULL);
action = gtk_action_group_get_action (action_group, "KeysDelete");
g_object_set (action, "short_label", _("Delete"), NULL);
action = gtk_action_group_get_action (action_group, "KeysSign");
g_object_set (action, "short_label", _("Sign"), NULL);
action = gtk_action_group_get_action (action_group, "KeysExport");
g_object_set (action, "short_label", _("Export"), NULL);
action = gtk_action_group_get_action (action_group, "KeysImport");
g_object_set (action, "short_label", _("Import"), NULL);
action = gtk_action_group_get_action (action_group, "WindowsFileManager");
g_object_set (action, "short_label", _("Files"), NULL);
action = gtk_action_group_get_action (action_group, "WindowsCardManager");
g_object_set (action, "short_label", _("Card"), NULL);
/* Take care of sensitiveness of widgets. */
action = gtk_action_group_get_action (action_group, "EditCopy");
add_selection_sensitive_action (self, action,
key_manager_has_selection);
action = gtk_action_group_get_action (action_group, "KeysDelete");
add_selection_sensitive_action (self, action,
key_manager_has_selection);
action = gtk_action_group_get_action (action_group, "KeysExport");
add_selection_sensitive_action (self, action,
key_manager_has_selection);
#ifdef ENABLE_KEYSERVER_SUPPORT
action = gtk_action_group_get_action (action_group, "ServerSend");
add_selection_sensitive_action (self, action,
key_manager_has_single_selection);
#endif /*ENABLE_KEYSERVER_SUPPORT*/
action = gtk_action_group_get_action (action_group, "KeysSetOwnerTrust");
add_selection_sensitive_action (self, action,
key_manager_has_single_selection_OpenPGP);
action = gtk_action_group_get_action (action_group, "KeysSign");
add_selection_sensitive_action (self, action,
key_manager_can_sign);
action = gtk_action_group_get_action (action_group, "KeysEditPrivateKey");
add_selection_sensitive_action (self, action,
key_manager_has_private_selected);
action = gtk_action_group_get_action (action_group, "KeysBackup");
add_selection_sensitive_action (self, action,
key_manager_has_private_selected);
*menu = gtk_ui_manager_get_widget (ui_manager, "/MainMenu");
*toolbar = gtk_ui_manager_get_widget (ui_manager, "/ToolBar");
gpa_toolbar_set_homogeneous (GTK_TOOLBAR (*toolbar), FALSE);
*popup = gtk_ui_manager_get_widget (ui_manager, "/PopupMenu");
}
/* Update the details widget according to the current selection. This
means that if there is exactly one key selected, display its
properties in the pages, otherwise show the number of currently
selected keys. */
static int
idle_update_details (gpointer param)
{
GpaKeyManager *self = param;
if (gpa_keylist_has_single_selection (self->keylist))
{
gpgme_key_t key = key_manager_current_key (self);
if (! key)
{
/* There is a single key selected, but the current key is
NULL. This means the key has not been returned yet, so
we exit the function asking GTK to run it again when
there is time. */
return TRUE;
}
gpa_key_details_update (self->details, key, 1);
}
else
{
GList *selection = gpa_keylist_get_selected_keys (self->keylist);
gpa_key_details_update (self->details, NULL,
g_list_length (selection));
g_list_free (selection);
}
/* Set the idle id to NULL to indicate that the idle handler has
been run. */
self->details_idle_id = 0;
/* Return false to indicate that this function shouldn't be called
again by GTK, only when we expicitly add it again. */
return FALSE;
}
/* Add an idle handler to update the details, but only when none has
been set yet. */
static void
keyring_update_details (GpaKeyManager *self)
{
if (! self->details_idle_id)
self->details_idle_id = g_idle_add (idle_update_details, self);
}
/* Status bar handling. */
static GtkWidget *
keyring_statusbar_new (GpaKeyManager *self)
{
GtkWidget *hbox;
GtkWidget *label;
hbox = gtk_hbox_new (FALSE, 0);
label = gtk_label_new ("");
self->status_label = label;
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
label = gtk_label_new ("");
self->status_key_id = label;
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 5);
label = gtk_label_new ("");
self->status_key_user = label;
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
return hbox;
}
/* Update the status bar. */
static void
keyring_update_status_bar (GpaKeyManager *self)
{
gpgme_key_t key = gpa_options_get_default_key (gpa_options_get_instance ());
if (key)
{
gchar *string;
gtk_label_set_text (GTK_LABEL (self->status_label),
_("Selected default key:"));
string = gpa_gpgme_key_get_userid (key->uids);
gtk_label_set_text (GTK_LABEL (self->status_key_user), string);
g_free (string);
gtk_label_set_text (GTK_LABEL (self->status_key_id),
gpa_gpgme_key_get_short_keyid (key));
}
else
{
gtk_label_set_text (GTK_LABEL (self->status_label),
_("No default key selected in the preferences."));
gtk_label_set_text (GTK_LABEL (self->status_key_user), "");
gtk_label_set_text (GTK_LABEL (self->status_key_id), "");
}
}
/* FIXME: Check. */
/* The context menu of the keyring list. This is the callback for the
"button_press_event" signal. */
static gint
display_popup_menu (GpaKeyManager *self, GdkEvent *event, GpaKeyList *list)
{
GtkMenu *menu;
GdkEventButton *event_button;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (event != NULL, FALSE);
menu = GTK_MENU (self->popup_menu);
if (event->type == GDK_BUTTON_PRESS)
{
event_button = (GdkEventButton *) event;
if (event_button->button == 3)
{
GtkTreeSelection *selection =
gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
GtkTreePath *path;
GtkTreeIter iter;
/* Make sure the clicked key is selected. */
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (list),
event_button->x,
event_button->y,
&path, NULL,
NULL, NULL))
{
gtk_tree_model_get_iter (gtk_tree_view_get_model
(GTK_TREE_VIEW(list)), &iter, path);
if (! gtk_tree_selection_iter_is_selected (selection, &iter))
{
/* Block selection updates. */
self->freeze_selection++;
gtk_tree_selection_unselect_all (selection);
self->freeze_selection--;
gtk_tree_selection_select_path (selection, path);
}
gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
event_button->button, event_button->time);
}
return TRUE;
}
}
return FALSE;
}
/* Signal handler for the "changed_default_key" signal. */
static void
keyring_default_key_changed (GpaOptions *options, gpointer param)
{
GpaKeyManager *self = param;
/* Update the status bar and the selection sensitive widgets because
some depend on the default key. */
keyring_update_status_bar (self);
update_selection_sensitive_actions (self);
}
/* Create all the widgets of this window. */
static void
construct_widgets (GpaKeyManager *self)
{
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *scrolled;
GtkWidget *keylist;
GtkWidget *menubar;
GtkWidget *toolbar;
GtkWidget *hbox;
GtkWidget *icon;
GtkWidget *paned;
GtkWidget *statusbar;
GtkWidget *main_box;
GtkWidget *align;
gchar *markup;
guint pt, pb, pl, pr;
gpa_window_set_title (GTK_WINDOW (self), _("Key Manager"));
gtk_window_set_default_size (GTK_WINDOW (self), 680, 600);
g_signal_connect_swapped (G_OBJECT (self), "map",
G_CALLBACK (key_manager_mapped), self);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (self), vbox);
/* FIXME: Check next line. */
key_manager_action_new (self, &menubar, &toolbar, &self->popup_menu);
gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0);
/* Add a fancy label that tells us: This is the key manager. */
/* FIXME: We should have a common function for this. */
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
icon = gtk_image_new_from_stock (GPA_STOCK_KEYMAN_SIMPLE,
GTK_ICON_SIZE_DND);
gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0);
label = gtk_label_new (NULL);
markup = g_strdup_printf ("<span font_desc=\"16\">%s</span>",
_("Key Manager"));
gtk_label_set_markup (GTK_LABEL (label), markup);
g_free (markup);
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 10);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
paned = gtk_vpaned_new ();
main_box = gtk_hbox_new (TRUE, 0);
align = gtk_alignment_new (0.5, 0.5, 1, 1);
gtk_alignment_get_padding (GTK_ALIGNMENT (align), &pt, &pb, &pl, &pr);
gtk_alignment_set_padding (GTK_ALIGNMENT (align), pt, pb + 5,
pl + 5, pr + 5);
gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (main_box), paned, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (align), main_box);
scrolled = gtk_scrolled_window_new (NULL, NULL);
gtk_paned_pack1 (GTK_PANED (paned), scrolled, TRUE, TRUE);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
/* FIXME: Which shadow type - get it from a global resource? */
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
GTK_SHADOW_IN);
keylist = gpa_keylist_new (GTK_WIDGET (self));
self->keylist = GPA_KEYLIST (keylist);
if (gpa_options_get_detailed_view (gpa_options_get_instance()))
gpa_keylist_set_detailed (self->keylist);
else
gpa_keylist_set_brief (self->keylist);
gtk_container_add (GTK_CONTAINER (scrolled), keylist);
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection
(GTK_TREE_VIEW (keylist))),
"changed", G_CALLBACK (key_manager_selection_changed),
self);
g_signal_connect_swapped (G_OBJECT (keylist), "button_press_event",
G_CALLBACK (display_popup_menu), self);
self->details = gpa_key_details_new ();
gtk_paned_pack2 (GTK_PANED (paned), self->details, TRUE, TRUE);
gtk_paned_set_position (GTK_PANED (paned), 250);
statusbar = keyring_statusbar_new (self);
gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, TRUE, 0);
g_signal_connect (G_OBJECT (gpa_options_get_instance ()),
"changed_default_key",
G_CALLBACK (keyring_default_key_changed), self);
keyring_update_status_bar (self);
update_selection_sensitive_actions (self);
keyring_update_details (self);
self->current_key = NULL;
self->ctx = gpa_context_new ();
self->freeze_selection = 0;
g_signal_connect (G_OBJECT (self->ctx), "next_key",
G_CALLBACK (key_manager_key_listed), self);
}
/* Callback for the "destroy" signal. */
static void
gpa_key_manager_closed (GtkWidget *widget, gpointer param)
{
this_instance = NULL;
}
/************************************************************
****************** Object Management ********************
************************************************************/
static void
gpa_key_manager_class_init (void *class_ptr, void *class_data)
{
GpaKeyManagerClass *klass = class_ptr;
G_OBJECT_CLASS (klass)->finalize = gpa_key_manager_finalize;
}
static void
gpa_key_manager_init (GTypeInstance *instance, void *class_ptr)
{
GpaKeyManager *self = GPA_KEY_MANAGER (instance);
construct_widgets (self);
g_signal_connect (self, "destroy",
G_CALLBACK (gpa_key_manager_closed), self);
}
static void
gpa_key_manager_finalize (GObject *object)
{
GpaKeyManager *self = GPA_KEY_MANAGER (object);
g_list_free (self->selection_sensitive_actions);
self->selection_sensitive_actions = NULL;
G_OBJECT_CLASS (g_type_class_peek_parent
(GPA_KEY_MANAGER_GET_CLASS (self)))->finalize (object);
}
GType
gpa_key_manager_get_type (void)
{
static GType this_type = 0;
if (!this_type)
{
static const GTypeInfo this_info =
{
sizeof (GpaKeyManagerClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
gpa_key_manager_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GpaKeyManager),
0, /* n_preallocs */
gpa_key_manager_init,
};
this_type = g_type_register_static (GTK_TYPE_WINDOW,
"GpaKeyManager",
&this_info, 0);
}
return this_type;
}
/************************************************************
********************** Public API ************************
************************************************************/
/* Return the instance of the the key manager. If a new instance has
been created, TRUE is stored at R_CREATED, otherwise FALSE. */
GtkWidget *
gpa_key_manager_get_instance (gboolean *r_created)
{
if (r_created)
*r_created = !this_instance;
if (!this_instance)
{
this_instance = g_object_new (GPA_KEY_MANAGER_TYPE, NULL);
/*FIXME load_all_keys (this_instance);*/
}
return GTK_WIDGET (this_instance);
}
gboolean
gpa_key_manager_is_open (void)
{
return !!this_instance;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:41 PM (20 h, 9 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f3/4e/cf829c2f4372c7cbeeda3c9816bb
Attached To
rGPA Gnu Privacy Assistant
Event Timeline
Log In to Comment