[telepathy-mission-control/master] mcd-account: refactored set_parameters to be async and iteratively set params

Jonny Lamb jonny.lamb at collabora.co.uk
Mon Sep 14 11:09:13 PDT 2009


Signed-off-by: Jonny Lamb <jonny.lamb at collabora.co.uk>
---
 src/mcd-account-connection.c |   28 +-
 src/mcd-account-manager.c    |   99 +++--
 src/mcd-account-priv.h       |   24 +-
 src/mcd-account.c            |  978 ++++++++++++++++++++++++++++--------------
 src/mcd-account.h            |   17 +-
 5 files changed, 782 insertions(+), 364 deletions(-)

diff --git a/src/mcd-account-connection.c b/src/mcd-account-connection.c
index 37cbd86..ec119d7 100644
--- a/src/mcd-account-connection.c
+++ b/src/mcd-account-connection.c
@@ -51,29 +51,37 @@ _mcd_account_connection_context_free (McdAccountConnectionContext *c)
     g_free (c);
 }
 
-void
-_mcd_account_connection_begin (McdAccount *account)
+static void
+connection_begin_dup_params_cb (McdAccount *account, GHashTable *params,
+                                gpointer user_data)
 {
     McdAccountConnectionContext *ctx;
 
-    /* check whether a connection process is already ongoing */
-    if (_mcd_account_get_connection_context (account) != NULL)
-    {
-        DEBUG ("already trying to connect");
-        return;
-    }
-
     /* get account params */
     /* create dynamic params HT */
     /* run the handlers */
     ctx = g_malloc (sizeof (McdAccountConnectionContext));
     ctx->i_filter = 0;
-    ctx->params = _mcd_account_dup_parameters (account);
+
+    ctx->params = params;
     g_assert (ctx->params != NULL);
     _mcd_account_set_connection_context (account, ctx);
     mcd_account_connection_proceed (account, TRUE);
 }
 
+void
+_mcd_account_connection_begin (McdAccount *account)
+{
+    /* check whether a connection process is already ongoing */
+    if (_mcd_account_get_connection_context (account) != NULL)
+    {
+        DEBUG ("already trying to connect");
+        return;
+    }
+
+    _mcd_account_dup_parameters (account, connection_begin_dup_params_cb, NULL);
+}
+
 void 
 mcd_account_connection_proceed (McdAccount *account, gboolean success)
 {
diff --git a/src/mcd-account-manager.c b/src/mcd-account-manager.c
index 2bb85f6..84e5090 100644
--- a/src/mcd-account-manager.c
+++ b/src/mcd-account-manager.c
@@ -115,6 +115,9 @@ typedef struct
     McdGetAccountCb callback;
     gpointer user_data;
     GDestroyNotify destroy;
+
+    gboolean ok;
+    GError *error;
 } McdCreateAccountData;
 
 enum
@@ -346,6 +349,9 @@ mcd_create_account_data_free (McdCreateAccountData *cad)
         g_hash_table_unref (cad->properties);
     }
 
+    if (G_UNLIKELY (cad->error))
+        g_error_free (cad->error);
+
     g_slice_free (McdCreateAccountData, cad);
 }
 
@@ -408,44 +414,22 @@ create_account_with_properties_cb (McdAccountManager *account_manager,
 }
 
 static void
-complete_account_creation (McdAccount *account,
-                           const GError *cb_error,
-                           gpointer user_data)
+complete_account_creation_finish (McdAccount *account, gboolean valid,
+                                  gpointer user_data)
 {
-    McdCreateAccountData *cad = user_data;
+    McdCreateAccountData *cad = (McdCreateAccountData *) user_data;
     McdAccountManager *account_manager;
-    GError *error = NULL;
-    gboolean ok;
 
     account_manager = cad->account_manager;
-    if (G_UNLIKELY (cb_error))
-    {
-        cad->callback (account_manager, account, cb_error, cad->user_data);
-        mcd_create_account_data_free (cad);
-        return;
-    }
-
-    ok = _mcd_account_set_parameters (account, cad->parameters, NULL, NULL,
-                                      &error);
 
-    if (ok && cad->properties != NULL)
+    if (!valid)
     {
-        ok = set_new_account_properties (account, cad->properties, &error);
-    }
-
-    if (ok)
-    {
-        add_account (account_manager, account);
-
-        if (!mcd_account_check_validity (account))
-        {
-            ok = FALSE;
-            g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+        cad->ok = FALSE;
+        g_set_error (&cad->error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
                          "The supplied CM parameters were not valid");
-        }
     }
 
-    if (!ok)
+    if (!cad->ok)
     {
         mcd_account_delete (account, NULL, NULL);
         g_object_unref (account);
@@ -454,15 +438,65 @@ complete_account_creation (McdAccount *account,
 
     mcd_account_manager_write_conf_async (account_manager, NULL, NULL);
 
-    cad->callback (account_manager, account, error, cad->user_data);
-    if (G_UNLIKELY (error))
-        g_error_free (error);
+    if (cad->callback != NULL)
+        cad->callback (account_manager, account, cad->error, cad->user_data);
     mcd_create_account_data_free (cad);
 
     if (account != NULL)
     {
         g_object_unref (account);
     }
+
+}
+
+static void
+complete_account_creation_set_cb (McdAccount *account, GPtrArray *not_yet,
+                                  const GError *set_error, gpointer user_data)
+{
+    McdCreateAccountData *cad = user_data;
+    McdAccountManager *account_manager;
+    cad->ok = (set_error == NULL);
+
+    account_manager = cad->account_manager;
+
+    if (cad->ok && cad->properties != NULL)
+    {
+        cad->ok = set_new_account_properties (account, cad->properties, &cad->error);
+    }
+
+    if (cad->ok)
+    {
+        add_account (account_manager, account);
+        mcd_account_check_validity (account, complete_account_creation_finish, cad);
+    }
+    else
+    {
+        complete_account_creation_finish (account, TRUE, cad);
+    }
+
+    g_ptr_array_foreach (not_yet, (GFunc) g_free, NULL);
+    g_ptr_array_free (not_yet, TRUE);
+}
+
+static void
+complete_account_creation (McdAccount *account,
+                           const GError *cb_error,
+                           gpointer user_data)
+{
+    McdCreateAccountData *cad = user_data;
+    McdAccountManager *account_manager;
+
+    account_manager = cad->account_manager;
+    if (G_UNLIKELY (cb_error))
+    {
+        cad->callback (account_manager, account, cb_error, cad->user_data);
+        mcd_create_account_data_free (cad);
+        return;
+    }
+
+    _mcd_account_set_parameters (account, cad->parameters, NULL,
+                                 complete_account_creation_set_cb,
+                                 cad);
 }
 
 static gchar *
@@ -566,6 +600,7 @@ _mcd_account_manager_create_account (McdAccountManager *account_manager,
         cad->callback = callback;
         cad->user_data = user_data;
         cad->destroy = destroy;
+        cad->error = NULL;
         _mcd_account_load (account, complete_account_creation, cad);
     }
     else
diff --git a/src/mcd-account-priv.h b/src/mcd-account-priv.h
index af4d130..b5e2f39 100644
--- a/src/mcd-account-priv.h
+++ b/src/mcd-account-priv.h
@@ -43,11 +43,17 @@ G_GNUC_INTERNAL void _mcd_account_maybe_autoconnect (McdAccount *account);
 G_GNUC_INTERNAL void _mcd_account_connect (McdAccount *account,
                                            GHashTable *params);
 
-G_GNUC_INTERNAL gboolean _mcd_account_set_parameters (McdAccount *account,
-                                                      GHashTable *params,
-                                                      const gchar **unset,
-                                                      GPtrArray *not_yet,
-                                                      GError **error);
+
+typedef void (McdAccountSetParametersCb) (McdAccount *account,
+                                          GPtrArray *not_yet,
+                                          const GError *error,
+                                          gpointer user_data);
+
+G_GNUC_INTERNAL void _mcd_account_set_parameters (McdAccount *account,
+                                                  GHashTable *params,
+                                                  const gchar **unset,
+                                                  McdAccountSetParametersCb callback,
+                                                  gpointer user_data);
 
 G_GNUC_INTERNAL void _mcd_account_request_temporary_presence (McdAccount *self,
     TpConnectionPresenceType type, const gchar *status);
@@ -152,7 +158,13 @@ void _mcd_account_set_connection_context (McdAccount *self,
 G_GNUC_INTERNAL void _mcd_account_connection_context_free
     (McdAccountConnectionContext *c);
 
-G_GNUC_INTERNAL GHashTable *_mcd_account_dup_parameters (McdAccount *account);
+typedef void (*McdAccountDupParametersCb) (McdAccount *account,
+                                           GHashTable *params,
+                                           gpointer user_data);
+
+G_GNUC_INTERNAL void _mcd_account_dup_parameters (McdAccount *account,
+                                                  McdAccountDupParametersCb callback,
+                                                  gpointer user_data);
 
 extern const McdDBusProp account_conditions_properties[];
 
diff --git a/src/mcd-account.c b/src/mcd-account.c
index 9a3e5ab..427b2a3 100644
--- a/src/mcd-account.c
+++ b/src/mcd-account.c
@@ -342,6 +342,14 @@ set_parameter (McdAccount *account, const gchar *name, const GValue *value,
     McdAccountPrivate *priv = account->priv;
     gchar key[MAX_KEY_LENGTH];
     gchar buf[21];  /* enough for '-' + the 19 digits of 2**63 + '\0' */
+    const TpConnectionManagerParam *param;
+    gboolean is_secret = FALSE;
+
+    param = mcd_manager_get_protocol_param (priv->manager,
+                                            priv->protocol_name, name);
+
+    if (param != NULL && param->flags & TP_CONN_MGR_PARAM_FLAG_SECRET)
+        is_secret = TRUE;
 
     g_snprintf (key, sizeof (key), "param-%s", name);
 
@@ -428,138 +436,184 @@ out:
     }
 }
 
-static gboolean
-get_parameter (McdAccount *account, const gchar *name, GValue *value)
+static GType mc_param_type (const TpConnectionManagerParam *param);
+
+
+static void
+get_parameter (McdAccount *account, const gchar *name,
+               McdAccountGetParameterCb callback, gpointer user_data)
 {
     McdAccountPrivate *priv = account->priv;
     gchar key[MAX_KEY_LENGTH];
+    const TpConnectionManagerParam *param;
+    gboolean is_secret = FALSE;
+    GError *error = NULL;
+    GValue *value = NULL;
+    GType type;
+
+    gchar *v_string = NULL;
+    gint64 v_int = 0;
+    guint64 v_uint = 0;
+    gboolean v_bool = FALSE;
+    double v_double = 0.0;
+
+    param = mcd_manager_get_protocol_param (priv->manager,
+                                            priv->protocol_name, name);
+
+    if (param != NULL && param->flags & TP_CONN_MGR_PARAM_FLAG_SECRET)
+        is_secret = TRUE;
 
     g_snprintf (key, sizeof (key), "param-%s", name);
     if (!g_key_file_has_key (priv->keyfile, priv->unique_name, key, NULL))
-        return FALSE;
+        goto error;
+
+    type = mc_param_type (param);
 
-    if (value)
+    switch (type)
     {
-        gchar *v_string = NULL;
-        gint64 v_int = 0;
-        guint64 v_uint = 0;
-        gboolean v_bool = FALSE;
-        double v_double = 0.0;
+    case G_TYPE_STRING:
+        v_string = g_key_file_get_string (priv->keyfile, priv->unique_name,
+                                          key, NULL);
+        value = tp_g_value_slice_new_take_string (v_string);
+        break;
+
+    case G_TYPE_INT:
+        v_int = g_key_file_get_integer (priv->keyfile, priv->unique_name,
+                                        key, NULL);
+        value = tp_g_value_slice_new_int (v_int);
+        break;
+
+    case G_TYPE_INT64:
+        v_int = tp_g_key_file_get_int64 (priv->keyfile, priv->unique_name,
+                                         key, NULL);
+        value = tp_g_value_slice_new_int64 (v_int);
+        break;
 
-        switch (G_VALUE_TYPE (value))
+    case G_TYPE_UINT:
+        v_uint = tp_g_key_file_get_uint64 (priv->keyfile,
+                                           priv->unique_name, key, NULL);
+
+        if (v_uint > 0xFFFFFFFFU)
         {
-        case G_TYPE_STRING:
-            v_string = g_key_file_get_string (priv->keyfile, priv->unique_name,
-                                              key, NULL);
-            g_value_take_string (value, v_string);
-            break;
-
-        case G_TYPE_INT:
-            v_int = g_key_file_get_integer (priv->keyfile, priv->unique_name,
-                                            key, NULL);
-            g_value_set_int (value, v_int);
-            break;
-
-        case G_TYPE_INT64:
-            v_int = tp_g_key_file_get_int64 (priv->keyfile, priv->unique_name,
-                                             key, NULL);
-            g_value_set_int64 (value, v_int);
-            break;
-
-        case G_TYPE_UCHAR:
-            v_int = g_key_file_get_integer (priv->keyfile, priv->unique_name,
-                                            key, NULL);
-
-            if (v_int < 0 || v_int > 0xFF)
-            {
-                return FALSE;
-            }
+            goto error;
+        }
 
-            g_value_set_uchar (value, v_int);
-            break;
+        value = tp_g_value_slice_new_uint (v_uint);
+        break;
 
-        case G_TYPE_UINT:
-            v_uint = tp_g_key_file_get_uint64 (priv->keyfile,
-                                               priv->unique_name, key, NULL);
+    case G_TYPE_UCHAR:
+        v_int = g_key_file_get_integer (priv->keyfile, priv->unique_name,
+                                        key, NULL);
 
-            if (v_uint > 0xFFFFFFFFU)
-            {
-                return FALSE;
-            }
+        if (v_int < 0 || v_int > 0xFF)
+        {
+            goto error;
+        }
+        value = tp_g_value_slice_new (G_TYPE_UCHAR);
+        g_value_set_uchar (value, v_int);
+        break;
 
-            g_value_set_uint (value, v_uint);
-            break;
+    case G_TYPE_UINT64:
+        v_uint = tp_g_key_file_get_uint64 (priv->keyfile,
+                                           priv->unique_name, key, NULL);
 
-        case G_TYPE_UINT64:
-            v_uint = tp_g_key_file_get_uint64 (priv->keyfile,
-                                               priv->unique_name, key, NULL);
-            g_value_set_uint64 (value, v_uint);
-            break;
+        value = tp_g_value_slice_new_uint64 (v_uint);
+        break;
 
-        case G_TYPE_BOOLEAN:
-            v_bool = g_key_file_get_boolean (priv->keyfile, priv->unique_name,
-                                             key, NULL);
-            g_value_set_boolean (value, v_bool);
-            break;
+    case G_TYPE_BOOLEAN:
+        v_bool = g_key_file_get_boolean (priv->keyfile, priv->unique_name,
+                                         key, NULL);
 
-        case G_TYPE_DOUBLE:
-            v_double = g_key_file_get_double (priv->keyfile, priv->unique_name,
-                                             key, NULL);
-            g_value_set_double (value, v_double);
-            break;
+        value = tp_g_value_slice_new_boolean (v_bool);
+        break;
 
-        default:
-            if (G_VALUE_HOLDS (value, G_TYPE_STRV))
-            {
-                gchar **v = g_key_file_get_string_list (priv->keyfile,
-                                                        priv->unique_name, key,
-                                                        NULL, NULL);
+    case G_TYPE_DOUBLE:
+        v_double = g_key_file_get_double (priv->keyfile, priv->unique_name,
+                                          key, NULL);
 
-                g_value_take_boxed (value, v);
-            }
-            else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
-            {
-                v_string = g_key_file_get_string (priv->keyfile,
-                                                  priv->unique_name, key,
-                                                  NULL);
+        value = tp_g_value_slice_new_double (v_double);
+        break;
 
-                if (!tp_dbus_check_valid_object_path (v_string, NULL))
-                {
-                    g_free (v_string);
-                    return FALSE;
-                }
+    default:
+        if (type == G_TYPE_STRV)
+        {
+            gchar **v = g_key_file_get_string_list (priv->keyfile,
+                                                    priv->unique_name, key,
+                                                    NULL, NULL);
 
-                g_value_take_boxed (value, v_string);
-            }
-            else
+            value = tp_g_value_slice_new_take_boxed (G_TYPE_STRV, v);
+        }
+        else if (type == DBUS_TYPE_G_OBJECT_PATH)
+        {
+            v_string = g_key_file_get_string (priv->keyfile,
+                                              priv->unique_name, key,
+                                              NULL);
+
+            if (!tp_dbus_check_valid_object_path (v_string, NULL))
             {
-                g_warning ("%s: skipping parameter %s, unknown type %s",
-                           G_STRFUNC, name, G_VALUE_TYPE_NAME (value));
-                return FALSE;
+                g_free (v_string);
+                goto error;
             }
+
+            value = tp_g_value_slice_new_take_object_path (v_string);
+        }
+        else
+        {
+            g_warning ("%s: skipping parameter %s, unknown type %s",
+                       G_STRFUNC, name,
+                       value ? G_VALUE_TYPE_NAME (value) : "(null)");
+            goto error;
         }
     }
 
-    return TRUE;
+    if (value != NULL)
+    {
+        if (callback != NULL)
+            callback (account, value, NULL, user_data);
+        tp_g_value_slice_free (value);
+
+        return;
+    }
+
+error:
+    if (callback != NULL)
+        callback (account, NULL, error, user_data);
+    if (error != NULL)
+        g_error_free (error);
 }
 
-static gboolean mcd_account_check_parameters (McdAccount *account);
+
+typedef void (*CheckParametersCb) (McdAccount *account, gboolean valid,
+                                   gpointer user_data);
+static void mcd_account_check_parameters (McdAccount *account,
+    CheckParametersCb callback, gpointer user_data);
+
+static void
+manager_ready_check_params_cb (McdAccount *account,
+    gboolean valid,
+    gpointer user_data)
+{
+    McdAccountPrivate *priv = account->priv;
+
+    priv->valid = valid;
+    mcd_account_loaded (account);
+}
 
 static void on_manager_ready (McdManager *manager, const GError *error,
                               gpointer user_data)
 {
     McdAccount *account = MCD_ACCOUNT (user_data);
-    McdAccountPrivate *priv = account->priv;
 
     if (error)
     {
         DEBUG ("got error: %s", error->message);
+        mcd_account_loaded (account);
     }
     else
     {
-        priv->valid = mcd_account_check_parameters (account);
+        mcd_account_check_parameters (account, manager_ready_check_params_cb,
+                                      NULL);
     }
-    mcd_account_loaded (account);
 }
 
 static gboolean
@@ -1122,10 +1176,16 @@ get_avatar (TpSvcDBusProperties *self, const gchar *name, GValue *value)
 static void
 get_parameters (TpSvcDBusProperties *self, const gchar *name, GValue *value)
 {
-    McdAccount *account = MCD_ACCOUNT (self);
-    GHashTable *parameters;
+    GHashTable *parameters = NULL;
+
+    /* TODO FIXME RIGHT NOW: move to async dbus properties solution
+     * so we can call dup_parameters
+     * parameters = _mcd_account_dup_parameters (account);
+     * For now, just return an empty asv.
+     */
+    parameters = g_hash_table_new_full (g_str_hash, g_str_equal,
+        NULL, (GDestroyNotify) tp_g_value_slice_free);
 
-    parameters = _mcd_account_dup_parameters (account);
     g_value_init (value, TP_HASH_TYPE_STRING_VARIANT_MAP);
     g_value_take_boxed (value, parameters);
 }
@@ -1591,50 +1651,90 @@ account_remove (TpSvcAccount *svc, DBusGMethodInvocation *context)
     mcd_account_delete (self, account_remove_delete_cb, data);
 }
 
-/*
+/**
  * mcd_account_get_parameter:
  * @account: the #McdAccount.
  * @name: the parameter name.
- * @value: a initialized #GValue to receive the parameter value, or %NULL.
- *
- * Get the @name parameter for @account.
+ * @callback: function to call with the value
+ * @user_data: data to pass to @callback
  *
- * Returns: %TRUE if found, %FALSE otherwise.
+ * Get the @name parameter for @account, asynchronously.
  */
-static gboolean
+static void
 mcd_account_get_parameter (McdAccount *account, const gchar *name,
-                           GValue *value)
+                           McdAccountGetParameterCb callback,
+                           gpointer user_data)
 {
-    return MCD_ACCOUNT_GET_CLASS (account)->get_parameter (account, name,
-                                                           value);
+    MCD_ACCOUNT_GET_CLASS (account)->get_parameter (account, name,
+                                                    callback, user_data);
 }
 
-static gboolean
-mcd_account_check_parameters (McdAccount *account)
+typedef struct
+{
+  McdAccount *account;
+  const TpConnectionManagerParam *param;
+  CheckParametersCb callback;
+  gpointer user_data;
+} CheckParameterData;
+
+static void
+check_parameters_get_param_cb (McdAccount *account, const GValue *value,
+                               const GError *error, gpointer user_data)
+{
+    CheckParameterData *data = (CheckParameterData *) user_data;
+    const TpConnectionManagerParam *p;
+
+    if ((account != NULL && value == NULL) || error != NULL)
+    {
+        data->callback (data->account, FALSE, data->user_data);
+    }
+    else
+    {
+        while (data->param->name != NULL
+            && !(data->param->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED))
+        {
+            data->param++;
+        }
+
+        if (data->param->name != NULL)
+        {
+            p = data->param++;
+            mcd_account_get_parameter (data->account, p->name,
+                                       check_parameters_get_param_cb, data);
+        }
+        else
+        {
+            data->callback (data->account, TRUE, data->user_data);
+            g_slice_free (CheckParameterData, data);
+        }
+    }
+}
+
+static void
+mcd_account_check_parameters (McdAccount *account,
+                              CheckParametersCb callback,
+                              gpointer user_data)
 {
     McdAccountPrivate *priv = account->priv;
     const TpConnectionManagerParam *param;
-    gboolean valid;
+    CheckParameterData *data;
 
     DEBUG ("called for %s", priv->unique_name);
     param = mcd_manager_get_parameters (priv->manager, priv->protocol_name);
-    if (!param) return FALSE;
-    valid = TRUE;
-    while (param->name != NULL)
+    if (!param)
     {
-        if (param->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED)
-	{
-	    if (!mcd_account_get_parameter (account, param->name, NULL))
-	    {
-                DEBUG ("missing required parameter %s", param->name);
-		valid = FALSE;
-		break;
-	    }
-	}
-        param++;
+        if (callback != NULL)
+            callback (account, FALSE, user_data);
+        return;
     }
 
-    return valid;
+    data = g_slice_new0 (CheckParameterData);
+    data->account = account;
+    data->param = param;
+    data->callback = callback;
+    data->user_data = user_data;
+
+    check_parameters_get_param_cb (NULL, NULL, NULL, data);
 }
 
 /*
@@ -1658,206 +1758,392 @@ _mcd_account_set_parameter (McdAccount *account, const gchar *name,
                                                     callback, user_data);
 }
 
+static GHashTable *
+hash_table_copy (GHashTable *orig)
+{
+    GHashTable *dest;
+
+    dest = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                  (GDestroyNotify) g_free,
+                                  (GDestroyNotify) tp_g_value_slice_free);
+    tp_g_hash_table_update (dest, orig, (GBoxedCopyFunc) g_strdup,
+                            (GBoxedCopyFunc) tp_g_value_slice_dup);
+    return dest;
+}
+
+typedef struct
+{
+  McdAccount *account;
+  GHashTable *params;
+  GHashTableIter iter;
+  gchar **unset;
+  gchar **unset_iter;
+  const TpConnectionManagerParam *param;
+  const GValue *new;
+  guint n_params;
+  GSList *dbus_properties;
+  GPtrArray *not_yet;
+  McdAccountSetParametersCb *callback;
+  gpointer user_data;
+} SetParametersData;
+
+static void
+set_parameters_data_free (SetParametersData *data)
+{
+    if (data->account != NULL)
+        g_object_unref (data->account);
+
+    if (data->params != NULL)
+        g_hash_table_destroy (data->params);
+
+    if (data->unset != NULL)
+        g_strfreev (data->unset);
+
+    if (data->dbus_properties != NULL)
+        g_slist_free (data->dbus_properties);
+
+    g_slice_free (SetParametersData, data);
+}
+
+static void
+set_parameters_finish (SetParametersData *data)
+{
+    McdAccountPrivate *priv = data->account->priv;
+
+    if (mcd_account_get_connection_status (data->account) ==
+        TP_CONNECTION_STATUS_CONNECTED)
+    {
+        GSList *list;
+        const gchar *name;
+        const GValue *value;
+
+        for (list = data->dbus_properties; list != NULL; list = list->next)
+        {
+            name = list->data;
+            DEBUG ("updating parameter %s", name);
+            value = g_hash_table_lookup (data->params, name);
+            _mcd_connection_update_property (priv->connection, name, value);
+        }
+    }
+
+    mcd_account_check_validity (data->account, NULL, NULL);
+    _mcd_account_maybe_autoconnect (data->account);
+
+    if (data->callback != NULL)
+    {
+        data->callback (data->account, data->not_yet, NULL, data->user_data);
+    }
+    set_parameters_data_free (data);
+}
+
+static void set_parameters_unset_single (McdAccount *account,
+                                         const GError *error,
+                                         gpointer user_data);
+
+
+static void
+set_parameters_unset_check_present (McdAccount *account,
+                                    const GValue *value,
+                                    const GError *error,
+                                    gpointer user_data)
+{
+    SetParametersData *data = (SetParametersData *) user_data;
+
+    if (value != NULL)
+    {
+        DEBUG ("unsetting %s", *data->unset_iter);
+        /* pessimistically assume that removing any parameter merits
+         * reconnection (in a perfect implementation, if the
+         * Has_Default flag was set we'd check whether the current
+         * value is the default already) */
+
+        g_ptr_array_add (data->not_yet, g_strdup (*data->unset_iter));
+    }
+    _mcd_account_set_parameter (data->account, *data->unset_iter, NULL,
+                                set_parameters_unset_single, data);
+}
+
+static void
+set_parameters_unset_single (McdAccount *account, const GError *error,
+                             gpointer user_data)
+{
+    SetParametersData *data = (SetParametersData *) user_data;
+
+    if (data->unset == NULL)
+    {
+        set_parameters_finish (data);
+        return;
+    }
+
+    if (account == NULL)
+    {
+        data->unset_iter = data->unset; /* first time */
+    }
+    else
+    {
+        data->unset_iter++;
+    }
+
+    if (*data->unset_iter != NULL)
+    {
+        mcd_account_get_parameter (data->account, *data->unset_iter,
+                                   set_parameters_unset_check_present, data);
+    }
+    else
+    {
+        set_parameters_finish (data);
+    }
+}
+
+static void
+set_parameters_set_single (McdAccount *account,
+                           const GError *error,
+                           gpointer user_data)
+{
+    SetParametersData *data = (SetParametersData *) user_data;
+    const gchar *name;
+    const GValue *value;
+
+    if (g_hash_table_iter_next (&data->iter, (gpointer) &name, (gpointer) &value))
+    {
+        _mcd_account_set_parameter (data->account, name, value,
+                                    set_parameters_set_single, data);
+    }
+    else
+    {
+        /* End of the hash table */
+        set_parameters_unset_single (NULL, NULL, data);
+    }
+}
+
+static void
+set_parameters_iter_param (McdAccount *account,
+                           const GValue *ret_value,
+                           const GError *error,
+                           gpointer user_data)
+{
+    SetParametersData *data = (SetParametersData *) user_data;
+    GError *out_error = NULL;
+
+    /* account == NULL means this is the first run by
+     * _mcd_account_set_parameters itself or in this function,
+     * but with no call to mcd_account_get_parameter. */
+    if (account != NULL
+        && (ret_value == NULL || !value_is_same (ret_value, data->new)))
+    {
+        DEBUG ("Parameter %s changed", data->param->name);
+        /* can the param be updated on the fly? If yes, prepare to
+         * do so; and if not, prepare to reset the connection */
+        if (data->param->flags & TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY)
+        {
+            data->dbus_properties = g_slist_prepend (data->dbus_properties,
+                                                     data->param->name);
+        }
+        else
+        {
+            g_ptr_array_add (data->not_yet, g_strdup (data->param->name));
+        }
+
+        data->param++;
+    }
+
+    if (data->param->name != NULL)
+    {
+        GType type;
+
+        type = mc_param_type (data->param);
+        data->new = g_hash_table_lookup (data->params, data->param->name);
+
+        if (data->new != NULL)
+        {
+            DEBUG ("Got param %s", data->param->name);
+            data->n_params++;
+
+            if (G_VALUE_TYPE (data->new) != type)
+            {
+                /* FIXME: define proper error */
+                g_set_error (&out_error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+                             "parameter %s must be of type %s, not %s",
+                             data->param->name,
+                             g_type_name (type), G_VALUE_TYPE_NAME (data->new));
+                goto error;
+            }
+
+            if (mcd_account_get_connection_status (data->account) ==
+                TP_CONNECTION_STATUS_CONNECTED)
+            {
+              mcd_account_get_parameter (data->account, data->param->name,
+                                         set_parameters_iter_param, data);
+            }
+            else
+            {
+                data->param++;
+                set_parameters_iter_param (NULL, NULL, NULL, data);
+            }
+        }
+        else
+        {
+            data->param++;
+            set_parameters_iter_param (NULL, NULL, NULL, data);
+        }
+    }
+    else
+    {
+        if (data->n_params != g_hash_table_size (data->params))
+        {
+            g_set_error (&out_error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+                         "Not all parameters were recognized");
+            goto error;
+        }
+        g_hash_table_iter_init (&data->iter, data->params);
+        set_parameters_set_single (data->account, NULL, data);
+    }
+
+    return;
+
+error:
+    if (data->callback != NULL)
+      data->callback (data->account, data->not_yet, out_error, data->user_data);
+
+    set_parameters_data_free (data);
+}
+
 /*
  * _mcd_account_set_parameters:
  * @account: the #McdAccount.
  * @name: the parameter name.
  * @params: names and values of parameters to set
  * @unset: names of parameters to unset
- * @not_yet: if not %NULL, borrowed names of parameters that cannot take
- *  effect until Reconnect() is called will be appended to this array
+ * @callback: function to be called when finished
+ * @user_data: data to be passed to @callback
  *
  * Alter the account parameters.
  *
- * Returns: %TRUE (possibly appending borrowed strings to @not_yet) on success,
- *  %FALSE (setting @error) on failure
  */
-gboolean
+void
 _mcd_account_set_parameters (McdAccount *account, GHashTable *params,
-                             const gchar ** unset, GPtrArray *not_yet,
-                             GError **error)
+                             const gchar **unset,
+                             McdAccountSetParametersCb callback,
+                             gpointer user_data)
 {
     McdAccountPrivate *priv = account->priv;
     const TpConnectionManagerParam *param;
-    guint n_params = 0;
-    GHashTableIter iter;
-    const gchar *name;
-    const GValue *value;
     GSList *dbus_properties = NULL;
-    gboolean reset_connection;
+    GPtrArray *not_yet = NULL;
+    SetParametersData *data;
+    GError *error = NULL;
+    guint unset_size;
 
     DEBUG ("called");
     if (G_UNLIKELY (!priv->manager && !load_manager (account)))
     {
-        g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+        g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
                      "Manager %s not found", priv->manager_name);
-        return FALSE;
+        goto error;
     }
 
     param = mcd_manager_get_parameters (priv->manager, priv->protocol_name);
     if (G_UNLIKELY (!param))
     {
-        g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+        g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
                      "Protocol %s not found", priv->protocol_name);
-        return FALSE;
+        goto error;
     }
 
-    reset_connection = FALSE;
-    while (param->name != NULL)
-    {
-	GType type;
+    unset_size = (unset != NULL) ? g_strv_length ((gchar **) unset) : 0;
 
-	type = mc_param_type (param);
-	value = g_hash_table_lookup (params, param->name);
-	if (value)
-	{
-            DEBUG ("Got param %s", param->name);
-	    if (G_VALUE_TYPE (value) != type)
-	    {
-		/* FIXME: define proper error */
-		g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
-			     "parameter %s must be of type %s, not %s",
-			     param->name,
-			     g_type_name (type), G_VALUE_TYPE_NAME (value));
-		return FALSE;
-	    }
+    /* pessimistically assume that every parameter mentioned will be deferred
+     * until reconnection */
+    not_yet = g_ptr_array_sized_new (g_hash_table_size (params) + unset_size);
+
+    data = g_slice_new0 (SetParametersData);
+    data->account = g_object_ref (account);
+    data->params = hash_table_copy (params);
+    data->unset = g_strdupv ((gchar **) unset);
+    data->param = param;
+    data->n_params = 0;
+    data->dbus_properties = dbus_properties;
+    data->not_yet = not_yet;
+    data->callback = callback;
+    data->user_data = user_data;
 
-            if (mcd_account_get_connection_status (account) ==
-                TP_CONNECTION_STATUS_CONNECTED)
-            {
-                GValue old = { 0 };
+    set_parameters_iter_param (NULL, NULL, NULL, data);
+    return;
 
-                g_value_init (&old, type);
-                if (!mcd_account_get_parameter (account, param->name, &old) ||
-                    !value_is_same (value, &old))
-                {
-                    DEBUG ("Parameter %s changed", param->name);
-                    /* can the param be updated on the fly? If yes, prepare to
-                     * do so; and if not, prepare to reset the connection */
-                    if (param->flags & TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY)
-                    {
-                        dbus_properties = g_slist_prepend (dbus_properties,
-                                                           param->name);
-                    }
-                    else
-                    {
-                        if (not_yet != NULL)
-                        {
-                            /* we assume that the TpConnectionManager won't get
-                             * freed */
-                            g_ptr_array_add (not_yet, param->name);
-                        }
-
-                        reset_connection = TRUE;
-                    }
-                }
-                g_value_unset (&old);
-            }
-	    n_params++;
-	}
-        param++;
-    }
+error:
+    if (callback != NULL)
+        callback (account, NULL, error, user_data);
 
-    if (n_params != g_hash_table_size (params))
-    {
-	g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
-		     "Not all parameters were recognized");
-	return FALSE;
-    }
+    g_error_free (error);
+    if (not_yet != NULL)
+        g_ptr_array_free (not_yet, TRUE);
+}
 
-    g_hash_table_iter_init (&iter, params);
-    while (g_hash_table_iter_next (&iter, (gpointer)&name, (gpointer)&value))
-    {
-        _mcd_account_set_parameter (account, name, value, NULL, NULL);
-    }
+typedef struct
+{
+  DBusGMethodInvocation *context;
+  GPtrArray *not_yet;
+} UpdateParametersData;
 
-    if (unset != NULL)
-    {
-        const gchar **unset_iter;
+static void
+update_parameters_dup_params_cb (McdAccount *account, GHashTable *params,
+                                 gpointer user_data)
+{
+    McdAccountPrivate *priv = account->priv;
+    UpdateParametersData *data = (UpdateParametersData *) user_data;
+    GValue value = { 0 };
 
-        for (unset_iter = unset; *unset_iter != NULL; unset_iter++)
-        {
-            if (mcd_account_get_parameter (account, *unset_iter, NULL))
-            {
-                DEBUG ("unsetting %s", *unset_iter);
-                /* pessimistically assume that removing any parameter merits
-                 * reconnection (in a perfect implementation, if the
-                 * Has_Default flag was set we'd check whether the current
-                 * value is the default already) */
-                if (not_yet != NULL)
-                {
-                    /* we assume that the TpConnectionManager won't get
-                     * freed */
-                    g_ptr_array_add (not_yet, (gchar *) *unset_iter);
-                }
+    g_value_init (&value, TP_HASH_TYPE_STRING_VARIANT_MAP);
+    g_value_take_boxed (&value, params);
+    mcd_account_changed_property (account, "Parameters", &value);
+    g_value_unset (&value);
 
-                reset_connection = TRUE;
-            }
+    mcd_account_manager_write_conf_async (priv->account_manager, NULL, NULL);
 
-            _mcd_account_set_parameter (account, *unset_iter, NULL, NULL, NULL);
-        }
-    }
+    g_ptr_array_add (data->not_yet, NULL);
 
-    if (mcd_account_get_connection_status (account) ==
-        TP_CONNECTION_STATUS_CONNECTED)
-    {
-        GSList *list;
+    tp_svc_account_return_from_update_parameters (data->context,
+        (const gchar **) data->not_yet->pdata);
 
-        for (list = dbus_properties; list != NULL; list = list->next)
-        {
-            name = list->data;
-            DEBUG ("updating parameter %s", name);
-            value = g_hash_table_lookup (params, name);
-            _mcd_connection_update_property (priv->connection, name, value);
-        }
-    }
-    g_slist_free (dbus_properties);
+    g_ptr_array_foreach (data->not_yet, (GFunc) g_free, NULL);
+    g_ptr_array_free (data->not_yet, TRUE);
 
-    mcd_account_check_validity (account);
-    _mcd_account_maybe_autoconnect (account);
-    return TRUE;
+    g_slice_free (UpdateParametersData, data);
 }
 
 static void
-account_update_parameters (TpSvcAccount *self, GHashTable *set,
-			   const gchar **unset, DBusGMethodInvocation *context)
+account_update_parameters_cb (McdAccount *account, GPtrArray *not_yet,
+                              const GError *error, gpointer user_data)
 {
-    McdAccount *account = MCD_ACCOUNT (self);
-    McdAccountPrivate *priv = account->priv;
-    GHashTable *parameters;
-    GValue value = { 0 };
-    GError *error = NULL;
-    GPtrArray *not_yet;
-
-    DEBUG ("called for %s", priv->unique_name);
-
-    /* pessimistically assume that every parameter mentioned will be deferred
-     * until reconnection */
-    not_yet = g_ptr_array_sized_new (g_hash_table_size (set) +
-                                     g_strv_length ((gchar **) unset) + 1);
+    DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+    UpdateParametersData *data;
 
-    if (!_mcd_account_set_parameters (account, set, unset, not_yet, &error))
+    if (error != NULL)
     {
-        g_ptr_array_free (not_yet, TRUE);
-        dbus_g_method_return_error (context, error);
-        g_error_free (error);
+        dbus_g_method_return_error (context, (GError *) error);
         return;
     }
 
+    data = g_slice_new0 (UpdateParametersData);
+    data->not_yet = not_yet;
+    data->context = context;
+
     /* emit the PropertiesChanged signal */
-    parameters = _mcd_account_dup_parameters (account);
-    g_value_init (&value, TP_HASH_TYPE_STRING_VARIANT_MAP);
-    g_value_take_boxed (&value, parameters);
-    mcd_account_changed_property (account, "Parameters", &value);
-    g_value_unset (&value);
+    _mcd_account_dup_parameters (account, update_parameters_dup_params_cb,
+                                 data);
+}
 
-    mcd_account_manager_write_conf_async (priv->account_manager, NULL, NULL);
+static void
+account_update_parameters (TpSvcAccount *self, GHashTable *set,
+			   const gchar **unset, DBusGMethodInvocation *context)
+{
+    McdAccount *account = MCD_ACCOUNT (self);
+    McdAccountPrivate *priv = account->priv;
 
-    g_ptr_array_add (not_yet, NULL);
+    DEBUG ("called for %s", priv->unique_name);
 
-    tp_svc_account_return_from_update_parameters (context,
-        (const gchar **) not_yet->pdata);
-    g_ptr_array_free (not_yet, TRUE);
+    _mcd_account_set_parameters (account, set, unset,
+                                 account_update_parameters_cb, context);
 }
 
 static void
@@ -2320,54 +2606,93 @@ mcd_account_get_object_path (McdAccount *account)
     return account->priv->object_path;
 }
 
-static inline void
-add_parameter (McdAccount *account, const TpConnectionManagerParam *param,
-	       GHashTable *params)
+typedef struct
 {
-    GValue *value;
-    GType type;
-
-    type = mc_param_type (param);
-    if (G_UNLIKELY (type == G_TYPE_INVALID)) return;
-
-    value = tp_g_value_slice_new (type);
+    GHashTable *params;
+    const TpConnectionManagerParam *param;
+    McdAccountDupParametersCb callback;
+    gpointer user_data;
+} DupParametersData;
 
-    if (mcd_account_get_parameter (account, param->name, value))
-        g_hash_table_insert (params, g_strdup (param->name), value);
-    else
-        tp_g_value_slice_free (value);
+static void
+dup_parameters_get_parameter_cb (McdAccount *account,
+                                 const GValue *value,
+                                 const GError *error,
+                                 gpointer user_data)
+{
+  DupParametersData *data = (DupParametersData *) user_data;
+
+  if (value != NULL)
+  {
+      g_hash_table_insert (data->params, g_strdup (data->param->name),
+                           tp_g_value_slice_dup (value));
+  }
+
+  data->param++;
+
+  if (data->param->name != NULL)
+  {
+      mcd_account_get_parameter (account, data->param->name,
+                                 dup_parameters_get_parameter_cb, data);
+  }
+  else
+  {
+      if (data->callback != NULL)
+          data->callback (account, data->params, data->user_data);
+      g_slice_free (DupParametersData, data);
+  }
 }
 
-/*
+/**
  * _mcd_account_dup_parameters:
  * @account: the #McdAccount.
+ * @callback: function to call with the result
+ * @user_data: data to pass to @callback
  *
- * Get the parameters set for this account.
- *
- * Returns: a newly allocated #GHashTable containing the account parameters.
+ * Get the parameters set for this account. The resulting #GHashTable in the
+ * callback will be newly allocated and must be g_hash_table_unref() 'd after
+ * use.
  */
-GHashTable *
-_mcd_account_dup_parameters (McdAccount *account)
+void
+_mcd_account_dup_parameters (McdAccount *account,
+                             McdAccountDupParametersCb callback,
+                             gpointer user_data)
 {
-    McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account);
+    McdAccountPrivate *priv;
+    DupParametersData *data;
     const TpConnectionManagerParam *param;
-    GHashTable *params;
+
+    g_return_if_fail (MCD_IS_ACCOUNT (account));
+
+    priv = account->priv;
 
     DEBUG ("called");
-    if (!priv->manager && !load_manager (account)) return NULL;
+    if (!priv->manager && !load_manager (account))
+    {
+        callback (account, NULL, user_data);
+        return;
+    }
 
-    params = g_hash_table_new_full (g_str_hash, g_str_equal,
-				    g_free,
-                                    (GDestroyNotify)tp_g_value_slice_free);
-    param = mcd_manager_get_parameters (priv->manager, priv->protocol_name);
-    if (G_UNLIKELY (!param)) return params;
+    param = mcd_manager_get_parameters (priv->manager,
+                                        priv->protocol_name);
 
-    while (param->name != NULL)
+    if (G_UNLIKELY (!param))
     {
-	add_parameter (account, param, params);
-        param++;
+        callback (account, NULL, user_data);
+        return;
     }
-    return params;
+
+    data = g_slice_new0 (DupParametersData);
+    data->param = param;
+    data->callback = callback;
+    data->user_data = user_data;
+
+    data->params = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                          g_free,
+                                          (GDestroyNotify) tp_g_value_slice_free);
+
+    mcd_account_get_parameter (account, param->name,
+                               dup_parameters_get_parameter_cb, data);
 }
 
 /**
@@ -2840,25 +3165,31 @@ mcd_account_get_connection (McdAccount *account)
     return priv->connection;
 }
 
-gboolean
-mcd_account_check_validity (McdAccount *account)
+typedef struct
 {
-    McdAccountPrivate *priv = account->priv;
-    gboolean valid;
+    McdAccountCheckValidityCb callback;
+    gpointer user_data;
+} CheckValidityData;
 
-    valid = (priv->loaded && mcd_account_check_parameters (account));
+static void
+check_validity_check_parameters_cb (McdAccount *account,
+                                    gboolean valid,
+                                    gpointer user_data)
+{
+    CheckValidityData *data = (CheckValidityData *) user_data;
+    McdAccountPrivate *priv = account->priv;
 
     if (valid != priv->valid)
     {
-	GValue value = { 0 };
+        GValue value = { 0 };
         DEBUG ("Account validity changed (old: %d, new: %d)",
                priv->valid, valid);
-	priv->valid = valid;
-	g_signal_emit (account, _mcd_account_signals[VALIDITY_CHANGED], 0,
-		       valid);
-	g_value_init (&value, G_TYPE_BOOLEAN);
-	g_value_set_boolean (&value, valid);
-	mcd_account_changed_property (account, "Valid", &value);
+        priv->valid = valid;
+        g_signal_emit (account, _mcd_account_signals[VALIDITY_CHANGED], 0,
+                       valid);
+        g_value_init (&value, G_TYPE_BOOLEAN);
+        g_value_set_boolean (&value, valid);
+        mcd_account_changed_property (account, "Valid", &value);
 
         if (valid)
         {
@@ -2869,7 +3200,28 @@ mcd_account_check_validity (McdAccount *account)
                                               priv->req_presence_message);
         }
     }
-    return valid;
+
+    if (data->callback != NULL)
+        data->callback (account, valid, data->user_data);
+
+    g_slice_free (CheckValidityData, data);
+}
+
+void
+mcd_account_check_validity (McdAccount *account,
+                            McdAccountCheckValidityCb callback,
+                            gpointer user_data)
+{
+    CheckValidityData *data;
+
+    g_return_if_fail (MCD_IS_ACCOUNT (account));
+
+    data = g_slice_new0 (CheckValidityData);
+    data->callback = callback;
+    data->user_data = user_data;
+
+    mcd_account_check_parameters (account, check_validity_check_parameters_cb,
+                                  data);
 }
 
 /*
diff --git a/src/mcd-account.h b/src/mcd-account.h
index 16aa229..85c2f28 100644
--- a/src/mcd-account.h
+++ b/src/mcd-account.h
@@ -57,12 +57,17 @@ typedef void (*McdAccountDeleteCb) (McdAccount *account,
 typedef void (*McdAccountSetParameterCb) (McdAccount *account,
                                           const GError *error,
                                           gpointer user_data);
+typedef void (*McdAccountGetParameterCb) (McdAccount *account,
+                                          const GValue *value,
+                                          const GError *error,
+                                          gpointer user_data);
 
 struct _McdAccountClass
 {
     GObjectClass parent_class;
-    gboolean (*get_parameter) (McdAccount *account, const gchar *name,
-                               GValue *value);
+    void (*get_parameter) (McdAccount *account, const gchar *name,
+                           McdAccountGetParameterCb callback,
+                           gpointer user_data);
     void (*set_parameter) (McdAccount *account, const gchar *name,
                            const GValue *value,
                            McdAccountSetParameterCb callback,
@@ -92,7 +97,13 @@ const gchar *mcd_account_get_unique_name (McdAccount *account);
 const gchar *mcd_account_get_object_path (McdAccount *account);
 
 gboolean mcd_account_is_valid (McdAccount *account);
-gboolean mcd_account_check_validity (McdAccount *account);
+
+typedef void (*McdAccountCheckValidityCb) (McdAccount *account,
+                                           gboolean valid,
+                                           gpointer user_data);
+void mcd_account_check_validity (McdAccount *account,
+                                 McdAccountCheckValidityCb callback,
+                                 gpointer user_data);
 
 gboolean mcd_account_is_enabled (McdAccount *account);
 
-- 
1.5.6.5




More information about the telepathy-commits mailing list