summaryrefslogtreecommitdiff
path: root/gcr/gcr-gnupg-process.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcr/gcr-gnupg-process.c')
-rw-r--r--gcr/gcr-gnupg-process.c1157
1 files changed, 0 insertions, 1157 deletions
diff --git a/gcr/gcr-gnupg-process.c b/gcr/gcr-gnupg-process.c
deleted file mode 100644
index 81423da4..00000000
--- a/gcr/gcr-gnupg-process.c
+++ /dev/null
@@ -1,1157 +0,0 @@
-/*
- * gnome-keyring
- *
- * Copyright (C) 2011 Collabora Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- *
- * Author: Stef Walter <stefw@collabora.co.uk>
- */
-
-#include "config.h"
-
-#define DEBUG_FLAG GCR_DEBUG_GNUPG
-#include "gcr-debug.h"
-#include "gcr-gnupg-process.h"
-#include "gcr-marshal.h"
-#include "gcr-util.h"
-
-#include <glib/gi18n-lib.h>
-
-#include <sys/wait.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-
-/**
- * GcrGnupgProcessFlags:
- * @GCR_GNUPG_PROCESS_NONE: No flags
- * @GCR_GNUPG_PROCESS_RESPECT_LOCALE: Respect the user's locale when running gnupg.
- * @GCR_GNUPG_PROCESS_WITH_STATUS: Ask the process to send status records.
- * @GCR_GNUPG_PROCESS_WITH_ATTRIBUTES: Ask the process to output attribute data.
- *
- * Flags for running a gnupg process.
- */
-
-enum {
- PROP_0,
- PROP_DIRECTORY,
- PROP_EXECUTABLE,
- PROP_INPUT_STREAM,
- PROP_OUTPUT_STREAM,
- PROP_ATTRIBUTE_STREAM
-};
-
-enum {
- FD_INPUT,
- FD_OUTPUT,
- FD_ERROR,
- FD_STATUS,
- FD_ATTRIBUTE,
- NUM_FDS
-};
-
-enum {
- ERROR_LINE,
- STATUS_RECORD,
- NUM_SIGNALS
-};
-
-static gint signals[NUM_SIGNALS] = { 0, };
-
-typedef struct _GnupgSource {
- GSource source;
- GPollFD polls[NUM_FDS]; /* The various fd's we're listening to */
-
- GcrGnupgProcess *process; /* Pointer back to the process object */
-
- GByteArray *input_buf;
- GString *error_buf;
- GString *status_buf;
- guint source_sig;
-
- GPid child_pid;
- guint child_sig;
-
- GCancellable *cancellable;
- guint cancel_sig;
-} GnupgSource;
-
-struct _GcrGnupgProcessPrivate {
- gchar *directory;
- gchar *executable;
-
- GInputStream *input;
- GOutputStream *output;
- GOutputStream *attributes;
-
- gboolean running;
- gboolean complete;
- GError *error;
-
- GAsyncReadyCallback async_callback;
- gpointer user_data;
-};
-
-/* Forward declarations */
-static void _gcr_gnupg_process_init_async (GAsyncResultIface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (GcrGnupgProcess, _gcr_gnupg_process, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, _gcr_gnupg_process_init_async));
-
-static void
-_gcr_gnupg_process_init (GcrGnupgProcess *self)
-{
- self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_GNUPG_PROCESS,
- GcrGnupgProcessPrivate);
-}
-
-static void
-_gcr_gnupg_process_constructed (GObject *obj)
-{
- GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
-
- if (G_OBJECT_CLASS (_gcr_gnupg_process_parent_class)->constructed)
- G_OBJECT_CLASS (_gcr_gnupg_process_parent_class)->constructed (obj);
-
- if (!self->pv->executable)
- self->pv->executable = g_strdup (GPG_EXECUTABLE);
-}
-
-static void
-_gcr_gnupg_process_get_property (GObject *obj, guint prop_id, GValue *value,
- GParamSpec *pspec)
-{
- GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
-
- switch (prop_id) {
- case PROP_DIRECTORY:
- g_value_set_string (value, self->pv->directory);
- break;
- case PROP_EXECUTABLE:
- g_value_set_string (value, self->pv->executable);
- break;
- case PROP_INPUT_STREAM:
- g_value_set_object (value, _gcr_gnupg_process_get_input_stream (self));
- break;
- case PROP_OUTPUT_STREAM:
- g_value_set_object (value, _gcr_gnupg_process_get_output_stream (self));
- break;
- case PROP_ATTRIBUTE_STREAM:
- g_value_set_object (value, _gcr_gnupg_process_get_attribute_stream (self));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
- break;
- }
-}
-
-static void
-_gcr_gnupg_process_set_property (GObject *obj, guint prop_id, const GValue *value,
- GParamSpec *pspec)
-{
- GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
-
- switch (prop_id) {
- case PROP_DIRECTORY:
- g_return_if_fail (!self->pv->directory);
- self->pv->directory = g_value_dup_string (value);
- break;
- case PROP_EXECUTABLE:
- g_return_if_fail (!self->pv->executable);
- self->pv->executable = g_value_dup_string (value);
- break;
- case PROP_INPUT_STREAM:
- _gcr_gnupg_process_set_input_stream (self, g_value_get_object (value));
- break;
- case PROP_OUTPUT_STREAM:
- _gcr_gnupg_process_set_output_stream (self, g_value_get_object (value));
- break;
- case PROP_ATTRIBUTE_STREAM:
- _gcr_gnupg_process_set_attribute_stream (self, g_value_get_object (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
- break;
- }
-}
-
-static void
-_gcr_gnupg_process_dispose (GObject *obj)
-{
- GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
-
- g_clear_object (&self->pv->input);
- g_clear_object (&self->pv->output);
- g_clear_object (&self->pv->attributes);
-
- G_OBJECT_CLASS (_gcr_gnupg_process_parent_class)->dispose (obj);
-}
-
-static void
-_gcr_gnupg_process_finalize (GObject *obj)
-{
- GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
-
- g_assert (!self->pv->running);
- g_free (self->pv->directory);
- g_free (self->pv->executable);
- g_clear_error (&self->pv->error);
-
- G_OBJECT_CLASS (_gcr_gnupg_process_parent_class)->finalize (obj);
-}
-
-static void
-_gcr_gnupg_process_class_init (GcrGnupgProcessClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
- gobject_class->constructed = _gcr_gnupg_process_constructed;
- gobject_class->get_property = _gcr_gnupg_process_get_property;
- gobject_class->set_property = _gcr_gnupg_process_set_property;
- gobject_class->dispose = _gcr_gnupg_process_dispose;
- gobject_class->finalize = _gcr_gnupg_process_finalize;
-
- /**
- * GcrGnupgProcess:directory:
- *
- * Directory to run as gnupg home directory, or %NULL for default
- * ~/.gnupg/ directory.
- */
- g_object_class_install_property (gobject_class, PROP_DIRECTORY,
- g_param_spec_string ("directory", "Directory", "Gnupg Directory",
- NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- /**
- * GcrGnupgProcess:executable:
- *
- * Path to the gnupg executable, or %NULL for default.
- */
- g_object_class_install_property (gobject_class, PROP_EXECUTABLE,
- g_param_spec_string ("executable", "Executable", "Gnupg Executable",
- GPG_EXECUTABLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- /**
- * GcrGnupgProcess:input-stream:
- *
- * Input for gnupg, or %NULL for no input.
- */
- g_object_class_install_property (gobject_class, PROP_INPUT_STREAM,
- g_param_spec_object ("input-stream", "Input Stream", "Input Stream",
- G_TYPE_INPUT_STREAM, G_PARAM_READWRITE));
-
- /**
- * GcrGnupgProcess:output-stream:
- *
- * Output from gnupg, or %NULL for ignored output.
- */
- g_object_class_install_property (gobject_class, PROP_OUTPUT_STREAM,
- g_param_spec_object ("output-stream", "Output Stream", "Output Stream",
- G_TYPE_OUTPUT_STREAM, G_PARAM_READWRITE));
-
- /**
- * GcrGnupgProcess:attribute-stream:
- *
- * Output of attribute data from gnupg, or %NULL for ignored attributes.
- */
- g_object_class_install_property (gobject_class, PROP_ATTRIBUTE_STREAM,
- g_param_spec_object ("attribute-stream", "Attribute Stream", "Attribute Stream",
- G_TYPE_OUTPUT_STREAM, G_PARAM_READWRITE));
-
- /**
- * GcrGnupgProcess::error-line:
- * @line: a line of error output.
- *
- * Signal emitted when a line of error output is available from the
- * gnupg process.
- */
- signals[ERROR_LINE] = g_signal_new ("error-line", GCR_TYPE_GNUPG_PROCESS,
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcrGnupgProcessClass, error_line),
- NULL, NULL, _gcr_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
-
- /**
- * GcrGnupgProcess::status-record:
- * @record: a status record.
- *
- * Signal emitted when a status record is available from the gnupg process.
- */
- signals[STATUS_RECORD] = g_signal_new ("status-record", GCR_TYPE_GNUPG_PROCESS,
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcrGnupgProcessClass, status_record),
- NULL, NULL, _gcr_marshal_VOID__BOXED,
- G_TYPE_NONE, 1, GCR_TYPE_RECORD);
-
- g_type_class_add_private (gobject_class, sizeof (GcrGnupgProcessPrivate));
-}
-
-static gpointer
-_gcr_gnupg_process_get_user_data (GAsyncResult *result)
-{
- g_return_val_if_fail (GCR_IS_GNUPG_PROCESS (result), NULL);
- return GCR_GNUPG_PROCESS (result)->pv->user_data;
-}
-
-static GObject*
-_gcr_gnupg_process_get_source_object (GAsyncResult *result)
-{
- g_return_val_if_fail (GCR_IS_GNUPG_PROCESS (result), NULL);
- return G_OBJECT (result);
-}
-
-static void
-_gcr_gnupg_process_init_async (GAsyncResultIface *iface)
-{
- iface->get_source_object = _gcr_gnupg_process_get_source_object;
- iface->get_user_data = _gcr_gnupg_process_get_user_data;
-}
-
-/**
- * _gcr_gnupg_process_new:
- * @directory: (allow-none): The gnupg home directory
- * @executable: (allow-none): The gpg executable
- *
- * Create a new GcrGnupgProcess.
- *
- * The gnupg home directory is where the keyring files live. If directory is
- * %NULL then the default gnupg home directory is used.
- *
- * The executable will default to the compiled in path if a %NULL executable
- * argument is used.
- *
- * Returns: (transfer full): A newly allocated process.
- */
-GcrGnupgProcess*
-_gcr_gnupg_process_new (const gchar *directory, const gchar *executable)
-{
- return g_object_new (GCR_TYPE_GNUPG_PROCESS,
- "directory", directory,
- "executable", executable,
- NULL);
-}
-
-const gchar *
-_gcr_gnupg_process_get_directory (GcrGnupgProcess *self)
-{
- g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL);
- return self->pv->directory;
-}
-
-GInputStream *
-_gcr_gnupg_process_get_input_stream (GcrGnupgProcess *self)
-{
- g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL);
- return self->pv->input;
-}
-
-void
-_gcr_gnupg_process_set_input_stream (GcrGnupgProcess *self,
- GInputStream *input)
-{
- g_return_if_fail (GCR_GNUPG_PROCESS (self));
- g_return_if_fail (input == NULL || G_INPUT_STREAM (input));
-
- if (input)
- g_object_ref (input);
- if (self->pv->input)
- g_object_unref (self->pv->input);
- self->pv->input = input;
- g_object_notify (G_OBJECT (self), "input-stream");
-}
-
-GOutputStream *
-_gcr_gnupg_process_get_output_stream (GcrGnupgProcess *self)
-{
- g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL);
- return self->pv->output;
-}
-
-void
-_gcr_gnupg_process_set_output_stream (GcrGnupgProcess *self,
- GOutputStream *output)
-{
- g_return_if_fail (GCR_GNUPG_PROCESS (self));
- g_return_if_fail (output == NULL || G_OUTPUT_STREAM (output));
-
- if (output)
- g_object_ref (output);
- if (self->pv->output)
- g_object_unref (self->pv->output);
- self->pv->output = output;
- g_object_notify (G_OBJECT (self), "output-stream");
-}
-
-GOutputStream *
-_gcr_gnupg_process_get_attribute_stream (GcrGnupgProcess *self)
-{
- g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL);
- return self->pv->attributes;
-}
-
-void
-_gcr_gnupg_process_set_attribute_stream (GcrGnupgProcess *self,
- GOutputStream *output)
-{
- g_return_if_fail (GCR_GNUPG_PROCESS (self));
- g_return_if_fail (output == NULL || G_OUTPUT_STREAM (output));
-
- if (output)
- g_object_ref (output);
- if (self->pv->attributes)
- g_object_unref (self->pv->attributes);
- self->pv->attributes = output;
- g_object_notify (G_OBJECT (self), "attribute-stream");
-}
-
-static void
-run_async_ready_callback (GcrGnupgProcess *self)
-{
- GAsyncReadyCallback callback;
- gpointer user_data;
-
- _gcr_debug ("running async callback");
-
- /* Remove these before completing */
- callback = self->pv->async_callback;
- user_data = self->pv->user_data;
- self->pv->async_callback = NULL;
- self->pv->user_data = NULL;
-
- if (callback != NULL)
- (callback) (G_OBJECT (self), G_ASYNC_RESULT (self), user_data);
-}
-
-static gboolean
-on_run_async_ready_callback_later (gpointer user_data)
-{
- run_async_ready_callback (GCR_GNUPG_PROCESS (user_data));
- return FALSE; /* Don't run this callback again */
-}
-
-static void
-run_async_ready_callback_later (GcrGnupgProcess *self)
-{
- _gcr_debug ("running async callback later");
- g_idle_add_full (G_PRIORITY_DEFAULT, on_run_async_ready_callback_later,
- g_object_ref (self), g_object_unref);
-}
-
-static void
-complete_run_process (GcrGnupgProcess *self)
-{
- g_return_if_fail (self->pv->running);
- g_return_if_fail (!self->pv->complete);
-
- self->pv->running = FALSE;
- self->pv->complete = TRUE;
-
- if (self->pv->error == NULL) {
- _gcr_debug ("completed process");
- } else {
- _gcr_debug ("completed process with error: %s",
- self->pv->error->message);
- }
-}
-
-static gboolean
-complete_source_is_done (GnupgSource *gnupg_source)
-{
- _gcr_debug ("all fds closed and process exited, completing");
-
- g_assert (gnupg_source->child_sig == 0);
- g_assert (gnupg_source->source_sig == 0);
-
- complete_run_process (gnupg_source->process);
- run_async_ready_callback (gnupg_source->process);
-
- /* All done, the source can go away now */
- g_source_unref ((GSource*)gnupg_source);
- return TRUE;
-}
-
-static void
-close_fd (int *fd)
-{
- g_assert (fd);
- if (*fd >= 0) {
- _gcr_debug ("closing fd: %d", *fd);
- close (*fd);
- }
- *fd = -1;
-}
-
-static void
-close_poll (GSource *source, GPollFD *poll)
-{
- g_source_remove_poll (source, poll);
- close_fd (&poll->fd);
- poll->revents = 0;
-}
-
-static gboolean
-unused_callback (gpointer data)
-{
- /* Never called */
- g_assert_not_reached ();
- return FALSE;
-}
-
-static gboolean
-on_gnupg_source_prepare (GSource *source, gint *timeout_)
-{
- GnupgSource *gnupg_source = (GnupgSource*)source;
- gint i;
-
- for (i = 0; i < NUM_FDS; ++i) {
- if (gnupg_source->polls[i].fd >= 0)
- return FALSE;
- }
-
- /* If none of the FDs are valid, then process immediately */
- return TRUE;
-}
-
-static gboolean
-on_gnupg_source_check (GSource *source)
-{
- GnupgSource *gnupg_source = (GnupgSource*)source;
- gint i;
-
- for (i = 0; i < NUM_FDS; ++i) {
- if (gnupg_source->polls[i].fd >= 0 && gnupg_source->polls[i].revents != 0)
- return TRUE;
- }
- return FALSE;
-}
-
-static void
-on_gnupg_source_finalize (GSource *source)
-{
- GnupgSource *gnupg_source = (GnupgSource*)source;
- gint i;
-
- if (gnupg_source->cancel_sig)
- g_signal_handler_disconnect (gnupg_source->cancellable, gnupg_source->cancel_sig);
- if (gnupg_source->cancellable)
- g_object_unref (gnupg_source->cancellable);
-
- for (i = 0; i < NUM_FDS; ++i)
- close_fd (&gnupg_source->polls[i].fd);
-
- g_object_unref (gnupg_source->process);
- if (gnupg_source->input_buf)
- g_byte_array_free (gnupg_source->input_buf, TRUE);
- g_string_free (gnupg_source->error_buf, TRUE);
- g_string_free (gnupg_source->status_buf, TRUE);
-
- g_assert (!gnupg_source->child_pid);
- g_assert (!gnupg_source->child_sig);
-}
-
-static gboolean
-read_output (int fd, GByteArray *buffer)
-{
- guchar block[1024];
- gssize result;
-
- g_return_val_if_fail (fd >= 0, FALSE);
-
- do {
- result = read (fd, block, sizeof (block));
- if (result < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return FALSE;
- } else {
- g_byte_array_append (buffer, block, result);
- }
- } while (result == sizeof (block));
-
- return TRUE;
-}
-
-static gboolean
-write_input (int fd, GByteArray *buffer)
-{
- gssize result;
-
- g_return_val_if_fail (fd >= 0, FALSE);
-
- for (;;) {
- result = write (fd, buffer->data, buffer->len);
- if (result < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return FALSE;
- } else {
- g_byte_array_remove_range (buffer, 0, result);
- return TRUE;
- }
- }
-}
-
-static void
-emit_status_for_each_line (const gchar *line, gpointer user_data)
-{
- GcrRecord *record;
-
- if (g_str_has_prefix (line, "[GNUPG:] ")) {
- _gcr_debug ("received status line: %s", line);
- line += 9;
- } else {
- g_message ("gnupg status record was not prefixed appropriately: %s", line);
- return;
- }
-
- record = _gcr_record_parse_spaces (line, -1);
- if (!record) {
- g_message ("couldn't parse status record: %s", line);
- return;
- }
-
- g_signal_emit (GCR_GNUPG_PROCESS (user_data), signals[STATUS_RECORD], 0, record);
- _gcr_record_free (record);
-}
-
-static void
-emit_error_for_each_line (const gchar *line, gpointer user_data)
-{
- _gcr_debug ("received error line: %s", line);
- g_signal_emit (GCR_GNUPG_PROCESS (user_data), signals[ERROR_LINE], 0, line);
-}
-
-static gboolean
-on_gnupg_source_input (GcrGnupgProcess *self,
- GnupgSource *gnupg_source,
- gint fd)
-{
- gssize read;
-
- if (gnupg_source->input_buf == NULL ||
- gnupg_source->input_buf->len == 0) {
- if (self->pv->input == NULL)
- return FALSE;
- if (!gnupg_source->input_buf)
- gnupg_source->input_buf = g_byte_array_new ();
- g_byte_array_set_size (gnupg_source->input_buf, 4096);
- read = g_input_stream_read (self->pv->input,
- gnupg_source->input_buf->data,
- gnupg_source->input_buf->len,
- gnupg_source->cancellable, NULL);
- g_byte_array_set_size (gnupg_source->input_buf, read < 0 ? 0 : read);
- if (read < 0)
- return FALSE;
- if (read == 0)
- return FALSE;
- }
-
- if (!write_input (fd, gnupg_source->input_buf)) {
- g_warning ("couldn't write output data to gnupg process");
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-on_gnupg_source_status (GcrGnupgProcess *self,
- GnupgSource *gnupg_source,
- gint fd)
-{
- GByteArray *buffer = g_byte_array_new ();
- gboolean result = TRUE;
-
- if (!read_output (fd, buffer)) {
- g_warning ("couldn't read status data from gnupg process");
- result = FALSE;
- } else {
- g_string_append_len (gnupg_source->status_buf, (gchar*)buffer->data, buffer->len);
- _gcr_util_parse_lines (gnupg_source->status_buf, buffer->len == 0,
- emit_status_for_each_line, self);
- }
-
- g_byte_array_unref (buffer);
- return result;
-}
-
-static gboolean
-on_gnupg_source_attribute (GcrGnupgProcess *self,
- GnupgSource *gnupg_source,
- gint fd)
-{
- GByteArray *buffer = g_byte_array_new ();
- gboolean result = TRUE;
-
- if (!read_output (fd, buffer)) {
- g_warning ("couldn't read attribute data from gnupg process");
- result = FALSE;
- } else if (buffer->len > 0) {
- _gcr_debug ("received %d bytes of attribute data", (gint)buffer->len);
- if (self->pv->attributes != NULL)
- g_output_stream_write_all (self->pv->attributes, buffer->data,
- buffer->len, NULL,
- gnupg_source->cancellable, NULL);
- }
-
- g_byte_array_unref (buffer);
- return result;
-}
-
-static gboolean
-on_gnupg_source_output (GcrGnupgProcess *self,
- GnupgSource *gnupg_source,
- gint fd)
-{
- GByteArray *buffer = g_byte_array_new ();
- gboolean result = TRUE;
-
- if (!read_output (fd, buffer)) {
- g_warning ("couldn't read output data from gnupg process");
- result = FALSE;
- } else if (buffer->len > 0) {
- _gcr_debug ("received %d bytes of output data", (gint)buffer->len);
- if (self->pv->output != NULL)
- g_output_stream_write_all (self->pv->output, buffer->data, buffer->len,
- NULL, gnupg_source->cancellable, NULL);
- }
-
- g_byte_array_unref (buffer);
- return result;
-}
-
-static gboolean
-on_gnupg_source_error (GcrGnupgProcess *self,
- GnupgSource *gnupg_source,
- gint fd,
- gboolean last)
-{
- GByteArray *buffer = g_byte_array_new ();
- gboolean result = TRUE;
-
- if (!read_output (fd, buffer)) {
- g_warning ("couldn't read error data from gnupg process");
- result = FALSE;
- } else {
- g_string_append_len (gnupg_source->error_buf, (gchar*)buffer->data, buffer->len);
- _gcr_util_parse_lines (gnupg_source->error_buf, last,
- emit_error_for_each_line, gnupg_source->process);
- }
-
- g_byte_array_unref (buffer);
- return result;
-}
-
-static gboolean
-on_gnupg_source_dispatch (GSource *source, GSourceFunc unused, gpointer user_data)
-{
- GnupgSource *gnupg_source = (GnupgSource*)source;
- GcrGnupgProcess *self = gnupg_source->process;
- GPollFD *poll;
- guint i;
-
- /* Standard input, no support yet */
- poll = &gnupg_source->polls[FD_INPUT];
- if (poll->fd >= 0) {
- if (poll->revents & G_IO_OUT)
- if (!on_gnupg_source_input (self, gnupg_source, poll->fd))
- poll->revents |= G_IO_HUP;
- if (poll->revents & G_IO_HUP)
- close_poll (source, poll);
- poll->revents = 0;
- }
-
- /* Status output */
- poll = &gnupg_source->polls[FD_STATUS];
- if (poll->fd >= 0) {
- if (poll->revents & G_IO_IN)
- if (!on_gnupg_source_status (self, gnupg_source, poll->fd))
- poll->revents |= G_IO_HUP;
- if (poll->revents & G_IO_HUP)
- close_poll (source, poll);
- poll->revents = 0;
- }
-
- /* Attribute output */
- poll = &gnupg_source->polls[FD_ATTRIBUTE];
- if (poll->fd >= 0) {
- if (poll->revents & G_IO_IN)
- if (!on_gnupg_source_attribute (self, gnupg_source, poll->fd))
- poll->revents |= G_IO_HUP;
- if (poll->revents & G_IO_HUP)
- close_poll (source, poll);
- poll->revents = 0;
- }
-
- /* Standard output */
- poll = &gnupg_source->polls[FD_OUTPUT];
- if (poll->fd >= 0) {
- if (poll->revents & G_IO_IN)
- if (!on_gnupg_source_output (self, gnupg_source, poll->fd))
- poll->revents |= G_IO_HUP;
- if (poll->revents & G_IO_HUP)
- close_poll (source, poll);
- poll->revents = 0;
- }
-
- /* Standard error */
- poll = &gnupg_source->polls[FD_ERROR];
- if (poll->fd >= 0) {
- if (poll->revents & G_IO_IN)
- if (!on_gnupg_source_error (self, gnupg_source, poll->fd,
- (poll->revents & G_IO_HUP) ? TRUE : FALSE))
- poll->revents |= G_IO_HUP;
- if (poll->revents & G_IO_HUP)
- close_poll (source, poll);
- poll->revents = 0;
- }
-
- for (i = 0; i < NUM_FDS; ++i) {
- if (gnupg_source->polls[i].fd >= 0)
- return TRUE;
- }
-
- /* Because we return below */
- gnupg_source->source_sig = 0;
-
- if (!gnupg_source->child_pid)
- complete_source_is_done (gnupg_source);
-
- return FALSE; /* Disconnect this source */
-}
-
-static GSourceFuncs gnupg_source_funcs = {
- on_gnupg_source_prepare,
- on_gnupg_source_check,
- on_gnupg_source_dispatch,
- on_gnupg_source_finalize,
-};
-
-static void
-on_gnupg_process_child_exited (GPid pid, gint status, gpointer user_data)
-{
- GnupgSource *gnupg_source = user_data;
- GcrGnupgProcess *self = gnupg_source->process;
- GError *error = NULL;
- gint code;
- guint i;
-
- _gcr_debug ("process exited: %d", (int)pid);
-
- g_spawn_close_pid (gnupg_source->child_pid);
- gnupg_source->child_pid = 0;
- gnupg_source->child_sig = 0;
-
- if (WIFEXITED (status)) {
- code = WEXITSTATUS (status);
- if (code != 0) {
- error = g_error_new (G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Gnupg process exited with code: %d"), code);
- }
- } else if (WIFSIGNALED (status)) {
- code = WTERMSIG (status);
- /* Ignore cases where we've signaled the process because we were cancelled */
- if (!g_error_matches (self->pv->error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
- error = g_error_new (G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Gnupg process was terminated with signal: %d"), code);
- }
-
- /* Take this as the async result error */
- if (error && !self->pv->error) {
- _gcr_debug ("%s", error->message);
- self->pv->error = error;
-
- /* Already have an error, just print out message */
- } else if (error) {
- g_warning ("%s", error->message);
- g_error_free (error);
- }
-
- for (i = 0; i < NUM_FDS; ++i) {
- if (gnupg_source->polls[i].fd >= 0)
- return;
- }
-
- complete_source_is_done (gnupg_source);
-}
-
-static void
-on_gnupg_process_child_setup (gpointer user_data)
-{
- int *child_fds = user_data;
- long val;
- guint i;
-
- /*
- * Clear close-on-exec flag for these file descriptors, so that
- * gnupg can write to them
- */
-
- for (i = 0; i < NUM_FDS; i++) {
- if (child_fds[i] >= 0) {
- val = fcntl (child_fds[i], F_GETFD);
- fcntl (child_fds[i], F_SETFD, val & ~FD_CLOEXEC);
- }
- }
-}
-
-static void
-on_cancellable_cancelled (GCancellable *cancellable, gpointer user_data)
-{
- GnupgSource *gnupg_source = user_data;
-
- g_assert (gnupg_source->process);
-
- _gcr_debug ("process cancelled");
-
- /* Set an error, which is respected when this actually completes. */
- if (gnupg_source->process->pv->error == NULL)
- gnupg_source->process->pv->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED,
- _("The operation was cancelled"));
-
- /* Try and kill the child process */
- if (gnupg_source->child_pid) {
- _gcr_debug ("sending term signal to process: %d",
- (int)gnupg_source->child_pid);
- kill (gnupg_source->child_pid, SIGTERM);
- }
-}
-
-/**
- * _gcr_gnupg_process_run_async:
- * @self: The process
- * @argv: (array zero-terminated=1): The arguments for the process, not including executable, terminated with %NULL.
- * @envp: (allow-none) (array zero-terminated=1): The environment for new process, terminated with %NULL.
- * @flags: Flags for starting the process.
- * @cancellable: (allow-none): Cancellation object
- * @callback: Will be called when operation completes.
- * @user_data: (closure): Data passed to callback.
- *
- * Run the gpg process. Only one 'run' operation can run per GcrGnupgProcess
- * object. The GcrGnupgProcess:output_data and GcrGnupgProcess:error_line
- * signals will be emitted when data is received from the gpg process.
- *
- * Unless the %GCR_GNUPG_PROCESS_RESPECT_LOCALE flag is specified, the process
- * will be run in the 'C' locale. If the %GCR_GNUPG_PROCESS_WITH_STATUS or
- * %GCR_GNUPG_PROCESS_WITH_ATTRIBUTES flags are set, then the gpg process
- * will be status and attribute output respectively. The
- * GcrGnupgProcess:status_record and GcrGnupgProcess:attribute_data signals
- * will provide this data.
- */
-void
-_gcr_gnupg_process_run_async (GcrGnupgProcess *self, const gchar **argv, const gchar **envp,
- GcrGnupgProcessFlags flags, GCancellable *cancellable,
- GAsyncReadyCallback callback, gpointer user_data)
-{
- GError *error = NULL;
- GPtrArray *args;
- GPtrArray *envs;
- int child_fds[NUM_FDS];
- int status_fds[2] = { -1, -1 };
- int attribute_fds[2] = { -1, -1 };
- int output_fd = -1;
- int error_fd = -1;
- int input_fd = -1;
- GnupgSource *gnupg_source;
- GSource *source;
- GPid pid;
- guint i;
-
- g_return_if_fail (GCR_IS_GNUPG_PROCESS (self));
- g_return_if_fail (argv);
- g_return_if_fail (callback);
- g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
-
- g_return_if_fail (self->pv->running == FALSE);
- g_return_if_fail (self->pv->complete == FALSE);
- g_return_if_fail (self->pv->executable);
-
- self->pv->async_callback = callback;
- self->pv->user_data = user_data;
-
- for (i = 0; i < NUM_FDS; i++)
- child_fds[i] = -1;
-
- /* The command needs to be updated with these status and attribute fds */
- args = g_ptr_array_new_with_free_func (g_free);
- g_ptr_array_add (args, g_strdup (self->pv->executable));
-
- /* Spawn/child will close all other attributes, besides thesthose in child_fds */
- child_fds[FD_INPUT] = 0;
- child_fds[FD_OUTPUT] = 1;
- child_fds[FD_ERROR] = 2;
-
- if (flags & GCR_GNUPG_PROCESS_WITH_STATUS) {
- if (pipe (status_fds) < 0)
- g_return_if_reached ();
- child_fds[FD_STATUS] = status_fds[1];
- g_ptr_array_add (args, g_strdup ("--status-fd"));
- g_ptr_array_add (args, g_strdup_printf ("%d", child_fds[FD_STATUS]));
- }
- if (flags & GCR_GNUPG_PROCESS_WITH_ATTRIBUTES) {
- if (pipe (attribute_fds) < 0)
- g_return_if_reached ();
- child_fds[FD_ATTRIBUTE] = attribute_fds[1];
- g_ptr_array_add (args, g_strdup ("--attribute-fd"));
- g_ptr_array_add (args, g_strdup_printf ("%d", child_fds[FD_ATTRIBUTE]));
- }
-
- if (self->pv->directory) {
- g_ptr_array_add (args, g_strdup ("--homedir"));
- g_ptr_array_add (args, g_strdup (self->pv->directory));
- }
-
- /* All the remaining arguments */
- for (i = 0; argv[i] != NULL; i++)
- g_ptr_array_add (args, g_strdup (argv[i]));
- g_ptr_array_add (args, NULL);
-
- envs = g_ptr_array_new ();
- for (i = 0; envp && envp[i] != NULL; i++) {
- if (flags & GCR_GNUPG_PROCESS_RESPECT_LOCALE ||
- !g_str_has_prefix (envp[i], "LOCALE="))
- g_ptr_array_add (envs, (gpointer)envp[i]);
- }
- if (!(flags & GCR_GNUPG_PROCESS_RESPECT_LOCALE))
- g_ptr_array_add (envs, (gpointer)"LOCALE=C");
- g_ptr_array_add (envs, NULL);
-
- if (_gcr_debugging) {
- gchar *command = g_strjoinv (" ", (gchar**)args->pdata);
- gchar *environ = g_strjoinv (", ", (gchar**)envs->pdata);
- _gcr_debug ("running command: %s", command);
- _gcr_debug ("process environment: %s", environ);
- g_free (command);
- g_free (environ);
- }
-
- g_spawn_async_with_pipes (self->pv->directory, (gchar**)args->pdata,
- (gchar**)envs->pdata, G_SPAWN_DO_NOT_REAP_CHILD,
- on_gnupg_process_child_setup, child_fds,
- &pid, &input_fd, &output_fd, &error_fd, &error);
-
- g_ptr_array_free (args, TRUE);
- g_ptr_array_free (envs, TRUE);
-
- /* Close 'wrong' ends of extra file descriptors */
- close_fd (&(status_fds[1]));
- close_fd (&(attribute_fds[1]));
-
- self->pv->complete = FALSE;
- self->pv->running = TRUE;
-
- if (error) {
- close_fd (&(status_fds[0]));
- close_fd (&(attribute_fds[0]));
- g_assert (!self->pv->error);
- self->pv->error = error;
- complete_run_process (self);
- run_async_ready_callback_later (self);
- return;
- }
-
- _gcr_debug ("process started: %d", (int)pid);
-
- source = g_source_new (&gnupg_source_funcs, sizeof (GnupgSource));
-
- /* Initialize the source */
- gnupg_source = (GnupgSource*)source;
- for (i = 0; i < NUM_FDS; i++)
- gnupg_source->polls[i].fd = -1;
- gnupg_source->error_buf = g_string_sized_new (128);
- gnupg_source->status_buf = g_string_sized_new (128);
- gnupg_source->process = g_object_ref (self);
- gnupg_source->child_pid = pid;
-
- gnupg_source->polls[FD_INPUT].fd = input_fd;
- if (input_fd >= 0) {
- gnupg_source->polls[FD_INPUT].events = G_IO_HUP | G_IO_OUT;
- g_source_add_poll (source, &gnupg_source->polls[FD_INPUT]);
- }
- gnupg_source->polls[FD_OUTPUT].fd = output_fd;
- if (output_fd >= 0) {
- gnupg_source->polls[FD_OUTPUT].events = G_IO_HUP | G_IO_IN;
- g_source_add_poll (source, &gnupg_source->polls[FD_OUTPUT]);
- }
- gnupg_source->polls[FD_ERROR].fd = error_fd;
- if (error_fd >= 0) {
- gnupg_source->polls[FD_ERROR].events = G_IO_HUP | G_IO_IN;
- g_source_add_poll (source, &gnupg_source->polls[FD_ERROR]);
- }
- gnupg_source->polls[FD_STATUS].fd = status_fds[0];
- if (status_fds[0] >= 0) {
- gnupg_source->polls[FD_STATUS].events = G_IO_HUP | G_IO_IN;
- g_source_add_poll (source, &gnupg_source->polls[FD_STATUS]);
- }
- gnupg_source->polls[FD_ATTRIBUTE].fd = attribute_fds[0];
- if (attribute_fds[0] >= 0) {
- gnupg_source->polls[FD_ATTRIBUTE].events = G_IO_HUP | G_IO_IN;
- g_source_add_poll (source, &gnupg_source->polls[FD_ATTRIBUTE]);
- }
-
- if (cancellable) {
- gnupg_source->cancellable = g_object_ref (cancellable);
- gnupg_source->cancel_sig = g_cancellable_connect (cancellable,
- G_CALLBACK (on_cancellable_cancelled),
- g_source_ref (source),
- (GDestroyNotify)g_source_unref);
- }
-
- g_source_set_callback (source, unused_callback, NULL, NULL);
- gnupg_source->source_sig = g_source_attach (source, g_main_context_default ());
-
- /* This assumes the outstanding reference to source */
- g_assert (!gnupg_source->child_sig);
- gnupg_source->child_sig = g_child_watch_add_full (G_PRIORITY_DEFAULT, pid,
- on_gnupg_process_child_exited,
- g_source_ref (source),
- (GDestroyNotify)g_source_unref);
-
- /* source is unreffed in complete_if_source_is_done() */
-}
-
-/**
- * _gcr_gnupg_process_run_finish:
- * @self: The process
- * @result: The result passed to the callback
- * @error: Location to raise an error on failure.
- *
- * Get the result of running a gnupg process.
- *
- * Return value: Whether the Gnupg process was run or not.
- */
-gboolean
-_gcr_gnupg_process_run_finish (GcrGnupgProcess *self, GAsyncResult *result,
- GError **error)
-{
- g_return_val_if_fail (GCR_IS_GNUPG_PROCESS (self), FALSE);
- g_return_val_if_fail (!error || !*error, FALSE);
- g_return_val_if_fail (G_ASYNC_RESULT (self) == result, FALSE);
- g_return_val_if_fail (self->pv->complete, FALSE);
-
- /* This allows the process to run again... */
- self->pv->complete = FALSE;
-
- g_assert (!self->pv->running);
- g_assert (!self->pv->async_callback);
- g_assert (!self->pv->user_data);
-
- if (self->pv->error) {
- g_propagate_error (error, self->pv->error);
- self->pv->error = NULL;
- return FALSE;
- }
-
- return TRUE;
-}