static gboolean
on_account_handle_ensure_credentials (GoaAccount            *account,
                                      GDBusMethodInvocation *invocation,
                                      gpointer               user_data)
{
  GoaDaemon *daemon = GOA_DAEMON (user_data);
  GoaProvider *provider;
  GoaObject *object;

  object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (account)));
  provider = goa_provider_get_for_provider_type (goa_account_get_provider_type (account));
  if (provider == NULL)
    {
      g_dbus_method_invocation_return_error (invocation,
                                             GOA_ERROR,
                                             GOA_ERROR_FAILED,
                                             "Unsupported account type %s for id %s (no provider)",
                                             goa_account_get_provider_type (account),
                                             goa_account_get_id (account));
      goto out;
    }

  goa_provider_ensure_credentials (provider,
                                   object,
                                   NULL, /* GCancellable */
                                   (GAsyncReadyCallback) ensure_credentials_cb,
                                   ensure_data_new (daemon, object, invocation));

 out:
  return TRUE; /* invocation was handled */
}
static void
add_provider_to_list (GisGoaPage *page, const char *provider_type)
{
  GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page);
  GtkWidget *row;
  GtkWidget *box;
  GtkWidget *image;
  GtkWidget *label;
  GtkWidget *checkmark;
  GtkWidget *account_label;
  GIcon *icon;
  gchar *markup;
  GoaProvider *provider;
  ProviderWidget *provider_widget;

  provider = goa_provider_get_for_provider_type (provider_type);
  if (provider == NULL)
    return;

  row = gtk_list_box_row_new ();
  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  gtk_widget_set_hexpand (box, TRUE);

  icon = goa_provider_get_provider_icon (provider, NULL);
  image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);

  markup = g_strdup_printf ("<b>%s</b>", goa_provider_get_provider_name (provider, NULL));
  label = gtk_label_new (NULL);
  gtk_label_set_markup (GTK_LABEL (label), markup);
  g_free (markup);

  checkmark = gtk_image_new_from_icon_name ("object-select-symbolic", GTK_ICON_SIZE_MENU);

  account_label = gtk_label_new (NULL);

  gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
  gtk_box_pack_end (GTK_BOX (box), checkmark, FALSE, FALSE, 8);
  gtk_box_pack_end (GTK_BOX (box), account_label, FALSE, FALSE, 0);

  gtk_container_add (GTK_CONTAINER (row), box);

  gtk_widget_show (label);
  gtk_widget_show (image);
  gtk_widget_show (box);
  gtk_widget_show (row);

  provider_widget = g_new0 (ProviderWidget, 1);
  provider_widget->page = page;
  provider_widget->provider = provider;
  provider_widget->row = row;
  provider_widget->checkmark = checkmark;
  provider_widget->account_label = account_label;

  g_object_set_data_full (G_OBJECT (row), "widget", provider_widget, g_free);

  g_hash_table_insert (priv->providers, (char *) provider_type, provider_widget);

  gtk_container_add (GTK_CONTAINER (priv->accounts_list), row);
}
static void
on_info_bar_response (GtkInfoBar *info_bar,
                      gint        response_id,
                      gpointer    user_data)
{
  GoaPanel *panel = GOA_PANEL (user_data);
  GtkTreeIter iter;

  if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->accounts_treeview)),
                                       NULL,
                                       &iter))
    {
      GoaProvider *provider;
      const gchar *provider_type;
      GoaAccount *account;
      GoaObject *object;
      GtkWindow *parent;
      GError *error;

      gtk_tree_model_get (GTK_TREE_MODEL (panel->accounts_model),
                          &iter,
                          GOA_PANEL_ACCOUNTS_MODEL_COLUMN_OBJECT, &object,
                          -1);

      account = goa_object_peek_account (object);
      provider_type = goa_account_get_provider_type (account);
      provider = goa_provider_get_for_provider_type (provider_type);

      parent = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (panel))));

      error = NULL;
      if (!goa_provider_refresh_account (provider,
                                         panel->client,
                                         object,
                                         parent,
                                         &error))
        {
          if (!(error->domain == GOA_ERROR && error->code == GOA_ERROR_DIALOG_DISMISSED))
            {
              GtkWidget *dialog;
              dialog = gtk_message_dialog_new (parent,
                                               GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                               GTK_MESSAGE_ERROR,
                                               GTK_BUTTONS_CLOSE,
                                               _("Error logging into the account"));
              gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                                        "%s",
                                                        error->message);
              gtk_widget_show_all (dialog);
              gtk_dialog_run (GTK_DIALOG (dialog));
              gtk_widget_destroy (dialog);
            }
          g_error_free (error);
        }
      g_object_unref (provider);
      g_object_unref (object);
    }
}
static gboolean
on_handle_get_password (GoaPasswordBased      *interface,
                        GDBusMethodInvocation *invocation,
                        const gchar           *id,
                        gpointer               user_data)
{
  GoaObject *object;
  GoaAccount *account;
  GoaProvider *provider;
  GError *error;
  GVariant *credentials;
  gchar *password;

  /* TODO: maybe log what app is requesting access */

  password = NULL;
  credentials = NULL;

  object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (interface)));
  account = goa_object_peek_account (object);
  provider = goa_provider_get_for_provider_type (goa_account_get_provider_type (account));

  error = NULL;
  credentials = goa_utils_lookup_credentials_sync (provider,
                                                   object,
                                                   NULL, /* GCancellable* */
                                                   &error);
  if (credentials == NULL)
    {
      g_dbus_method_invocation_take_error (invocation, error);
      goto out;
    }

  if (!g_variant_lookup (credentials, "password", "s", &password))
    {
      g_dbus_method_invocation_return_error (invocation,
                                             GOA_ERROR,
                                             GOA_ERROR_FAILED, /* TODO: more specific */
                                             _("Did not find password with username `%s' in credentials"),
                                             id);
      goto out;
    }

  goa_password_based_complete_get_password (interface, invocation, password);

 out:
  g_free (password);
  if (credentials != NULL)
    g_variant_unref (credentials);
  g_object_unref (provider);
  return TRUE; /* invocation was handled */
}
static gboolean
on_account_handle_remove (GoaAccount            *account,
                          GDBusMethodInvocation *invocation,
                          gpointer               user_data)
{
  GoaDaemon *daemon = GOA_DAEMON (user_data);
  GoaProvider *provider;
  GKeyFile *key_file;
  const gchar *provider_type;
  gchar *path;
  gchar *group;
  gchar *data;
  gsize length;
  GError *error;

  provider = NULL;
  provider_type = NULL;
  path = NULL;
  group = NULL;
  key_file = NULL;
  data = NULL;

  /* update key-file - right now we only support removing the account
   * if the entry is in ~/.config/goa-1.0/accounts.conf
   */

  key_file = g_key_file_new ();
  path = g_strdup_printf ("%s/goa-1.0/accounts.conf", g_get_user_config_dir ());

  error = NULL;
  if (!g_key_file_load_from_file (key_file,
                                  path,
                                  G_KEY_FILE_KEEP_COMMENTS,
                                  &error))
    {
      g_dbus_method_invocation_return_gerror (invocation, error);
      g_error_free (error);
      goto out;
    }

  group = g_strdup_printf ("Account %s", goa_account_get_id (account));

  error = NULL;
  if (!g_key_file_remove_group (key_file, group, &error))
    {
      g_dbus_method_invocation_return_gerror (invocation, error);
      g_error_free (error);
      goto out;
    }

  error = NULL;
  data = g_key_file_to_data (key_file,
                             &length,
                             &error);
  if (data == NULL)
    {
      g_prefix_error (&error, "Error generating key-value-file: ");
      g_dbus_method_invocation_return_gerror (invocation, error);
      goto out;
    }

  error = NULL;
  if (!g_file_set_contents (path,
                            data,
                            length,
                            &error))
    {
      g_prefix_error (&error, "Error writing key-value-file %s: ", path);
      g_dbus_method_invocation_return_gerror (invocation, error);
      goto out;
    }

  provider_type = goa_account_get_provider_type (account);
  if (provider_type == NULL)
    {
      error = NULL;
      g_set_error_literal (&error,
                           GOA_ERROR,
                           GOA_ERROR_FAILED, /* TODO: more specific */
                           _("ProviderType property is not set for account"));
      g_dbus_method_invocation_return_gerror (invocation, error);
      goto out;
    }

  provider = goa_provider_get_for_provider_type (provider_type);
  if (provider == NULL)
    {
      error = NULL;
      g_set_error (&error,
                   GOA_ERROR,
                   GOA_ERROR_FAILED, /* TODO: more specific */
                   _("Failed to find a provider for: %s"),
                   provider_type);
      g_dbus_method_invocation_return_gerror (invocation, error);
      goto out;
    }

  error = NULL;
  if (!goa_utils_delete_credentials_sync (provider, account, NULL, &error))
    {
      g_dbus_method_invocation_return_gerror (invocation, error);
      goto out;
    }

  goa_daemon_reload_configuration (daemon);

  goa_account_complete_remove (account, invocation);

 out:
  if (provider != NULL)
    g_object_unref (provider);
  g_free (data);
  if (key_file != NULL)
    g_key_file_free (key_file);
  g_free (group);
  g_free (path);
  return TRUE; /* invocation was handled */
}
/* returns FALSE if object is not (or no longer) valid */
static gboolean
update_account_object (GoaDaemon           *daemon,
                       GoaObjectSkeleton   *object,
                       const gchar         *path,
                       const gchar         *group,
                       GKeyFile            *key_file,
                       gboolean             just_added)
{
  GoaAccount *account;
  GoaProvider *provider;
  gboolean ret;
  gchar *identity;
  gchar *presentation_identity;
  gchar *type;
  gchar *name;
  GIcon *icon;
  gchar *serialized_icon;
  GError *error;

  g_return_val_if_fail (GOA_IS_DAEMON (daemon), FALSE);
  g_return_val_if_fail (G_IS_DBUS_OBJECT_SKELETON (object), FALSE);
  g_return_val_if_fail (group != NULL, FALSE);
  g_return_val_if_fail (key_file != NULL, FALSE);

  ret = FALSE;
  identity = NULL;
  type = NULL;
  account = NULL;
  name = NULL;
  icon = NULL;
  serialized_icon = NULL;

  g_debug ("updating %s %d", g_dbus_object_get_object_path (G_DBUS_OBJECT (object)), just_added);

  type = g_key_file_get_string (key_file, group, "Provider", NULL);
  identity = g_key_file_get_string (key_file, group, "Identity", NULL);
  presentation_identity = g_key_file_get_string (key_file, group, "PresentationIdentity", NULL);
  if (just_added)
    {
      account = goa_account_skeleton_new ();
      goa_object_skeleton_set_account (object, account);
    }
  else
    {
      account = goa_object_get_account (GOA_OBJECT (object));
    }

  provider = goa_provider_get_for_provider_type (type);
  if (provider == NULL)
    {
      g_warning ("Unsupported account type %s for identity %s (no provider)", type, identity);
      goto out;
    }

  goa_account_set_id (account, g_strrstr (g_dbus_object_get_object_path (G_DBUS_OBJECT (object)), "/") + 1);
  goa_account_set_provider_type (account, type);
  goa_account_set_identity (account, identity);
  goa_account_set_presentation_identity (account, presentation_identity);

  error = NULL;
  if (!goa_provider_build_object (provider, object, key_file, group, daemon->connection, just_added, &error))
    {
      g_warning ("Error parsing account: %s (%s, %d)",
                 error->message, g_quark_to_string (error->domain), error->code);
      g_error_free (error);
      goto out;
    }

  name = goa_provider_get_provider_name (provider, GOA_OBJECT (object));
  goa_account_set_provider_name (account, name);

  icon = goa_provider_get_provider_icon (provider, GOA_OBJECT (object));
  serialized_icon = g_icon_to_string (icon);
  goa_account_set_provider_icon (account, serialized_icon);

  ret = TRUE;

 out:
  g_free (serialized_icon);
  if (icon != NULL)
    g_object_unref (icon);
  g_free (name);
  if (provider != NULL)
    g_object_unref (provider);
  g_object_unref (account);
  g_free (type);
  g_free (identity);
  return ret;
}
static void
show_page_account (GoaPanel  *panel,
                   GoaObject *object)
{
  GList *children;
  GList *l;
  GtkWidget *box;
  GtkWidget *grid;
  GtkWidget *left_grid;
  GtkWidget *right_grid;
  GtkWidget *bar;
  GtkWidget *label;
  GoaProvider *provider;
  GoaAccount *account;
  const gchar *provider_type;

  provider = NULL;

  show_page (panel, 1);
  box = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-box"));
  gtk_widget_set_sensitive (box, TRUE);

  label = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-label"));
  gtk_widget_hide (label);

  /* Out with the old */
  children = gtk_container_get_children (GTK_CONTAINER (panel->accounts_vbox));
  for (l = children; l != NULL; l = l->next)
    gtk_container_remove (GTK_CONTAINER (panel->accounts_vbox), GTK_WIDGET (l->data));
  g_list_free (children);

  account = goa_object_peek_account (object);
  provider_type = goa_account_get_provider_type (account);
  provider = goa_provider_get_for_provider_type (provider_type);

  /* And in with the new */
  if (goa_account_get_attention_needed (account))
    {
      bar = gtk_info_bar_new ();
      label = gtk_label_new (_("Expired credentials. Please log in again."));
      gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label);
      if (provider != NULL)
        gtk_info_bar_add_button (GTK_INFO_BAR (bar), _("_Log In"), GTK_RESPONSE_OK);
      gtk_box_pack_start (GTK_BOX (panel->accounts_vbox), bar, FALSE, TRUE, 0);
      g_signal_connect (bar, "response", G_CALLBACK (on_info_bar_response), panel);
    }

  left_grid = gtk_grid_new ();
  gtk_widget_set_halign (left_grid, GTK_ALIGN_END);
  gtk_widget_set_hexpand (left_grid, TRUE);
  gtk_orientable_set_orientation (GTK_ORIENTABLE (left_grid), GTK_ORIENTATION_VERTICAL);
  gtk_grid_set_row_spacing (GTK_GRID (left_grid), 0);

  right_grid = gtk_grid_new ();
  gtk_widget_set_hexpand (right_grid, TRUE);
  gtk_orientable_set_orientation (GTK_ORIENTABLE (right_grid), GTK_ORIENTATION_VERTICAL);
  gtk_grid_set_row_spacing (GTK_GRID (right_grid), 0);

  if (provider != NULL)
    {
      goa_provider_show_account (provider,
                                 panel->client,
                                 object,
                                 GTK_BOX (panel->accounts_vbox),
                                 GTK_GRID (left_grid),
                                 GTK_GRID (right_grid));
    }

  grid = gtk_grid_new ();
  gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_HORIZONTAL);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
  gtk_container_add (GTK_CONTAINER (grid), left_grid);
  gtk_container_add (GTK_CONTAINER (grid), right_grid);
  gtk_box_pack_start (GTK_BOX (panel->accounts_vbox), grid, FALSE, TRUE, 0);

  gtk_widget_show_all (panel->accounts_vbox);

  if (provider != NULL)
    g_object_unref (provider);
}