diff options
Diffstat (limited to 'src/mcd-storage.c')
-rw-r--r-- | src/mcd-storage.c | 1773 |
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; +} |