diff --git a/src/gpastreamencryptop.c b/src/gpastreamencryptop.c
index 146707f..70a5e24 100644
--- a/src/gpastreamencryptop.c
+++ b/src/gpastreamencryptop.c
@@ -1,521 +1,525 @@
/* gpastreamdecryptop.c - The GpaOperation object.
* 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 .
*/
#include
#include
#include "gpgmetools.h"
#include "recipientdlg.h"
#include "gpawidgets.h"
#include "gpastreamencryptop.h"
#include "selectkeydlg.h"
-struct _GpaStreamEncryptOperation
+struct _GpaStreamEncryptOperation
{
GpaStreamOperation parent;
-
- SelectKeyDlg *key_dialog;
+
+ SelectKeyDlg *key_dialog;
RecipientDlg *recp_dialog;
GSList *recipients;
gpgme_key_t *keys;
gpgme_protocol_t selected_protocol;
};
-struct _GpaStreamEncryptOperationClass
+struct _GpaStreamEncryptOperationClass
{
GpaStreamOperationClass parent_class;
};
/* Indentifiers for our properties. */
-enum
+enum
{
PROP_0,
PROP_RECIPIENTS,
PROP_RECIPIENT_KEYS,
PROP_PROTOCOL
};
static void response_cb (GtkDialog *dialog,
gint response,
gpointer user_data);
static gboolean start_encryption_cb (gpointer data);
static void done_error_cb (GpaContext *context, gpg_error_t err,
GpaStreamEncryptOperation *op);
static void done_cb (GpaContext *context, gpg_error_t err,
GpaStreamEncryptOperation *op);
static GObjectClass *parent_class;
/* Helper to be used as a GFunc for free. */
static void
free_func (void *p, void *dummy)
{
(void)dummy;
g_free (p);
}
static void
release_recipients (GSList *recipients)
{
if (recipients)
{
g_slist_foreach (recipients, free_func, NULL);
g_slist_free (recipients);
}
}
/* Return a deep copy of the recipients list. */
static GSList *
copy_recipients (GSList *recipients)
{
GSList *recp, *newlist;
-
+
newlist= NULL;
for (recp = recipients; recp; recp = g_slist_next (recp))
newlist = g_slist_append (newlist, g_strdup (recp->data));
return newlist;
}
static void
gpa_stream_encrypt_operation_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GpaStreamEncryptOperation *op = GPA_STREAM_ENCRYPT_OPERATION (object);
-
+
switch (prop_id)
{
case PROP_RECIPIENTS:
g_value_set_pointer (value, op->recipients);
break;
case PROP_RECIPIENT_KEYS:
g_value_set_pointer (value, op->keys);
break;
case PROP_PROTOCOL:
g_value_set_int (value, op->selected_protocol);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gpa_stream_encrypt_operation_set_property (GObject *object, guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GpaStreamEncryptOperation *op = GPA_STREAM_ENCRYPT_OPERATION (object);
switch (prop_id)
{
case PROP_RECIPIENTS:
op->recipients = (GSList*)g_value_get_pointer (value);
break;
case PROP_RECIPIENT_KEYS:
op->keys = (gpgme_key_t*)g_value_get_pointer (value);
break;
case PROP_PROTOCOL:
op->selected_protocol = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gpa_stream_encrypt_operation_finalize (GObject *object)
-{
+{
GpaStreamEncryptOperation *op = GPA_STREAM_ENCRYPT_OPERATION (object);
release_recipients (op->recipients);
op->recipients = NULL;
gpa_gpgme_release_keyarray (op->keys);
op->keys = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gpa_stream_encrypt_operation_init (GpaStreamEncryptOperation *op)
{
op->key_dialog = NULL;
op->recp_dialog = NULL;
op->recipients = NULL;
op->keys = NULL;
op->selected_protocol = GPGME_PROTOCOL_UNKNOWN;
}
static GObject*
-gpa_stream_encrypt_operation_constructor
+gpa_stream_encrypt_operation_constructor
(GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GObject *object;
GpaStreamEncryptOperation *op;
object = parent_class->constructor (type,
n_construct_properties,
construct_properties);
op = GPA_STREAM_ENCRYPT_OPERATION (object);
/* Create the recipient key selection dialog if we don't know the
keys yet. */
if (!op->keys && (!op->recipients || !g_slist_length (op->recipients)))
{
/* No recipients - use a generic key selection dialog. */
op->key_dialog = select_key_dlg_new (GPA_OPERATION (op)->window);
g_signal_connect (G_OBJECT (op->key_dialog), "response",
G_CALLBACK (response_cb), op);
}
else if (!op->keys)
{
/* Caller gave us some recipients - use the mail address
matching key selectiion dialog. */
op->recp_dialog = recipient_dlg_new (GPA_OPERATION (op)->window);
recipient_dlg_set_recipients (op->recp_dialog,
op->recipients,
op->selected_protocol);
g_signal_connect (G_OBJECT (op->recp_dialog), "response",
G_CALLBACK (response_cb), op);
}
else
g_idle_add (start_encryption_cb, op);
/* We connect the done signal to two handles. The error handler is
called first. */
g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done",
G_CALLBACK (done_error_cb), op);
g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done",
G_CALLBACK (done_cb), op);
- gtk_window_set_title
+ gtk_window_set_title
(GTK_WINDOW (GPA_STREAM_OPERATION (op)->progress_dialog),
_("Encrypting message ..."));
if (op->key_dialog)
gtk_widget_show_all (GTK_WIDGET (op->key_dialog));
if (op->recp_dialog)
gtk_widget_show_all (GTK_WIDGET (op->recp_dialog));
return object;
}
static void
gpa_stream_encrypt_operation_class_init (GpaStreamEncryptOperationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
+
parent_class = g_type_class_peek_parent (klass);
object_class->constructor = gpa_stream_encrypt_operation_constructor;
object_class->finalize = gpa_stream_encrypt_operation_finalize;
object_class->set_property = gpa_stream_encrypt_operation_set_property;
object_class->get_property = gpa_stream_encrypt_operation_get_property;
- g_object_class_install_property
+ g_object_class_install_property
(object_class, PROP_RECIPIENTS,
- g_param_spec_pointer
+ g_param_spec_pointer
("recipients", "Recipients",
"A list of recipients in rfc-822 mailbox format.",
G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property
+ g_object_class_install_property
(object_class, PROP_RECIPIENT_KEYS,
- g_param_spec_pointer
+ g_param_spec_pointer
("recipient-keys", "Recipient-keys",
"An array of gpgme_key_t with the selected keys.",
G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property
+ g_object_class_install_property
(object_class, PROP_PROTOCOL,
- g_param_spec_int
+ g_param_spec_int
("protocol", "Protocol",
"The gpgme protocol currently selected.",
GPGME_PROTOCOL_OpenPGP, GPGME_PROTOCOL_UNKNOWN, GPGME_PROTOCOL_UNKNOWN,
G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
}
GType
gpa_stream_encrypt_operation_get_type (void)
{
static GType stream_encrypt_operation_type = 0;
-
+
if (!stream_encrypt_operation_type)
{
static const GTypeInfo stream_encrypt_operation_info =
{
sizeof (GpaStreamEncryptOperationClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gpa_stream_encrypt_operation_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GpaStreamEncryptOperation),
0, /* n_preallocs */
(GInstanceInitFunc) gpa_stream_encrypt_operation_init,
};
-
- stream_encrypt_operation_type = g_type_register_static
+
+ stream_encrypt_operation_type = g_type_register_static
(GPA_STREAM_OPERATION_TYPE, "GpaStreamEncryptOperation",
&stream_encrypt_operation_info, 0);
}
-
+
return stream_encrypt_operation_type;
}
/* Return true if all keys are matching the protocol. */
static int
keys_match_protocol_p (gpgme_key_t *keys, gpgme_protocol_t protocol)
{
int idx;
if (!keys)
return 1; /* No keys: assume match. */
for (idx = 0; keys[idx]; idx++)
if (keys[idx]->protocol != protocol)
return 0;
return 1;
}
/*
* Fire up the encryption.
*/
static void
start_encryption (GpaStreamEncryptOperation *op)
{
gpg_error_t err;
int prep_only = 0;
if (!op->keys || !op->keys[0])
{
err = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
if (op->selected_protocol == GPGME_PROTOCOL_OpenPGP)
err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
"OpenPGP", NULL);
else if (op->selected_protocol == GPGME_PROTOCOL_CMS)
err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
"CMS", NULL);
else
err = gpg_error (GPG_ERR_NO_PUBKEY);
if (err)
goto leave;
/* Set the output encoding. */
- if (GPA_STREAM_OPERATION (op)->input_stream
+ if (GPA_STREAM_OPERATION (op)->input_stream
&& GPA_STREAM_OPERATION (op)->output_stream)
{
- if (op->selected_protocol == GPGME_PROTOCOL_CMS)
+ if (gpgme_data_get_encoding (GPA_STREAM_OPERATION(op)->output_stream))
+ gpgme_data_set_encoding
+ (GPA_STREAM_OPERATION (op)->output_stream,
+ gpgme_data_get_encoding (GPA_STREAM_OPERATION(op)->output_stream));
+ else if (op->selected_protocol == GPGME_PROTOCOL_CMS)
gpgme_data_set_encoding (GPA_STREAM_OPERATION (op)->output_stream,
GPGME_DATA_ENCODING_BASE64);
else
gpgme_set_armor (GPA_OPERATION (op)->context->ctx, 1);
if (!keys_match_protocol_p (op->keys, op->selected_protocol))
{
g_debug ("the selected keys do not match the protocol");
err = gpg_error (GPG_ERR_CONFLICT);
goto leave;
}
-
- gpgme_set_protocol (GPA_OPERATION (op)->context->ctx,
+
+ gpgme_set_protocol (GPA_OPERATION (op)->context->ctx,
op->selected_protocol);
/* We always trust the keys because the recipient selection
dialog has already sorted unusable out. */
err = gpgme_op_encrypt_start (GPA_OPERATION (op)->context->ctx,
op->keys, GPGME_ENCRYPT_ALWAYS_TRUST,
GPA_STREAM_OPERATION (op)->input_stream,
GPA_STREAM_OPERATION (op)->output_stream);
if (err)
{
gpa_gpgme_warning (err);
goto leave;
}
/* Show and update the progress dialog. */
gtk_widget_show_all (GPA_STREAM_OPERATION (op)->progress_dialog);
- gpa_progress_dialog_set_label
+ gpa_progress_dialog_set_label
(GPA_PROGRESS_DIALOG (GPA_STREAM_OPERATION (op)->progress_dialog),
_("Message encryption"));
}
else
{
/* We are just preparing an encryption. */
prep_only = 1;
err = 0;
}
leave:
if (err || prep_only)
g_signal_emit_by_name (GPA_OPERATION (op), "completed", err);
}
/* The recipient key selection dialog has returned. */
-static void
+static void
response_cb (GtkDialog *dialog, int response, void *user_data)
{
GpaStreamEncryptOperation *op = user_data;
gtk_widget_hide (GTK_WIDGET (dialog));
-
+
if (response != GTK_RESPONSE_OK)
{
/* The dialog was canceled, so we do nothing and complete the
operation. */
g_signal_emit_by_name (GPA_OPERATION (op), "completed",
gpg_error (GPG_ERR_CANCELED));
/* FIXME: We might need to destroy the widget in the KEY_DIALOG case. */
return;
}
/* Get the keys. */
gpa_gpgme_release_keyarray (op->keys);
op->keys = NULL;
if (op->key_dialog)
op->keys = select_key_dlg_get_keys (op->key_dialog);
else if (op->recp_dialog)
op->keys = recipient_dlg_get_keys (op->recp_dialog, &op->selected_protocol);
start_encryption (op);
}
/* This is the idle function used to start the encryption if no
recipient key selection dialog has been requested. */
static gboolean
start_encryption_cb (void *user_data)
{
GpaStreamEncryptOperation *op = user_data;
start_encryption (op);
return FALSE; /* Remove this callback from the event loop. */
}
/*Show an error message. */
static void
done_error_cb (GpaContext *context, gpg_error_t err,
GpaStreamEncryptOperation *op)
{
switch (gpg_err_code (err))
{
case GPG_ERR_NO_ERROR:
case GPG_ERR_CANCELED:
/* Ignore these */
break;
/* case GPG_ERR_BAD_PASSPHRASE: */
/* gpa_window_error (_("Wrong passphrase!"), GPA_OPERATION (op)->window); */
/* break; */
default:
gpa_gpgme_warning (err);
break;
}
}
/* Operation is ready. Tell the server. */
static void
done_cb (GpaContext *context, gpg_error_t err, GpaStreamEncryptOperation *op)
{
gtk_widget_hide (GPA_STREAM_OPERATION (op)->progress_dialog);
g_signal_emit_by_name (GPA_OPERATION (op), "completed", err);
}
/* Public API. */
/* Start encrypting INPUT_STREAM to OUTPUT_STREAM using WINDOW.
RECIPIENTS gives a list of recipients and the function matches them
with existing keys and selects appropriate keys. RECP_KEYS is
either NULL or an array with gpgme keys which will then immediatley
be used and suppresses the recipient key selection dialog.
If it is not possible to unambigiously select keys and SILENT is
not given, a key selection dialog offers the user a way to manually
select keys. INPUT_STREAM and OUTPUT_STREAM may be given as NULL
in which case the function skips the actual encryption step and
just verifies the recipients. */
GpaStreamEncryptOperation*
gpa_stream_encrypt_operation_new (GtkWidget *window,
gpgme_data_t input_stream,
gpgme_data_t output_stream,
GSList *recipients,
gpgme_key_t *recp_keys,
gpgme_protocol_t protocol,
int silent)
{
GpaStreamEncryptOperation *op;
/* Fixme: SILENT is not yet implemented. */
g_debug ("recipients %p recp_keys %p", recipients, recp_keys);
op = g_object_new (GPA_STREAM_ENCRYPT_OPERATION_TYPE,
"window", window,
"input_stream", input_stream,
"output_stream", output_stream,
"recipients", copy_recipients (recipients),
"recipient-keys", gpa_gpgme_copy_keyarray (recp_keys),
"protocol", (int)protocol,
NULL);
return op;
}
/* Return an array of keys for the set of recipients of this object.
The function also returns the selected protocol. */
gpgme_key_t *
gpa_stream_encrypt_operation_get_keys (GpaStreamEncryptOperation *op,
gpgme_protocol_t *r_protocol)
{
g_return_val_if_fail (op, NULL);
-
+
if (r_protocol)
*r_protocol = op->selected_protocol;
return gpa_gpgme_copy_keyarray (op->keys);
}
diff --git a/src/gpastreamsignop.c b/src/gpastreamsignop.c
index 0b955eb..a19b0a3 100644
--- a/src/gpastreamsignop.c
+++ b/src/gpastreamsignop.c
@@ -1,507 +1,511 @@
/* gpastreamsignop.c - The GpaStreamSignOperation object.
* Copyright (C) 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 .
*/
#include
#include
#include "gpgmetools.h"
#include "gtktools.h"
#include "filesigndlg.h"
#include "gpastreamsignop.h"
-struct _GpaStreamSignOperation
+struct _GpaStreamSignOperation
{
GpaStreamOperation parent;
GtkWidget *sign_dialog;
-
+
const char *sender;
gpgme_protocol_t requested_protocol;
gboolean detached;
};
-struct _GpaStreamSignOperationClass
+struct _GpaStreamSignOperationClass
{
GpaStreamOperationClass parent_class;
};
/* Indentifiers for our properties. */
-enum
+enum
{
PROP_0,
PROP_SENDER,
PROP_PROTOCOL,
PROP_DETACHED
};
static void response_cb (GtkDialog *dialog,
gint response,
gpointer user_data);
static void done_error_cb (GpaContext *context, gpg_error_t err,
GpaStreamSignOperation *op);
static void done_cb (GpaContext *context, gpg_error_t err,
GpaStreamSignOperation *op);
static GObjectClass *parent_class;
static void
gpa_stream_sign_operation_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GpaStreamSignOperation *op = GPA_STREAM_SIGN_OPERATION (object);
-
+
switch (prop_id)
{
case PROP_SENDER:
g_value_set_string (value, op->sender);
break;
case PROP_PROTOCOL:
g_value_set_int (value, op->requested_protocol);
break;
case PROP_DETACHED:
g_value_set_boolean (value, op->detached);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gpa_stream_sign_operation_set_property (GObject *object, guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GpaStreamSignOperation *op = GPA_STREAM_SIGN_OPERATION (object);
switch (prop_id)
{
case PROP_SENDER:
op->sender = g_value_get_string (value);
break;
case PROP_PROTOCOL:
op->requested_protocol = g_value_get_int (value);
break;
case PROP_DETACHED:
op->detached = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gpa_stream_sign_operation_finalize (GObject *object)
-{
+{
/* GpaStreamSignOperation *op = GPA_STREAM_SIGN_OPERATION (object); */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gpa_stream_sign_operation_init (GpaStreamSignOperation *op)
{
op->requested_protocol = GPGME_PROTOCOL_UNKNOWN;
}
static GObject*
gpa_stream_sign_operation_ctor (GType type, guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GObject *object;
GpaStreamSignOperation *op;
object = parent_class->constructor (type,
n_construct_properties,
construct_properties);
op = GPA_STREAM_SIGN_OPERATION (object);
op->sign_dialog = gpa_file_sign_dialog_new (GPA_OPERATION (op)->window);
{
GpaFileSignDialog *dialog = GPA_FILE_SIGN_DIALOG (op->sign_dialog);
/* Note: The information here is wrong. The actual sig_mode and
armor settings are determined from the selected key (which
determines the protocol). We set the values here to those for
OpenPGP, and force (==hide) the selection widgets. */
gpa_file_sign_dialog_set_armor (dialog, TRUE);
gpa_file_sign_dialog_set_force_armor (dialog, TRUE);
gpa_file_sign_dialog_set_sig_mode (dialog, GPGME_SIG_MODE_NORMAL);
gpa_file_sign_dialog_set_force_sig_mode (dialog, TRUE);
}
g_signal_connect (G_OBJECT (op->sign_dialog), "response",
G_CALLBACK (response_cb), op);
/* We connect the done signal to two handles. The error handler is
called first. */
g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done",
G_CALLBACK (done_error_cb), op);
g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done",
G_CALLBACK (done_cb), op);
- gtk_window_set_title
+ gtk_window_set_title
(GTK_WINDOW (GPA_STREAM_OPERATION (op)->progress_dialog),
_("Signing message ..."));
if (op->sign_dialog)
gtk_widget_show_all (GTK_WIDGET (op->sign_dialog));
return object;
}
static void
gpa_stream_sign_operation_class_init (GpaStreamSignOperationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
+
parent_class = g_type_class_peek_parent (klass);
object_class->constructor = gpa_stream_sign_operation_ctor;
object_class->finalize = gpa_stream_sign_operation_finalize;
object_class->set_property = gpa_stream_sign_operation_set_property;
object_class->get_property = gpa_stream_sign_operation_get_property;
- g_object_class_install_property
+ g_object_class_install_property
(object_class, PROP_SENDER,
- g_param_spec_pointer
+ g_param_spec_pointer
("sender", "Sender",
"The sender of the message in rfc-822 mailbox format or NULL.",
G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property
+ g_object_class_install_property
(object_class, PROP_PROTOCOL,
- g_param_spec_int
+ g_param_spec_int
("protocol", "Protocol",
"The requested gpgme protocol.",
GPGME_PROTOCOL_OpenPGP, GPGME_PROTOCOL_UNKNOWN, GPGME_PROTOCOL_UNKNOWN,
G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property
+ g_object_class_install_property
(object_class, PROP_DETACHED,
- g_param_spec_boolean
+ g_param_spec_boolean
("detached", "Detached",
"Flag requesting a detached signature.",
FALSE,
G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
}
GType
gpa_stream_sign_operation_get_type (void)
{
static GType stream_sign_operation_type = 0;
-
+
if (!stream_sign_operation_type)
{
static const GTypeInfo stream_sign_operation_info =
{
sizeof (GpaStreamSignOperationClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gpa_stream_sign_operation_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GpaStreamSignOperation),
0, /* n_preallocs */
(GInstanceInitFunc) gpa_stream_sign_operation_init,
};
-
- stream_sign_operation_type = g_type_register_static
+
+ stream_sign_operation_type = g_type_register_static
(GPA_STREAM_OPERATION_TYPE, "GpaStreamSignOperation",
&stream_sign_operation_info, 0);
}
-
+
return stream_sign_operation_type;
}
/* Setting the signers and the protocol for the context. The protocol
* to use is derived from the keys. An error will be displayed if the
* selected keys are not all of one protocol. Returns true on
* success. */
static gboolean
set_signers (GpaStreamSignOperation *op, GList *signers)
{
GList *cur;
gpg_error_t err;
gpgme_protocol_t protocol = GPGME_PROTOCOL_UNKNOWN;
gpgme_signers_clear (GPA_OPERATION (op)->context->ctx);
if (!signers)
{
/* Can't happen */
gpa_window_error (_("You didn't select any key for signing"),
GPA_OPERATION (op)->window);
return FALSE;
}
for (cur = signers; cur; cur = g_list_next (cur))
{
gpgme_key_t key = cur->data;
if (protocol == GPGME_PROTOCOL_UNKNOWN)
protocol = key->protocol;
else if (key->protocol != protocol)
{
/* Should not happen because the selection dialog should
have not allowed to select different key types. */
- gpa_window_error
+ gpa_window_error
(_("The selected certificates are not all of the same type."
" That is, you mixed OpenPGP and X.509 certificates."
" Please make sure to select only certificates of the"
- " same type."),
+ " same type."),
GPA_OPERATION (op)->window);
return FALSE;
}
}
gpgme_set_protocol (GPA_OPERATION (op)->context->ctx, protocol);
for (cur = signers; cur; cur = g_list_next (cur))
{
gpgme_key_t key = cur->data;
err = gpgme_signers_add (GPA_OPERATION (op)->context->ctx, key);
if (err)
gpa_gpgme_error (err);
}
return TRUE;
}
/*
* Fire up the signing
*/
static void
start_signing (GpaStreamSignOperation *op)
{
gpg_error_t err;
int prep_only = 0;
GList *signers;
gpgme_protocol_t protocol;
- signers = gpa_file_sign_dialog_signers
+ signers = gpa_file_sign_dialog_signers
(GPA_FILE_SIGN_DIALOG (op->sign_dialog));
if (!set_signers (op, signers))
{
err = gpg_error (GPG_ERR_NO_SECKEY);
goto leave;
}
protocol = gpgme_get_protocol (GPA_OPERATION (op)->context->ctx);
if (protocol == GPGME_PROTOCOL_OpenPGP)
err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
"OpenPGP", NULL);
else if (protocol == GPGME_PROTOCOL_CMS)
err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
"CMS", NULL);
else
err = gpg_error (GPG_ERR_NO_SECKEY);
if (err)
goto leave;
/* Set the output encoding. */
- if (GPA_STREAM_OPERATION (op)->input_stream
+ if (GPA_STREAM_OPERATION (op)->input_stream
&& GPA_STREAM_OPERATION (op)->output_stream)
{
- if (protocol == GPGME_PROTOCOL_CMS)
+ if (gpgme_data_get_encoding (GPA_STREAM_OPERATION(op)->output_stream))
+ gpgme_data_set_encoding
+ (GPA_STREAM_OPERATION (op)->output_stream,
+ gpgme_data_get_encoding (GPA_STREAM_OPERATION(op)->output_stream));
+ else if (protocol == GPGME_PROTOCOL_CMS)
gpgme_data_set_encoding (GPA_STREAM_OPERATION (op)->output_stream,
GPGME_DATA_ENCODING_BASE64);
else
gpgme_set_armor (GPA_OPERATION (op)->context->ctx, 1);
- err = gpgme_op_sign_start (GPA_OPERATION (op)->context->ctx,
+ err = gpgme_op_sign_start (GPA_OPERATION (op)->context->ctx,
GPA_STREAM_OPERATION (op)->input_stream,
GPA_STREAM_OPERATION (op)->output_stream,
(op->detached? GPGME_SIG_MODE_DETACH
- /* */ : GPGME_SIG_MODE_NORMAL));
+ /* */ : GPGME_SIG_MODE_NORMAL));
if (err)
{
gpa_gpgme_warning (err);
goto leave;
}
-
+
/* Show and update the progress dialog. */
gtk_widget_show_all (GPA_STREAM_OPERATION (op)->progress_dialog);
- gpa_progress_dialog_set_label
+ gpa_progress_dialog_set_label
(GPA_PROGRESS_DIALOG (GPA_STREAM_OPERATION (op)->progress_dialog),
_("Message signing"));
}
else
{
/* We are just preparing an encryption. */
prep_only = 1;
err = 0;
}
leave:
if (err || prep_only)
g_signal_emit_by_name (GPA_OPERATION (op), "completed", err);
}
/* The recipient key selection dialog has returned. */
-static void
+static void
response_cb (GtkDialog *dialog, int response, void *user_data)
{
GpaStreamSignOperation *op = user_data;
gtk_widget_hide (GTK_WIDGET (dialog));
-
+
if (response != GTK_RESPONSE_OK)
{
/* The dialog was canceled, so we do nothing and complete the
operation. */
g_signal_emit_by_name (GPA_OPERATION (op), "completed",
gpg_error (GPG_ERR_CANCELED));
return;
}
start_signing (op);
}
/* This is the idle function used to start the encryption if no
recipient key selection dialog has been requested. */
#if 0 /* Not yet used. */
static gboolean
start_signing_cb (void *user_data)
{
GpaStreamSignOperation *op = user_data;
start_signing (op);
return FALSE; /* Remove this callback from the event loop. */
}
#endif
/* Show an error message. */
static void
done_error_cb (GpaContext *context, gpg_error_t err,
GpaStreamSignOperation *op)
{
switch (gpg_err_code (err))
{
case GPG_ERR_NO_ERROR:
case GPG_ERR_CANCELED:
/* Ignore these */
break;
case GPG_ERR_BAD_PASSPHRASE:
gpa_window_error (_("Wrong passphrase!"), GPA_OPERATION (op)->window);
break;
default:
gpa_gpgme_warning (err);
break;
}
}
/* Operation is ready. Tell the server. */
static void
done_cb (GpaContext *context, gpg_error_t err, GpaStreamSignOperation *op)
{
gtk_widget_hide (GPA_STREAM_OPERATION (op)->progress_dialog);
if (! err)
{
gpgme_protocol_t protocol;
gpgme_sign_result_t res;
gpgme_new_signature_t sig;
protocol = gpgme_get_protocol (GPA_OPERATION (op)->context->ctx);
res = gpgme_op_sign_result (GPA_OPERATION (op)->context->ctx);
if (res)
{
sig = res->signatures;
while (sig)
{
char *str;
char *algo_name;
-
+
str = g_strdup_printf
("%s%s", (protocol == GPGME_PROTOCOL_OpenPGP) ? "pgp-" : "",
gpgme_hash_algo_name (sig->hash_algo));
algo_name = g_ascii_strdown (str, -1);
g_free (str);
/* FIXME: Error handling. */
err = gpa_operation_write_status (GPA_OPERATION (op), "MICALG",
algo_name, NULL);
g_free (algo_name);
sig = sig->next;
}
}
}
g_signal_emit_by_name (GPA_OPERATION (op), "completed", err);
}
/* Public API. */
/* Start signing INPUT_STREAM to OUTPUT_STREAM using WINDOW. SENDER
gives the name of the sender's role (usually a mailbox) or is NULL
for the default sender.
If it is not possible to unambigiously select a signing key a key
selection dialog offers the user a way to manually select signing
keys. INPUT_STREAM and OUTPUT_STREAM may be given as NULL in which
case the function skips the actual signing step and just verifies
the signing key. */
GpaStreamSignOperation*
gpa_stream_sign_operation_new (GtkWidget *window,
gpgme_data_t input_stream,
gpgme_data_t output_stream,
const gchar *sender,
gpgme_protocol_t protocol,
gboolean detached)
{
GpaStreamSignOperation *op;
op = g_object_new (GPA_STREAM_SIGN_OPERATION_TYPE,
"window", window,
"input_stream", input_stream,
"output_stream", output_stream,
"sender", sender,
"protocol", (int)protocol,
"detached", detached,
NULL);
return op;
}
diff --git a/src/server.c b/src/server.c
index ee4ec8f..d220ccb 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1,2050 +1,2073 @@
/* 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 . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#ifndef HAVE_W32_SYSTEM
# include
# include
#endif /*HAVE_W32_SYSTEM*/
#include
#include
#include
#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 []\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 \n"
"\n"
"Set the recipient for the encryption. 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"
"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 \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 \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_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);
}
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 \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"
" 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
err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
return assuan_process_done (ctx, err);
}
/* Convert two hexadecimal digits from STR to the value they
represent. Returns -1 if one of the characters is not a
hexadecimal digit. */
static int
hextobyte (const char *str)
{
int val = 0;
int i;
#define NROFHEXDIGITS 2
for (i = 0; i < NROFHEXDIGITS; i++)
{
if (*str >= '0' && *str <= '9')
val += *str - '0';
else if (*str >= 'A' && *str <= 'F')
val += 10 + *str - 'A';
else if (*str >= 'a' && *str <= 'f')
val += 10 + *str - 'a';
else
return -1;
if (i < NROFHEXDIGITS - 1)
val *= 16;
str++;
}
return val;
}
/* Decode the percent escaped string STR in place. */
static void
decode_percent_string (char *str)
{
char *src = str;
char *dest = str;
/* Convert the string. */
while (*src)
{
if (*src != '%')
{
*(dest++) = *(src++);
continue;
}
else
{
int val = hextobyte (&src[1]);
if (val == -1)
{
/* Should not happen. */
*(dest++) = *(src++);
if (*src)
*(dest++) = *(src++);
if (*src)
*(dest++) = *(src++);
}
else
{
if (!val)
{
/* A binary zero is not representable in a C
string. */
*(dest++) = '\\';
*(dest++) = '0';
}
else
*((unsigned char *) dest++) = val;
src += 3;
}
}
}
*(dest++) = 0;
}
/* 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);
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);
}
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_CONFDIALOG", cmd_start_confdialog, hlp_start_confdialog },
{ "START_CARDMANAGER", cmd_start_cardmanager, hlp_start_cardmanager },
{ "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 },
{ 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 ();
}