Blame src/goabackend/goatpaccountlinker.c

Packit Service c6b9b0
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
Packit Service c6b9b0
/*
Packit Service c6b9b0
 * Copyright © 2010 – 2013 Collabora Ltd.
Packit Service c6b9b0
 * Copyright © 2013 Intel Corporation
Packit Service c6b9b0
 *
Packit Service c6b9b0
 * This library is free software; you can redistribute it and/or
Packit Service c6b9b0
 * modify it under the terms of the GNU Lesser General Public
Packit Service c6b9b0
 * License as published by the Free Software Foundation; either
Packit Service c6b9b0
 * version 2 of the License, or (at your option) any later version.
Packit Service c6b9b0
 *
Packit Service c6b9b0
 * This library is distributed in the hope that it will be useful,
Packit Service c6b9b0
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service c6b9b0
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service c6b9b0
 * Lesser General Public License for more details.
Packit Service c6b9b0
 *
Packit Service c6b9b0
 * You should have received a copy of the GNU Lesser General
Packit Service c6b9b0
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit Service c6b9b0
 */
Packit Service c6b9b0
Packit Service c6b9b0
/* This class makes sure we have a GOA account for each Telepathy account
Packit Service c6b9b0
 * configured in the system.
Packit Service c6b9b0
 * Note that this handles only plain Telepathy accounts; the ones with
Packit Service c6b9b0
 * multiple capabilities (e.g. Facebook) are handled differently. */
Packit Service c6b9b0
Packit Service c6b9b0
#include "config.h"
Packit Service c6b9b0
Packit Service c6b9b0
#include <gio/gio.h>
Packit Service c6b9b0
#include <telepathy-glib/telepathy-glib.h>
Packit Service c6b9b0
Packit Service c6b9b0
#include "goatpaccountlinker.h"
Packit Service c6b9b0
#include "goabackend/goautils.h"
Packit Service c6b9b0
Packit Service c6b9b0
#define GOA_TP_ACCOUNT_LINKER_GET_PRIVATE(obj) \
Packit Service c6b9b0
  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOA_TYPE_TP_ACCOUNT_LINKER, \
Packit Service c6b9b0
                                GoaTpAccountLinkerPrivate))
Packit Service c6b9b0
Packit Service c6b9b0
G_DEFINE_TYPE (GoaTpAccountLinker, goa_tp_account_linker, G_TYPE_OBJECT)
Packit Service c6b9b0
Packit Service c6b9b0
struct _GoaTpAccountLinkerPrivate
Packit Service c6b9b0
{
Packit Service c6b9b0
  TpAccountManager *account_manager;
Packit Service c6b9b0
  GoaClient *goa_client;
Packit Service c6b9b0
Packit Service c6b9b0
  GHashTable *tp_accounts; /* owned gchar *id -> reffed TpAccount * */
Packit Service c6b9b0
  GHashTable *goa_accounts; /* owned gchar *id -> reffed GoaObject * */
Packit Service c6b9b0
Packit Service c6b9b0
  GQueue *remove_tp_account_queue;
Packit Service c6b9b0
};
Packit Service c6b9b0
Packit Service c6b9b0
/* The path of the Telepathy account is used as common identifier between
Packit Service c6b9b0
 * GOA and Telepathy */
Packit Service c6b9b0
static const gchar *
Packit Service c6b9b0
get_id_from_tp_account (TpAccount *tp_account)
Packit Service c6b9b0
{
Packit Service c6b9b0
  return tp_proxy_get_object_path (tp_account);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static const gchar *
Packit Service c6b9b0
get_id_from_goa_account (GoaAccount *goa_account)
Packit Service c6b9b0
{
Packit Service c6b9b0
  return goa_account_get_identity (goa_account);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static gboolean
Packit Service c6b9b0
is_telepathy_account (GoaAccount *goa_account)
Packit Service c6b9b0
{
Packit Service c6b9b0
  const gchar *type = goa_account_get_provider_type (goa_account);
Packit Service c6b9b0
  return g_str_has_prefix (type, "telepathy/");
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
tp_account_removed_by_us_cb (GObject      *object,
Packit Service c6b9b0
                             GAsyncResult *res,
Packit Service c6b9b0
                             gpointer      user_data)
Packit Service c6b9b0
{
Packit Service c6b9b0
  TpAccount *tp_account = TP_ACCOUNT (object);
Packit Service c6b9b0
  GError *error = NULL;
Packit Service c6b9b0
  GTask *task = G_TASK (user_data);
Packit Service c6b9b0
Packit Service c6b9b0
  if (!tp_account_remove_finish (tp_account, res, &error))
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_critical ("Error removing Telepathy account %s: %s (%s, %d)",
Packit Service c6b9b0
          get_id_from_tp_account (tp_account),
Packit Service c6b9b0
          error->message,
Packit Service c6b9b0
          g_quark_to_string (error->domain),
Packit Service c6b9b0
          error->code);
Packit Service c6b9b0
      g_task_return_error (task, error);
Packit Service c6b9b0
      goto out;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  g_task_return_boolean (task, TRUE);
Packit Service c6b9b0
Packit Service c6b9b0
 out:
Packit Service c6b9b0
  g_object_unref (task);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
remove_tp_account_queue_check (GoaTpAccountLinker *self)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
  GList *l;
Packit Service c6b9b0
Packit Service c6b9b0
  if (priv->goa_client == NULL ||
Packit Service c6b9b0
      priv->account_manager == NULL ||
Packit Service c6b9b0
      !tp_proxy_is_prepared (priv->account_manager, TP_ACCOUNT_MANAGER_FEATURE_CORE))
Packit Service c6b9b0
    {
Packit Service c6b9b0
      /* Not everything is ready yet */
Packit Service c6b9b0
      return;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  if (priv->remove_tp_account_queue->length == 0)
Packit Service c6b9b0
    return;
Packit Service c6b9b0
Packit Service c6b9b0
  for (l = priv->remove_tp_account_queue->head; l != NULL; l = l->next)
Packit Service c6b9b0
    {
Packit Service c6b9b0
      GTask *task = G_TASK (l->data);
Packit Service c6b9b0
      GoaAccount *goa_account;
Packit Service c6b9b0
      GoaObject *goa_object;
Packit Service c6b9b0
      TpAccount *tp_account;
Packit Service c6b9b0
      const gchar *id;
Packit Service c6b9b0
Packit Service c6b9b0
      goa_object = GOA_OBJECT (g_task_get_task_data (task));
Packit Service c6b9b0
      goa_account = goa_object_peek_account (goa_object);
Packit Service c6b9b0
Packit Service c6b9b0
      id = get_id_from_goa_account (goa_account);
Packit Service c6b9b0
      if (!g_hash_table_remove (priv->goa_accounts, id))
Packit Service c6b9b0
        {
Packit Service c6b9b0
          /* 1 - The user removes the Telepathy account (but not the GOA one)
Packit Service c6b9b0
           * 2 - We delete the corresponding GOA account and remove it
Packit Service c6b9b0
           *     from priv->goa_accounts
Packit Service c6b9b0
           * 3 - The Telepathy provider again tries to remove the
Packit Service c6b9b0
           *     corresponding Telepathy account
Packit Service c6b9b0
           */
Packit Service c6b9b0
          g_debug ("Ignoring removal of GOA account we asked to remove "
Packit Service c6b9b0
                   "(%s, Telepathy object path: %s)",
Packit Service c6b9b0
                   goa_account_get_id (goa_account),
Packit Service c6b9b0
                   id);
Packit Service c6b9b0
          g_task_return_boolean (task, TRUE);
Packit Service c6b9b0
          continue;
Packit Service c6b9b0
        }
Packit Service c6b9b0
Packit Service c6b9b0
      g_info ("GOA account %s for Telepathy account %s removed, "
Packit Service c6b9b0
              "removing Telepathy account",
Packit Service c6b9b0
              goa_account_get_id (goa_account), id);
Packit Service c6b9b0
Packit Service c6b9b0
      tp_account = g_hash_table_lookup (priv->tp_accounts, id);
Packit Service c6b9b0
      if (tp_account == NULL)
Packit Service c6b9b0
        {
Packit Service c6b9b0
          g_critical ("There is no Telepathy account for removed GOA "
Packit Service c6b9b0
                      "account %s (Telepathy object path: %s)",
Packit Service c6b9b0
                      goa_account_get_id (goa_account), id);
Packit Service c6b9b0
          g_task_return_boolean (task, TRUE);
Packit Service c6b9b0
          continue;
Packit Service c6b9b0
        }
Packit Service c6b9b0
      tp_account_remove_async (tp_account, tp_account_removed_by_us_cb, g_object_ref (task));
Packit Service c6b9b0
      g_hash_table_remove (priv->tp_accounts, id);
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  g_queue_foreach (priv->remove_tp_account_queue, (GFunc) g_object_unref, NULL);
Packit Service c6b9b0
  g_queue_clear (priv->remove_tp_account_queue);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
goa_account_chat_disabled_changed_cb (GoaAccount         *goa_account,
Packit Service c6b9b0
                                      GParamSpec         *spec,
Packit Service c6b9b0
                                      GoaTpAccountLinker *self)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
  const gchar *id;
Packit Service c6b9b0
  TpAccount *tp_account;
Packit Service c6b9b0
  gboolean tp_enabled;
Packit Service c6b9b0
  gboolean goa_enabled;
Packit Service c6b9b0
Packit Service c6b9b0
  id = get_id_from_goa_account (goa_account);
Packit Service c6b9b0
  tp_account = g_hash_table_lookup (priv->tp_accounts, id);
Packit Service c6b9b0
  if (tp_account == NULL)
Packit Service c6b9b0
    return;
Packit Service c6b9b0
Packit Service c6b9b0
  goa_enabled = !goa_account_get_chat_disabled (goa_account);
Packit Service c6b9b0
  tp_enabled = tp_account_is_enabled (tp_account);
Packit Service c6b9b0
  if (tp_enabled != goa_enabled)
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_info ("The GOA account %s (Telepathy object path: %s) has been %s, "
Packit Service c6b9b0
          "propagating to Telepathy",
Packit Service c6b9b0
          goa_account_get_id (goa_account), id,
Packit Service c6b9b0
          goa_enabled ? "enabled" : "disabled");
Packit Service c6b9b0
      tp_account_set_enabled_async (tp_account, goa_enabled, NULL, NULL);
Packit Service c6b9b0
    }
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
tp_account_chat_enabled_changed_cb (TpAccount          *tp_account,
Packit Service c6b9b0
                                    GParamSpec         *spec,
Packit Service c6b9b0
                                    GoaTpAccountLinker *self)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
  const gchar *id;
Packit Service c6b9b0
  GoaObject *goa_object;
Packit Service c6b9b0
  GoaAccount *goa_account;
Packit Service c6b9b0
  gboolean tp_enabled;
Packit Service c6b9b0
  gboolean goa_enabled;
Packit Service c6b9b0
Packit Service c6b9b0
  id = get_id_from_tp_account (tp_account);
Packit Service c6b9b0
  goa_object = g_hash_table_lookup (priv->goa_accounts, id);
Packit Service c6b9b0
  if (goa_object == NULL)
Packit Service c6b9b0
    return;
Packit Service c6b9b0
Packit Service c6b9b0
  goa_account = goa_object_peek_account (goa_object);
Packit Service c6b9b0
  goa_enabled = !goa_account_get_chat_disabled (goa_account);
Packit Service c6b9b0
  tp_enabled = tp_account_is_enabled (tp_account);
Packit Service c6b9b0
  if (tp_enabled != goa_enabled)
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_info ("The Telepathy account %s has been %s, propagating to GOA",
Packit Service c6b9b0
          id, tp_enabled ? "enabled" : "disabled");
Packit Service c6b9b0
      /* When we set this property, the autogenerated code emits a notify
Packit Service c6b9b0
       * signal immediately even if the property hasn't changed, so
Packit Service c6b9b0
       * goa_account_chat_disabled_changed_cb() thinks that the property
Packit Service c6b9b0
       * changed back to the old value and a cycle starts.
Packit Service c6b9b0
       * The right notify signal will be emitted later when the property is
Packit Service c6b9b0
       * actually changed. */
Packit Service c6b9b0
      g_signal_handlers_block_by_func (goa_account,
Packit Service c6b9b0
          goa_account_chat_disabled_changed_cb, self);
Packit Service c6b9b0
      goa_account_set_chat_disabled (goa_account, !tp_enabled);
Packit Service c6b9b0
      g_signal_handlers_unblock_by_func (goa_account,
Packit Service c6b9b0
          goa_account_chat_disabled_changed_cb, self);
Packit Service c6b9b0
    }
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
goa_account_created_cb (GoaManager   *manager,
Packit Service c6b9b0
                        GAsyncResult *res,
Packit Service c6b9b0
                        gpointer      user_data)
Packit Service c6b9b0
{
Packit Service c6b9b0
  TpAccount *tp_account = user_data;
Packit Service c6b9b0
  gchar *goa_account_object_path = NULL;
Packit Service c6b9b0
  GError *error = NULL;
Packit Service c6b9b0
Packit Service c6b9b0
  if (!goa_manager_call_add_account_finish (manager,
Packit Service c6b9b0
        &goa_account_object_path, res, &error))
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_critical ("Failed to create a GOA account for %s: %s (%s, %d)",
Packit Service c6b9b0
          get_id_from_tp_account (tp_account),
Packit Service c6b9b0
          error->message,
Packit Service c6b9b0
          g_quark_to_string (error->domain),
Packit Service c6b9b0
          error->code);
Packit Service c6b9b0
      g_error_free (error);
Packit Service c6b9b0
      goto out;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  g_info ("Created new %s GOA account for Telepathy account %s",
Packit Service c6b9b0
      goa_account_object_path, get_id_from_tp_account (tp_account));
Packit Service c6b9b0
Packit Service c6b9b0
 out:
Packit Service c6b9b0
  g_object_unref (tp_account);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
create_goa_account (GoaTpAccountLinker *self,
Packit Service c6b9b0
                    TpAccount          *tp_account)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
  GVariantBuilder credentials;
Packit Service c6b9b0
  GVariantBuilder details;
Packit Service c6b9b0
  gchar *provider;
Packit Service c6b9b0
Packit Service c6b9b0
  g_info ("Creating new GOA account for Telepathy account %s",
Packit Service c6b9b0
      get_id_from_tp_account (tp_account));
Packit Service c6b9b0
Packit Service c6b9b0
  g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT);
Packit Service c6b9b0
Packit Service c6b9b0
  g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
Packit Service c6b9b0
  g_variant_builder_add (&details, "{ss}", "ChatEnabled",
Packit Service c6b9b0
      tp_account_is_enabled (tp_account) ? "true" : "false");
Packit Service c6b9b0
Packit Service c6b9b0
  provider = g_strdup_printf ("telepathy/%s",
Packit Service c6b9b0
      tp_account_get_protocol_name (tp_account));
Packit Service c6b9b0
Packit Service c6b9b0
  goa_manager_call_add_account (goa_client_get_manager (priv->goa_client),
Packit Service c6b9b0
      provider,
Packit Service c6b9b0
      get_id_from_tp_account (tp_account),
Packit Service c6b9b0
      tp_account_get_display_name (tp_account),
Packit Service c6b9b0
      g_variant_builder_end (&credentials),
Packit Service c6b9b0
      g_variant_builder_end (&details),
Packit Service c6b9b0
      NULL, /* GCancellable* */
Packit Service c6b9b0
      (GAsyncReadyCallback) goa_account_created_cb,
Packit Service c6b9b0
      g_object_ref (tp_account));
Packit Service c6b9b0
Packit Service c6b9b0
  g_free (provider);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static gboolean
Packit Service c6b9b0
is_account_filtered (TpAccount *tp_account)
Packit Service c6b9b0
{
Packit Service c6b9b0
  const gchar *env;
Packit Service c6b9b0
  const gchar *id;
Packit Service c6b9b0
Packit Service c6b9b0
  env = g_getenv ("GOA_TELEPATHY_DEBUG_ACCOUNT_FILTER");
Packit Service c6b9b0
  if (env == NULL || env[0] == '\0')
Packit Service c6b9b0
    return FALSE;
Packit Service c6b9b0
Packit Service c6b9b0
  id = get_id_from_tp_account (tp_account);
Packit Service c6b9b0
  if (g_strstr_len (id, -1, env) != NULL)
Packit Service c6b9b0
    return FALSE; /* "env" is contained in "id" */
Packit Service c6b9b0
  else
Packit Service c6b9b0
    return TRUE;
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
tp_account_added (GoaTpAccountLinker *self,
Packit Service c6b9b0
                  TpAccount          *tp_account)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
  const gchar *id = get_id_from_tp_account (tp_account);
Packit Service c6b9b0
  GoaObject *goa_object = NULL;
Packit Service c6b9b0
Packit Service c6b9b0
  if (g_strcmp0 (tp_account_get_storage_provider (tp_account),
Packit Service c6b9b0
        "org.gnome.OnlineAccounts") == 0)
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_debug ("Skipping Telepathy account %s as it's handled directly by GOA", id);
Packit Service c6b9b0
      return;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  if (is_account_filtered (tp_account))
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_debug ("The account %s is ignored for debugging reasons", id);
Packit Service c6b9b0
      return;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  g_debug ("Telepathy account found: %s", id);
Packit Service c6b9b0
Packit Service c6b9b0
  g_hash_table_replace (priv->tp_accounts, g_strdup (id),
Packit Service c6b9b0
      g_object_ref (tp_account));
Packit Service c6b9b0
Packit Service c6b9b0
  g_signal_connect_object (tp_account, "notify::enabled",
Packit Service c6b9b0
      G_CALLBACK (tp_account_chat_enabled_changed_cb),
Packit Service c6b9b0
      self, 0);
Packit Service c6b9b0
Packit Service c6b9b0
  goa_object = g_hash_table_lookup (priv->goa_accounts, id);
Packit Service c6b9b0
  if (goa_object == NULL)
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_debug ("Found a Telepathy account with no corresponding "
Packit Service c6b9b0
          "GOA account: %s", id);
Packit Service c6b9b0
      create_goa_account (self, tp_account);
Packit Service c6b9b0
    }
Packit Service c6b9b0
  else
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_debug ("Found a Telepathy account with a matching "
Packit Service c6b9b0
          "GOA account: %s", id);
Packit Service c6b9b0
      /* Make sure the initial state is synced. */
Packit Service c6b9b0
      tp_account_chat_enabled_changed_cb (tp_account, NULL, self);
Packit Service c6b9b0
    }
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
goa_account_removed_by_us_cb (GObject      *object,
Packit Service c6b9b0
                              GAsyncResult *res,
Packit Service c6b9b0
                              gpointer      user_data)
Packit Service c6b9b0
{
Packit Service c6b9b0
  /* This callback is only used for debugging */
Packit Service c6b9b0
  GoaAccount *goa_account = GOA_ACCOUNT (object);
Packit Service c6b9b0
  GError *error = NULL;
Packit Service c6b9b0
Packit Service c6b9b0
  if (!goa_account_call_remove_finish (goa_account, res, &error))
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_critical ("Error removing GOA account %s (Telepathy object path: %s): "
Packit Service c6b9b0
          "%s (%s, %d)",
Packit Service c6b9b0
          goa_account_get_id (goa_account),
Packit Service c6b9b0
          get_id_from_goa_account (goa_account),
Packit Service c6b9b0
          error->message,
Packit Service c6b9b0
          g_quark_to_string (error->domain),
Packit Service c6b9b0
          error->code);
Packit Service c6b9b0
      g_error_free (error);
Packit Service c6b9b0
    }
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
tp_account_removed_cb (TpAccountManager *manager,
Packit Service c6b9b0
                       TpAccount        *tp_account,
Packit Service c6b9b0
                       gpointer          user_data)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinker *self = user_data;
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
  const gchar *id = get_id_from_tp_account (tp_account);
Packit Service c6b9b0
  GoaObject *goa_object = NULL;
Packit Service c6b9b0
Packit Service c6b9b0
  if (!g_hash_table_remove (priv->tp_accounts, id))
Packit Service c6b9b0
    {
Packit Service c6b9b0
      /* 1 - The user removes the GOA account
Packit Service c6b9b0
       * 2 - We delete the corresponding Telepathy account and remove it
Packit Service c6b9b0
       *     from priv->tp_accounts
Packit Service c6b9b0
       * 3 - "account-removed" is emitted by the account manager
Packit Service c6b9b0
       * 4 - tp_account_removed_cb is called for an unknown account
Packit Service c6b9b0
       */
Packit Service c6b9b0
      g_debug ("Ignoring removal of Telepathy account we asked to "
Packit Service c6b9b0
          "remove (%s)", id);
Packit Service c6b9b0
      return;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  g_info ("Telepathy account %s removed, removing corresponding "
Packit Service c6b9b0
      "GOA account", id);
Packit Service c6b9b0
Packit Service c6b9b0
  goa_object = g_hash_table_lookup (priv->goa_accounts, id);
Packit Service c6b9b0
  if (goa_object == NULL)
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_critical ("There is no GOA account for removed Telepathy "
Packit Service c6b9b0
          "account %s", id);
Packit Service c6b9b0
      return;
Packit Service c6b9b0
    }
Packit Service c6b9b0
  goa_account_call_remove (goa_object_peek_account (goa_object),
Packit Service c6b9b0
      NULL, /* cancellable */
Packit Service c6b9b0
      goa_account_removed_by_us_cb, NULL);
Packit Service c6b9b0
  g_hash_table_remove (priv->goa_accounts, id);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
tp_account_validity_changed_cb (TpAccountManager *manager,
Packit Service c6b9b0
                                TpAccount        *tp_account,
Packit Service c6b9b0
                                gboolean          valid,
Packit Service c6b9b0
                                gpointer          user_data)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinker *self = user_data;
Packit Service c6b9b0
Packit Service c6b9b0
  if (valid)
Packit Service c6b9b0
    tp_account_added (self, tp_account);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
goa_account_added_cb (GoaClient *client,
Packit Service c6b9b0
                      GoaObject *goa_object,
Packit Service c6b9b0
                      gpointer   user_data)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinker *self = user_data;
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
  GoaAccount *goa_account = goa_object_peek_account (goa_object);
Packit Service c6b9b0
  const gchar *id = NULL;
Packit Service c6b9b0
  TpAccount *tp_account;
Packit Service c6b9b0
Packit Service c6b9b0
  if (!is_telepathy_account (goa_account))
Packit Service c6b9b0
    return;
Packit Service c6b9b0
Packit Service c6b9b0
  id = get_id_from_goa_account (goa_account);
Packit Service c6b9b0
  g_debug ("GOA account %s for Telepathy account %s added",
Packit Service c6b9b0
      goa_account_get_id (goa_account), id);
Packit Service c6b9b0
Packit Service c6b9b0
  g_signal_connect_object (goa_account, "notify::chat-disabled",
Packit Service c6b9b0
      G_CALLBACK (goa_account_chat_disabled_changed_cb), self, 0);
Packit Service c6b9b0
Packit Service c6b9b0
  g_hash_table_insert (priv->goa_accounts, g_strdup (id),
Packit Service c6b9b0
      g_object_ref (goa_object));
Packit Service c6b9b0
Packit Service c6b9b0
  tp_account = g_hash_table_lookup (priv->tp_accounts, id);
Packit Service c6b9b0
  if (tp_account != NULL)
Packit Service c6b9b0
    {
Packit Service c6b9b0
      /* The chat enabled status may have changed during the creation of the
Packit Service c6b9b0
       * GOA account, so we need to make sure it's synced. */
Packit Service c6b9b0
      tp_account_chat_enabled_changed_cb (tp_account, NULL, self);
Packit Service c6b9b0
    }
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
start_if_ready (GoaTpAccountLinker *self)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
  GList *goa_accounts = NULL;
Packit Service c6b9b0
  GList *tp_accounts = NULL;
Packit Service c6b9b0
  GList *l = NULL;
Packit Service c6b9b0
  GHashTableIter iter;
Packit Service c6b9b0
  gpointer key, value;
Packit Service c6b9b0
Packit Service c6b9b0
  if (priv->goa_client == NULL ||
Packit Service c6b9b0
      priv->account_manager == NULL ||
Packit Service c6b9b0
      !tp_proxy_is_prepared (priv->account_manager,
Packit Service c6b9b0
        TP_ACCOUNT_MANAGER_FEATURE_CORE))
Packit Service c6b9b0
    {
Packit Service c6b9b0
      /* Not everything is ready yet */
Packit Service c6b9b0
      return;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  g_debug ("Both GOA and Tp are ready, starting tracking of accounts");
Packit Service c6b9b0
Packit Service c6b9b0
  /* GOA */
Packit Service c6b9b0
  goa_accounts = goa_client_get_accounts (priv->goa_client);
Packit Service c6b9b0
  for (l = goa_accounts; l != NULL; l = l->next)
Packit Service c6b9b0
    goa_account_added_cb (priv->goa_client, l->data, self);
Packit Service c6b9b0
  g_list_free_full (goa_accounts, g_object_unref);
Packit Service c6b9b0
Packit Service c6b9b0
  g_signal_connect_object (priv->goa_client, "account-added",
Packit Service c6b9b0
      G_CALLBACK (goa_account_added_cb), self, 0);
Packit Service c6b9b0
Packit Service c6b9b0
  /* Telepathy */
Packit Service c6b9b0
  tp_accounts = tp_account_manager_dup_valid_accounts (priv->account_manager);
Packit Service c6b9b0
  for (l = tp_accounts; l != NULL; l = l->next)
Packit Service c6b9b0
    tp_account_added (self, l->data);
Packit Service c6b9b0
  g_list_free_full (tp_accounts, g_object_unref);
Packit Service c6b9b0
Packit Service c6b9b0
  g_signal_connect_object (priv->account_manager, "account-validity-changed",
Packit Service c6b9b0
      G_CALLBACK (tp_account_validity_changed_cb), self, 0);
Packit Service c6b9b0
  g_signal_connect_object (priv->account_manager, "account-removed",
Packit Service c6b9b0
      G_CALLBACK (tp_account_removed_cb), self, 0);
Packit Service c6b9b0
Packit Service c6b9b0
  /* Now we check if any Telepathy account was deleted while goa-daemon
Packit Service c6b9b0
   * was not running. */
Packit Service c6b9b0
  g_hash_table_iter_init (&iter, priv->goa_accounts);
Packit Service c6b9b0
  while (g_hash_table_iter_next (&iter, &key, &value))
Packit Service c6b9b0
    {
Packit Service c6b9b0
      const gchar *id = key;
Packit Service c6b9b0
      GoaObject *goa_object = value;
Packit Service c6b9b0
Packit Service c6b9b0
      if (!g_hash_table_lookup (priv->tp_accounts, id))
Packit Service c6b9b0
        {
Packit Service c6b9b0
          g_warning ("The Telepathy account %s was removed while the daemon "
Packit Service c6b9b0
              "was not running, removing the corresponding GOA account", id);
Packit Service c6b9b0
          goa_account_call_remove (goa_object_peek_account (goa_object),
Packit Service c6b9b0
              NULL, /* cancellable */
Packit Service c6b9b0
              goa_account_removed_by_us_cb,
Packit Service c6b9b0
              NULL); /* user data */
Packit Service c6b9b0
        }
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  remove_tp_account_queue_check (self);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
account_manager_prepared_cb (GObject      *object,
Packit Service c6b9b0
                             GAsyncResult *res,
Packit Service c6b9b0
                             gpointer      user_data)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinker *self = user_data;
Packit Service c6b9b0
  GError *error = NULL;
Packit Service c6b9b0
Packit Service c6b9b0
  if (!tp_proxy_prepare_finish (object, res, &error))
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_critical ("Error preparing AM: %s", error->message);
Packit Service c6b9b0
      g_clear_error (&error);
Packit Service c6b9b0
      return;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  g_debug("Telepathy account manager prepared");
Packit Service c6b9b0
  start_if_ready (self);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
goa_client_new_cb (GObject      *object,
Packit Service c6b9b0
                   GAsyncResult *result,
Packit Service c6b9b0
                   gpointer      user_data)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinker *self = user_data;
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
  GError *error = NULL;
Packit Service c6b9b0
Packit Service c6b9b0
  priv->goa_client = goa_client_new_finish (result, &error);
Packit Service c6b9b0
  if (priv->goa_client == NULL)
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_critical ("Error connecting to GOA: %s", error->message);
Packit Service c6b9b0
      g_clear_error (&error);
Packit Service c6b9b0
      return;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  g_debug("GOA client ready");
Packit Service c6b9b0
  start_if_ready (self);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
goa_tp_account_linker_dispose (GObject *object)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinker *self = GOA_TP_ACCOUNT_LINKER (object);
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv = self->priv;
Packit Service c6b9b0
Packit Service c6b9b0
  if (priv->remove_tp_account_queue != NULL)
Packit Service c6b9b0
    {
Packit Service c6b9b0
      g_queue_free_full (priv->remove_tp_account_queue, g_object_unref);
Packit Service c6b9b0
      priv->remove_tp_account_queue = NULL;
Packit Service c6b9b0
    }
Packit Service c6b9b0
Packit Service c6b9b0
  g_clear_object (&priv->account_manager);
Packit Service c6b9b0
  g_clear_object (&priv->goa_client);
Packit Service c6b9b0
Packit Service c6b9b0
  g_clear_pointer (&priv->goa_accounts, g_hash_table_unref);
Packit Service c6b9b0
  g_clear_pointer (&priv->tp_accounts, g_hash_table_unref);
Packit Service c6b9b0
Packit Service c6b9b0
  G_OBJECT_CLASS (goa_tp_account_linker_parent_class)->dispose (object);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
goa_tp_account_linker_init (GoaTpAccountLinker *self)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv;
Packit Service c6b9b0
Packit Service c6b9b0
  g_debug ("Starting GOA <-> Telepathy account linker");
Packit Service c6b9b0
Packit Service c6b9b0
  self->priv = GOA_TP_ACCOUNT_LINKER_GET_PRIVATE (self);
Packit Service c6b9b0
  priv = self->priv;
Packit Service c6b9b0
Packit Service c6b9b0
  priv->goa_accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
Packit Service c6b9b0
      g_free, g_object_unref);
Packit Service c6b9b0
  priv->tp_accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
Packit Service c6b9b0
      g_free, g_object_unref);
Packit Service c6b9b0
Packit Service c6b9b0
  priv->remove_tp_account_queue = g_queue_new ();
Packit Service c6b9b0
Packit Service c6b9b0
  priv->account_manager = tp_account_manager_dup ();
Packit Service c6b9b0
  tp_proxy_prepare_async (priv->account_manager, NULL,
Packit Service c6b9b0
      account_manager_prepared_cb, self);
Packit Service c6b9b0
Packit Service c6b9b0
  goa_client_new (NULL, goa_client_new_cb, self);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
static void
Packit Service c6b9b0
goa_tp_account_linker_class_init (GoaTpAccountLinkerClass *klass)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit Service c6b9b0
Packit Service c6b9b0
  goa_utils_initialize_client_factory ();
Packit Service c6b9b0
Packit Service c6b9b0
  g_type_class_add_private (gobject_class,
Packit Service c6b9b0
      sizeof (GoaTpAccountLinkerPrivate));
Packit Service c6b9b0
Packit Service c6b9b0
  gobject_class->dispose = goa_tp_account_linker_dispose;
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
GoaTpAccountLinker *
Packit Service c6b9b0
goa_tp_account_linker_new (void)
Packit Service c6b9b0
{
Packit Service c6b9b0
  return g_object_new (GOA_TYPE_TP_ACCOUNT_LINKER, NULL);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
void
Packit Service c6b9b0
goa_tp_account_linker_remove_tp_account (GoaTpAccountLinker   *self,
Packit Service c6b9b0
                                         GoaObject            *object,
Packit Service c6b9b0
                                         GCancellable         *cancellable,
Packit Service c6b9b0
                                         GAsyncReadyCallback   callback,
Packit Service c6b9b0
                                         gpointer              user_data)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GoaTpAccountLinkerPrivate *priv;
Packit Service c6b9b0
  GTask *task;
Packit Service c6b9b0
Packit Service c6b9b0
  g_return_if_fail (GOA_IS_TP_ACCOUNT_LINKER (self));
Packit Service c6b9b0
  priv = self->priv;
Packit Service c6b9b0
Packit Service c6b9b0
  g_return_if_fail (GOA_IS_OBJECT (object));
Packit Service c6b9b0
  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
Packit Service c6b9b0
Packit Service c6b9b0
  task = g_task_new (self, cancellable, callback, user_data);
Packit Service c6b9b0
  g_task_set_source_tag (task, goa_tp_account_linker_remove_tp_account);
Packit Service c6b9b0
  g_task_set_task_data (task, g_object_ref (object), g_object_unref);
Packit Service c6b9b0
  g_queue_push_tail (priv->remove_tp_account_queue, g_object_ref (task));
Packit Service c6b9b0
Packit Service c6b9b0
  remove_tp_account_queue_check (self);
Packit Service c6b9b0
Packit Service c6b9b0
  g_object_unref (task);
Packit Service c6b9b0
}
Packit Service c6b9b0
Packit Service c6b9b0
gboolean
Packit Service c6b9b0
goa_tp_account_linker_remove_tp_account_finish (GoaTpAccountLinker  *self,
Packit Service c6b9b0
                                                GAsyncResult        *res,
Packit Service c6b9b0
                                                GError             **error)
Packit Service c6b9b0
{
Packit Service c6b9b0
  GTask *task;
Packit Service c6b9b0
Packit Service c6b9b0
  g_return_val_if_fail (GOA_IS_TP_ACCOUNT_LINKER (self), FALSE);
Packit Service c6b9b0
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit Service c6b9b0
Packit Service c6b9b0
  g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
Packit Service c6b9b0
  task = G_TASK (res);
Packit Service c6b9b0
Packit Service c6b9b0
  g_return_val_if_fail (g_task_get_source_tag (task) == goa_tp_account_linker_remove_tp_account, FALSE);
Packit Service c6b9b0
Packit Service c6b9b0
  return g_task_propagate_boolean (task, error);
Packit Service c6b9b0
}