Add a Secret Service password plugin. soc.2008.masterpassword

Thu, 23 Aug 2012 01:27:48 -0400

author
Elliott Sales de Andrade <qulogic@pidgin.im>
date
Thu, 23 Aug 2012 01:27:48 -0400
branch
soc.2008.masterpassword
changeset 34107
efe6019c9e91
parent 34106
1b0c94670bcc
child 34108
7a07b6857540

Add a Secret Service password plugin.

No, not *that* Secret Service... The password storing one:
http://developer.gnome.org/libsecret/

configure.ac file | annotate | diff | comparison | revisions
libpurple/plugins/keyrings/Makefile.am file | annotate | diff | comparison | revisions
libpurple/plugins/keyrings/secretservice.c file | annotate | diff | comparison | revisions
--- a/configure.ac	Wed Aug 22 18:19:53 2012 -0400
+++ b/configure.ac	Thu Aug 23 01:27:48 2012 -0400
@@ -1447,6 +1447,29 @@
 fi
 
 dnl #######################################################################
+dnl # Check for Secret Service headers
+dnl #######################################################################
+
+AC_ARG_ENABLE(libsecret, [AC_HELP_STRING([--disable-secret-service], [disable Secret Service support])], enable_secret_service=$enableval, enable_secret_service=yes)
+
+dnl Check for libsecret; if we don't have it, oh well
+if test "x$enable_secret_service" = "xyes" ; then
+	PKG_CHECK_MODULES(SECRETSERVICE, [libsecret-1], [
+		AC_SUBST(SECRETSERVICE_CFLAGS)
+		AC_SUBST(SECRETSERVICE_LIBS)
+		AC_DEFINE(HAVE_SECRETSERVICE, 1, [Define if we have Secret Service.])
+	], [
+		if test "x$force_deps" = "xyes" ; then
+			AC_MSG_ERROR([
+Secret Service development headers not found.
+Use --disable-secret-service if you do not need Secret Service support.
+])
+		fi])
+fi
+
+AM_CONDITIONAL(ENABLE_SECRETSERVICE, test "x$enable_secret_service" = "xyes")
+
+dnl #######################################################################
 dnl # Check for GNOME Keyring headers
 dnl #######################################################################
 
@@ -2806,6 +2829,7 @@
 fi
 echo Build with GNU Libidn......... : $enable_idn
 echo Build with NetworkManager..... : $enable_nm
+echo Build with Secret Service..... : $enable_secret_service
 echo Build with GNOME Keyring...... : $enable_gnome_keyring
 echo Build with KWallet............ : $enable_kwallet
 echo SSL Library/Libraries......... : $msg_ssl
--- a/libpurple/plugins/keyrings/Makefile.am	Wed Aug 22 18:19:53 2012 -0400
+++ b/libpurple/plugins/keyrings/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
@@ -8,6 +8,15 @@
 internalkeyring_la_SOURCES = internalkeyring.c
 internalkeyring_la_LIBADD  = $(GLIB_LIBS)
 
+if ENABLE_SECRETSERVICE
+
+secretservice_la_CFLAGS  = $(AM_CPPFLAGS) $(SECRETSERVICE_CFLAGS)
+secretservice_la_LDFLAGS = -module -avoid-version
+secretservice_la_SOURCES = secretservice.c
+secretservice_la_LIBADD  = $(GLIB_LIBS) $(SECRETSERVICE_LIBS)
+
+endif
+
 if ENABLE_GNOMEKEYRING
 
 gnomekeyring_la_CFLAGS  = $(AM_CPPFLAGS) $(GNOMEKEYRING_CFLAGS)
@@ -35,6 +44,11 @@
 plugin_LTLIBRARIES = \
 	internalkeyring.la
 
+if ENABLE_SECRETSERVICE
+plugin_LTLIBRARIES += \
+	secretservice.la
+endif
+
 if ENABLE_GNOMEKEYRING
 plugin_LTLIBRARIES += \
 	gnomekeyring.la
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/keyrings/secretservice.c	Thu Aug 23 01:27:48 2012 -0400
@@ -0,0 +1,360 @@
+/* purple
+ * @file secretservice.c Secret Service password storage
+ * @ingroup plugins
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program ; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include "internal.h"
+#include "account.h"
+#include "debug.h"
+#include "keyring.h"
+#include "plugin.h"
+#include "version.h"
+
+#include <libsecret/secret.h>
+
+#define SECRETSERVICE_NAME        N_("Secret Service")
+#define SECRETSERVICE_ID          "keyring-libsecret"
+
+#define ERR_SECRETSERVICEPLUGIN   (ss_error_domain())
+
+static PurpleKeyring *keyring_handler = NULL;
+
+static const SecretSchema purple_schema = {
+	"im.pidgin.Purple", SECRET_SCHEMA_NONE,
+	{
+		{"user", SECRET_SCHEMA_ATTRIBUTE_STRING},
+		{"protocol", SECRET_SCHEMA_ATTRIBUTE_STRING},
+		{"NULL", 0}
+	},
+	/* Reserved fields */
+	0, 0, 0, 0, 0, 0, 0, 0
+};
+
+typedef struct _InfoStorage InfoStorage;
+
+struct _InfoStorage
+{
+	PurpleAccount *account;
+	gpointer cb;
+	gpointer user_data;
+};
+
+static GQuark
+ss_error_domain(void)
+{
+	return g_quark_from_static_string("SecretService plugin");
+}
+
+
+/***********************************************/
+/*     Keyring interface                       */
+/***********************************************/
+static void
+ss_read_continue(GObject *object, GAsyncResult *result, gpointer data)
+{
+	InfoStorage *storage = data;
+	PurpleAccount *account = storage->account;
+	PurpleKeyringReadCallback cb = storage->cb;
+	char *password;
+	GError *error = NULL;
+
+	password = secret_password_lookup_finish(result, &error);
+
+	if (error != NULL) {
+		int code = error->code;
+		g_error_free(error);
+
+		switch (code) {
+			case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND:
+			case G_DBUS_ERROR_IO_ERROR:
+				error = g_error_new(ERR_SECRETSERVICEPLUGIN,
+				                    PURPLE_KEYRING_ERROR_NOCHANNEL,
+				                    "Failed to communicate with Secret Service (account : %s).",
+				                    purple_account_get_username(account));
+				if (cb != NULL)
+					cb(account, NULL, error, storage->user_data);
+				g_error_free(error);
+				break;
+
+			default:
+				error = g_error_new(ERR_SECRETSERVICEPLUGIN,
+				                    PURPLE_KEYRING_ERROR_NOCHANNEL,
+				                    "Unknown error (account : %s).",
+				                    purple_account_get_username(account));
+				if (cb != NULL)
+					cb(account, NULL, error, storage->user_data);
+				g_error_free(error);
+				break;
+		}
+
+	} else if (password == NULL) {
+		error = g_error_new(ERR_SECRETSERVICEPLUGIN,
+		                    PURPLE_KEYRING_ERROR_NOPASSWD,
+		                    "No password found for account: %s",
+		                    purple_account_get_username(account));
+		if (cb != NULL)
+			cb(account, NULL, error, storage->user_data);
+		g_error_free(error);
+
+	} else {
+		if (cb != NULL)
+			cb(account, password, NULL, storage->user_data);
+	}
+
+	g_free(storage);
+}
+
+static void
+ss_read(PurpleAccount *account, PurpleKeyringReadCallback cb, gpointer data)
+{
+	InfoStorage *storage = g_new0(InfoStorage, 1);
+
+	storage->account = account;
+	storage->cb = cb;
+	storage->user_data = data;
+
+	secret_password_lookup(&purple_schema,
+	                       NULL, ss_read_continue, storage,
+	                       "user", purple_account_get_username(account),
+	                       "protocol", purple_account_get_protocol_id(account),
+	                       NULL);
+}
+
+static void
+ss_save_continue(GObject *object, GAsyncResult *result, gpointer data)
+{
+	InfoStorage *storage = data;
+	PurpleKeyringSaveCallback cb;
+	GError *error = NULL;
+	PurpleAccount *account;
+
+	account = storage->account;
+	cb = storage->cb;
+
+	secret_password_store_finish(result, &error);
+
+	if (error != NULL) {
+		int code = error->code;
+		g_error_free(error);
+
+		switch (code) {
+			case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND:
+			case G_DBUS_ERROR_IO_ERROR:
+				purple_debug_info("keyring-libsecret",
+				                  "Failed to communicate with Secret Service (account : %s (%s)).\n",
+				                  purple_account_get_username(account),
+				                  purple_account_get_protocol_id(account));
+				error = g_error_new(ERR_SECRETSERVICEPLUGIN,
+				                    PURPLE_KEYRING_ERROR_NOCHANNEL,
+				                    "Failed to communicate with Secret Service (account : %s).",
+				                    purple_account_get_username(account));
+				if (cb != NULL)
+					cb(account, error, storage->user_data);
+				g_error_free(error);
+				break;
+
+			default:
+				purple_debug_info("keyring-libsecret",
+				                  "Unknown error (account : %s (%s)).\n",
+				                  purple_account_get_username(account),
+				                  purple_account_get_protocol_id(account));
+				error = g_error_new(ERR_SECRETSERVICEPLUGIN,
+				                    PURPLE_KEYRING_ERROR_NOCHANNEL,
+				                    "Unknown error (account : %s).",
+				                    purple_account_get_username(account));
+				if (cb != NULL)
+					cb(account, error, storage->user_data);
+				g_error_free(error);
+				break;
+		}
+
+	} else {
+		purple_debug_info("keyring-libsecret", "Password for %s updated.\n",
+			purple_account_get_username(account));
+
+		if (cb != NULL)
+			cb(account, NULL, storage->user_data);
+	}
+
+	g_free(storage);
+}
+
+static void
+ss_save(PurpleAccount *account,
+         const gchar *password,
+         PurpleKeyringSaveCallback cb,
+         gpointer data)
+{
+	InfoStorage *storage = g_new0(InfoStorage, 1);
+
+	storage->account = account;
+	storage->cb = cb;
+	storage->user_data = data;
+
+	if (password != NULL && *password != '\0') {
+		const char *username = purple_account_get_username(account);
+		char *label;
+
+		purple_debug_info("keyring-libsecret",
+			"Updating password for account %s (%s).\n",
+			username, purple_account_get_protocol_id(account));
+
+		label = g_strdup_printf(_("Pidgin IM password for account %s"), username);
+		secret_password_store(&purple_schema, SECRET_COLLECTION_DEFAULT,
+		                      label, password,
+		                      NULL, ss_save_continue, storage,
+		                      "user", username,
+		                      "protocol", purple_account_get_protocol_id(account),
+		                      NULL);
+		g_free(label);
+
+	} else {	/* password == NULL, delete password. */
+		purple_debug_info("keyring-libsecret",
+			"Forgetting password for account %s (%s).\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+
+		secret_password_clear(&purple_schema, NULL, ss_save_continue, storage,
+		                      "user", purple_account_get_username(account),
+		                      "protocol", purple_account_get_protocol_id(account),
+		                      NULL);
+	}
+}
+
+static void
+ss_close(GError **error)
+{
+}
+
+static gboolean
+ss_import_password(PurpleAccount *account,
+                    const char *mode,
+                    const char *data,
+                    GError **error)
+{
+	purple_debug_info("keyring-libsecret", "Importing password.\n");
+	return TRUE;
+}
+
+static gboolean
+ss_export_password(PurpleAccount *account,
+                    const char **mode,
+                    char **data,
+                    GError **error,
+                    GDestroyNotify *destroy)
+{
+	purple_debug_info("keyring-libsecret", "Exporting password.\n");
+	*data = NULL;
+	*mode = NULL;
+	*destroy = NULL;
+
+	return TRUE;
+}
+
+static gboolean
+ss_init(void)
+{
+	purple_debug_info("keyring-libsecret", "Init.\n");
+
+	keyring_handler = purple_keyring_new();
+
+	purple_keyring_set_name(keyring_handler, SECRETSERVICE_NAME);
+	purple_keyring_set_id(keyring_handler, SECRETSERVICE_ID);
+	purple_keyring_set_read_password(keyring_handler, ss_read);
+	purple_keyring_set_save_password(keyring_handler, ss_save);
+	purple_keyring_set_close_keyring(keyring_handler, ss_close);
+	purple_keyring_set_import_password(keyring_handler, ss_import_password);
+	purple_keyring_set_export_password(keyring_handler, ss_export_password);
+
+	purple_keyring_register(keyring_handler);
+
+	return TRUE;
+}
+
+static void
+ss_uninit(void)
+{
+	purple_debug_info("keyring-libsecret", "Uninit.\n");
+	ss_close(NULL);
+	purple_keyring_unregister(keyring_handler);
+	purple_keyring_free(keyring_handler);
+	keyring_handler = NULL;
+}
+
+/***********************************************/
+/*     Plugin interface                        */
+/***********************************************/
+
+static gboolean
+ss_load(PurplePlugin *plugin)
+{
+	return ss_init();
+}
+
+static gboolean
+ss_unload(PurplePlugin *plugin)
+{
+	if (purple_keyring_get_inuse() == keyring_handler)
+		return FALSE;
+
+	ss_uninit();
+
+	return TRUE;
+}
+
+PurplePluginInfo plugininfo =
+{
+	PURPLE_PLUGIN_MAGIC,		/* magic */
+	PURPLE_MAJOR_VERSION,		/* major_version */
+	PURPLE_MINOR_VERSION,		/* minor_version */
+	PURPLE_PLUGIN_STANDARD,		/* type */
+	NULL,						/* ui_requirement */
+	PURPLE_PLUGIN_FLAG_INVISIBLE|PURPLE_PLUGIN_FLAG_AUTOLOAD,	/* flags */
+	NULL,						/* dependencies */
+	PURPLE_PRIORITY_DEFAULT,	/* priority */
+	SECRETSERVICE_ID,			/* id */
+	SECRETSERVICE_NAME,			/* name */
+	DISPLAY_VERSION,			/* version */
+	"Secret Service Plugin",		/* summary */
+	N_("This plugin will store passwords in Secret Service."),	/* description */
+	"Elliott Sales de Andrade (qulogic[at]pidgin.im)",		/* author */
+	PURPLE_WEBSITE,				/* homepage */
+	ss_load,					/* load */
+	ss_unload,					/* unload */
+	NULL,						/* destroy */
+	NULL,						/* ui_info */
+	NULL,						/* extra_info */
+	NULL,						/* prefs_info */
+	NULL,						/* actions */
+	NULL,						/* padding... */
+	NULL,
+	NULL,
+	NULL,
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+	purple_debug_info("keyring-libsecret", "Init plugin called.\n");
+}
+
+PURPLE_INIT_PLUGIN(secret_service, init_plugin, plugininfo)
+

mercurial