diff --git a/mission-control-plugins/account-storage.c b/mission-control-plugins/account-storage.c index dd478d8..6ce8c72 100644 --- a/mission-control-plugins/account-storage.c +++ b/mission-control-plugins/account-storage.c @@ -56,7 +56,6 @@ * iface->desc = "The FOO storage backend"; * iface->provider = "org.freedesktop.Telepathy.MissionControl5.FooStorage"; * - * iface->get = foo_plugin_get; * iface->delete = foo_plugin_delete; * iface->commit = foo_plugin_commit; * iface->list = foo_plugin_list; @@ -65,6 +64,8 @@ * iface->get_additional_info = foo_plugin_get_additional_info; * iface->get_restrictions = foo_plugin_get_restrictions; * iface->create = foo_plugin_create; + * 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; * } @@ -109,24 +110,30 @@ enum static guint signals[NO_SIGNAL] = { 0 }; -static gboolean -default_set_attribute (McpAccountStorage *storage, +static void +default_delete_async (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, - const gchar *attribute, - GVariant *value, - McpAttributeFlags flags) + 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_set_parameter (McpAccountStorage *storage, - McpAccountManager *am, - const gchar *account, - const gchar *parameter, - GVariant *value, - McpParameterFlags flags) +default_delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static gboolean +default_commit (const McpAccountStorage *storage, + const McpAccountManager *am, + const gchar *account) { return FALSE; } @@ -140,31 +147,22 @@ default_create (const McpAccountStorage *storage, GError **error) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, - "This storage does not implement create function"); + "This storage does not implement the create() function"); return NULL; } -static gboolean -default_delete (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key) -{ - return FALSE; -} - -static gboolean -default_commit (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account) +static GList * +default_list (const McpAccountStorage *storage, + const McpAccountManager *am) { - return FALSE; + return NULL; } static void default_ready (const McpAccountStorage *storage, const McpAccountManager *am) { + /* do nothing */ } static void @@ -190,6 +188,50 @@ default_get_restrictions (const McpAccountStorage *storage, return 0; } +static GVariant * +default_get_attribute (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) +{ + return NULL; +} + +static GVariant * +default_get_parameter (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) +{ + return NULL; +} + +static McpAccountStorageSetResult +default_set_attribute (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + GVariant *value, + McpAttributeFlags flags) +{ + return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED; +} + +static McpAccountStorageSetResult +default_set_parameter (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + GVariant *value, + McpParameterFlags flags) +{ + return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED; +} + static void class_init (gpointer klass, gpointer data) @@ -197,15 +239,19 @@ class_init (gpointer klass, GType type = G_TYPE_FROM_CLASS (klass); McpAccountStorageIface *iface = klass; - iface->set_attribute = default_set_attribute; - iface->set_parameter = default_set_parameter; iface->create = default_create; - iface->delete = default_delete; + iface->delete_async = default_delete_async; + iface->delete_finish = default_delete_finish; iface->commit = default_commit; + iface->list = default_list; 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->get_attribute = default_get_attribute; + iface->get_parameter = default_get_parameter; + iface->set_attribute = default_set_attribute; + iface->set_parameter = default_set_parameter; if (signals[CREATED] != 0) { @@ -231,20 +277,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 */ @@ -261,11 +307,8 @@ class_init (gpointer klass, * Emitted if an external entity deletes an account * in the backend the emitting plugin handles. * - * Should not be fired until mcp_account_storage_ready() has been called. + * Should not be fired until mcp_account_storage_ready() has been called * - * mcp_account_storage_commit() and mcp_account_storage_delete() won't be - * called for this account, it is assumed that the storage backend already - * deleted all the related information. */ signals[DELETED] = g_signal_new ("deleted", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -280,8 +323,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 * @@ -354,9 +400,9 @@ mcp_account_storage_get_type (void) * @set: implementation of mcp_account_storage_set() * @get: implementation of mcp_account_storage_get() * @delete: implementation of mcp_account_storage_delete() + * @commit: implementation of mcp_account_storage_commit() * @list: implementation of mcp_account_storage_list() * @ready: implementation of mcp_account_storage_ready() - * @commit: implementation of mcp_account_storage_commit() * @get_identifier: implementation of mcp_account_storage_get_identifier() * @get_additional_info: implementation of * mcp_account_storage_get_additional_info() @@ -411,57 +457,83 @@ mcp_account_storage_priority (const McpAccountStorage *storage) } /** - * McpAccountStorageGetFunc: - * @storage: the account storage plugin - * @am: object used to call back into the account manager + * 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" + * @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. * - * An implementation of mcp_account_storage_get(). + * There is a default implementation, which just returns %NULL. + * All account storage plugins must override this method. * - * Returns: %TRUE if @storage is responsible for @account + * 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 */ +GVariant * +mcp_account_storage_get_attribute (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) +{ + McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + + SDEBUG (storage, ""); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->get_attribute != NULL, FALSE); + + return iface->get_attribute (storage, am, account, attribute, type, flags); +} /** - * mcp_account_storage_get: + * mcp_account_storage_get_parameter: * @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() - * 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 + * @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 + * + * Retrieve a parameter. + * + * There is a default implementation, which just returns %NULL. + * 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 parameters may return + * the stored type, not the expected type, if they differ. + * + * Returns: (transfer full): the value of the parameter, or %NULL if it + * is not present */ -gboolean -mcp_account_storage_get (const McpAccountStorage *storage, +GVariant * +mcp_account_storage_get_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->get != NULL, FALSE); + g_return_val_if_fail (iface->get_parameter != NULL, FALSE); - return iface->get (storage, am, account, key); + return iface->get_parameter (storage, am, account, parameter, type, flags); } /** @@ -470,7 +542,8 @@ mcp_account_storage_get (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. @@ -489,7 +562,7 @@ mcp_account_storage_get (const McpAccountStorage *storage, * * Since: 5.15.0 */ -gboolean +McpAccountStorageSetResult mcp_account_storage_set_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -500,8 +573,10 @@ 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); + 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); } @@ -513,7 +588,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. @@ -532,7 +608,7 @@ mcp_account_storage_set_attribute (McpAccountStorage *storage, * * Since: 5.15.0 */ -gboolean +McpAccountStorageSetResult mcp_account_storage_set_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -544,7 +620,10 @@ mcp_account_storage_set_parameter (McpAccountStorage *storage, SDEBUG (storage, ""); 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); } @@ -586,8 +665,12 @@ 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. * - * There is a default implementation, which just returns %NULL and raise an - * error. + * 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 @@ -603,7 +686,6 @@ mcp_account_storage_create (const McpAccountStorage *storage, { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->create != NULL, NULL); @@ -611,61 +693,62 @@ mcp_account_storage_create (const McpAccountStorage *storage, } /** - * 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 + * @callback: called on success or failure + * @user_data: data for @callback + * + * Delete the account @account, and commit the change, + * emitting #McpAccountStorage::deleted afterwards. * - * An implementation of mcp_account_storage_delete(). + * 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. * - * Returns: %TRUE if the setting or settings are not - * the plugin's cache after this operation, %FALSE otherwise. + * The default implementation just returns failure (asynchronously), + * and is appropriate for read-only storage. */ +void +mcp_account_storage_delete_async (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + GAsyncReadyCallback callback, + gpointer user_data) +{ + McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + + SDEBUG (storage, ""); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->delete_async != NULL); + + iface->delete_async (storage, am, account, 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 + * @res: the result of mcp_account_storage_delete_async() + * @error: used to raise an error if %FALSE is returned * - * 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. + * Process the result of mcp_account_storage_delete_async(). * - * If @key is %NULL, the plugin should forget all its settings for - * @account,and remember to delete the entire account from its storage later. - * - * The plugin is not expected to update its long term storage at - * this point. - * - * There is a default implementation, which just returns %FALSE, for read-only - * plugins. - * - * 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 != 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); } /** @@ -673,7 +756,7 @@ mcp_account_storage_delete (const McpAccountStorage *storage, * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: (allow-none): the unique suffix of an account's object path, - * or %NULL + * or %NULL to commit all accounts * * An implementation of mcp_account_storage_commit(). * @@ -694,11 +777,8 @@ 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 @account = %NULL it means that it should commit all accounts owned by the - * storage plugin. - * - * A default implementation that simply returns %FALSE is provided for read-only - * plugins. + * 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 @@ -739,6 +819,8 @@ mcp_account_storage_commit (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. * + * The default implementation returns %NULL, i.e. an empty list. + * * 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 @@ -774,8 +856,9 @@ mcp_account_storage_list (const McpAccountStorage *storage, * ie it can now fire its "created", "altered-one", "toggled" and "deleted" * signals. * - * There is a default implementation for plugins that can't create accounts from - * external sources, as they can never fire the async account change 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, @@ -809,10 +892,10 @@ 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. - * - * There is default implementation that sets @identifier to @account string. */ void mcp_account_storage_get_identifier (const McpAccountStorage *storage, @@ -852,10 +935,10 @@ mcp_account_storage_get_identifier (const McpAccountStorage *storage, * This method will only be called for the storage plugin that "owns" * the account. * - * There is a default implementation that return an empty table. + * 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, @@ -888,7 +971,7 @@ mcp_account_storage_get_additional_info (const McpAccountStorage *storage, * This method will only be called for the storage plugin that "owns" * the account. * - * There is a default implementation that just return 0 (no restrictions). + * The default implementation returns 0, i.e. no restrictions. * * Returns: a bitmask of %TpStorageRestrictionFlags with the restrictions to * account storage. diff --git a/mission-control-plugins/account-storage.h b/mission-control-plugins/account-storage.h index 1a3a012..42898a5 100644 --- a/mission-control-plugins/account-storage.h +++ b/mission-control-plugins/account-storage.h @@ -32,6 +32,12 @@ G_BEGIN_DECLS #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_NORMAL 100 #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING 10000 +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; typedef struct _McpAccountStorageIface McpAccountStorageIface; @@ -59,11 +65,6 @@ 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 gchar * (*McpAccountStorageCreate) ( const McpAccountStorage *storage, const McpAccountManager *am, @@ -106,24 +107,42 @@ struct _McpAccountStorageIface const gchar *desc; const gchar *provider; - McpAccountStorageGetFunc get; - McpAccountStorageDeleteFunc delete; + void (*delete_async) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*delete_finish) (McpAccountStorage *storage, + GAsyncResult *res, + GError **error); + McpAccountStorageCommitFunc commit; McpAccountStorageListFunc list; McpAccountStorageReadyFunc ready; - McpAccountStorageCommitFunc commit; McpAccountStorageGetIdentifierFunc get_identifier; McpAccountStorageGetAdditionalInfoFunc get_additional_info; McpAccountStorageGetRestrictionsFunc get_restrictions; McpAccountStorageCreate create; /* Since 5.15.0 */ - gboolean (*set_attribute) (McpAccountStorage *storage, + GVariant *(*get_attribute) (McpAccountStorage *storage, + McpAccountManager *am, + 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, @@ -134,11 +153,6 @@ struct _McpAccountStorageIface /* virtual methods */ gint mcp_account_storage_priority (const McpAccountStorage *storage); -gboolean mcp_account_storage_get (const McpAccountStorage *storage, - McpAccountManager *am, - const gchar *account, - const gchar *key); - gchar * mcp_account_storage_create (const McpAccountStorage *storage, const McpAccountManager *am, const gchar *manager, @@ -146,10 +160,14 @@ gchar * mcp_account_storage_create (const McpAccountStorage *storage, 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); + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mcp_account_storage_delete_finish (McpAccountStorage *storage, + GAsyncResult *result, + GError **error); void mcp_account_storage_ready (const McpAccountStorage *storage, const McpAccountManager *am); @@ -179,13 +197,28 @@ const gchar *mcp_account_storage_name (const McpAccountStorage *storage); const gchar *mcp_account_storage_description (const McpAccountStorage *storage); const gchar *mcp_account_storage_provider (const McpAccountStorage *storage); -gboolean mcp_account_storage_set_attribute (McpAccountStorage *storage, +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); + +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 c9859d7..2c19461 100644 --- a/mission-control-plugins/account.c +++ b/mission-control-plugins/account.c @@ -74,123 +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_unique_name: * @mcpa: an #McpAccountManager instance * @manager: the name of the manager @@ -295,3 +178,32 @@ mcp_account_manager_escape_variant_for_keyfile (const McpAccountManager *mcpa, return iface->escape_variant_for_keyfile (mcpa, variant); } + +/** + * mcp_account_manager_unescape_variant_from_keyfile: + * @mcpa: a #McpAccountManager + * @escaped: a string that could have come from g_key_file_get_value() + * @type: the type of the variant to which to unescape + * + * Unescape @escaped as if it had appeared in a #GKeyFile, with syntax + * appropriate for @type. + * + * It is a programming error to use an unsupported type. + * + * Returns: (transfer full): the unescaped form of @escaped + * (*not* a floating reference) + */ +GVariant * +mcp_account_manager_unescape_variant_from_keyfile ( + const McpAccountManager *mcpa, + const gchar *escaped, + const GVariantType *type, + GError **error) +{ + McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); + + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->unescape_variant_from_keyfile != NULL, NULL); + + 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 4356b3c..2a46c3a 100644 --- a/mission-control-plugins/account.h +++ b/mission-control-plugins/account.h @@ -45,35 +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_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_variant_for_keyfile ( const McpAccountManager *mcpa, GVariant *variant); +GVariant *mcp_account_manager_unescape_variant_from_keyfile ( + const McpAccountManager *mcpa, + const gchar *escaped, + const GVariantType *type, + GError **error); + 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 9d44b3b..59096f9 100644 --- a/mission-control-plugins/implementation.h +++ b/mission-control-plugins/implementation.h @@ -77,33 +77,17 @@ struct _McpDispatchOperationIface { struct _McpAccountManagerIface { GTypeInterface parent; - void (*set_value) (const McpAccountManager *ma, - const gchar *acct, - const gchar *key, - const gchar *value); - 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_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/src/mcd-account-addressing.c b/src/mcd-account-addressing.c index ec3d8f5..c9740c7 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 e967b73..47d261f 100644 --- a/src/mcd-account-manager-default.c +++ b/src/mcd-account-manager-default.c @@ -47,13 +47,17 @@ 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; } 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 +83,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; } @@ -166,7 +169,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 +180,61 @@ set_parameter (McpAccountStorage *self, McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); McdDefaultStoredAccount *sa; - sa = ensure_stored_account (amd, account); - amd->save = TRUE; + if (val == NULL) + { + gboolean changed = FALSE; + + sa = lookup_stored_account (amd, account); + + if (sa == NULL) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + + 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); + + if (!changed) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + } + else + { + GVariant *old; + + sa = ensure_stored_account (amd, account); + old = g_hash_table_lookup (sa->parameters, parameter); + + if (old == NULL) + { + /* it might still be in untyped_parameters? */ + const gchar *escaped = g_hash_table_lookup (sa->untyped_parameters, + parameter); + gchar *new = mcp_account_manager_escape_variant_for_keyfile ( + am, val); + + if (!tp_strdiff (escaped, new)) + { + g_free (new); + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + } - /* 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); + g_free (new); + } + else if (g_variant_equal (old, val)) + { + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + } - if (val != NULL) - g_hash_table_insert (sa->parameters, g_strdup (parameter), - g_variant_ref (val)); + g_hash_table_remove (sa->untyped_parameters, parameter); + g_hash_table_insert (sa->parameters, g_strdup (parameter), + g_variant_ref (val)); + } - return TRUE; + amd->save = TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } -static gboolean +static McpAccountStorageSetResult set_attribute (McpAccountStorage *self, McpAccountManager *am, const gchar *account, @@ -201,129 +243,89 @@ set_attribute (McpAccountStorage *self, McpAttributeFlags flags) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); - McdDefaultStoredAccount *sa = ensure_stored_account (amd, account); - - amd->save = TRUE; - - if (val != NULL) - g_hash_table_insert (sa->attributes, g_strdup (attribute), - g_variant_ref (val)); - else - g_hash_table_remove (sa->attributes, attribute); - - return TRUE; -} - -static gboolean -get_parameter (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *prefixed, - const gchar *parameter) -{ - McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); - McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); + McdDefaultStoredAccount *sa; - if (parameter != NULL) + if (val == NULL) { - gchar *v = NULL; - GVariant *variant = NULL; - - if (sa == NULL || sa->absent) - return FALSE; - - variant = g_hash_table_lookup (sa->parameters, parameter); - - 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); + sa = lookup_stored_account (amd, account); - if (v == NULL) - return FALSE; + if (sa == NULL) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; - mcp_account_manager_set_value (am, account, prefixed, v); + if (!g_hash_table_remove (sa->attributes, attribute)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; } else { - g_assert_not_reached (); + GVariant *old; + + sa = ensure_stored_account (amd, account); + old = g_hash_table_lookup (sa->attributes, attribute); + + 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)); } - return TRUE; + amd->save = TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } -static gboolean -_get (const McpAccountStorage *self, - const McpAccountManager *am, +static GVariant * +get_attribute (McpAccountStorage *self, + McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); + if (flags != NULL) + *flags = 0; + 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); + /* 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)); +} - if (v == NULL) - return FALSE; +static GVariant * +get_parameter (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + 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; - mcp_account_manager_set_attribute (am, account, key, v, - MCP_ATTRIBUTE_FLAG_NONE); - } - else - { - GHashTableIter iter; - gpointer k, v; + if (flags != NULL) + *flags = 0; - g_hash_table_iter_init (&iter, sa->attributes); + if (sa == NULL || sa->absent) + return FALSE; - 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); - } + variant = g_hash_table_lookup (sa->parameters, parameter); - g_hash_table_iter_init (&iter, sa->parameters); + if (variant != NULL) + return g_variant_ref (variant); - 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); - } + str = g_hash_table_lookup (sa->untyped_parameters, parameter); - g_hash_table_iter_init (&iter, sa->untyped_parameters); - - 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 * @@ -334,71 +336,108 @@ _create (const McpAccountStorage *self, 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, + 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; + + task = g_task_new (amd, NULL, callback, user_data); if (sa == NULL || sa->absent) { /* Apparently we never had this account anyway. The plugin API * considers this to be "success". */ - return TRUE; + g_task_return_boolean (task, TRUE); + goto finally; } - if (key == NULL) - { - amd->save = TRUE; + filename = account_file_in (g_get_user_data_dir (), account); - /* 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 + 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; } + } - /* 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) + 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 (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 @@ -418,58 +457,6 @@ am_default_commit_one (McdAccountManagerDefault *self, filename = account_file_in (g_get_user_data_dir (), account_name); - if (sa->pending_deletion) - { - const gchar * const *iter; - - 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; - } - } - - g_free (filename); - return TRUE; - } - DEBUG ("Saving account %s to %s", account_name, filename); g_variant_builder_init (&attrs_builder, G_VARIANT_TYPE_VARDICT); @@ -565,17 +552,6 @@ _commit (const McpAccountStorage *self, amd->save = FALSE; } - g_hash_table_iter_init (&outer, amd->accounts); - - /* forget about any entirely removed accounts */ - while (g_hash_table_iter_next (&outer, NULL, &sa_p)) - { - McdDefaultStoredAccount *sa = sa_p; - - if (sa->pending_deletion) - g_hash_table_iter_remove (&outer); - } - return all_succeeded; } @@ -639,7 +615,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) @@ -651,7 +627,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) @@ -999,11 +975,13 @@ account_storage_iface_init (McpAccountStorageIface *iface, iface->desc = PLUGIN_DESCRIPTION; iface->priority = PLUGIN_PRIORITY; - iface->get = _get; + 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->delete_async = delete_async; + iface->delete_finish = delete_finish; iface->commit = _commit; iface->list = _list; diff --git a/src/mcd-account-manager.c b/src/mcd-account-manager.c index 8495d44..c0d4dd7 100644 --- a/src/mcd-account-manager.c +++ b/src/mcd-account-manager.c @@ -108,6 +108,7 @@ typedef struct gchar *cm_name; gchar *protocol_name; gchar *display_name; + gchar *provider; GHashTable *parameters; GHashTable *properties; McdGetAccountCb callback; @@ -170,7 +171,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) @@ -180,6 +181,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); @@ -189,6 +191,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); @@ -266,15 +280,23 @@ created_cb (GObject *storage_plugin_obj, McdMaster *master = mcd_master_get_default (); McdManager *cm = NULL; const gchar *cm_name = NULL; + GError *error = NULL; lad->account_manager = am; lad->storage_plugin = plugin; lad->account_lock = 1; /* will be released at the end of this function */ /* actually fetch the data into our cache from the plugin: */ - mcd_storage_add_account_from_plugin (storage, plugin, name); - account = mcd_account_new (am, name, priv->minotaur); - lad->account = account; + if (mcd_storage_add_account_from_plugin (storage, plugin, name, &error)) + { + account = mcd_account_new (am, name, priv->minotaur, plugin); + lad->account = account; + } + else + { + WARNING ("%s", error->message); + goto finish; + } if (G_UNLIKELY (!account)) { @@ -312,6 +334,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); @@ -325,6 +348,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); @@ -360,11 +395,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 */ @@ -383,14 +434,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 altered-one 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); } } @@ -558,7 +623,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); @@ -568,10 +632,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_DBUS_PROP_SET_FLAG_NONE); - mcd_account_manager_write_conf_async (account_manager, account, NULL, - NULL); } static inline void @@ -651,6 +711,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); @@ -701,8 +762,10 @@ 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); } @@ -794,10 +857,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); @@ -809,12 +872,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) { @@ -837,8 +897,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)) { @@ -881,18 +943,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); @@ -1126,12 +1203,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); } @@ -1155,8 +1233,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 @@ -1183,7 +1261,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, @@ -1197,7 +1277,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); @@ -1337,9 +1419,9 @@ _mcd_account_manager_setup (McdAccountManager *account_manager) McdAccountManagerPrivate *priv = account_manager->priv; McdStorage *storage = priv->storage; McdLoadAccountsData *lad; - gchar **accounts, **name; + GHashTable *accounts; GHashTableIter iter; - gpointer v; + gpointer k, v; tp_list_connection_names (priv->dbus_daemon, list_connection_names_cb, NULL, NULL, @@ -1349,29 +1431,35 @@ _mcd_account_manager_setup (McdAccountManager *account_manager) lad->account_manager = account_manager; lad->account_lock = 1; /* will be released at the end of this function */ - 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; } @@ -1386,7 +1474,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; } @@ -1396,7 +1484,8 @@ _mcd_account_manager_setup (McdAccountManager *account_manager) _mcd_account_load (account, account_loaded, lad); g_object_unref (account); } - g_strfreev (accounts); + + g_hash_table_unref (accounts); uncork_storage_plugins (account_manager); @@ -1683,15 +1772,8 @@ mcd_account_manager_write_conf_async (McdAccountManager *account_manager, } else { - GStrv groups; - gsize n_accounts = 0; - - groups = mcd_storage_dup_accounts (storage, &n_accounts); - DEBUG ("updating all %" G_GSIZE_FORMAT " accounts", n_accounts); - + DEBUG ("updating all accounts"); mcd_storage_commit (storage, NULL); - - g_strfreev (groups); } if (callback != NULL) diff --git a/src/mcd-account.c b/src/mcd-account.c index 12e1513..edadbee 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, }; @@ -382,7 +383,8 @@ _mcd_account_set_parameter (McdAccount *account, const gchar *name, 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: @@ -405,18 +407,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) @@ -427,7 +433,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) { @@ -674,16 +681,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 @@ -694,16 +704,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)) { @@ -728,12 +740,12 @@ 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, flags); + mcd_storage_delete_account (priv->storage, name); data_dir_str = get_old_account_data_path (priv); @@ -760,21 +772,25 @@ mcd_account_delete (McdAccount *account, g_free (data_dir_str); } - if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0) - 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), NULL); + + return g_task_propagate_boolean (G_TASK (result), error); } void @@ -1081,7 +1097,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 +2093,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 +2111,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 +2130,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 +2142,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 +2186,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 +2324,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 +2342,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 +2401,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 +2433,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 +2461,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 +2480,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 +2670,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 +2680,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 +3276,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 +3349,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 +3399,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 +3490,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 +3668,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 +3749,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 +3758,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 +3772,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. diff --git a/src/mcd-account.h b/src/mcd-account.h index 3aad723..a0b29af 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); @@ -139,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); @@ -158,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 f83f1b4..978c12d 100644 --- a/src/mcd-storage.c +++ b/src/mcd-storage.c @@ -61,56 +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; - - /* owned storage plugin owning this account */ - McpAccountStorage *storage; -} McdStorageAccount; - -static McdStorageAccount * -mcd_storage_account_new (McpAccountStorage *storage) -{ - McdStorageAccount *sa; - - 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->storage = g_object_ref (storage); - - return sa; -} - -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_object_unref (sa->storage); - 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 @@ -217,13 +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 struct { const gchar *type; const gchar *name; @@ -264,7 +212,7 @@ static struct { { NULL, NULL } }; -const gchar * +const GVariantType * mcd_storage_get_attribute_type (const gchar *attribute) { guint i; @@ -272,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; @@ -280,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); @@ -304,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); @@ -319,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; @@ -331,132 +283,6 @@ mcd_storage_init_value_for_attribute (GValue *value, return FALSE; } -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 = lookup_account (self, account); - - g_return_if_fail (sa != NULL); - - 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 = lookup_account (self, account); - - g_return_if_fail (sa != NULL); - - 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)); -} - -static void -set_value (const McpAccountManager *ma, - const gchar *account, - const gchar *key, - const gchar *value) -{ - McdStorage *self = MCD_STORAGE (ma); - McdStorageAccount *sa = lookup_account (self, account); - - g_return_if_fail (sa != NULL); - - 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 gchar * unique_name (const McpAccountManager *ma, const gchar *manager, @@ -503,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); } } @@ -663,10 +498,10 @@ mcd_storage_load (McdStorage *self) sort_and_cache_plugins (); - store = g_list_last (stores); + store = stores; - /* fetch accounts stored in plugins, in reverse priority so higher prio * - * plugins can overwrite lower prio ones' account data */ + /* fetch accounts stored in plugins, highest priority first, so that + * low priority plugins can be overidden by high priority */ while (store != NULL) { GList *account; @@ -678,80 +513,39 @@ 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); + store = store->next; } } /* - * 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(). - */ -GStrv -mcd_storage_dup_accounts (McdStorage *self, - gsize *n) -{ - 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(). + * Returns: (transfer container) (element-type utf8 Mcp.AccountStorage): a + * map from account object path tail to plugin */ -GStrv -mcd_storage_dup_attributes (McdStorage *self, - const gchar *account, - gsize *n) +GHashTable * +mcd_storage_get_accounts (McdStorage *self) { - 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 g_hash_table_ref (self->accounts); } /* @@ -771,15 +565,15 @@ McpAccountStorage * mcd_storage_get_plugin (McdStorage *self, const gchar *account) { - McdStorageAccount *sa; + McpAccountStorage *plugin; g_return_val_if_fail (MCD_IS_STORAGE (self), NULL); g_return_val_if_fail (account != NULL, NULL); - sa = lookup_account (self, account); - g_return_val_if_fail (sa != NULL, NULL); + plugin = g_hash_table_lookup (self->accounts, account); + g_return_val_if_fail (plugin != NULL, NULL); - return sa->storage; + return plugin; } /* @@ -806,7 +600,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); @@ -853,36 +648,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; } /* @@ -897,43 +698,42 @@ gboolean mcd_storage_get_parameter (McdStorage *self, const gchar *account, const gchar *parameter, + const GVariantType *type, GValue *value, GError **error) { - McdStorageAccount *sa; - const gchar *escaped; + McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + McpAccountStorage *plugin; GVariant *variant; + gboolean ret; g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE); g_return_val_if_fail (account != NULL, FALSE); g_return_val_if_fail (parameter != NULL, FALSE); + g_return_val_if_fail (!g_str_has_prefix (parameter, "param-"), FALSE); - sa = lookup_account (self, account); + plugin = g_hash_table_lookup (self->accounts, account); - if (sa == NULL) + if (plugin == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Account %s does not exist", account); return FALSE; } - variant = g_hash_table_lookup (sa->parameters, parameter); + variant = mcp_account_storage_get_parameter (plugin, ma, account, + parameter, type, NULL); - if (variant != NULL) - return mcd_storage_coerce_variant_to_value (variant, value, error); - - /* OK, we don't have it as a variant. How about the keyfile-escaped - * version? */ - escaped = g_hash_table_lookup (sa->escaped_parameters, parameter); - - if (escaped == NULL) + if (variant == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, - "Parameter '%s' not stored by account %s", parameter, account); + "Account %s has no parameter '%s'", account, parameter); return FALSE; } - return mcd_keyfile_unescape_value (escaped, value, error); + ret = mcd_storage_coerce_variant_to_value (variant, value, error); + g_variant_unref (variant); + return ret; } /* @@ -1389,7 +1189,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); @@ -1417,44 +1218,66 @@ 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) { McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); - gboolean parameter = g_str_has_prefix (key, "param-"); - McdStorageAccount *sa; - const gchar *pn; + gboolean updated = FALSE; + McpAccountStorage *plugin; - sa = lookup_account (self, account); - g_return_if_fail (sa != NULL); + plugin = g_hash_table_lookup (self->accounts, account); - pn = mcp_account_storage_name (sa->storage); - if (variant == NULL) - { - DEBUG ("MCP:%s -> delete %s.%s", pn, account, key); - mcp_account_storage_delete (sa->storage, ma, account, key); - } - else if (parameter) + if (plugin != NULL) { - DEBUG ("MCP:%s -> store parameter %s.%s", pn, account, key); - mcp_account_storage_set_parameter (sa->storage, ma, account, key + 6, - variant, MCP_PARAMETER_FLAG_NONE); + const gchar *pn = mcp_account_storage_name (plugin); + McpAccountStorageSetResult res; + + 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); + + switch (res) + { + case MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED: + DEBUG ("MCP:%s -> store %s %s.%s", pn, + parameter ? "parameter" : "attribute", account, key); + updated = TRUE; + break; + + 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 (); + } } else { - DEBUG ("MCP:%s -> store attribute %s.%s", pn, account, key); - mcp_account_storage_set_attribute (sa->storage, ma, account, key, - variant, MCP_PARAMETER_FLAG_NONE); + g_assert_not_reached (); } + + return updated; } /* @@ -1525,39 +1348,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 = lookup_account (self, account); - g_return_val_if_fail (sa != NULL, FALSE); + 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)) - { - /* 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)); - - update_storage (self, account, attribute, new_v); - updated = TRUE; - } + updated = update_storage (self, account, FALSE, attribute, new_v); tp_clear_pointer (&new_v, g_variant_unref); return updated; @@ -1585,44 +1393,24 @@ mcd_storage_set_parameter (McdStorage *self, const gchar *parameter, const GValue *value) { - GVariant *old_v; GVariant *new_v = 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 = lookup_account (self, account); - g_return_val_if_fail (sa != NULL, FALSE); + 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)); - - old_v = g_hash_table_lookup (sa->parameters, parameter); - - if (old_v != NULL) - updated = !mcd_nullable_variant_equal (old_v, new_v); - 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); - return TRUE; + new_v = g_variant_ref_sink (dbus_g_value_build_g_variant (value)); } + updated = update_storage (self, account, TRUE, parameter, new_v); + tp_clear_pointer (&new_v, g_variant_unref); return updated; } @@ -1883,6 +1671,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 @@ -1898,11 +1687,15 @@ mcd_storage_create_account (McdStorage *self, const gchar *manager, const gchar *protocol, const gchar *identification, + McpAccountStorage **plugin_out, GError **error) { - McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); GList *store; - gchar *account; + 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); @@ -1917,10 +1710,21 @@ mcd_storage_create_account (McdStorage *self, if (!tp_strdiff (mcp_account_storage_provider (plugin), provider)) { - account = mcp_account_storage_create (plugin, ma, manager, + ret = mcp_account_storage_create (plugin, ma, manager, protocol, identification, error); - mcd_storage_add_account_from_plugin (self, plugin, account); - return account; + 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; + } } } @@ -1937,13 +1741,24 @@ mcd_storage_create_account (McdStorage *self, { McpAccountStorage *plugin = store->data; - account = mcp_account_storage_create (plugin, ma, manager, protocol, + ret = mcp_account_storage_create (plugin, ma, manager, protocol, identification, error); - if (account != NULL) + if (ret != NULL) { - mcd_storage_add_account_from_plugin (self, plugin, account); - return account; + 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); @@ -1958,6 +1773,28 @@ mcd_storage_create_account (McdStorage *self, return NULL; } +static void +delete_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GError *error = NULL; + + if (mcp_account_storage_delete_finish (MCP_ACCOUNT_STORAGE (source), + res, &error)) + { + DEBUG ("deleted account %s", (const gchar *) user_data); + } + else + { + DEBUG ("could not delete account %s (but no way to signal that): " + "%s #%d: %s", (const gchar *) user_data, + g_quark_to_string (error->domain), error->code, error->message); + g_error_free (error); + } + + g_free (user_data); +} /* * mcd_storage_delete_account: @@ -1971,22 +1808,31 @@ mcd_storage_create_account (McdStorage *self, */ void mcd_storage_delete_account (McdStorage *self, - const gchar *account, - McdDBusPropSetFlags flags) + const gchar *account) { McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); - McdStorageAccount *sa; + McpAccountStorage *plugin; g_return_if_fail (MCD_IS_STORAGE (self)); g_return_if_fail (account != NULL); - sa = lookup_account (self, account); - g_return_if_fail (sa != NULL); + plugin = g_hash_table_lookup (self->accounts, account); - if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0) - mcp_account_storage_delete (sa->storage, ma, account, NULL); + if (plugin == NULL) + { + /* we wanted no account, we got no account, I call that success! */ + return; + } + g_object_ref (plugin); g_hash_table_remove (self->accounts, account); + + /* 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, + delete_cb, g_strdup (account)); + + g_object_unref (plugin); } /* @@ -2000,29 +1846,31 @@ mcd_storage_delete_account (McdStorage *self, void mcd_storage_commit (McdStorage *self, const gchar *account) { - McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); GList *store; + McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + McpAccountStorage *plugin; + const gchar *pname; g_return_if_fail (MCD_IS_STORAGE (self)); if (account != NULL) { - McdStorageAccount *sa = lookup_account (self, account); + plugin = g_hash_table_lookup (self->accounts, account); + g_return_val_if_fail (plugin != NULL, FALSE); - g_return_if_fail (sa != NULL); + pname = mcp_account_storage_name (plugin); - DEBUG ("flushing plugin %s %s to long term storage", - mcp_account_storage_name (sa->storage), account); - mcp_account_storage_commit (sa->storage, ma, account); + DEBUG ("flushing plugin %s %s to long term storage", pname, account); + mcp_account_storage_commit (plugin, ma, account); return; } for (store = stores; store != NULL; store = g_list_next (store)) { - McpAccountStorage *plugin = store->data; + plugin = store->data; + pname = mcp_account_storage_name (plugin); - DEBUG ("flushing plugin %s to long term storage", - mcp_account_storage_name (plugin)); + DEBUG ("flushing plugin %s to long term storage", pname); mcp_account_storage_commit (plugin, ma, NULL); } } @@ -2081,35 +1929,70 @@ mcd_storage_ready (McdStorage *self) } } +static GVariant * +mcd_keyfile_unescape_variant (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 GVariant * +mcpa_unescape_variant_from_keyfile (const McpAccountManager *mcpa, + const gchar *escaped, + const GVariantType *type, + GError **error) +{ + return mcd_keyfile_unescape_variant (escaped, type, error); +} + static void plugin_iface_init (McpAccountManagerIface *iface, gpointer unused G_GNUC_UNUSED) { DEBUG (); - iface->set_value = set_value; - iface->set_attribute = mcpa_set_attribute; - iface->set_parameter = mcpa_set_parameter; 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_variant_for_keyfile = mcpa_escape_variant_for_keyfile; + iface->unescape_variant_from_keyfile = mcpa_unescape_variant_from_keyfile; } -void +gboolean mcd_storage_add_account_from_plugin (McdStorage *self, McpAccountStorage *plugin, - const gchar *account) + const gchar *account, + GError **error) { - g_return_if_fail (MCD_IS_STORAGE (self)); - g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin)); - g_return_if_fail (account != NULL); - g_return_if_fail (!g_hash_table_contains (self->accounts, account)); + McpAccountStorage *other = g_hash_table_lookup (self->accounts, account); - g_hash_table_insert (self->accounts, g_strdup (account), - mcd_storage_account_new (plugin)); + if (other != NULL) + { + 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; + } - /* This will fill our parameters/attributes tables */ - mcp_account_storage_get (plugin, MCP_ACCOUNT_MANAGER (self), account, NULL); + 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 0ea52eb..c2624a4 100644 --- a/src/mcd-storage.h +++ b/src/mcd-storage.h @@ -22,8 +22,6 @@ #include #include -#include - #ifndef MCD_STORAGE_H #define MCD_STORAGE_H @@ -32,7 +30,7 @@ G_BEGIN_DECLS typedef struct { GObject parent; TpDBusDaemon *dbusd; - /* owned string => owned McdStorageAccount */ + /* owned string => owned McpAccountStorage */ GHashTable *accounts; } McdStorage; @@ -66,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, @@ -97,11 +91,10 @@ gchar *mcd_storage_create_account (McdStorage *storage, const gchar *manager, const gchar *protocol, const gchar *identification, + McpAccountStorage **plugin_out, GError **error); -void mcd_storage_delete_account (McdStorage *storage, - const gchar *account, - McdDBusPropSetFlags flags); +void mcd_storage_delete_account (McdStorage *storage, const gchar *account); void mcd_storage_commit (McdStorage *storage, const gchar *account); @@ -112,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); @@ -134,9 +129,10 @@ McpAccountStorage * mcd_storage_get_plugin (McdStorage *storage, G_GNUC_INTERNAL void _mcd_storage_store_connections (McdStorage *storage); -void mcd_storage_add_account_from_plugin (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, @@ -162,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/account-manager/bad-cm.py b/tests/twisted/account-manager/bad-cm.py index 74277b4..008ac60 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/dbus-account-plugin.c b/tests/twisted/dbus-account-plugin.c index b2ae25b..9e0025a 100644 --- a/tests/twisted/dbus-account-plugin.c +++ b/tests/twisted/dbus-account-plugin.c @@ -54,7 +54,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; @@ -158,7 +158,7 @@ ensure_account (TestDBusAccountPlugin *self, if (account == NULL) { - account = g_slice_new (Account); + account = g_slice_new0 (Account); account->path = g_strdup_printf ("%s%s", TP_ACCOUNT_OBJECT_PATH_BASE, account_name); @@ -183,7 +183,6 @@ ensure_account (TestDBusAccountPlugin *self, g_hash_table_insert (self->accounts, g_strdup (account_name), account); } - account->flags &= ~UNCOMMITTED_DELETION; return account; } @@ -211,18 +210,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); } @@ -448,8 +443,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 +466,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); @@ -557,8 +548,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 +575,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 +608,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); @@ -908,185 +894,144 @@ 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) + 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, NULL, callback, user_data); + + g_task_set_task_data (task, g_strdup (user_data), g_free); 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_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); + /* We were asked to delete an account we don't have. It's + * a bit like success. */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; } - 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)); - 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_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); + if (flags != NULL) + *flags = 0; - mcp_account_manager_set_value (am, account_name, k, escaped); - g_free (escaped); - } + if (!self->active || account == NULL) + return NULL; - g_hash_table_iter_init (&iter, account->untyped_parameters); + v = g_hash_table_lookup (account->attributes, attribute); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *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); - param_foo = g_strdup_printf ("param-%s", (const gchar *) k); - mcp_account_manager_set_value (am, account_name, param_foo, v); + if (v != NULL) + { + return g_variant_ref (v); + } + else + { + return NULL; + } +} - g_free (param_foo); - } +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; - g_hash_table_iter_init (&iter, account->parameters); + if (flags != NULL) + *flags = 0; - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *param_foo; - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); + if (!self->active || account == NULL) + return NULL; - param_foo = g_strdup_printf ("param-%s", (const gchar *) k); - mcp_account_manager_set_value (am, account_name, param_foo, escaped); - g_free (escaped); + 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); - - 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 (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 +static McpAccountStorageSetResult test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account_name, @@ -1096,17 +1041,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; + if (!self->active || account == NULL) + return 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)); @@ -1120,10 +1088,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, @@ -1133,17 +1101,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; + if (!self->active || account == NULL) + return 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), @@ -1158,6 +1151,36 @@ test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, g_variant_new_parsed ("(%o, %s, %v)", account->path, parameter, value), NULL); + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; +} + +static gboolean +test_dbus_account_plugin_commit_all (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 (storage, am, k)) + { + g_warning ("declined to commit account %s", (const gchar *) k); + } + } + return TRUE; } @@ -1166,27 +1189,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 @@ -1292,10 +1319,12 @@ update_parameters_cb (GObject *source_object, } static gboolean -commit_one (TestDBusAccountPlugin *self, +test_dbus_account_plugin_commit (const McpAccountStorage *storage, + const McpAccountManager *am, const gchar *account_name) { - Account *account = lookup_account (self, account_name); + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); + Account *account; GHashTableIter iter; gpointer k; GVariantBuilder a_sv_builder; @@ -1305,6 +1334,11 @@ commit_one (TestDBusAccountPlugin *self, DEBUG ("%s", account_name); + if (account_name == NULL) + return test_dbus_account_plugin_commit_all (storage, am); + + account = lookup_account (self, account_name); + if (!self->active || account == NULL) return FALSE; @@ -1312,25 +1346,6 @@ commit_one (TestDBusAccountPlugin *self, 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, @@ -1456,40 +1471,6 @@ commit_one (TestDBusAccountPlugin *self, return TRUE; } -static gboolean -test_dbus_account_plugin_commit (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account_name) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - GHashTableIter iter; - gpointer k; - - DEBUG ("called"); - - if (!self->active) - return FALSE; - - if (account_name != NULL) - return commit_one (self, account_name); - - 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 (!commit_one (self, k)) - { - g_warning ("declined to commit account %s", (const gchar *) k); - } - } - - return TRUE; -} - static void test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, const gchar *account_name, @@ -1500,7 +1481,7 @@ test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) + if (!self->active || account == NULL) return; /* Our "library-specific unique identifier" is just the object-path @@ -1519,7 +1500,7 @@ test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) + if (!self->active || account == NULL) return NULL; ret = g_hash_table_new_full (g_str_hash, g_str_equal, @@ -1539,7 +1520,7 @@ test_dbus_account_plugin_get_restrictions (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) + if (!self->active || account == NULL) return 0; return account->restrictions; @@ -1553,12 +1534,14 @@ 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->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->get_identifier = test_dbus_account_plugin_get_identifier; iface->get_additional_info = test_dbus_account_plugin_get_additional_info; diff --git a/tests/twisted/mc-debug-server.c b/tests/twisted/mc-debug-server.c index 48f0248..917a5d3 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; @@ -185,6 +186,18 @@ main (int argc, char **argv) g_dbus_connection_set_exit_on_close (gdbus, FALSE); + gdbus_system = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + + if (gdbus_system == NULL) + { + g_warning ("%s", error->message); + g_error_free (error); + error = NULL; + goto out; + } + + g_dbus_connection_set_exit_on_close (gdbus_system, FALSE); + bus_daemon = tp_dbus_daemon_dup (&error); if (bus_daemon == NULL) @@ -238,6 +251,7 @@ out: } 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 8707d06..11c7caa 100644 --- a/tests/twisted/mcp-account-diversion.c +++ b/tests/twisted/mcp-account-diversion.c @@ -111,7 +111,7 @@ _create_config (void) DEBUG ("created %s", file); } -static gboolean +static McpAccountStorageSetResult _set (McpAccountStorage *self, McpAccountManager *am, const gchar *account, @@ -121,20 +121,55 @@ _set (McpAccountStorage *self, { 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; + + if (val == NULL) + { + gsize n; + GStrv keys; + + if (g_key_file_remove_key (adp->keyfile, account, key, NULL)) + { + adp->save = TRUE; + changed = TRUE; + } + + keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); + + if (keys == NULL || n == 0) + g_key_file_remove_group (adp->keyfile, account, NULL); + + g_strfreev (keys); + } + else + { + gchar *old; + + val_str = mcp_account_manager_escape_variant_for_keyfile (am, val); - adp->save = TRUE; + old = g_key_file_get_value (adp->keyfile, account, key, NULL); - val_str = mcp_account_manager_escape_variant_for_keyfile (am, val); - g_key_file_set_value (adp->keyfile, account, key, val_str); - g_free (val_str); + if (tp_strdiff (old, val_str)) + { + g_key_file_set_value (adp->keyfile, account, key, val_str); + adp->save = TRUE; + changed = TRUE; + } - return TRUE; + g_free (val_str); + g_free (old); + } + + if (changed) + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; + else + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; } -static gboolean +static McpAccountStorageSetResult _set_attribute (McpAccountStorage *self, McpAccountManager *am, const gchar *account, @@ -145,7 +180,7 @@ _set_attribute (McpAccountStorage *self, return _set (self, am, account, attribute, val, flags); } -static gboolean +static McpAccountStorageSetResult _set_parameter (McpAccountStorage *self, McpAccountManager *am, const gchar *account, @@ -162,99 +197,118 @@ _set_parameter (McpAccountStorage *self, return ret; } -static gboolean -_get (const McpAccountStorage *self, - const McpAccountManager *am, +static GVariant * +_get_attribute (McpAccountStorage *self, + McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) { AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *v; + GVariant *ret; - if (key != NULL) - { - gchar *v = g_key_file_get_value (adp->keyfile, account, key, NULL); + if (flags != NULL) + *flags = 0; - if (v == NULL) - return FALSE; + v = g_key_file_get_value (adp->keyfile, account, attribute, NULL); - mcp_account_manager_set_value (am, account, key, v); - g_free (v); - } - else - { - gsize i; - gsize n; - GStrv keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); + if (v == NULL) + return NULL; - if (keys == NULL) - n = 0; + ret = mcp_account_manager_unescape_variant_from_keyfile (am, v, type, NULL); + g_free (v); + return ret; +} - for (i = 0; i < n; i++) - { - gchar *v = g_key_file_get_value (adp->keyfile, account, keys[i], NULL); +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 (v != NULL) - mcp_account_manager_set_value (am, account, keys[i], v); + if (flags != NULL) + *flags = 0; - g_free (v); - } + key = g_strdup_printf ("param-%s", parameter); + v = g_key_file_get_value (adp->keyfile, account, key, NULL); + g_free (key); - g_strfreev (keys); - } + if (v == NULL) + return NULL; - return TRUE; + ret = mcp_account_manager_unescape_variant_from_keyfile (am, v, type, NULL); + g_free (v); + return ret; } -static gboolean -_delete (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key) +static gboolean _commit (const McpAccountStorage *self, + const McpAccountManager *am, + const gchar *account_name); + +static void +delete_async (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + GAsyncReadyCallback callback, + gpointer user_data) { AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + GTask *task = g_task_new (adp, NULL, callback, user_data); - if (key == NULL) + if (g_key_file_remove_group (adp->keyfile, account, NULL)) + adp->save = TRUE; + + if (_commit (self, am, account)) { - if (g_key_file_remove_group (adp->keyfile, account, NULL)) - adp->save = TRUE; + mcp_account_storage_emit_deleted (self, account); + g_task_return_boolean (task, TRUE); } else { - gsize n; - GStrv keys; - - if (g_key_file_remove_key (adp->keyfile, account, key, NULL)) - adp->save = TRUE; - - keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); - - if (keys == NULL || n == 0) - g_key_file_remove_group (adp->keyfile, account, NULL); - - g_strfreev (keys); + 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, - const gchar *account_name) + 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 (!adp->save) return TRUE; if (!_have_config ()) _create_config (); - /* In this naïve implementation, we always commit all accounts together */ data = g_key_file_to_data (adp->keyfile, &n, NULL); rval = g_file_set_contents (_conf_filename (), data, n, NULL); adp->save = !rval; @@ -290,6 +344,35 @@ _list (const McpAccountStorage *self, return rval; } +static gchar * +create (const McpAccountStorage *self, + const 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) @@ -298,12 +381,15 @@ account_storage_iface_init (McpAccountStorageIface *iface, iface->desc = PLUGIN_DESCRIPTION; iface->priority = PLUGIN_PRIORITY; - iface->get = _get; + iface->get_attribute = _get_attribute; + iface->get_parameter = _get_parameter; iface->set_attribute = _set_attribute; iface->set_parameter = _set_parameter; - iface->delete = _delete; + 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 b093ee8..4267af2 100644 --- a/tests/twisted/mctest.py +++ b/tests/twisted/mctest.py @@ -1025,12 +1025,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 @@ -1139,11 +1138,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')