#include "gcr-marshal.h"
#include "gcr-util.h"
-#include "egg/egg-spawn.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,
GPid child_pid;
guint child_sig;
+ GCancellable *cancellable;
guint cancel_sig;
} GnupgSource;
gobject_class->set_property = _gcr_gnupg_process_set_property;
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::output-data:
+ * @data: a #GByteArray of output data.
+ *
+ * Signal emitted when normal output data is available from the gnupg
+ * process. The data does not necessarily come on line boundaries, and
+ * won't be null-terminated.
+ */
signals[OUTPUT_DATA] = g_signal_new ("output-data", GCR_TYPE_GNUPG_PROCESS,
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcrGnupgProcessClass, output_data),
NULL, NULL, _gcr_marshal_VOID__BOXED,
G_TYPE_NONE, 1, G_TYPE_BYTE_ARRAY);
+ /**
+ * 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);
+ /**
+ * GcrGnupgProcess::attribute-data:
+ * @data: a #GByteArray of attribute data.
+ *
+ * Signal emitted when attribute data is available from the gnupg
+ * process.
+ */
signals[ATTRIBUTE_DATA] = g_signal_new ("attribute-data", GCR_TYPE_GNUPG_PROCESS,
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcrGnupgProcessClass, attribute_data),
NULL, NULL, _gcr_marshal_VOID__BOXED,
* The executable will default to the compiled in path if a %NULL executable
* argument is used.
*
- * Returns: (transfer full) A newly allocated process.
+ * Returns: (transfer full): A newly allocated process.
*/
GcrGnupgProcess*
_gcr_gnupg_process_new (const gchar *directory, const gchar *executable)
GnupgSource *gnupg_source = (GnupgSource*)source;
gint i;
- if (gnupg_source->cancel_sig) {
- g_source_remove (gnupg_source->cancel_sig);
- gnupg_source->cancel_sig = 0;
- }
+ 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);
guchar block[1024];
gssize result;
- g_return_val_if_fail (fd >= 0, -1);
+ g_return_val_if_fail (fd >= 0, FALSE);
do {
result = read (fd, block, sizeof (block));
GByteArray *buffer;
GPollFD *poll;
- /* Standard input, no suport yet */
+ /* Standard input, no support yet */
poll = &gnupg_source->polls[FD_INPUT];
if (poll->fd >= 0 && poll->revents != 0) {
close_poll (source, poll);
if (!read_output (poll->fd, buffer)) {
g_warning ("couldn't read output data from gnupg process");
} else if (buffer->len > 0) {
- _gcr_debug ("received %d bytes of attribute data", (gint)buffer->len);
+ _gcr_debug ("received %d bytes of output data", (gint)buffer->len);
g_signal_emit (gnupg_source->process, signals[OUTPUT_DATA], 0, buffer);
}
g_byte_array_unref (buffer);
code = WEXITSTATUS (status);
if (code != 0) {
error = g_error_new (G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- "Gnupg process exited with code: %d", code);
+ _("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);
+ _("Gnupg process was terminated with signal: %d"), code);
}
/* Take this as the async result error */
/* Already have an error, just print out message */
} else if (error) {
- g_message ("%s", error->message);
+ g_warning ("%s", error->message);
g_error_free (error);
}
/* 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");
+ _("The operation was cancelled"));
complete_if_source_is_done (gnupg_source);
}
/**
* _gcr_gnupg_process_run_async:
* @self: The process
- * @argv: The arguments for the process, not including executable
- * @envp: (allow-none): The environment for new 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
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);
}
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),
* @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,
static void
teardown (Test *test, gconstpointer unused)
{
- if (test->result)
- g_object_unref (test->result);
- if (test->process)
- g_object_unref (test->process);
+ g_assert (!test->result);
+ g_assert (!test->process);
if (test->output_buf)
g_string_free (test->output_buf, TRUE);
if (test->error_buf)
g_object_get (test->process, "executable", &value, NULL);
g_assert_cmpstr (value, ==, "/path/to/executable");
g_free (value);
+
+ g_clear_object (&test->process);
}
static void
{
const gchar *argv[] = { NULL };
GError *error = NULL;
+ gboolean ret;
gchar *script;
script = build_script_path ("mock-simple-output");
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_no_error (error);
+ g_assert (ret == TRUE);
g_assert_cmpstr ("simple-output\n", ==, test->output_buf->str);
+
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
static void
const gchar *argv[] = { NULL };
GError *error = NULL;
gchar *script;
+ gboolean ret;
script = build_script_path ("mock-simple-error");
test->process = _gcr_gnupg_process_new (NULL, script);
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_no_error (error);
+ g_assert (ret == TRUE);
g_assert_cmpstr ("line 1: more line 1\nline 2\nline 3\n", ==, test->error_buf->str);
+
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
static void
const gchar *argv[] = { NULL };
GError *error = NULL;
gchar *script;
+ gboolean ret;
script = build_script_path ("mock-status-and-output");
test->process = _gcr_gnupg_process_new (NULL, script);
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_no_error (error);
+ g_assert (ret == TRUE);
g_assert (test->record);
g_assert_cmpstr (_gcr_record_get_raw (test->record, 0), ==, "SCHEMA");
g_assert_cmpstr (_gcr_record_get_raw (test->record, 4), ==, "four");
g_assert_cmpstr (_gcr_record_get_raw (test->record, 5), ==, NULL);
g_assert_cmpstr ("Here's some output\nMore output\n", ==, test->output_buf->str);
+
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
static void
const gchar *argv[] = { NULL };
GError *error = NULL;
gchar *script;
+ gboolean ret;
script = build_script_path ("mock-status-and-attribute");
test->process = _gcr_gnupg_process_new (NULL, script);
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_no_error (error);
+ g_assert (ret == TRUE);
g_assert (test->record);
g_assert_cmpstr (_gcr_record_get_raw (test->record, 0), ==, "SCHEMA");
g_assert_cmpstr (_gcr_record_get_raw (test->record, 4), ==, "four");
g_assert_cmpstr (_gcr_record_get_raw (test->record, 5), ==, NULL);
g_assert_cmpstr ("1lc923g4laoeurc23rc241lcg2r23c4gr3", ==, test->attribute_buf->str);
+
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
{
GError *error = NULL;
gchar *script;
+ gboolean ret;
const gchar *argv[] = {
"-1", "value1",
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_no_error (error);
+ g_assert (ret == TRUE);
g_assert_cmpstr ("value1\nvalue2\n", ==, test->output_buf->str);
g_assert_cmpstr ("VALUE1VALUE2\n", ==, test->error_buf->str);
+
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
static void
gchar *script;
gchar *directory;
gchar *check;
+ gboolean ret;
directory = g_get_current_dir ();
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_no_error (error);
+ g_assert (ret == TRUE);
check = g_strdup_printf ("DIR: %s\n", directory);
g_assert_cmpstr (check, ==, test->output_buf->str);
g_free (check);
g_free (directory);
+
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
static void
GError *error = NULL;
gchar *script;
const gchar *argv[] = { NULL };
+ gboolean ret;
script = build_script_path ("mock-invalid");
test->process = _gcr_gnupg_process_new (NULL, script);
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT);
g_clear_error (&error);
+ g_assert (ret == FALSE);
+
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
static void
GError *error = NULL;
gchar *script;
const gchar *argv[] = { "55" };
+ gboolean ret;
script = build_script_path ("mock-fail-exit");
test->process = _gcr_gnupg_process_new (NULL, script);
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED);
g_assert_cmpstr (error->message, ==, "Gnupg process exited with code: 55");
g_clear_error (&error);
+ g_assert (ret == FALSE);
+
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
static void
GError *error = NULL;
gchar *script;
const gchar *argv[] = { "15" };
+ gboolean ret;
script = build_script_path ("mock-fail-signal");
test->process = _gcr_gnupg_process_new (NULL, script);
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED);
g_assert_cmpstr (error->message, ==, "Gnupg process was terminated with signal: 15");
g_clear_error (&error);
+ g_assert (ret == FALSE);
+
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
static void
gchar *script;
const gchar *argv[] = { "15" };
GCancellable *cancellable;
+ gboolean ret;
cancellable = g_cancellable_new ();
egg_test_wait_until (500);
g_assert (test->result);
- _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_clear_error (&error);
+ g_assert (ret == FALSE);
+
+ g_object_unref (cancellable);
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
+}
+
+static void
+on_process_output_cancel (GcrGnupgProcess *process, GByteArray *buffer, gpointer user_data)
+{
+ GCancellable *cancellable = G_CANCELLABLE (user_data);
+ g_cancellable_cancel (cancellable);
+}
+
+static void
+test_run_and_cancel_later (Test *test, gconstpointer unused)
+{
+ GError *error = NULL;
+ gchar *script;
+ const gchar *argv[] = { "15" };
+ GCancellable *cancellable;
+ gboolean ret;
+
+ cancellable = g_cancellable_new ();
+
+ script = build_script_path ("mock-simple-output");
+ test->process = _gcr_gnupg_process_new (NULL, script);
+ g_signal_connect (test->process, "output-data", G_CALLBACK (on_process_output_cancel), cancellable);
+ g_free (script);
+
+ _gcr_gnupg_process_run_async (test->process, argv, NULL, 0, cancellable, on_async_ready, test);
+ egg_test_wait_until (500);
+
+ g_assert (test->result);
+ ret = _gcr_gnupg_process_run_finish (test->process, test->result, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_clear_error (&error);
+ g_assert (ret == FALSE);
+
+ g_object_unref (cancellable);
+ g_clear_object (&test->result);
+ g_clear_object (&test->process);
}
int
g_test_add ("/gcr/gnupg-process/run_fail_exit", Test, NULL, setup, test_run_fail_exit, teardown);
g_test_add ("/gcr/gnupg-process/run_fail_signal", Test, NULL, setup, test_run_fail_signal, teardown);
g_test_add ("/gcr/gnupg-process/run_and_cancel", Test, NULL, setup, test_run_and_cancel, teardown);
+ g_test_add ("/gcr/gnupg-process/run_and_cancel_later", Test, NULL, setup, test_run_and_cancel_later, teardown);
return egg_tests_run_in_thread_with_loop ();
}