static void add_config_file (const gchar *path, GHashTable *group_name_to_key_file_data, GList **key_files_to_free) { GKeyFile *key_file; GError *error; key_file = g_key_file_new (); error = NULL; if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &error)) { if (!(error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT)) { goa_warning ("Error loading %s: %s (%s, %d)", path, error->message, g_quark_to_string (error->domain), error->code); } g_error_free (error); g_key_file_free (key_file); } else { gchar **groups; gsize num_groups; guint n; groups = g_key_file_get_groups (key_file, &num_groups); for (n = 0; n < num_groups; n++) { if (g_str_has_prefix (groups[n], "Account ")) { g_hash_table_insert (group_name_to_key_file_data, groups[n], /* steals string */ key_file_data_new (key_file, path)); } else { goa_warning ("Unexpected group \"%s\" in file %s", groups[n], path); g_free (groups[n]); } } g_free (groups); *key_files_to_free = g_list_prepend (*key_files_to_free, key_file); } }
static void notification_cb (NotifyNotification *notification, gchar *action, gpointer user_data) { GAppLaunchContext *ctx; GDesktopAppInfo *app; GError *error; /* TODO: Hmm, would be nice to set the screen, timestamp etc etc */ ctx = g_app_launch_context_new (); app = g_desktop_app_info_new ("gnome-online-accounts-panel.desktop"); error = NULL; if (!g_app_info_launch (G_APP_INFO (app), NULL, /* files */ ctx, &error)) { goa_warning ("Error launching: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); } g_object_unref (app); g_object_unref (ctx); }
static GFileMonitor * create_monitor (const gchar *path, gboolean is_dir) { GFile *file; GFileMonitor *monitor; GError *error; error = NULL; file = g_file_new_for_path (path); if (is_dir) monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &error); else monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error); if (monitor == NULL) { goa_warning ("Error monitoring %s at %s: %s (%s, %d)", is_dir ? "directory" : "file", path, error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); } g_object_unref (file); return monitor; }
static gboolean on_timer_source_ready (GObject *stream, GoaAlarm *self) { gint64 number_of_fires; gssize bytes_read; gboolean run_again = FALSE; g_return_val_if_fail (GOA_IS_ALARM (self), FALSE); g_return_val_if_fail (self->priv->type == GOA_ALARM_TYPE_TIMER, FALSE); g_rec_mutex_lock (&self->priv->lock); if (g_cancellable_is_cancelled (self->priv->cancellable)) goto out; bytes_read = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream), &number_of_fires, sizeof (gint64), NULL, NULL); if (bytes_read == sizeof (gint64)) { if (number_of_fires < 0 || number_of_fires > 1) { goa_warning ("GoaAlarm: expected timerfd to report firing once," "but it reported firing %ld times\n", (long) number_of_fires); } } fire_or_rearm_alarm (self); run_again = TRUE; out: g_rec_mutex_unlock (&self->priv->lock); return run_again; }
static void goa_daemon_init (GoaDaemon *daemon) { static volatile GQuark goa_error_domain = 0; GoaObjectSkeleton *object; gchar *path; /* this will force associating errors in the GOA_ERROR error domain * with org.freedesktop.Goa.Error.* errors via g_dbus_error_register_error_domain(). */ goa_error_domain = GOA_ERROR; goa_error_domain; /* shut up -Wunused-but-set-variable */ notify_init ("goa-daemon"); /* TODO: maybe nicer to pass in a GDBusConnection* construct property */ daemon->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); /* Create object manager */ daemon->object_manager = g_dbus_object_manager_server_new ("/org/gnome/OnlineAccounts"); /* Create and export Manager */ daemon->manager = goa_manager_skeleton_new (); g_signal_connect (daemon->manager, "handle-add-account", G_CALLBACK (on_manager_handle_add_account), daemon); object = goa_object_skeleton_new ("/org/gnome/OnlineAccounts/Manager"); goa_object_skeleton_set_manager (object, daemon->manager); g_dbus_object_manager_server_export (daemon->object_manager, G_DBUS_OBJECT_SKELETON (object)); g_object_unref (object); /* create ~/.config/goa-1.0 directory */ path = g_strdup_printf ("%s/goa-1.0", g_get_user_config_dir ()); if (g_mkdir_with_parents (path, 0755) != 0) { goa_warning ("Error creating directory %s: %m", path); } g_free (path); /* set up file monitoring */ path = g_strdup_printf ("%s/goa-1.0/accounts.conf", g_get_user_config_dir ()); daemon->home_conf_file_monitor = create_monitor (path, FALSE); if (daemon->home_conf_file_monitor != NULL) g_signal_connect (daemon->home_conf_file_monitor, "changed", G_CALLBACK (on_file_monitor_changed), daemon); g_free (path); /* prime the list of accounts */ goa_daemon_reload_configuration (daemon); /* Export objects */ g_dbus_object_manager_server_set_connection (daemon->object_manager, daemon->connection); }
static void update_account (GoaPanelAccountsModel *model, GoaObject *object) { GtkTreeIter iter; if (!find_iter_for_object (model, object, &iter)) { goa_warning ("Error updating object %s - not in tree", g_dbus_object_get_object_path (G_DBUS_OBJECT (object))); } else { set_values (model, object, &iter); } }
static void remove_account (GoaPanelAccountsModel *model, GoaObject *object) { GtkTreeIter iter; if (!find_iter_for_object (model, object, &iter)) { goa_warning ("Error removing object %s - not in tree", g_dbus_object_get_object_path (G_DBUS_OBJECT (object))); } else { gtk_list_store_remove (GTK_LIST_STORE (model), &iter); } }
static void clear_scheduled_timer_wakeups (GoaAlarm *self) { #ifdef HAVE_TIMERFD GError *error; gboolean is_closed; g_clear_pointer (&self->priv->timer.source, (GDestroyNotify) g_source_destroy); error = NULL; is_closed = g_input_stream_close (self->priv->timer.stream, NULL, &error); if (!is_closed) { goa_warning ("GoaAlarm: could not close timer stream: %s", error->message); g_error_free (error); } g_clear_object (&self->priv->timer.stream); #endif }
static void set_values (GoaPanelAccountsModel *model, GoaObject *object, GtkTreeIter *iter) { GoaAccount *account; GIcon *icon; gchar *markup; GError *error; account = goa_object_peek_account (object); error = NULL; icon = g_icon_new_for_string (goa_account_get_provider_icon (account), &error); if (icon == NULL) { goa_warning ("Error creating GIcon for account: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); } markup = g_strdup_printf ("<b>%s</b>\n<small>%s</small>", goa_account_get_provider_name (account), goa_account_get_presentation_identity (account)); gtk_list_store_set (GTK_LIST_STORE (model), iter, GOA_PANEL_ACCOUNTS_MODEL_COLUMN_SORT_KEY, goa_account_get_id (account), GOA_PANEL_ACCOUNTS_MODEL_COLUMN_OBJECT, object, GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ATTENTION_NEEDED, goa_account_get_attention_needed (account), GOA_PANEL_ACCOUNTS_MODEL_COLUMN_MARKUP, markup, GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ICON, icon, -1); g_free (markup); g_clear_object (&icon); }
static void goa_daemon_update_notifications (GoaDaemon *daemon) { gboolean show_notification; GList *objects; GList *l; show_notification = FALSE; objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (daemon->object_manager)); for (l = objects; l != NULL; l = l->next) { GoaObject *object = GOA_OBJECT (l->data); GoaAccount *account; account = goa_object_peek_account (object); if (account != NULL) { if (goa_account_get_attention_needed (account)) { show_notification = TRUE; break; } } } if (show_notification) { if (daemon->notification == NULL) { GError *error; daemon->notification = notify_notification_new (_("An online account needs attention"), NULL, NULL); notify_notification_set_timeout (daemon->notification, NOTIFY_EXPIRES_NEVER); g_object_set (daemon->notification, "icon-name", "gtk-dialog-error", NULL); notify_notification_add_action (daemon->notification, "open-online-accounts", _("Open Online Accounts..."), notification_cb, daemon, NULL); /* GFreeFunc */ error = NULL; if (!notify_notification_show (daemon->notification, &error)) { goa_warning ("Error showing notification: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); g_object_unref (daemon->notification); daemon->notification = NULL; } else { g_signal_connect (daemon->notification, "closed", G_CALLBACK (on_notification_closed), daemon); } } } else { if (daemon->notification != NULL) { GError *error; error = NULL; if (!notify_notification_close (daemon->notification, &error)) { goa_warning ("Error closing notification: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); } g_signal_handlers_disconnect_by_func (daemon->notification, G_CALLBACK (on_notification_closed), daemon); g_object_unref (daemon->notification); daemon->notification = NULL; } } g_list_foreach (objects, (GFunc) g_object_unref, NULL); g_list_free (objects); }
static void process_config_entries (GoaDaemon *daemon, GHashTable *group_name_to_key_file_data) { GHashTableIter iter; const gchar *id; KeyFileData *key_file_data; GList *existing_object_paths; GList *config_object_paths; GList *added; GList *removed; GList *unchanged; GList *l; existing_object_paths = NULL; { GList *existing_objects; existing_objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (daemon->object_manager)); for (l = existing_objects; l != NULL; l = l->next) { GoaObject *object = GOA_OBJECT (l->data); const gchar *object_path; object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object)); if (g_str_has_prefix (object_path, "/org/gnome/OnlineAccounts/Accounts/")) existing_object_paths = g_list_prepend (existing_object_paths, g_strdup (object_path)); } g_list_foreach (existing_objects, (GFunc) g_object_unref, NULL); g_list_free (existing_objects); } config_object_paths = NULL; g_hash_table_iter_init (&iter, group_name_to_key_file_data); while (g_hash_table_iter_next (&iter, (gpointer*) &id, (gpointer*) &key_file_data)) { gchar *object_path; /* create and validate object path */ object_path = g_strdup_printf ("/org/gnome/OnlineAccounts/Accounts/%s", id + sizeof "Account " - 1); if (strstr (id + sizeof "Account " - 1, "/") != NULL || !g_variant_is_object_path (object_path)) { goa_warning ("`%s' is not a valid account identifier", id); g_free (object_path); continue; } /* steals object_path variable */ config_object_paths = g_list_prepend (config_object_paths, object_path); } existing_object_paths = g_list_sort (existing_object_paths, (GCompareFunc) g_strcmp0); config_object_paths = g_list_sort (config_object_paths, (GCompareFunc) g_strcmp0); diff_sorted_lists (existing_object_paths, config_object_paths, (GCompareFunc) g_strcmp0, &added, &removed, &unchanged); for (l = removed; l != NULL; l = l->next) { const gchar *object_path = l->data; GoaObject *object; object = GOA_OBJECT (g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (daemon->object_manager), object_path)); g_warn_if_fail (object != NULL); g_signal_handlers_disconnect_by_func (goa_object_peek_account (object), G_CALLBACK (account_on_attention_needed_notify), daemon); g_signal_handlers_disconnect_by_func (goa_object_peek_account (object), G_CALLBACK (on_account_handle_remove), daemon); goa_debug ("removing %s", object_path); g_warn_if_fail (g_dbus_object_manager_server_unexport (daemon->object_manager, object_path)); } for (l = added; l != NULL; l = l->next) { const gchar *object_path = l->data; GoaObjectSkeleton *object; gchar *group; goa_debug ("adding %s", object_path); group = object_path_to_group (object_path); key_file_data = g_hash_table_lookup (group_name_to_key_file_data, group); g_warn_if_fail (key_file_data != NULL); object = goa_object_skeleton_new (object_path); if (update_account_object (daemon, object, key_file_data->path, group, key_file_data->key_file, TRUE)) { g_dbus_object_manager_server_export (daemon->object_manager, G_DBUS_OBJECT_SKELETON (object)); g_signal_connect (goa_object_peek_account (GOA_OBJECT (object)), "notify::attention-needed", G_CALLBACK (account_on_attention_needed_notify), daemon); g_signal_connect (goa_object_peek_account (GOA_OBJECT (object)), "handle-remove", G_CALLBACK (on_account_handle_remove), daemon); g_signal_connect (goa_object_peek_account (GOA_OBJECT (object)), "handle-ensure-credentials", G_CALLBACK (on_account_handle_ensure_credentials), daemon); } g_object_unref (object); g_free (group); } for (l = unchanged; l != NULL; l = l->next) { const gchar *object_path = l->data; GoaObject *object; gchar *group; goa_debug ("unchanged %s", object_path); group = object_path_to_group (object_path); key_file_data = g_hash_table_lookup (group_name_to_key_file_data, group); g_warn_if_fail (key_file_data != NULL); object = GOA_OBJECT (g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (daemon->object_manager), object_path)); g_warn_if_fail (object != NULL); if (!update_account_object (daemon, GOA_OBJECT_SKELETON (object), key_file_data->path, group, key_file_data->key_file, FALSE)) { g_signal_handlers_disconnect_by_func (goa_object_peek_account (object), G_CALLBACK (account_on_attention_needed_notify), daemon); g_signal_handlers_disconnect_by_func (goa_object_peek_account (object), G_CALLBACK (on_account_handle_remove), daemon); g_signal_handlers_disconnect_by_func (goa_object_peek_account (object), G_CALLBACK (on_account_handle_ensure_credentials), daemon); g_warn_if_fail (g_dbus_object_manager_server_unexport (daemon->object_manager, object_path)); } g_object_unref (object); g_free (group); } g_list_free (removed); g_list_free (added); g_list_free (unchanged); g_list_foreach (existing_object_paths, (GFunc) g_free, NULL); g_list_free (existing_object_paths); g_list_foreach (config_object_paths, (GFunc) g_free, NULL); g_list_free (config_object_paths); }
/* 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; goa_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) { goa_warning ("Unsupported account type %s for identity %s (no provider)", type, identity); goto out; } name = goa_provider_get_provider_name (provider, GOA_OBJECT (object)); icon = goa_provider_get_provider_icon (provider, GOA_OBJECT (object)); serialized_icon = g_icon_to_string (icon); 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_provider_name (account, name); goa_account_set_provider_icon (account, serialized_icon); 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, &error)) { goa_warning ("Error parsing account: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); goto out; } 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 goa_panel_init (GoaPanel *panel) { GtkWidget *button; GtkWidget *w; GError *error; GtkStyleContext *context; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTreeIter iter; panel->builder = gtk_builder_new (); error = NULL; if (gtk_builder_add_from_file (panel->builder, CINNAMONCC_UI_DIR "/online-accounts.ui", &error) == 0) { goa_warning ("Error loading UI file: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); goto out; } panel->toolbar = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-toolbar")); panel->toolbar_add_button = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-toolbutton-add")); g_signal_connect (panel->toolbar_add_button, "clicked", G_CALLBACK (on_toolbar_add_button_clicked), panel); panel->toolbar_remove_button = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-toolbutton-remove")); g_signal_connect (panel->toolbar_remove_button, "clicked", G_CALLBACK (on_toolbar_remove_button_clicked), panel); context = gtk_widget_get_style_context (GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-scrolledwindow"))); gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM); context = gtk_widget_get_style_context (panel->toolbar); gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP); panel->accounts_treeview = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-treeview")); g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->accounts_treeview)), "changed", G_CALLBACK (on_tree_view_selection_changed), panel); button = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-button-add")); g_signal_connect (button, "clicked", G_CALLBACK (on_add_button_clicked), panel); panel->accounts_vbox = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-vbox")); /* TODO: probably want to avoid _sync() ... */ error = NULL; panel->client = goa_client_new_sync (NULL /* GCancellable */, &error); if (panel->client == NULL) { goa_warning ("Error getting a GoaClient: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); w = GTK_WIDGET (gtk_builder_get_object (panel->builder, "goa-top-widget")); gtk_widget_set_sensitive (w, FALSE); g_error_free (error); goto out; } g_signal_connect (panel->client, "account-changed", G_CALLBACK (on_account_changed), panel); panel->accounts_model = goa_panel_accounts_model_new (panel->client); gtk_tree_view_set_model (GTK_TREE_VIEW (panel->accounts_treeview), GTK_TREE_MODEL (panel->accounts_model)); g_signal_connect (panel->accounts_model, "row-deleted", G_CALLBACK (on_model_row_deleted), panel); g_signal_connect (panel->accounts_model, "row-inserted", G_CALLBACK (on_model_row_inserted), panel); column = gtk_tree_view_column_new (); gtk_tree_view_append_column (GTK_TREE_VIEW (panel->accounts_treeview), column); renderer = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_column_pack_start (column, renderer, FALSE); g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_DIALOG, NULL); gtk_tree_view_column_set_attributes (column, renderer, "gicon", GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ICON, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, FALSE); g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, "ellipsize-set", TRUE, "width-chars", 30, NULL); gtk_tree_view_column_set_attributes (column, renderer, "markup", GOA_PANEL_ACCOUNTS_MODEL_COLUMN_MARKUP, NULL); renderer = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_column_pack_end (column, renderer, FALSE); g_object_set (G_OBJECT (renderer), "icon-name", "dialog-error-symbolic", NULL); gtk_tree_view_column_set_attributes (column, renderer, "visible", GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ATTENTION_NEEDED, NULL); /* Select the first row, if any */ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (panel->accounts_model), &iter)) gtk_tree_selection_select_iter (gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->accounts_treeview)), &iter); out: w = GTK_WIDGET (gtk_builder_get_object (panel->builder, "goa-top-widget")); gtk_widget_reparent (w, GTK_WIDGET (panel)); gtk_widget_show_all (w); }