summaryrefslogtreecommitdiff
path: root/src/mcd-storage.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mcd-storage.c')
-rw-r--r--src/mcd-storage.c1773
1 files changed, 1017 insertions, 756 deletions
diff --git a/src/mcd-storage.c b/src/mcd-storage.c
index 1a0ebbf5..fea76c2a 100644
--- a/src/mcd-storage.c
+++ b/src/mcd-storage.c
@@ -32,6 +32,7 @@
#include <errno.h>
#include <string.h>
+#include <dbus/dbus.h>
#include <telepathy-glib/telepathy-glib.h>
#include <telepathy-glib/telepathy-glib-dbus.h>
@@ -40,13 +41,6 @@
/* these pseudo-plugins take care of the actual account storage/retrieval */
#include "mcd-account-manager-default.h"
-#if ENABLE_LIBACCOUNTS_SSO
-#include "mcd-account-manager-sso.h"
-# ifdef ACCOUNTS_GLIB_HIDDEN_SERVICE_TYPE
-# include "mcd-storage-ag-hidden.h"
-# endif
-#endif
-
#define MAX_KEY_LENGTH (DBUS_MAXIMUM_NAME_LENGTH + 6)
static GList *stores = NULL;
@@ -56,6 +50,17 @@ enum {
PROP_DBUS_DAEMON = 1,
};
+enum {
+ SIGNAL_CREATED,
+ SIGNAL_TOGGLED,
+ SIGNAL_DELETED,
+ SIGNAL_ALTERED_ONE,
+ SIGNAL_RECONNECT,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
struct _McdStorageClass {
GObjectClass parent;
};
@@ -67,39 +72,11 @@ G_DEFINE_TYPE_WITH_CODE (McdStorage, mcd_storage,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_MANAGER, plugin_iface_init))
-typedef struct {
- /* owned string => GVariant
- * e.g. { 'DisplayName': <'Frederick Bloggs'> } */
- GHashTable *attributes;
- /* owned string => owned GVariant
- * e.g. { 'account': <'fred@example.com'>, 'password': <'foo'> } */
- GHashTable *parameters;
- /* owned string => owned string escaped as if for a keyfile
- * e.g. { 'account': 'fred@example.com', 'password': 'foo' }
- * keys of @parameters and @escaped_parameters are disjoint */
- GHashTable *escaped_parameters;
- /* set of owned strings
- * e.g. { 'password': 'password' } */
- GHashTable *secrets;
-} McdStorageAccount;
-
-static void
-mcd_storage_account_free (gpointer p)
-{
- McdStorageAccount *sa = p;
-
- g_hash_table_unref (sa->attributes);
- g_hash_table_unref (sa->parameters);
- g_hash_table_unref (sa->escaped_parameters);
- g_hash_table_unref (sa->secrets);
- g_slice_free (McdStorageAccount, sa);
-}
-
static void
mcd_storage_init (McdStorage *self)
{
self->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, mcd_storage_account_free);
+ g_free, g_object_unref);
}
static void
@@ -181,6 +158,31 @@ mcd_storage_class_init (McdStorageClass *cls)
object_class->finalize = storage_finalize;
g_object_class_install_property (object_class, PROP_DBUS_DAEMON, spec);
+
+ signals[SIGNAL_CREATED] = g_signal_new ("created",
+ MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+ NULL, G_TYPE_NONE,
+ 2, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING);
+
+ signals[SIGNAL_ALTERED_ONE] = g_signal_new ("altered-one",
+ MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+ NULL, G_TYPE_NONE,
+ 3, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING, G_TYPE_STRING);
+
+ signals[SIGNAL_DELETED] = g_signal_new ("deleted",
+ MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+ NULL, G_TYPE_NONE,
+ 2, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING);
+
+ signals[SIGNAL_TOGGLED] = g_signal_new ("toggled",
+ MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+ NULL, G_TYPE_NONE,
+ 3, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING, G_TYPE_BOOLEAN);
+
+ signals[SIGNAL_RECONNECT] = g_signal_new ("reconnect",
+ MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+ NULL, G_TYPE_NONE,
+ 2, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING);
}
McdStorage *
@@ -194,106 +196,18 @@ mcd_storage_new (TpDBusDaemon *dbus_daemon)
static gchar *
mcd_keyfile_escape_variant (GVariant *variant)
{
- GValue value = G_VALUE_INIT;
+ GKeyFile *keyfile;
gchar *ret;
- dbus_g_value_parse_g_variant (variant, &value);
-
- if (G_IS_VALUE (&value))
- {
- ret = mcd_keyfile_escape_value (&value);
- g_value_unset (&value);
- }
- else
- {
- gchar *printed = g_variant_print (variant, TRUE);
-
- ret = NULL;
- g_warning ("Unable to translate variant %s", printed);
- g_free (printed);
- }
+ g_return_val_if_fail (variant != NULL, NULL);
+ keyfile = g_key_file_new ();
+ mcd_keyfile_set_variant (keyfile, "g", "k", variant);
+ ret = g_key_file_get_value (keyfile, "g", "k", NULL);
+ g_key_file_free (keyfile);
return ret;
}
-static McdStorageAccount *
-lookup_account (McdStorage *self,
- const gchar *account)
-{
- return g_hash_table_lookup (self->accounts, account);
-}
-
-static McdStorageAccount *
-ensure_account (McdStorage *self,
- const gchar *account)
-{
- McdStorageAccount *sa = lookup_account (self, account);
-
- if (sa == NULL)
- {
- sa = g_slice_new (McdStorageAccount);
- sa->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, (GDestroyNotify) g_variant_unref);
- sa->parameters = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, (GDestroyNotify) g_variant_unref);
- sa->escaped_parameters = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, g_free);
- sa->secrets = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, NULL);
- g_hash_table_insert (self->accounts, g_strdup (account), sa);
- }
-
- return sa;
-}
-
-static gchar *
-get_value (const McpAccountManager *ma,
- const gchar *account,
- const gchar *key)
-{
- McdStorage *self = MCD_STORAGE (ma);
- McdStorageAccount *sa = lookup_account (self, account);
- GVariant *variant;
- gchar *ret;
-
- if (sa == NULL)
- return NULL;
-
- if (g_str_has_prefix (key, "param-"))
- {
- variant = g_hash_table_lookup (sa->parameters, key + 6);
-
- if (variant != NULL)
- {
- ret = mcd_keyfile_escape_variant (variant);
- g_variant_unref (variant);
- return ret;
- }
- else
- {
- /* OK, we don't have it as a variant. How about the keyfile-escaped
- * version? */
- return g_strdup (g_hash_table_lookup (sa->escaped_parameters,
- key + 6));
- }
- }
- else
- {
- variant = g_hash_table_lookup (sa->attributes, key);
-
- if (variant != NULL)
- {
- ret = mcd_keyfile_escape_variant (variant);
- g_variant_unref (variant);
- return ret;
- }
- else
- {
- return NULL;
- }
- }
-}
-
static struct {
const gchar *type;
const gchar *name;
@@ -314,7 +228,6 @@ static struct {
{ "b", MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY },
{ "b", MC_ACCOUNTS_KEY_ENABLED },
{ "b", MC_ACCOUNTS_KEY_HAS_BEEN_ONLINE },
- { "b", MC_ACCOUNTS_KEY_HIDDEN },
/* Strings */
{ "s", MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE },
@@ -335,7 +248,7 @@ static struct {
{ NULL, NULL }
};
-const gchar *
+const GVariantType *
mcd_storage_get_attribute_type (const gchar *attribute)
{
guint i;
@@ -343,26 +256,26 @@ mcd_storage_get_attribute_type (const gchar *attribute)
for (i = 0; known_attributes[i].type != NULL; i++)
{
if (!tp_strdiff (attribute, known_attributes[i].name))
- return known_attributes[i].type;
+ return G_VARIANT_TYPE (known_attributes[i].type);
}
- /* special case for mcd-account-conditions.c */
- if (g_str_has_prefix (attribute, "condition-"))
- return "s";
-
return NULL;
}
gboolean
mcd_storage_init_value_for_attribute (GValue *value,
- const gchar *attribute)
+ const gchar *attribute,
+ const GVariantType **variant_type)
{
- const gchar *s = mcd_storage_get_attribute_type (attribute);
+ const GVariantType *s = mcd_storage_get_attribute_type (attribute);
if (s == NULL)
return FALSE;
- switch (s[0])
+ if (variant_type != NULL)
+ *variant_type = s;
+
+ switch (g_variant_type_peek_string (s)[0])
{
case 's':
g_value_init (value, G_TYPE_STRING);
@@ -379,7 +292,7 @@ mcd_storage_init_value_for_attribute (GValue *value,
case 'a':
{
- switch (s[1])
+ switch (g_variant_type_peek_string (s)[1])
{
case 'o':
g_value_init (value, TP_ARRAY_TYPE_OBJECT_PATH_LIST);
@@ -394,7 +307,7 @@ mcd_storage_init_value_for_attribute (GValue *value,
case '(':
{
- if (!tp_strdiff (s, "(uss)"))
+ if (g_variant_type_equal (s, G_VARIANT_TYPE ("(uss)")))
{
g_value_init (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE);
return TRUE;
@@ -406,230 +319,134 @@ mcd_storage_init_value_for_attribute (GValue *value,
return FALSE;
}
-static gboolean
-mcpa_init_value_for_attribute (const McpAccountManager *mcpa,
- GValue *value,
- const gchar *attribute)
-{
- return mcd_storage_init_value_for_attribute (value, attribute);
-}
-
-static void
-mcpa_set_attribute (const McpAccountManager *ma,
- const gchar *account,
- const gchar *attribute,
- GVariant *value,
- McpAttributeFlags flags)
+static gchar *
+unique_name (const McpAccountManager *ma,
+ const gchar *manager,
+ const gchar *protocol,
+ const gchar *identification)
{
McdStorage *self = MCD_STORAGE (ma);
- McdStorageAccount *sa = ensure_account (self, account);
+ gchar *esc_manager, *esc_protocol, *esc_base;
+ guint i;
+ gsize base_len = strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
+ DBusGConnection *connection = tp_proxy_get_dbus_connection (self->dbusd);
- if (value != NULL)
- {
- g_hash_table_insert (sa->attributes, g_strdup (attribute),
- g_variant_ref_sink (value));
- }
- else
- {
- g_hash_table_remove (sa->attributes, attribute);
- }
-}
+ esc_manager = tp_escape_as_identifier (manager);
+ esc_protocol = g_strdelimit (g_strdup (protocol), "-", '_');
+ esc_base = tp_escape_as_identifier (identification);
-static void
-mcpa_set_parameter (const McpAccountManager *ma,
- const gchar *account,
- const gchar *parameter,
- GVariant *value,
- McpParameterFlags flags)
-{
- McdStorage *self = MCD_STORAGE (ma);
- McdStorageAccount *sa = ensure_account (self, account);
+ for (i = 0; i < G_MAXUINT; i++)
+ {
+ gchar *path = g_strdup_printf (
+ TP_ACCOUNT_OBJECT_PATH_BASE "%s/%s/%s%u",
+ esc_manager, esc_protocol, esc_base, i);
- g_hash_table_remove (sa->parameters, parameter);
- g_hash_table_remove (sa->escaped_parameters, parameter);
+ if (!g_hash_table_contains (self->accounts, path + base_len) &&
+ dbus_g_connection_lookup_g_object (connection, path) == NULL)
+ {
+ gchar *ret = g_strdup (path + base_len);
- if (value != NULL)
- g_hash_table_insert (sa->parameters, g_strdup (parameter),
- g_variant_ref_sink (value));
+ g_free (path);
+ return ret;
+ }
- if (flags & MCP_PARAMETER_FLAG_SECRET)
- {
- DEBUG ("flagging %s parameter %s as secret", account, parameter);
- g_hash_table_add (sa->secrets, g_strdup (parameter));
+ g_free (path);
}
+
+ return NULL;
}
static void
-set_value (const McpAccountManager *ma,
- const gchar *account,
- const gchar *key,
- const gchar *value)
+identify_account_cb (TpProxy *proxy,
+ const gchar *identification,
+ const GError *error,
+ gpointer task,
+ GObject *weak_object G_GNUC_UNUSED)
{
- McdStorage *self = MCD_STORAGE (ma);
- McdStorageAccount *sa = ensure_account (self, account);
-
- if (g_str_has_prefix (key, "param-"))
+ if (error == NULL)
{
- g_hash_table_remove (sa->parameters, key + 6);
- g_hash_table_remove (sa->escaped_parameters, key + 6);
-
- if (value != NULL)
- g_hash_table_insert (sa->escaped_parameters, g_strdup (key + 6),
- g_strdup (value));
+ DEBUG ("identified account: %s", identification);
+ g_task_return_pointer (task, g_strdup (identification), g_free);
}
- else
+ else if (g_error_matches (error, TP_ERROR, TP_ERROR_INVALID_HANDLE) ||
+ g_error_matches (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT))
{
- if (value != NULL)
- {
- GValue tmp = G_VALUE_INIT;
- GError *error = NULL;
-
- if (!mcd_storage_init_value_for_attribute (&tmp, key))
- {
- g_warning ("Not sure what the type of '%s' is, assuming string",
- key);
- g_value_init (&tmp, G_TYPE_STRING);
- }
-
- if (mcd_keyfile_unescape_value (value, &tmp, &error))
- {
- g_hash_table_insert (sa->attributes, g_strdup (key),
- g_variant_ref_sink (dbus_g_value_build_g_variant (&tmp)));
- g_value_unset (&tmp);
- }
- else
- {
- g_warning ("Could not decode attribute '%s':'%s' from plugin: %s",
- key, value, error->message);
- g_error_free (error);
- g_hash_table_remove (sa->attributes, key);
- }
- }
- else
- {
- g_hash_table_remove (sa->attributes, key);
- }
+ /* The connection manager didn't like our account parameters.
+ * Give up now. */
+ DEBUG ("failed to identify account: %s #%d: %s",
+ g_quark_to_string (error->domain), error->code, error->message);
+ g_task_return_error (task, g_error_copy (error));
}
-}
-
-static GStrv
-list_keys (const McpAccountManager *ma,
- const gchar * account)
-{
- McdStorage *self = MCD_STORAGE (ma);
- GPtrArray *ret = g_ptr_array_new ();
- McdStorageAccount *sa = lookup_account (self, account);
-
- if (sa != NULL)
+ else
{
- GHashTableIter iter;
- gpointer k;
-
- g_hash_table_iter_init (&iter, sa->attributes);
-
- while (g_hash_table_iter_next (&iter, &k, NULL))
- g_ptr_array_add (ret, g_strdup (k));
-
- g_hash_table_iter_init (&iter, sa->parameters);
-
- while (g_hash_table_iter_next (&iter, &k, NULL))
- g_ptr_array_add (ret, g_strdup_printf ("param-%s", (gchar *) k));
+ /* We weren't able to identify the account, but carry on and hope
+ * for the best... */
+ DEBUG ("ignoring failure to identify account: %s #%d: %s",
+ g_quark_to_string (error->domain), error->code, error->message);
+ g_task_return_pointer (task, g_strdup (g_task_get_task_data (task)),
+ g_free);
}
-
- g_ptr_array_add (ret, NULL);
- return (GStrv) g_ptr_array_free (ret, FALSE);
-}
-
-static gboolean
-is_secret (const McpAccountManager *ma,
- const gchar *account,
- const gchar *key)
-{
- McdStorage *self = MCD_STORAGE (ma);
- McdStorageAccount *sa = lookup_account (self, account);
-
- if (sa == NULL || !g_str_has_prefix (key, "param-"))
- return FALSE;
-
- return g_hash_table_contains (sa->secrets, key + 6);
}
-static void
-mcd_storage_make_secret (McdStorage *self,
- const gchar *account,
- const gchar *key)
+static gchar *
+identify_account_finish (McpAccountManager *mcpa,
+ GAsyncResult *result,
+ GError **error)
{
- McdStorageAccount *sa;
+ g_return_val_if_fail (g_task_is_valid (result, mcpa), NULL);
- g_return_if_fail (MCD_IS_STORAGE (self));
- g_return_if_fail (account != NULL);
- g_return_if_fail (key != NULL);
-
- if (!g_str_has_prefix (key, "param-"))
- return;
-
- DEBUG ("flagging %s parameter %s as secret", account, key + 6);
- sa = ensure_account (self, account);
- g_hash_table_add (sa->secrets, g_strdup (key + 6));
+ return g_task_propagate_pointer (G_TASK (result), error);
}
static void
-make_secret (const McpAccountManager *ma,
- const gchar *account,
- const gchar *key)
-{
- mcd_storage_make_secret (MCD_STORAGE (ma), account, key);
-}
-
-static gchar *
-unique_name (const McpAccountManager *ma,
+identify_account_async (McpAccountManager *mcpa,
const gchar *manager,
- const gchar *protocol,
- const GHashTable *params)
+ const gchar *protocol_name,
+ GVariant *parameters,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- McdStorage *self = MCD_STORAGE (ma);
- const gchar *base = NULL;
- gchar *esc_manager, *esc_protocol, *esc_base;
- guint i;
- gsize base_len = strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
- DBusGConnection *connection = tp_proxy_get_dbus_connection (self->dbusd);
+ McdStorage *self = MCD_STORAGE (mcpa);
+ GError *error = NULL;
+ TpProtocol *protocol;
+ GTask *task;
+ GValue value = G_VALUE_INIT;
+ const gchar *base;
- base = tp_asv_get_string (params, "account");
+ task = g_task_new (self, cancellable, callback, user_data);
- if (base == NULL)
+ /* in case IdentifyAccount fails and we need to make something up */
+ if (!g_variant_lookup (parameters, "account", "&s", &base))
base = "account";
- esc_manager = tp_escape_as_identifier (manager);
- esc_protocol = g_strdelimit (g_strdup (protocol), "-", '_');
- esc_base = tp_escape_as_identifier (base);
-
- for (i = 0; i < G_MAXUINT; i++)
- {
- gchar *path = g_strdup_printf (
- TP_ACCOUNT_OBJECT_PATH_BASE "%s/%s/%s%u",
- esc_manager, esc_protocol, esc_base, i);
+ g_task_set_task_data (task, g_strdup (base), g_free);
- if (!g_hash_table_contains (self->accounts, path + base_len) &&
- dbus_g_connection_lookup_g_object (connection, path) == NULL)
- {
- gchar *ret = g_strdup (path + base_len);
+ protocol = tp_protocol_new (self->dbusd, manager, protocol_name,
+ NULL, &error);
- g_free (path);
- return ret;
- }
-
- g_free (path);
+ if (protocol == NULL)
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
- return NULL;
+ dbus_g_value_parse_g_variant (parameters, &value);
+
+ tp_cli_protocol_call_identify_account (protocol, -1,
+ g_value_get_boxed (&value), identify_account_cb, task, g_object_unref,
+ NULL);
+ g_object_unref (protocol);
+ g_value_unset (&value);
}
/* sort in descending order of priority (ie higher prio => earlier in list) */
static gint
account_storage_cmp (gconstpointer a, gconstpointer b)
{
- gint pa = mcp_account_storage_priority (a);
- gint pb = mcp_account_storage_priority (b);
+ gint pa = mcp_account_storage_priority ((McpAccountStorage *) a);
+ gint pb = mcp_account_storage_priority ((McpAccountStorage *) b);
if (pa > pb) return -1;
if (pa < pb) return 1;
@@ -644,17 +461,6 @@ add_storage_plugin (McpAccountStorage *plugin)
}
static void
-add_libaccounts_plugins_if_enabled (void)
-{
-#if ENABLE_LIBACCOUNTS_SSO
- add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_account_manager_sso_new ()));
-# ifdef ACCOUNTS_GLIB_HIDDEN_SERVICE_TYPE
- add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_storage_ag_hidden_new ()));
-# endif
-#endif
-}
-
-static void
sort_and_cache_plugins ()
{
const GList *p;
@@ -668,7 +474,6 @@ sort_and_cache_plugins ()
/* Add compiled-in plugins */
add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_account_manager_default_new ()));
- add_libaccounts_plugins_if_enabled ();
for (p = mcp_list_objects(); p != NULL; p = g_list_next (p))
{
@@ -694,23 +499,125 @@ sort_and_cache_plugins ()
plugins_cached = TRUE;
}
-void
-mcd_storage_connect_signal (const gchar *signame,
- GCallback func,
- gpointer user_data)
+static void
+created_cb (McpAccountStorage *plugin,
+ const gchar *account_name,
+ McdStorage *self)
{
- GList *p;
+ GError *error = NULL;
- for (p = stores; p != NULL; p = g_list_next (p))
+ g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin));
+ g_return_if_fail (MCD_IS_STORAGE (self));
+
+ if (mcd_storage_add_account_from_plugin (self, plugin, account_name, &error))
{
- McpAccountStorage *plugin = p->data;
+ DEBUG ("%s", account_name);
+ g_signal_emit (self, signals[SIGNAL_CREATED], 0, plugin, account_name);
+ }
+ else
+ {
+ WARNING ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+check_is_responsible (McdStorage *self,
+ McpAccountStorage *plugin,
+ const gchar *account_name,
+ const gchar *changing,
+ GError **error)
+{
+ McpAccountStorage *other = g_hash_table_lookup (self->accounts,
+ account_name);
+
+ if (other == NULL)
+ {
+ WARNING ("account %s does not exist, preventing plugin '%s' from "
+ "%s it", account_name, mcp_account_storage_name (plugin), changing);
+ return FALSE;
+ }
+
+ if (other != plugin)
+ {
+ WARNING ("account %s is in plugin '%s', preventing plugin '%s' from "
+ "%s it", account_name, mcp_account_storage_name (other),
+ mcp_account_storage_name (plugin), changing);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+toggled_cb (McpAccountStorage *plugin,
+ const gchar *account_name,
+ gboolean on,
+ McdStorage *self)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin));
+ g_return_if_fail (MCD_IS_STORAGE (self));
- DEBUG ("connecting handler to %s plugin signal %s ",
- mcp_account_storage_name (plugin), signame);
- g_signal_connect (plugin, signame, func, user_data);
+ if (check_is_responsible (self, plugin, account_name, "toggling", &error))
+ g_signal_emit (self, signals[SIGNAL_TOGGLED], 0, plugin,
+ account_name, on);
+}
+
+static void
+deleted_cb (McpAccountStorage *plugin,
+ const gchar *account_name,
+ McdStorage *self)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin));
+ g_return_if_fail (MCD_IS_STORAGE (self));
+
+ if (check_is_responsible (self, plugin, account_name, "deleting",
+ &error))
+ {
+ g_hash_table_remove (self->accounts, account_name);
+
+ g_signal_emit (self, signals[SIGNAL_DELETED], 0, plugin,
+ account_name);
}
}
+static void
+altered_one_cb (McpAccountStorage *plugin,
+ const gchar *account_name,
+ const gchar *key,
+ McdStorage *self)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin));
+ g_return_if_fail (MCD_IS_STORAGE (self));
+
+ if (check_is_responsible (self, plugin, account_name, "altering",
+ &error))
+ g_signal_emit (self, signals[SIGNAL_ALTERED_ONE], 0, plugin,
+ account_name, key);
+}
+
+static void
+reconnect_cb (McpAccountStorage *plugin,
+ const gchar *account_name,
+ McdStorage *self)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin));
+ g_return_if_fail (MCD_IS_STORAGE (self));
+
+ if (check_is_responsible (self, plugin, account_name, "reconnecting",
+ &error))
+ g_signal_emit (self, signals[SIGNAL_RECONNECT], 0, plugin,
+ account_name);
+}
+
/*
* mcd_storage_load:
* @storage: An object implementing the #McdStorage interface
@@ -729,95 +636,68 @@ mcd_storage_load (McdStorage *self)
sort_and_cache_plugins ();
- store = g_list_last (stores);
-
- /* fetch accounts stored in plugins, in reverse priority so higher prio *
- * plugins can overwrite lower prio ones' account data */
- while (store != NULL)
+ /* fetch accounts stored in plugins, highest priority first, so that
+ * low priority plugins can be overidden by high priority */
+ for (store = stores; store != NULL; store = store->next)
{
GList *account;
McpAccountStorage *plugin = store->data;
- GList *stored = mcp_account_storage_list (plugin, ma);
+ GList *stored;
const gchar *pname = mcp_account_storage_name (plugin);
const gint prio = mcp_account_storage_priority (plugin);
- DEBUG ("listing from plugin %s [prio: %d]", pname, prio);
+ DEBUG ("listing initial accounts from plugin %s [prio: %d]", pname, prio);
+ stored = mcp_account_storage_list (plugin, ma);
+
+ /* Connect to signals for non-initial accounts. We only do this
+ * after we have called list(), to make sure the plugins don't need
+ * to queue up change-notification signals until after we've
+ * called the old "ready" vfunc. */
+ g_signal_connect_object (plugin, "created", G_CALLBACK (created_cb),
+ self, 0);
+ g_signal_connect_object (plugin, "toggled", G_CALLBACK (toggled_cb),
+ self, 0);
+ g_signal_connect_object (plugin, "deleted", G_CALLBACK (deleted_cb),
+ self, 0);
+ g_signal_connect_object (plugin, "altered-one",
+ G_CALLBACK (altered_one_cb), self, 0);
+ g_signal_connect_object (plugin, "reconnect", G_CALLBACK (reconnect_cb),
+ self, 0);
+
for (account = stored; account != NULL; account = g_list_next (account))
{
+ GError *error = NULL;
gchar *name = account->data;
DEBUG ("fetching %s from plugin %s [prio: %d]", name, pname, prio);
- mcd_storage_add_account_from_plugin (self, plugin, name);
+
+ if (!mcd_storage_add_account_from_plugin (self, plugin, name,
+ &error))
+ {
+ DEBUG ("%s", error->message);
+ g_clear_error (&error);
+ }
+
g_free (name);
}
/* already freed the contents, just need to free the list itself */
g_list_free (stored);
- store = g_list_previous (store);
}
}
/*
- * mcd_storage_dup_accounts:
+ * mcd_storage_get_accounts:
* @storage: An object implementing the #McdStorage interface
* @n: place for the number of accounts to be written to (or %NULL)
*
- * Returns: a newly allocated GStrv containing the unique account names,
- * which must be freed by the caller with g_strfreev().
+ * Returns: (transfer none) (element-type utf8 Mcp.AccountStorage): a
+ * map from account object path tail to plugin
*/
-GStrv
-mcd_storage_dup_accounts (McdStorage *self,
- gsize *n)
+GHashTable *
+mcd_storage_get_accounts (McdStorage *self)
{
- GPtrArray *ret = g_ptr_array_new ();
- GHashTableIter iter;
- gpointer k, v;
-
- g_hash_table_iter_init (&iter, self->accounts);
-
- while (g_hash_table_iter_next (&iter, &k, &v))
- {
- McdStorageAccount *sa = v;
-
- if (g_hash_table_size (sa->attributes) > 0)
- g_ptr_array_add (ret, g_strdup (k));
- }
-
- g_ptr_array_add (ret, NULL);
- return (GStrv) g_ptr_array_free (ret, FALSE);
-}
-
-/*
- * mcd_storage_dup_attributes:
- * @storage: An object implementing the #McdStorage interface
- * @account: unique name of the account
- * @n: place for the number of attributes to be written to (or %NULL)
- *
- * Returns: a newly allocated GStrv containing the names of all the
- * attributes or parameters currently stored for @account. Must be
- * freed by the caller with g_strfreev().
- */
-GStrv
-mcd_storage_dup_attributes (McdStorage *self,
- const gchar *account,
- gsize *n)
-{
- GPtrArray *ret = g_ptr_array_new ();
- McdStorageAccount *sa = lookup_account (self, account);
-
- if (sa != NULL)
- {
- GHashTableIter iter;
- gpointer k;
-
- g_hash_table_iter_init (&iter, sa->attributes);
-
- while (g_hash_table_iter_next (&iter, &k, NULL))
- g_ptr_array_add (ret, g_strdup (k));
- }
-
- g_ptr_array_add (ret, NULL);
- return (GStrv) g_ptr_array_free (ret, FALSE);
+ return self->accounts;
}
/*
@@ -837,22 +717,15 @@ McpAccountStorage *
mcd_storage_get_plugin (McdStorage *self,
const gchar *account)
{
- GList *store = stores;
- McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
- McpAccountStorage *owner = NULL;
+ McpAccountStorage *plugin;
g_return_val_if_fail (MCD_IS_STORAGE (self), NULL);
g_return_val_if_fail (account != NULL, NULL);
- for (; store != NULL && owner == NULL; store = g_list_next (store))
- {
- McpAccountStorage *plugin = store->data;
-
- if (mcp_account_storage_owns (plugin, ma, account))
- owner = plugin;
- }
+ plugin = g_hash_table_lookup (self->accounts, account);
+ g_return_val_if_fail (plugin != NULL, NULL);
- return owner;
+ return plugin;
}
/*
@@ -879,7 +752,8 @@ mcd_storage_dup_string (McdStorage *self,
g_value_init (&tmp, G_TYPE_STRING);
- if (!mcd_storage_get_attribute (self, account, attribute, &tmp, NULL))
+ if (!mcd_storage_get_attribute (self, account, attribute,
+ G_VARIANT_TYPE_STRING, &tmp, NULL))
return NULL;
ret = g_value_dup_string (&tmp);
@@ -907,10 +781,10 @@ mcd_storage_coerce_variant_to_value (GVariant *variant,
/* This is really pretty stupid but it'll do for now.
* FIXME: implement a better similar-type-coercion mechanism than
* round-tripping through a GKeyFile. */
- escaped = mcd_keyfile_escape_value (&tmp);
+ g_value_unset (&tmp);
+ escaped = mcd_keyfile_escape_variant (variant);
ret = mcd_keyfile_unescape_value (escaped, value, error);
g_free (escaped);
- g_value_unset (&tmp);
return ret;
}
@@ -926,36 +800,42 @@ gboolean
mcd_storage_get_attribute (McdStorage *self,
const gchar *account,
const gchar *attribute,
+ const GVariantType *type,
GValue *value,
GError **error)
{
- McdStorageAccount *sa;
+ McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
+ McpAccountStorage *plugin;
GVariant *variant;
+ gboolean ret;
g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
g_return_val_if_fail (account != NULL, FALSE);
g_return_val_if_fail (attribute != NULL, FALSE);
g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), FALSE);
- sa = lookup_account (self, account);
+ plugin = g_hash_table_lookup (self->accounts, account);
- if (sa == NULL)
+ if (plugin == NULL)
{
g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
"Account %s does not exist", account);
return FALSE;
}
- variant = g_hash_table_lookup (sa->attributes, attribute);
+ variant = mcp_account_storage_get_attribute (plugin, ma, account,
+ attribute, type, NULL);
if (variant == NULL)
{
g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "Setting '%s' not stored by account %s", attribute, account);
+ "Account %s has no attribute '%s'", account, attribute);
return FALSE;
}
- return mcd_storage_coerce_variant_to_value (variant, value, error);
+ ret = mcd_storage_coerce_variant_to_value (variant, value, error);
+ g_variant_unref (variant);
+ return ret;
}
/*
@@ -970,52 +850,42 @@ gboolean
mcd_storage_get_parameter (McdStorage *self,
const gchar *account,
const gchar *parameter,
+ const GVariantType *type,
GValue *value,
GError **error)
{
- McdStorageAccount *sa;
- const gchar *escaped;
+ McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
+ McpAccountStorage *plugin;
GVariant *variant;
+ gboolean ret;
g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
g_return_val_if_fail (account != NULL, FALSE);
g_return_val_if_fail (parameter != NULL, FALSE);
+ g_return_val_if_fail (!g_str_has_prefix (parameter, "param-"), FALSE);
- sa = lookup_account (self, account);
+ plugin = g_hash_table_lookup (self->accounts, account);
- if (sa == NULL)
+ if (plugin == NULL)
{
g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
"Account %s does not exist", account);
return FALSE;
}
- variant = g_hash_table_lookup (sa->parameters, parameter);
+ variant = mcp_account_storage_get_parameter (plugin, ma, account,
+ parameter, type, NULL);
- if (variant != NULL)
- return mcd_storage_coerce_variant_to_value (variant, value, error);
-
- /* OK, we don't have it as a variant. How about the keyfile-escaped
- * version? */
- escaped = g_hash_table_lookup (sa->escaped_parameters, parameter);
-
- if (escaped == NULL)
+ if (variant == NULL)
{
g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "Parameter '%s' not stored by account %s", parameter, account);
+ "Account %s has no parameter '%s'", account, parameter);
return FALSE;
}
- return mcd_keyfile_unescape_value (escaped, value, error);
-}
-
-static gboolean
-mcpa_unescape_value_from_keyfile (const McpAccountManager *unused G_GNUC_UNUSED,
- const gchar *escaped,
- GValue *value,
- GError **error)
-{
- return mcd_keyfile_unescape_value (escaped, value, error);
+ ret = mcd_storage_coerce_variant_to_value (variant, value, error);
+ g_variant_unref (variant);
+ return ret;
}
/*
@@ -1061,8 +931,8 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
GValue *value,
GError **error)
{
- gboolean ret = FALSE;
GType type;
+ GVariant *variant = NULL;
g_return_val_if_fail (keyfile != NULL, FALSE);
g_return_val_if_fail (group != NULL, FALSE);
@@ -1074,20 +944,155 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
switch (type)
{
case G_TYPE_STRING:
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_STRING, error);
+ break;
+
+ case G_TYPE_INT:
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_INT32, error);
+ break;
+
+ case G_TYPE_INT64:
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_INT64, error);
+ break;
+
+ case G_TYPE_UINT:
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_UINT32, error);
+ break;
+
+ case G_TYPE_UCHAR:
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_BYTE, error);
+ break;
+
+ case G_TYPE_UINT64:
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_UINT64, error);
+ break;
+
+ case G_TYPE_BOOLEAN:
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_BOOLEAN, error);
+ break;
+
+ case G_TYPE_DOUBLE:
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_DOUBLE, error);
+ break;
+
+ default:
+ if (type == G_TYPE_STRV)
+ {
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_STRING_ARRAY, error);
+ }
+ else if (type == DBUS_TYPE_G_OBJECT_PATH)
+ {
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_OBJECT_PATH, error);
+ }
+ else if (type == TP_ARRAY_TYPE_OBJECT_PATH_LIST)
+ {
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE_OBJECT_PATH_ARRAY, error);
+ }
+ else if (type == TP_STRUCT_TYPE_SIMPLE_PRESENCE)
+ {
+ variant = mcd_keyfile_get_variant (keyfile, group, key,
+ G_VARIANT_TYPE ("(uss)"), error);
+ }
+ else
+ {
+ gchar *message =
+ g_strdup_printf ("cannot get key %s from group %s: "
+ "unknown type %s",
+ key, group, g_type_name (type));
+
+ g_warning ("%s: %s", G_STRFUNC, message);
+ g_set_error (error, MCD_ACCOUNT_ERROR,
+ MCD_ACCOUNT_ERROR_GET_PARAMETER,
+ "%s", message);
+ g_free (message);
+ }
+ }
+
+ if (variant == NULL)
+ return FALSE;
+
+ g_variant_ref_sink (variant);
+ g_value_unset (value);
+ dbus_g_value_parse_g_variant (variant, value);
+ g_assert (G_VALUE_TYPE (value) == type);
+ g_variant_unref (variant);
+ return TRUE;
+}
+
+/*
+ * mcd_keyfile_get_variant:
+ * @keyfile: A #GKeyFile
+ * @group: name of a group
+ * @key: name of a key
+ * @type: the desired type
+ * @error: a place to store any #GError<!-- -->s that occur
+ *
+ * Returns: a new floating #GVariant
+ */
+GVariant *
+mcd_keyfile_get_variant (GKeyFile *keyfile,
+ const gchar *group,
+ const gchar *key,
+ const GVariantType *type,
+ GError **error)
+{
+ const gchar *type_str = g_variant_type_peek_string (type);
+ GVariant *ret = NULL;
+
+ g_return_val_if_fail (keyfile != NULL, NULL);
+ g_return_val_if_fail (group != NULL, NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (g_variant_type_string_scan (type_str, NULL, NULL),
+ NULL);
+
+ switch (type_str[0])
+ {
+ case G_VARIANT_CLASS_STRING:
{
gchar *v_string = g_key_file_get_string (keyfile, group,
key, error);
if (v_string != NULL)
+ ret = g_variant_new_string (v_string);
+ /* else error is already set */
+ }
+ break;
+
+ case G_VARIANT_CLASS_INT16:
+ {
+ GError *e = NULL;
+ gint v_int = g_key_file_get_integer (keyfile, group,
+ key, &e);
+
+ if (e != NULL)
{
- g_value_take_string (value, v_string);
- ret = TRUE;
+ g_propagate_error (error, e);
+ }
+ else if (v_int < G_MININT16 || v_int > G_MAXINT16)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "integer %d out of range [%d,%d]",
+ v_int, G_MININT16, G_MAXINT16);
+ }
+ else
+ {
+ ret = g_variant_new_int16 (v_int);
}
- /* else error is already set */
}
break;
- case G_TYPE_INT:
+ case G_VARIANT_CLASS_INT32:
{
GError *e = NULL;
gint v_int = g_key_file_get_integer (keyfile, group,
@@ -1099,13 +1104,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
}
else
{
- g_value_set_int (value, v_int);
- ret = TRUE;
+ ret = g_variant_new_int32 (v_int);
}
}
break;
- case G_TYPE_INT64:
+ case G_VARIANT_CLASS_INT64:
{
GError *e = NULL;
gint64 v_int = g_key_file_get_int64 (keyfile, group,
@@ -1117,13 +1121,34 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
}
else
{
- g_value_set_int64 (value, v_int);
- ret = TRUE;
+ ret = g_variant_new_int64 (v_int);
}
}
break;
- case G_TYPE_UINT:
+ case G_VARIANT_CLASS_UINT16:
+ {
+ GError *e = NULL;
+ gint v_int = g_key_file_get_integer (keyfile, group,
+ key, &e);
+
+ if (e != NULL)
+ {
+ g_propagate_error (error, e);
+ }
+ else if (v_int < 0 || (unsigned) v_int > G_MAXUINT16)
+ {
+ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+ "integer %d out of range [0,%d]", v_int, G_MAXUINT16);
+ }
+ else
+ {
+ ret = g_variant_new_uint16 (v_int);
+ }
+ }
+ break;
+
+ case G_VARIANT_CLASS_UINT32:
{
GError *e = NULL;
guint64 v_uint = g_key_file_get_uint64 (keyfile, group,
@@ -1142,13 +1167,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
}
else
{
- g_value_set_uint (value, v_uint);
- ret = TRUE;
+ ret = g_variant_new_uint32 (v_uint);
}
}
break;
- case G_TYPE_UCHAR:
+ case G_VARIANT_CLASS_BYTE:
{
GError *e = NULL;
gint v_int = g_key_file_get_integer (keyfile, group,
@@ -1167,13 +1191,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
}
else
{
- g_value_set_uchar (value, v_int);
- ret = TRUE;
+ ret = g_variant_new_byte (v_int);
}
}
break;
- case G_TYPE_UINT64:
+ case G_VARIANT_CLASS_UINT64:
{
GError *e = NULL;
guint64 v_uint = g_key_file_get_uint64 (keyfile, group,
@@ -1185,13 +1208,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
}
else
{
- g_value_set_uint64 (value, v_uint);
- ret = TRUE;
+ ret = g_variant_new_uint64 (v_uint);
}
}
break;
- case G_TYPE_BOOLEAN:
+ case G_VARIANT_CLASS_BOOLEAN:
{
GError *e = NULL;
gboolean v_bool = g_key_file_get_boolean (keyfile, group,
@@ -1203,13 +1225,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
}
else
{
- g_value_set_boolean (value, v_bool);
- ret = TRUE;
+ ret = g_variant_new_boolean (v_bool);
}
}
break;
- case G_TYPE_DOUBLE:
+ case G_VARIANT_CLASS_DOUBLE:
{
GError *e = NULL;
gdouble v_double = g_key_file_get_double (keyfile, group,
@@ -1221,25 +1242,24 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
}
else
{
- g_value_set_double (value, v_double);
- ret = TRUE;
+ ret = g_variant_new_double (v_double);
}
}
break;
default:
- if (type == G_TYPE_STRV)
+ if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY))
{
gchar **v = g_key_file_get_string_list (keyfile, group,
key, NULL, error);
if (v != NULL)
{
- g_value_take_boxed (value, v);
- ret = TRUE;
+ ret = g_variant_new_strv ((const gchar **) v, -1);
+ g_strfreev (v);
}
}
- else if (type == DBUS_TYPE_G_OBJECT_PATH)
+ else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
{
gchar *v_string = g_key_file_get_string (keyfile, group,
key, error);
@@ -1253,15 +1273,15 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
g_set_error (error, MCD_ACCOUNT_ERROR,
MCD_ACCOUNT_ERROR_GET_PARAMETER,
"Invalid object path %s", v_string);
- g_free (v_string);
}
else
{
- g_value_take_boxed (value, v_string);
- ret = TRUE;
+ ret = g_variant_new_object_path (v_string);
}
+
+ g_free (v_string);
}
- else if (type == TP_ARRAY_TYPE_OBJECT_PATH_LIST)
+ else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH_ARRAY))
{
gchar **v = g_key_file_get_string_list (keyfile, group,
key, NULL, error);
@@ -1269,7 +1289,6 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
if (v != NULL)
{
gchar **iter;
- GPtrArray *arr = g_ptr_array_new ();
for (iter = v; iter != NULL && *iter != NULL; iter++)
{
@@ -1284,21 +1303,11 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
}
}
- for (iter = v; iter != NULL && *iter != NULL; iter++)
- {
- /* transfer ownership from v to arr */
- g_ptr_array_add (arr, *iter);
- }
-
- /* not g_strfreev - the strings' ownership has been
- * transferred */
- g_free (v);
-
- g_value_take_boxed (value, arr);
- ret = TRUE;
+ ret = g_variant_new_objv ((const gchar **) v, -1);
+ g_strfreev (v);
}
}
- else if (type == TP_STRUCT_TYPE_SIMPLE_PRESENCE)
+ else if (g_variant_type_equal (type, G_VARIANT_TYPE ("(uss)")))
{
gchar **v = g_key_file_get_string_list (keyfile, group,
key, NULL, error);
@@ -1328,13 +1337,8 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
else
{
/* a syntactically valid simple presence */
- g_value_take_boxed (value,
- tp_value_array_build (3,
- G_TYPE_UINT, (guint) u,
- G_TYPE_STRING, v[1],
- G_TYPE_STRING, v[2],
- G_TYPE_INVALID));
- ret = TRUE;
+ ret = g_variant_new_parsed ("(%u, %s, %s)",
+ (guint32) u, v[1], v[2]);
}
}
@@ -1344,8 +1348,9 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
{
gchar *message =
g_strdup_printf ("cannot get key %s from group %s: "
- "unknown type %s",
- key, group, g_type_name (type));
+ "unknown type %.*s", key, group,
+ (int) g_variant_type_get_string_length (type),
+ type_str);
g_warning ("%s: %s", G_STRFUNC, message);
g_set_error (error, MCD_ACCOUNT_ERROR,
@@ -1355,6 +1360,7 @@ mcd_keyfile_get_value (GKeyFile *keyfile,
}
}
+ g_assert (ret == NULL || g_variant_is_of_type (ret, type));
return ret;
}
@@ -1380,7 +1386,8 @@ mcd_storage_get_boolean (McdStorage *self,
g_value_init (&tmp, G_TYPE_BOOLEAN);
- if (!mcd_storage_get_attribute (self, account, attribute, &tmp, NULL))
+ if (!mcd_storage_get_attribute (self, account, attribute,
+ G_VARIANT_TYPE_BOOLEAN, &tmp, NULL))
return FALSE;
return g_value_get_boolean (&tmp);
@@ -1408,65 +1415,60 @@ mcd_storage_get_integer (McdStorage *self,
g_value_init (&tmp, G_TYPE_INT);
- if (!mcd_storage_get_attribute (self, account, attribute, &tmp, NULL))
+ if (!mcd_storage_get_attribute (self, account, attribute,
+ G_VARIANT_TYPE_INT32, &tmp, NULL))
return FALSE;
return g_value_get_int (&tmp);
}
-static void
+static gboolean
update_storage (McdStorage *self,
const gchar *account,
+ gboolean parameter,
const gchar *key,
- GVariant *variant,
- const gchar *escaped,
- gboolean secret)
+ GVariant *variant)
{
- GList *store;
- gboolean done = FALSE;
- gboolean parameter = g_str_has_prefix (key, "param-");
McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
+ gboolean updated = FALSE;
+ McpAccountStorage *plugin;
+ const gchar *pn;
+ McpAccountStorageSetResult res;
- if (secret)
- mcd_storage_make_secret (self, account, key);
+ plugin = g_hash_table_lookup (self->accounts, account);
+ g_return_val_if_fail (plugin != NULL, FALSE);
+ pn = mcp_account_storage_name (plugin);
- /* we're deleting, which is unconditional, no need to check if anyone *
- * claims this setting for themselves */
- if (escaped == NULL)
- done = TRUE;
+ if (parameter)
+ res = mcp_account_storage_set_parameter (plugin, ma, account,
+ key, variant, MCP_PARAMETER_FLAG_NONE);
+ else
+ res = mcp_account_storage_set_attribute (plugin, ma, account,
+ key, variant, MCP_ATTRIBUTE_FLAG_NONE);
- for (store = stores; store != NULL; store = g_list_next (store))
+ switch (res)
{
- McpAccountStorage *plugin = store->data;
- const gchar *pn = mcp_account_storage_name (plugin);
+ case MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED:
+ DEBUG ("MCP:%s -> store %s %s.%s", pn,
+ parameter ? "parameter" : "attribute", account, key);
+ updated = TRUE;
+ break;
- if (done)
- {
- DEBUG ("MCP:%s -> delete %s.%s", pn, account, key);
- mcp_account_storage_delete (plugin, ma, account, key);
- }
- else if (variant != NULL && !parameter &&
- mcp_account_storage_set_attribute (plugin, ma, account, key, variant,
- MCP_ATTRIBUTE_FLAG_NONE))
- {
- done = TRUE;
- DEBUG ("MCP:%s -> store attribute %s.%s", pn, account, key);
- }
- else if (variant != NULL && parameter &&
- mcp_account_storage_set_parameter (plugin, ma, account, key + 6,
- variant,
- secret ? MCP_PARAMETER_FLAG_SECRET : MCP_PARAMETER_FLAG_NONE))
- {
- done = TRUE;
- DEBUG ("MCP:%s -> store parameter %s.%s", pn, account, key);
- }
- else
- {
- done = mcp_account_storage_set (plugin, ma, account, key, escaped);
- DEBUG ("MCP:%s -> %s %s.%s",
- pn, done ? "store" : "ignore", account, key);
- }
+ case MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED:
+ DEBUG ("MCP:%s -> failed to store %s %s.%s",
+ pn, parameter ? "parameter" : "attribute", account, key);
+ break;
+
+ case MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED:
+ DEBUG ("MCP:%s -> no change to %s %s.%s",
+ pn, parameter ? "parameter" : "attribute", account, key);
+ break;
+
+ default:
+ g_warn_if_reached ();
}
+
+ return updated;
}
/*
@@ -1537,45 +1539,24 @@ mcd_storage_set_attribute (McdStorage *self,
const gchar *attribute,
const GValue *value)
{
- McdStorageAccount *sa;
- GVariant *old_v;
GVariant *new_v;
gboolean updated = FALSE;
+ McpAccountStorage *plugin;
g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
g_return_val_if_fail (account != NULL, FALSE);
g_return_val_if_fail (attribute != NULL, FALSE);
g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), FALSE);
- sa = ensure_account (self, account);
+ plugin = g_hash_table_lookup (self->accounts, account);
+ g_return_val_if_fail (plugin != NULL, FALSE);
if (value != NULL)
new_v = g_variant_ref_sink (dbus_g_value_build_g_variant (value));
else
new_v = NULL;
- old_v = g_hash_table_lookup (sa->attributes, attribute);
-
- if (!mcd_nullable_variant_equal (old_v, new_v))
- {
- gchar *escaped = NULL;
-
- /* First put it in the attributes hash table. (Watch out, this might
- * invalidate old_v.) */
- if (new_v == NULL)
- g_hash_table_remove (sa->attributes, attribute);
- else
- g_hash_table_insert (sa->attributes, g_strdup (attribute),
- g_variant_ref (new_v));
-
- /* OK now we have to escape it in a stupid way for plugins */
- if (value != NULL)
- escaped = mcd_keyfile_escape_value (value);
-
- update_storage (self, account, attribute, new_v, escaped, FALSE);
- g_free (escaped);
- updated = TRUE;
- }
+ updated = update_storage (self, account, FALSE, attribute, new_v);
tp_clear_pointer (&new_v, g_variant_unref);
return updated;
@@ -1587,8 +1568,6 @@ mcd_storage_set_attribute (McdStorage *self,
* @account: the unique name of an account
* @parameter: the name of the parameter, e.g. "account"
* @value: the value to be stored (or %NULL to erase it)
- * @secret: whether the value is confidential (might get stored in the
- * keyring, for example)
*
* Copies and stores the supplied @value (or removes it if %NULL) in the
* internal cache.
@@ -1603,66 +1582,30 @@ gboolean
mcd_storage_set_parameter (McdStorage *self,
const gchar *account,
const gchar *parameter,
- const GValue *value,
- gboolean secret)
+ const GValue *value)
{
- GVariant *old_v;
GVariant *new_v = NULL;
- const gchar *old_escaped;
- gchar *new_escaped = NULL;
- McdStorageAccount *sa;
gboolean updated = FALSE;
+ McpAccountStorage *plugin;
g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE);
g_return_val_if_fail (account != NULL, FALSE);
g_return_val_if_fail (parameter != NULL, FALSE);
- sa = ensure_account (self, account);
+ plugin = g_hash_table_lookup (self->accounts, account);
+ g_return_val_if_fail (plugin != NULL, FALSE);
if (value != NULL)
{
- new_escaped = mcd_keyfile_escape_value (value);
new_v = g_variant_ref_sink (dbus_g_value_build_g_variant (value));
}
- old_v = g_hash_table_lookup (sa->parameters, parameter);
- old_escaped = g_hash_table_lookup (sa->escaped_parameters, parameter);
-
- if (old_v != NULL)
- updated = !mcd_nullable_variant_equal (old_v, new_v);
- else if (old_escaped != NULL)
- updated = tp_strdiff (old_escaped, new_escaped);
- else
- updated = (value != NULL);
+ updated = update_storage (self, account, TRUE, parameter, new_v);
- if (updated)
- {
- gchar key[MAX_KEY_LENGTH];
-
- g_hash_table_remove (sa->parameters, parameter);
- g_hash_table_remove (sa->escaped_parameters, parameter);
-
- if (new_v != NULL)
- g_hash_table_insert (sa->parameters, g_strdup (parameter),
- g_variant_ref (new_v));
-
- g_snprintf (key, sizeof (key), "param-%s", parameter);
- update_storage (self, account, key, new_v, new_escaped, secret);
- return TRUE;
- }
-
- g_free (new_escaped);
tp_clear_pointer (&new_v, g_variant_unref);
return updated;
}
-static gchar *
-mcpa_escape_value_for_keyfile (const McpAccountManager *unused G_GNUC_UNUSED,
- const GValue *value)
-{
- return mcd_keyfile_escape_value (value);
-}
-
/*
* @value: a populated #GValue of a supported #GType
*
@@ -1675,15 +1618,23 @@ mcpa_escape_value_for_keyfile (const McpAccountManager *unused G_GNUC_UNUSED,
gchar *
mcd_keyfile_escape_value (const GValue *value)
{
- GKeyFile *keyfile;
+ GVariant *variant;
gchar *ret;
g_return_val_if_fail (G_IS_VALUE (value), NULL);
- keyfile = g_key_file_new ();
- mcd_keyfile_set_value (keyfile, "g", "k", value);
- ret = g_key_file_get_value (keyfile, "g", "k", NULL);
- g_key_file_free (keyfile);
+ variant = dbus_g_value_build_g_variant (value);
+
+ if (variant == NULL)
+ {
+ g_warning ("Unable to convert %s to GVariant",
+ G_VALUE_TYPE_NAME (value));
+ return NULL;
+ }
+
+ g_variant_ref_sink (variant);
+ ret = mcd_keyfile_escape_variant (variant);
+ g_variant_unref (variant);
return ret;
}
@@ -1721,6 +1672,53 @@ mcd_keyfile_set_value (GKeyFile *keyfile,
if (value == NULL)
{
+ return mcd_keyfile_set_variant (keyfile, name, key, NULL);
+ }
+ else
+ {
+ GVariant *variant;
+ gboolean ret;
+
+ variant = dbus_g_value_build_g_variant (value);
+
+ if (variant == NULL)
+ {
+ g_warning ("Unable to convert %s to GVariant",
+ G_VALUE_TYPE_NAME (value));
+ return FALSE;
+ }
+
+ g_variant_ref_sink (variant);
+ ret = mcd_keyfile_set_variant (keyfile, name, key, variant);
+ g_variant_unref (variant);
+ return ret;
+ }
+}
+
+/*
+ * mcd_keyfile_set_variant:
+ * @keyfile: a keyfile
+ * @name: the name of a group
+ * @key: the key in the group
+ * @value: the value to be stored (or %NULL to erase it)
+ *
+ * Escape @variant and store it in the keyfile.
+ *
+ * Returns: %TRUE if the keyfile actually changed,
+ * so that the caller can decide whether to request a commit to
+ * long term storage or not.
+ */
+gboolean
+mcd_keyfile_set_variant (GKeyFile *keyfile,
+ const gchar *name,
+ const gchar *key,
+ GVariant *value)
+{
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+
+ if (value == NULL)
+ {
gchar *old = g_key_file_get_value (keyfile, name, key, NULL);
gboolean updated = (old != NULL);
@@ -1735,75 +1733,88 @@ mcd_keyfile_set_value (GKeyFile *keyfile,
gchar *new = NULL;
gchar *buf = NULL;
- switch (G_VALUE_TYPE (value))
+ switch (g_variant_classify (value))
{
- case G_TYPE_STRING:
+ case G_VARIANT_CLASS_STRING:
+ case G_VARIANT_CLASS_OBJECT_PATH:
+ case G_VARIANT_CLASS_SIGNATURE:
g_key_file_set_string (keyfile, name, key,
- g_value_get_string (value));
+ g_variant_get_string (value, NULL));
+ break;
+
+ case G_VARIANT_CLASS_UINT16:
+ buf = g_strdup_printf ("%u", g_variant_get_uint16 (value));
break;
- case G_TYPE_UINT:
- buf = g_strdup_printf ("%u", g_value_get_uint (value));
+ case G_VARIANT_CLASS_UINT32:
+ buf = g_strdup_printf ("%u", g_variant_get_uint32 (value));
break;
- case G_TYPE_INT:
- g_key_file_set_integer (keyfile, name, key,
- g_value_get_int (value));
+ case G_VARIANT_CLASS_INT16:
+ buf = g_strdup_printf ("%d", g_variant_get_int16 (value));
break;
- case G_TYPE_BOOLEAN:
+ case G_VARIANT_CLASS_INT32:
+ buf = g_strdup_printf ("%d", g_variant_get_int32 (value));
+ break;
+
+ case G_VARIANT_CLASS_BOOLEAN:
g_key_file_set_boolean (keyfile, name, key,
- g_value_get_boolean (value));
+ g_variant_get_boolean (value));
break;
- case G_TYPE_UCHAR:
- buf = g_strdup_printf ("%u", g_value_get_uchar (value));
+ case G_VARIANT_CLASS_BYTE:
+ buf = g_strdup_printf ("%u", g_variant_get_byte (value));
break;
- case G_TYPE_UINT64:
+ case G_VARIANT_CLASS_UINT64:
buf = g_strdup_printf ("%" G_GUINT64_FORMAT,
- g_value_get_uint64 (value));
+ g_variant_get_uint64 (value));
break;
- case G_TYPE_INT64:
+ case G_VARIANT_CLASS_INT64:
buf = g_strdup_printf ("%" G_GINT64_FORMAT,
- g_value_get_int64 (value));
+ g_variant_get_int64 (value));
break;
- case G_TYPE_DOUBLE:
+ case G_VARIANT_CLASS_DOUBLE:
g_key_file_set_double (keyfile, name, key,
- g_value_get_double (value));
+ g_variant_get_double (value));
break;
- default:
- if (G_VALUE_HOLDS (value, G_TYPE_STRV))
+ case G_VARIANT_CLASS_ARRAY:
+ if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
{
- gchar **strings = g_value_get_boxed (value);
+ gsize len;
+ const gchar **strings = g_variant_get_strv (value, &len);
- g_key_file_set_string_list (keyfile, name, key,
- (const gchar **)strings,
- g_strv_length (strings));
+ g_key_file_set_string_list (keyfile, name, key, strings, len);
}
- else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
+ else if (g_variant_is_of_type (value,
+ G_VARIANT_TYPE_OBJECT_PATH_ARRAY))
{
- g_key_file_set_string (keyfile, name, key,
- g_value_get_boxed (value));
+ gsize len;
+ const gchar **strings = g_variant_get_objv (value, &len);
+
+ g_key_file_set_string_list (keyfile, name, key, strings, len);
}
- else if (G_VALUE_HOLDS (value, TP_ARRAY_TYPE_OBJECT_PATH_LIST))
+ else
{
- GPtrArray *arr = g_value_get_boxed (value);
-
- g_key_file_set_string_list (keyfile, name, key,
- (const gchar * const *) arr->pdata, arr->len);
+ g_warning ("Unexpected array type %s",
+ g_variant_get_type_string (value));
+ return FALSE;
}
- else if (G_VALUE_HOLDS (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE))
+ break;
+
+ case G_VARIANT_CLASS_TUPLE:
+ if (g_variant_is_of_type (value, G_VARIANT_TYPE ("(uss)")))
{
- guint type;
+ guint32 type;
/* enough for "4294967296" + \0 */
gchar printf_buf[11];
const gchar * strv[4] = { NULL, NULL, NULL, NULL };
- tp_value_array_unpack (g_value_get_boxed (value), 3,
+ g_variant_get (value, "(u&s&s)",
&type,
&(strv[1]),
&(strv[2]));
@@ -1814,8 +1825,16 @@ mcd_keyfile_set_value (GKeyFile *keyfile,
}
else
{
- g_warning ("Unexpected param type %s",
- G_VALUE_TYPE_NAME (value));
+ g_warning ("Unexpected struct type %s",
+ g_variant_get_type_string (value));
+ return FALSE;
+ }
+ break;
+
+ default:
+ {
+ g_warning ("Unexpected variant type %s",
+ g_variant_get_type_string (value));
return FALSE;
}
}
@@ -1842,7 +1861,8 @@ mcd_keyfile_set_value (GKeyFile *keyfile,
* @provider: the desired storage provider, or %NULL
* @manager: the name of the manager
* @protocol: the name of the protocol
- * @params: A gchar * / GValue * hash table of account parameters
+ * @identification: the result of IdentifyAccount
+ * @plugin_out: (out) (transfer full): the plugin we used
* @error: a #GError to fill when returning %NULL
*
* Create a new account in storage. This should not store any
@@ -1857,11 +1877,16 @@ mcd_storage_create_account (McdStorage *self,
const gchar *provider,
const gchar *manager,
const gchar *protocol,
- GHashTable *params,
+ const gchar *identification,
+ McpAccountStorage **plugin_out,
GError **error)
{
GList *store;
McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
+ gchar *ret;
+
+ if (plugin_out != NULL)
+ *plugin_out = NULL;
g_return_val_if_fail (MCD_IS_STORAGE (self), NULL);
g_return_val_if_fail (!tp_str_empty (manager), NULL);
@@ -1876,8 +1901,21 @@ mcd_storage_create_account (McdStorage *self,
if (!tp_strdiff (mcp_account_storage_provider (plugin), provider))
{
- return mcp_account_storage_create (plugin, ma, manager,
- protocol, params, error);
+ ret = mcp_account_storage_create (plugin, ma, manager,
+ protocol, identification, error);
+ if (mcd_storage_add_account_from_plugin (self, plugin, ret,
+ error))
+ {
+ if (plugin_out != NULL)
+ *plugin_out = g_object_ref (plugin);
+
+ return ret;
+ }
+ else
+ {
+ g_free (ret);
+ return NULL;
+ }
}
}
@@ -1889,50 +1927,30 @@ mcd_storage_create_account (McdStorage *self,
/* No provider specified, let's pick the first plugin able to create this
* account in priority order.
- *
- * FIXME: This is rather subtle, and relies on the fact that accounts
- * aren't always strongly tied to a single plugin.
- *
- * For plugins that only store their accounts set up specifically
- * through them (like the libaccounts/SSO pseudo-plugin,
- * McdAccountManagerSSO), create() will fail as unimplemented,
- * and we'll fall through to the next plugin. Eventually we'll
- * reach the default keyfile+gnome-keyring plugin, or another
- * plugin that accepts arbitrary accounts. When set() is called,
- * the libaccounts/SSO plugin will reject that too, and again,
- * we'll fall through to a plugin that accepts arbitrary
- * accounts.
- *
- * Plugins that will accept arbitrary accounts being created
- * via D-Bus (like the default keyfile+gnome-keyring plugin,
- * and the account-diversion plugin in tests/twisted)
- * should, in principle, implement create() to be successful.
- * If they do, their create() will succeed, and later, so will
- * their set().
- *
- * We can't necessarily rely on all such plugins implementing
- * create(), because it isn't a mandatory part of the plugin
- * API (it was added later). However, as it happens, the
- * default plugin returns successfully from create() without
- * really doing anything. When we iterate through the accounts again
- * to call set(), higher-priority plugins are given a second
- * chance to intercept that; so we end up with create() in
- * the default plugin being followed by set() from the
- * higher-priority plugin. In theory that's bad because it
- * splits the account across two plugins, but in practice
- * it isn't a problem because the default plugin's create()
- * doesn't really do anything anyway.
*/
for (store = stores; store != NULL; store = g_list_next (store))
{
McpAccountStorage *plugin = store->data;
- gchar *ret;
- ret = mcp_account_storage_create (plugin, ma, manager, protocol, params,
- error);
+ ret = mcp_account_storage_create (plugin, ma, manager, protocol,
+ identification, error);
if (ret != NULL)
- return ret;
+ {
+ if (mcd_storage_add_account_from_plugin (self, plugin, ret,
+ error))
+ {
+ if (plugin_out != NULL)
+ *plugin_out = g_object_ref (plugin);
+
+ return ret;
+ }
+ else
+ {
+ g_free (ret);
+ return NULL;
+ }
+ }
g_clear_error (error);
}
@@ -1946,6 +1964,29 @@ mcd_storage_create_account (McdStorage *self,
return NULL;
}
+static void
+delete_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ const gchar *account_name = user_data;
+
+ if (mcp_account_storage_delete_finish (MCP_ACCOUNT_STORAGE (source),
+ res, &error))
+ {
+ DEBUG ("deleted account %s", account_name);
+ }
+ else
+ {
+ DEBUG ("could not delete account %s (but no way to signal that): "
+ "%s #%d: %s", account_name,
+ g_quark_to_string (error->domain), error->code, error->message);
+ g_error_free (error);
+ }
+
+ g_free (user_data);
+}
/*
* mcd_storage_delete_account:
@@ -1961,20 +2002,19 @@ void
mcd_storage_delete_account (McdStorage *self,
const gchar *account)
{
- GList *store;
McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
+ McpAccountStorage *plugin;
g_return_if_fail (MCD_IS_STORAGE (self));
g_return_if_fail (account != NULL);
- g_hash_table_remove (self->accounts, account);
+ plugin = g_hash_table_lookup (self->accounts, account);
+ g_return_if_fail (plugin != NULL);
- for (store = stores; store != NULL; store = g_list_next (store))
- {
- McpAccountStorage *plugin = store->data;
-
- mcp_account_storage_delete (plugin, ma, account, NULL);
- }
+ /* FIXME: stop ignoring the error (if any), and make this method async
+ * in order to pass the error up to McdAccount */
+ mcp_account_storage_delete_async (plugin, ma, account, NULL,
+ delete_cb, g_strdup (account));
}
/*
@@ -1988,27 +2028,21 @@ mcd_storage_delete_account (McdStorage *self,
void
mcd_storage_commit (McdStorage *self, const gchar *account)
{
- GList *store;
McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
+ McpAccountStorage *plugin;
+ const gchar *pname;
g_return_if_fail (MCD_IS_STORAGE (self));
+ g_return_if_fail (account != NULL);
- for (store = stores; store != NULL; store = g_list_next (store))
- {
- McpAccountStorage *plugin = store->data;
- const gchar *pname = mcp_account_storage_name (plugin);
+ plugin = g_hash_table_lookup (self->accounts, account);
+ g_return_if_fail (plugin != NULL);
- if (account != NULL)
- {
- DEBUG ("flushing plugin %s %s to long term storage", pname, account);
- mcp_account_storage_commit_one (plugin, ma, account);
- }
- else
- {
- DEBUG ("flushing plugin %s to long term storage", pname);
- mcp_account_storage_commit (plugin, ma);
- }
- }
+ pname = mcp_account_storage_name (plugin);
+
+ /* FIXME: fd.o #29563: this should be async, really */
+ DEBUG ("flushing plugin %s %s to long term storage", pname, account);
+ mcp_account_storage_commit (plugin, ma, account);
}
/*
@@ -2049,20 +2083,27 @@ mcd_storage_set_strv (McdStorage *storage,
return ret;
}
-void
-mcd_storage_ready (McdStorage *self)
+static GVariant *
+mcpa_unescape_variant_from_keyfile (const McpAccountManager *mcpa,
+ const gchar *escaped,
+ const GVariantType *type,
+ GError **error)
{
- GList *store;
- McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self);
+ GKeyFile *keyfile;
+ GVariant *ret;
- for (store = stores; store != NULL; store = g_list_next (store))
- {
- McpAccountStorage *plugin = store->data;
- const gchar *plugin_name = mcp_account_storage_name (plugin);
+ g_return_val_if_fail (escaped != NULL, NULL);
+ g_return_val_if_fail (type != NULL, NULL);
- DEBUG ("Unblocking async account ops by %s", plugin_name);
- mcp_account_storage_ready (plugin, ma);
- }
+ keyfile = g_key_file_new ();
+ g_key_file_set_value (keyfile, "g", "k", escaped);
+ ret = mcd_keyfile_get_variant (keyfile, "g", "k", type, error);
+ g_key_file_free (keyfile);
+
+ if (ret != NULL)
+ g_variant_ref_sink (ret);
+
+ return ret;
}
static void
@@ -2071,32 +2112,252 @@ plugin_iface_init (McpAccountManagerIface *iface,
{
DEBUG ();
- iface->get_value = get_value;
- iface->set_value = set_value;
- iface->set_attribute = mcpa_set_attribute;
- iface->set_parameter = mcpa_set_parameter;
- iface->is_secret = is_secret;
- iface->make_secret = make_secret;
iface->unique_name = unique_name;
- iface->list_keys = list_keys;
- iface->escape_value_for_keyfile = mcpa_escape_value_for_keyfile;
+ iface->identify_account_async = identify_account_async;
+ iface->identify_account_finish = identify_account_finish;
iface->escape_variant_for_keyfile = mcpa_escape_variant_for_keyfile;
- iface->unescape_value_from_keyfile = mcpa_unescape_value_from_keyfile;
- iface->init_value_for_attribute = mcpa_init_value_for_attribute;
+ iface->unescape_variant_from_keyfile = mcpa_unescape_variant_from_keyfile;
}
gboolean
mcd_storage_add_account_from_plugin (McdStorage *self,
McpAccountStorage *plugin,
- const gchar *account)
+ const gchar *account,
+ GError **error)
{
- if (!mcp_account_storage_get (plugin, MCP_ACCOUNT_MANAGER (self),
- account, NULL))
+ McpAccountStorage *other = g_hash_table_lookup (self->accounts, account);
+ McpAccountManager *api = (McpAccountManager *) self;
+ gchar **typed_parameters;
+ gchar **untyped_parameters;
+
+ if (other != NULL)
{
- g_warning ("plugin %s disowned account %s",
- mcp_account_storage_name (plugin), account);
+ g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
+ "account %s already exists in plugin '%s', cannot create "
+ "for plugin '%s'",
+ account,
+ mcp_account_storage_name (other),
+ mcp_account_storage_name (plugin));
return FALSE;
}
+ g_hash_table_insert (self->accounts, g_strdup (account),
+ g_object_ref (plugin));
+
+ typed_parameters = mcp_account_storage_list_typed_parameters (plugin, api,
+ account);
+ untyped_parameters = mcp_account_storage_list_untyped_parameters (plugin,
+ api, account);
+
+ DEBUG ("Account parameters for %s", account);
+
+ if (typed_parameters != NULL)
+ {
+ gsize i;
+
+ for (i = 0; typed_parameters[i] != NULL; i++)
+ {
+ GVariant *v = mcp_account_storage_get_parameter (plugin, api, account,
+ typed_parameters[i], NULL, NULL);
+
+ if (v == NULL)
+ {
+ CRITICAL ("%s: could not be retrieved", typed_parameters[i]);
+ }
+ else
+ {
+ DEBUG ("%s: type '%s'", typed_parameters[i],
+ g_variant_get_type_string (v));
+ g_variant_unref (v);
+ }
+ }
+ }
+
+ if (untyped_parameters != NULL)
+ {
+ gsize i;
+
+ for (i = 0; untyped_parameters[i] != NULL; i++)
+ {
+ DEBUG ("%s: type not stored", untyped_parameters[i]);
+ }
+ }
+
+ DEBUG ("End of parameters");
+
+ g_strfreev (typed_parameters);
+ g_strfreev (untyped_parameters);
+
return TRUE;
}
+
+GHashTable *
+mcd_storage_dup_typed_parameters (McdStorage *self,
+ const gchar *account_name)
+{
+ McpAccountStorage *plugin;
+ McpAccountManager *api = (McpAccountManager *) self;
+ gsize i;
+ gchar **typed_parameters;
+ GHashTable *params;
+
+ g_return_val_if_fail (MCD_IS_STORAGE (self), NULL);
+
+ plugin = g_hash_table_lookup (self->accounts, account_name);
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ typed_parameters = mcp_account_storage_list_typed_parameters (plugin, api,
+ account_name);
+
+ params = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) tp_g_value_slice_free);
+
+ for (i = 0;
+ typed_parameters != NULL && typed_parameters[i] != NULL;
+ i++)
+ {
+ GVariant *v = mcp_account_storage_get_parameter (plugin, api,
+ account_name, typed_parameters[i], NULL, NULL);
+ GValue *value;
+
+ if (v == NULL)
+ {
+ CRITICAL ("%s was in list_typed_parameters() but could not be "
+ "retrieved", typed_parameters[i]);
+ continue;
+ }
+
+ value = g_slice_new0 (GValue);
+ dbus_g_value_parse_g_variant (v, value);
+
+ if (!G_IS_VALUE (value))
+ {
+ CRITICAL ("could not turn %s into a GValue", typed_parameters[i]);
+ g_slice_free (GValue, value);
+ continue;
+ }
+
+ g_hash_table_insert (params, g_strdup (typed_parameters[i]),
+ value);
+ g_variant_unref (v);
+ }
+
+ return params;
+}
+
+/* See whether we can migrate the parameters from being stored without
+ * their types, to being stored with their types.
+ * Commit changes and return TRUE if anything happened. */
+gboolean
+mcd_storage_maybe_migrate_parameters (McdStorage *self,
+ const gchar *account_name,
+ TpProtocol *protocol)
+{
+ McpAccountManager *api = MCP_ACCOUNT_MANAGER (self);
+ McpAccountStorage *plugin;
+ gchar **untyped_parameters = NULL;
+ gsize i;
+ gboolean ret = FALSE;
+
+ plugin = g_hash_table_lookup (self->accounts, account_name);
+ g_return_val_if_fail (plugin != NULL, FALSE);
+
+ /* If the storage backend can't store typed parameters, there's no point. */
+ if (!mcp_account_storage_has_any_flag (plugin, account_name,
+ MCP_ACCOUNT_STORAGE_FLAG_STORES_TYPES))
+ goto finally;
+
+ untyped_parameters = mcp_account_storage_list_untyped_parameters (
+ plugin, api, account_name);
+
+ /* If there's nothing to migrate, there's also no point. */
+ if (untyped_parameters == NULL || untyped_parameters[0] == NULL)
+ goto finally;
+
+ DEBUG ("trying to migrate %s", account_name);
+
+ for (i = 0; untyped_parameters[i] != NULL; i++)
+ {
+ const gchar *param_name = untyped_parameters[i];
+ const TpConnectionManagerParam *param = tp_protocol_get_param (protocol,
+ param_name);
+ GVariantType *type = NULL;
+ GVariant *value;
+ McpAccountStorageSetResult res;
+
+ if (param == NULL)
+ {
+ DEBUG ("cannot migrate parameter '%s': not supported by %s/%s",
+ param_name, tp_protocol_get_cm_name (protocol),
+ tp_protocol_get_name (protocol));
+ goto next_param;
+ }
+
+ type = tp_connection_manager_param_dup_variant_type (param);
+
+ DEBUG ("Migrating parameter '%s' of type '%.*s'",
+ param_name,
+ (gint) g_variant_type_get_string_length (type),
+ g_variant_type_peek_string (type));
+
+ value = mcp_account_storage_get_parameter (plugin, api,
+ account_name, param_name, type, NULL);
+
+ if (value == NULL)
+ {
+ DEBUG ("cannot migrate parameter '%s'", param_name);
+ goto next_param;
+ }
+
+ if (!g_variant_is_of_type (value, type))
+ {
+ DEBUG ("trying to convert parameter from type '%s'",
+ g_variant_get_type_string (value));
+
+ /* consumes parameter */
+ value = tp_variant_convert (value, type);
+
+ if (value == NULL)
+ {
+ DEBUG ("could not convert parameter to desired type");
+ goto next_param;
+ }
+ }
+
+ res = mcp_account_storage_set_parameter (plugin, api,
+ account_name, param_name, value, MCP_PARAMETER_FLAG_NONE);
+
+ switch (res)
+ {
+ case MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED:
+ /* it really ought to be CHANGED, surely? */
+ DEBUG ("Tried to upgrade parameter %s but the "
+ "storage backend claims not to have changed it? "
+ "Not sure I really believe that", param_name);
+ /* fall through to the CHANGED case */
+
+ case MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED:
+ ret = TRUE;
+ break;
+
+ case MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED:
+ WARNING ("Failed to set parameter %s", param_name);
+ break;
+
+ default:
+ WARNING ("set_parameter returned invalid result code %d "
+ "for parameter %s", res, param_name);
+ }
+
+next_param:
+ if (type != NULL)
+ g_variant_type_free (type);
+ }
+
+ if (ret)
+ mcp_account_storage_commit (plugin, api, account_name);
+
+finally:
+ g_strfreev (untyped_parameters);
+ return ret;
+}