Blame gcr/gcr-gnupg-importer.c

Packit b00eeb
/*
Packit b00eeb
 * gnome-keyring
Packit b00eeb
 *
Packit b00eeb
 * Copyright (C) 2011 Collabora Ltd.
Packit b00eeb
 *
Packit b00eeb
 * This program is free software; you can redistribute it and/or modify
Packit b00eeb
 * it under the terms of the GNU Lesser General Public License as
Packit b00eeb
 * published by the Free Software Foundation; either version 2.1 of
Packit b00eeb
 * the License, or (at your option) any later version.
Packit b00eeb
 *
Packit b00eeb
 * This program is distributed in the hope that it will be useful, but
Packit b00eeb
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b00eeb
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit b00eeb
 * Lesser General Public License for more details.
Packit b00eeb
 *
Packit b00eeb
 * You should have received a copy of the GNU Lesser General Public
Packit b00eeb
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
Packit b00eeb
 *
Packit b00eeb
 * Author: Stef Walter <stefw@collabora.co.uk>
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
#include "config.h"
Packit b00eeb
Packit b00eeb
#include "gcr-gnupg-importer.h"
Packit b00eeb
#include "gcr-gnupg-process.h"
Packit b00eeb
#include "gcr-internal.h"
Packit b00eeb
Packit b00eeb
#include <glib/gi18n-lib.h>
Packit b00eeb
Packit b00eeb
enum {
Packit b00eeb
	PROP_0,
Packit b00eeb
	PROP_LABEL,
Packit b00eeb
	PROP_ICON,
Packit b00eeb
	PROP_IMPORTED,
Packit b00eeb
	PROP_DIRECTORY,
Packit b00eeb
	PROP_INTERACTION,
Packit b00eeb
	PROP_URI
Packit b00eeb
};
Packit b00eeb
Packit b00eeb
struct _GcrGnupgImporterPrivate {
Packit b00eeb
	GcrGnupgProcess *process;
Packit b00eeb
	GMemoryInputStream *packets;
Packit b00eeb
	GTlsInteraction *interaction;
Packit b00eeb
	gchar *first_error;
Packit b00eeb
	GArray *imported;
Packit b00eeb
};
Packit b00eeb
Packit b00eeb
static void gcr_gnupg_importer_iface (GcrImporterIface *iface);
Packit b00eeb
Packit b00eeb
G_DEFINE_TYPE_WITH_CODE (GcrGnupgImporter, _gcr_gnupg_importer, G_TYPE_OBJECT,
Packit b00eeb
                         G_IMPLEMENT_INTERFACE (GCR_TYPE_IMPORTER, gcr_gnupg_importer_iface);
Packit b00eeb
);
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
_gcr_gnupg_importer_init (GcrGnupgImporter *self)
Packit b00eeb
{
Packit b00eeb
	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_GNUPG_IMPORTER, GcrGnupgImporterPrivate);
Packit b00eeb
	self->pv->packets = G_MEMORY_INPUT_STREAM (g_memory_input_stream_new ());
Packit b00eeb
	self->pv->imported = g_array_new (TRUE, TRUE, sizeof (gchar *));
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
_gcr_gnupg_importer_dispose (GObject *obj)
Packit b00eeb
{
Packit b00eeb
	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj);
Packit b00eeb
Packit b00eeb
	if (self->pv->process)
Packit b00eeb
		g_object_run_dispose (G_OBJECT (self->pv->process));
Packit b00eeb
	g_clear_object (&self->pv->process);
Packit b00eeb
	g_clear_object (&self->pv->packets);
Packit b00eeb
	g_clear_object (&self->pv->interaction);
Packit b00eeb
Packit b00eeb
	G_OBJECT_CLASS (_gcr_gnupg_importer_parent_class)->dispose (obj);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
_gcr_gnupg_importer_finalize (GObject *obj)
Packit b00eeb
{
Packit b00eeb
	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj);
Packit b00eeb
Packit b00eeb
	g_array_free (self->pv->imported, TRUE);
Packit b00eeb
	g_free (self->pv->first_error);
Packit b00eeb
Packit b00eeb
	G_OBJECT_CLASS (_gcr_gnupg_importer_parent_class)->finalize (obj);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gchar *
Packit b00eeb
calculate_label (GcrGnupgImporter *self)
Packit b00eeb
{
Packit b00eeb
	const gchar *directory;
Packit b00eeb
Packit b00eeb
	directory = _gcr_gnupg_process_get_directory (self->pv->process);
Packit b00eeb
	if (directory == NULL)
Packit b00eeb
		return g_strdup (_("GnuPG Keyring"));
Packit b00eeb
	else
Packit b00eeb
		return g_strdup_printf (_("GnuPG Keyring: %s"), directory);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static GIcon *
Packit b00eeb
calculate_icon (GcrGnupgImporter *self)
Packit b00eeb
{
Packit b00eeb
	const gchar *directory;
Packit b00eeb
Packit b00eeb
	directory = _gcr_gnupg_process_get_directory (self->pv->process);
Packit b00eeb
	if (directory == NULL)
Packit b00eeb
		return g_themed_icon_new ("user-home");
Packit b00eeb
	else
Packit b00eeb
		return g_themed_icon_new ("folder");
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gchar *
Packit b00eeb
calculate_uri (GcrGnupgImporter *self)
Packit b00eeb
{
Packit b00eeb
	const gchar *directory;
Packit b00eeb
Packit b00eeb
	directory = _gcr_gnupg_process_get_directory (self->pv->process);
Packit b00eeb
	if (directory == NULL)
Packit b00eeb
		return g_strdup ("gnupg://");
Packit b00eeb
	else
Packit b00eeb
		return g_strdup_printf ("gnupg://%s", directory);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
on_process_error_line (GcrGnupgProcess *process,
Packit b00eeb
                       const gchar *line,
Packit b00eeb
                       gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (user_data);
Packit b00eeb
Packit b00eeb
	if (self->pv->first_error)
Packit b00eeb
		return TRUE;
Packit b00eeb
Packit b00eeb
	if (g_str_has_prefix (line, "gpg: ")) {
Packit b00eeb
		line += 5;
Packit b00eeb
		if (g_pattern_match_simple ("key ????????:*", line))
Packit b00eeb
			line += 13;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	while (line[0] && g_ascii_isspace (line[0]))
Packit b00eeb
		line++;
Packit b00eeb
Packit b00eeb
	self->pv->first_error = g_strdup (line);
Packit b00eeb
	g_strstrip (self->pv->first_error);
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
on_process_status_record (GcrGnupgProcess *process,
Packit b00eeb
                          GcrRecord *record,
Packit b00eeb
                          gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (user_data);
Packit b00eeb
	const gchar *value;
Packit b00eeb
	gchar *fingerprint;
Packit b00eeb
Packit b00eeb
	if (_gcr_record_get_schema (record) != GCR_RECORD_SCHEMA_IMPORT_OK)
Packit b00eeb
		return TRUE;
Packit b00eeb
Packit b00eeb
	value = _gcr_record_get_raw (record, GCR_RECORD_IMPORT_FINGERPRINT);
Packit b00eeb
	if (value != NULL && value[0] != 0) {
Packit b00eeb
		fingerprint = g_strdup (value);
Packit b00eeb
		g_array_append_val (self->pv->imported, fingerprint);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
_gcr_gnupg_importer_set_property (GObject *obj,
Packit b00eeb
                                  guint prop_id,
Packit b00eeb
                                  const GValue *value,
Packit b00eeb
                                  GParamSpec *pspec)
Packit b00eeb
{
Packit b00eeb
	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj);
Packit b00eeb
Packit b00eeb
	switch (prop_id) {
Packit b00eeb
	case PROP_DIRECTORY:
Packit b00eeb
		self->pv->process = _gcr_gnupg_process_new (g_value_get_string (value), NULL);
Packit b00eeb
		_gcr_gnupg_process_set_input_stream (self->pv->process, G_INPUT_STREAM (self->pv->packets));
Packit b00eeb
		g_signal_connect (self->pv->process, "error-line", G_CALLBACK (on_process_error_line), self);
Packit b00eeb
		g_signal_connect (self->pv->process, "status-record", G_CALLBACK (on_process_status_record), self);
Packit b00eeb
		break;
Packit b00eeb
	case PROP_INTERACTION:
Packit b00eeb
		g_clear_object (&self->pv->interaction);
Packit b00eeb
		self->pv->interaction = g_value_dup_object (value);
Packit b00eeb
		break;
Packit b00eeb
	default:
Packit b00eeb
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
Packit b00eeb
		break;
Packit b00eeb
	}
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
_gcr_gnupg_importer_get_property (GObject *obj,
Packit b00eeb
                                  guint prop_id,
Packit b00eeb
                                  GValue *value,
Packit b00eeb
                                  GParamSpec *pspec)
Packit b00eeb
{
Packit b00eeb
	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj);
Packit b00eeb
Packit b00eeb
	switch (prop_id) {
Packit b00eeb
	case PROP_LABEL:
Packit b00eeb
		g_value_take_string (value, calculate_label (self));
Packit b00eeb
		break;
Packit b00eeb
	case PROP_ICON:
Packit b00eeb
		g_value_take_object (value, calculate_icon (self));
Packit b00eeb
		break;
Packit b00eeb
	case PROP_IMPORTED:
Packit b00eeb
		g_value_set_boxed (value, _gcr_gnupg_importer_get_imported (self));
Packit b00eeb
		break;
Packit b00eeb
	case PROP_DIRECTORY:
Packit b00eeb
		g_value_set_string (value, _gcr_gnupg_process_get_directory (self->pv->process));
Packit b00eeb
		break;
Packit b00eeb
	case PROP_INTERACTION:
Packit b00eeb
		g_value_set_object (value, self->pv->interaction);
Packit b00eeb
		break;
Packit b00eeb
	case PROP_URI:
Packit b00eeb
		g_value_take_string (value, calculate_uri (self));
Packit b00eeb
		break;
Packit b00eeb
	default:
Packit b00eeb
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
Packit b00eeb
		break;
Packit b00eeb
	}
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
_gcr_gnupg_importer_class_init (GcrGnupgImporterClass *klass)
Packit b00eeb
{
Packit b00eeb
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit b00eeb
	GckBuilder builder = GCK_BUILDER_INIT;
Packit b00eeb
Packit b00eeb
	gobject_class->dispose = _gcr_gnupg_importer_dispose;
Packit b00eeb
	gobject_class->finalize = _gcr_gnupg_importer_finalize;
Packit b00eeb
	gobject_class->set_property = _gcr_gnupg_importer_set_property;
Packit b00eeb
	gobject_class->get_property = _gcr_gnupg_importer_get_property;
Packit b00eeb
Packit b00eeb
	g_type_class_add_private (gobject_class, sizeof (GcrGnupgImporterPrivate));
Packit b00eeb
Packit b00eeb
	g_object_class_override_property (gobject_class, PROP_LABEL, "label");
Packit b00eeb
	g_object_class_override_property (gobject_class, PROP_ICON, "icon");
Packit b00eeb
	g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
Packit b00eeb
	g_object_class_override_property (gobject_class, PROP_URI, "uri");
Packit b00eeb
Packit b00eeb
	g_object_class_install_property (gobject_class, PROP_IMPORTED,
Packit b00eeb
	           g_param_spec_boxed ("imported", "Imported", "Fingerprints of imported keys",
Packit b00eeb
	                               G_TYPE_STRV, G_PARAM_READABLE));
Packit b00eeb
Packit b00eeb
	g_object_class_install_property (gobject_class, PROP_DIRECTORY,
Packit b00eeb
	           g_param_spec_string ("directory", "Directory", "Directory to import keys to",
Packit b00eeb
	                                NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
Packit b00eeb
Packit b00eeb
	gck_builder_add_ulong (&builder, CKA_CLASS, CKO_GCR_GNUPG_RECORDS);
Packit b00eeb
	gcr_importer_register (GCR_TYPE_GNUPG_IMPORTER, gck_builder_end (&builder));
Packit b00eeb
Packit b00eeb
	_gcr_initialize_library ();
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static GList *
Packit b00eeb
_gcr_gnupg_importer_create_for_parsed (GcrParsed *parsed)
Packit b00eeb
{
Packit b00eeb
	GcrImporter *self;
Packit b00eeb
Packit b00eeb
	if (gcr_parsed_get_format (parsed) != GCR_FORMAT_OPENPGP_PACKET)
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
	self = _gcr_gnupg_importer_new (NULL);
Packit b00eeb
	if (!gcr_importer_queue_for_parsed (self, parsed))
Packit b00eeb
		g_assert_not_reached ();
Packit b00eeb
Packit b00eeb
	return g_list_append (NULL, self);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
_gcr_gnupg_importer_queue_for_parsed (GcrImporter *importer,
Packit b00eeb
                                      GcrParsed *parsed)
Packit b00eeb
{
Packit b00eeb
	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (importer);
Packit b00eeb
	gconstpointer block;
Packit b00eeb
	gsize n_block;
Packit b00eeb
Packit b00eeb
	if (gcr_parsed_get_format (parsed) != GCR_FORMAT_OPENPGP_PACKET)
Packit b00eeb
		return FALSE;
Packit b00eeb
Packit b00eeb
	block = gcr_parsed_get_data (parsed, &n_block);
Packit b00eeb
	g_return_val_if_fail (block, FALSE);
Packit b00eeb
Packit b00eeb
	g_memory_input_stream_add_data (self->pv->packets, g_memdup (block, n_block),
Packit b00eeb
	                                n_block, g_free);
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
on_process_run_complete (GObject *source,
Packit b00eeb
                         GAsyncResult *result,
Packit b00eeb
                         gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
Packit b00eeb
	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (g_async_result_get_source_object (user_data));
Packit b00eeb
	GError *error = NULL;
Packit b00eeb
Packit b00eeb
	if (!_gcr_gnupg_process_run_finish (GCR_GNUPG_PROCESS (source), result, &error)) {
Packit b00eeb
		if (g_error_matches (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED) && self->pv->first_error) {
Packit b00eeb
			g_simple_async_result_set_error (res, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
Packit b00eeb
			                                 "%s", self->pv->first_error);
Packit b00eeb
			g_error_free (error);
Packit b00eeb
		} else {
Packit b00eeb
			g_simple_async_result_take_error (res, error);
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	g_simple_async_result_complete (res);
Packit b00eeb
	g_object_unref (self);
Packit b00eeb
	g_object_unref (res);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
_gcr_gnupg_importer_import_async (GcrImporter *importer,
Packit b00eeb
                                  GCancellable *cancellable,
Packit b00eeb
                                  GAsyncReadyCallback callback,
Packit b00eeb
                                  gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (importer);
Packit b00eeb
	GSimpleAsyncResult *res;
Packit b00eeb
	const gchar *argv[] = { "--import", NULL };
Packit b00eeb
Packit b00eeb
	g_free (self->pv->first_error);
Packit b00eeb
	self->pv->first_error = NULL;
Packit b00eeb
Packit b00eeb
	res = g_simple_async_result_new (G_OBJECT (importer), callback, user_data,
Packit b00eeb
	                                 _gcr_gnupg_importer_import_async);
Packit b00eeb
Packit b00eeb
	_gcr_gnupg_process_run_async (self->pv->process, argv, NULL,
Packit b00eeb
	                              GCR_GNUPG_PROCESS_WITH_STATUS,
Packit b00eeb
	                              cancellable, on_process_run_complete,
Packit b00eeb
	                              g_object_ref (res));
Packit b00eeb
Packit b00eeb
	g_object_unref (res);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
_gcr_gnupg_importer_import_finish (GcrImporter *importer,
Packit b00eeb
                                   GAsyncResult *result,
Packit b00eeb
                                   GError **error)
Packit b00eeb
{
Packit b00eeb
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (importer),
Packit b00eeb
	                      _gcr_gnupg_importer_import_async), FALSE);
Packit b00eeb
Packit b00eeb
	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
Packit b00eeb
		return FALSE;
Packit b00eeb
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_gnupg_importer_iface (GcrImporterIface *iface)
Packit b00eeb
{
Packit b00eeb
	iface->create_for_parsed = _gcr_gnupg_importer_create_for_parsed;
Packit b00eeb
	iface->queue_for_parsed = _gcr_gnupg_importer_queue_for_parsed;
Packit b00eeb
	iface->import_async = _gcr_gnupg_importer_import_async;
Packit b00eeb
	iface->import_finish = _gcr_gnupg_importer_import_finish;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * _gcr_gnupg_importer_new:
Packit b00eeb
 * @directory: (allow-none): the directory to import to, or %NULL for default
Packit b00eeb
 *
Packit b00eeb
 * Create a new #GcrGnupgImporter.
Packit b00eeb
 *
Packit b00eeb
 * Returns: (transfer full) (type Gcr.GnupgImporter): the new importer
Packit b00eeb
 */
Packit b00eeb
GcrImporter *
Packit b00eeb
_gcr_gnupg_importer_new (const gchar *directory)
Packit b00eeb
{
Packit b00eeb
	return g_object_new (GCR_TYPE_GNUPG_IMPORTER,
Packit b00eeb
	                     "directory", directory,
Packit b00eeb
	                     NULL);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
const gchar **
Packit b00eeb
_gcr_gnupg_importer_get_imported (GcrGnupgImporter *self)
Packit b00eeb
{
Packit b00eeb
	g_return_val_if_fail (GCR_IS_GNUPG_IMPORTER (self), NULL);
Packit b00eeb
	return (const gchar **)self->pv->imported->data;
Packit b00eeb
}