diff --git a/src/clipboard.c b/src/clipboard.c
index 682bd34..01b6058 100644
--- a/src/clipboard.c
+++ b/src/clipboard.c
@@ -1,1127 +1,1115 @@
/* clipboard.c - The GNU Privacy Assistant
Copyright (C) 2000, 2001 G-N-U GmbH.
Copyright (C) 2007, 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 . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "gpa.h"
#include "gtktools.h"
#include "gpgmetools.h"
#include "gpawidgets.h"
#include "siglist.h"
#include "helpmenu.h"
#include "icons.h"
#include "clipboard.h"
#include "gpafiledecryptop.h"
#include "gpafileencryptop.h"
#include "gpafilesignop.h"
#include "gpafileverifyop.h"
/* Support for Gtk 2.8. */
#if ! GTK_CHECK_VERSION (2, 10, 0)
#define gtk_text_buffer_get_has_selection(textbuf) \
gtk_text_buffer_get_selection_bounds (textbuf, NULL, NULL);
#define gdk_atom_intern_static_string(str) gdk_atom_intern (str, FALSE)
#define GTK_STOCK_SELECT_ALL "gtk-select-all"
#define MY_GTK_TEXT_BUFFER_NO_HAS_SELECTION
#endif
/* FIXME: Move to a global file. */
#ifndef DIM
#define DIM(array) (sizeof (array) / sizeof (*array))
#endif
/* Object and class definition. */
struct _GpaClipboard
{
GtkWindow parent;
GtkWidget *text_view;
GtkTextBuffer *text_buffer;
/* List of sensitive widgets. See below */
GList *selection_sensitive_actions;
GList *paste_sensitive_actions;
gboolean paste_p;
};
struct _GpaClipboardClass
{
GtkWindowClass parent_class;
};
/* There is only one instance of the clipboard class. Use a global
variable to keep track of it. */
static GpaClipboard *instance;
/* We also need to save the parent class. */
static GObjectClass *parent_class;
/* Local prototypes */
static GObject *gpa_clipboard_constructor
(GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties);
/* GtkWidget boilerplate. */
static void
gpa_clipboard_finalize (GObject *object)
{
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gpa_clipboard_init (GpaClipboard *clipboard)
{
clipboard->selection_sensitive_actions = NULL;
clipboard->paste_sensitive_actions = NULL;
}
static void
gpa_clipboard_class_init (GpaClipboardClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->constructor = gpa_clipboard_constructor;
object_class->finalize = gpa_clipboard_finalize;
}
GType
gpa_clipboard_get_type (void)
{
static GType clipboard_type = 0;
if (!clipboard_type)
{
static const GTypeInfo clipboard_info =
{
sizeof (GpaClipboardClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gpa_clipboard_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GpaClipboard),
0, /* n_preallocs */
(GInstanceInitFunc) gpa_clipboard_init,
};
clipboard_type = g_type_register_static (GTK_TYPE_WINDOW,
"GpaClipboard",
&clipboard_info, 0);
}
return clipboard_type;
}
/* Definition of the sensitivity function type. */
typedef gboolean (*sensitivity_func_t)(gpointer);
/* Add widget to the list of sensitive widgets of editor. */
static void
add_selection_sensitive_action (GpaClipboard *clipboard,
GtkAction *action)
{
clipboard->selection_sensitive_actions
= g_list_append (clipboard->selection_sensitive_actions, action);
}
/* Return true if a selection is active. */
static gboolean
has_selection (gpointer param)
{
GpaClipboard *clipboard = param;
return gtk_text_buffer_get_has_selection (clipboard->text_buffer);
}
/* Update the sensitivity of the widget DATA and pass PARAM through to
the sensitivity callback. Usable as an iterator function in
g_list_foreach. */
static void
update_selection_sensitive_action (gpointer data, gpointer param)
{
gtk_action_set_sensitive (GTK_ACTION (data), has_selection (param));
}
/* Call update_selection_sensitive_widget for all widgets in the list
of sensitive widgets and pass CLIPBOARD through as the user data
parameter. */
static void
update_selection_sensitive_actions (GpaClipboard *clipboard)
{
g_list_foreach (clipboard->selection_sensitive_actions,
update_selection_sensitive_action,
(gpointer) clipboard);
}
/* Add ACTION to the list of sensitive actions of CLIPBOARD. */
static void
add_paste_sensitive_action (GpaClipboard *clipboard, GtkAction *action)
{
clipboard->paste_sensitive_actions
= g_list_append (clipboard->paste_sensitive_actions, action);
}
static void
update_paste_sensitive_action (gpointer data, gpointer param)
{
GpaClipboard *clipboard = param;
gtk_action_set_sensitive (GTK_ACTION (data), clipboard->paste_p);
}
static void
update_paste_sensitive_actions (GtkClipboard *clip,
GtkSelectionData *selection_data,
GpaClipboard *clipboard)
{
clipboard->paste_p = gtk_selection_data_targets_include_text (selection_data);
g_list_foreach (clipboard->paste_sensitive_actions,
update_paste_sensitive_action, (gpointer) clipboard);
}
static void
set_paste_sensitivity (GpaClipboard *clipboard, GtkClipboard *clip)
{
GdkDisplay *display;
display = gtk_clipboard_get_display (clip);
if (gdk_display_supports_selection_notification (display))
gtk_clipboard_request_contents
(clip, gdk_atom_intern_static_string ("TARGETS"),
(GtkClipboardReceivedFunc) update_paste_sensitive_actions,
clipboard);
}
static void
clipboard_owner_change_cb (GtkClipboard *clip, GdkEventOwnerChange *event,
GpaClipboard *clipboard)
{
set_paste_sensitivity (clipboard, clip);
}
/* Add a file created by an operation to the list */
static void
file_created_cb (GpaFileOperation *op, gpa_file_item_t item, gpointer data)
{
GpaClipboard *clipboard = data;
gboolean suc;
const gchar *end;
suc = g_utf8_validate (item->direct_out, item->direct_out_len, &end);
if (! suc)
{
gchar *str;
gsize len;
str = g_strdup_printf ("No valid UTF-8 encoding at position %i.\n"
"Assuming Latin-1 encoding instead.",
((int) (end - item->direct_out)));
gpa_window_message (str, GTK_WIDGET (clipboard));
g_free (str);
str = g_convert (item->direct_out, item->direct_out_len,
"UTF-8", "ISO-8859-1",
NULL, &len, NULL);
if (str)
{
-#ifdef G_OS_WIN32
- dos_to_unix (str, &item->direct_out_len);
-#endif
-
gtk_text_buffer_set_text (clipboard->text_buffer, str, len);
g_free (str);
return;
}
gpa_window_error ("Error converting Latin-1 to UTF-8",
GTK_WIDGET (clipboard));
/* Enough warnings: Try to show even with invalid encoding. */
}
-#ifdef G_OS_WIN32
- dos_to_unix (item->direct_out, &item->direct_out_len);
-#endif
-
gtk_text_buffer_set_text (clipboard->text_buffer,
item->direct_out, item->direct_out_len);
}
/* Do whatever is required with a file operation, to ensure proper clean up */
static void
register_operation (GpaClipboard *clipboard, GpaFileOperation *op)
{
g_signal_connect (G_OBJECT (op), "created_file",
G_CALLBACK (file_created_cb), clipboard);
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), NULL);
}
/* Actions as called by the menu items. */
/* Handle menu item "File/Clear". */
static void
file_clear (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = param;
gtk_text_buffer_set_text (clipboard->text_buffer, "", -1);
}
/* The directory last visited by load or save operations. */
static gchar *last_directory;
static gchar *
get_load_file_name (GtkWidget *parent, const gchar *title)
{
static GtkWidget *dialog;
GtkResponseType response;
gchar *filename = NULL;
if (! dialog)
{
dialog = gtk_file_chooser_dialog_new
(title, GTK_WINDOW (parent), GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
}
if (last_directory)
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
last_directory);
gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog));
/* Run the dialog until there is a valid response. */
response = gtk_dialog_run (GTK_DIALOG (dialog));
if (response == GTK_RESPONSE_OK)
{
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
if (filename)
filename = g_strdup (filename);
}
if (last_directory)
g_free (last_directory);
last_directory
= gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
if (last_directory)
last_directory = g_strdup (last_directory);
gtk_widget_hide (dialog);
return filename;
}
/* Handle menu item "File/Open". */
static void
file_open (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = param;
gchar *filename;
struct stat buf;
int res;
gboolean suc;
gchar *contents;
gsize length;
GError *err = NULL;
const gchar *end;
filename = get_load_file_name (GTK_WIDGET (clipboard), _("Open File"));
if (! filename)
return;
res = g_stat (filename, &buf);
if (res < 0)
{
gchar *str;
str = g_strdup_printf ("Error determining size of file %s:\n%s",
filename, strerror (errno));
gpa_window_error (str, GTK_WIDGET (clipboard));
g_free (str);
g_free (filename);
return;
}
#define MAX_CLIPBOARD_SIZE (2*1024*1024)
if (buf.st_size > MAX_CLIPBOARD_SIZE)
{
GtkWidget *window;
GtkWidget *hbox;
GtkWidget *labelMessage;
GtkWidget *pixmap;
gint result;
gchar *str;
window = gtk_dialog_new_with_buttons
(_("GPA Message"), GTK_WINDOW (clipboard), GTK_DIALOG_MODAL,
GTK_STOCK_OPEN, GTK_RESPONSE_OK,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
gtk_container_set_border_width (GTK_CONTAINER (window), 5);
gtk_dialog_set_default_response (GTK_DIALOG (window),
GTK_RESPONSE_CANCEL);
hbox = gtk_hbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
gtk_box_pack_start_defaults (GTK_BOX (GTK_DIALOG (window)->vbox), hbox);
pixmap = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO,
GTK_ICON_SIZE_DIALOG);
gtk_box_pack_start (GTK_BOX (hbox), pixmap, TRUE, FALSE, 10);
/* TRANSLATORS: The arguments are the filename, the integer size
and the unit (such as KB or MB). */
str = g_strdup_printf (_("The file %s is %lli%s large. Do you really "
" want to open it?"), filename,
buf.st_size / 1024 / 1024, "MB");
labelMessage = gtk_label_new (str);
g_free (str);
gtk_label_set_line_wrap (GTK_LABEL (labelMessage), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), labelMessage, TRUE, FALSE, 10);
gtk_widget_show_all (window);
result = gtk_dialog_run (GTK_DIALOG (window));
gtk_widget_destroy (window);
if (result != GTK_RESPONSE_OK)
{
g_free (filename);
return;
}
}
suc = g_file_get_contents (filename, &contents, &length, &err);
if (! suc)
{
gchar *str;
str = g_strdup_printf ("Error loading content of file %s:\n%s",
filename, err->message);
gpa_window_error (str, GTK_WIDGET (clipboard));
g_free (str);
g_error_free (err);
g_free (filename);
return;
}
suc = g_utf8_validate (contents, length, &end);
if (! suc)
{
gchar *str;
str = g_strdup_printf ("Error opening file %s:\n"
"No valid UTF-8 at position %i.",
filename, ((int) (end - contents)));
gpa_window_error (str, GTK_WIDGET (clipboard));
g_free (str);
g_free (contents);
g_free (filename);
return;
}
-#ifdef G_OS_WIN32
- dos_to_unix (contents, &length);
-#endif
-
gtk_text_buffer_set_text (clipboard->text_buffer, contents, length);
g_free (contents);
}
/* Run the modal file selection dialog and return a new copy of the
filename if the user pressed OK and NULL otherwise. */
static gchar *
get_save_file_name (GtkWidget *parent, const gchar *title)
{
static GtkWidget *dialog;
GtkResponseType response;
gchar *filename = NULL;
if (! dialog)
{
dialog = gtk_file_chooser_dialog_new
(title, 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);
}
if (last_directory)
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
last_directory);
gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog));
/* Run the dialog until there is a valid response. */
response = gtk_dialog_run (GTK_DIALOG (dialog));
if (response == GTK_RESPONSE_OK)
{
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
if (filename)
filename = g_strdup (filename);
}
if (last_directory)
g_free (last_directory);
last_directory
= gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
if (last_directory)
last_directory = g_strdup (last_directory);
if (last_directory)
g_free (last_directory);
last_directory
= gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
if (last_directory)
last_directory = g_strdup (last_directory);
gtk_widget_hide (dialog);
return filename;
}
/* Handle menu item "File/Save As...". */
static void
file_save_as (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = param;
gchar *filename;
GError *err = NULL;
gchar *contents;
gssize length;
gboolean suc;
GtkTextIter begin;
GtkTextIter end;
filename = get_save_file_name (GTK_WIDGET (clipboard), _("Save As..."));
if (! filename)
return;
gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end);
contents = gtk_text_buffer_get_text (clipboard->text_buffer, &begin, &end,
FALSE);
length = strlen (contents);
suc = g_file_set_contents (filename, contents, length, &err);
g_free (contents);
if (! suc)
{
gchar *str;
str = g_strdup_printf ("Error saving content to file %s:\n%s",
filename, err->message);
gpa_window_error (str, GTK_WIDGET (clipboard));
g_free (str);
g_error_free (err);
g_free (filename);
return;
}
g_free (filename);
}
/* Handle menu item "File/Verify". */
static void
file_verify (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = (GpaClipboard *) param;
GpaFileVerifyOperation *op;
GList *files = NULL;
gpa_file_item_t file_item;
GtkTextIter begin;
GtkTextIter end;
gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end);
file_item = g_malloc0 (sizeof (*file_item));
file_item->direct_name = g_strdup (_("Clipboard"));
file_item->direct_in
= gtk_text_buffer_get_slice (clipboard->text_buffer, &begin, &end, TRUE);
/* FIXME: One would think there exists a function to get the number
of bytes between two GtkTextIter, but no, that's too obvious. */
file_item->direct_in_len = strlen (file_item->direct_in);
files = g_list_append (files, file_item);
/* Start the operation. */
op = gpa_file_verify_operation_new (GTK_WIDGET (clipboard), files);
register_operation (clipboard, GPA_FILE_OPERATION (op));
}
/* Handle menu item "File/Sign". */
static void
file_sign (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = (GpaClipboard *) param;
GpaFileSignOperation *op;
GList *files = NULL;
gpa_file_item_t file_item;
GtkTextIter begin;
GtkTextIter end;
gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end);
file_item = g_malloc0 (sizeof (*file_item));
file_item->direct_name = g_strdup (_("Clipboard"));
file_item->direct_in
= gtk_text_buffer_get_text (clipboard->text_buffer, &begin, &end, FALSE);
/* FIXME: One would think there exists a function to get the number
of bytes between two GtkTextIter, but no, that's too obvious. */
file_item->direct_in_len = strlen (file_item->direct_in);
files = g_list_append (files, file_item);
/* Start the operation. */
op = gpa_file_sign_operation_new (GTK_WIDGET (clipboard), files, TRUE);
register_operation (clipboard, GPA_FILE_OPERATION (op));
}
/* Handle menu item "File/Encrypt". */
static void
file_encrypt (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = (GpaClipboard *) param;
GpaFileEncryptOperation *op;
GList *files = NULL;
gpa_file_item_t file_item;
GtkTextIter begin;
GtkTextIter end;
gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end);
file_item = g_malloc0 (sizeof (*file_item));
file_item->direct_name = g_strdup (_("Clipboard"));
file_item->direct_in
= gtk_text_buffer_get_text (clipboard->text_buffer, &begin, &end, FALSE);
/* FIXME: One would think there exists a function to get the number
of bytes between two GtkTextIter, but no, that's too obvious. */
file_item->direct_in_len = strlen (file_item->direct_in);
files = g_list_append (files, file_item);
/* Start the operation. */
op = gpa_file_encrypt_operation_new (GTK_WIDGET (clipboard), files, TRUE);
register_operation (clipboard, GPA_FILE_OPERATION (op));
}
/* Handle menu item "File/Decrypt". */
static void
file_decrypt (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = (GpaClipboard *) param;
GpaFileDecryptOperation *op;
GList *files = NULL;
gpa_file_item_t file_item;
GtkTextIter begin;
GtkTextIter end;
gtk_text_buffer_get_bounds (clipboard->text_buffer, &begin, &end);
file_item = g_malloc0 (sizeof (*file_item));
file_item->direct_name = g_strdup (_("Clipboard"));
file_item->direct_in
= gtk_text_buffer_get_text (clipboard->text_buffer, &begin, &end, FALSE);
/* FIXME: One would think there exists a function to get the number
of bytes between two GtkTextIter, but no, that's too obvious. */
file_item->direct_in_len = strlen (file_item->direct_in);
files = g_list_append (files, file_item);
/* Start the operation. */
op = gpa_file_decrypt_operation_new (GTK_WIDGET (clipboard), files);
register_operation (clipboard, GPA_FILE_OPERATION (op));
}
/* Handle menu item "File/Close". */
static void
file_close (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = param;
gtk_widget_destroy (GTK_WIDGET (clipboard));
}
static void
edit_cut (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = param;
g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view),
"cut-clipboard");
}
static void
edit_copy (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = param;
g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view),
"copy-clipboard");
}
static void
edit_paste (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = param;
g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view),
"paste-clipboard");
}
static void
edit_delete (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = param;
g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view), "backspace");
}
/* Handle menu item "Edit/Select All". */
static void
edit_select_all (GtkAction *action, gpointer param)
{
GpaClipboard *clipboard = param;
g_signal_emit_by_name (GTK_TEXT_VIEW (clipboard->text_view), "select-all");
}
/* Construct the file manager menu window and return that object. */
static void
clipboard_action_new (GpaClipboard *clipboard,
GtkWidget **menu, GtkWidget **toolbar)
{
static const GtkActionEntry entries[] =
{
/* Toplevel. */
{ "File", NULL, N_("_File"), NULL },
{ "Edit", NULL, N_("_Edit"), NULL },
/* File menu. */
{ "FileClear", GTK_STOCK_CLEAR, NULL, NULL,
N_("Clear buffer"), G_CALLBACK (file_clear) },
{ "FileOpen", GTK_STOCK_OPEN, NULL, NULL,
N_("Open a file"), G_CALLBACK (file_open) },
{ "FileSaveAs", GTK_STOCK_SAVE_AS, NULL, NULL,
N_("Save to a file"), G_CALLBACK (file_save_as) },
{ "FileSign", GPA_STOCK_SIGN, NULL, NULL,
N_("Sign buffer text"), G_CALLBACK (file_sign) },
{ "FileVerify", GPA_STOCK_VERIFY, NULL, NULL,
N_("Check signatures of buffer text"), G_CALLBACK (file_verify) },
{ "FileEncrypt", GPA_STOCK_ENCRYPT, NULL, NULL,
N_("Encrypt the buffer text"), G_CALLBACK (file_encrypt) },
{ "FileDecrypt", GPA_STOCK_DECRYPT, NULL, NULL,
N_("Decrypt the buffer text"), G_CALLBACK (file_decrypt) },
{ "FileClose", GTK_STOCK_CLOSE, NULL, NULL,
N_("Close the buffer"), G_CALLBACK (file_close) },
{ "FileQuit", GTK_STOCK_QUIT, NULL, NULL,
N_("Quit the program"), G_CALLBACK (gtk_main_quit) },
/* Edit menu. */
#if 0
/* FIXME: Not implemented yet. */
{ "EditUndo", GTK_STOCK_UNDO, NULL, NULL,
N_("Undo the last action"), G_CALLBACK (edit_undo) },
{ "EditRedo", GTK_STOCK_REDO, NULL, NULL,
N_("Redo the last undone action"), G_CALLBACK (edit_redo) },
#endif
{ "EditCut", GTK_STOCK_CUT, NULL, NULL,
N_("Cut the selection"), G_CALLBACK (edit_cut) },
{ "EditCopy", GTK_STOCK_COPY, NULL, NULL,
N_("Copy the selection"), G_CALLBACK (edit_copy) },
{ "EditPaste", GTK_STOCK_PASTE, NULL, NULL,
N_("Paste the clipboard"), G_CALLBACK (edit_paste) },
{ "EditDelete", GTK_STOCK_DELETE, NULL, NULL,
N_("Delete the selected text"), G_CALLBACK (edit_delete) },
{ "EditSelectAll", GTK_STOCK_SELECT_ALL, NULL, "A",
N_("Select the entire document"), G_CALLBACK (edit_select_all) }
};
static const char *ui_description =
""
" "
" "
" "
" "
" "
" "
" "
" "
#if 0
/* Disabled because the toolbar arrow mode doesn't work, and the
toolbar takes up too much space otherwise. */
" "
" "
#endif
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
#if 0
" "
#endif
" "
"";
GtkAccelGroup *accel_group;
GtkActionGroup *action_group;
GtkAction *action;
GtkUIManager *ui_manager;
GError *error;
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),
clipboard);
gtk_action_group_add_actions (action_group, gpa_help_menu_action_entries,
G_N_ELEMENTS (gpa_help_menu_action_entries),
clipboard);
gtk_action_group_add_actions (action_group, gpa_windows_menu_action_entries,
G_N_ELEMENTS (gpa_windows_menu_action_entries),
clipboard);
gtk_action_group_add_actions
(action_group, gpa_preferences_menu_action_entries,
G_N_ELEMENTS (gpa_preferences_menu_action_entries), clipboard);
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 (clipboard), accel_group);
if (! gtk_ui_manager_add_ui_from_string (ui_manager, ui_description,
-1, &error))
{
g_message ("building clipboard 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, "WindowsKeyringEditor");
g_object_set (action, "short_label", _("Keyring"), 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, "EditCut");
add_selection_sensitive_action (clipboard, action);
action = gtk_action_group_get_action (action_group, "EditCopy");
add_selection_sensitive_action (clipboard, action);
action = gtk_action_group_get_action (action_group, "EditDelete");
add_selection_sensitive_action (clipboard, action);
action = gtk_action_group_get_action (action_group, "EditPaste");
/* Initialized later. */
add_paste_sensitive_action (clipboard, action);
*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);
}
/* Callback for the destroy signal. */
static void
clipboard_closed (GtkWidget *widget, gpointer param)
{
instance = NULL;
}
/* Construct the clipboard text. */
static GtkWidget *
clipboard_text_new (GpaClipboard *clipboard)
{
GtkWidget *scroller;
clipboard->text_view = gtk_text_view_new ();
gtk_widget_grab_focus (clipboard->text_view);
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (clipboard->text_view), 4);
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (clipboard->text_view), 4);
clipboard->text_buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (clipboard->text_view));
#ifndef MY_GTK_TEXT_BUFFER_NO_HAS_SELECTION
/* A change in selection status causes a property change, which we
can listen in on. */
g_signal_connect_swapped (clipboard->text_buffer, "notify::has-selection",
G_CALLBACK (update_selection_sensitive_actions),
clipboard);
#else
/* Runs very often. The changed signal is necessary for backspace
actions. */
g_signal_connect_swapped (clipboard->text_buffer, "mark-set",
G_CALLBACK (update_selection_sensitive_actions),
clipboard);
g_signal_connect_after (clipboard->text_buffer, "backspace",
G_CALLBACK (update_selection_sensitive_actions),
clipboard);
#endif
scroller = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (scroller), clipboard->text_view);
return scroller;
}
/* Construct a new class object of GpaClipboard. */
static GObject *
gpa_clipboard_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GObject *object;
GpaClipboard *clipboard;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *icon;
GtkWidget *label;
gchar *markup;
GtkWidget *menubar;
GtkWidget *text_box;
GtkWidget *text_frame;
GtkWidget *toolbar;
GtkWidget *align;
guint pt, pb, pl, pr;
/* Invoke parent's constructor. */
object = parent_class->constructor (type,
n_construct_properties,
construct_properties);
clipboard = GPA_CLIPBOARD (object);
/* Initialize. */
gtk_window_set_title (GTK_WINDOW (clipboard),
_("GNU Privacy Assistant - Clipboard"));
gtk_window_set_default_size (GTK_WINDOW (clipboard), 640, 480);
/* Realize the window so that we can create pixmaps without warnings
and also access the clipboard. */
gtk_widget_realize (GTK_WIDGET (clipboard));
/* Use a vbox to show the menu, toolbar and the text container. */
vbox = gtk_vbox_new (FALSE, 0);
/* We need to create the text_buffer before we create the menus and
the toolbar, because of widget sensitivity issues, which depend
on the selection status of the text_buffer. */
text_frame = clipboard_text_new (clipboard);
/* Get the menu and the toolbar. */
clipboard_action_new (clipboard, &menubar, &toolbar);
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 clipboard. */
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
/* FIXME: Need better icon. */
icon = gtk_image_new_from_stock (GTK_STOCK_PASTE, GTK_ICON_SIZE_DND);
gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0);
label = gtk_label_new (NULL);
markup = g_strdup_printf ("%s",
_("Clipboard"));
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);
/* Third a text entry. */
text_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 (text_box), text_frame, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (align), text_box);
gtk_container_add (GTK_CONTAINER (clipboard), vbox);
g_signal_connect (object, "destroy",
G_CALLBACK (clipboard_closed), object);
/* Update the sensitivity of paste items. */
{
GtkClipboard *clip;
/* Do this once for all paste sensitive items. Note that the
window is realized already. */
clip = gtk_widget_get_clipboard (GTK_WIDGET (clipboard),
GDK_SELECTION_CLIPBOARD);
g_signal_connect (clip, "owner_change",
G_CALLBACK (clipboard_owner_change_cb), clipboard);
set_paste_sensitivity (clipboard, clip);
}
/* Update the sensitivity of selection items. */
update_selection_sensitive_actions (clipboard);
return object;
}
static GpaClipboard *
gpa_clipboard_new ()
{
GpaClipboard *clipboard;
clipboard = g_object_new (GPA_CLIPBOARD_TYPE, NULL);
return clipboard;
}
/* API */
GtkWidget *
gpa_clipboard_get_instance (void)
{
if (! instance)
instance = gpa_clipboard_new ();
return GTK_WIDGET (instance);
}
gboolean
gpa_clipboard_is_open (void)
{
return (instance != NULL);
}
diff --git a/src/gpaexportclipop.c b/src/gpaexportclipop.c
index 0ce88af..62edb12 100644
--- a/src/gpaexportclipop.c
+++ b/src/gpaexportclipop.c
@@ -1,156 +1,156 @@
/* gpaexportclipop.c - The GpaExportClipboardOperation 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
#include
#include
#include "gpa.h"
#include "i18n.h"
#include "gtktools.h"
#include "gpgmetools.h"
#include "gpaexportclipop.h"
static GObjectClass *parent_class = NULL;
static gboolean
gpa_export_clipboard_operation_get_destination (GpaExportOperation *operation,
gpgme_data_t *dest,
gboolean *armor);
static void
gpa_export_clipboard_operation_complete_export (GpaExportOperation *operation);
/* GObject boilerplate */
static void
gpa_export_clipboard_operation_finalize (GObject *object)
{
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gpa_export_clipboard_operation_init (GpaExportClipboardOperation *op)
{
}
static GObject*
gpa_export_clipboard_operation_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GObject *object;
/* GpaExportClipboardOperation *op; */
/* Invoke parent's constructor */
object = parent_class->constructor (type,
n_construct_properties,
construct_properties);
/* op = GPA_EXPORT_CLIPBOARD_OPERATION (object); */
return object;
}
static void
gpa_export_clipboard_operation_class_init (GpaExportClipboardOperationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GpaExportOperationClass *export_class = GPA_EXPORT_OPERATION_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->constructor = gpa_export_clipboard_operation_constructor;
object_class->finalize = gpa_export_clipboard_operation_finalize;
export_class->get_destination = gpa_export_clipboard_operation_get_destination;
export_class->complete_export = gpa_export_clipboard_operation_complete_export;
}
GType
gpa_export_clipboard_operation_get_type (void)
{
static GType file_operation_type = 0;
if (!file_operation_type)
{
static const GTypeInfo file_operation_info =
{
sizeof (GpaExportClipboardOperationClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gpa_export_clipboard_operation_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GpaExportClipboardOperation),
0, /* n_preallocs */
(GInstanceInitFunc) gpa_export_clipboard_operation_init,
};
file_operation_type = g_type_register_static (GPA_EXPORT_OPERATION_TYPE,
"GpaExportClipboardOperation",
&file_operation_info, 0);
}
return file_operation_type;
}
/* Virtual methods */
static gboolean
gpa_export_clipboard_operation_get_destination (GpaExportOperation *operation,
gpgme_data_t *dest,
gboolean *armor)
{
gpg_error_t err;
*armor = TRUE;
err = gpgme_data_new (dest);
if (err)
{
gpa_gpgme_warning (err);
return FALSE;
}
else
{
return TRUE;
}
}
static void
gpa_export_clipboard_operation_complete_export (GpaExportOperation *operation)
{
GpaExportClipboardOperation *op = GPA_EXPORT_CLIPBOARD_OPERATION (operation);
- dump_data_to_clipboard (operation->dest, gtk_clipboard_get
- (GDK_SELECTION_CLIPBOARD));
- gpa_window_message (_("The keys have been copied to the clipboard."),
- GPA_OPERATION (op)->window);
+ if (!dump_data_to_clipboard (operation->dest, gtk_clipboard_get
+ (GDK_SELECTION_CLIPBOARD)))
+ gpa_window_message (_("The keys have been copied to the clipboard."),
+ GPA_OPERATION (op)->window);
}
/* API */
GpaExportClipboardOperation*
gpa_export_clipboard_operation_new (GtkWidget *window, GList *keys)
{
GpaExportClipboardOperation *op;
op = g_object_new (GPA_EXPORT_CLIPBOARD_OPERATION_TYPE,
"window", window,
"keys", keys,
NULL);
return op;
}
diff --git a/src/gpgmetools.c b/src/gpgmetools.c
index 2b4be56..63f6b8d 100644
--- a/src/gpgmetools.c
+++ b/src/gpgmetools.c
@@ -1,1750 +1,1721 @@
/* 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 .
*/
/* A set of auxiliary functions for common tasks related to GPGME */
#include
#include
#include
#include
#include "gpa.h"
#include "gtktools.h"
#include "gpgmetools.h"
#include
#ifdef G_OS_UNIX
#include
#include
#include
#include
#else
#include
#endif
#ifdef G_OS_WIN32
#include
#endif
#include
#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);
}
void
gpa_gpgme_warning_ext (gpg_error_t err, const char *desc)
{
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. The error was:\n\n"
"\t%s\n\n"
"This is probably a bug in GPA.\n"
"GPA will now try to recover from this error."),
arg);
g_free (argbuf);
gpa_window_error (message, NULL);
g_free (message);
}
void
gpa_gpgme_warning (gpg_error_t err)
{
gpa_gpgme_warning_ext (err, NULL);
}
/* 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;
}
-#ifdef G_OS_WIN32
-/* Convert newlines. */
-void
-dos_to_unix (gchar *str, gsize *len)
-{
- /* On Windows 2000, we need to convert \r\n to \n in the output for
- cut & paste to work properly (otherwise, extra newlines will be
- inserted). */
- gchar *src;
- gchar *dst;
-
- src = str;
- dst = str;
- while (*src)
- {
- if (src[0] == '\r' && src[1] == '\n')
- src++;
- *(dst++) = *(src++);
- }
- *dst = '\0';
- *len = dst - str;
-}
-#endif
-
-
/* Write the contents of the gpgme_data_t into the clipboard. Assumes
- that the data is ASCII. */
-void
+ that the data is ASCII. Return 0 on success. */
+int
dump_data_to_clipboard (gpgme_data_t data, GtkClipboard *clipboard)
{
- char buffer[128];
+ 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);
- exit (EXIT_FAILURE);
+ return -1;
}
while ((nread = gpgme_data_read (data, buffer, sizeof (buffer))) > 0)
{
- text = g_realloc (text, len + nread);
+ text = g_realloc (text, len + nread + 1);
strncpy (text + len, buffer, nread);
len += nread;
}
if (nread == -1)
{
gpa_window_error (strerror (errno), NULL);
- exit (EXIT_FAILURE);
+ return -1;
}
-#ifdef G_OS_WIN32
- dos_to_unix (text, &len);
-#endif
-
gtk_clipboard_set_text (clipboard, text, (int)len);
g_free (text);
- return;
+ 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 ("\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"
"\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. */
gboolean
gpa_backup_key (const gchar *fpr, const char *filename)
{
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
};
const gchar *path;
mode_t mask;
/* Get the gpg path. */
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;
/* 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 (_("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))
{
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,
&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. */
void
gpa_switch_to_gpg2 (const char *gpg_binary, const char *gpgsm_binary)
{
const char *oldname;
char *newname;
char *p;
if (gpgsm_binary)
{
if (!access (gpgsm_binary, X_OK))
{
gpgme_set_engine_info (GPGME_PROTOCOL_CMS, gpgsm_binary, NULL);
g_message ("switched CMS engine to `%s'", gpgsm_binary);
}
else
g_message ("problem accessing CMS engine `%s'", gpgsm_binary);
}
if (gpg_binary)
{
if (!access (gpg_binary, X_OK))
{
gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP, gpg_binary, NULL);
g_message ("switched OpenPGP engine to `%s'", gpg_binary);
return;
}
g_message ("problem accessing OpenPGP engine `%s'", gpg_binary);
}
if (is_gpg_version_at_least ("2.0.0"))
return; /* Already using 2.0. */
oldname = get_gpg_path ();
g_return_if_fail (oldname && *oldname);
newname = xmalloc (strlen (oldname) + 1 + 1);
strcpy (newname, oldname);
#ifdef G_OS_WIN32
# define OLD_NAME "gpg.exe"
# define NEW_NAME "gpg2.exe"
for (p=newname; *p; p++)
if (*p == '\\')
*p = '/';
#else
# define OLD_NAME "gpg"
# define NEW_NAME "gpg2"
#endif /*G_OS_WIN32*/
p = strrchr (newname, '/');
if (p)
p++;
if (!strcmp (p, OLD_NAME))
{
strcpy (p, NEW_NAME);
if (!access (newname, X_OK))
{
gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP, newname, NULL);
g_message ("switched engine to `%s'", newname);
}
}
#undef OLD_NAME
#undef NEW_NAME
xfree (newname);
}
/* 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. */
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;
g_return_val_if_fail (argv[0], 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)
{
gpa_start_simple_gpg_command (NULL, NULL, GPGME_PROTOCOL_ASSUAN, 1,
"NOP", "/bye", NULL);
}
/* 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 a4ce122..2c6877d 100644
--- a/src/gpgmetools.h
+++ b/src/gpgmetools.h
@@ -1,225 +1,220 @@
/* 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 .
*/
/* A set of auxiliary functions for common tasks related to GPGME. */
#ifndef GPGMETOOLS_H
#define GPGMETOOLS_H
#include
#include
/* 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. */
void gpa_gpgme_warning_ext (gpg_error_t err, const char *desc);
void gpa_gpgme_warning (gpg_error_t err);
/* 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);
-#ifdef G_OS_WIN32
-/* Convert newlines. */
-void dos_to_unix (gchar *str, gsize *len);
-#endif
-
/* 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);
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*/