static void
on_account_notify_changing_presence (GObject *object,
    GParamSpec *pspec,
    gpointer user_data)
{
  TpAccount *account = TP_ACCOUNT (object);
  ChangingPresenceData *data = user_data;
  TpConnection *connection;

  if (tp_account_get_changing_presence (account))
    {
      g_source_remove (data->source_id);
      g_signal_handler_disconnect (data->account, data->handler_id);

      connection = tp_account_get_connection (account);

      if (connection == NULL)
        {
          /* now just wait for the connection */
          tp_g_signal_connect_object (account, "notify::connection",
              G_CALLBACK (on_account_notify_connection), data->res, 0);
        }
      else
        {
          /* TODO: this should be given the cancellable when it's used. */
          _tp_yts_connection_future_ensure_sidecar_async (connection,
              TP_YTS_IFACE_STATUS, NULL,
              on_connection_future_ensure_sidecar_returned, data->res);
        }

      g_slice_free (ChangingPresenceData, data);
    }
}
/**
 * empathy_account_chooser_filter_supports_multichat:
 * @account: a #TpAccount
 * @callback: an #EmpathyAccountChooserFilterResultCallback accepting the result
 * @callback_data: data passed to the @callback
 * @user_data: user data or %NULL
 *
 * An #EmpathyAccountChooserFilterFunc that returns accounts that both
 * support multiuser text chat and are connected.
 *
 * Returns (via the callback) TRUE if @account both supports muc and is connected
 */
void
empathy_account_chooser_filter_supports_chatrooms (
	TpAccount                                 *account,
	EmpathyAccountChooserFilterResultCallback  callback,
	gpointer                                   callback_data,
	gpointer                                   user_data)
{
	TpConnection       *connection;
	FilterCallbackData *cb_data;
	GQuark              features[] = { TP_CONNECTION_FEATURE_CAPABILITIES, 0 };

	if (tp_account_get_connection_status (account, NULL) !=
		TP_CONNECTION_STATUS_CONNECTED) {
		callback (FALSE, callback_data);
		return;
	}

	/* check if CM supports multiuser text chat */
	connection = tp_account_get_connection (account);
	if (connection == NULL) {
		callback (FALSE, callback_data);
		return;
	}

	cb_data = g_slice_new0 (FilterCallbackData);
	cb_data->callback = callback;
	cb_data->user_data = callback_data;

	tp_proxy_prepare_async (connection, features, conn_prepared_cb,
		cb_data);
}
static void
contact_blocking_dialog_connection_status_changed (TpAccount *account,
    guint old_status,
    guint new_status,
    guint reason,
    const char *dbus_reason,
    GHashTable *details,
    EmpathyContactBlockingDialog *self)
{
  TpConnection *conn = tp_account_get_connection (account);

  switch (new_status)
    {
      case TP_CONNECTION_STATUS_DISCONNECTED:
        DEBUG ("Connection %s invalidated", get_pretty_conn_name (conn));

        contact_blocking_dialog_refilter_account_chooser (self);
        break;

      case TP_CONNECTION_STATUS_CONNECTING:
        break;

      case TP_CONNECTION_STATUS_CONNECTED:
        DEBUG ("Connection %s reconnected", get_pretty_conn_name (conn));

        contact_blocking_dialog_refilter_account_chooser (self);
    }
}
/**
 * empathy_account_chooser_filter_supports_multichat:
 * @account: a #TpAccount
 * @user_data: user data or %NULL
 *
 * An #EmpathyAccountChooserFilterFunc that returns accounts that both
 * support multiuser text chat and are connected.
 *
 * Return value: TRUE if @account both supports muc and is connected
 */
static gboolean
empathy_account_chooser_filter_supports_multichat (TpAccount *account,
						   gpointer   user_data)
{
	TpConnection *connection;
	EmpathyDispatcher *dispatcher;
	GList *classes;

	if (tp_account_get_connection_status (account, NULL) !=
	    TP_CONNECTION_STATUS_CONNECTED)
	return FALSE;

	/* check if CM supports multiuser text chat */
	connection = tp_account_get_connection (account);
	if (connection == NULL)
		return FALSE;

	dispatcher = empathy_dispatcher_dup_singleton ();

	classes = empathy_dispatcher_find_requestable_channel_classes
		(dispatcher, connection, TP_IFACE_CHANNEL_TYPE_TEXT,
		TP_HANDLE_TYPE_ROOM, NULL);

	g_object_unref (dispatcher);

	if (classes == NULL)
		return FALSE;

	g_list_free (classes);
	return TRUE;
}
static void
check_account (EmpathyConnectionAggregator *self,
    TpAccount *account)
{
  TpConnection *conn;

  conn = tp_account_get_connection (account);
  if (conn != NULL)
    check_connection (self, conn);
}
Example #6
0
static void
account_manager_chatroom_ready_cb (GObject *source_object,
    GAsyncResult *result,
    gpointer user_data)
{
  TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
  EmpathyChatroomManager *chatroom_manager = user_data;
  GList *accounts, *l;
  GError *error = NULL;

  if (!tp_account_manager_prepare_finish (account_manager, result, &error))
    {
      DEBUG ("Failed to prepare account manager: %s", error->message);
      g_error_free (error);
      return;
    }

  accounts = tp_account_manager_get_valid_accounts (account_manager);

  for (l = accounts; l != NULL; l = g_list_next (l))
    {
      TpAccount *account = TP_ACCOUNT (l->data);
      TpConnection *conn;
      GList *chatrooms, *p;

      conn = tp_account_get_connection (account);

      chatrooms = empathy_chatroom_manager_get_chatrooms (
          chatroom_manager, account);

      for (p = chatrooms; p != NULL; p = p->next)
        {
          EmpathyChatroom *room = EMPATHY_CHATROOM (p->data);

          if (!empathy_chatroom_get_auto_connect (room))
            continue;

          if (conn == NULL)
            {
              g_signal_connect (G_OBJECT (account), "status-changed",
                  G_CALLBACK (account_status_changed_cb), room);
            }
          else
            {
              empathy_dispatcher_join_muc (conn,
                  empathy_chatroom_get_room (room), NULL, NULL);
            }
        }

      g_list_free (chatrooms);
    }

  g_list_free (accounts);
}
static void
connection_notify_cb (TpawUserInfo *self)
{
  TpConnection *connection = tp_account_get_connection (self->priv->account);

  if (connection != NULL)
    {
      tp_g_signal_connect_object (connection, "notify::self-contact",
          G_CALLBACK (reload_contact_info), self, G_CONNECT_SWAPPED);
    }

  reload_contact_info (self);
}
static void
_status_changed (PresenceChooser *self,
                 guint            old_status,
                 guint            new_status,
                 guint            reason,
                 TpAccount       *account)
{
  TpConnection *conn = tp_account_get_connection (account);

  if (conn == NULL) return;
  else if (new_status == TP_CONNECTION_STATUS_CONNECTED ||
           new_status == TP_CONNECTION_STATUS_DISCONNECTED)
    {
      tp_connection_call_when_ready (conn, _connection_ready, self);
    }
}
Example #9
0
static void
main_window_favorite_chatroom_join (EmpathyChatroom *chatroom)
{
	TpAccount      *account;
	TpConnection   *connection;
	const gchar    *room;

	account = empathy_chatroom_get_account (chatroom);
	connection = tp_account_get_connection (account);
	room = empathy_chatroom_get_room (chatroom);

	if (connection != NULL) {
		DEBUG ("Requesting channel for '%s'", room);
		empathy_dispatcher_join_muc (connection, room, NULL, NULL);
	}
}
static void
contact_blocking_dialog_filter_account_chooser (TpAccount *account,
    EmpathyAccountChooserFilterResultCallback callback,
    gpointer callback_data,
    gpointer user_data)
{
  TpConnection *conn = tp_account_get_connection (account);
  gboolean enable;

  enable =
    conn != NULL &&
    tp_proxy_has_interface_by_id (conn,
      TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_BLOCKING);

  callback (enable, callback_data);
}
Example #11
0
static void
account_status_changed_cb (TpAccount *account,
    guint old_status,
    guint new_status,
    guint reason,
    gchar *dbus_error_name,
    GHashTable *details,
    EmpathyChatroom *room)
{
  TpConnection *conn;

  conn = tp_account_get_connection (account);

  empathy_dispatcher_join_muc (conn,
      empathy_chatroom_get_room (room), NULL, NULL);
}
/**
 * tp_yts_status_ensure_async:
 * @account: The Ytstenut enabled account
 * @cancellable: Not used
 * @callback: A callback which should be called when the result is ready
 * @user_data: Data to pass to the callback
 *
 * Create a #TpYtsStatus object for a Ytstenut enabled
 * connection. @account must be either connected, or in the process of
 * connecting. Calling tp_yts_account_manager_hold() before this
 * function (even if haven't re-entered the mainloop yet) is
 * acceptable for connecting the account.
 */
void
tp_yts_status_ensure_async (TpAccount *account,
    GCancellable *cancellable,
    GAsyncReadyCallback callback,
    gpointer user_data)
{
  GSimpleAsyncResult *res;
  TpConnection *connection;

  g_return_if_fail (TP_IS_ACCOUNT (account));

  connection = tp_account_get_connection (account);

  res = g_simple_async_result_new (G_OBJECT (account), callback, user_data,
      tp_yts_status_ensure_async);

  if (connection == NULL)
    {
      if (tp_account_get_changing_presence (account))
        {
          /* just wait for the connection */
          tp_g_signal_connect_object (account, "notify::connection",
              G_CALLBACK (on_account_notify_connection), res, 0);
        }
      else
        {
          /* wait to see if the connection appears */
          ChangingPresenceData *data = g_slice_new0 (ChangingPresenceData);
          data->res = res;
          data->account = account;

          data->source_id = g_timeout_add_seconds (1, on_account_timeout, data);

          data->handler_id = g_signal_connect (account,
              "notify::changing-presence",
              G_CALLBACK (on_account_notify_changing_presence),
              data);
        }
    }
  else
    {
      _tp_yts_connection_future_ensure_sidecar_async (connection,
          TP_YTS_IFACE_STATUS, cancellable,
          on_connection_future_ensure_sidecar_returned, res);
    }
}
static gboolean
can_add_contact_to_account (TpAccount *account,
    gpointer user_data)
{
  EmpathyIndividualManager *individual_manager;
  TpConnection *connection;
  gboolean result;

  connection = tp_account_get_connection (account);
  if (connection == NULL)
    return FALSE;

  individual_manager = empathy_individual_manager_dup_singleton ();
  result = empathy_connection_can_add_personas (connection);
  g_object_unref (individual_manager);

  return result;
}
static void
on_account_notify_connection (GObject *object,
    GParamSpec *pspec,
    gpointer user_data)
{
  TpAccount *account = TP_ACCOUNT (object);
  GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
  TpConnection *connection;

  connection = tp_account_get_connection (account);

  if (connection != NULL)
    {
      /* TODO: this should be given the cancellable when it's used. */
      _tp_yts_connection_future_ensure_sidecar_async (connection,
          TP_YTS_IFACE_STATUS, NULL,
          on_connection_future_ensure_sidecar_returned, res);
    }
}
Example #15
0
static void
check_hashing (CallbacksData *data)
{
  EmpathyFTHandler *handler = data->handler;
  EmpathyFTHandlerPriv *priv = handler->priv;
  GError *myerr = NULL;
  TpCapabilities *caps;
  GPtrArray *classes;
  TpConnection *conn;

  conn = tp_account_get_connection (priv->account);

  caps = tp_connection_get_capabilities (conn);
  if (caps == NULL)
    {
      data->callback (handler, NULL, data->user_data);
      goto out;
    }

  classes = tp_capabilities_get_channel_classes (caps);

  /* set whether we support hash and the type of it */
  if (!set_content_hash_type_from_classes (handler, classes))
    {
      g_set_error_literal (&myerr, EMPATHY_FT_ERROR_QUARK,
          EMPATHY_FT_ERROR_NOT_SUPPORTED,
          _("File transfer not supported by remote contact"));

      if (!g_cancellable_is_cancelled (priv->cancellable))
        g_cancellable_cancel (priv->cancellable);

      data->callback (handler, myerr, data->user_data);
      g_clear_error (&myerr);
    }
  else
    {
      /* get back to the caller now */
      data->callback (handler, NULL, data->user_data);
    }

out:
  callbacks_data_free (data);
}
static void
contact_manager_status_changed_cb (TpAccount *account,
				   guint old_status,
				   guint new_status,
				   guint reason,
				   gchar *dbus_error_name,
				   GHashTable *details,
				   EmpathyContactManager *self)
{
	EmpathyContactManagerPriv *priv = GET_PRIV (self);
	EmpathyTpContactList      *list;
	TpConnection              *connection;

	if (new_status == TP_CONNECTION_STATUS_DISCONNECTED)
		/* No point to start tracking a connection which is about to die */
		return;

	connection = tp_account_get_connection (account);

	if (connection == NULL || g_hash_table_lookup (priv->lists, connection)) {
		return;
	}

	DEBUG ("Adding new connection: %s",
		tp_proxy_get_object_path (TP_PROXY (connection)));

	list = empathy_tp_contact_list_new (connection);
	g_hash_table_insert (priv->lists, g_object_ref (connection), list);
	g_signal_connect (connection, "invalidated",
			  G_CALLBACK (contact_manager_invalidated_cb),
			  self);

	/* Connect signals */
	g_signal_connect (list, "members-changed",
			  G_CALLBACK (contact_manager_members_changed_cb),
			  self);
	g_signal_connect (list, "pendings-changed",
			  G_CALLBACK (contact_manager_pendings_changed_cb),
			  self);
	g_signal_connect (list, "groups-changed",
			  G_CALLBACK (contact_manager_groups_changed_cb),
			  self);
}
/**
 * empathy_account_chooser_get_connection:
 * @self: an #EmpathyAccountChooser
 *
 * Returns a borrowed reference to the #TpConnection associated with the
 * account currently selected. The caller must reference the returned object
 * with g_object_ref() if it will be kept
 *
 * Return value: a borrowed reference to the #TpConnection associated with the
 * account curently selected.
 */
TpConnection *
empathy_account_chooser_get_connection (EmpathyAccountChooser *self)
{
  TpAccount *account;
  TpConnection *connection;

  g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (self), NULL);

  account = empathy_account_chooser_dup_account (self);

  /* if the returned account is NULL, then the account manager probably
   * hasn't been prepared yet. It should be safe to return NULL here
   * though. */
  if (account == NULL)
    return NULL;

  connection = tp_account_get_connection (account);
  g_object_unref (account);

  return connection;
}
static void
chat_conversations_list_account_manager_prepare (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
  ChatConversationsList *self = CHAT_CONVERSATIONS_LIST (user_data);
  ChatConversationsListPrivate *priv = self->priv;
  GError *error;
  GList *accounts = NULL;
  GList *l;

  error = NULL;
  if (!tp_proxy_prepare_finish (source_object, res, &error))
    {
      g_warning ("Unable to prepare the account manager: %s", error->message);
      g_error_free (error);
      goto out;
    }

  accounts = tp_account_manager_dup_valid_accounts (priv->am);
  for (l = accounts; l != NULL; l = l->next)
    {
      TpAccount *account = TP_ACCOUNT (l->data);
      TpConnection *conn;
      const gchar *cm_name;

      cm_name = tp_account_get_cm_name (account);
      if (g_strcmp0 (cm_name, "idle") == 0)
        continue;

      conn = tp_account_get_connection (account);
      if (conn == NULL)
        continue;

      tp_proxy_prepare_async (conn, NULL, chat_conversations_list_connection_prepare, g_object_ref (self));
      g_hash_table_insert (priv->accounts, g_object_ref (account), NULL);
    }

 out:
  g_list_free_full (accounts, g_object_unref);
  g_object_unref (self);
}
Example #19
0
TpfPersonaStore *
empathy_dup_persona_store_for_connection (TpConnection *connection)
{
  FolksBackendStore *backend_store;
  FolksBackend *backend;
  TpfPersonaStore *result = NULL;

  backend_store = folks_backend_store_dup ();
  backend = folks_backend_store_dup_backend_by_name (backend_store,
      "telepathy");
  if (backend != NULL)
    {
      GeeMap *stores_map;
      GeeMapIterator *iter;

      stores_map = folks_backend_get_persona_stores (backend);
      iter = gee_map_map_iterator (stores_map);
      while (gee_map_iterator_next (iter))
        {
          TpfPersonaStore *persona_store = gee_map_iterator_get_value (iter);
          TpAccount *account;
          TpConnection *conn_cur;

          account = tpf_persona_store_get_account (persona_store);
          conn_cur = tp_account_get_connection (account);
          if (conn_cur == connection)
            result = g_object_ref (persona_store);

          g_clear_object (&persona_store);
        }
      g_clear_object (&iter);
    }

  g_object_unref (backend);
  g_object_unref (backend_store);

  return result;
}
static void
can_add_contact_to_account (TpAccount *account,
    EmpathyAccountChooserFilterResultCallback callback,
    gpointer callback_data,
    gpointer user_data)
{
  EmpathyIndividualManager *individual_manager;
  TpConnection *connection;
  gboolean result;

  connection = tp_account_get_connection (account);
  if (connection == NULL)
    {
      callback (FALSE, callback_data);
      return;
    }

  individual_manager = empathy_individual_manager_dup_singleton ();
  result = empathy_connection_can_add_personas (connection);
  g_object_unref (individual_manager);

  callback (result, callback_data);
}
static void
account_manager_prepared_cb (GObject *source_object,
			     GAsyncResult *result,
			     gpointer user_data)
{
	GList *accounts, *l;
	EmpathyContactManager *manager = user_data;
	TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
	GError *error = NULL;

	if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
		DEBUG ("Failed to prepare account manager: %s", error->message);
		g_error_free (error);
		return;
	}

	accounts = tp_account_manager_get_valid_accounts (account_manager);

	for (l = accounts; l != NULL; l = l->next) {
		TpAccount *account = l->data;
		TpConnection *conn = tp_account_get_connection (account);

		if (conn != NULL) {
			contact_manager_status_changed_cb (account, 0, 0, 0,
							   NULL, NULL, manager);
		}

		tp_g_signal_connect_object (account, "status-changed",
		    G_CALLBACK (contact_manager_status_changed_cb),
		    manager, 0);
	}
	g_list_free (accounts);

	tp_g_signal_connect_object (account_manager, "account-validity-changed",
			     G_CALLBACK (contact_manager_validity_changed_cb),
			     manager, 0);
}
/**
 * empathy_account_chooser_filter_supports_multichat:
 * @account: a #TpAccount
 * @callback: an #EmpathyAccountChooserFilterResultCallback accepting the result
 * @callback_data: data passed to the @callback
 * @user_data: user data or %NULL
 *
 * An #EmpathyAccountChooserFilterFunc that returns accounts that both
 * support multiuser text chat and are connected.
 *
 * Returns (via the callback) TRUE if @account both supports muc and
 * is connected
 */
void
empathy_account_chooser_filter_supports_chatrooms (TpAccount *account,
  EmpathyAccountChooserFilterResultCallback callback,
  gpointer callback_data,
  gpointer user_data)
{
  TpConnection *connection;
  gboolean supported = FALSE;
  TpCapabilities *caps;

  /* check if CM supports multiuser text chat */
  connection = tp_account_get_connection (account);
  if (connection == NULL)
    goto out;

  caps = tp_connection_get_capabilities (connection);
  if (caps == NULL)
    goto out;

  supported = tp_capabilities_supports_text_chatrooms (caps);

out:
  callback (supported, callback_data);
}
static guint
fill_contact_info_grid (TpawUserInfo *self)
{
  TpConnection *connection;
  TpContact *contact;
  GList *specs, *l;
  guint n_rows = 0;
  GList *info;
  const char **field_names = tpaw_contact_info_get_field_names (NULL);
  guint i;

  g_assert (self->priv->details_to_set == NULL);

  connection = tp_account_get_connection (self->priv->account);
  contact = tp_connection_get_self_contact (connection);
  specs = tp_connection_dup_contact_info_supported_fields (connection);
  info = tp_contact_dup_contact_info (contact);

  /* Look at the fields set in our vCard */
  for (l = info; l != NULL; l = l->next)
    {
      TpContactInfoField *field = l->data;

      /* For some reason it can happen that the vCard contains fields the CM
       * claims to be not supported. This is a workaround for gabble bug
       * https://bugs.freedesktop.org/show_bug.cgi?id=64319. But we shouldn't
       * crash on buggy CM anyway. */
      if (get_spec_from_list (specs, field->field_name) == NULL)
        {
          DEBUG ("Buggy CM: self's vCard contains %s field but it is not in "
              "Connection' supported fields", field->field_name);
          continue;
        }

      /* make a copy for the details_to_set list */
      field = tp_contact_info_field_copy (field);
      DEBUG ("Field %s is in our vCard", field->field_name);

      self->priv->details_to_set = g_list_prepend (self->priv->details_to_set,
          field);
    }

  /* Add fields which are supported but not in the vCard */
  for (i = 0; field_names[i] != NULL; i++)
    {
      TpContactInfoFieldSpec *spec;
      TpContactInfoField *field;

      /* Check if the field was in the vCard */
      if (field_name_in_field_list (self->priv->details_to_set,
            field_names[i]))
        continue;

      /* Check if the CM supports the field */
      spec = get_spec_from_list (specs, field_names[i]);
      if (spec == NULL)
        continue;

      /* add an empty field so user can set a value */
      field = tp_contact_info_field_new (spec->name, spec->parameters, NULL);

      self->priv->details_to_set = g_list_prepend (self->priv->details_to_set,
          field);
    }

  /* Add widgets for supported fields */
  self->priv->details_to_set = g_list_sort (self->priv->details_to_set,
      (GCompareFunc) tpaw_contact_info_field_spec_cmp);

  for (l = self->priv->details_to_set; l != NULL; l= g_list_next (l))
    {
      TpContactInfoField *field = l->data;
      GtkWidget *label, *w;
      TpContactInfoFieldSpec *spec;
      gboolean has_field;
      char *title;

      has_field = tpaw_contact_info_lookup_field (field->field_name,
          NULL, NULL);
      if (!has_field)
        {
          /* We don't display this field so we can't change it.
           * But we put it in the details_to_set list so it won't be erased
           * when calling SetContactInfo (bgo #630427) */
          DEBUG ("Unhandled ContactInfo field spec: %s", field->field_name);
          continue;
        }

      spec = get_spec_from_list (specs, field->field_name);
      /* We shouldn't have added the field to details_to_set if it's not
       * supported by the CM */
      g_assert (spec != NULL);

      if (spec->flags & TP_CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME)
        {
          DEBUG ("Ignoring field '%s' due it to having the "
              "Overwritten_By_Nickname flag", field->field_name);
          continue;
        }

      /* Add Title */
      title = tpaw_contact_info_field_label (field->field_name,
          field->parameters,
          (spec->flags & TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT));
      label = gtk_label_new (title);
      g_free (title);

      /* TODO: if TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT is not set we
       * should allow user to tag the vCard fields (bgo#672034) */

      /* Add Value */
      if (!tp_strdiff (field->field_name, "bday"))
        {
          w = tpaw_calendar_button_new ();

          if (field->field_value[0])
            {
              GDate date;

              g_date_set_parse (&date, field->field_value[0]);
              if (g_date_valid (&date))
                {
                  tpaw_calendar_button_set_date (TPAW_CALENDAR_BUTTON (w),
                      &date);
                }
            }

          g_signal_connect (w, "date-changed",
            G_CALLBACK (bday_changed_cb), self);
        }
      else
        {
          w = gtk_entry_new ();
          gtk_entry_set_text (GTK_ENTRY (w),
              field->field_value[0] ? field->field_value[0] : "");
          g_signal_connect (w, "changed",
            G_CALLBACK (contact_info_changed_cb), self);
        }

      add_row (GTK_GRID (self), label, w, TRUE);

      g_object_set_data ((GObject *) w, DATA_FIELD, field);

      n_rows++;
    }

  tp_contact_info_spec_list_free (specs);
  tp_contact_info_list_free (info);

  return n_rows;
}
static void
reload_contact_info (TpawUserInfo *self)
{
  TpConnection *connection;
  TpContact *contact = NULL;
  TpContactInfoFlags flags;

  /* Cancel previous RequestContactInfo, if any */
  if (self->priv->details_cancellable != NULL)
    g_cancellable_cancel (self->priv->details_cancellable);
  g_clear_object (&self->priv->details_cancellable);

  /* Remove current contact info widgets, if any */
  gtk_container_foreach (GTK_CONTAINER (self), grid_foreach_cb, NULL);
  gtk_widget_hide (self->priv->details_label);
  gtk_widget_hide (self->priv->details_spinner);

  tp_clear_pointer (&self->priv->details_to_set, tp_contact_info_list_free);
  self->priv->details_changed = FALSE;

  connection = tp_account_get_connection (self->priv->account);
  if (connection != NULL)
    {
      contact = tp_connection_get_self_contact (connection);

      /* FIXME: we should rely on the factory to do this, see bgo#706892 */
      if (!tp_proxy_is_prepared (connection,
            TP_CONNECTION_FEATURE_CONTACT_INFO) &&
          !self->priv->tried_preparing_contact_info)
        {
          GQuark features[] = { TP_CONNECTION_FEATURE_CONTACT_INFO, 0 };

          /* Prevent an infinite loop if the connection doesn't implement
           * ContactInfo, see bgo#709677 */
          self->priv->tried_preparing_contact_info = TRUE;

          tp_proxy_prepare_async (connection, features,
              connection_contact_info_prepared_cb, g_object_ref (self));
        }
    }

  /* Display infobar if we don't have a self contact (probably offline) */
  if (contact == NULL)
    {
      GtkWidget *infobar;
      GtkWidget *content;
      GtkWidget *label;

      infobar = gtk_info_bar_new ();
      gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_INFO);
      content = gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar));
      label = gtk_label_new (_("Go online to edit your personal information."));
      gtk_container_add (GTK_CONTAINER (content), label);
      gtk_widget_show (label);

      gtk_grid_attach_next_to ((GtkGrid *) self, infobar,
          NULL, GTK_POS_BOTTOM, 3, 1);
      gtk_widget_show (infobar);

      g_object_set_data (G_OBJECT (infobar),
          DATA_IS_CONTACT_INFO, (gpointer) TRUE);
      return;
    }

  if (!tp_proxy_has_interface_by_id (connection,
          TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO))
    return;

  flags = tp_connection_get_contact_info_flags (connection);
  if ((flags & TP_CONTACT_INFO_FLAG_CAN_SET) == 0)
    return;

  /* Request the contact's info */
  gtk_widget_show (self->priv->details_spinner);
  gtk_spinner_start (GTK_SPINNER (self->priv->details_spinner));

  g_assert (self->priv->details_cancellable == NULL);
  self->priv->details_cancellable = g_cancellable_new ();
  tp_contact_request_contact_info_async (contact,
      self->priv->details_cancellable, request_contact_info_cb,
      self);
}
void
tpaw_user_info_apply_async (TpawUserInfo *self,
    GAsyncReadyCallback callback,
    gpointer user_data)
{
  GSimpleAsyncResult *result;
  const gchar *new_nickname;
  guint count = 0;
  GList *l, *next;

  g_return_if_fail (TPAW_IS_USER_INFO (self));

  result = g_simple_async_result_new ((GObject *) self, callback, user_data,
      tpaw_user_info_apply_async);

  /* Apply avatar */
  tpaw_avatar_chooser_apply_async (
      (TpawAvatarChooser *) self->priv->avatar_chooser,
      avatar_chooser_apply_cb, g_object_ref (result));
  count++;

  /* Apply nickname */
  new_nickname = gtk_entry_get_text (GTK_ENTRY (self->priv->nickname_entry));
  if (tp_strdiff (new_nickname, tp_account_get_nickname (self->priv->account)))
    {
      tp_account_set_nickname_async (self->priv->account, new_nickname,
          set_nickname_cb, g_object_ref (result));
      count++;
    }

  /* Remove empty fields */
  for (l = self->priv->details_to_set; l != NULL; l = next)
    {
      TpContactInfoField *field = l->data;

      next = l->next;
      if (field_value_is_empty (field))
        {
          DEBUG ("Drop empty field: %s", field->field_name);
          tp_contact_info_field_free (field);
          self->priv->details_to_set =
              g_list_delete_link (self->priv->details_to_set, l);
        }
    }

  if (self->priv->details_to_set != NULL)
    {
      if (self->priv->details_changed)
        {
          tp_connection_set_contact_info_async (
              tp_account_get_connection (self->priv->account),
              self->priv->details_to_set, set_contact_info_cb,
              g_object_ref (result));
          count++;
        }

      tp_contact_info_list_free (self->priv->details_to_set);
      self->priv->details_to_set = NULL;
    }

  self->priv->details_changed = FALSE;

  g_simple_async_result_set_op_res_gssize (result, count);

  g_object_unref (result);
}