diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2014-01-30 12:34:04 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2014-01-30 12:34:04 +0000 |
commit | 79f0081b0a50ab59794827dd5af79f8671870fcd (patch) | |
tree | f284178ed89e37c85043c41f2b256eb4e89f4792 | |
parent | 0e2b32c64568c39a9bbcc556f5ee1a3cae811928 (diff) | |
parent | 0b2293f16df6259d2f2aab1db1fc126c2edae59e (diff) |
Merge branch 'account-rework-27727-round4'
Reviewed-by: Xavier Claessens <xavier.claessens@collabora.co.uk>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=27727
31 files changed, 2341 insertions, 2686 deletions
diff --git a/configure.ac b/configure.ac index 6a2f35aa..22b7f0a4 100644 --- a/configure.ac +++ b/configure.ac @@ -35,7 +35,7 @@ esac # If API has been removed or changed since last release, change MCP_API (which # is part of the directory name under /usr/include) to the version that # changed it -MCP_API_VERSION=5.15 +MCP_API_VERSION=5.18 AC_SUBST([MCP_API_VERSION]) # If ABI has been removed or changed since last release diff --git a/mission-control-plugins/account-storage.c b/mission-control-plugins/account-storage.c index b51c1264..288df077 100644 --- a/mission-control-plugins/account-storage.c +++ b/mission-control-plugins/account-storage.c @@ -56,18 +56,17 @@ * iface->desc = "The FOO storage backend"; * iface->provider = "org.freedesktop.Telepathy.MissionControl5.FooStorage"; * - * iface->get = foo_plugin_get; - * iface->set = foo_plugin_get; - * iface->delete = foo_plugin_delete; + * iface->delete_async = foo_plugin_delete_async; + * iface->delete_finish = foo_plugin_delete_finish; * iface->commit = foo_plugin_commit; - * iface->commit_one = foo_plugin_commit_one; * iface->list = foo_plugin_list; * iface->ready = foo_plugin_ready; * iface->get_identifier = foo_plugin_get_identifier; * iface->get_additional_info = foo_plugin_get_additional_info; * iface->get_restrictions = foo_plugin_get_restrictions; * iface->create = foo_plugin_create; - * iface->owns = foo_plugin_owns; + * iface->get_attribute = foo_plugin_get_attribute; + * iface->get_parameter = foo_plugin_get_parameter; * iface->set_attribute = foo_plugin_set_attribute; * iface->set_parameter = foo_plugin_set_parameter; * } @@ -112,17 +111,79 @@ enum static guint signals[NO_SIGNAL] = { 0 }; -static gboolean -default_set (const McpAccountStorage *storage, - const McpAccountManager *am, +static void +default_delete_async (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account, - const gchar *key, - const gchar *val) + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - return FALSE; + g_task_report_new_error (storage, callback, user_data, + default_delete_async, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, + "This storage plugin cannot delete accounts"); +} + +static gboolean +default_delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); } static gboolean +default_commit (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account) +{ + return FALSE; +} + +static gchar * +default_create (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + const gchar *identification, + GError **error) +{ + g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, + "This storage does not implement the create() function"); + return NULL; +} + +static void +default_ready (McpAccountStorage *storage, + McpAccountManager *am) +{ + /* do nothing */ +} + +static void +default_get_identifier (McpAccountStorage *storage, + const gchar *account, + GValue *identifier) +{ + g_value_init (identifier, G_TYPE_STRING); + g_value_set_string (identifier, account); +} + +static GHashTable * +default_get_additional_info (McpAccountStorage *storage, + const gchar *account) +{ + return g_hash_table_new (g_str_hash, g_str_equal); +} + +static TpStorageRestrictionFlags +default_get_restrictions (McpAccountStorage *storage, + const gchar *account) +{ + return 0; +} + +static McpAccountStorageSetResult default_set_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -130,10 +191,10 @@ default_set_attribute (McpAccountStorage *storage, GVariant *value, McpAttributeFlags flags) { - return FALSE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED; } -static gboolean +static McpAccountStorageSetResult default_set_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -141,20 +202,7 @@ default_set_parameter (McpAccountStorage *storage, GVariant *value, McpParameterFlags flags) { - return FALSE; -} - -static gboolean -default_owns (McpAccountStorage *storage, - McpAccountManager *am, - const gchar *account) -{ - /* This has the side-effect of pushing the "manager" key back into @am, - * but that should be a no-op in practice: we always call this - * method in priority order and stop at the first one that says "yes", - * and @am's idea of what "manager" is should have come from that same - * plugin anyway. */ - return mcp_account_storage_get (storage, am, account, "manager"); + return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED; } static void @@ -164,8 +212,14 @@ class_init (gpointer klass, GType type = G_TYPE_FROM_CLASS (klass); McpAccountStorageIface *iface = klass; - iface->owns = default_owns; - iface->set = default_set; + iface->create = default_create; + iface->delete_async = default_delete_async; + iface->delete_finish = default_delete_finish; + iface->commit = default_commit; + iface->ready = default_ready; + iface->get_identifier = default_get_identifier; + iface->get_additional_info = default_get_additional_info; + iface->get_restrictions = default_get_restrictions; iface->set_attribute = default_set_attribute; iface->set_parameter = default_set_parameter; @@ -193,20 +247,20 @@ class_init (gpointer klass, /** * McpAccountStorage::altered-one * @account: the unique name of the altered account - * @name: the name of the altered property (its key) + * @name: either an attribute name such as DisplayName, + * or "param-" plus a parameter name, e.g. "param-require-encryption" * * Emitted if an external entity alters an account * in the backend that the emitting plugin handles. * - * Before emitting this signal, the plugin must call - * either mcp_account_manager_set_attribute(), - * either mcp_account_manager_set_parameter() or - * mcp_account_manager_set_value() to push the new value - * into the account manager. + * Before emitting this signal, the plugin must update its + * internal cache (if any) so that mcp_account_storage_get_attribute() + * or mcp_account_storage_get_parameter() will return the new value + * when queried. * - * Note that mcp_account_manager_set_parameter() does not use the - * "param-" prefix, but this signal and mcp_account_manager_set_value() - * both do. + * Note that mcp_account_manager_get_parameter() and + * mcp_account_manager_set_parameter() do not use the + * "param-" prefix, but this signal does. * * Should not be fired until mcp_account_storage_ready() has been called */ @@ -239,8 +293,11 @@ class_init (gpointer klass, * Emitted if an external entity enables/disables an account * in the backend the emitting plugin handles. This is similar to * emitting #McpAccountStorage::altered-one for the attribute - * "Enabled", except that the plugin is not required to call - * a function like mcp_account_manager_set_value() first. + * "Enabled". + * + * Before emitting this signal, the plugin must update its + * internal cache (if any) so that mcp_account_storage_get_attribute() + * will return the new value for Enabled when queried. * * Should not be fired until mcp_account_storage_ready() has been called * @@ -316,7 +373,6 @@ mcp_account_storage_get_type (void) * @commit: implementation of mcp_account_storage_commit() * @list: implementation of mcp_account_storage_list() * @ready: implementation of mcp_account_storage_ready() - * @commit_one: implementation of mcp_account_storage_commit_one() * @get_identifier: implementation of mcp_account_storage_get_identifier() * @get_additional_info: implementation of * mcp_account_storage_get_additional_info() @@ -361,7 +417,7 @@ mcp_account_storage_get_type (void) * Returns: the priority of this plugin **/ gint -mcp_account_storage_priority (const McpAccountStorage *storage) +mcp_account_storage_priority (McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); @@ -371,111 +427,89 @@ mcp_account_storage_priority (const McpAccountStorage *storage) } /** - * McpAccountStorageGetFunc: - * @storage: the account storage plugin - * @am: object used to call back into the account manager - * @account: the unique name of the account - * @key: the setting whose value we wish to fetch: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" - * - * An implementation of mcp_account_storage_get(). - * - * Returns: %TRUE if @storage is responsible for @account - */ - -/** - * mcp_account_storage_get: + * mcp_account_storage_get_attribute: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique name of the account - * @key: the setting whose value we wish to fetch: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" - * - * Get a value from the plugin's in-memory cache. - * Before emitting this signal, the plugin must call - * either mcp_account_manager_set_attribute(), - * mcp_account_manager_set_parameter(), - * or mcp_account_manager_set_value() and (if appropriate) - * mcp_account_manager_parameter_make_secret() - * before returning from this method call. - * - * Note that mcp_account_manager_set_parameter() does not use the - * "param-" prefix, even if called from this function. - * - * If @key is %NULL the plugin should iterate through all attributes and - * parameters, and push each of them into @am, as if this method had - * been called once for each attribute or parameter. It must then return - * %TRUE if any attributes or parameters were found, or %FALSE if it - * was not responsible for @account. - * - * Returns: %TRUE if @storage is responsible for @account + * @attribute: the name of an attribute, e.g. "DisplayName" + * @type: the expected type of @attribute, as a hint for + * legacy account storage plugins that do not store attributes' types + * @flags: (allow-none) (out): used to return attribute flags + * + * Retrieve an attribute. + * + * There is no default implementation. + * All account storage plugins must override this method. + * + * The returned variant does not necessarily have to match @type: + * Mission Control will coerce it to an appropriate type if required. In + * particular, plugins that store strongly-typed attributes may return + * the stored type, not the expected type, if they differ. + * + * Returns: (transfer full): the value of the attribute, or %NULL if it + * is not present */ -gboolean -mcp_account_storage_get (const McpAccountStorage *storage, +GVariant * +mcp_account_storage_get_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); + SDEBUG (storage, "%s.%s (type '%.*s')", account, attribute, + (int) g_variant_type_get_string_length (type), + g_variant_type_peek_string (type)); + g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->get != NULL, FALSE); + g_return_val_if_fail (iface->get_attribute != NULL, FALSE); - return iface->get (storage, am, account, key); + return iface->get_attribute (storage, am, account, attribute, type, flags); } /** - * McpAccountStorageSetFunc: - * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * @account: the unique name of the account - * @key: the setting whose value we wish to store: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" - * @val: a non-%NULL value for @key - * - * An implementation of mcp_account_storage_set(). - * - * Returns: %TRUE if @storage is responsible for @account - */ - -/** - * mcp_account_storage_set: + * mcp_account_storage_get_parameter: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique name of the account - * @key: the non-%NULL setting whose value we wish to store: either an - * attribute like "DisplayName", or "param-" plus a parameter like "account" - * @value: a value to associate with @key, escaped as if for a #GKeyFile + * @parameter: the name of a parameter, e.g. "require-encryption" + * @type: the expected type of @parameter, as a hint for + * legacy account storage plugins that do not store parameters' types + * @flags: (allow-none) (out): used to return parameter flags * - * The plugin is expected to either quickly and synchronously - * update its internal cache of values with @value, or to - * decline to store the setting. + * Retrieve a parameter. * - * The plugin is not expected to write to its long term storage - * at this point. It can expect Mission Control to call either - * mcp_account_storage_commit() or mcp_account_storage_commit_one() - * after a short delay. + * There is no default implementation. + * All account storage plugins must override this method. * - * Plugins that implement mcp_storage_set_attribute() and - * mcp_account_storage_set_parameter() can just return %FALSE here. - * There is a default implementation, which just returns %FALSE. + * The returned variant does not necessarily have to match @type: + * Mission Control will coerce it to an appropriate type if required. In + * particular, plugins that store strongly-typed parameters may return + * the stored type, not the expected type, if they differ. * - * Returns: %TRUE if the attribute was claimed, %FALSE otherwise + * Returns: (transfer full): the value of the parameter, or %NULL if it + * is not present */ -gboolean -mcp_account_storage_set (const McpAccountStorage *storage, - const McpAccountManager *am, +GVariant * +mcp_account_storage_get_parameter (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account, - const gchar *key, - const gchar *value) + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); + SDEBUG (storage, "%s.%s (type '%.*s')", account, parameter, + (int) g_variant_type_get_string_length (type), + g_variant_type_peek_string (type)); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->get_parameter != NULL, FALSE); - return iface->set (storage, am, account, key, value); + return iface->get_parameter (storage, am, account, parameter, type, flags); } /** @@ -484,7 +518,8 @@ mcp_account_storage_set (const McpAccountStorage *storage, * @am: an #McpAccountManager instance * @account: the unique name of the account * @attribute: the name of an attribute, e.g. "DisplayName" - * @value: a value to associate with @attribute + * @value: (allow-none): a value to associate with @attribute, + * or %NULL to delete * @flags: flags influencing how the attribute is to be stored * * Store an attribute. @@ -496,15 +531,14 @@ mcp_account_storage_set (const McpAccountStorage *storage, * The plugin is not expected to write to its long term storage * at this point. * - * There is a default implementation, which just returns %FALSE. - * Mission Control will call mcp_account_storage_set() instead, - * using a keyfile-escaped version of @value. + * There is a default implementation, which just returns %FALSE for read-only + * storage plugins. * * Returns: %TRUE if the attribute was claimed, %FALSE otherwise * * Since: 5.15.0 */ -gboolean +McpAccountStorageSetResult mcp_account_storage_set_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -514,9 +548,13 @@ mcp_account_storage_set_attribute (McpAccountStorage *storage, { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->set_attribute != NULL, FALSE); + SDEBUG (storage, "%s.%s (type '%s')", account, attribute, + value == NULL ? "null" : g_variant_get_type_string (value)); + + g_return_val_if_fail (iface != NULL, + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (iface->set_attribute != NULL, + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); return iface->set_attribute (storage, am, account, attribute, value, flags); } @@ -528,7 +566,8 @@ mcp_account_storage_set_attribute (McpAccountStorage *storage, * @account: the unique name of the account * @parameter: the name of a parameter, e.g. "account" (note that there * is no "param-" prefix here) - * @value: a value to associate with @parameter + * @value: (allow-none): a value to associate with @parameter, + * or %NULL to delete * @flags: flags influencing how the parameter is to be stored * * Store a parameter. @@ -540,16 +579,14 @@ mcp_account_storage_set_attribute (McpAccountStorage *storage, * The plugin is not expected to write to its long term storage * at this point. * - * There is a default implementation, which just returns %FALSE. - * Mission Control will call mcp_account_storage_set() instead, - * using "param-" + @parameter as key and a keyfile-escaped version - * of @value as value. + * There is a default implementation, which just returns %FALSE for read-only + * storage plugins. * * Returns: %TRUE if the parameter was claimed, %FALSE otherwise * * Since: 5.15.0 */ -gboolean +McpAccountStorageSetResult mcp_account_storage_set_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -559,9 +596,14 @@ mcp_account_storage_set_parameter (McpAccountStorage *storage, { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); + SDEBUG (storage, "%s.%s (type '%s')", account, parameter, + value == NULL ? "null" : g_variant_get_type_string (value)); + g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->set_parameter != NULL, FALSE); + g_return_val_if_fail (iface != NULL, + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (iface->set_parameter != NULL, + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); return iface->set_parameter (storage, am, account, parameter, value, flags); } @@ -603,13 +645,20 @@ mcp_account_storage_set_parameter (McpAccountStorage *storage, * #McpAccountStorage::created signal should not be emitted for this account, * not even when mcp_account_storage_commit() will be called. * + * The default implementation just returns %NULL, and is appropriate for + * read-only storage. + * + * Since Mission Control 5.17, all storage plugins in which new accounts + * can be created by Mission Control must implement this method. + * Previously, it was not mandatory. + * * Returns: (transfer full): the newly allocated account name, which should * be freed once the caller is done with it, or %NULL if that couldn't * be done. */ gchar * -mcp_account_storage_create (const McpAccountStorage *storage, - const McpAccountManager *am, +mcp_account_storage_create (McpAccountStorage *storage, + McpAccountManager *am, const gchar *manager, const gchar *protocol, const gchar *identification, @@ -617,76 +666,84 @@ mcp_account_storage_create (const McpAccountStorage *storage, { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - g_return_val_if_fail (iface != NULL, NULL); + SDEBUG (storage, "%s/%s \"%s\"", manager, protocol, identification); - if (iface->create == NULL) - { - g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, - "This storage does not implement create function"); - return NULL; - } + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->create != NULL, NULL); return iface->create (storage, am, manager, protocol, identification, error); } /** - * McpAccountStorageDeleteFunc: + * mcp_account_storage_delete_async: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique name of the account - * @key: (allow-none): the setting whose value we wish to store - either an - * attribute like "DisplayName", or "param-" plus a parameter like - * "account" - or %NULL to delete the entire account + * @cancellable: (allow-none): optionally used to (try to) cancel the operation + * @callback: called on success or failure + * @user_data: data for @callback * - * An implementation of mcp_account_storage_delete(). + * Delete the account @account, and commit the change, + * emitting #McpAccountStorage::deleted afterwards. * - * Returns: %TRUE if the setting or settings are not - * the plugin's cache after this operation, %FALSE otherwise. + * Unlike the 'delete' virtual method in earlier MC versions, this + * function is expected to commit the change to long-term storage, + * is expected to emit #McpAccountStorage::deleted, and is + * not called for the deletion of individual attributes or parameters. + * + * The default implementation just returns failure (asynchronously), + * and is appropriate for read-only storage. + * + * Implementations that override delete_async must also override + * delete_finish. */ +void +mcp_account_storage_delete_async (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + + SDEBUG (storage, "%s", account); + + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->delete_async != NULL); + + iface->delete_async (storage, am, account, cancellable, callback, user_data); +} /** - * mcp_account_storage_delete: + * mcp_account_storage_delete_finish: * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * @account: the unique name of the account - * @key: (allow-none): the setting whose value we wish to store - either an - * attribute like "DisplayName", or "param-" plus a parameter like - * "account" - or %NULL to delete the entire account - * - * The plugin is expected to remove the setting for @key from its - * internal cache and to remember that its state has changed, so - * that it can delete said setting from its long term storage if - * its long term storage method makes this necessary. + * @res: the result of mcp_account_storage_delete_async() + * @error: used to raise an error if %FALSE is returned * - * If @key is %NULL, the plugin should forget all its settings for - * @account,and remember to delete the entire account from its storage later. + * Process the result of mcp_account_storage_delete_async(). * - * The plugin is not expected to update its long term storage at - * this point. - * - * Returns: %TRUE if the setting or settings are not - * the plugin's cache after this operation, %FALSE otherwise. - * This is very unlikely to ever be %FALSE, as a plugin is always - * expected to be able to manipulate its own cache. + * Returns: %TRUE on success, %FALSE if the account could not be deleted */ gboolean -mcp_account_storage_delete (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key) +mcp_account_storage_delete_finish (McpAccountStorage *storage, + GAsyncResult *result, + GError **error) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->delete_finish != NULL, FALSE); - return iface->delete (storage, am, account, key); + return iface->delete_finish (storage, result, error); } /** * McpAccountStorageCommitFunc: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance + * @account: the unique suffix of an account's object path * * An implementation of mcp_account_storage_commit(). * @@ -697,6 +754,7 @@ mcp_account_storage_delete (const McpAccountStorage *storage, * mcp_account_storage_commit: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance + * @account: the unique suffix of an account's object path * * The plugin is expected to write its cache to long term storage, * deleting, adding or updating entries in said storage as needed. @@ -705,87 +763,28 @@ mcp_account_storage_delete (const McpAccountStorage *storage, * not required to have finished its commit operation when it returns, * merely to have started the operation. * - * If the @commit_one method is implemented, it will be called preferentially - * if only one account is to be committed. If the @commit_one method is - * implemented but @commit is not, @commit_one will be called with - * @account_name = %NULL to commit all accounts. + * The default implementation just returns %FALSE, and is appropriate for + * read-only storage. * - * Returns: %TRUE if the commit process was started (but not necessarily - * completed) successfully; %FALSE if there was a problem that was immediately - * obvious. - */ -gboolean -mcp_account_storage_commit (const McpAccountStorage *storage, - const McpAccountManager *am) -{ - McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - - SDEBUG (storage, "committing all accounts"); - g_return_val_if_fail (iface != NULL, FALSE); - - if (iface->commit != NULL) - { - return iface->commit (storage, am); - } - else if (iface->commit_one != NULL) - { - return iface->commit_one (storage, am, NULL); - } - else - { - SDEBUG (storage, - "neither commit nor commit_one is implemented; cannot save accounts"); - return FALSE; - } -} - -/** - * McpAccountStorageCommitOneFunc: - * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * @account: (allow-none): the unique suffix of an account's object path, - * or %NULL - * - * An implementation of mcp_account_storage_commit_one(). - * - * Returns: %TRUE if the commit process was started successfully - */ - -/** - * mcp_account_storage_commit_one: - * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * @account: (allow-none): the unique suffix of an account's object path, - * or %NULL if all accounts are to be committed and - * mcp_account_storage_commit() is unimplemented - * - * The same as mcp_account_storage_commit(), but only commit the given - * account. This is optional to implement; the default implementation - * is to call @commit. - * - * If both mcp_account_storage_commit_one() and mcp_account_storage_commit() - * are implemented, Mission Control will never pass @account = %NULL to - * this method. + * Mission Control 5.17+ no longer requires plugins to cope with + * account == NULL. * * Returns: %TRUE if the commit process was started (but not necessarily * completed) successfully; %FALSE if there was a problem that was immediately * obvious. */ gboolean -mcp_account_storage_commit_one (const McpAccountStorage *storage, - const McpAccountManager *am, +mcp_account_storage_commit (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, "called for %s", account ? account : "<all accounts>"); g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->commit != NULL, FALSE); - if (iface->commit_one != NULL) - return iface->commit_one (storage, am, account); - else - /* Fall back to plain ->commit() */ - return mcp_account_storage_commit (storage, am); + return iface->commit (storage, am, account); } /** @@ -809,19 +808,23 @@ mcp_account_storage_commit_one (const McpAccountStorage *storage, * This method is called only at initialisation time, before the dbus name * has been claimed, and is the only one permitted to block. * + * There is no default implementation. All implementations of this interface + * must override this method. + * * Returns: (element-type utf8) (transfer full): a list of account names that * the plugin has settings for. The account names should be freed with * g_free(), and the list with g_list_free(), when the caller is done with * them. */ GList * -mcp_account_storage_list (const McpAccountStorage *storage, - const McpAccountManager *am) +mcp_account_storage_list (McpAccountStorage *storage, + McpAccountManager *am) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->list != NULL, NULL); return iface->list (storage, am); } @@ -842,20 +845,22 @@ mcp_account_storage_list (const McpAccountStorage *storage, * Informs the plugin that it is now permitted to create new accounts, * ie it can now fire its "created", "altered-one", "toggled" and "deleted" * signals. + * + * The default implementation does nothing. It should be overridden by + * any plugin that will emit "created", "altered-one", "toggled" and/or + * "deleted". */ void -mcp_account_storage_ready (const McpAccountStorage *storage, - const McpAccountManager *am) +mcp_account_storage_ready (McpAccountStorage *storage, + McpAccountManager *am) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + SDEBUG (storage, ""); g_return_if_fail (iface != NULL); + g_return_if_fail (iface->ready != NULL); - /* plugins that can't create accounts from external sources don't * - * need to implement this method, as they can never fire the async * - * account change signals: */ - if (iface->ready != NULL) - iface->ready (storage, am); + iface->ready (storage, am); } /** @@ -878,30 +883,25 @@ mcp_account_storage_ready (const McpAccountStorage *storage, * Get the storage-specific identifier for this account. The type is variant, * hence the GValue. * + * The default implementation returns @account as a %G_TYPE_STRING. + * * This method will only be called for the storage plugin that "owns" * the account. */ void -mcp_account_storage_get_identifier (const McpAccountStorage *storage, +mcp_account_storage_get_identifier (McpAccountStorage *storage, const gchar *account, GValue *identifier) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); + SDEBUG (storage, "%s", account); g_return_if_fail (iface != NULL); + g_return_if_fail (iface->get_identifier != NULL); g_return_if_fail (identifier != NULL); g_return_if_fail (!G_IS_VALUE (identifier)); - if (iface->get_identifier == NULL) - { - g_value_init (identifier, G_TYPE_STRING); - g_value_set_string (identifier, account); - } - else - { - iface->get_identifier (storage, account, identifier); - } + iface->get_identifier (storage, account, identifier); } /** @@ -926,26 +926,22 @@ mcp_account_storage_get_identifier (const McpAccountStorage *storage, * This method will only be called for the storage plugin that "owns" * the account. * + * The default implementation returns an empty map. + * * Returns: (transfer container) (element-type utf8 GObject.Value): additional - * storage-specific information + * storage-specific information, which must not be %NULL */ GHashTable * -mcp_account_storage_get_additional_info (const McpAccountStorage *storage, +mcp_account_storage_get_additional_info (McpAccountStorage *storage, const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - GHashTable *ret = NULL; - SDEBUG (storage, ""); + SDEBUG (storage, "%s", account); g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->get_additional_info != NULL, FALSE); - if (iface->get_additional_info != NULL) - ret = iface->get_additional_info (storage, account); - - if (ret == NULL) - ret = g_hash_table_new (g_str_hash, g_str_equal); - - return ret; + return iface->get_additional_info (storage, account); } /** @@ -966,21 +962,22 @@ mcp_account_storage_get_additional_info (const McpAccountStorage *storage, * This method will only be called for the storage plugin that "owns" * the account. * + * The default implementation returns 0, i.e. no restrictions. + * * Returns: a bitmask of %TpStorageRestrictionFlags with the restrictions to * account storage. */ TpStorageRestrictionFlags -mcp_account_storage_get_restrictions (const McpAccountStorage *storage, +mcp_account_storage_get_restrictions (McpAccountStorage *storage, const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + SDEBUG (storage, "%s", account); g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->get_restrictions != NULL, 0); - if (iface->get_restrictions == NULL) - return 0; - else - return iface->get_restrictions (storage, account); + return iface->get_restrictions (storage, account); } /** @@ -992,7 +989,7 @@ mcp_account_storage_get_restrictions (const McpAccountStorage *storage, * Returns: the plugin's name (for logging etc) */ const gchar * -mcp_account_storage_name (const McpAccountStorage *storage) +mcp_account_storage_name (McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); @@ -1010,7 +1007,7 @@ mcp_account_storage_name (const McpAccountStorage *storage) * Returns: the plugin's description (for logging etc) */ const gchar * -mcp_account_storage_description (const McpAccountStorage *storage) +mcp_account_storage_description (McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); @@ -1029,7 +1026,7 @@ mcp_account_storage_description (const McpAccountStorage *storage) * was provided in #McpAccountStorageIface.provider. */ const gchar * -mcp_account_storage_provider (const McpAccountStorage *storage) +mcp_account_storage_provider (McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); @@ -1039,7 +1036,7 @@ mcp_account_storage_provider (const McpAccountStorage *storage) } /** - * mcp_account_storage_emit_create: + * mcp_account_storage_emit_created: * @storage: an #McpAccountStorage instance * @account: the unique name of the created account * @@ -1049,6 +1046,7 @@ void mcp_account_storage_emit_created (McpAccountStorage *storage, const gchar *account) { + SDEBUG (storage, "%s", account); g_signal_emit (storage, signals[CREATED], 0, account); } @@ -1066,6 +1064,7 @@ mcp_account_storage_emit_altered_one (McpAccountStorage *storage, const gchar *account, const gchar *key) { + SDEBUG (storage, "%s", account); g_signal_emit (storage, signals[ALTERED_ONE], 0, account, key); } @@ -1080,6 +1079,7 @@ void mcp_account_storage_emit_deleted (McpAccountStorage *storage, const gchar *account) { + SDEBUG (storage, "%s", account); g_signal_emit (storage, signals[DELETED], 0, account); } @@ -1096,6 +1096,7 @@ mcp_account_storage_emit_toggled (McpAccountStorage *storage, const gchar *account, gboolean enabled) { + SDEBUG (storage, "%s: Enabled=%s", account, enabled ? "True" : "False"); g_signal_emit (storage, signals[TOGGLED], 0, account, enabled); } @@ -1110,35 +1111,6 @@ void mcp_account_storage_emit_reconnect (McpAccountStorage *storage, const gchar *account) { + SDEBUG (storage, "%s", account); g_signal_emit (storage, signals[RECONNECT], 0, account); } - -/** - * mcp_account_storage_owns: - * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * @account: the unique name (object-path tail) of an account - * - * Check whether @account is stored in @storage. The highest-priority - * plugin for which this function returns %TRUE is considered to be - * responsible for @account. - * - * There is a default implementation, which calls mcp_account_storage_get() - * for the well-known key "manager". - * - * Returns: %TRUE if @account is stored in @storage - * - * Since: 5.15.0 - */ -gboolean -mcp_account_storage_owns (McpAccountStorage *storage, - McpAccountManager *am, - const gchar *account) -{ - McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->owns != NULL, FALSE); - - return iface->owns (storage, am, account); -} diff --git a/mission-control-plugins/account-storage.h b/mission-control-plugins/account-storage.h index 5c111025..fd6daee1 100644 --- a/mission-control-plugins/account-storage.h +++ b/mission-control-plugins/account-storage.h @@ -30,7 +30,22 @@ G_BEGIN_DECLS #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_READONLY -1 #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_DEFAULT 0 #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_NORMAL 100 -#define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING 10000 + +typedef enum /*< flags >*/ +{ + MCP_PARAMETER_FLAG_NONE = 0 +} McpParameterFlags; + +typedef enum /*< flags >*/ +{ + MCP_ATTRIBUTE_FLAG_NONE = 0 +} McpAttributeFlags; + +typedef enum { + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED = 0, + MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED, + MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED +} McpAccountStorageSetResult; /* API for plugins to implement */ typedef struct _McpAccountStorage McpAccountStorage; @@ -58,52 +73,15 @@ struct _McpAccountStorage { }; GType mcp_account_storage_get_type (void); -/* Virtual method implementation signatures */ -typedef gboolean (*McpAccountStorageGetFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key); -typedef gboolean (*McpAccountStorageSetFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key, - const gchar *val); typedef gchar * (*McpAccountStorageCreate) ( - const McpAccountStorage *storage, - const McpAccountManager *am, + McpAccountStorage *storage, + McpAccountManager *am, const gchar *manager, const gchar *protocol, const gchar *identification, GError **error); -typedef gboolean (*McpAccountStorageDeleteFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key); -typedef GList * (*McpAccountStorageListFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am); -typedef gboolean (*McpAccountStorageCommitFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am); -typedef gboolean (*McpAccountStorageCommitOneFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account); -typedef void (*McpAccountStorageReadyFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am); -typedef void (*McpAccountStorageGetIdentifierFunc) ( - const McpAccountStorage *storage, - const gchar *account, - GValue *identifier); -typedef GHashTable * (*McpAccountStorageGetAdditionalInfoFunc) ( - const McpAccountStorage *storage, - const gchar *account); typedef TpStorageRestrictionFlags (*McpAccountStorageGetRestrictionsFunc) ( - const McpAccountStorage *storage, + McpAccountStorage *storage, const gchar *account); struct _McpAccountStorageIface @@ -115,29 +93,63 @@ struct _McpAccountStorageIface const gchar *desc; const gchar *provider; - McpAccountStorageSetFunc set; - McpAccountStorageGetFunc get; - McpAccountStorageDeleteFunc delete; - McpAccountStorageCommitFunc commit; - McpAccountStorageListFunc list; - McpAccountStorageReadyFunc ready; - McpAccountStorageCommitOneFunc commit_one; - McpAccountStorageGetIdentifierFunc get_identifier; - McpAccountStorageGetAdditionalInfoFunc get_additional_info; - McpAccountStorageGetRestrictionsFunc get_restrictions; - McpAccountStorageCreate create; - - /* Since 5.15.0 */ - gboolean (*owns) (McpAccountStorage *storage, + void (*delete_async) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*delete_finish) (McpAccountStorage *storage, + GAsyncResult *res, + GError **error); + + gboolean (*commit) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account); + + GList * (*list) (McpAccountStorage *storage, + McpAccountManager *am); + + void (*ready) (McpAccountStorage *storage, + McpAccountManager *am); + + void (*get_identifier) (McpAccountStorage *storage, + const gchar *account, + GValue *identifier); + + GHashTable * (*get_additional_info) (McpAccountStorage *storage, + const gchar *account); + + TpStorageRestrictionFlags (*get_restrictions) (McpAccountStorage *storage, + const gchar *account); + + gchar * (*create) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + const gchar *identification, + GError **error); + + GVariant *(*get_attribute) (McpAccountStorage *storage, McpAccountManager *am, - const gchar *account); - gboolean (*set_attribute) (McpAccountStorage *storage, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags); + GVariant *(*get_parameter) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags); + + McpAccountStorageSetResult (*set_attribute) (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, const gchar *attribute, GVariant *val, McpAttributeFlags flags); - gboolean (*set_parameter) (McpAccountStorage *storage, + McpAccountStorageSetResult (*set_parameter) (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, const gchar *parameter, @@ -146,74 +158,75 @@ struct _McpAccountStorageIface }; /* virtual methods */ -gint mcp_account_storage_priority (const McpAccountStorage *storage); +gint mcp_account_storage_priority (McpAccountStorage *storage); -gboolean mcp_account_storage_get (const McpAccountStorage *storage, +gchar * mcp_account_storage_create (McpAccountStorage *storage, McpAccountManager *am, - const gchar *account, - const gchar *key); - -gboolean mcp_account_storage_set (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key, - const gchar *value); - -gchar * mcp_account_storage_create (const McpAccountStorage *storage, - const McpAccountManager *am, const gchar *manager, const gchar *protocol, const gchar *identification, GError **error); -gboolean mcp_account_storage_delete (const McpAccountStorage *storage, - const McpAccountManager *am, +void mcp_account_storage_delete_async (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account, - const gchar *key); - -void mcp_account_storage_ready (const McpAccountStorage *storage, - const McpAccountManager *am); + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mcp_account_storage_delete_finish (McpAccountStorage *storage, + GAsyncResult *result, + GError **error); -gboolean -mcp_account_storage_commit (const McpAccountStorage *storage, - const McpAccountManager *am); +void mcp_account_storage_ready (McpAccountStorage *storage, + McpAccountManager *am); gboolean -mcp_account_storage_commit_one (const McpAccountStorage *storage, - const McpAccountManager *am, +mcp_account_storage_commit (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account); -GList *mcp_account_storage_list (const McpAccountStorage *storage, - const McpAccountManager *am); +GList *mcp_account_storage_list (McpAccountStorage *storage, + McpAccountManager *am); -void mcp_account_storage_get_identifier (const McpAccountStorage *storage, +void mcp_account_storage_get_identifier (McpAccountStorage *storage, const gchar *account, GValue *identifier); GHashTable *mcp_account_storage_get_additional_info ( - const McpAccountStorage *storage, + McpAccountStorage *storage, const gchar *account); TpStorageRestrictionFlags mcp_account_storage_get_restrictions ( - const McpAccountStorage *storage, + McpAccountStorage *storage, const gchar *account); -const gchar *mcp_account_storage_name (const McpAccountStorage *storage); +const gchar *mcp_account_storage_name (McpAccountStorage *storage); -const gchar *mcp_account_storage_description (const McpAccountStorage *storage); -const gchar *mcp_account_storage_provider (const McpAccountStorage *storage); +const gchar *mcp_account_storage_description (McpAccountStorage *storage); +const gchar *mcp_account_storage_provider (McpAccountStorage *storage); -gboolean mcp_account_storage_owns (McpAccountStorage *storage, - McpAccountManager *am, - const gchar *account); +GVariant *mcp_account_storage_get_attribute (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags); +GVariant *mcp_account_storage_get_parameter (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags); -gboolean mcp_account_storage_set_attribute (McpAccountStorage *storage, +McpAccountStorageSetResult mcp_account_storage_set_attribute ( + McpAccountStorage *storage, McpAccountManager *am, const gchar *account, const gchar *attribute, GVariant *value, McpAttributeFlags flags); -gboolean mcp_account_storage_set_parameter (McpAccountStorage *storage, +McpAccountStorageSetResult mcp_account_storage_set_parameter ( + McpAccountStorage *storage, McpAccountManager *am, const gchar *account, const gchar *parameter, diff --git a/mission-control-plugins/account.c b/mission-control-plugins/account.c index 1744ef6a..2c194616 100644 --- a/mission-control-plugins/account.c +++ b/mission-control-plugins/account.c @@ -74,207 +74,6 @@ mcp_account_manager_get_type (void) } /** - * mcp_account_manager_set_value: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @key: the setting whose value we wish to change: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" - * @value: the new value, escaped as if for a #GKeyFile, or %NULL to delete - * the setting/parameter - * - * Inform Mission Control that @key has changed its value to @value. - * - * This function may either be called from mcp_account_storage_get(), - * or just before emitting #McpAccountStorage::altered-one. - * - * New plugins should call mcp_account_manager_set_attribute() or - * mcp_account_manager_set_parameter() instead. - */ -void -mcp_account_manager_set_value (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key, - const gchar *value) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->set_value != NULL); - - iface->set_value (mcpa, account, key, value); -} - -/** - * mcp_account_manager_set_attribute: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @attribute: the name of an attribute, such as "DisplayName" - * @value: (allow-none): the new value, or %NULL to delete the attribute - * @flags: flags for the new value (only used if @value is non-%NULL) - * - * Inform Mission Control that @attribute has changed its value to @value. - * - * If @value is a floating reference, Mission Control will take ownership - * of it, much like g_variant_builder_add_value(). - * - * This function may either be called from mcp_account_storage_get(), - * or just before emitting #McpAccountStorage::altered-one. - */ -void -mcp_account_manager_set_attribute (const McpAccountManager *mcpa, - const gchar *account, - const gchar *attribute, - GVariant *value, - McpAttributeFlags flags) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->set_attribute != NULL); - - iface->set_attribute (mcpa, account, attribute, value, flags); -} - -/** - * mcp_account_manager_set_parameter: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @parameter: the name of a parameter, such as "account", without - * the "param-" prefix - * @value: (allow-none): the new value, or %NULL to delete the parameter - * @flags: flags for the new value (only used if @value is non-%NULL) - * - * Inform Mission Control that @parameter has changed its value to @value. - * - * If @value is a floating reference, Mission Control will take ownership - * of it, much like g_variant_builder_add_value(). - * - * This function may either be called from mcp_account_storage_get(), - * or just before emitting #McpAccountStorage::altered-one. - */ -void -mcp_account_manager_set_parameter (const McpAccountManager *mcpa, - const gchar *account, - const gchar *parameter, - GVariant *value, - McpParameterFlags flags) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->set_parameter != NULL); - - iface->set_parameter (mcpa, account, parameter, value, flags); -} - -/** - * mcp_account_manage_list_keys: - * @mcpa: a #McpAccountManager instance - * @account: the unique name of an account - * - * <!-- --> - * - * Returns: (transfer full): a list of all keys (attributes and - * "param-"-prefixed parameters) stored for @account by any plugin - */ -GStrv -mcp_account_manager_list_keys (const McpAccountManager *mcpa, - const gchar *account) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_val_if_fail (iface != NULL, NULL); - g_return_val_if_fail (iface->list_keys != NULL, NULL); - g_return_val_if_fail (account != NULL, NULL); - - return iface->list_keys (mcpa, account); -} - -/** - * mcp_account_manager_get_value: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @key: the setting whose value we wish to fetch: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" - * - * Fetch a copy of the current value of an account setting held by - * the account manager. - * - * Returns: (transfer full): the value of @key - */ -gchar * -mcp_account_manager_get_value (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_val_if_fail (iface != NULL, NULL); - g_return_val_if_fail (iface->set_value != NULL, NULL); - - return iface->get_value (mcpa, account, key); -} - -/** - * mcp_account_manager_parameter_is_secret: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @key: the constant string "param-", plus a parameter name like - * "account" or "password" - * - * Determine whether a given account parameter is secret. - * Generally this is determined by MC and passed down to plugins, - * but any #McpAccountStorage plugin may decide a parameter is - * secret, in which case the return value for this call will - * indicate that fact too. - * - * For historical reasons, this function only operates on parameters, - * but requires its argument to be prefixed with "param-". - * - * Returns: %TRUE for secret settings, %FALSE otherwise - */ -gboolean -mcp_account_manager_parameter_is_secret (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->is_secret != NULL, FALSE); - - return iface->is_secret (mcpa, account, key); -} - -/** - * mcp_account_manager_parameter_make_secret: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @key: the constant string "param-", plus a parameter name like - * "account" or "password" - * - * Flag an account setting as secret for the lifetime of this - * #McpAccountManager. For instance, this should be called if - * @key has been retrieved from gnome-keyring. - * - * For historical reasons, this function only operates on parameters, - * but requires its argument to be prefixed with "param-". - */ -void -mcp_account_manager_parameter_make_secret (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->make_secret != NULL); - - g_debug ("%s.%s should be secret", account, key); - iface->make_secret (mcpa, account, key); -} - -/** * mcp_account_manager_get_unique_name: * @mcpa: an #McpAccountManager instance * @manager: the name of the manager @@ -351,34 +150,6 @@ mcp_account_manager_identify_account_finish (McpAccountManager *mcpa, } /** - * mcp_account_manager_escape_value_from_keyfile: - * @mcpa: a #McpAccountManager - * @value: a value with a supported #GType - * - * Escape @value so it could be passed to g_key_file_set_value(). - * For instance, escaping the boolean value TRUE returns "true", - * and escaping the string value containing one space returns "\s". - * - * It is a programming error to use an unsupported type. - * The supported types are currently %G_TYPE_STRING, %G_TYPE_BOOLEAN, - * %G_TYPE_INT, %G_TYPE_UINT, %G_TYPE_INT64, %G_TYPE_UINT64, %G_TYPE_UCHAR, - * %G_TYPE_STRV, %DBUS_TYPE_G_OBJECT_PATH and %TP_ARRAY_TYPE_OBJECT_PATH_LIST. - * - * Returns: the escaped form of @value - */ -gchar * -mcp_account_manager_escape_value_for_keyfile (const McpAccountManager *mcpa, - const GValue *value) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_val_if_fail (iface != NULL, NULL); - g_return_val_if_fail (iface->escape_value_for_keyfile != NULL, NULL); - - return iface->escape_value_for_keyfile (mcpa, value); -} - -/** * mcp_account_manager_escape_variant_for_keyfile: * @mcpa: a #McpAccountManager * @variant: a #GVariant with a supported #GVariantType @@ -409,57 +180,30 @@ mcp_account_manager_escape_variant_for_keyfile (const McpAccountManager *mcpa, } /** - * mcp_account_manager_unescape_value_from_keyfile: + * mcp_account_manager_unescape_variant_from_keyfile: * @mcpa: a #McpAccountManager - * @escaped: an escaped string as returned by g_key_file_get_value() - * @value: a value to populate, with a supported #GType - * @error: used to raise an error if %FALSE is returned + * @escaped: a string that could have come from g_key_file_get_value() + * @type: the type of the variant to which to unescape * - * Attempt to interpret @escaped as a value of @value's type. - * If successful, put it in @value and return %TRUE. + * Unescape @escaped as if it had appeared in a #GKeyFile, with syntax + * appropriate for @type. * - * It is a programming error to try to escape an unsupported type. - * The supported types are currently %G_TYPE_STRING, %G_TYPE_BOOLEAN, - * %G_TYPE_INT, %G_TYPE_UINT, %G_TYPE_INT64, %G_TYPE_UINT64, %G_TYPE_UCHAR, - * %G_TYPE_STRV, %DBUS_TYPE_G_OBJECT_PATH and %TP_ARRAY_TYPE_OBJECT_PATH_LIST. + * It is a programming error to use an unsupported type. * - * Returns: %TRUE if @value was filled in + * Returns: (transfer full): the unescaped form of @escaped + * (*not* a floating reference) */ -gboolean -mcp_account_manager_unescape_value_from_keyfile (const McpAccountManager *mcpa, +GVariant * +mcp_account_manager_unescape_variant_from_keyfile ( + const McpAccountManager *mcpa, const gchar *escaped, - GValue *value, + const GVariantType *type, GError **error) { McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->unescape_value_from_keyfile != NULL, FALSE); - - return iface->unescape_value_from_keyfile (mcpa, escaped, value, error); -} - -/** - * mcp_account_manager_init_value_for_attribute: - * @mcpa: a #McpAccountManager - * @value: a zero-filled value to initialize - * @attribute: a supported Mission Control attribute - * - * If @attribute is a known Mission Control attribute, initialize @value - * with an appropriate type for @attribute and return %TRUE. Otherwise, - * return %FALSE. - * - * Returns: %TRUE if @value was initialized - */ -gboolean -mcp_account_manager_init_value_for_attribute (const McpAccountManager *mcpa, - GValue *value, - const gchar *attribute) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->init_value_for_attribute != NULL, FALSE); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->unescape_variant_from_keyfile != NULL, NULL); - return iface->init_value_for_attribute (mcpa, value, attribute); + return iface->unescape_variant_from_keyfile (mcpa, escaped, type, error); } diff --git a/mission-control-plugins/account.h b/mission-control-plugins/account.h index 4015457b..2a46c3ab 100644 --- a/mission-control-plugins/account.h +++ b/mission-control-plugins/account.h @@ -45,62 +45,21 @@ typedef struct _McpAccountManagerIface McpAccountManagerIface; GType mcp_account_manager_get_type (void) G_GNUC_CONST; -void mcp_account_manager_set_value (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key, - const gchar *value); - -void mcp_account_manager_set_attribute (const McpAccountManager *mcpa, - const gchar *account, - const gchar *attribute, - GVariant *value, - McpAttributeFlags flags); - -void mcp_account_manager_set_parameter (const McpAccountManager *mcpa, - const gchar *account, - const gchar *parameter, - GVariant *value, - McpParameterFlags flags); - -gchar * mcp_account_manager_get_value (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key); - -gboolean mcp_account_manager_parameter_is_secret (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key); - -void mcp_account_manager_parameter_make_secret (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key); - gchar * mcp_account_manager_get_unique_name (McpAccountManager *mcpa, const gchar *manager, const gchar *protocol, const gchar *identification); -GStrv mcp_account_manager_list_keys (const McpAccountManager *mcpa, - const gchar *account); - -gchar *mcp_account_manager_escape_value_for_keyfile ( - const McpAccountManager *mcpa, - const GValue *value); - gchar *mcp_account_manager_escape_variant_for_keyfile ( const McpAccountManager *mcpa, GVariant *variant); -gboolean mcp_account_manager_unescape_value_from_keyfile ( +GVariant *mcp_account_manager_unescape_variant_from_keyfile ( const McpAccountManager *mcpa, const gchar *escaped, - GValue *value, + const GVariantType *type, GError **error); -gboolean mcp_account_manager_init_value_for_attribute ( - const McpAccountManager *mcpa, - GValue *value, - const gchar *attribute); - void mcp_account_manager_identify_account_async (McpAccountManager *mcpa, const gchar *manager, const gchar *protocol, diff --git a/mission-control-plugins/implementation.h b/mission-control-plugins/implementation.h index 2ad28938..59096f93 100644 --- a/mission-control-plugins/implementation.h +++ b/mission-control-plugins/implementation.h @@ -77,57 +77,17 @@ struct _McpDispatchOperationIface { struct _McpAccountManagerIface { GTypeInterface parent; - void (*set_value) (const McpAccountManager *ma, - const gchar *acct, - const gchar *key, - const gchar *value); - - gchar * (*get_value) (const McpAccountManager *ma, - const gchar *acct, - const gchar *key); - - gboolean (*is_secret) (const McpAccountManager *ma, - const gchar *acct, - const gchar *key); - - void (* make_secret) (const McpAccountManager *ma, - const gchar *acct, - const gchar *key); - gchar * (* unique_name) (const McpAccountManager *ma, const gchar *manager, const gchar *protocol, const gchar *identification); - GStrv (* list_keys) (const McpAccountManager *ma, - const gchar *acct); - - gchar * (* escape_value_for_keyfile) (const McpAccountManager *mcpa, - const GValue *value); - - gboolean (* unescape_value_from_keyfile) (const McpAccountManager *mcpa, - const gchar *escaped, - GValue *value, - GError **error); - - gboolean (* init_value_for_attribute) (const McpAccountManager *mcpa, - GValue *value, - const gchar *attribute); - gchar * (* escape_variant_for_keyfile) (const McpAccountManager *mcpa, GVariant *variant); - - void (* set_attribute) (const McpAccountManager *mcpa, - const gchar *account, - const gchar *attribute, - GVariant *value, - McpAttributeFlags flags); - - void (* set_parameter) (const McpAccountManager *mcpa, - const gchar *account, - const gchar *parameter, - GVariant *value, - McpParameterFlags flags); + GVariant *(* unescape_variant_from_keyfile) (const McpAccountManager *mcpa, + const gchar *escaped, + const GVariantType *type, + GError **error); void (* identify_account_async) (McpAccountManager *mcpa, const gchar *manager, diff --git a/mission-control-plugins/loader.c b/mission-control-plugins/loader.c index 640d1d87..a23028b3 100644 --- a/mission-control-plugins/loader.c +++ b/mission-control-plugins/loader.c @@ -62,6 +62,21 @@ static gboolean debugging = FALSE; #endif /** + * MCP_API_VERSION_5_18: + * + * Defined to allow simple plugin implementations to support both Mission + * Control 5.16 and 5.18 plugin APIs: + * + * |[ + * #ifdef MCP_API_VERSION_5_18 + * ... // implement MC 5.18 API + * #else + * ... // implement MC 5.16 API + * #endif + * ]| + */ + +/** * mcp_set_debug: * @debug: whether to log debug output * diff --git a/mission-control-plugins/mission-control-plugins.h b/mission-control-plugins/mission-control-plugins.h index 13d87e6f..186da91d 100644 --- a/mission-control-plugins/mission-control-plugins.h +++ b/mission-control-plugins/mission-control-plugins.h @@ -25,14 +25,7 @@ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> -typedef enum { - MCP_PARAMETER_FLAG_NONE = 0, - MCP_PARAMETER_FLAG_SECRET = TP_CONN_MGR_PARAM_FLAG_SECRET -} McpParameterFlags; - -typedef enum { - MCP_ATTRIBUTE_FLAG_NONE = 0 -} McpAttributeFlags; +#define MCP_API_VERSION_5_18 (518) #define _MCP_IN_MISSION_CONTROL_PLUGINS_H #include <mission-control-plugins/account.h> diff --git a/src/mcd-account-addressing.c b/src/mcd-account-addressing.c index ec3d8f5e..c9740c78 100644 --- a/src/mcd-account-addressing.c +++ b/src/mcd-account-addressing.c @@ -48,7 +48,7 @@ addressing_set_uri_scheme_association (TpSvcAccountInterfaceAddressing *iface, g_value_init (&value, G_TYPE_STRV); if (mcd_storage_get_attribute (storage, account, MC_ACCOUNTS_KEY_URI_SCHEMES, - &value, NULL)) + G_VARIANT_TYPE_STRING_ARRAY, &value, NULL)) { schemes = g_value_get_boxed (&value); old_association = tp_strv_contains ((const gchar * const *) schemes, @@ -109,7 +109,7 @@ addressing_get_uri_schemes (TpSvcDBusProperties *iface, g_value_init (value, G_TYPE_STRV); if (!mcd_storage_get_attribute (storage, account, MC_ACCOUNTS_KEY_URI_SCHEMES, - value, NULL)) + G_VARIANT_TYPE_STRING_ARRAY, value, NULL)) { g_value_set_boxed (value, NULL); } diff --git a/src/mcd-account-manager-default.c b/src/mcd-account-manager-default.c index 85b5e4e5..e9f2bf20 100644 --- a/src/mcd-account-manager-default.c +++ b/src/mcd-account-manager-default.c @@ -47,13 +47,19 @@ typedef struct { /* owned string, parameter (without "param-") => owned string, value * parameters of unknwn type to be stored in the variant-file */ GHashTable *untyped_parameters; - /* TRUE if the entire account is pending deletion */ - gboolean pending_deletion; /* TRUE if the account doesn't really exist, but is here to stop us * loading it from a lower-priority file */ gboolean absent; + /* TRUE if this account needs saving */ + gboolean dirty; } McdDefaultStoredAccount; +static GVariant * +variant_ref0 (GVariant *v) +{ + return (v == NULL ? NULL : g_variant_ref (v)); +} + static McdDefaultStoredAccount * lookup_stored_account (McdAccountManagerDefault *self, const gchar *account) @@ -79,7 +85,6 @@ ensure_stored_account (McdAccountManagerDefault *self, g_hash_table_insert (self->accounts, g_strdup (account), sa); } - sa->pending_deletion = FALSE; sa->absent = FALSE; return sa; } @@ -156,7 +161,6 @@ mcd_account_manager_default_init (McdAccountManagerDefault *self) self->directory = account_directory_in (g_get_user_data_dir ()); self->accounts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, stored_account_free); - self->save = FALSE; self->loaded = FALSE; } @@ -166,7 +170,7 @@ mcd_account_manager_default_class_init (McdAccountManagerDefaultClass *cls) DEBUG ("mcd_account_manager_default_class_init"); } -static gboolean +static McpAccountStorageSetResult set_parameter (McpAccountStorage *self, McpAccountManager *am, const gchar *account, @@ -177,22 +181,48 @@ set_parameter (McpAccountStorage *self, McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); McdDefaultStoredAccount *sa; - sa = ensure_stored_account (amd, account); - amd->save = TRUE; + sa = lookup_stored_account (amd, account); + g_return_val_if_fail (sa != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (!sa->absent, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + + if (val == NULL) + { + gboolean changed = FALSE; + + changed = g_hash_table_remove (sa->parameters, parameter); + /* deliberately not ||= - if we removed it from parameters, we + * still want to remove it from untyped_parameters if it was there */ + changed |= g_hash_table_remove (sa->untyped_parameters, parameter); - /* remove it from all sets, then re-add it to the right one if - * non-null */ - g_hash_table_remove (sa->parameters, parameter); - g_hash_table_remove (sa->untyped_parameters, parameter); + if (!changed) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + } + else + { + GVariant *old; - if (val != NULL) - g_hash_table_insert (sa->parameters, g_strdup (parameter), - g_variant_ref (val)); + old = g_hash_table_lookup (sa->parameters, parameter); - return TRUE; + if (old != NULL && g_variant_equal (old, val)) + { + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + } + + /* We haven't checked whether it's in untyped_parameters with the + * same value - but if it is, we want to migrate it to parameters + * anyway (in order to record its type), so treat it as having + * actually changed. */ + + g_hash_table_remove (sa->untyped_parameters, parameter); + g_hash_table_insert (sa->parameters, g_strdup (parameter), + g_variant_ref (val)); + } + + sa->dirty = TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } -static gboolean +static McpAccountStorageSetResult set_attribute (McpAccountStorage *self, McpAccountManager *am, const gchar *account, @@ -201,214 +231,195 @@ set_attribute (McpAccountStorage *self, McpAttributeFlags flags) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); - McdDefaultStoredAccount *sa = ensure_stored_account (amd, account); + McdDefaultStoredAccount *sa; - amd->save = TRUE; + sa = lookup_stored_account (amd, account); + g_return_val_if_fail (sa != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (!sa->absent, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); - if (val != NULL) - g_hash_table_insert (sa->attributes, g_strdup (attribute), - g_variant_ref (val)); + if (val == NULL) + { + if (!g_hash_table_remove (sa->attributes, attribute)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + } else - g_hash_table_remove (sa->attributes, attribute); + { + GVariant *old; - return TRUE; -} + old = g_hash_table_lookup (sa->attributes, attribute); -static gboolean -_set (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key, - const gchar *val) -{ - return FALSE; + if (old != NULL && g_variant_equal (old, val)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + + g_hash_table_insert (sa->attributes, g_strdup (attribute), + g_variant_ref (val)); + } + + sa->dirty = TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } -static gboolean -get_parameter (const McpAccountStorage *self, - const McpAccountManager *am, +static GVariant * +get_attribute (McpAccountStorage *self, + McpAccountManager *am, const gchar *account, - const gchar *prefixed, - const gchar *parameter) + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); - if (parameter != NULL) - { - gchar *v = NULL; - GVariant *variant = NULL; - - if (sa == NULL || sa->absent) - return FALSE; - - variant = g_hash_table_lookup (sa->parameters, parameter); + if (flags != NULL) + *flags = 0; - if (variant != NULL) - { - mcp_account_manager_set_parameter (am, account, parameter, - variant, MCP_PARAMETER_FLAG_NONE); - return TRUE; - } - - v = g_hash_table_lookup (sa->untyped_parameters, parameter); + g_return_val_if_fail (sa != NULL, NULL); + g_return_val_if_fail (!sa->absent, NULL); - if (v == NULL) - return FALSE; - - mcp_account_manager_set_value (am, account, prefixed, v); - } - else - { - g_assert_not_reached (); - } - - return TRUE; + /* ignore @type, we store every attribute with its type anyway; MC will + * coerce values to an appropriate type if needed */ + return variant_ref0 (g_hash_table_lookup (sa->attributes, attribute)); } -static gboolean -_get (const McpAccountStorage *self, - const McpAccountManager *am, +static GVariant * +get_parameter (McpAccountStorage *self, + McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); + GVariant *variant; + gchar *str; - if (sa == NULL || sa->absent) - return FALSE; - - if (key != NULL) - { - GVariant *v = NULL; - - if (g_str_has_prefix (key, "param-")) - { - return get_parameter (self, am, account, key, key + 6); - } - - v = g_hash_table_lookup (sa->attributes, key); - - if (v == NULL) - return FALSE; - - mcp_account_manager_set_attribute (am, account, key, v, - MCP_ATTRIBUTE_FLAG_NONE); - } - else - { - GHashTableIter iter; - gpointer k, v; - - g_hash_table_iter_init (&iter, sa->attributes); + if (flags != NULL) + *flags = 0; - while (g_hash_table_iter_next (&iter, &k, &v)) - { - if (v != NULL) - mcp_account_manager_set_attribute (am, account, k, - v, MCP_ATTRIBUTE_FLAG_NONE); - } + g_return_val_if_fail (sa != NULL, NULL); + g_return_val_if_fail (!sa->absent, NULL); - g_hash_table_iter_init (&iter, sa->parameters); + variant = g_hash_table_lookup (sa->parameters, parameter); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - if (v != NULL) - mcp_account_manager_set_parameter (am, account, k, v, - MCP_PARAMETER_FLAG_NONE); - } + if (variant != NULL) + return g_variant_ref (variant); - g_hash_table_iter_init (&iter, sa->untyped_parameters); + str = g_hash_table_lookup (sa->untyped_parameters, parameter); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - if (v != NULL) - { - gchar *prefixed = g_strdup_printf ("param-%s", - (const gchar *) k); - - mcp_account_manager_set_value (am, account, prefixed, v); - g_free (prefixed); - } - } - } + if (str == NULL) + return NULL; - return TRUE; + return mcp_account_manager_unescape_variant_from_keyfile (am, + str, type, NULL); } static gchar * -_create (const McpAccountStorage *self, - const McpAccountManager *am, +_create (McpAccountStorage *self, + McpAccountManager *am, const gchar *manager, const gchar *protocol, const gchar *identification, GError **error) { + McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); gchar *unique_name; - /* See comment in plugin-account.c::_storage_create_account() before changing - * this implementation, it's more subtle than it looks */ unique_name = mcp_account_manager_get_unique_name (MCP_ACCOUNT_MANAGER (am), manager, protocol, identification); g_return_val_if_fail (unique_name != NULL, NULL); + ensure_stored_account (amd, unique_name); return unique_name; } -static gboolean -_delete (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key) +static void +delete_async (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); + GTask *task; + gchar *filename = NULL; + const gchar * const *iter; - if (sa == NULL || sa->absent) - { - /* Apparently we never had this account anyway. The plugin API - * considers this to be "success". */ - return TRUE; - } + task = g_task_new (amd, cancellable, callback, user_data); - if (key == NULL) - { - amd->save = TRUE; + g_return_if_fail (sa != NULL); + g_return_if_fail (!sa->absent); - /* flag the whole account as purged */ - sa->pending_deletion = TRUE; - g_hash_table_remove_all (sa->attributes); - g_hash_table_remove_all (sa->parameters); - g_hash_table_remove_all (sa->untyped_parameters); - } - else + filename = account_file_in (g_get_user_data_dir (), account); + + DEBUG ("Deleting account %s from %s", account, filename); + + if (g_unlink (filename) != 0) { - if (g_str_has_prefix (key, "param-")) - { - if (g_hash_table_remove (sa->parameters, key + 6)) - amd->save = TRUE; + int e = errno; - if (g_hash_table_remove (sa->untyped_parameters, key + 6)) - amd->save = TRUE; - } - else + /* ENOENT is OK, anything else is more upsetting */ + if (e != ENOENT) { - if (g_hash_table_remove (sa->attributes, key)) - amd->save = TRUE; + WARNING ("Unable to delete %s: %s", filename, + g_strerror (e)); + g_task_return_new_error (task, G_IO_ERROR, g_io_error_from_errno (e), + "Unable to delete %s: %s", filename, g_strerror (e)); + goto finally; } + } + + for (iter = g_get_system_data_dirs (); + iter != NULL && *iter != NULL; + iter++) + { + gchar *other = account_file_in (*iter, account); + gboolean other_exists = g_file_test (other, G_FILE_TEST_EXISTS); + + g_free (other); - /* if that was the last attribute or parameter, the account is gone - * too */ - if (g_hash_table_size (sa->attributes) == 0 && - g_hash_table_size (sa->untyped_parameters) == 0 && - g_hash_table_size (sa->parameters) == 0) + if (other_exists) { - sa->pending_deletion = TRUE; + GError *error = NULL; + + /* There is a lower-priority file that would provide this + * account. We can't delete a file from XDG_DATA_DIRS which + * are conceptually read-only, but we can mask it with an + * empty file (prior art: systemd) */ + if (!g_file_set_contents (filename, "", 0, &error)) + { + g_prefix_error (&error, + "Unable to save empty account file to %s: ", filename); + WARNING ("%s", error->message); + g_task_return_error (task, error); + g_free (filename); + goto finally; + } + + break; } } - return TRUE; + /* clean up the mess */ + g_hash_table_remove (amd->accounts, account); + mcp_account_storage_emit_deleted (self, account); + + g_task_return_boolean (task, TRUE); + +finally: + g_free (filename); + g_object_unref (task); +} + +static gboolean +delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); } static gboolean @@ -426,60 +437,21 @@ am_default_commit_one (McdAccountManagerDefault *self, gboolean ret; GError *error = NULL; - filename = account_file_in (g_get_user_data_dir (), account_name); - - if (sa->pending_deletion) - { - const gchar * const *iter; + g_return_val_if_fail (sa != NULL, FALSE); + g_return_val_if_fail (!sa->absent, FALSE); - DEBUG ("Deleting account %s from %s", account_name, filename); - - if (g_unlink (filename) != 0) - { - int e = errno; - - /* ENOENT is OK, anything else is more upsetting */ - if (e != ENOENT) - { - WARNING ("Unable to delete %s: %s", filename, - g_strerror (e)); - g_free (filename); - return FALSE; - } - } - - for (iter = g_get_system_data_dirs (); - iter != NULL && *iter != NULL; - iter++) - { - gchar *other = account_file_in (*iter, account_name); - gboolean other_exists = g_file_test (other, G_FILE_TEST_EXISTS); - - g_free (other); - - if (other_exists) - { - /* There is a lower-priority file that would provide this - * account. We can't delete a file from XDG_DATA_DIRS which - * are conceptually read-only, but we can mask it with an - * empty file (prior art: systemd) */ - if (!g_file_set_contents (filename, "", 0, &error)) - { - WARNING ("Unable to save empty account file to %s: %s", - filename, error->message); - g_clear_error (&error); - g_free (filename); - return FALSE; - } - - break; - } - } + if (!sa->dirty) + return TRUE; - g_free (filename); - return TRUE; + if (!mcd_ensure_directory (self->directory, &error)) + { + g_warning ("%s", error->message); + g_error_free (error); + return FALSE; } + filename = account_file_in (g_get_user_data_dir (), account_name); + DEBUG ("Saving account %s to %s", account_name, filename); g_variant_builder_init (&attrs_builder, G_VARIANT_TYPE_VARDICT); @@ -520,6 +492,7 @@ am_default_commit_one (McdAccountManagerDefault *self, if (g_file_set_contents (filename, content_text, -1, &error)) { + sa->dirty = FALSE; ret = TRUE; } else @@ -536,57 +509,19 @@ am_default_commit_one (McdAccountManagerDefault *self, } static gboolean -_commit (const McpAccountStorage *self, - const McpAccountManager *am, +_commit (McpAccountStorage *self, + McpAccountManager *am, const gchar *account) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); - gboolean all_succeeded = TRUE; - GError *error = NULL; - GHashTableIter outer; - gpointer account_p, sa_p; - - if (!amd->save) - return TRUE; - - DEBUG ("Saving accounts to %s", amd->directory); - - if (!mcd_ensure_directory (amd->directory, &error)) - { - g_warning ("%s", error->message); - g_clear_error (&error); - /* fall through anyway: writing to the files will fail, but it does - * give us a chance to commit to the keyring too */ - } - - g_hash_table_iter_init (&outer, amd->accounts); - - while (g_hash_table_iter_next (&outer, &account_p, &sa_p)) - { - if (account == NULL || !tp_strdiff (account, account_p)) - { - if (!am_default_commit_one (amd, account_p, sa_p)) - all_succeeded = FALSE; - } - } - - if (all_succeeded) - { - amd->save = FALSE; - } - - g_hash_table_iter_init (&outer, amd->accounts); + McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); - /* forget about any entirely removed accounts */ - while (g_hash_table_iter_next (&outer, NULL, &sa_p)) - { - McdDefaultStoredAccount *sa = sa_p; + g_return_val_if_fail (sa != NULL, FALSE); + g_return_val_if_fail (!sa->absent, FALSE); - if (sa->pending_deletion) - g_hash_table_iter_remove (&outer); - } + DEBUG ("Saving account %s to %s", account, amd->directory); - return all_succeeded; + return am_default_commit_one (amd, account, sa); } static gboolean @@ -633,7 +568,7 @@ am_default_load_keyfile (McdAccountManagerDefault *self, GStrv keys = g_key_file_get_keys (keyfile, account, &m, NULL); /* We're going to need to migrate this account. */ - self->save = TRUE; + sa->dirty = TRUE; for (j = 0; j < m; j++) { @@ -649,7 +584,7 @@ am_default_load_keyfile (McdAccountManagerDefault *self, } else { - const gchar *type = mcd_storage_get_attribute_type (key); + const GVariantType *type = mcd_storage_get_attribute_type (key); GVariant *variant = NULL; if (type == NULL) @@ -661,7 +596,7 @@ am_default_load_keyfile (McdAccountManagerDefault *self, else { variant = mcd_keyfile_get_variant (keyfile, - account, key, G_VARIANT_TYPE (type), &error); + account, key, type, &error); } if (variant == NULL) @@ -873,14 +808,15 @@ am_default_load_directory (McdAccountManagerDefault *self, } static GList * -_list (const McpAccountStorage *self, - const McpAccountManager *am) +_list (McpAccountStorage *self, + McpAccountManager *am) { GList *rval = NULL; McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); GHashTableIter hash_iter; gchar *migrate_from = NULL; gpointer k, v; + gboolean save = FALSE; if (!amd->loaded) { @@ -956,7 +892,7 @@ _list (const McpAccountStorage *self, if (!am_default_load_keyfile (amd, migrate_from)) tp_clear_pointer (&migrate_from, g_free); amd->loaded = TRUE; - amd->save = TRUE; + save = TRUE; } else { @@ -968,14 +904,45 @@ _list (const McpAccountStorage *self, { DEBUG ("Creating initial account data"); amd->loaded = TRUE; - amd->save = TRUE; + save = TRUE; + } + + if (!save) + { + g_hash_table_iter_init (&hash_iter, amd->accounts); + + while (g_hash_table_iter_next (&hash_iter, NULL, &v)) + { + McdDefaultStoredAccount *sa = v; + + if (sa->dirty) + { + save = TRUE; + break; + } + } } - if (amd->save) + if (save) { + gboolean all_succeeded = TRUE; + DEBUG ("Saving initial or migrated account data"); - if (_commit (self, am, NULL)) + g_hash_table_iter_init (&hash_iter, amd->accounts); + + while (g_hash_table_iter_next (&hash_iter, &k, &v)) + { + McdDefaultStoredAccount *sa = v; + + if (sa->absent) + continue; + + if (!am_default_commit_one (amd, k, v)) + all_succeeded = FALSE; + } + + if (all_succeeded) { if (migrate_from != NULL) { @@ -995,7 +962,10 @@ _list (const McpAccountStorage *self, while (g_hash_table_iter_next (&hash_iter, &k, &v)) { - rval = g_list_prepend (rval, g_strdup (k)); + McdDefaultStoredAccount *sa = v; + + if (!sa->absent) + rval = g_list_prepend (rval, g_strdup (k)); } return rval; @@ -1009,13 +979,14 @@ account_storage_iface_init (McpAccountStorageIface *iface, iface->desc = PLUGIN_DESCRIPTION; iface->priority = PLUGIN_PRIORITY; - iface->get = _get; - iface->set = _set; + iface->get_attribute = get_attribute; + iface->get_parameter = get_parameter; iface->set_attribute = set_attribute; iface->set_parameter = set_parameter; iface->create = _create; - iface->delete = _delete; - iface->commit_one = _commit; + iface->delete_async = delete_async; + iface->delete_finish = delete_finish; + iface->commit = _commit; iface->list = _list; } diff --git a/src/mcd-account-manager-default.h b/src/mcd-account-manager-default.h index e16e062f..fb2cb75e 100644 --- a/src/mcd-account-manager-default.h +++ b/src/mcd-account-manager-default.h @@ -51,7 +51,6 @@ typedef struct { GObject parent; GHashTable *accounts; gchar *directory; - gboolean save; gboolean loaded; } _McdAccountManagerDefault; diff --git a/src/mcd-account-manager.c b/src/mcd-account-manager.c index 81afa31d..5ff7bc9c 100644 --- a/src/mcd-account-manager.c +++ b/src/mcd-account-manager.c @@ -110,6 +110,7 @@ typedef struct gchar *cm_name; gchar *protocol_name; gchar *display_name; + gchar *provider; GHashTable *parameters; GHashTable *properties; McdGetAccountCb callback; @@ -133,8 +134,6 @@ enum PROP_CLIENT_FACTORY }; -static guint write_conf_id = 0; - static void register_dbus_service (McdAccountManager *account_manager); static void release_setup_lock (McdAccountManager *account_manager); static void setup_account_loaded (McdAccount *account, @@ -176,7 +175,7 @@ async_altered_one_manager_cb (McdManager *cm, static void -altered_one_cb (GObject *storage, +altered_one_cb (McpAccountStorage *storage, const gchar *account_name, const gchar *key, gpointer data) @@ -186,6 +185,7 @@ altered_one_cb (GObject *storage, McdAccount *account = NULL; McdManager *cm = NULL; const gchar *cm_name = NULL; + McpAccountStorage *its_plugin; account = mcd_account_manager_lookup_account (am, account_name); @@ -195,6 +195,18 @@ altered_one_cb (GObject *storage, return; } + its_plugin = mcd_account_get_storage_plugin (account); + + if (storage != its_plugin) + { + DEBUG ("Ignoring altered-one from plugin %s because account %s " + "belongs to %s", + mcp_account_storage_name (storage), + account_name, + mcp_account_storage_name (its_plugin)); + return; + } + /* in theory, the CM is already ready by this point, but make sure: */ cm_name = mcd_account_get_manager_name (account); @@ -277,11 +289,12 @@ created_cb (GObject *storage_plugin_obj, McdMaster *master = mcd_master_get_default (); McdManager *cm = NULL; const gchar *cm_name = NULL; + GError *error = NULL; /* actually fetch the data into our cache from the plugin: */ - if (mcd_storage_add_account_from_plugin (storage, plugin, name)) + if (mcd_storage_add_account_from_plugin (storage, plugin, name, &error)) { - account = mcd_account_new (am, name, priv->minotaur); + account = mcd_account_new (am, name, priv->minotaur, plugin); g_assert (MCD_IS_ACCOUNT (account)); lad = g_slice_new (McdLoadAccountsData); @@ -292,7 +305,8 @@ created_cb (GObject *storage_plugin_obj, } else { - /* that function already warned about it */ + WARNING ("%s", error->message); + g_clear_error (&error); goto finish; } @@ -333,6 +347,7 @@ toggled_cb (GObject *plugin, const gchar *name, gboolean on, gpointer data) McdAccountManager *manager = MCD_ACCOUNT_MANAGER (data); McdAccount *account = NULL; GError *error = NULL; + McpAccountStorage *its_plugin; account = mcd_account_manager_lookup_account (manager, name); @@ -346,6 +361,18 @@ toggled_cb (GObject *plugin, const gchar *name, gboolean on, gpointer data) return; } + its_plugin = mcd_account_get_storage_plugin (account); + + if (storage_plugin != its_plugin) + { + DEBUG ("Ignoring toggled signal from plugin %s because account %s " + "belongs to %s", + mcp_account_storage_name (storage_plugin), + name, + mcp_account_storage_name (its_plugin)); + return; + } + _mcd_account_set_enabled (account, on, FALSE, MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE, &error); @@ -381,11 +408,27 @@ reconnect_cb (GObject *plugin, const gchar *name, gpointer data) } static void -_mcd_account_delete_cb (McdAccount *account, const GError *error, gpointer data) +mcd_account_delete_debug_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { - /* no need to do anything other than release the account ref, which * - * should be the last ref we hold by the time this rolls arouns: */ - g_object_unref (account); + McdAccount *account = MCD_ACCOUNT (source); + GError *error = NULL; + + if (mcd_account_delete_finish (account, res, &error)) + { + DEBUG ("successfully deleted account %s (%s)", + mcd_account_get_unique_name (account), + (const gchar *) user_data); + } + else + { + WARNING ("could not delete account %s (%s): %s #%d: %s", + mcd_account_get_unique_name (account), + (const gchar *) user_data, + g_quark_to_string (error->domain), error->code, error->message); + g_clear_error (&error); + } } /* a backend plugin notified us that an account was vaporised: remove it */ @@ -404,14 +447,28 @@ deleted_cb (GObject *plugin, const gchar *name, gpointer data) if (account != NULL) { const gchar * object_path = mcd_account_get_object_path (account); + McpAccountStorage *its_plugin = mcd_account_get_storage_plugin ( + account); + + if (storage_plugin != its_plugin) + { + DEBUG ("Ignoring deleted signal from plugin %s because account %s " + "belongs to %s", + mcp_account_storage_name (storage_plugin), + name, + mcp_account_storage_name (its_plugin)); + return; + } g_object_ref (account); /* this unhooks the account's signal handlers */ g_hash_table_remove (manager->priv->accounts, name); tp_svc_account_manager_emit_account_removed (manager, object_path); - mcd_account_delete (account, - MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE, - _mcd_account_delete_cb, NULL); + mcd_account_delete_async (account, + MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE, + mcd_account_delete_debug_cb, + "in response to McpAccountStorage::deleted"); + g_object_unref (account); } } @@ -579,7 +636,6 @@ static void on_account_removed (McdAccount *account, McdAccountManager *account_manager) { McdAccountManagerPrivate *priv = account_manager->priv; - McdStorage *storage = priv->storage; const gchar *name, *object_path; object_path = mcd_account_get_object_path (account); @@ -589,10 +645,6 @@ on_account_removed (McdAccount *account, McdAccountManager *account_manager) name = mcd_account_get_unique_name (account); g_hash_table_remove (priv->accounts, name); - - mcd_storage_delete_account (storage, name); - mcd_account_manager_write_conf_async (account_manager, account, NULL, - NULL); } static inline void @@ -672,6 +724,7 @@ mcd_create_account_data_free (McdCreateAccountData *cad) if (cad->destroy != NULL) cad->destroy (cad->user_data); + g_free (cad->provider); g_free (cad->cm_name); g_free (cad->protocol_name); g_free (cad->display_name); @@ -722,13 +775,18 @@ complete_account_creation_finish (McdAccount *account, if (!cad->ok) { - mcd_account_delete (account, MCD_DBUS_PROP_SET_FLAG_NONE, - NULL, NULL); + mcd_account_delete_async (account, + MCD_DBUS_PROP_SET_FLAG_NONE, + mcd_account_delete_debug_cb, + "while recovering from failure to create"); tp_clear_object (&account); } - mcd_account_manager_write_conf_async (account_manager, account, NULL, - NULL); + if (account != NULL) + { + mcd_storage_commit (account_manager->priv->storage, + mcd_account_get_unique_name (account)); + } if (cad->callback != NULL) cad->callback (account_manager, account, cad->error, cad->user_data); @@ -815,10 +873,10 @@ identify_account_cb (GObject *source_object, { McdStorage *storage = MCD_STORAGE (source_object); McdCreateAccountData *cad = user_data; - const gchar *provider; gchar *id; gchar *unique_name; McdAccount *account; + McpAccountStorage *plugin; id = mcp_account_manager_identify_account_finish ( MCP_ACCOUNT_MANAGER (storage), result, &cad->error); @@ -830,12 +888,9 @@ identify_account_cb (GObject *source_object, return; } - provider = tp_asv_get_string (cad->properties, - TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER); - - unique_name = mcd_storage_create_account (storage, provider, + unique_name = mcd_storage_create_account (storage, cad->provider, cad->cm_name, cad->protocol_name, - id, &cad->error); + id, &plugin, &cad->error); if (unique_name == NULL) { @@ -858,8 +913,10 @@ identify_account_cb (GObject *source_object, cad->display_name); account = mcd_account_new (cad->account_manager, unique_name, - cad->account_manager->priv->minotaur); + cad->account_manager->priv->minotaur, + plugin); g_free (unique_name); + g_object_unref (plugin); if (G_LIKELY (account)) { @@ -902,18 +959,33 @@ _mcd_account_manager_create_account (McdAccountManager *account_manager, return; } - cad = g_slice_new (McdCreateAccountData); + cad = g_slice_new0 (McdCreateAccountData); cad->account_manager = account_manager; cad->cm_name = g_strdup (manager); cad->protocol_name = g_strdup (protocol); cad->display_name = g_strdup (display_name); cad->parameters = g_hash_table_ref (params); - cad->properties = (properties ? g_hash_table_ref (properties) : NULL); cad->callback = callback; cad->user_data = user_data; cad->destroy = destroy; cad->error = NULL; + if (properties != NULL) + { + cad->properties = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) tp_g_value_slice_free); + + tp_g_hash_table_update (cad->properties, properties, + (GBoxedCopyFunc) g_strdup, + (GBoxedCopyFunc) tp_g_value_slice_dup); + + /* special case: "construct-only" */ + cad->provider = g_strdup (tp_asv_get_string (cad->properties, + TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER)); + g_hash_table_remove (cad->properties, + TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER); + } + g_value_init (&value, TP_HASH_TYPE_STRING_VARIANT_MAP); g_value_set_static_boxed (&value, params); variant_params = dbus_g_value_build_g_variant (&value); @@ -1061,20 +1133,6 @@ properties_iface_init (TpSvcDBusPropertiesClass *iface, gpointer iface_data) #undef IMPLEMENT } -static gboolean -write_conf (gpointer userdata) -{ - McdStorage *storage = MCD_STORAGE (userdata); - - DEBUG ("called"); - g_source_remove (write_conf_id); - write_conf_id = 0; - - mcd_storage_commit (storage, NULL); - - return TRUE; -} - static void release_setup_lock (McdAccountManager *self) { @@ -1136,15 +1194,6 @@ setup_account_loaded (McdAccount *account, g_object_unref (self); } -static void -uncork_storage_plugins (McdAccountManager *account_manager) -{ - McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (account_manager); - - mcd_account_manager_write_conf_async (account_manager, NULL, NULL, NULL); - mcd_storage_ready (priv->storage); -} - typedef struct { McdAccountManager *self; @@ -1176,12 +1225,13 @@ migrate_ctx_free (MigrateCtx *ctx) static void -migrate_delete_account_cb (McdAccount *account, - const GError *error, - gpointer user_data) +migrate_delete_account_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { MigrateCtx *ctx = user_data; + mcd_account_delete_debug_cb (source, res, "after migrating it"); migrate_ctx_free (ctx); } @@ -1205,8 +1255,8 @@ migrate_create_account_cb (McdAccountManager *account_manager, DEBUG ("Account %s migrated, removing it", mcd_account_get_unique_name (ctx->account)); - mcd_account_delete (ctx->account, MCD_DBUS_PROP_SET_FLAG_NONE, - migrate_delete_account_cb, ctx); + mcd_account_delete_async (ctx->account, MCD_DBUS_PROP_SET_FLAG_NONE, + migrate_delete_account_cb, ctx); } static void @@ -1233,7 +1283,9 @@ migrate_butterfly_haze_ready (McdManager *manager, /* Parameters; the only mandatory one is 'account' */ if (!mcd_account_get_parameter_of_known_type (ctx->account, - "account", G_TYPE_STRING, + "account", + G_VARIANT_TYPE_STRING, + G_TYPE_STRING, &v, NULL)) { _mcd_account_set_enabled (ctx->account, FALSE, TRUE, @@ -1247,7 +1299,9 @@ migrate_butterfly_haze_ready (McdManager *manager, /* If MC is storing the password, let's copy that too, so Empathy * can migrate it somewhere better. */ if (mcd_account_get_parameter_of_known_type (ctx->account, - "password", G_TYPE_STRING, + "password", + G_VARIANT_TYPE_STRING, + G_TYPE_STRING, &password_v, NULL)) { g_hash_table_insert (parameters, "password", &password_v); @@ -1384,9 +1438,9 @@ _mcd_account_manager_setup (McdAccountManager *account_manager) { McdAccountManagerPrivate *priv = account_manager->priv; McdStorage *storage = priv->storage; - gchar **accounts, **name; + GHashTable *accounts; GHashTableIter iter; - gpointer v; + gpointer k, v; /* for simplicity we don't support re-entrant setup */ g_return_if_fail (priv->setup_lock == 0); @@ -1397,29 +1451,35 @@ _mcd_account_manager_setup (McdAccountManager *account_manager) list_connection_names_cb, NULL, NULL, (GObject *)account_manager); - accounts = mcd_storage_dup_accounts (storage, NULL); + accounts = mcd_storage_get_accounts (storage); + g_hash_table_iter_init (&iter, accounts); - for (name = accounts; *name != NULL; name++) + while (g_hash_table_iter_next (&iter, &k, &v)) { gboolean plausible = FALSE; const gchar *manager = NULL; const gchar *protocol = NULL; + const gchar *account_name = k; + McpAccountStorage *plugin = v; McdAccount *account = mcd_account_manager_lookup_account ( - account_manager, *name); + account_manager, account_name); if (account != NULL) { - /* FIXME: this shouldn't really happen */ - DEBUG ("already have account %p called '%s'; skipping", account, *name); + /* FIXME: can't happen? We shouldn't create any accounts before + * we got here, and there can't be any duplicates in @accounts */ + DEBUG ("already have account %p called '%s'; skipping", + account, account_name); continue; } - account = mcd_account_new (account_manager, *name, priv->minotaur); + account = mcd_account_new (account_manager, account_name, + priv->minotaur, plugin); if (G_UNLIKELY (!account)) { g_warning ("%s: account %s failed to instantiate", G_STRFUNC, - *name); + account_name); continue; } @@ -1434,7 +1494,7 @@ _mcd_account_manager_setup (McdAccountManager *account_manager) const gchar *dbg_protocol = (protocol == NULL) ? "(nil)" : protocol; g_warning ("%s: account %s has implausible manager/protocol: %s/%s", - G_STRFUNC, *name, dbg_manager, dbg_protocol); + G_STRFUNC, account_name, dbg_manager, dbg_protocol); g_object_unref (account); continue; } @@ -1445,9 +1505,18 @@ _mcd_account_manager_setup (McdAccountManager *account_manager) g_object_ref (account_manager)); g_object_unref (account); } - g_strfreev (accounts); - uncork_storage_plugins (account_manager); + /* FIXME: why do we need to commit the accounts at this point? + * It was added to uncork_storage_plugins() in 3d5b5e7a248d + * without explanation */ + g_hash_table_iter_init (&iter, account_manager->priv->accounts); + while (g_hash_table_iter_next (&iter, &k, NULL)) + { + mcd_storage_commit (storage, k); + } + + /* uncork signals from storage plugins */ + mcd_storage_ready (priv->storage); migrate_accounts (account_manager); @@ -1533,12 +1602,6 @@ _mcd_account_manager_finalize (GObject *object) { McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (object); - if (write_conf_id) - { - write_conf (priv->storage); - g_assert (write_conf_id == 0); - } - tp_clear_object (&priv->storage); g_free (priv->account_connections_dir); remove (priv->account_connections_file); @@ -1690,63 +1753,6 @@ mcd_account_manager_get_connectivity_monitor (McdAccountManager *self) return self->priv->minotaur; } -/** - * McdAccountManagerWriteConfCb: - * @account_manager: the #McdAccountManager - * @error: a set #GError on failure or %NULL if there was no error - * @user_data: user data - * - * The callback from mcd_account_manager_write_conf_async(). If the config - * writing was successful, @error will be %NULL, otherwise it will be set - * with the appropriate error. - */ - -/** - * mcd_account_manager_write_conf_async: - * @account_manager: the #McdAccountManager - * @account: the account to be written, or %NULL to flush all accounts - * @callback: a callback to be called on write success or failure - * @user_data: data to be passed to @callback - * - * Write the account manager configuration to disk. - */ -void -mcd_account_manager_write_conf_async (McdAccountManager *account_manager, - McdAccount *account, - McdAccountManagerWriteConfCb callback, - gpointer user_data) -{ - McdStorage *storage = NULL; - const gchar *account_name = NULL; - - g_return_if_fail (MCD_IS_ACCOUNT_MANAGER (account_manager)); - - storage = account_manager->priv->storage; - - if (account != NULL) - { - account_name = mcd_account_get_unique_name (account); - - DEBUG ("updating %s", account_name); - mcd_storage_commit (storage, account_name); - } - else - { - GStrv groups; - gsize n_accounts = 0; - - groups = mcd_storage_dup_accounts (storage, &n_accounts); - DEBUG ("updating all %" G_GSIZE_FORMAT " accounts", n_accounts); - - mcd_storage_commit (storage, NULL); - - g_strfreev (groups); - } - - if (callback != NULL) - callback (account_manager, NULL, user_data); -} - GHashTable * _mcd_account_manager_get_accounts (McdAccountManager *account_manager) { diff --git a/src/mcd-account-manager.h b/src/mcd-account-manager.h index 922f4896..f33c2949 100644 --- a/src/mcd-account-manager.h +++ b/src/mcd-account-manager.h @@ -66,15 +66,6 @@ McdAccountManager *mcd_account_manager_new ( TpDBusDaemon *mcd_account_manager_get_dbus_daemon (McdAccountManager *account_manager); -typedef void (McdAccountManagerWriteConfCb) (McdAccountManager *account_manager, - const GError *error, - gpointer user_data); - -void mcd_account_manager_write_conf_async (McdAccountManager *account_manager, - McdAccount *account, - McdAccountManagerWriteConfCb callback, - gpointer user_data); - McdAccount *mcd_account_manager_lookup_account (McdAccountManager *account_manager, const gchar *name); McdAccount *mcd_account_manager_lookup_account_by_path (McdAccountManager *account_manager, diff --git a/src/mcd-account.c b/src/mcd-account.c index 7b51afb8..28e3602c 100644 --- a/src/mcd-account.c +++ b/src/mcd-account.c @@ -181,6 +181,7 @@ enum PROP_DBUS_DAEMON, PROP_CONNECTIVITY_MONITOR, PROP_STORAGE, + PROP_STORAGE_PLUGIN, PROP_NAME, }; @@ -314,6 +315,20 @@ mcd_account_loaded (McdAccount *account) g_return_if_fail (!account->priv->loaded); account->priv->loaded = TRUE; + if (account->priv->invalid_reason == NULL) + { + DEBUG ("account %s is now loaded and valid", + account->priv->unique_name); + } + else + { + DEBUG ("account %s is now loaded, but not valid: %s #%d: %s", + account->priv->unique_name, + g_quark_to_string (account->priv->invalid_reason->domain), + account->priv->invalid_reason->code, + account->priv->invalid_reason->message); + } + /* invoke all the callbacks */ g_object_ref (account); @@ -378,12 +393,12 @@ _mcd_account_set_parameter (McdAccount *account, const gchar *name, McdAccountPrivate *priv = account->priv; McdStorage *storage = priv->storage; const gchar *account_name = mcd_account_get_unique_name (account); - gboolean secret = mcd_account_parameter_is_secret (account, name); - mcd_storage_set_parameter (storage, account_name, name, value, secret); + mcd_storage_set_parameter (storage, account_name, name, value); } -static GType mc_param_type (const TpConnectionManagerParam *param); +static GType mc_param_type (const TpConnectionManagerParam *param, + const GVariantType **variant_type_out); /** * mcd_account_get_parameter: @@ -406,18 +421,22 @@ mcd_account_get_parameter (McdAccount *account, const gchar *name, McdAccountPrivate *priv = account->priv; const TpConnectionManagerParam *param; GType type; + const GVariantType *variant_type; + gboolean ret; param = mcd_manager_get_protocol_param (priv->manager, priv->protocol_name, name); - type = mc_param_type (param); + type = mc_param_type (param, &variant_type); - return mcd_account_get_parameter_of_known_type (account, name, - type, parameter, error); + ret = mcd_account_get_parameter_of_known_type (account, name, + variant_type, type, parameter, error); + return ret; } gboolean mcd_account_get_parameter_of_known_type (McdAccount *account, const gchar *name, + const GVariantType *variant_type, GType type, GValue *parameter, GError **error) @@ -428,7 +447,8 @@ mcd_account_get_parameter_of_known_type (McdAccount *account, g_value_init (&tmp, type); - if (mcd_storage_get_parameter (storage, account_name, name, &tmp, error)) + if (mcd_storage_get_parameter (storage, account_name, name, variant_type, + &tmp, error)) { if (parameter != NULL) { @@ -675,16 +695,19 @@ static TpStorageRestrictionFlags mcd_account_get_storage_restrictions ( McdAccount *account); void -mcd_account_delete (McdAccount *account, - McdDBusPropSetFlags flags, - McdAccountDeleteCb callback, - gpointer user_data) +mcd_account_delete_async (McdAccount *account, + McdDBusPropSetFlags flags, + GAsyncReadyCallback callback, + gpointer user_data) { McdAccountPrivate *priv = account->priv; gchar *data_dir_str; GError *error = NULL; const gchar *name = mcd_account_get_unique_name (account); TpConnectionManager *cm = mcd_account_get_cm (account); + GTask *task; + + task = g_task_new (account, NULL, callback, user_data); /* We don't really have a flag for "cannot delete accounts" yet, but * it seems reasonable that if you can't disable it or put it @@ -695,16 +718,18 @@ mcd_account_delete (McdAccount *account, (TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_ENABLED | TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PRESENCE)) != 0) { - g_set_error (&error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, + g_task_return_new_error (task, TP_ERROR, TP_ERROR_PERMISSION_DENIED, "Storage plugin for %s does not allow deleting it", name); - callback (account, error, user_data); - g_error_free (error); + g_object_unref (task); return; } - /* if the CM implements CM.I.AccountStorage, we need to tell the CM - * to forget any account credentials it knows */ + /* If the CM implements CM.I.AccountStorage, we need to tell the CM + * to forget any account credentials it knows. + * + * FIXME: put this in the main flow rather than doing it async and + * throwing away its result? */ if (tp_proxy_has_interface_by_id (cm, MC_IFACE_QUARK_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE)) { @@ -729,12 +754,13 @@ mcd_account_delete (McdAccount *account, flags, &error)) { g_warning ("could not disable account %s (%s)", name, error->message); - callback (account, error, user_data); - g_error_free (error); + g_task_return_error (task, error); + g_object_unref (task); return; } - mcd_storage_delete_account (priv->storage, name); + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0) + mcd_storage_delete_account (priv->storage, name); data_dir_str = get_old_account_data_path (priv); @@ -761,20 +787,25 @@ mcd_account_delete (McdAccount *account, g_free (data_dir_str); } - mcd_storage_commit (priv->storage, name); - - if (callback != NULL) - callback (account, NULL, user_data); - - /* If the account was not removed via the DBus Account interface code * - * path and something is holding a ref to it so it does not get disposed, * - * then this signal may not get fired, so we make sure it _does_ here */ if (!priv->removed) { - DEBUG ("Forcing Account.Removed for %s", name); + DEBUG ("emitting Account.Removed for %s", name); priv->removed = TRUE; tp_svc_account_emit_removed (account); } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +gboolean +mcd_account_delete_finish (McdAccount *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); } void @@ -782,10 +813,29 @@ _mcd_account_load (McdAccount *account, McdAccountLoadCb callback, gpointer user_data) { if (account->priv->loaded) + { + if (account->priv->invalid_reason == NULL) + { + DEBUG ("account %s already loaded and valid", + account->priv->unique_name); + } + else + { + DEBUG ("account %s already loaded, but not valid: %s #%d: %s", + account->priv->unique_name, + g_quark_to_string (account->priv->invalid_reason->domain), + account->priv->invalid_reason->code, + account->priv->invalid_reason->message); + } + callback (account, NULL, user_data); + } else + { + DEBUG ("account %s not yet loaded", account->priv->unique_name); _mcd_object_call_when_ready (account, account_ready_quark, (McdReadyCb)callback, user_data); + } } static void @@ -1081,7 +1131,8 @@ mcd_account_get_string_val (McdAccount *account, const gchar *key, g_value_init (value, G_TYPE_STRING); - if (!mcd_storage_get_attribute (priv->storage, name, key, value, NULL)) + if (!mcd_storage_get_attribute (priv->storage, name, key, + G_VARIANT_TYPE_STRING, value, NULL)) { g_value_set_static_string (value, NULL); } @@ -2076,58 +2127,16 @@ get_supersedes (TpSvcDBusProperties *svc, g_value_set_boxed (value, self->priv->supersedes); } -static McpAccountStorage * -get_storage_plugin (McdAccount *account) -{ - McdAccountPrivate *priv = account->priv; - const gchar *account_name = mcd_account_get_unique_name (account); - - if (priv->storage_plugin != NULL) - return priv->storage_plugin; - - priv->storage_plugin = mcd_storage_get_plugin (priv->storage, account_name); - - if (priv->storage_plugin != NULL) - g_object_ref (priv->storage_plugin); - - return priv->storage_plugin; -} - static void get_storage_provider (TpSvcDBusProperties *self, const gchar *name, GValue *value) { McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); g_value_init (value, G_TYPE_STRING); - if (storage_plugin != NULL) - g_value_set_string (value, mcp_account_storage_provider (storage_plugin)); - else - g_value_set_static_string (value, ""); -} - -static gboolean -set_storage_provider (TpSvcDBusProperties *self, - const gchar *name, - const GValue *value, - McdDBusPropSetFlags flags, - GError **error) -{ - McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); - const gchar *current_provider = mcp_account_storage_provider (storage_plugin); - - if (!G_VALUE_HOLDS_STRING (value) || - tp_strdiff (g_value_get_string (value), current_provider)) - { - g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "Cannot change provider, it is defined at account creation only"); - return FALSE; - } - - return TRUE; + g_value_set_string (value, + mcp_account_storage_provider (account->priv->storage_plugin)); } static void @@ -2136,22 +2145,13 @@ get_storage_identifier (TpSvcDBusProperties *self, { McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); GValue identifier = G_VALUE_INIT; g_value_init (value, G_TYPE_VALUE); - if (storage_plugin != NULL) - { - mcp_account_storage_get_identifier ( - storage_plugin, account->priv->unique_name, &identifier); - } - else - { - g_value_init (&identifier, G_TYPE_UINT); - - g_value_set_uint (&identifier, 0); - } + mcp_account_storage_get_identifier ( + account->priv->storage_plugin, account->priv->unique_name, + &identifier); g_value_set_boxed (value, &identifier); @@ -2164,15 +2164,11 @@ get_storage_specific_info (TpSvcDBusProperties *self, { GHashTable *storage_specific_info; McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); g_value_init (value, TP_HASH_TYPE_STRING_VARIANT_MAP); - if (storage_plugin != NULL) - storage_specific_info = mcp_account_storage_get_additional_info ( - storage_plugin, account->priv->unique_name); - else - storage_specific_info = g_hash_table_new (g_str_hash, g_str_equal); + storage_specific_info = mcp_account_storage_get_additional_info ( + account->priv->storage_plugin, account->priv->unique_name); g_value_take_boxed (value, storage_specific_info); } @@ -2180,11 +2176,7 @@ get_storage_specific_info (TpSvcDBusProperties *self, static TpStorageRestrictionFlags mcd_account_get_storage_restrictions (McdAccount *self) { - McpAccountStorage *storage_plugin = get_storage_plugin (self); - - g_return_val_if_fail (storage_plugin != NULL, 0); - - return mcp_account_storage_get_restrictions (storage_plugin, + return mcp_account_storage_get_restrictions (self->priv->storage_plugin, self->priv->unique_name); } @@ -2228,7 +2220,7 @@ static const McdDBusProp account_avatar_properties[] = { }; static const McdDBusProp account_storage_properties[] = { - { "StorageProvider", set_storage_provider, get_storage_provider }, + { "StorageProvider", NULL, get_storage_provider }, { "StorageIdentifier", NULL, get_storage_identifier }, { "StorageSpecificInformation", NULL, get_storage_specific_info }, { "StorageRestrictions", NULL, get_storage_restrictions }, @@ -2366,10 +2358,13 @@ properties_iface_init (TpSvcDBusPropertiesClass *iface, gpointer iface_data) } static GType -mc_param_type (const TpConnectionManagerParam *param) +mc_param_type (const TpConnectionManagerParam *param, + const GVariantType **variant_type_out) { const gchar *dbus_signature; + *variant_type_out = NULL; + if (G_UNLIKELY (param == NULL)) return G_TYPE_INVALID; @@ -2381,37 +2376,49 @@ mc_param_type (const TpConnectionManagerParam *param) switch (dbus_signature[0]) { case DBUS_TYPE_STRING: + *variant_type_out = G_VARIANT_TYPE_STRING; return G_TYPE_STRING; case DBUS_TYPE_BYTE: + *variant_type_out = G_VARIANT_TYPE_BYTE; return G_TYPE_UCHAR; case DBUS_TYPE_INT16: case DBUS_TYPE_INT32: + *variant_type_out = G_VARIANT_TYPE_INT32; return G_TYPE_INT; case DBUS_TYPE_UINT16: case DBUS_TYPE_UINT32: + *variant_type_out = G_VARIANT_TYPE_UINT32; return G_TYPE_UINT; case DBUS_TYPE_BOOLEAN: + *variant_type_out = G_VARIANT_TYPE_BOOLEAN; return G_TYPE_BOOLEAN; case DBUS_TYPE_DOUBLE: + *variant_type_out = G_VARIANT_TYPE_DOUBLE; return G_TYPE_DOUBLE; case DBUS_TYPE_OBJECT_PATH: + *variant_type_out = G_VARIANT_TYPE_OBJECT_PATH; return DBUS_TYPE_G_OBJECT_PATH; case DBUS_TYPE_INT64: + *variant_type_out = G_VARIANT_TYPE_INT64; return G_TYPE_INT64; case DBUS_TYPE_UINT64: + *variant_type_out = G_VARIANT_TYPE_UINT64; return G_TYPE_UINT64; case DBUS_TYPE_ARRAY: if (dbus_signature[1] == DBUS_TYPE_STRING) + { + *variant_type_out = G_VARIANT_TYPE_STRING_ARRAY; return G_TYPE_STRV; + } /* other array types are not supported: * fall through the default case */ default: @@ -2428,25 +2435,24 @@ typedef struct } RemoveMethodData; static void -account_remove_delete_cb (McdAccount *account, const GError *error, +account_remove_delete_cb (GObject *source, + GAsyncResult *res, gpointer user_data) { RemoveMethodData *data = (RemoveMethodData *) user_data; + GError *error = NULL; - if (error != NULL) + if (!mcd_account_delete_finish (MCD_ACCOUNT (source), res, &error)) { dbus_g_method_return_error (data->context, (GError *) error); + g_error_free (error); return; } - if (!data->self->priv->removed) - { - data->self->priv->removed = TRUE; - tp_svc_account_emit_removed (data->self); - } + /* mcd_account_delete() is meant to have deleted it */ + g_warn_if_fail (data->self->priv->removed); tp_svc_account_return_from_remove (data->context); - g_slice_free (RemoveMethodData, data); } @@ -2461,8 +2467,8 @@ account_remove (TpSvcAccount *svc, DBusGMethodInvocation *context) data->context = context; DEBUG ("called"); - mcd_account_delete (self, MCD_DBUS_PROP_SET_FLAG_NONE, - account_remove_delete_cb, data); + mcd_account_delete_async (self, MCD_DBUS_PROP_SET_FLAG_NONE, + account_remove_delete_cb, data); } /* @@ -2489,11 +2495,13 @@ mcd_account_altered_by_plugin (McdAccount *account, const McdDBusProp *prop = NULL; GValue value = G_VALUE_INIT; GError *error = NULL; + const GVariantType *variant_type = NULL; DEBUG ("%s", name); if (tp_strdiff (name, "Parameters") && - !mcd_storage_init_value_for_attribute (&value, name)) + !mcd_storage_init_value_for_attribute (&value, name, + &variant_type)) { WARNING ("plugin wants to alter %s but I don't know what " "type that ought to be", name); @@ -2506,7 +2514,8 @@ mcd_account_altered_by_plugin (McdAccount *account, } else if (!mcd_storage_get_attribute (account->priv->storage, account->priv->unique_name, - name, &value, &error)) + name, variant_type, &value, + &error)) { WARNING ("cannot get new value of %s: %s", name, error->message); g_error_free (error); @@ -2695,6 +2704,7 @@ check_one_parameter_update (McdAccount *account, const TpConnectionManagerParam *param = tp_protocol_get_param (protocol, name); GType type; + const GVariantType *variant_type; if (param == NULL) { @@ -2704,15 +2714,18 @@ check_one_parameter_update (McdAccount *account, return FALSE; } - type = mc_param_type (param); + type = mc_param_type (param, &variant_type); if (G_VALUE_TYPE (new_value) != type) { /* FIXME: use D-Bus type names, not GType names. */ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "parameter '%s' must be of type %s, not %s", + "parameter '%s' must be of type %s ('%.*s'), not %s", tp_connection_manager_param_get_name (param), - g_type_name (type), G_VALUE_TYPE_NAME (new_value)); + g_type_name (type), + (int) g_variant_type_get_string_length (variant_type), + g_variant_type_peek_string (variant_type), + G_VALUE_TYPE_NAME (new_value)); return FALSE; } @@ -3297,7 +3310,8 @@ mcd_account_setup (McdAccount *account) g_free (priv->auto_presence_message); if (mcd_storage_get_attribute (storage, name, - MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE, &value, + MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE, + G_VARIANT_TYPE ("(uss)"), &value, NULL)) { GValueArray *va = g_value_get_boxed (&value); @@ -3369,7 +3383,9 @@ mcd_account_setup (McdAccount *account) g_ptr_array_unref (priv->supersedes); if (mcd_storage_get_attribute (storage, name, - MC_ACCOUNTS_KEY_SUPERSEDES, &value, NULL)) + MC_ACCOUNTS_KEY_SUPERSEDES, + G_VARIANT_TYPE_OBJECT_PATH_ARRAY, + &value, NULL)) { priv->supersedes = g_value_dup_boxed (&value); } @@ -3417,6 +3433,11 @@ set_property (GObject *obj, guint prop_id, priv->storage = g_value_dup_object (val); break; + case PROP_STORAGE_PLUGIN: + g_assert (priv->storage_plugin == NULL); + priv->storage_plugin = g_value_dup_object (val); + break; + case PROP_DBUS_DAEMON: g_assert (priv->dbus_daemon == NULL); priv->dbus_daemon = g_value_dup_object (val); @@ -3503,6 +3524,9 @@ _mcd_account_dispose (GObject *object) if (!self->priv->removed) { + /* this can happen in certain account-creation error paths, + * as far as I can see */ + DEBUG ("Account never emitted Removed, emitting it now"); self->priv->removed = TRUE; tp_svc_account_emit_removed (self); } @@ -3678,6 +3702,12 @@ mcd_account_class_init (McdAccountClass * klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property + (object_class, PROP_STORAGE_PLUGIN, + g_param_spec_object ("storage-plugin", "storage-plugin", + "Storage plugin", MCP_TYPE_ACCOUNT_STORAGE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", "Unique name", "Unique name", NULL, @@ -3753,7 +3783,8 @@ mcd_account_init (McdAccount *account) McdAccount * mcd_account_new (McdAccountManager *account_manager, const gchar *name, - McdConnectivityMonitor *connectivity) + McdConnectivityMonitor *connectivity, + McpAccountStorage *storage_plugin) { gpointer *obj; McdStorage *storage = mcd_account_manager_get_storage (account_manager); @@ -3761,6 +3792,7 @@ mcd_account_new (McdAccountManager *account_manager, obj = g_object_new (MCD_TYPE_ACCOUNT, "storage", storage, + "storage-plugin", storage_plugin, "dbus-daemon", dbus, "connectivity-monitor", connectivity, "name", name, @@ -3774,6 +3806,12 @@ _mcd_account_get_storage (McdAccount *account) return account->priv->storage; } +McpAccountStorage * +mcd_account_get_storage_plugin (McdAccount *account) +{ + return account->priv->storage_plugin; +} + /* * mcd_account_is_valid: * @account: the #McdAccount. @@ -5156,19 +5194,6 @@ _mcd_account_needs_dispatch (McdAccount *self) return self->priv->always_dispatch; } -gboolean -mcd_account_parameter_is_secret (McdAccount *self, const gchar *name) -{ - McdAccountPrivate *priv = self->priv; - const TpConnectionManagerParam *param; - - param = mcd_manager_get_protocol_param (priv->manager, - priv->protocol_name, name); - - return (param != NULL && - tp_connection_manager_param_is_secret (param)); -} - void _mcd_account_set_changing_presence (McdAccount *self, gboolean value) { diff --git a/src/mcd-account.h b/src/mcd-account.h index 59d8d9e3..a0b29afb 100644 --- a/src/mcd-account.h +++ b/src/mcd-account.h @@ -61,9 +61,6 @@ GQuark mcd_account_error_quark (void); typedef void (*McdAccountLoadCb) (McdAccount *account, const GError *error, gpointer user_data); -typedef void (*McdAccountDeleteCb) (McdAccount *account, - const GError *error, - gpointer user_data); struct _McdAccountClass { @@ -83,12 +80,16 @@ GType mcd_account_get_type (void); McdAccount *mcd_account_new (McdAccountManager *account_manager, const gchar *name, - McdConnectivityMonitor *minotaur); + McdConnectivityMonitor *minotaur, + McpAccountStorage *storage_plugin); -void mcd_account_delete (McdAccount *account, - McdDBusPropSetFlags flags, - McdAccountDeleteCb callback, - gpointer user_data); +void mcd_account_delete_async (McdAccount *account, + McdDBusPropSetFlags flags, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mcd_account_delete_finish (McdAccount *account, + GAsyncResult *result, + GError **error); const gchar *mcd_account_get_unique_name (McdAccount *account); const gchar *mcd_account_get_object_path (McdAccount *account); @@ -129,9 +130,6 @@ McdConnection *mcd_account_get_connection (McdAccount *account); gboolean mcd_account_check_request (McdAccount *account, GHashTable *request, GError **error); -gboolean mcd_account_parameter_is_secret (McdAccount *self, - const gchar *name); - void mcd_account_altered_by_plugin (McdAccount *account, const gchar *name); gchar * mcd_account_dup_display_name (McdAccount *self); @@ -142,6 +140,7 @@ gboolean mcd_account_get_parameter (McdAccount *account, const gchar *name, gboolean mcd_account_get_parameter_of_known_type (McdAccount *account, const gchar *name, + const GVariantType *variant_type, GType type, GValue *parameter, GError **error); @@ -161,6 +160,8 @@ void mcd_account_connection_proceed (McdAccount *account, gboolean success); void mcd_account_connection_proceed_with_reason (McdAccount *account, gboolean success, TpConnectionStatusReason reason); +McpAccountStorage *mcd_account_get_storage_plugin (McdAccount *account); + G_END_DECLS #endif diff --git a/src/mcd-storage.c b/src/mcd-storage.c index f82cb797..828d5a1b 100644 --- a/src/mcd-storage.c +++ b/src/mcd-storage.c @@ -61,39 +61,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 @@ -200,84 +172,6 @@ mcd_keyfile_escape_variant (GVariant *variant) 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; @@ -318,7 +212,7 @@ static struct { { NULL, NULL } }; -const gchar * +const GVariantType * mcd_storage_get_attribute_type (const gchar *attribute) { guint i; @@ -326,7 +220,7 @@ 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); } return NULL; @@ -334,14 +228,18 @@ mcd_storage_get_attribute_type (const gchar *attribute) 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); @@ -358,7 +256,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); @@ -373,7 +271,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; @@ -385,181 +283,6 @@ 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) -{ - McdStorage *self = MCD_STORAGE (ma); - McdStorageAccount *sa = ensure_account (self, account); - - 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); - } -} - -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); - - g_hash_table_remove (sa->parameters, parameter); - g_hash_table_remove (sa->escaped_parameters, parameter); - - if (value != NULL) - g_hash_table_insert (sa->parameters, g_strdup (parameter), - g_variant_ref_sink (value)); - - if (flags & MCP_PARAMETER_FLAG_SECRET) - { - DEBUG ("flagging %s parameter %s as secret", account, parameter); - g_hash_table_add (sa->secrets, g_strdup (parameter)); - } -} - -static void -set_value (const McpAccountManager *ma, - const gchar *account, - const gchar *key, - const gchar *value) -{ - McdStorage *self = MCD_STORAGE (ma); - McdStorageAccount *sa = ensure_account (self, account); - - if (g_str_has_prefix (key, "param-")) - { - 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)); - } - else - { - 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); - } - } -} - -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) - { - 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)); - } - - 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) -{ - McdStorageAccount *sa; - - 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)); -} - -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, const gchar *manager, @@ -606,17 +329,26 @@ identify_account_cb (TpProxy *proxy, { if (error == NULL) { + DEBUG ("identified account: %s", identification); g_task_return_pointer (task, g_strdup (identification), g_free); } - else if (g_error_matches (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED) || - g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)) + else if (g_error_matches (error, TP_ERROR, TP_ERROR_INVALID_HANDLE) || + g_error_matches (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT)) { - g_task_return_pointer (task, g_strdup (g_task_get_task_data (task)), - g_free); + /* 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)); } else { - g_task_return_error (task, g_error_copy (error)); + /* 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); } } @@ -677,8 +409,8 @@ identify_account_async (McpAccountManager *mcpa, 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; @@ -766,11 +498,9 @@ 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; @@ -781,80 +511,38 @@ mcd_storage_load (McdStorage *self) DEBUG ("listing from plugin %s [prio: %d]", pname, prio); 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; } /* @@ -874,22 +562,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; } /* @@ -916,7 +597,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); @@ -963,36 +645,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; } /* @@ -1007,52 +695,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); - - if (variant != NULL) - return mcd_storage_coerce_variant_to_value (variant, value, error); + variant = mcp_account_storage_get_parameter (plugin, ma, account, + parameter, type, NULL); - /* 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; } /* @@ -1508,7 +1186,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); @@ -1536,65 +1215,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; } /* @@ -1665,45 +1339,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; @@ -1715,8 +1368,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. @@ -1731,66 +1382,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); - - 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; - } + updated = update_storage (self, account, TRUE, parameter, new_v); - 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 * @@ -2047,6 +1662,7 @@ mcd_keyfile_set_variant (GKeyFile *keyfile, * @manager: the name of the manager * @protocol: the name of the protocol * @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 @@ -2062,10 +1678,15 @@ mcd_storage_create_account (McdStorage *self, const gchar *manager, const gchar *protocol, 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); @@ -2080,8 +1701,21 @@ mcd_storage_create_account (McdStorage *self, if (!tp_strdiff (mcp_account_storage_provider (plugin), provider)) { - return mcp_account_storage_create (plugin, ma, manager, + 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; + } } } @@ -2093,50 +1727,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, 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); } @@ -2150,6 +1764,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: @@ -2165,20 +1802,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); - - for (store = stores; store != NULL; store = g_list_next (store)) - { - McpAccountStorage *plugin = store->data; + plugin = g_hash_table_lookup (self->accounts, account); + g_return_if_fail (plugin != NULL); - 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)); } /* @@ -2192,27 +1828,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); } /* @@ -2269,40 +1899,62 @@ mcd_storage_ready (McdStorage *self) } } +static GVariant * +mcpa_unescape_variant_from_keyfile (const McpAccountManager *mcpa, + const gchar *escaped, + const GVariantType *type, + GError **error) +{ + GKeyFile *keyfile; + GVariant *ret; + + g_return_val_if_fail (escaped != NULL, NULL); + g_return_val_if_fail (type != NULL, NULL); + + 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 plugin_iface_init (McpAccountManagerIface *iface, gpointer unused G_GNUC_UNUSED) { 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->identify_account_async = identify_account_async; iface->identify_account_finish = identify_account_finish; - iface->list_keys = list_keys; - iface->escape_value_for_keyfile = mcpa_escape_value_for_keyfile; 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); + + 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)); return TRUE; } diff --git a/src/mcd-storage.h b/src/mcd-storage.h index e4408451..c2624a40 100644 --- a/src/mcd-storage.h +++ b/src/mcd-storage.h @@ -30,7 +30,7 @@ G_BEGIN_DECLS typedef struct { GObject parent; TpDBusDaemon *dbusd; - /* owned string => owned McdStorageAccount */ + /* owned string => owned McpAccountStorage */ GHashTable *accounts; } McdStorage; @@ -64,11 +64,7 @@ void mcd_storage_connect_signal (const gchar *signal, void mcd_storage_load (McdStorage *storage); -GStrv mcd_storage_dup_accounts (McdStorage *storage, gsize *n); - -GStrv mcd_storage_dup_attributes (McdStorage *storage, - const gchar *account, - gsize *n); +GHashTable *mcd_storage_get_accounts (McdStorage *storage); gboolean mcd_storage_set_string (McdStorage *storage, const gchar *account, @@ -88,14 +84,14 @@ gboolean mcd_storage_set_attribute (McdStorage *storage, gboolean mcd_storage_set_parameter (McdStorage *storage, const gchar *account, const gchar *parameter, - const GValue *value, - gboolean secret); + const GValue *value); gchar *mcd_storage_create_account (McdStorage *storage, const gchar *provider, const gchar *manager, const gchar *protocol, const gchar *identification, + McpAccountStorage **plugin_out, GError **error); void mcd_storage_delete_account (McdStorage *storage, const gchar *account); @@ -109,12 +105,14 @@ gchar *mcd_storage_dup_string (McdStorage *storage, gboolean mcd_storage_get_attribute (McdStorage *storage, const gchar *account, const gchar *attribute, + const GVariantType *type, GValue *value, GError **error); gboolean mcd_storage_get_parameter (McdStorage *storage, const gchar *account, const gchar *parameter, + const GVariantType *type, GValue *value, GError **error); @@ -133,7 +131,8 @@ G_GNUC_INTERNAL void _mcd_storage_store_connections (McdStorage *storage); gboolean mcd_storage_add_account_from_plugin (McdStorage *storage, McpAccountStorage *plugin, - const gchar *account); + const gchar *account, + GError **error); GVariant *mcd_keyfile_get_variant (GKeyFile *keyfile, const gchar *group, @@ -159,9 +158,10 @@ gboolean mcd_keyfile_unescape_value (const gchar *escaped, GValue *value, GError **error); -const gchar *mcd_storage_get_attribute_type (const gchar *attribute); +const GVariantType *mcd_storage_get_attribute_type (const gchar *attribute); gboolean mcd_storage_init_value_for_attribute (GValue *value, - const gchar *attribute); + const gchar *attribute, + const GVariantType **variant_type); G_END_DECLS diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 587d6b0c..2a96647f 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -74,8 +74,12 @@ TWISTED_BASIC_TESTS = \ # account-storage/*.py need their own instances. TWISTED_SPECIAL_BUILD_TESTS = \ account-manager/connectivity.py \ - account-storage/default-keyring-storage.py \ - account-storage/diverted-storage.py + account-storage/diverted-storage.py \ + account-storage/5-12.py \ + account-storage/5-14.py \ + account-storage/create-new.py \ + account-storage/load-keyfiles.py \ + $(NULL) # Tests that are usually too slow to run. TWISTED_SLOW_TESTS = \ @@ -105,6 +109,7 @@ TWISTED_OTHER_FILES = \ fakeconnectivity.py \ mctest.py \ servicetest.py \ + account-storage/storage_helper.py \ telepathy/clients/README \ telepathy/clients/AbiWord.client \ telepathy/clients/Logger.client \ diff --git a/tests/twisted/account-manager/auto-connect.py b/tests/twisted/account-manager/auto-connect.py index 46b13364..28cfb2ab 100644 --- a/tests/twisted/account-manager/auto-connect.py +++ b/tests/twisted/account-manager/auto-connect.py @@ -21,6 +21,7 @@ import dbus """ import os +import os.path import dbus import dbus.service @@ -65,8 +66,12 @@ def preseed(q, bus, fake_accounts_service): 'password': r'\\\\ionstorm\\\\', }) - os.makedirs(accounts_dir + '/' + account_id) - avatar_bin = open(accounts_dir + '/' + account_id + '/avatar.bin', 'w') + avatar_filename = account_id + avatar_filename = avatar_filename.replace('/', '-') + '.avatar' + avatar_filename = (os.environ['XDG_DATA_HOME'] + + '/telepathy/mission-control/' + avatar_filename) + os.makedirs(os.path.dirname(avatar_filename)) + avatar_bin = open(avatar_filename, 'w') avatar_bin.write('Deus Ex') avatar_bin.close() diff --git a/tests/twisted/account-manager/avatar-refresh.py b/tests/twisted/account-manager/avatar-refresh.py index f76d7fb6..d914c439 100644 --- a/tests/twisted/account-manager/avatar-refresh.py +++ b/tests/twisted/account-manager/avatar-refresh.py @@ -90,15 +90,6 @@ class Account(object): avatar_bin = open(avatar_filename, 'w') avatar_bin.write(local_avatar) avatar_bin.close() - elif not avatars_persist: - self.avatar_location = 'old' - # exercise migration from ~/.mission-control in a - # situation where MC should "win" - os.makedirs(accounts_dir + '/' + self.id) - avatar_bin = open( - accounts_dir + '/' + self.id + '/avatar.bin', 'w') - avatar_bin.write(local_avatar) - avatar_bin.close() else: # store it in the normal location self.avatar_location = 'home' @@ -258,18 +249,7 @@ class Account(object): self.test_migration(bus, q, conn, account_proxy) def test_migration(self, bus, q, conn, account_proxy): - if self.avatar_location == 'old': - # The avatar got migrated to the new location. - assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/' + - self.id + '/avatar.bin') - assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/fakecm') - avatar_filename = self.id - avatar_filename = avatar_filename.replace('/', '-') + '.avatar' - avatar_filename = (os.environ['XDG_DATA_HOME'] + - '/telepathy/mission-control/' + avatar_filename) - assertEquals(conn.avatar[0], ''.join(open(avatar_filename, - 'r').readlines())) - elif self.avatar_location == 'datadir' and self.winner == 'service': + if self.avatar_location == 'datadir' and self.winner == 'service': # The avatar wasn't deleted from $XDG_DATA_DIRS, but it was # overridden. assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/' + diff --git a/tests/twisted/account-manager/bad-cm.py b/tests/twisted/account-manager/bad-cm.py index 74277b46..008ac605 100644 --- a/tests/twisted/account-manager/bad-cm.py +++ b/tests/twisted/account-manager/bad-cm.py @@ -22,11 +22,12 @@ import dbus from servicetest import call_async, assertEquals, assertContains -from mctest import exec_test, AccountManager +from mctest import (exec_test, SimulatedConnectionManager, AccountManager) import constants as cs def test(q, bus, mc): am = AccountManager(bus) + simulated_cm = SimulatedConnectionManager(q, bus) def call_create(cm='fakecm', protocol='fakeprotocol', parameters=None): if parameters is None: @@ -74,5 +75,13 @@ def test(q, bus, mc): assertEquals(cs.INVALID_ARGUMENT, e.name) assertContains("password", e.message) + # Create an account that will fail IdentifyAccount + call_create(parameters={ "account": "", + "password": "ohai", + }) + e = q.expect('dbus-error', method='CreateAccount') + assertEquals(cs.INVALID_HANDLE, e.name) + assertContains("Invalid account name", e.message) + if __name__ == '__main__': exec_test(test, {}) diff --git a/tests/twisted/account-storage/5-12.py b/tests/twisted/account-storage/5-12.py new file mode 100644 index 00000000..9075d767 --- /dev/null +++ b/tests/twisted/account-storage/5-12.py @@ -0,0 +1,29 @@ +# Test for a former default account storage backend: +# ~/.mission-control/accounts.cfg, as used in MC 5.0 to 5.13.1 +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +from storage_helper import test_keyfile +from mctest import exec_test + +def test_5_12(q, bus, mc): + test_keyfile(q, bus, mc, '5.12') + +if __name__ == '__main__': + exec_test(test_5_12, {}, preload_mc=False, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/5-14.py b/tests/twisted/account-storage/5-14.py new file mode 100644 index 00000000..42deb3ec --- /dev/null +++ b/tests/twisted/account-storage/5-14.py @@ -0,0 +1,29 @@ +# Test for a former default account storage backend: +# XDG_DATA_HOME/telepathy/mission-control/accounts.cfg, as used in MC 5.14 +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +from storage_helper import test_keyfile +from mctest import exec_test + +def test_5_14(q, bus, mc): + test_keyfile(q, bus, mc, '5.14') + +if __name__ == '__main__': + exec_test(test_5_14, {}, preload_mc=False, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/create-new.py b/tests/twisted/account-storage/create-new.py new file mode 100644 index 00000000..f77624a9 --- /dev/null +++ b/tests/twisted/account-storage/create-new.py @@ -0,0 +1,136 @@ +# Test for "stringified GVariant per account" storage backend introduced in +# Mission Control 5.16, when creating a new account stored in this default +# backend +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import time +import os +import os.path +import signal + +import dbus +import dbus.service + +from servicetest import ( + EventPattern, assertEquals, + ) +from mctest import ( + exec_test, create_fakecm_account, connect_to_mc, + ) +from storage_helper import (account_store) +import constants as cs + +def test(q, bus, mc): + ctl_dir = os.environ['MC_ACCOUNT_DIR'] + old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') + newer_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') + new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert_40example_2ecom0.account') + + account_manager, properties, interfaces = connect_to_mc(q, bus, mc) + + assert properties.get('ValidAccounts') == [], \ + properties.get('ValidAccounts') + assert properties.get('InvalidAccounts') == [], \ + properties.get('InvalidAccounts') + + params = dbus.Dictionary({"account": "dontdivert@example.com", + "password": "secrecy", + "snakes": dbus.UInt32(23)}, signature='sv') + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) + + account_path = account.__dbus_object_path__ + + # Check the account is correctly created + properties = account_manager.GetAll(cs.AM, + dbus_interface=cs.PROPERTIES_IFACE) + assert properties is not None + assert properties.get('ValidAccounts') == [account_path], properties + account_path = properties['ValidAccounts'][0] + assert isinstance(account_path, dbus.ObjectPath), repr(account_path) + assert properties.get('InvalidAccounts') == [], properties + + account_iface = dbus.Interface(account, cs.ACCOUNT) + account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + + # Alter some miscellaneous r/w properties + + account_props.Set(cs.ACCOUNT, 'DisplayName', 'Work account') + account_props.Set(cs.ACCOUNT, 'Icon', 'im-jabber') + account_props.Set(cs.ACCOUNT, 'Nickname', 'Joe Bloggs') + account_props.Set(cs.ACCOUNT, 'ConnectAutomatically', True) + account_props.Set(cs.ACCOUNT, 'AutomaticPresence', + (dbus.UInt32(cs.PRESENCE_EXTENDED_AWAY), 'xa', + 'never online')) + + # .. let's check the keyfile + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(new_variant_file_name) + assert 'Joe Bloggs' in open(new_variant_file_name).read() + assertEquals("'fakecm'", account_store('get', 'variant-file', 'manager')) + assertEquals("'fakeprotocol'", account_store('get', 'variant-file', + 'protocol')) + assertEquals("'Work account'", account_store('get', 'variant-file', + 'DisplayName')) + assertEquals("'im-jabber'", account_store('get', 'variant-file', + 'Icon')) + assertEquals("'Joe Bloggs'", account_store('get', 'variant-file', + 'Nickname')) + assertEquals('true', account_store('get', 'variant-file', + 'ConnectAutomatically')) + assertEquals("(uint32 4, 'xa', 'never online')", + account_store('get', 'variant-file', 'AutomaticPresence')) + assertEquals("'dontdivert@example.com'", + account_store('get', 'variant-file', 'param-account')) + assertEquals("uint32 23", + account_store('get', 'variant-file', 'param-snakes')) + assertEquals("'secrecy'", + account_store('get', 'variant-file', 'param-password')) + + assertEquals({'password': 'secrecy', 'account': 'dontdivert@example.com', + 'snakes': 23}, account.Properties.Get(cs.ACCOUNT, 'Parameters')) + + # Delete the account + assert account_iface.Remove() is None + account_event, account_manager_event = q.expect_many( + EventPattern('dbus-signal', + path=account_path, + signal='Removed', + interface=cs.ACCOUNT, + args=[] + ), + EventPattern('dbus-signal', + path=cs.AM_PATH, + signal='AccountRemoved', + interface=cs.AM, + args=[account_path] + ), + ) + + # Check the account is correctly deleted + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert not os.path.exists(new_variant_file_name) + +if __name__ == '__main__': + exec_test(test, {}, timeout=10, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/default-keyring-storage.py b/tests/twisted/account-storage/default-keyring-storage.py deleted file mode 100644 index 27b45e51..00000000 --- a/tests/twisted/account-storage/default-keyring-storage.py +++ /dev/null @@ -1,317 +0,0 @@ -# Test for default account storage backend. -# -# Copyright (C) 2009-2010 Nokia Corporation -# Copyright (C) 2009-2010 Collabora Ltd. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA - -import time -import os -import os.path -import signal - -import dbus -import dbus.service - -from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ - call_async, assertEquals, assertContains, assertDoesNotContain -from mctest import ( - exec_test, create_fakecm_account, get_fakecm_account, connect_to_mc, - keyfile_read, tell_mc_to_die, resuscitate_mc - ) -import constants as cs - -# This doesn't escape its parameters before passing them to the shell, -# so be careful. -def account_store(op, backend, key=None, value=None, - account='fakecm/fakeprotocol/dontdivert_40example_2ecom0'): - cmd = [ '../account-store', op, backend, account ] - if key: - cmd.append(key) - if value: - cmd.append(value) - - lines = os.popen(' '.join(cmd)).read() - ret = [] - for line in lines.split('\n'): - if line.startswith('** '): - continue - - if line: - ret.append(line) - - if len(ret) > 0: - return ret[0] - else: - return None - -def test(q, bus, mc): - ctl_dir = os.environ['MC_ACCOUNT_DIR'] - old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') - newer_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], - 'telepathy', 'mission-control', 'accounts.cfg') - new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], - 'telepathy', 'mission-control', - 'fakecm-fakeprotocol-dontdivert_40example_2ecom0.account') - group = 'fakecm/fakeprotocol/dontdivert_40example_2ecom0' - - account_manager, properties, interfaces = connect_to_mc(q, bus, mc) - - assert properties.get('ValidAccounts') == [], \ - properties.get('ValidAccounts') - assert properties.get('InvalidAccounts') == [], \ - properties.get('InvalidAccounts') - - params = dbus.Dictionary({"account": "dontdivert@example.com", - "password": "secrecy", - "snakes": dbus.UInt32(23)}, signature='sv') - (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) - - account_path = account.__dbus_object_path__ - - # Check the account is correctly created - properties = account_manager.GetAll(cs.AM, - dbus_interface=cs.PROPERTIES_IFACE) - assert properties is not None - assert properties.get('ValidAccounts') == [account_path], properties - account_path = properties['ValidAccounts'][0] - assert isinstance(account_path, dbus.ObjectPath), repr(account_path) - assert properties.get('InvalidAccounts') == [], properties - - account_iface = dbus.Interface(account, cs.ACCOUNT) - account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) - - # Alter some miscellaneous r/w properties - - account_props.Set(cs.ACCOUNT, 'DisplayName', 'Work account') - account_props.Set(cs.ACCOUNT, 'Icon', 'im-jabber') - account_props.Set(cs.ACCOUNT, 'Nickname', 'Joe Bloggs') - account_props.Set(cs.ACCOUNT, 'ConnectAutomatically', True) - account_props.Set(cs.ACCOUNT, 'AutomaticPresence', - (dbus.UInt32(cs.PRESENCE_EXTENDED_AWAY), 'xa', - 'never online')) - - tell_mc_to_die(q, bus) - - # .. let's check the keyfile - assert not os.path.exists(old_key_file_name) - assert not os.path.exists(newer_key_file_name) - assert 'Joe Bloggs' in open(new_variant_file_name).read() - assertEquals("'fakecm'", account_store('get', 'variant-file', 'manager')) - assertEquals("'fakeprotocol'", account_store('get', 'variant-file', - 'protocol')) - assertEquals("'Work account'", account_store('get', 'variant-file', - 'DisplayName')) - assertEquals("'im-jabber'", account_store('get', 'variant-file', - 'Icon')) - assertEquals("'Joe Bloggs'", account_store('get', 'variant-file', - 'Nickname')) - assertEquals('true', account_store('get', 'variant-file', - 'ConnectAutomatically')) - assertEquals("(uint32 4, 'xa', 'never online')", - account_store('get', 'variant-file', 'AutomaticPresence')) - assertEquals("'dontdivert@example.com'", - account_store('get', 'variant-file', 'param-account')) - assertEquals("uint32 23", - account_store('get', 'variant-file', 'param-snakes')) - assertEquals("'secrecy'", - account_store('get', 'variant-file', 'param-password')) - - # Reactivate MC - account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - - assertEquals({'password': 'secrecy', 'account': 'dontdivert@example.com', - 'snakes': 23}, account.Properties.Get(cs.ACCOUNT, 'Parameters')) - - # Delete the account - assert account_iface.Remove() is None - account_event, account_manager_event = q.expect_many( - EventPattern('dbus-signal', - path=account_path, - signal='Removed', - interface=cs.ACCOUNT, - args=[] - ), - EventPattern('dbus-signal', - path=cs.AM_PATH, - signal='AccountRemoved', - interface=cs.AM, - args=[account_path] - ), - ) - - # Check the account is correctly deleted - assert not os.path.exists(old_key_file_name) - assert not os.path.exists(newer_key_file_name) - assert not os.path.exists(new_variant_file_name) - - # Tell MC to die, again - tell_mc_to_die(q, bus) - - low_prio_variant_file_name = os.path.join( - os.environ['XDG_DATA_DIRS'].split(':')[0], - 'telepathy', 'mission-control', - 'fakecm-fakeprotocol-dontdivert_40example_2ecom0.account') - os.makedirs(os.path.dirname(low_prio_variant_file_name), 0700) - - # This is deliberately a lower-priority location - open(low_prio_variant_file_name, 'w').write( -"""{ -'manager': <'fakecm'>, -'protocol': <'fakeprotocol'>, -'DisplayName': <'New and improved account'>, -'AutomaticPresence': <(uint32 2, 'available', '')>, -'KeyFileParameters': <{ - 'account': 'dontdivert@example.com', - 'password': 'password_in_variant_file', - 'snakes': '42' - }> -} -""") - - # This version of this account will be used - open(new_variant_file_name.replace('.account', 'priority.account'), - 'w').write("""{ -'manager': <'fakecm'>, -'protocol': <'fakeprotocol'>, -'DisplayName': <'Visible'>, -'AutomaticPresence': <(uint32 2, 'available', '')>, -'KeyFileParameters': <{'account': 'dontdivert@example.com', - 'password': 'password_in_variant_file'}> -} -""") - # This one won't, because it's "masked" by the higher-priority one - open(low_prio_variant_file_name.replace('.account', 'priority.account'), - 'w').write("""{ -'manager': <'fakecm'>, -'protocol': <'fakeprotocol'>, -'DisplayName': <'Hidden'>, -'Nickname': <'Hidden'>, -'AutomaticPresence': <(uint32 2, 'available', '')>, -'KeyFileParameters': <{'account': 'dontdivert@example.com', - 'password': 'password_in_variant_file'}> -} -""") - - # This empty file is considered to "mask" the lower-priority one - open(new_variant_file_name.replace('.account', 'masked.account'), - 'w').write('') - open(low_prio_variant_file_name.replace('.account', 'masked.account'), - 'w').write("""{ -'manager': <'fakecm'>, -'protocol': <'fakeprotocol'>, -'AutomaticPresence': <(uint32 2, 'available', '')>, -'KeyFileParameters': <{'account': 'dontdivert@example.com', - 'password': 'password_in_variant_file'}> -} -""") - - account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) - assertContains(account_path, properties['ValidAccounts']) - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - - assertEquals(42, - account.Properties.Get(cs.ACCOUNT, 'Parameters')['snakes']) - assertEquals(dbus.UInt32, - type(account.Properties.Get(cs.ACCOUNT, 'Parameters')['snakes'])) - - # Files in lower-priority XDG locations aren't copied until something - # actually changes, and they aren't deleted. - assert not os.path.exists(new_variant_file_name) - assert os.path.exists(low_prio_variant_file_name) - - # Delete the password (only), like Empathy 3.0-3.4 do when migrating - account_iface.UpdateParameters({}, ['password']) - q.expect('dbus-signal', - path=account_path, - signal='AccountPropertyChanged', - interface=cs.ACCOUNT, - predicate=(lambda e: - 'Parameters' in e.args[0]), - ) - - # test that "masking" works - assertDoesNotContain(account_path + "masked", properties['ValidAccounts']) - assertDoesNotContain(account_path + "masked", - properties['InvalidAccounts']) - - # test that priority works - assertContains(account_path + "priority", properties['ValidAccounts']) - priority_account = get_fakecm_account(bus, mc, account_path + "priority") - assertEquals('', priority_account.Properties.Get(cs.ACCOUNT, 'Nickname')) - assertEquals('Visible', - priority_account.Properties.Get(cs.ACCOUNT, 'DisplayName')) - - # test what happens when we delete an account that has a lower-priority - # "other self" - assert priority_account.Remove() is None - - # Tell MC to die yet again - tell_mc_to_die(q, bus) - - # Check the account has copied (not moved! XDG_DATA_DIRS are, - # conceptually, read-only) from the old to the new name - assert not os.path.exists(old_key_file_name) - assert not os.path.exists(newer_key_file_name) - assert os.path.exists(low_prio_variant_file_name) - assert os.path.exists(new_variant_file_name) - assert open(new_variant_file_name.replace('.account', 'masked.account'), - 'r').read() == '' - assert open(new_variant_file_name.replace('.account', 'priority.account'), - 'r').read() == '' - - pwd = account_store('get', 'variant-file', 'param-password') - assertEquals(None, pwd) - - # Write out an account configuration in the old keyfile, to test - # migration from there - os.remove(new_variant_file_name) - os.remove(new_variant_file_name.replace('.account', 'masked.account')) - os.remove(new_variant_file_name.replace('.account', 'priority.account')) - os.remove(low_prio_variant_file_name) - os.remove(low_prio_variant_file_name.replace('.account', 'masked.account')) - os.remove(low_prio_variant_file_name.replace('.account', 'priority.account')) - open(old_key_file_name, 'w').write( -r"""# Telepathy accounts -[%s] -manager=fakecm -protocol=fakeprotocol -param-account=dontdivert@example.com -DisplayName=Ye olde account -AutomaticPresence=2;available;; -""" % group) - - account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - - # This time it *does* get deleted automatically during MC startup, - # after copying its contents to the new name/format - assert not os.path.exists(old_key_file_name) - assert not os.path.exists(low_prio_variant_file_name) - assertEquals("'Ye olde account'", - account_store('get', 'variant-file', 'DisplayName')) - -if __name__ == '__main__': - ctl_dir = os.environ['MC_ACCOUNT_DIR'] - try: - os.mkdir(ctl_dir, 0700) - except OSError: - pass - exec_test(test, {}, timeout=10, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/load-keyfiles.py b/tests/twisted/account-storage/load-keyfiles.py new file mode 100644 index 00000000..dfba53b4 --- /dev/null +++ b/tests/twisted/account-storage/load-keyfiles.py @@ -0,0 +1,274 @@ +# Test for "stringified GVariant per account" storage backend introduced in +# Mission Control 5.16, when loading pre-prepared files +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import errno +import os +import os.path + +import dbus + +from servicetest import ( + assertEquals, assertContains, assertDoesNotContain, + ) +from mctest import ( + MC, exec_test, get_fakecm_account, connect_to_mc, + SimulatedConnectionManager, + ) +from storage_helper import ( + account_store, + ) +import constants as cs + +def test(q, bus, mc): + simulated_cm = SimulatedConnectionManager(q, bus) + + ctl_dir = os.environ['MC_ACCOUNT_DIR'] + old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') + newer_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') + + # We do several scenarios in one MC run, to speed up testing a bit. + scenarios = ('low', 'priority', 'masked', 'migration', 'absentcm') + + variant_file_names = {} + low_prio_variant_file_names = {} + account_paths = {} + tails = {} + + for s in scenarios: + variant_file_names[s] = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert%s_40example_2ecom0.account' + % s) + tails[s] = ('fakecm/fakeprotocol/dontdivert%s_40example_2ecom0' % s) + account_paths[s] = cs.ACCOUNT_PATH_PREFIX + tails[s] + low_prio_variant_file_names[s] = os.path.join( + os.environ['XDG_DATA_DIRS'].split(':')[0], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert%s_40example_2ecom0.account' % + s) + + try: + os.makedirs(os.path.dirname(variant_file_names[s]), 0700) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + try: + os.makedirs(os.path.dirname(low_prio_variant_file_names[s]), 0700) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + # This is deliberately a lower-priority location + open(low_prio_variant_file_names['low'], 'w').write( +"""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Account in a low-priority location'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'Parameters': <{ + 'account': <'dontdivertlow@example.com'>, + 'password': <'password_in_variant_file'>, + 'snakes': <uint32 42> + }> +} +""") + + # This is in a lower-priority location and we don't know the + # parameters' types yet + open(low_prio_variant_file_names['migration'], 'w').write( +"""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Account in a low-priority location with KeyFileParameters'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{ + 'account': 'dontdivertmigration@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + + # This is in a lower-priority location, and we don't know the + # parameters' types, and we can't learn them by asking the CM + # because it isn't installed + open(low_prio_variant_file_names['absentcm'], 'w').write( +"""{ +'manager': <'absentcm'>, +'protocol': <'absentprotocol'>, +'DisplayName': <'Account in a low-priority location with absent CM'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{ + 'account': 'dontdivertabsentcm@example.com', + 'password': 'hello', + 'snakes': '42' + }> +} +""") + + # This version of this account will be used + open(variant_file_names['priority'], 'w').write("""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Visible'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{'account': 'dontdivertpriority@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + # This one won't, because it's "masked" by the higher-priority one + open(low_prio_variant_file_names['priority'], 'w').write("""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Hidden'>, +'Nickname': <'Hidden'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{'account': 'dontdivertpriority@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + + # This empty file is considered to "mask" the lower-priority one + open(variant_file_names['masked'], 'w').write('') + open(low_prio_variant_file_names['masked'], 'w').write("""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{'account': 'dontdivert@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + + mc = MC(q, bus) + account_manager, properties, interfaces = connect_to_mc(q, bus, mc) + + for s in scenarios: + if s == 'masked': + assertDoesNotContain(account_paths[s], properties['ValidAccounts']) + assertDoesNotContain(account_paths[s], properties['InvalidAccounts']) + elif s == 'absentcm': + assertContains(account_paths[s], properties['InvalidAccounts']) + assertDoesNotContain(account_paths[s], properties['ValidAccounts']) + else: + assertContains(account_paths[s], properties['ValidAccounts']) + assertDoesNotContain(account_paths[s], properties['InvalidAccounts']) + + accounts = {} + account_ifaces = {} + + for s in scenarios: + if s != 'masked': + accounts[s] = get_fakecm_account(bus, mc, account_paths[s]) + account_ifaces[s] = dbus.Interface(accounts[s], cs.ACCOUNT) + + if s not in ('masked', 'absentcm'): + # We can't get untyped parameters if we don't know what types + # the CM gives them. + assertEquals(42, accounts[s].Properties.Get(cs.ACCOUNT, + 'Parameters')['snakes']) + assertEquals(dbus.UInt32, + type(accounts[s].Properties.Get(cs.ACCOUNT, + 'Parameters')['snakes'])) + + # Files in lower-priority XDG locations aren't copied until something + # actually changes, and they aren't deleted. + + if s == 'low': + assert os.path.exists(low_prio_variant_file_names[s]) + + # Delete the password (only), like Empathy 3.0-3.4 do when migrating. + # This results in the higher-priority file being written out. + account_ifaces['low'].UpdateParameters({}, ['password']) + q.expect('dbus-signal', + path=account_paths['low'], + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + predicate=(lambda e: + 'Parameters' in e.args[0]), + ) + # Check the account has copied (not moved! XDG_DATA_DIRS are, + # conceptually, read-only) 'low' from the old to the new name + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['low']) + assert os.path.exists(variant_file_names['low']) + + # test that priority works + assertContains(account_paths["priority"], properties['ValidAccounts']) + assertEquals('', + accounts['priority'].Properties.Get(cs.ACCOUNT, 'Nickname')) + assertEquals('Visible', + accounts['priority'].Properties.Get(cs.ACCOUNT, 'DisplayName')) + + # test what happens when we delete an account that has a lower-priority + # "other self": it becomes masked + assert accounts['priority'].Remove() is None + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['priority']) + assert os.path.exists(variant_file_names['priority']) + assert open(variant_file_names['priority'], 'r').read() == '' + assertContains('password_in_variant_file', + open(low_prio_variant_file_names['priority'], 'r').read()) + + # The masked account is still masked + assert open(variant_file_names['masked'], 'r').read() == '' + + # Teach the one that knows its CM that the 'password' is a string. + # This results in the higher-priority file being written out. + account_ifaces['migration'].UpdateParameters({'password': 'hello'}, []) + q.expect('dbus-signal', + path=account_paths['migration'], + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + predicate=(lambda e: + 'Parameters' in e.args[0]), + ) + # Check the account has copied (not moved! XDG_DATA_DIRS are, + # conceptually, read-only) 'migration' from the old to the new name + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['migration']) + assert os.path.exists(variant_file_names['migration']) + assertEquals("'hello'", account_store('get', 'variant-file', + 'param-password', account=tails['migration'])) + # Parameters whose types are still unknown are copied too, but their + # types are still unknown + assertEquals("keyfile-escaped '42'", account_store('get', 'variant-file', + 'param-snakes', account=tails['migration'])) + + # 'absentcm' is still only in the low-priority location: we can't + # known the types of its parameters + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['absentcm']) + assert not os.path.exists(variant_file_names['absentcm']) + +if __name__ == '__main__': + exec_test(test, {}, preload_mc=False, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/storage_helper.py b/tests/twisted/account-storage/storage_helper.py new file mode 100644 index 00000000..ebbe78e6 --- /dev/null +++ b/tests/twisted/account-storage/storage_helper.py @@ -0,0 +1,174 @@ +# Helper code for former default account storage backends +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import errno +import os +import os.path + +import dbus + +from servicetest import ( + assertEquals, assertContains, assertLength, + ) +from mctest import ( + exec_test, get_fakecm_account, connect_to_mc, + MC, SimulatedConnectionManager, + ) +import constants as cs + +# This doesn't escape its parameters before passing them to the shell, +# so be careful. +def account_store(op, backend, key=None, value=None, + account='fakecm/fakeprotocol/dontdivert_40example_2ecom0'): + cmd = [ '../account-store', op, backend, account ] + if key: + cmd.append(key) + if value: + cmd.append(value) + + lines = os.popen(' '.join(cmd)).read() + ret = [] + for line in lines.split('\n'): + if line.startswith('** '): + continue + + if line: + ret.append(line) + + if len(ret) > 0: + return ret[0] + else: + return None + +def test_keyfile(q, bus, mc, how_old='5.12'): + simulated_cm = SimulatedConnectionManager(q, bus) + + if how_old == '5.12': + # This is not actually ~/.mission-control, but it uses the same + # code paths. + dot_mission_control = os.environ['MC_ACCOUNT_DIR'] + old_key_file_name = os.path.join(dot_mission_control, 'accounts.cfg') + + os.makedirs(dot_mission_control + + '/fakecm/fakeprotocol/dontdivert1_40example_2ecom0') + avatar_bin = open(dot_mission_control + + '/fakecm/fakeprotocol/dontdivert1_40example_2ecom0/avatar.bin', + 'w') + avatar_bin.write('hello, world') + avatar_bin.close() + elif how_old == '5.14': + # Same format, different location. + old_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') + + # exercise override of an avatar in XDG_DATA_DIRS + avatar_dir = (os.environ['XDG_DATA_DIRS'].split(':')[0] + + '/telepathy/mission-control') + os.makedirs(avatar_dir) + avatar_bin = open(avatar_dir + + '/fakecm-fakeprotocol-dontdivert1_40example_2ecom0.avatar', + 'w') + avatar_bin.write('hello, world') + avatar_bin.close() + else: + raise AssertionError('Unsupported value for how_old') + + a1_new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert1_40example_2ecom0.account') + a1_tail = 'fakecm/fakeprotocol/dontdivert1_40example_2ecom0' + + a2_new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert2_40example_2ecom0.account') + a2_tail = 'fakecm/fakeprotocol/dontdivert2_40example_2ecom0' + + try: + os.makedirs(os.path.dirname(old_key_file_name), 0700) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + open(old_key_file_name, 'w').write( +r"""# Telepathy accounts +[%s] +manager=fakecm +protocol=fakeprotocol +param-account=dontdivert1@example.com +param-password=1 +DisplayName=First among equals +AutomaticPresence=2;available;; +AvatarMime=text/plain +avatar_token=hello, world + +[%s] +manager=fakecm +protocol=fakeprotocol +param-account=dontdivert2@example.com +param-password=2 +DisplayName=Second to none +AutomaticPresence=2;available;; +""" % (a1_tail, a2_tail)) + + mc = MC(q, bus) + account_manager, properties, interfaces = connect_to_mc(q, bus, mc) + + # During MC startup, it moved the old keyfile's contents into + # variant-based files, and deleted the old keyfile. + assert not os.path.exists(old_key_file_name) + assert os.path.exists(a1_new_variant_file_name) + assert os.path.exists(a2_new_variant_file_name) + assertEquals("'First among equals'", + account_store('get', 'variant-file', 'DisplayName', + account=a1_tail)) + assertEquals("'Second to none'", + account_store('get', 'variant-file', 'DisplayName', + account=a2_tail)) + # MC doesn't currently ensure that parameters are stored with their + # proper types. + assertEquals("keyfile-escaped 'dontdivert1@example.com'", + account_store('get', 'variant-file', 'param-account', + account=a1_tail)) + assertEquals("keyfile-escaped 'dontdivert2@example.com'", + account_store('get', 'variant-file', 'param-account', + account=a2_tail)) + + # Also, MC has both accounts in memory... + assertContains(cs.ACCOUNT_PATH_PREFIX + a1_tail, + properties['ValidAccounts']) + account = get_fakecm_account(bus, mc, cs.ACCOUNT_PATH_PREFIX + a1_tail) + assertEquals('dontdivert1@example.com', + account.Properties.Get(cs.ACCOUNT, 'Parameters')['account']) + assertEquals('First among equals', + account.Properties.Get(cs.ACCOUNT, 'DisplayName')) + assertEquals((dbus.ByteArray('hello, world'), 'text/plain'), + account.Properties.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + byte_arrays=True)) + + assertContains(cs.ACCOUNT_PATH_PREFIX + a2_tail, + properties['ValidAccounts']) + account = get_fakecm_account(bus, mc, cs.ACCOUNT_PATH_PREFIX + a2_tail) + assertEquals('dontdivert2@example.com', + account.Properties.Get(cs.ACCOUNT, 'Parameters')['account']) + assertEquals('Second to none', + account.Properties.Get(cs.ACCOUNT, 'DisplayName')) + + # ... and no other accounts. + assertLength(2, properties['ValidAccounts']) diff --git a/tests/twisted/dbus-account-plugin.c b/tests/twisted/dbus-account-plugin.c index a8a2e4df..446a2ad7 100644 --- a/tests/twisted/dbus-account-plugin.c +++ b/tests/twisted/dbus-account-plugin.c @@ -27,6 +27,8 @@ #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG(format, ...) g_debug ("%s: " format, G_STRFUNC, ##__VA_ARGS__) +#define CRITICAL(format, ...) \ + g_critical ("%s: " format, G_STRFUNC, ##__VA_ARGS__) #define TESTDOT "org.freedesktop.Telepathy.Test." #define TESTSLASH "/org/freedesktop/Telepathy/Test/" @@ -54,7 +56,7 @@ typedef struct { GHashTable *parameter_flags; /* set of strings */ GHashTable *uncommitted_parameters; - enum { UNCOMMITTED_CREATION, UNCOMMITTED_DELETION } flags; + enum { UNCOMMITTED_CREATION = 1 } flags; TpStorageRestrictionFlags restrictions; } Account; @@ -183,7 +185,6 @@ ensure_account (TestDBusAccountPlugin *self, g_hash_table_insert (self->accounts, g_strdup (account_name), account); } - account->flags &= ~UNCOMMITTED_DELETION; return account; } @@ -211,18 +212,14 @@ service_vanished_cb (GDBusConnection *bus, { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); GHashTableIter iter; - gpointer k, v; + gpointer k; self->active = FALSE; g_hash_table_iter_init (&iter, self->accounts); - while (g_hash_table_iter_next (&iter, &k, &v)) + while (g_hash_table_iter_next (&iter, &k, NULL)) { - Account *account = v; - - if ((account->flags & UNCOMMITTED_DELETION) == 0) - mcp_account_storage_emit_deleted (MCP_ACCOUNT_STORAGE (self), k); - + mcp_account_storage_emit_deleted (MCP_ACCOUNT_STORAGE (self), k); g_hash_table_iter_remove (&iter); } @@ -376,7 +373,7 @@ test_dbus_account_plugin_process_account_deletion (TestDBusAccountPlugin *self, if (account == NULL) { - g_warning ("accounts service deleted %s but we don't " + CRITICAL ("accounts service deleted %s but we don't " "have any record of that account", account_name); } else @@ -414,7 +411,7 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, if (account == NULL) { - g_warning ("accounts service altered %s but we don't " + CRITICAL ("accounts service altered %s but we don't " "have any record of that account", account_name); } else @@ -448,8 +445,6 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, g_hash_table_insert (account->attributes, g_strdup (attr), g_variant_ref (value)); - mcp_account_manager_set_attribute (self->feedback, - account_name, attr, value, flags); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, attr); @@ -473,8 +468,6 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, DEBUG ("%s deleted", attr); g_hash_table_remove (account->attributes, attr); - mcp_account_manager_set_attribute (self->feedback, - account_name, attr, NULL, 0); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, attr); @@ -525,7 +518,7 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, if (account == NULL) { - g_warning ("accounts service altered %s but we don't " + CRITICAL ("accounts service altered %s but we don't " "have any record of that account", account_name); } else @@ -557,8 +550,6 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, g_hash_table_insert (account->parameters, g_strdup (param), g_variant_ref (value)); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_parameter (self->feedback, - account_name, param, value, flags); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); @@ -586,11 +577,10 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, (stored == NULL || !g_variant_equal (value, stored))) { + g_hash_table_remove (account->parameters, param); g_hash_table_insert (account->untyped_parameters, g_strdup (param), g_strdup (escaped)); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_value (self->feedback, - account_name, key, escaped); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); @@ -620,8 +610,6 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, g_hash_table_remove (account->untyped_parameters, param); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_parameter (self->feedback, - account_name, param, NULL, 0); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); @@ -760,8 +748,8 @@ parameters_changed_cb (GDBusConnection *bus, } static GList * -test_dbus_account_plugin_list (const McpAccountStorage *storage, - const McpAccountManager *am) +test_dbus_account_plugin_list (McpAccountStorage *storage, + McpAccountManager *am) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); GError *error = NULL; @@ -870,8 +858,8 @@ test_dbus_account_plugin_list (const McpAccountStorage *storage, } static void -test_dbus_account_plugin_ready (const McpAccountStorage *storage, - const McpAccountManager *am) +test_dbus_account_plugin_ready (McpAccountStorage *storage, + McpAccountManager *am) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); @@ -885,8 +873,8 @@ test_dbus_account_plugin_ready (const McpAccountStorage *storage, } static gchar * -test_dbus_account_plugin_create (const McpAccountStorage *storage, - const McpAccountManager *am, +test_dbus_account_plugin_create (McpAccountStorage *storage, + McpAccountManager *am, const gchar *manager, const gchar *protocol, const gchar *identifier, @@ -908,218 +896,139 @@ test_dbus_account_plugin_create (const McpAccountStorage *storage, return name; } -static gboolean -test_dbus_account_plugin_delete (const McpAccountStorage *storage, - const McpAccountManager *am, +static void delete_account_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data); + +static void +test_dbus_account_plugin_delete_async (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account_name, - const gchar *key) + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GTask *task = g_task_new (self, cancellable, callback, user_data); - DEBUG ("called"); - - if (account == NULL || !self->active) - return FALSE; - - if (key == NULL) - { - account->flags |= UNCOMMITTED_DELETION; - g_hash_table_remove_all (account->attributes); - g_hash_table_remove_all (account->parameters); - g_hash_table_remove_all (account->untyped_parameters); - g_hash_table_remove_all (account->attribute_flags); - g_hash_table_remove_all (account->parameter_flags); - - account->flags &= ~UNCOMMITTED_CREATION; - g_hash_table_remove_all (account->uncommitted_attributes); - g_hash_table_remove_all (account->uncommitted_parameters); + g_task_set_task_data (task, g_strdup (user_data), g_free); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "DeferringDelete", g_variant_new_parsed ("(%o,)", account->path), - NULL); - } - else if (g_str_has_prefix (key, "param-")) - { - g_hash_table_remove (account->parameters, key + 6); - g_hash_table_remove (account->untyped_parameters, key + 6); - g_hash_table_remove (account->parameter_flags, key + 6); - g_hash_table_add (account->uncommitted_parameters, g_strdup (key + 6)); + DEBUG ("called"); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "DeferringDeleteParameter", - g_variant_new_parsed ("(%o, %s)", account->path, key + 6), NULL); - } - else - { - g_hash_table_remove (account->attributes, key); - g_hash_table_remove (account->attribute_flags, key); - g_hash_table_add (account->uncommitted_attributes, g_strdup (key)); + g_return_if_fail (self->active); + g_return_if_fail (account != NULL); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "DeferringDeleteAttribute", - g_variant_new_parsed ("(%o, %s)", account->path, key), NULL); - } + /* deletion used to be delayed, so the regression tests will expect this + * to happen - leave them unmodified for now */ + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "DeferringDelete", g_variant_new_parsed ("(%o,)", account->path), + NULL); + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "CommittingOne", g_variant_new_parsed ("(%o,)", account->path), NULL); - return TRUE; + g_dbus_connection_call (self->bus, + TEST_DBUS_ACCOUNT_SERVICE, + TEST_DBUS_ACCOUNT_SERVICE_PATH, + TEST_DBUS_ACCOUNT_SERVICE_IFACE, + "DeleteAccount", + g_variant_new_parsed ("(%s,)", account_name), + G_VARIANT_TYPE_UNIT, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* no cancellable */ + delete_account_cb, + task); } static gboolean -test_dbus_account_plugin_get (const McpAccountStorage *storage, - const McpAccountManager *am, +test_dbus_account_plugin_delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static GVariant * +test_dbus_account_plugin_get_attribute (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account_name, - const gchar *key) + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GVariant *v; - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return FALSE; - - if (key == NULL) - { - GHashTableIter iter; - gpointer k, v; - - /* get everything */ - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "GetAllKeys", - g_variant_new_parsed ("(%o,)", account->path), NULL); - - g_hash_table_iter_init (&iter, account->attributes); - - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); - - mcp_account_manager_set_value (am, account_name, k, escaped); - g_free (escaped); - } - - g_hash_table_iter_init (&iter, account->untyped_parameters); - - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *param_foo; - McpParameterFlags flags; - - param_foo = g_strdup_printf ("param-%s", (const gchar *) k); - mcp_account_manager_set_value (am, account_name, param_foo, v); + if (flags != NULL) + *flags = 0; - flags = GPOINTER_TO_UINT (g_hash_table_lookup ( - account->parameter_flags, k)); + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); - if (flags & MCP_PARAMETER_FLAG_SECRET) - mcp_account_manager_parameter_make_secret (am, account_name, - param_foo); + v = g_hash_table_lookup (account->attributes, attribute); - g_free (param_foo); - } + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "GetAttribute", + g_variant_new_parsed ("(%o, %s)", account->path, attribute), NULL); - g_hash_table_iter_init (&iter, account->parameters); + if (v != NULL) + { + return g_variant_ref (v); + } + else + { + return NULL; + } +} - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *param_foo; - guint32 flags; - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); +static GVariant * +test_dbus_account_plugin_get_parameter (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account_name, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) +{ + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); + Account *account = lookup_account (self, account_name); + GVariant *v; + const gchar *s; - param_foo = g_strdup_printf ("param-%s", (const gchar *) k); - mcp_account_manager_set_value (am, account_name, param_foo, escaped); - g_free (escaped); + if (flags != NULL) + *flags = 0; - flags = GPOINTER_TO_UINT (g_hash_table_lookup (account->parameter_flags, - k)); + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); - if (flags & MCP_PARAMETER_FLAG_SECRET) - mcp_account_manager_parameter_make_secret (am, account_name, - param_foo); + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "GetParameter", + g_variant_new_parsed ("(%o, %s)", account->path, parameter), NULL); - g_free (param_foo); - } + v = g_hash_table_lookup (account->parameters, parameter); + s = g_hash_table_lookup (account->untyped_parameters, parameter); - return TRUE; + if (v != NULL) + { + return g_variant_ref (v); } - - /* get one parameter */ - - if (g_str_has_prefix (key, "param-")) + else if (s != NULL) { - GVariant *v = g_hash_table_lookup (account->parameters, key + 6); - const gchar *s = g_hash_table_lookup (account->untyped_parameters, key + 6); - guint32 flags = GPOINTER_TO_UINT ( - g_hash_table_lookup (account->parameter_flags, key + 6)); - - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "GetParameter", - g_variant_new_parsed ("(%o, %s)", account->path, key + 6), NULL); - - if (flags & MCP_PARAMETER_FLAG_SECRET) - mcp_account_manager_parameter_make_secret (am, account_name, key); - - if (v != NULL) - { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); - - mcp_account_manager_set_value (am, account_name, key, escaped); - g_free (escaped); - } - else if (s != NULL) - { - mcp_account_manager_set_value (am, account_name, key, s); - } - else - { - return FALSE; - } + return mcp_account_manager_unescape_variant_from_keyfile (am, + s, type, NULL); } else { - GVariant *v = g_hash_table_lookup (account->attributes, key); - - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "GetAttribute", - g_variant_new_parsed ("(%o, %s)", account->path, key), NULL); - - if (v != NULL) - { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); - - mcp_account_manager_set_value (am, account_name, key, escaped); - g_free (escaped); - } - else - { - return FALSE; - } + return NULL; } - - return TRUE; -} - -static gboolean -test_dbus_account_plugin_set (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account_name, - const gchar *key, - const gchar *value) -{ - /* Now that we implement set_attribute and set_parameter, this no longer - * needs a real implementation. */ - return FALSE; } -static gboolean +static McpAccountStorageSetResult test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account_name, @@ -1129,17 +1038,40 @@ test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GVariant *old; + guint old_flags; g_return_val_if_fail (account_name != NULL, FALSE); g_return_val_if_fail (attribute != NULL, FALSE); - /* for deletions, MC would call delete() instead */ - g_return_val_if_fail (value != NULL, FALSE); DEBUG ("%s of %s", attribute, account_name); - if (!self->active || account == NULL || - (account->flags & UNCOMMITTED_DELETION)) - return FALSE; + g_return_val_if_fail (self->active, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (account != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + + if (value == NULL) + { + if (!g_hash_table_contains (account->attributes, attribute)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + + g_hash_table_remove (account->attributes, attribute); + g_hash_table_remove (account->attribute_flags, attribute); + g_hash_table_add (account->uncommitted_attributes, g_strdup (attribute)); + + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "DeferringDeleteAttribute", + g_variant_new_parsed ("(%o, %s)", account->path, attribute), NULL); + + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; + } + + old = g_hash_table_lookup (account->attributes, attribute); + old_flags = GPOINTER_TO_UINT (g_hash_table_lookup ( + account->attribute_flags, attribute)); + + if (old != NULL && g_variant_equal (old, value) && old_flags == flags) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; g_hash_table_insert (account->attributes, g_strdup (attribute), g_variant_ref (value)); @@ -1153,10 +1085,10 @@ test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, g_variant_new_parsed ("(%o, %s, %v)", account->path, attribute, value), NULL); - return TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } -static gboolean +static McpAccountStorageSetResult test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account_name, @@ -1166,17 +1098,42 @@ test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GVariant *old; + guint old_flags; g_return_val_if_fail (account_name != NULL, FALSE); g_return_val_if_fail (parameter != NULL, FALSE); - /* for deletions, MC would call delete() instead */ - g_return_val_if_fail (value != NULL, FALSE); DEBUG ("%s of %s", parameter, account_name); - if (!self->active || account == NULL || - (account->flags & UNCOMMITTED_DELETION)) - return FALSE; + g_return_val_if_fail (self->active, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (account != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + + if (value == NULL) + { + if (!g_hash_table_contains (account->parameters, parameter) && + !g_hash_table_contains (account->untyped_parameters, parameter)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + + g_hash_table_remove (account->parameters, parameter); + g_hash_table_remove (account->untyped_parameters, parameter); + g_hash_table_remove (account->parameter_flags, parameter); + g_hash_table_add (account->uncommitted_parameters, g_strdup (parameter)); + + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "DeferringDeleteParameter", + g_variant_new_parsed ("(%o, %s)", account->path, parameter), NULL); + + return TRUE; + } + + old = g_hash_table_lookup (account->parameters, parameter); + old_flags = GPOINTER_TO_UINT (g_hash_table_lookup ( + account->parameter_flags, parameter)); + + if (old != NULL && g_variant_equal (old, value) && old_flags == flags) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; g_hash_table_remove (account->untyped_parameters, parameter); g_hash_table_insert (account->parameters, g_strdup (parameter), @@ -1191,37 +1148,7 @@ test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, g_variant_new_parsed ("(%o, %s, %v)", account->path, parameter, value), NULL); - return TRUE; -} - -static gboolean -test_dbus_account_plugin_commit (const McpAccountStorage *storage, - const McpAccountManager *am) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - GHashTableIter iter; - gpointer k; - - DEBUG ("called"); - - if (!self->active) - return FALSE; - - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "CommittingAll", NULL, NULL); - - g_hash_table_iter_init (&iter, self->accounts); - - while (g_hash_table_iter_next (&iter, &k, NULL)) - { - if (!mcp_account_storage_commit_one (storage, am, k)) - { - g_warning ("declined to commit account %s", (const gchar *) k); - } - } - - return TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } static void @@ -1229,27 +1156,31 @@ delete_account_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { - AsyncData *ad = user_data; + GTask *task = user_data; GVariant *tuple; GError *error = NULL; + TestDBusAccountPlugin *self = g_task_get_source_object (task); + const gchar *account_name = g_task_get_task_data (task); - tuple = g_dbus_connection_call_finish (ad->self->bus, res, &error); + tuple = g_dbus_connection_call_finish (self->bus, res, &error); if (tuple != NULL) { - g_hash_table_remove (ad->self->accounts, ad->account_name); + /* we'll emit ::deleted when we see the signal, which probably + * already happened */ + g_hash_table_remove (self->accounts, account_name); g_variant_unref (tuple); + g_task_return_boolean (task, TRUE); } else { - g_warning ("Unable to delete account %s: %s", ad->account_name, - error->message); - g_clear_error (&error); - /* FIXME: we could roll back the deletion by claiming that - * the service re-created the account? */ + g_prefix_error (&error, "Unable to delete account %s: ", + account_name); + g_warning ("%s", error->message); + g_task_return_error (task, error); } - async_data_free (ad); + g_object_unref (task); } static void @@ -1355,12 +1286,12 @@ update_parameters_cb (GObject *source_object, } static gboolean -test_dbus_account_plugin_commit_one (const McpAccountStorage *storage, - const McpAccountManager *am, +test_dbus_account_plugin_commit (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account_name) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - Account *account = lookup_account (self, account_name); + Account *account; GHashTableIter iter; gpointer k; GVariantBuilder a_sv_builder; @@ -1370,36 +1301,15 @@ test_dbus_account_plugin_commit_one (const McpAccountStorage *storage, DEBUG ("%s", account_name); - /* MC does not call @commit_one with parameter %NULL (meaning "all accounts") - * if we also implement commit(), which, as it happens, we do */ - g_return_val_if_fail (account_name != NULL, FALSE); + account = lookup_account (self, account_name); - if (!self->active || account == NULL) - return FALSE; + g_return_val_if_fail (self->active, FALSE); + g_return_val_if_fail (account != NULL, FALSE); g_dbus_connection_emit_signal (self->bus, NULL, TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, "CommittingOne", g_variant_new_parsed ("(%o,)", account->path), NULL); - if (account->flags & UNCOMMITTED_DELETION) - { - g_dbus_connection_call (self->bus, - TEST_DBUS_ACCOUNT_SERVICE, - TEST_DBUS_ACCOUNT_SERVICE_PATH, - TEST_DBUS_ACCOUNT_SERVICE_IFACE, - "DeleteAccount", - g_variant_new_parsed ("(%s,)", account_name), - G_VARIANT_TYPE_UNIT, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, /* no cancellable */ - delete_account_cb, - async_data_new (self, account_name)); - - /* this doesn't mean we succeeded: it means we tried */ - return TRUE; - } - if (account->flags & UNCOMMITTED_CREATION) { g_dbus_connection_call (self->bus, @@ -1526,7 +1436,7 @@ test_dbus_account_plugin_commit_one (const McpAccountStorage *storage, } static void -test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, +test_dbus_account_plugin_get_identifier (McpAccountStorage *storage, const gchar *account_name, GValue *identifier) { @@ -1535,8 +1445,8 @@ test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return; + g_return_if_fail (self->active); + g_return_if_fail (account != NULL); /* Our "library-specific unique identifier" is just the object-path * as a string. */ @@ -1545,7 +1455,7 @@ test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, } static GHashTable * -test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, +test_dbus_account_plugin_get_additional_info (McpAccountStorage *storage, const gchar *account_name) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); @@ -1554,8 +1464,8 @@ test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return NULL; + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); @@ -1566,7 +1476,7 @@ test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, } static guint -test_dbus_account_plugin_get_restrictions (const McpAccountStorage *storage, +test_dbus_account_plugin_get_restrictions (McpAccountStorage *storage, const gchar *account_name) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); @@ -1574,28 +1484,12 @@ test_dbus_account_plugin_get_restrictions (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return 0; + g_return_val_if_fail (self->active, 0); + g_return_val_if_fail (account != NULL, 0); return account->restrictions; } -static gboolean -test_dbus_account_plugin_owns (McpAccountStorage *storage, - McpAccountManager *am, - const gchar *account_name) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - Account *account = lookup_account (self, account_name); - - DEBUG ("%s", account_name); - - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return FALSE; - - return TRUE; -} - static void account_storage_iface_init (McpAccountStorageIface *iface) { @@ -1604,18 +1498,17 @@ account_storage_iface_init (McpAccountStorageIface *iface) /* this should be higher priority than the diverted-keyfile one */ iface->priority = MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_NORMAL + 100; - iface->get = test_dbus_account_plugin_get; - iface->set = test_dbus_account_plugin_set; + iface->get_attribute = test_dbus_account_plugin_get_attribute; + iface->get_parameter = test_dbus_account_plugin_get_parameter; iface->set_attribute = test_dbus_account_plugin_set_attribute; iface->set_parameter = test_dbus_account_plugin_set_parameter; iface->list = test_dbus_account_plugin_list; iface->ready = test_dbus_account_plugin_ready; - iface->delete = test_dbus_account_plugin_delete; + iface->delete_async = test_dbus_account_plugin_delete_async; + iface->delete_finish = test_dbus_account_plugin_delete_finish; iface->commit = test_dbus_account_plugin_commit; - iface->commit_one = test_dbus_account_plugin_commit_one; iface->get_identifier = test_dbus_account_plugin_get_identifier; iface->get_additional_info = test_dbus_account_plugin_get_additional_info; iface->get_restrictions = test_dbus_account_plugin_get_restrictions; iface->create = test_dbus_account_plugin_create; - iface->owns = test_dbus_account_plugin_owns; } diff --git a/tests/twisted/mc-debug-server.c b/tests/twisted/mc-debug-server.c index 48f02484..559f3f58 100644 --- a/tests/twisted/mc-debug-server.c +++ b/tests/twisted/mc-debug-server.c @@ -154,6 +154,7 @@ main (int argc, char **argv) { GError *error = NULL; GDBusConnection *gdbus = NULL; + GDBusConnection *gdbus_system = NULL; DBusConnection *connection = NULL; int ret = 1; GMainLoop *teardown_loop; @@ -174,26 +175,18 @@ main (int argc, char **argv) G_LOG_FATAL_MASK | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); gdbus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - - if (gdbus == NULL) - { - g_warning ("%s", error->message); - g_error_free (error); - error = NULL; - goto out; - } - + g_assert_no_error (error); + g_assert (gdbus != NULL); g_dbus_connection_set_exit_on_close (gdbus, FALSE); - bus_daemon = tp_dbus_daemon_dup (&error); + gdbus_system = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + g_assert_no_error (error); + g_assert (gdbus_system != NULL); + g_dbus_connection_set_exit_on_close (gdbus_system, FALSE); - if (bus_daemon == NULL) - { - g_warning ("%s", error->message); - g_error_free (error); - error = NULL; - goto out; - } + bus_daemon = tp_dbus_daemon_dup (&error); + g_assert_no_error (error); + g_assert (bus_daemon != NULL); /* It appears that dbus-glib registers a filter that wrongly returns * DBUS_HANDLER_RESULT_HANDLED for signals, so for *our* filter to have any @@ -230,14 +223,13 @@ main (int argc, char **argv) g_main_loop_run (teardown_loop); -out: - if (connection != NULL) { dbus_connection_flush (connection); } tp_clear_object (&gdbus); + tp_clear_object (&gdbus_system); tp_clear_object (&bus_daemon); dbus_shutdown (); diff --git a/tests/twisted/mcp-account-diversion.c b/tests/twisted/mcp-account-diversion.c index 923f51b4..466d3ee3 100644 --- a/tests/twisted/mcp-account-diversion.c +++ b/tests/twisted/mcp-account-diversion.c @@ -111,109 +111,201 @@ _create_config (void) DEBUG ("created %s", file); } -static gboolean -_set (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key, - const gchar *val) +static McpAccountStorageSetResult +_set (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *key, + GVariant *val, + McpParameterFlags flags) { AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *val_str; + gboolean changed; if (g_str_has_prefix (account, DONT_DIVERT)) - return FALSE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED; - adp->save = TRUE; - g_key_file_set_value (adp->keyfile, account, key, val); - - return TRUE; -} + if (val == NULL) + { + gsize n; + GStrv keys; -static gboolean -_get (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key) -{ - AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + if (g_key_file_remove_key (adp->keyfile, account, key, NULL)) + { + adp->save = TRUE; + changed = TRUE; + } - if (key != NULL) - { - gchar *v = g_key_file_get_value (adp->keyfile, account, key, NULL); + keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); - if (v == NULL) - return FALSE; + if (keys == NULL || n == 0) + g_key_file_remove_group (adp->keyfile, account, NULL); - mcp_account_manager_set_value (am, account, key, v); - g_free (v); + g_strfreev (keys); } else { - gsize i; - gsize n; - GStrv keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); + gchar *old; - if (keys == NULL) - n = 0; + val_str = mcp_account_manager_escape_variant_for_keyfile (am, val); - for (i = 0; i < n; i++) - { - gchar *v = g_key_file_get_value (adp->keyfile, account, keys[i], NULL); + old = g_key_file_get_value (adp->keyfile, account, key, NULL); - if (v != NULL) - mcp_account_manager_set_value (am, account, keys[i], v); - - g_free (v); + if (tp_strdiff (old, val_str)) + { + g_key_file_set_value (adp->keyfile, account, key, val_str); + adp->save = TRUE; + changed = TRUE; } - g_strfreev (keys); + g_free (val_str); + g_free (old); } - return TRUE; + if (changed) + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; + else + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; } -static gboolean -_delete (const McpAccountStorage *self, - const McpAccountManager *am, +static McpAccountStorageSetResult +_set_attribute (McpAccountStorage *self, + McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *attribute, + GVariant *val, + McpAttributeFlags flags) +{ + return _set (self, am, account, attribute, val, flags); +} + +static McpAccountStorageSetResult +_set_parameter (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + GVariant *val, + McpParameterFlags flags) +{ + gchar *param = g_strdup_printf ("param-%s", parameter); + gboolean ret; + + ret = _set (self, am, account, param, val, flags); + g_free (param); + + return ret; +} + +static GVariant * +_get_attribute (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) { AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *v; + GVariant *ret; - if (key == NULL) - { - if (g_key_file_remove_group (adp->keyfile, account, NULL)) - adp->save = TRUE; - } - else - { - gsize n; - GStrv keys; + if (flags != NULL) + *flags = 0; - if (g_key_file_remove_key (adp->keyfile, account, key, NULL)) - adp->save = TRUE; + v = g_key_file_get_value (adp->keyfile, account, attribute, NULL); - keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); + if (v == NULL) + return NULL; - if (keys == NULL || n == 0) - g_key_file_remove_group (adp->keyfile, account, NULL); + ret = mcp_account_manager_unescape_variant_from_keyfile (am, v, type, NULL); + g_free (v); + return ret; +} - g_strfreev (keys); +static GVariant * +_get_parameter (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) +{ + AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *key; + gchar *v; + GVariant *ret; + + if (flags != NULL) + *flags = 0; + + key = g_strdup_printf ("param-%s", parameter); + v = g_key_file_get_value (adp->keyfile, account, key, NULL); + g_free (key); + + if (v == NULL) + return NULL; + + ret = mcp_account_manager_unescape_variant_from_keyfile (am, v, type, NULL); + g_free (v); + return ret; +} + +static gboolean _commit (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account_name); + +static void +delete_async (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + GTask *task = g_task_new (adp, cancellable, callback, user_data); + + if (g_key_file_remove_group (adp->keyfile, account, NULL)) + adp->save = TRUE; + + if (_commit (self, am, account)) + { + mcp_account_storage_emit_deleted (self, account); + g_task_return_boolean (task, TRUE); + } + else + { + g_task_return_new_error (task, TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "_commit()'s error handling is not good enough to know why " + "I couldn't commit the deletion of %s", account); } - return TRUE; + g_object_unref (task); } +static gboolean +delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} static gboolean -_commit (const McpAccountStorage *self, - const McpAccountManager *am) +_commit (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account_name G_GNUC_UNUSED) { gsize n; gchar *data; AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); gboolean rval = FALSE; + /* This simple implementation ignores account_name and commits everything: + * we're writing out the whole keyfile anyway. If MC is looping over + * accounts, the second and subsequent accounts will find that + * adp->save is false, so there's no write-amplification. */ + if (!adp->save) return TRUE; @@ -229,8 +321,8 @@ _commit (const McpAccountStorage *self, } static GList * -_list (const McpAccountStorage *self, - const McpAccountManager *am) +_list (McpAccountStorage *self, + McpAccountManager *am) { gsize i; gsize n; @@ -255,6 +347,35 @@ _list (const McpAccountStorage *self, return rval; } +static gchar * +create (McpAccountStorage *self, + McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + const gchar *identification, + GError **error) +{ + gchar *unique_name; + + unique_name = mcp_account_manager_get_unique_name (MCP_ACCOUNT_MANAGER (am), + manager, protocol, + identification); + + g_return_val_if_fail (unique_name != NULL, NULL); + + if (g_str_has_prefix (unique_name, DONT_DIVERT)) + { + g_free (unique_name); + return NULL; + } + + /* No need to actually create anything: we'll happily return values + * from get(., ., ., NULL) regardless of whether we have that account + * in our list */ + + return unique_name; +} + static void account_storage_iface_init (McpAccountStorageIface *iface, gpointer unused G_GNUC_UNUSED) @@ -263,11 +384,15 @@ account_storage_iface_init (McpAccountStorageIface *iface, iface->desc = PLUGIN_DESCRIPTION; iface->priority = PLUGIN_PRIORITY; - iface->get = _get; - iface->set = _set; - iface->delete = _delete; + iface->get_attribute = _get_attribute; + iface->get_parameter = _get_parameter; + iface->set_attribute = _set_attribute; + iface->set_parameter = _set_parameter; + iface->delete_async = delete_async; + iface->delete_finish = delete_finish; iface->commit = _commit; iface->list = _list; + iface->create = create; } diff --git a/tests/twisted/mctest.py b/tests/twisted/mctest.py index 35ccec97..2229525b 100644 --- a/tests/twisted/mctest.py +++ b/tests/twisted/mctest.py @@ -1018,12 +1018,11 @@ class SimulatedConnectionManager(object): for protocol_name in protocol_names: assert '-' not in protocol_name - q.add_dbus_method_impl(self.IdentifyAccount, - path=self.object_path + '/' + protocol_name, - interface=cs.PROTOCOL, method='IdentifyAccount') - q.add_dbus_method_impl(self.NormalizeContact, - path=self.object_path + '/' + protocol_name, - interface=cs.PROTOCOL, method='NormalizeContact') + + q.add_dbus_method_impl(self.IdentifyAccount, + interface=cs.PROTOCOL, method='IdentifyAccount') + q.add_dbus_method_impl(self.NormalizeContact, + interface=cs.PROTOCOL, method='NormalizeContact') def release_name(self): del self._bus_name_ref @@ -1132,11 +1131,32 @@ class SimulatedConnectionManager(object): }, signature='a{sv}', bus=self.bus) def IdentifyAccount(self, e): + if e.path.startswith(self.object_path + '/'): + protocol = e.path[len(self.object_path + '/'):] + + if protocol not in self.protocol_names: + self.q.dbus_raise(e.message, cs.DBUS_ERROR_UNKNOWN_METHOD, + 'Not my protocol') + return + else: + self.q.dbus_raise(e.message, cs.DBUS_ERROR_UNKNOWN_METHOD, + 'Not even my object path') + return + + if protocol in ('serializable', 'defaults') and 's' in e.args[0]: + ret = e.args[0]['s'].lower() + if ret: + self.q.dbus_return(e.message, ret, signature='s') + return + if 'account' in e.args[0]: ret = e.args[0]['account'].lower() - else: - ret = 'account' - self.q.dbus_return(e.message, ret, signature='s') + if ret: + self.q.dbus_return(e.message, ret, signature='s') + return + + self.q.dbus_raise(e.message, cs.INVALID_HANDLE, + 'Invalid account name %r' % e.args[0].get('account')) def NormalizeContact(self, e): self.q.dbus_return(e.message, e.args[0].lower(), signature='s') |