Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623171
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
74 KB
Subscribers
None
View Options
diff --git a/src/gpa.c b/src/gpa.c
index 4e67bb2..1ff0c5e 100644
--- a/src/gpa.c
+++ b/src/gpa.c
@@ -1,594 +1,607 @@
/* gpa.c - The GNU Privacy Assistant main file.
Copyright (C) 2000-2002 G-N-U GmbH.
Copyright (C) 2005, 2008, 2012, 2014 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 <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <gpgme.h>
#include "get-path.h"
#include "gpa.h"
#include "keymanager.h"
#include "fileman.h"
#include "clipboard.h"
#include "cardman.h"
#include "keyserver.h"
#include "settingsdlg.h"
#include "confdialog.h"
#include "icons.h"
#ifdef __MINGW32__
#include "hidewnd.h"
#endif
/* Global variables. */
/* The home directory of GnuPG. */
gchar *gnupg_homedir;
/* True if CMS hack mode is enabled. */
gboolean cms_hack;
/* True if the ticker used for card operations should not be started. */
gboolean disable_ticker;
/* True if the gpgme edit FSM shall output debug messages. */
gboolean debug_edit_fsm;
/* Local variables. */
typedef struct
{
gboolean start_key_manager;
gboolean start_file_manager;
gboolean start_card_manager;
gboolean start_clipboard;
gboolean start_settings;
gboolean start_only_server;
+ gboolean stop_running_server;
gboolean disable_x509;
gboolean no_remote;
gboolean enable_logging;
gchar *options_filename;
} gpa_args_t;
static char *dummy_arg;
static gpa_args_t args;
/* The copyright notice. */
static const char *copyright =
"Copyright (C) 2000-2002 Miguel Coca, G-N-U GmbH, Intevation GmbH.\n"
"Copyright (C) 2005-2014 g10 Code GmbH.\n"
"This program comes with ABSOLUTELY NO WARRANTY.\n"
"This is free software, and you are welcome to redistribute it\n"
"under certain conditions. See the file COPYING for details.\n";
static GtkWidget *backend_config_dialog = NULL;
static void print_version (void);
/* All command line options of the main application. */
static GOptionEntry option_entries[] =
{
{ "version", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
(gpointer) &print_version,
N_("Output version information and exit"), NULL, },
{ "keyring", 'k', 0, G_OPTION_ARG_NONE, &args.start_key_manager,
N_("Open key manager (default)"), NULL },
{ "files", 'f', 0, G_OPTION_ARG_NONE, &args.start_file_manager,
N_("Open file manager"), NULL },
#ifdef ENABLE_CARD_MANAGER
{ "card", 'C', 0, G_OPTION_ARG_NONE, &args.start_card_manager,
N_("Open the card manager"), NULL },
#endif
{ "clipboard", 'c', 0, G_OPTION_ARG_NONE, &args.start_clipboard,
N_("Open clipboard"), NULL },
{ "settings", 's', 0, G_OPTION_ARG_NONE, &args.start_settings,
N_("Open the settings dialog"), NULL },
{ "daemon", 'd', 0, G_OPTION_ARG_NONE, &args.start_only_server,
N_("Only start the UI server"), NULL },
{ "disable-x509", 0, 0, G_OPTION_ARG_NONE, &args.disable_x509,
N_("Disable support for X.509"), NULL },
{ "options", 'o', 0, G_OPTION_ARG_FILENAME, &args.options_filename,
N_("Read options from file"), "FILE" },
{ "no-remote", 0, 0, G_OPTION_ARG_NONE, &args.no_remote,
N_("Do not connect to a running instance"), NULL },
+ { "stop-server", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
+ &args.stop_running_server, NULL, NULL },
/* Note: the cms option will eventually be removed. */
{ "cms", 'x', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
&cms_hack, NULL, NULL },
{ "disable-ticker", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
&disable_ticker, NULL, NULL },
{ "debug-edit-fsm", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
&debug_edit_fsm, NULL, NULL },
{ "enable-logging", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
&args.enable_logging, NULL, NULL },
{ "gpg-binary", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME,
&dummy_arg, NULL, NULL },
{ "gpgsm-binary", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME,
&dummy_arg, NULL, NULL },
{ NULL }
};
/* Return a malloced string with the locale dir. Return NULL on
error. */
static char *
get_locale_dir (void)
{
#ifdef G_OS_WIN32
char name[530];
char *p;
if (! GetModuleFileNameA (0, name, sizeof (name) - 30))
return NULL;
p = strrchr (name, '\\');
if (!p)
{
*name = '\\';
p = name;
}
p++;
strcpy (p, "share\\locale");
return strdup (name);
#else
return strdup (LOCALEDIR);
#endif
}
static void
i18n_init (void)
{
#ifdef ENABLE_NLS
char *tmp;
gtk_set_locale ();
bind_textdomain_codeset (PACKAGE, "UTF-8");
tmp = get_locale_dir ();
if (tmp)
{
bindtextdomain (PACKAGE, tmp);
free (tmp);
}
textdomain (PACKAGE);
#endif
}
/* Manage the main windows and the settings dialog. */
static void
quit_if_no_window (void)
{
if (!args.start_only_server
&& !gpa_key_manager_is_open ()
&& !gpa_file_manager_is_open ()
&& !gpa_clipboard_is_open ()
#ifdef ENABLE_CARD_MANAGER
&& !gpa_card_manager_is_open ()
#endif /*ENABLE_CARD_MANAGER*/
)
gpa_stop_server ();
}
static void
close_main_window (GtkWidget *widget, gpointer param)
{
GtkWidget ** window = param;
*window = NULL;
quit_if_no_window ();
}
/* Show the key manager dialog. */
void
gpa_open_key_manager (GtkAction *action, void *data)
{
GtkWidget *widget;
gboolean created;
widget = gpa_key_manager_get_instance (&created);
if (created)
g_signal_connect (G_OBJECT (widget), "destroy",
G_CALLBACK (quit_if_no_window), NULL);
gtk_widget_show_all (widget);
gtk_window_present (GTK_WINDOW (widget));
}
/* Show the clipboard dialog. */
void
gpa_open_clipboard (GtkAction *action, void *data)
{
/* FIXME: Shouldn't this connect only happen if the instance is
created the first time? Looks like a memory leak to me. Right:
although the closure is ref counted an internal data object will
get allocated. */
g_signal_connect (G_OBJECT (gpa_clipboard_get_instance ()), "destroy",
G_CALLBACK (quit_if_no_window), NULL);
gtk_widget_show_all (gpa_clipboard_get_instance ());
gtk_window_present (GTK_WINDOW (gpa_clipboard_get_instance ()));
}
/* Show the filemanager dialog. */
void
gpa_open_filemanager (GtkAction *action, void *data)
{
/* FIXME: Shouldn't this connect only happen if the instance is
created the first time? Looks like a memory leak to me. */
g_signal_connect (G_OBJECT (gpa_file_manager_get_instance ()), "destroy",
G_CALLBACK (quit_if_no_window), NULL);
gtk_widget_show_all (gpa_file_manager_get_instance ());
gtk_window_present (GTK_WINDOW (gpa_file_manager_get_instance ()));
}
/* Show the card manager. */
#ifdef ENABLE_CARD_MANAGER
void
gpa_open_cardmanager (GtkAction *action, void *data)
{
/* FIXME: Shouldn't this connect only happen if the instance is
created the first time? Looks like a memory leak to me. */
g_signal_connect (G_OBJECT (gpa_card_manager_get_instance ()), "destroy",
G_CALLBACK (quit_if_no_window), NULL);
gtk_widget_show_all (gpa_card_manager_get_instance ());
gtk_window_present (GTK_WINDOW (gpa_card_manager_get_instance ()));
}
#endif /*ENABLE_CARD_MANAGER*/
/* Show the settings dialog. */
void
gpa_open_settings_dialog (GtkAction *action, void *data)
{
settings_dlg_new (data);
}
/* Show the backend configuration dialog. */
void
gpa_open_backend_config_dialog (GtkAction *action, void *data)
{
if (!backend_config_dialog)
{
backend_config_dialog = gpa_backend_config_dialog_new ();
g_signal_connect (G_OBJECT (backend_config_dialog), "destroy",
G_CALLBACK (close_main_window), &backend_config_dialog);
gtk_widget_show_all (backend_config_dialog);
}
gtk_window_present (GTK_WINDOW (backend_config_dialog));
}
/* Command line options. */
/* Print version information and exit. */
static void
print_version (void)
{
printf ("%s %s\n%s", PACKAGE, VERSION, copyright);
exit (0);
}
static void
dummy_log_func (const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer user_data)
{
/* Nothing to be done. */
}
/* Helper for main. */
static gpg_error_t
open_requested_window (int argc, char **argv, int use_server)
{
gpg_error_t err = 0;
int did_server = 0;
int i;
/* Don't open the key manager if any files are given on the command
line. Ditto for the clipboard. */
if (args.start_key_manager && (optind >= argc))
{
if (use_server)
{
did_server = 1;
err = gpa_send_to_server ("START_KEYMANAGER");
}
else
gpa_open_key_manager (NULL, NULL);
}
if (args.start_clipboard && (optind >= argc))
{
if (use_server)
{
did_server = 1;
err = gpa_send_to_server ("START_CLIPBOARD");
}
else
gpa_open_clipboard (NULL, NULL);
}
if (args.start_file_manager || (optind < argc))
{
/* Do not use the server if there are file args - see below. */
if (use_server && !(optind < argc))
{
did_server = 1;
err = gpa_send_to_server ("START_FILEMANAGER");
}
else
gpa_open_filemanager (NULL, NULL);
}
#ifdef ENABLE_CARD_MANAGER
if (args.start_card_manager)
{
if (use_server)
{
did_server = 1;
err = gpa_send_to_server ("START_CARDMANAGER");
}
else
gpa_open_cardmanager (NULL, NULL);
}
#endif /*ENABLE_CARD_MANAGER*/
if (args.start_settings)
{
if (use_server)
{
did_server = 1;
err = gpa_send_to_server ("START_CONFDIALOG");
}
else
gpa_open_settings_dialog (NULL, NULL);
}
/* If there are any command line arguments that are not options, try
to open them as files in the filemanager. However if we are to
connect to a server this can't be done because the running
instance may already have files in the file manager and thus we
better do not add new files. Instead we start a new instance. */
if (use_server)
{
if (!did_server)
err = -1; /* Create a new instance. */
}
else
{
for (i = optind; i < argc; i++)
gpa_file_manager_open_file (GPA_FILE_MANAGER
(gpa_file_manager_get_instance ()),
argv[i]);
}
return err;
}
int
main (int argc, char *argv[])
{
GError *err = NULL;
GOptionContext *context;
char *configname = NULL;
char *keyservers_configname = NULL;
/* Under W32 logging is disabled by default to prevent MS Windows NT
from opening a console. */
#ifndef G_OS_WIN32
args.enable_logging = 1;
#endif
/* Set locale before option parsing for UTF-8 conversion. */
i18n_init ();
/* Parse command line options. */
context = g_option_context_new (N_("[FILE...]"));
#if GLIB_CHECK_VERSION (2, 12, 0)
g_option_context_set_summary (context, N_("Graphical frontend to GnuPG"));
g_option_context_set_description (context, N_("Please report bugs to <"
PACKAGE_BUGREPORT ">."));
g_option_context_set_translation_domain (context, PACKAGE);
#endif
g_option_context_add_main_entries (context, option_entries, PACKAGE);
g_option_context_add_group (context, gtk_get_option_group (TRUE));
if (! g_option_context_parse (context, &argc, &argv, &err))
{
g_print ("option parsing failed: %s\n", err->message);
exit (1);
}
if (!args.enable_logging)
{
#ifdef __MINGW32__
hide_gpa_console_window();
#endif
g_log_set_handler ("Glib", G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING
| G_LOG_LEVEL_MESSAGE
| G_LOG_LEVEL_INFO, dummy_log_func, NULL);
g_log_set_handler ("Gdk", G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING
| G_LOG_LEVEL_MESSAGE
| G_LOG_LEVEL_INFO, dummy_log_func, NULL);
g_log_set_handler ("Gtk", G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING
| G_LOG_LEVEL_MESSAGE
| G_LOG_LEVEL_INFO, dummy_log_func, NULL);
}
gtk_init (&argc, &argv);
#ifdef G_OS_WIN32
gtk_settings_set_string_property(gtk_settings_get_default(),
"gtk-theme-name",
"MS-Windows",
"XProperty");
#endif
/* Default icon for all windows. */
gtk_window_set_default_icon_from_file (GPA_DATADIR "/gpa.png", &err);
if (err)
g_error_free (err);
gpa_register_stock_items ();
#ifdef IS_DEVELOPMENT_VERSION
fprintf (stderr, "NOTE: This is a development version!\n");
#endif
/* Initialize GPGME. */
gpgme_check_version (NULL);
#ifdef USE_SIMPLE_GETTEXT
/* FIXME */
#else
#ifdef ENABLE_NLS
gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
#endif
#endif
#ifndef G_OS_WIN32
/* Internationalisation with gtk+-2.0 wants UTF-8 instead of the
character set determined by locale. */
putenv ("OUTPUT_CHARSET=utf8");
/* We don't want the SIGPIPE. I wonder why gtk_init does not set it
to ignore. Nobody wants this signal. */
{
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset (&sa.sa_mask);
sa.sa_flags = 0;
sigaction (SIGPIPE, &sa, NULL);
}
#endif
/* Start the agent if needed. We need to do this because the card
manager uses direct assuan commands to the agent and thus expects
that the agent has been startet. */
gpa_start_agent ();
/* Handle command line options. */
cms_hack = !args.disable_x509;
/* Start the key manger by default. */
if (!args.start_key_manager
&& !args.start_file_manager
&& !args.start_clipboard
&& !args.start_settings
&& !args.start_card_manager
)
args.start_key_manager = TRUE;
/* Note: We can not use GPGME's engine info, as that returns NULL
(default) for home_dir. Consider improving GPGME to get it from
there, or using gpgconf (via GPGME). */
gnupg_homedir = default_homedir ();
/* FIXME: GnuPG can not create a key if its home directory is
missing. We help it out here. Should be fixed in GnuPG. */
if (! g_file_test (gnupg_homedir, G_FILE_TEST_IS_DIR))
g_mkdir (gnupg_homedir, 0700);
/* Locate GPA's configuration file. */
if (! args.options_filename)
configname = g_build_filename (gnupg_homedir, "gpa.conf", NULL);
else
configname = args.options_filename;
gpa_options_set_file (gpa_options_get_instance (), configname);
g_free (configname);
+ if (args.stop_running_server)
+ {
+ /* If a UI server with the same name is already running, stop
+ it. If not, do nothing. */
+ if (gpa_check_server () == 2)
+ gpa_send_to_server ("KILL_UISERVER");
+ return 0;
+ }
+
+
/* Check whether we need to start a server or to simply open a
- windowin an existing server. */
+ window in an already running server. */
switch (gpa_check_server ())
{
case 0: /* No running server on the expected socket. Start one. */
gpa_start_server ();
break;
case 1: /* An old instance or a differen UI server is already running.
Do not start a server. */
break;
case 2: /* An instance is already running - open the appropriate
window and terminate. */
if (args.no_remote)
break;
if (!open_requested_window (argc, argv, 1))
return 0; /* ready */
/* Start a new instance on error. */
break;
}
/* Locate the list of keyservers. */
keyservers_configname = g_build_filename (gnupg_homedir, "keyservers", NULL);
/* Read the list of available keyservers. */
keyserver_read_list (keyservers_configname);
gpa_options_update_default_key (gpa_options_get_instance ());
/* Now, make sure there are reasonable defaults for the default key
and keyserver. */
if (!gpa_options_get_default_keyserver (gpa_options_get_instance ()))
{
GList *keyservers = keyserver_get_as_glist ();
gpa_options_set_default_keyserver (gpa_options_get_instance (),
keyservers->data);
}
/* Initialize the file watch facility. */
gpa_init_filewatch ();
/* Startup whatever has been requested by the user. */
if (!args.start_only_server)
open_requested_window (argc, argv, 0);
gtk_main ();
return 0;
}
diff --git a/src/server.c b/src/server.c
index 78bb4b9..023eaf8 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1,2133 +1,2146 @@
/* server.c - The UI server part of GPA.
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 <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#ifndef HAVE_W32_SYSTEM
# include <sys/socket.h>
# include <sys/un.h>
#endif /*HAVE_W32_SYSTEM*/
#include <gpgme.h>
#include <glib.h>
#include <assuan.h>
#include "gpa.h"
#include "i18n.h"
#include "gpastreamencryptop.h"
#include "gpastreamsignop.h"
#include "gpastreamdecryptop.h"
#include "gpastreamverifyop.h"
#include "gpafileop.h"
#include "gpafileencryptop.h"
#include "gpafilesignop.h"
#include "gpafiledecryptop.h"
#include "gpafileverifyop.h"
#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
/* The object used to keep track of the a connection's state. */
struct conn_ctrl_s;
typedef struct conn_ctrl_s *conn_ctrl_t;
struct conn_ctrl_s
{
/* True if we are currently processing a command. */
int in_command;
/* NULL or continuation function for a command. */
void (*cont_cmd) (assuan_context_t, gpg_error_t);
/* Flag indicating that the client died while a continuation was
still registyered. */
int client_died;
/* This is a helper to detect that the unfinished error code actually
comes from our command handler. */
int is_unfinished;
/* An GPAOperation object. */
GpaOperation *gpa_op;
/* File descriptors used by the gpgme callbacks. */
int input_fd;
int output_fd;
/* File descriptor set with the MESSAGE command. */
int message_fd;
/* Flag indicating the the output shall be binary. */
int output_binary;
/* Channels used with the gpgme callbacks. */
GIOChannel *input_channel;
GIOChannel *output_channel;
GIOChannel *message_channel;
/* List of collected recipients. */
GSList *recipients;
/* Array of keys already prepared for RECIPIENTS. */
gpgme_key_t *recipient_keys;
/* The protocol as selected by the user. */
gpgme_protocol_t selected_protocol;
/* The current sender address (malloced) and a flag telleing whether
the sender ist just informational. */
gchar *sender;
int sender_just_info;
gpgme_protocol_t sender_protocol_hint;
/* Session information: A session number and a malloced title or NULL. */
unsigned int session_number;
char *session_title;
/* The list of all files to be processed. */
GList *files;
gboolean files_finished;
};
/* The number of active connections. */
static int connection_counter;
/* A flag requesting a shutdown. */
static gboolean shutdown_pending;
/* The nonce used by the server connection. This nonce is required
under Windows to emulate Unix Domain Sockets. This is managed by
libassuan but we need to store the nonce in the application. Under
Unix this is just a stub. */
static assuan_sock_nonce_t socket_nonce;
/* Forward declarations. */
static void run_server_continuation (assuan_context_t ctx, gpg_error_t err);
static int
not_finished (conn_ctrl_t ctrl)
{
ctrl->is_unfinished = 1;
return gpg_error (GPG_ERR_UNFINISHED);
}
/* Test whether LINE contains thye option NAME. An optional argument
of the option is ignored. For example with NAME being "--protocol"
this function returns true for "--protocol" as well as for
"--protocol=foo". The returned pointer points right behind the
option name, which may be an equal sign, Nul or a space. If tehre
is no option NAME, false (i.e. NULL) is returned.
*/
static const char *
has_option_name (const char *line, const char *name)
{
const char *s;
int n = strlen (name);
s = strstr (line, name);
return (s && (s == line || spacep (s-1))
&& (!s[n] || spacep (s+n) || s[n] == '=')) ? (s+n) : NULL;
}
/* Check whether LINE contains the option NAME. */
static int
has_option (const char *line, const char *name)
{
const char *s;
int n = strlen (name);
s = strstr (line, name);
return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
}
/* Skip over options. */
static char *
skip_options (char *line)
{
while (spacep (line))
line++;
while ( *line == '-' && line[1] == '-' )
{
while (*line && !spacep (line))
line++;
while (spacep (line))
line++;
}
return line;
}
/* Helper to be used as a GFunc for free. */
static void
free_func (void *p, void *dummy)
{
(void)dummy;
g_free (p);
}
static ssize_t
my_gpgme_read_cb (void *opaque, void *buffer, size_t size)
{
conn_ctrl_t ctrl = opaque;
GIOStatus status;
size_t nread;
int retval;
/* g_debug ("my_gpgme_read_cb: requesting %d bytes\n", (int)size); */
status = g_io_channel_read_chars (ctrl->input_channel, buffer, size,
&nread, NULL);
if (status == G_IO_STATUS_AGAIN
|| (status == G_IO_STATUS_NORMAL && !nread))
{
errno = EAGAIN;
retval = -1;
}
else if (status == G_IO_STATUS_NORMAL)
retval = (int)nread;
else if (status == G_IO_STATUS_EOF)
retval = 0;
else
{
errno = EIO;
retval = -1;
}
/* g_debug ("my_gpgme_read_cb: got status=%x, %d bytes, retval=%d\n", */
/* status, (int)size, retval); */
return retval;
}
static ssize_t
my_gpgme_write_cb (void *opaque, const void *buffer, size_t size)
{
conn_ctrl_t ctrl = opaque;
GIOStatus status;
size_t nwritten;
int retval;
status = g_io_channel_write_chars (ctrl->output_channel, buffer, size,
&nwritten, NULL);
if (status == G_IO_STATUS_AGAIN)
{
errno = EAGAIN;
retval = -1;
}
else if (status == G_IO_STATUS_NORMAL)
retval = (int)nwritten;
else
{
errno = EIO;
retval = 1;
}
return retval;
}
static struct gpgme_data_cbs my_gpgme_data_cbs =
{
my_gpgme_read_cb,
my_gpgme_write_cb,
NULL,
NULL
};
static ssize_t
my_gpgme_message_read_cb (void *opaque, void *buffer, size_t size)
{
conn_ctrl_t ctrl = opaque;
GIOStatus status;
size_t nread;
int retval;
status = g_io_channel_read_chars (ctrl->message_channel, buffer, size,
&nread, NULL);
if (status == G_IO_STATUS_AGAIN
|| (status == G_IO_STATUS_NORMAL && !nread))
{
errno = EAGAIN;
retval = -1;
}
else if (status == G_IO_STATUS_NORMAL)
retval = (int)nread;
else if (status == G_IO_STATUS_EOF)
retval = 0;
else
{
errno = EIO;
retval = -1;
}
return retval;
}
static struct gpgme_data_cbs my_gpgme_message_cbs =
{
my_gpgme_message_read_cb,
NULL,
NULL,
NULL
};
static ssize_t
my_devnull_write_cb (void *opaque, const void *buffer, size_t size)
{
return size;
}
static struct gpgme_data_cbs my_devnull_data_cbs =
{
NULL,
my_devnull_write_cb,
NULL,
NULL
};
/* Release the recipients stored in the connection context. */
static void
release_recipients (conn_ctrl_t ctrl)
{
if (ctrl->recipients)
{
g_slist_foreach (ctrl->recipients, free_func, NULL);
g_slist_free (ctrl->recipients);
ctrl->recipients = NULL;
}
}
static void
free_file_item (gpa_file_item_t item)
{
if (item->filename_in)
g_free (item->filename_in);
if (item->filename_out)
g_free (item->filename_out);
}
static void
release_files (conn_ctrl_t ctrl)
{
if (! ctrl->files)
return;
g_list_foreach (ctrl->files, (GFunc) free_file_item, NULL);
g_list_free (ctrl->files);
ctrl->files = NULL;
ctrl->files_finished = FALSE;
}
static void
release_keys (gpgme_key_t *keys)
{
if (keys)
{
int idx;
for (idx=0; keys[idx]; idx++)
gpgme_key_unref (keys[idx]);
g_free (keys);
}
}
/* Reset already prepared keys. */
static void
reset_prepared_keys (conn_ctrl_t ctrl)
{
release_keys (ctrl->recipient_keys);
ctrl->recipient_keys = NULL;
ctrl->selected_protocol = GPGME_PROTOCOL_UNKNOWN;
}
/* Helper to parse an protocol option. */
static gpg_error_t
parse_protocol_option (assuan_context_t ctx, char *line, int mandatory,
gpgme_protocol_t *r_protocol)
{
*r_protocol = GPGME_PROTOCOL_UNKNOWN;
if (has_option (line, "--protocol=OpenPGP"))
*r_protocol = GPGME_PROTOCOL_OpenPGP;
else if (has_option (line, "--protocol=CMS"))
*r_protocol = GPGME_PROTOCOL_CMS;
else if (has_option_name (line, "--protocol"))
return set_error (GPG_ERR_ASS_PARAMETER, "invalid protocol");
else if (mandatory)
return set_error (GPG_ERR_ASS_PARAMETER, "no protocol specified");
return 0;
}
static void
close_message_fd (conn_ctrl_t ctrl)
{
if (ctrl->message_fd != -1)
{
close (ctrl->message_fd);
ctrl->message_fd = -1;
}
}
static void
finish_io_streams (assuan_context_t ctx,
gpgme_data_t *r_input_data, gpgme_data_t *r_output_data,
gpgme_data_t *r_message_data)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
if (r_input_data)
gpgme_data_release (*r_input_data);
if (r_output_data)
gpgme_data_release (*r_output_data);
if (r_message_data)
gpgme_data_release (*r_message_data);
if (ctrl->input_channel)
{
g_io_channel_shutdown (ctrl->input_channel, 0, NULL);
ctrl->input_channel = NULL;
}
if (ctrl->output_channel)
{
g_io_channel_shutdown (ctrl->output_channel, 0, NULL);
ctrl->output_channel = NULL;
}
if (ctrl->message_channel)
{
g_io_channel_shutdown (ctrl->message_channel, 0, NULL);
ctrl->message_channel = NULL;
}
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
ctrl->input_fd = -1;
ctrl->output_fd = -1;
}
/* Translate the input and output file descriptors and return an error
if they are not set. */
static gpg_error_t
translate_io_streams (assuan_context_t ctx)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
ctrl->input_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
if (ctrl->input_fd == -1)
return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
ctrl->output_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
if (ctrl->output_fd == -1)
return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
return 0;
}
static gpg_error_t
prepare_io_streams (assuan_context_t ctx,
gpgme_data_t *r_input_data, gpgme_data_t *r_output_data,
gpgme_data_t *r_message_data)
{
gpg_error_t err;
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
if (r_input_data)
*r_input_data = NULL;
if (r_output_data)
*r_output_data = NULL;
if (r_message_data)
*r_message_data = NULL;
if (ctrl->input_fd != -1 && r_input_data)
{
#ifdef HAVE_W32_SYSTEM
ctrl->input_channel = g_io_channel_win32_new_fd (ctrl->input_fd);
#else
ctrl->input_channel = g_io_channel_unix_new (ctrl->input_fd);
#endif
if (!ctrl->input_channel)
{
g_debug ("error creating input channel");
err = gpg_error (GPG_ERR_EIO);
goto leave;
}
g_io_channel_set_encoding (ctrl->input_channel, NULL, NULL);
g_io_channel_set_buffered (ctrl->input_channel, FALSE);
}
if (ctrl->output_fd != -1 && r_output_data)
{
#ifdef HAVE_W32_SYSTEM
ctrl->output_channel = g_io_channel_win32_new_fd (ctrl->output_fd);
#else
ctrl->output_channel = g_io_channel_unix_new (ctrl->output_fd);
#endif
if (!ctrl->output_channel)
{
g_debug ("error creating output channel");
err = gpg_error (GPG_ERR_EIO);
goto leave;
}
g_io_channel_set_encoding (ctrl->output_channel, NULL, NULL);
g_io_channel_set_buffered (ctrl->output_channel, FALSE);
}
if (ctrl->message_fd != -1 && r_message_data)
{
#ifdef HAVE_W32_SYSTEM
ctrl->message_channel = g_io_channel_win32_new_fd (ctrl->message_fd);
#else
ctrl->message_channel = g_io_channel_unix_new (ctrl->message_fd);
#endif
if (!ctrl->message_channel)
{
g_debug ("error creating message channel");
err = gpg_error (GPG_ERR_EIO);
goto leave;
}
g_io_channel_set_encoding (ctrl->message_channel, NULL, NULL);
g_io_channel_set_buffered (ctrl->message_channel, FALSE);
}
if (ctrl->input_channel)
{
err = gpgme_data_new_from_cbs (r_input_data, &my_gpgme_data_cbs, ctrl);
if (err)
goto leave;
}
if (ctrl->output_channel)
{
err = gpgme_data_new_from_cbs (r_output_data, &my_gpgme_data_cbs, ctrl);
if (err)
goto leave;
if (ctrl->output_binary)
gpgme_data_set_encoding (*r_output_data, GPGME_DATA_ENCODING_BINARY);
}
if (ctrl->message_channel)
{
err = gpgme_data_new_from_cbs (r_message_data,
&my_gpgme_message_cbs, ctrl);
if (err)
goto leave;
}
err = 0;
leave:
if (err)
finish_io_streams (ctx, r_input_data, r_output_data, r_message_data);
return err;
}
static const char hlp_session[] =
"SESSION <number> [<string>]\n"
"\n"
"The NUMBER is an arbitrary value, a server may use to associate\n"
"simultaneous running sessions. It is a 32 bit unsigned integer\n"
"with 0 as a special value indicating that no session association\n"
"shall be done.\n"
"\n"
"If STRING is given, the server may use this as the title of a\n"
"window or, in the case of an email operation, to extract the\n"
"sender's address. The string may contain spaces; thus no\n"
"plus-escaping is used.\n"
"\n"
"This command may be used at any time and overrides the effect of\n"
"the last command. A RESET command undoes the effect of this\n"
"command.";
static gpg_error_t
cmd_session (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
char *endp;
line = skip_options (line);
ctrl->session_number = strtoul (line, &endp, 10);
for (line = endp; spacep (line); line++)
;
xfree (ctrl->session_title);
ctrl->session_title = *line? xstrdup (line) : NULL;
return assuan_process_done (ctx, 0);
}
static const char hlp_recipient[] =
"RECIPIENT <recipient>\n"
"\n"
"Set the recipient for the encryption. <recipient> is an RFC2822\n"
"recipient name. This command may or may not check the recipient for\n"
"validity right away; if it does not (as here) all recipients are\n"
"checked at the time of the ENCRYPT command. All RECIPIENT commands\n"
"are cumulative until a RESET or an successful ENCRYPT command.";
static gpg_error_t
cmd_recipient (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err = 0;
reset_prepared_keys (ctrl);
if (*line)
ctrl->recipients = g_slist_append (ctrl->recipients, xstrdup (line));
return assuan_process_done (ctx, err);
}
static const char hlp_message[] =
"MESSAGE FD[=<n>]\n"
"\n"
"Set the file descriptor to read a message of a detached signature to N.";
static gpg_error_t
cmd_message (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t sysfd;
int fd;
err = assuan_command_parse_fd (ctx, line, &sysfd);
if (!err)
{
fd = translate_sys2libc_fd (sysfd, 0);
if (fd == -1)
err = set_error (GPG_ERR_ASS_NO_INPUT, NULL);
else
ctrl->message_fd = fd;
}
return assuan_process_done (ctx, err);
}
/* Continuation for cmd_encrypt. */
static void
cont_encrypt (assuan_context_t ctx, gpg_error_t err)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
g_debug ("cont_encrypt called with ERR=%s <%s>",
gpg_strerror (err), gpg_strsource (err));
finish_io_streams (ctx, NULL, NULL, NULL);
if (!err)
release_recipients (ctrl);
assuan_process_done (ctx, err);
}
static const char hlp_encrypt[] =
"ENCRYPT --protocol=OpenPGP|CMS\n"
"\n"
"Encrypt the data received on INPUT to OUTPUT.";
static gpg_error_t
cmd_encrypt (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
gpgme_protocol_t protocol = 0;
GpaStreamEncryptOperation *op;
gpgme_data_t input_data = NULL;
gpgme_data_t output_data = NULL;
err = parse_protocol_option (ctx, line, 1, &protocol);
if (err)
goto leave;
if (protocol != ctrl->selected_protocol)
{
if (ctrl->selected_protocol != GPGME_PROTOCOL_UNKNOWN)
g_debug ("note: protocol does not match the one from PREP_ENCRYPT");
reset_prepared_keys (ctrl);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
goto leave;
}
err = translate_io_streams (ctx);
if (err)
goto leave;
err = prepare_io_streams (ctx, &input_data, &output_data, NULL);
if (err)
goto leave;
ctrl->cont_cmd = cont_encrypt;
op = gpa_stream_encrypt_operation_new (NULL, input_data, output_data,
ctrl->recipients,
ctrl->recipient_keys,
protocol, 0);
input_data = output_data = NULL;
g_signal_connect_swapped (G_OBJECT (op), "completed",
G_CALLBACK (run_server_continuation), ctx);
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), NULL);
g_signal_connect_swapped (G_OBJECT (op), "status",
G_CALLBACK (assuan_write_status), ctx);
return not_finished (ctrl);
leave:
finish_io_streams (ctx, &input_data, &output_data, NULL);
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
ctrl->input_fd = -1;
ctrl->output_fd = -1;
return assuan_process_done (ctx, err);
}
/* Continuation for cmd_prep_encrypt. */
static void
cont_prep_encrypt (assuan_context_t ctx, gpg_error_t err)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
g_debug ("cont_prep_encrypt called with ERR=%s <%s>",
gpg_strerror (err), gpg_strsource (err));
if (!err)
{
release_keys (ctrl->recipient_keys);
ctrl->recipient_keys = gpa_stream_encrypt_operation_get_keys
(GPA_STREAM_ENCRYPT_OPERATION (ctrl->gpa_op),
&ctrl->selected_protocol);
if (ctrl->recipient_keys)
g_print ("received some keys\n");
else
g_print ("received no keys\n");
}
if (ctrl->gpa_op)
{
g_object_unref (ctrl->gpa_op);
ctrl->gpa_op = NULL;
}
assuan_process_done (ctx, err);
}
static const char hlp_prep_encrypt[] =
"PREP_ENCRYPT [--protocol=OpenPGP|CMS]\n"
"\n"
"Dummy encryption command used to check whether the given recipients\n"
"are all valid and to tell the client the preferred protocol.";
static gpg_error_t
cmd_prep_encrypt (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
gpgme_protocol_t protocol;
GpaStreamEncryptOperation *op;
err = parse_protocol_option (ctx, line, 0, &protocol);
if (err)
goto leave;
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
goto leave;
}
reset_prepared_keys (ctrl);
if (ctrl->gpa_op)
{
g_debug ("Oops: there is still an GPA_OP active\n");
g_object_unref (ctrl->gpa_op);
ctrl->gpa_op = NULL;
}
ctrl->cont_cmd = cont_prep_encrypt;
op = gpa_stream_encrypt_operation_new (NULL, NULL, NULL,
ctrl->recipients,
ctrl->recipient_keys,
protocol, 0);
/* Store that instance for later use but also install a signal
handler to unref it. */
g_object_ref (op);
ctrl->gpa_op = GPA_OPERATION (op);
g_signal_connect_swapped (G_OBJECT (op), "completed",
G_CALLBACK (run_server_continuation), ctx);
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), NULL);
g_signal_connect_swapped (G_OBJECT (op), "status",
G_CALLBACK (assuan_write_status), ctx);
return not_finished (ctrl);
leave:
return assuan_process_done (ctx, err);
}
static const char hlp_sender[] =
"SENDER <email>\n"
"\n"
"EMAIL is the plain ASCII encoded address (\"addr-spec\" as per\n"
"RFC-2822) enclosed in angle brackets. The address set by this\n"
"command is valid until a successful \"SIGN\" command or until a\n"
"\"RESET\" command. A second command overrides the effect of\n"
"the first one; if EMAIL is not given the server shall use the\n"
"default signing key.";
static gpg_error_t
cmd_sender (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
gpgme_protocol_t protocol;
err = parse_protocol_option (ctx, line, 0, &protocol);
if (err)
goto leave;
ctrl->sender_just_info = has_option (line, "--info");
line = skip_options (line);
xfree (ctrl->sender);
ctrl->sender = NULL;
if (*line)
ctrl->sender = xstrdup (line);
if (!err)
ctrl->sender_protocol_hint = protocol;
leave:
return assuan_process_done (ctx, err);
}
/* Continuation for cmd_sign. */
static void
cont_sign (assuan_context_t ctx, gpg_error_t err)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
g_debug ("cont_sign called with ERR=%s <%s>",
gpg_strerror (err), gpg_strsource (err));
finish_io_streams (ctx, NULL, NULL, NULL);
if (!err)
{
xfree (ctrl->sender);
ctrl->sender = NULL;
ctrl->sender_protocol_hint = GPGME_PROTOCOL_UNKNOWN;
}
assuan_process_done (ctx, err);
}
static const char hlp_sign[] =
"SIGN --protocol=OpenPGP|CMS [--detached]\n"
"\n"
"Sign the data received on INPUT to OUTPUT.";
static gpg_error_t
cmd_sign (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
gpgme_protocol_t protocol;
gboolean detached;
GpaStreamSignOperation *op;
gpgme_data_t input_data = NULL;
gpgme_data_t output_data = NULL;
err = parse_protocol_option (ctx, line, 1, &protocol);
if (err)
goto leave;
detached = has_option (line, "--detached");
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
goto leave;
}
err = translate_io_streams (ctx);
if (err)
goto leave;
err = prepare_io_streams (ctx, &input_data, &output_data, NULL);
if (err)
goto leave;
ctrl->cont_cmd = cont_sign;
op = gpa_stream_sign_operation_new (NULL, input_data, output_data,
ctrl->sender, protocol, detached);
input_data = output_data = NULL;
g_signal_connect_swapped (G_OBJECT (op), "completed",
G_CALLBACK (run_server_continuation), ctx);
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), NULL);
g_signal_connect_swapped (G_OBJECT (op), "status",
G_CALLBACK (assuan_write_status), ctx);
return not_finished (ctrl);
leave:
finish_io_streams (ctx, &input_data, &output_data, NULL);
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
ctrl->input_fd = -1;
ctrl->output_fd = -1;
return assuan_process_done (ctx, err);
}
/* Continuation for cmd_decrypt. */
static void
cont_decrypt (assuan_context_t ctx, gpg_error_t err)
{
g_debug ("cont_decrypt called with ERR=%s <%s>",
gpg_strerror (err), gpg_strsource (err));
finish_io_streams (ctx, NULL, NULL, NULL);
assuan_process_done (ctx, err);
}
static const char hlp_decrypt[] =
"DECRYPT --protocol=OpenPGP|CMS [--no-verify]\n"
"\n"
"Decrypt a message given by the source set with the INPUT command\n"
"and write the plaintext to the sink set with the OUTPUT command.\n"
"\n"
"If the option --no-verify is given, the server should not try to\n"
"verify a signature, in case the input data is an OpenPGP combined\n"
"message.";
static gpg_error_t
cmd_decrypt (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
gpgme_protocol_t protocol = 0;
int no_verify;
GpaStreamDecryptOperation *op;
gpgme_data_t input_data = NULL;
gpgme_data_t output_data = NULL;
err = parse_protocol_option (ctx, line, 1, &protocol);
if (err)
goto leave;
no_verify = has_option (line, "--no-verify");
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
goto leave;
}
err = translate_io_streams (ctx);
if (err)
goto leave;
err = prepare_io_streams (ctx, &input_data, &output_data, NULL);
if (err)
goto leave;
ctrl->cont_cmd = cont_decrypt;
op = gpa_stream_decrypt_operation_new (NULL, input_data, output_data,
no_verify, protocol,
ctrl->session_title);
input_data = output_data = NULL;
g_signal_connect_swapped (G_OBJECT (op), "completed",
G_CALLBACK (run_server_continuation), ctx);
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), NULL);
g_signal_connect_swapped (G_OBJECT (op), "status",
G_CALLBACK (assuan_write_status), ctx);
return not_finished (ctrl);
leave:
finish_io_streams (ctx, &input_data, &output_data, NULL);
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
ctrl->input_fd = -1;
ctrl->output_fd = -1;
return assuan_process_done (ctx, err);
}
/* Continuation for cmd_verify. */
static void
cont_verify (assuan_context_t ctx, gpg_error_t err)
{
g_debug ("cont_verify called with ERR=%s <%s>",
gpg_strerror (err), gpg_strsource (err));
finish_io_streams (ctx, NULL, NULL, NULL);
assuan_process_done (ctx, err);
}
static const char hlp_verify[] =
"VERIFY --protocol=OpenPGP|CMS [--silent]\n"
"\n"
"Verify a message. Depending on the combination of the\n"
"sources/sinks set by the commands MESSAGE, INPUT and OUTPUT, the\n"
"verification mode is selected like this:\n"
"\n"
"MESSAGE and INPUT\n"
" This indicates a detached signature. Output data is not applicable.\n"
"\n"
"INPUT \n"
" This indicates an opaque signature. As no output command has\n"
" been given, we are only required to check the signature.\n"
"\n"
"INPUT and OUTPUT\n"
" This indicates an opaque signature. We write the signed data to\n"
" the file descriptor set by the OUTPUT command. This data is even\n"
" written if the signature can't be verified.\n"
"\n"
"With the option --silent we won't display any dialog; this is for\n"
"example used by the client to get the content of an opaque signed\n"
"message. This status message is always send before an OK response:\n"
"\n"
" SIGSTATUS <flag> <displaystring>\n"
"\n"
"Defined FLAGS are:\n"
"\n"
" none\n"
" The message has a signature but it could not not be verified\n"
" due to a missing key.\n"
"\n"
" green\n"
" The signature is fully valid.\n"
"\n"
" yellow\n"
" The signature is valid but additional information was shown\n"
" regarding the validity of the key.\n"
"\n"
" red\n"
" The signature is not valid. \n"
"\n"
"The DISPLAYSTRING is a percent-and-plus-encoded string with a short\n"
"human readable description of the status.";
static gpg_error_t
cmd_verify (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
gpgme_protocol_t protocol = 0;
int silent;
GpaStreamVerifyOperation *op;
gpgme_data_t input_data = NULL;
gpgme_data_t output_data = NULL;
gpgme_data_t message_data = NULL;
enum { VERIFY_DETACH, VERIFY_OPAQUE, VERIFY_OPAQUE_WITH_OUTPUT } op_mode;
err = parse_protocol_option (ctx, line, 1, &protocol);
if (err)
goto leave;
silent = has_option (line, "--silent");
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
goto leave;
}
/* Note: We can't use translate_io_streams because that returns an
error if one is not opened but that is not an error here. */
ctrl->input_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
ctrl->output_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
if (ctrl->message_fd != -1 && ctrl->input_fd != -1
&& ctrl->output_fd == -1)
op_mode = VERIFY_DETACH;
else if (ctrl->message_fd == -1 && ctrl->input_fd != -1
&& ctrl->output_fd == -1)
op_mode = VERIFY_OPAQUE;
else if (ctrl->message_fd == -1 && ctrl->input_fd != -1
&& ctrl->output_fd != -1)
op_mode = VERIFY_OPAQUE_WITH_OUTPUT;
else
{
err = set_error (GPG_ERR_CONFLICT, "invalid verify mode");
goto leave;
}
err = prepare_io_streams (ctx, &input_data, &output_data, &message_data);
if (! err && op_mode == VERIFY_OPAQUE)
err = gpgme_data_new_from_cbs (&output_data, &my_devnull_data_cbs, ctrl);
if (err)
goto leave;
ctrl->cont_cmd = cont_verify;
op = gpa_stream_verify_operation_new (NULL, input_data, message_data,
output_data, silent, protocol,
ctrl->session_title);
input_data = output_data = message_data = NULL;
g_signal_connect_swapped (G_OBJECT (op), "completed",
G_CALLBACK (run_server_continuation), ctx);
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), NULL);
g_signal_connect_swapped (G_OBJECT (op), "status",
G_CALLBACK (assuan_write_status), ctx);
return not_finished (ctrl);
leave:
finish_io_streams (ctx, &input_data, &output_data, &message_data);
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
ctrl->input_fd = -1;
ctrl->output_fd = -1;
return assuan_process_done (ctx, err);
}
static const char hlp_start_keymanager[] =
"START_KEYMANAGER\n"
"\n"
"Pop up the key manager window. The client expects that the key\n"
"manager is brought into the foregound and that this command\n"
"immediatley returns.";
static gpg_error_t
cmd_start_keymanager (assuan_context_t ctx, char *line)
{
gpa_open_key_manager (NULL, NULL);
return assuan_process_done (ctx, 0);
}
static const char hlp_start_clipboard[] =
"START_CLIPBOARD\n"
"\n"
"Pop up the clipboard window. The client expects that the\n"
"clipboard is brought into the foregound and that this command\n"
"immediatley returns.";
static gpg_error_t
cmd_start_clipboard (assuan_context_t ctx, char *line)
{
gpa_open_clipboard (NULL, NULL);
return assuan_process_done (ctx, 0);
}
static const char hlp_start_filemanager[] =
"START_FILEMANAGER\n"
"\n"
"Pop up the file manager window. The client expects that the file\n"
"manager is brought into the foregound and that this command\n"
"immediatley returns.";
static gpg_error_t
cmd_start_filemanager (assuan_context_t ctx, char *line)
{
gpa_open_filemanager (NULL, NULL);
return assuan_process_done (ctx, 0);
}
#ifdef ENABLE_CARD_MANAGER
static const char hlp_start_cardmanager[] =
"START_CARDMANAGER\n"
"\n"
"Pop up the card manager window. The client expects that the key\n"
"manager is brought into the foregound and that this command\n"
"immediatley returns.";
static gpg_error_t
cmd_start_cardmanager (assuan_context_t ctx, char *line)
{
gpa_open_cardmanager (NULL, NULL);
return assuan_process_done (ctx, 0);
}
#endif /*ENABLE_CARD_MANAGER*/
static const char hlp_start_confdialog[] =
"START_CONFDIALOG\n"
"\n"
"Pop up the configure dialog. The client expects that the key\n"
"manager is brought into the foregound and that this command\n"
"immediatley returns.";
static gpg_error_t
cmd_start_confdialog (assuan_context_t ctx, char *line)
{
gpa_open_settings_dialog (NULL, NULL);
return assuan_process_done (ctx, 0);
}
static const char hlp_getinfo[] =
"GETINFO <what>\n"
"\n"
"Multipurpose function to return a variety of information.\n"
"Supported values for WHAT are:\n"
"\n"
" version - Return the version of the program.\n"
" name - Return the name of the program\n"
" pid - Return the process id of the server.";
static gpg_error_t
cmd_getinfo (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (!strcmp (line, "version"))
{
const char *s = PACKAGE_NAME " " PACKAGE_VERSION;
err = assuan_send_data (ctx, s, strlen (s));
}
else if (!strcmp (line, "pid"))
{
char numbuf[50];
snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
err = assuan_send_data (ctx, numbuf, strlen (numbuf));
}
else if (!strcmp (line, "name"))
{
const char *s = PACKAGE_NAME;
err = assuan_send_data (ctx, s, strlen (s));
}
else
err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
return assuan_process_done (ctx, err);
}
/* FILE <file> [--continued]
Set the files on which to operate.
*/
static gpg_error_t
cmd_file (assuan_context_t ctx, char *line)
{
gpg_error_t err = 0;
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gboolean continued;
gpa_file_item_t file_item;
char *tail;
continued = has_option (line, "--continued");
if (ctrl->files_finished)
release_files (ctrl);
tail = line;
while (*tail && ! spacep (tail))
tail++;
*tail = '\0';
decode_percent_string (line);
file_item = g_malloc0 (sizeof (*file_item));
file_item->filename_in = g_strdup (line);
ctrl->files = g_list_append (ctrl->files, file_item);
if (! continued)
ctrl->files_finished = TRUE;
return assuan_process_done (ctx, err);
}
static gpg_error_t
impl_encrypt_sign_files (assuan_context_t ctx, int encr, int sign)
{
gpg_error_t err = 0;
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
GpaFileOperation *op;
if (! ctrl->files)
{
err = set_error (GPG_ERR_ASS_SYNTAX, "no files specified");
return assuan_process_done (ctx, err);
}
else if (! ctrl->files_finished)
{
err = set_error (GPG_ERR_ASS_SYNTAX, "more files expected");
return assuan_process_done (ctx, err);
}
/* FIXME: Needs a root window. Need to set "sign" default. */
if (encr && sign)
op = (GpaFileOperation *)
gpa_file_encrypt_sign_operation_new (NULL, ctrl->files, FALSE);
else if (encr)
op = (GpaFileOperation *)
gpa_file_encrypt_operation_new (NULL, ctrl->files, FALSE);
else
op = (GpaFileOperation *)
gpa_file_sign_operation_new (NULL, ctrl->files, FALSE);
/* Ownership of CTRL->files was passed to callee. */
ctrl->files = NULL;
ctrl->files_finished = FALSE;
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), NULL);
return assuan_process_done (ctx, err);
}
/* ENCRYPT_FILES --nohup */
static gpg_error_t
cmd_encrypt_files (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (! has_option (line, "--nohup"))
{
err = set_error (GPG_ERR_ASS_PARAMETER, "file ops require --nohup");
return assuan_process_done (ctx, err);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
return assuan_process_done (ctx, err);
}
return impl_encrypt_sign_files (ctx, 1, 0);
}
/* SIGN_FILES --nohup */
static gpg_error_t
cmd_sign_files (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (! has_option (line, "--nohup"))
{
err = set_error (GPG_ERR_ASS_PARAMETER, "file ops require --nohup");
return assuan_process_done (ctx, err);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
return assuan_process_done (ctx, err);
}
return impl_encrypt_sign_files (ctx, 0, 1);
}
/* ENCRYPT_SIGN_FILES --nohup */
static gpg_error_t
cmd_encrypt_sign_files (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (! has_option (line, "--nohup"))
{
err = set_error (GPG_ERR_ASS_PARAMETER, "file ops require --nohup");
return assuan_process_done (ctx, err);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
return assuan_process_done (ctx, err);
}
return impl_encrypt_sign_files (ctx, 1, 1);
}
static gpg_error_t
impl_decrypt_verify_files (assuan_context_t ctx, int decrypt, int verify)
{
gpg_error_t err = 0;
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
GpaFileOperation *op;
if (! ctrl->files)
{
err = set_error (GPG_ERR_ASS_SYNTAX, "no files specified");
return assuan_process_done (ctx, err);
}
else if (! ctrl->files_finished)
{
err = set_error (GPG_ERR_ASS_SYNTAX, "more files expected");
return assuan_process_done (ctx, err);
}
/* FIXME: Needs a root window. Need to enable "verify". */
if (decrypt && verify)
op = (GpaFileOperation *)
gpa_file_decrypt_verify_operation_new (NULL, ctrl->files);
else if (decrypt)
op = (GpaFileOperation *)
gpa_file_decrypt_operation_new (NULL, ctrl->files);
else
op = (GpaFileOperation *)
gpa_file_verify_operation_new (NULL, ctrl->files);
/* Ownership of CTRL->files was passed to callee. */
ctrl->files = NULL;
ctrl->files_finished = FALSE;
g_signal_connect (G_OBJECT (op), "completed",
G_CALLBACK (g_object_unref), NULL);
return assuan_process_done (ctx, err);
}
/* DECRYPT_FILES --nohup */
static gpg_error_t
cmd_decrypt_files (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (! has_option (line, "--nohup"))
{
err = set_error (GPG_ERR_ASS_PARAMETER, "file ops require --nohup");
return assuan_process_done (ctx, err);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
return assuan_process_done (ctx, err);
}
return impl_decrypt_verify_files (ctx, 1, 0);
}
/* VERIFY_FILES --nohup */
static gpg_error_t
cmd_verify_files (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (! has_option (line, "--nohup"))
{
err = set_error (GPG_ERR_ASS_PARAMETER, "file ops require --nohup");
return assuan_process_done (ctx, err);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
return assuan_process_done (ctx, err);
}
return impl_decrypt_verify_files (ctx, 0, 1);
}
/* DECRYPT_VERIFY_FILES --nohup */
static gpg_error_t
cmd_decrypt_verify_files (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (! has_option (line, "--nohup"))
{
err = set_error (GPG_ERR_ASS_PARAMETER, "file ops require --nohup");
return assuan_process_done (ctx, err);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
return assuan_process_done (ctx, err);
}
return impl_decrypt_verify_files (ctx, 1, 1);
}
/* IMPORT_FILES --nohup */
static gpg_error_t
cmd_import_files (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (! has_option (line, "--nohup"))
{
err = set_error (GPG_ERR_ASS_PARAMETER, "file ops require --nohup");
return assuan_process_done (ctx, err);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
return assuan_process_done (ctx, err);
}
err = set_error (GPG_ERR_NOT_IMPLEMENTED, "not implemented");
return assuan_process_done (ctx, err);
}
/* CHECKSUM_CREATE_FILES --nohup */
static gpg_error_t
cmd_checksum_create_files (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (! has_option (line, "--nohup"))
{
err = set_error (GPG_ERR_ASS_PARAMETER, "file ops require --nohup");
return assuan_process_done (ctx, err);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
return assuan_process_done (ctx, err);
}
err = set_error (GPG_ERR_NOT_IMPLEMENTED, "not implemented");
return assuan_process_done (ctx, err);
}
/* CHECKSUM_VERIFY_FILES --nohup */
static gpg_error_t
cmd_checksum_verify_files (assuan_context_t ctx, char *line)
{
gpg_error_t err;
if (! has_option (line, "--nohup"))
{
err = set_error (GPG_ERR_ASS_PARAMETER, "file ops require --nohup");
return assuan_process_done (ctx, err);
}
line = skip_options (line);
if (*line)
{
err = set_error (GPG_ERR_ASS_SYNTAX, NULL);
return assuan_process_done (ctx, err);
}
err = set_error (GPG_ERR_NOT_IMPLEMENTED, "not implemented");
return assuan_process_done (ctx, err);
}
+
+
+/* KILL_UISERVER */
+static gpg_error_t
+cmd_kill_uiserver (assuan_context_t ctx, char *line)
+{
+ (void)line;
+ shutdown_pending = TRUE;
+ return assuan_process_done (ctx, 0);
+}
+
+
static gpg_error_t
reset_notify (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
reset_prepared_keys (ctrl);
release_recipients (ctrl);
release_files (ctrl);
xfree (ctrl->sender);
ctrl->sender = NULL;
ctrl->sender_protocol_hint = GPGME_PROTOCOL_UNKNOWN;
finish_io_streams (ctx, NULL, NULL, NULL);
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
ctrl->input_fd = -1;
ctrl->output_fd = -1;
ctrl->output_binary = 0;
if (ctrl->gpa_op)
{
g_object_unref (ctrl->gpa_op);
ctrl->gpa_op = NULL;
}
ctrl->session_number = 0;
xfree (ctrl->session_title);
ctrl->session_title = NULL;
return 0;
}
static gpg_error_t
output_notify (assuan_context_t ctx, char *line)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
if (strstr (line, "--binary"))
ctrl->output_binary = 1;
else
ctrl->output_binary = 0;
/* Note: We also allow --armor and --base64 but because we don't
check for errors we don't need to parse them. */
return 0;
}
/* Tell libassuan about our commands. */
static int
register_commands (assuan_context_t ctx)
{
static struct {
const char *name;
assuan_handler_t handler;
const char * const help;
} table[] = {
{ "SESSION", cmd_session, hlp_session },
{ "RECIPIENT", cmd_recipient, hlp_recipient },
{ "INPUT", NULL },
{ "OUTPUT", NULL },
{ "MESSAGE", cmd_message, hlp_message },
{ "ENCRYPT", cmd_encrypt, hlp_encrypt },
{ "PREP_ENCRYPT", cmd_prep_encrypt, hlp_prep_encrypt },
{ "SENDER", cmd_sender, hlp_sender },
{ "SIGN", cmd_sign, hlp_sign },
{ "DECRYPT", cmd_decrypt, hlp_decrypt },
{ "VERIFY", cmd_verify, hlp_verify },
{ "START_KEYMANAGER", cmd_start_keymanager, hlp_start_keymanager },
{ "START_CLIPBOARD", cmd_start_clipboard, hlp_start_clipboard },
{ "START_FILEMANAGER", cmd_start_filemanager, hlp_start_filemanager },
#ifdef ENABLE_CARD_MANAGER
{ "START_CARDMANAGER", cmd_start_cardmanager, hlp_start_cardmanager },
#endif /*ENABLE_CARD_MANAGER*/
{ "START_CONFDIALOG", cmd_start_confdialog, hlp_start_confdialog },
{ "GETINFO", cmd_getinfo, hlp_getinfo },
{ "FILE", cmd_file },
{ "ENCRYPT_FILES", cmd_encrypt_files },
{ "SIGN_FILES", cmd_sign_files },
{ "ENCRYPT_SIGN_FILES", cmd_encrypt_sign_files },
{ "DECRYPT_FILES", cmd_decrypt_files },
{ "VERIFY_FILES", cmd_verify_files },
{ "DECRYPT_VERIFY_FILES", cmd_decrypt_verify_files },
{ "IMPORT_FILES", cmd_import_files },
{ "CHECKSUM_CREATE_FILES", cmd_checksum_create_files },
{ "CHECKSUM_VERIFY_FILES", cmd_checksum_verify_files },
+ { "KILL_UISERVER", cmd_kill_uiserver },
{ NULL }
};
int i, rc;
for (i=0; table[i].name; i++)
{
rc = assuan_register_command (ctx, table[i].name, table[i].handler,
table[i].help);
if (rc)
return rc;
}
return 0;
}
/* Prepare for a new connection on descriptor FD. */
static assuan_context_t
connection_startup (assuan_fd_t fd)
{
gpg_error_t err;
assuan_context_t ctx;
conn_ctrl_t ctrl;
/* Get an Assuan context for the already accepted file descriptor
FD. Allow descriptor passing. */
err = assuan_new (&ctx);
if (err)
{
g_debug ("failed to initialize the new connection: %s",
gpg_strerror (err));
return NULL;
}
err = assuan_init_socket_server (ctx, fd, (ASSUAN_SOCKET_SERVER_FDPASSING
| ASSUAN_SOCKET_SERVER_ACCEPTED));
if (err)
{
g_debug ("failed to initialize the new connection: %s",
gpg_strerror (err));
return NULL;
}
err = register_commands (ctx);
if (err)
{
g_debug ("failed to register commands with Assuan: %s",
gpg_strerror (err));
assuan_release (ctx);
return NULL;
}
ctrl = g_malloc0 (sizeof *ctrl);
assuan_set_pointer (ctx, ctrl);
assuan_set_log_stream (ctx, stderr);
assuan_register_reset_notify (ctx, reset_notify);
assuan_register_output_notify (ctx, output_notify);
ctrl->message_fd = -1;
connection_counter++;
return ctx;
}
/* Finish a connection. This releases all resources and needs to be
called becore the file descriptor is closed. */
static void
connection_finish (assuan_context_t ctx)
{
if (ctx)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
reset_notify (ctx, NULL);
assuan_release (ctx);
g_free (ctrl);
connection_counter--;
if (!connection_counter && shutdown_pending)
gtk_main_quit ();
}
}
/* If the assuan context CTX has a registered continuation function,
run it. */
static void
run_server_continuation (assuan_context_t ctx, gpg_error_t err)
{
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
void (*cont_cmd) (assuan_context_t, gpg_error_t);
if (!ctrl)
{
g_debug ("no context in gpa_run_server_continuation");
return;
}
g_debug ("calling gpa_run_server_continuation (%s)", gpg_strerror (err));
if (!ctrl->cont_cmd)
{
g_debug ("no continuation defined; using default");
assuan_process_done (ctx, err);
}
else if (ctrl->client_died)
{
g_debug ("not running continuation as client has disconnected");
connection_finish (ctx);
}
else
{
cont_cmd = ctrl->cont_cmd;
ctrl->cont_cmd = NULL;
cont_cmd (ctx, err);
}
g_debug ("leaving gpa_run_server_continuation");
}
/* This function is called by the main event loop if data can be read
from the status channel. */
static gboolean
receive_cb (GIOChannel *channel, GIOCondition condition, void *data)
{
assuan_context_t ctx = data;
conn_ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
assert (ctrl);
if (condition & G_IO_IN)
{
g_debug ("receive_cb");
if (ctrl->cont_cmd)
{
g_debug (" input received while waiting for continuation");
g_usleep (2000000);
}
else if (ctrl->in_command)
{
g_debug (" input received while still processing command");
g_usleep (2000000);
}
else
{
int done = 0;
ctrl->in_command++;
err = assuan_process_next (ctx, &done);
ctrl->in_command--;
if (err)
{
g_debug ("assuan_process_next returned: %s <%s>",
gpg_strerror (err), gpg_strsource (err));
}
else
{
g_debug ("assuan_process_next returned: %s",
done ? "done" : "success");
}
if (gpg_err_code (err) == GPG_ERR_EAGAIN)
; /* Ignore. */
else if (!err && done)
{
if (ctrl->cont_cmd)
ctrl->client_died = 1; /* Need to delay the cleanup. */
else
connection_finish (ctx);
return FALSE; /* Remove from the watch. */
}
else if (gpg_err_code (err) == GPG_ERR_UNFINISHED)
{
if (!ctrl->is_unfinished)
{
/* It is quite possible that some other subsystem
returns that error code. Tell the user about
this curiosity and finish the command. */
g_debug ("note: Unfinished error code not emitted by us");
if (ctrl->cont_cmd)
g_debug ("OOPS: pending continuation!");
assuan_process_done (ctx, err);
}
}
else
assuan_process_done (ctx, err);
}
}
return TRUE;
}
/* This function is called by the main event loop if the listen fd is
readable. The function runs the accept and prepares the
connection. */
static gboolean
accept_connection_cb (GIOChannel *listen_channel,
GIOCondition condition, void *data)
{
gpg_error_t err;
int listen_fd, fd;
struct sockaddr_un paddr;
socklen_t plen = sizeof paddr;
assuan_context_t ctx;
GIOChannel *channel;
unsigned int source_id;
g_debug ("new connection request");
#ifdef HAVE_W32_SYSTEM
listen_fd = g_io_channel_win32_get_fd (listen_channel);
#else
listen_fd = g_io_channel_unix_get_fd (listen_channel);
#endif
fd = accept (listen_fd, (struct sockaddr *)&paddr, &plen);
if (fd == -1)
{
g_debug ("error accepting connection: %s", strerror (errno));
goto leave;
}
if (assuan_sock_check_nonce ((assuan_fd_t) fd, &socket_nonce))
{
g_debug ("new connection at fd %d refused", fd);
goto leave;
}
g_debug ("new connection at fd %d", fd);
ctx = connection_startup ((assuan_fd_t) fd);
if (!ctx)
goto leave;
#ifdef HAVE_W32_SYSTEM
channel = g_io_channel_win32_new_socket (fd);
#else
channel = g_io_channel_unix_new (fd);
#endif
if (!channel)
{
g_debug ("error creating a channel for fd %d\n", fd);
goto leave;
}
g_io_channel_set_encoding (channel, NULL, NULL);
g_io_channel_set_buffered (channel, FALSE);
source_id = g_io_add_watch (channel, G_IO_IN, receive_cb, ctx);
if (!source_id)
{
g_debug ("error creating watch for fd %d", fd);
g_io_channel_shutdown (channel, 0, NULL);
goto leave;
}
err = assuan_accept (ctx);
if (err)
{
g_debug ("assuan accept failed: %s", gpg_strerror (err));
g_io_channel_shutdown (channel, 0, NULL);
goto leave;
}
g_debug ("connection at fd %d ready", fd);
fd = -1;
leave:
if (fd != -1)
assuan_sock_close ((assuan_fd_t) fd);
return TRUE; /* Keep the listen_fd in the event loop. */
}
/* Startup the server. */
void
gpa_start_server (void)
{
char *socket_name;
gpg_error_t err;
int rc;
assuan_fd_t fd;
struct sockaddr_un serv_addr;
socklen_t serv_addr_len = sizeof serv_addr;
GIOChannel *channel;
unsigned int source_id;
assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
err = assuan_sock_init ();
if (err)
{
g_debug ("assuan_sock_init failed: %s <%s>",
gpg_strerror (err), gpg_strsource (err));
return;
}
socket_name = g_build_filename (gnupg_homedir, "S.uiserver", NULL);
if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path )
{
g_debug ("name of socket too long\n");
g_free (socket_name);
return;
}
g_debug ("using server socket `%s'", socket_name);
fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
if (fd == ASSUAN_INVALID_FD)
{
g_debug ("can't create socket: %s\n", strerror(errno));
g_free (socket_name);
return;
}
memset (&serv_addr, 0, sizeof serv_addr);
serv_addr.sun_family = AF_UNIX;
strcpy (serv_addr.sun_path, socket_name);
serv_addr_len = (offsetof (struct sockaddr_un, sun_path)
+ strlen(serv_addr.sun_path) + 1);
rc = assuan_sock_bind (fd, (struct sockaddr*) &serv_addr, serv_addr_len);
if (rc == -1 && errno == EADDRINUSE)
{
remove (socket_name);
rc = assuan_sock_bind (fd, (struct sockaddr*) &serv_addr, serv_addr_len);
}
if (rc != -1 && (rc=assuan_sock_get_nonce ((struct sockaddr*) &serv_addr,
serv_addr_len, &socket_nonce)))
g_debug ("error getting nonce for the socket");
if (rc == -1)
{
g_debug ("error binding socket to `%s': %s\n",
serv_addr.sun_path, strerror (errno) );
assuan_sock_close (fd);
g_free (socket_name);
return;
}
g_free (socket_name);
socket_name = NULL;
if (listen ((int) fd, 5) == -1)
{
g_debug ("listen() failed: %s\n", strerror (errno));
assuan_sock_close (fd);
return;
}
#ifdef HAVE_W32_SYSTEM
channel = g_io_channel_win32_new_socket ((int) fd);
#else
channel = g_io_channel_unix_new (fd);
#endif
if (!channel)
{
g_debug ("error creating a new listening channel\n");
assuan_sock_close (fd);
return;
}
g_io_channel_set_encoding (channel, NULL, NULL);
g_io_channel_set_buffered (channel, FALSE);
source_id = g_io_add_watch (channel, G_IO_IN, accept_connection_cb, NULL);
if (!source_id)
{
g_debug ("error creating watch for listening channel\n");
g_io_channel_shutdown (channel, 0, NULL);
assuan_sock_close (fd);
return;
}
}
/* Set a flag to shutdown the server in a friendly way. */
void
gpa_stop_server (void)
{
shutdown_pending = TRUE;
if (!connection_counter)
gtk_main_quit ();
}
/* Helper for gpa_check-server. */
static gpg_error_t
check_name_cb (void *opaque, const void *buffer, size_t length)
{
int *result = opaque;
const char *name = PACKAGE_NAME;
if (length == strlen (name) && !strcmp (name, buffer))
*result = 1;
return 0;
}
/* Check whether an UI server is already running:
0 = no
1 = yes
2 = yes - same program
*/
int
gpa_check_server (void)
{
gpg_error_t err;
assuan_context_t ctx;
int name_check = 0;
int result;
err = assuan_new (&ctx);
if (!err)
err = assuan_socket_connect (ctx,
gpgme_get_dirinfo ("uiserver-socket"), 0, 0);
if (err)
{
g_message ("error connecting an UI server: %s - %s",
gpg_strerror (err), "assuming not running");
result = 0;
goto leave;
}
err = assuan_transact (ctx, "GETINFO name",
check_name_cb, &name_check, NULL, NULL, NULL, NULL);
if (err)
{
g_message ("requesting name of UI server failed: %s - %s",
gpg_strerror (err), "assuming not running");
result = 1;
goto leave;
}
if (name_check)
{
g_message ("an instance of this program is already running");
result = 2;
}
else
{
g_message ("an different UI server is already running");
result = 1;
}
leave:
assuan_release (ctx);
return result;
}
/* Send a command to the server. */
gpg_error_t
gpa_send_to_server (const char *cmd)
{
gpg_error_t err;
assuan_context_t ctx;
err = assuan_new (&ctx);
if (!err)
err = assuan_socket_connect (ctx,
gpgme_get_dirinfo ("uiserver-socket"), 0, 0);
if (err)
{
g_message ("error connecting the UI server: %s", gpg_strerror (err));
goto leave;
}
err = assuan_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL);
if (err)
{
g_message ("error sending '%s' to the UI server: %s",
cmd, gpg_strerror (err));
goto leave;
}
leave:
assuan_release (ctx);
return err;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:41 PM (14 h, 54 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a0/82/bd1c4ae548e065635f19a599cb9c
Attached To
rGPA Gnu Privacy Assistant
Event Timeline
Log In to Comment