/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright © 2012 – 2017 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . */ #include "config.h" #include #include "goaewsclient.h" #include "goaprovider.h" #include "goaexchangeprovider.h" #include "goaobjectskeletonutils.h" #include "goautils.h" struct _GoaExchangeProvider { GoaProvider parent_instance; }; G_DEFINE_TYPE_WITH_CODE (GoaExchangeProvider, goa_exchange_provider, GOA_TYPE_PROVIDER, goa_provider_ensure_extension_points_registered (); g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, g_define_type_id, GOA_EXCHANGE_NAME, 0)); /* ---------------------------------------------------------------------------------------------------- */ static const gchar * get_provider_type (GoaProvider *provider) { return GOA_EXCHANGE_NAME; } static gchar * get_provider_name (GoaProvider *provider, GoaObject *object) { return g_strdup(_("Microsoft Exchange")); } static GoaProviderGroup get_provider_group (GoaProvider *provider) { return GOA_PROVIDER_GROUP_BRANDED; } static GoaProviderFeatures get_provider_features (GoaProvider *provider) { return GOA_PROVIDER_FEATURE_BRANDED | GOA_PROVIDER_FEATURE_MAIL | GOA_PROVIDER_FEATURE_CALENDAR | GOA_PROVIDER_FEATURE_CONTACTS; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean on_handle_get_password (GoaPasswordBased *interface, GDBusMethodInvocation *invocation, const gchar *id, gpointer user_data); static gboolean build_object (GoaProvider *provider, GoaObjectSkeleton *object, GKeyFile *key_file, const gchar *group, GDBusConnection *connection, gboolean just_added, GError **error) { GoaAccount *account = NULL; GoaExchange *exchange = NULL; GoaMail *mail = NULL; GoaPasswordBased *password_based = NULL; gboolean calendar_enabled; gboolean contacts_enabled; gboolean mail_enabled; gboolean ret = FALSE; /* Chain up */ if (!GOA_PROVIDER_CLASS (goa_exchange_provider_parent_class)->build_object (provider, object, key_file, group, connection, just_added, error)) goto out; password_based = goa_object_get_password_based (GOA_OBJECT (object)); if (password_based == NULL) { password_based = goa_password_based_skeleton_new (); /* Ensure D-Bus method invocations run in their own thread */ g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (password_based), G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); goa_object_skeleton_set_password_based (object, password_based); g_signal_connect (password_based, "handle-get-password", G_CALLBACK (on_handle_get_password), NULL); } account = goa_object_get_account (GOA_OBJECT (object)); /* Email */ mail = goa_object_get_mail (GOA_OBJECT (object)); mail_enabled = g_key_file_get_boolean (key_file, group, "MailEnabled", NULL); if (mail_enabled) { if (mail == NULL) { const gchar *email_address; email_address = goa_account_get_presentation_identity (account); mail = goa_mail_skeleton_new (); g_object_set (G_OBJECT (mail), "email-address", email_address, NULL); goa_object_skeleton_set_mail (object, mail); } } else { if (mail != NULL) goa_object_skeleton_set_mail (object, NULL); } /* Calendar */ calendar_enabled = g_key_file_get_boolean (key_file, group, "CalendarEnabled", NULL); goa_object_skeleton_attach_calendar (object, NULL, calendar_enabled, FALSE); /* Contacts */ contacts_enabled = g_key_file_get_boolean (key_file, group, "ContactsEnabled", NULL); goa_object_skeleton_attach_contacts (object, NULL, contacts_enabled, FALSE); /* Exchange */ exchange = goa_object_get_exchange (GOA_OBJECT (object)); if (exchange == NULL) { gboolean accept_ssl_errors; gchar *host; accept_ssl_errors = g_key_file_get_boolean (key_file, group, "AcceptSslErrors", NULL); host = g_key_file_get_string (key_file, group, "Host", NULL); exchange = goa_exchange_skeleton_new (); g_object_set (G_OBJECT (exchange), "accept-ssl-errors", accept_ssl_errors, "host", host, NULL); goa_object_skeleton_set_exchange (object, exchange); g_free (host); } if (just_added) { goa_account_set_mail_disabled (account, !mail_enabled); goa_account_set_calendar_disabled (account, !calendar_enabled); goa_account_set_contacts_disabled (account, !contacts_enabled); g_signal_connect (account, "notify::mail-disabled", G_CALLBACK (goa_util_account_notify_property_cb), (gpointer) "MailEnabled"); g_signal_connect (account, "notify::calendar-disabled", G_CALLBACK (goa_util_account_notify_property_cb), (gpointer) "CalendarEnabled"); g_signal_connect (account, "notify::contacts-disabled", G_CALLBACK (goa_util_account_notify_property_cb), (gpointer) "ContactsEnabled"); } ret = TRUE; out: g_clear_object (&exchange); g_clear_object (&mail); g_clear_object (&password_based); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean ensure_credentials_sync (GoaProvider *provider, GoaObject *object, gint *out_expires_in, GCancellable *cancellable, GError **error) { GoaAccount *account; GoaEwsClient *ews_client = NULL; GoaExchange *exchange; gboolean accept_ssl_errors; gboolean ret = FALSE; const gchar *email_address; const gchar *server; gchar *username = NULL; gchar *password = NULL; if (!goa_utils_get_credentials (provider, object, "password", &username, &password, cancellable, error)) { if (error != NULL) { (*error)->domain = GOA_ERROR; (*error)->code = GOA_ERROR_NOT_AUTHORIZED; } goto out; } account = goa_object_peek_account (object); email_address = goa_account_get_presentation_identity (account); exchange = goa_object_peek_exchange (object); accept_ssl_errors = goa_exchange_get_accept_ssl_errors (exchange); server = goa_exchange_get_host (exchange); ews_client = goa_ews_client_new (); if (!goa_ews_client_autodiscover_sync (ews_client, email_address, password, username, server, accept_ssl_errors, cancellable, error)) { if (error != NULL) { g_prefix_error (error, /* Translators: the first %s is the username * (eg., debarshi.ray@gmail.com or rishi), and the * (%s, %d) is the error domain and code. */ _("Invalid password with username “%s” (%s, %d): "), username, g_quark_to_string ((*error)->domain), (*error)->code); (*error)->domain = GOA_ERROR; (*error)->code = GOA_ERROR_NOT_AUTHORIZED; } goto out; } if (out_expires_in != NULL) *out_expires_in = 0; ret = TRUE; out: g_clear_object (&ews_client); g_free (username); g_free (password); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static void add_entry (GtkWidget *grid, gint row, const gchar *text, GtkWidget **out_entry) { GtkStyleContext *context; GtkWidget *label; GtkWidget *entry; label = gtk_label_new_with_mnemonic (text); context = gtk_widget_get_style_context (label); gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL); gtk_widget_set_halign (label, GTK_ALIGN_END); gtk_widget_set_hexpand (label, TRUE); gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1); entry = gtk_entry_new (); gtk_widget_set_hexpand (entry, TRUE); gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); gtk_grid_attach (GTK_GRID (grid), entry, 1, row, 3, 1); gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry); if (out_entry != NULL) *out_entry = entry; } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { GCancellable *cancellable; GtkDialog *dialog; GMainLoop *loop; GtkWidget *cluebar; GtkWidget *cluebar_label; GtkWidget *connect_button; GtkWidget *progress_grid; GtkWidget *email_address; GtkWidget *password; GtkWidget *expander; GtkWidget *username; GtkWidget *server; gchar *account_object_path; GError *error; } AddAccountData; /* ---------------------------------------------------------------------------------------------------- */ static void on_email_address_or_password_changed (GtkEditable *editable, gpointer user_data) { AddAccountData *data = user_data; gboolean can_add = FALSE; const gchar *email; gchar *domain = NULL; gchar *url = NULL; gchar *username = NULL; email = gtk_entry_get_text (GTK_ENTRY (data->email_address)); if (!goa_utils_parse_email_address (email, &username, &domain)) goto out; if (data->username != NULL) gtk_entry_set_text (GTK_ENTRY (data->username), username); if (data->server != NULL) gtk_entry_set_text (GTK_ENTRY (data->server), domain); can_add = gtk_entry_get_text_length (GTK_ENTRY (data->password)) != 0; out: gtk_dialog_set_response_sensitive (data->dialog, GTK_RESPONSE_OK, can_add); g_free (url); g_free (domain); g_free (username); } static void create_account_details_ui (GoaProvider *provider, GtkDialog *dialog, GtkBox *vbox, gboolean new_account, AddAccountData *data) { GtkWidget *grid0; GtkWidget *grid1; GtkWidget *label; GtkWidget *spinner; gint row; gint width; goa_utils_set_dialog_title (provider, dialog, new_account); grid0 = gtk_grid_new (); gtk_container_set_border_width (GTK_CONTAINER (grid0), 5); gtk_widget_set_margin_bottom (grid0, 6); gtk_orientable_set_orientation (GTK_ORIENTABLE (grid0), GTK_ORIENTATION_VERTICAL); gtk_grid_set_row_spacing (GTK_GRID (grid0), 12); gtk_container_add (GTK_CONTAINER (vbox), grid0); data->cluebar = gtk_info_bar_new (); gtk_info_bar_set_message_type (GTK_INFO_BAR (data->cluebar), GTK_MESSAGE_ERROR); gtk_widget_set_hexpand (data->cluebar, TRUE); gtk_widget_set_no_show_all (data->cluebar, TRUE); gtk_container_add (GTK_CONTAINER (grid0), data->cluebar); data->cluebar_label = gtk_label_new (""); gtk_label_set_line_wrap (GTK_LABEL (data->cluebar_label), TRUE); gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (data->cluebar))), data->cluebar_label); grid1 = gtk_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (grid1), 12); gtk_grid_set_row_spacing (GTK_GRID (grid1), 12); gtk_container_add (GTK_CONTAINER (grid0), grid1); row = 0; add_entry (grid1, row++, _("_E-mail"), &data->email_address); add_entry (grid1, row++, _("_Password"), &data->password); if (new_account) { data->expander = gtk_expander_new_with_mnemonic (_("_Custom")); gtk_expander_set_expanded (GTK_EXPANDER (data->expander), FALSE); gtk_expander_set_resize_toplevel (GTK_EXPANDER (data->expander), TRUE); gtk_container_add (GTK_CONTAINER (grid0), data->expander); grid1 = gtk_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (grid1), 12); gtk_grid_set_row_spacing (GTK_GRID (grid1), 12); gtk_container_add (GTK_CONTAINER (data->expander), grid1); row = 0; add_entry (grid1, row++, _("User_name"), &data->username); add_entry (grid1, row++, _("_Server"), &data->server); } gtk_entry_set_visibility (GTK_ENTRY (data->password), FALSE); gtk_widget_grab_focus ((new_account) ? data->email_address : data->password); g_signal_connect (data->email_address, "changed", G_CALLBACK (on_email_address_or_password_changed), data); g_signal_connect (data->password, "changed", G_CALLBACK (on_email_address_or_password_changed), data); gtk_dialog_add_button (data->dialog, _("_Cancel"), GTK_RESPONSE_CANCEL); data->connect_button = gtk_dialog_add_button (data->dialog, _("C_onnect"), GTK_RESPONSE_OK); gtk_dialog_set_default_response (data->dialog, GTK_RESPONSE_OK); gtk_dialog_set_response_sensitive (data->dialog, GTK_RESPONSE_OK, FALSE); data->progress_grid = gtk_grid_new (); gtk_widget_set_no_show_all (data->progress_grid, TRUE); gtk_orientable_set_orientation (GTK_ORIENTABLE (data->progress_grid), GTK_ORIENTATION_HORIZONTAL); gtk_grid_set_column_spacing (GTK_GRID (data->progress_grid), 3); gtk_container_add (GTK_CONTAINER (grid0), data->progress_grid); spinner = gtk_spinner_new (); gtk_widget_set_size_request (spinner, 20, 20); gtk_widget_show (spinner); gtk_spinner_start (GTK_SPINNER (spinner)); gtk_container_add (GTK_CONTAINER (data->progress_grid), spinner); label = gtk_label_new (_("Connecting…")); gtk_widget_show (label); gtk_container_add (GTK_CONTAINER (data->progress_grid), label); if (new_account) { gtk_window_get_size (GTK_WINDOW (data->dialog), &width, NULL); gtk_window_set_default_size (GTK_WINDOW (data->dialog), width, -1); } else { GtkWindow *parent; /* Keep in sync with GoaPanelAddAccountDialog in * gnome-control-center. */ parent = gtk_window_get_transient_for (GTK_WINDOW (data->dialog)); if (parent != NULL) { gtk_window_get_size (parent, &width, NULL); gtk_window_set_default_size (GTK_WINDOW (data->dialog), (gint) (0.5 * width), -1); } } } /* ---------------------------------------------------------------------------------------------------- */ static void add_account_cb (GoaManager *manager, GAsyncResult *res, gpointer user_data) { AddAccountData *data = user_data; goa_manager_call_add_account_finish (manager, &data->account_object_path, res, &data->error); g_main_loop_quit (data->loop); } static void autodiscover_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GoaEwsClient *client = GOA_EWS_CLIENT (source_object); AddAccountData *data = user_data; goa_ews_client_autodiscover_finish (client, res, &data->error); g_main_loop_quit (data->loop); gtk_widget_set_sensitive (data->connect_button, TRUE); gtk_widget_hide (data->progress_grid); } static void dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data) { AddAccountData *data = user_data; if (response_id == GTK_RESPONSE_CANCEL || response_id == GTK_RESPONSE_DELETE_EVENT) g_cancellable_cancel (data->cancellable); } /* ---------------------------------------------------------------------------------------------------- */ static GoaObject * add_account (GoaProvider *provider, GoaClient *client, GtkDialog *dialog, GtkBox *vbox, GError **error) { AddAccountData data; GVariantBuilder credentials; GVariantBuilder details; GoaEwsClient *ews_client = NULL; GoaObject *ret = NULL; gboolean accept_ssl_errors = FALSE; const gchar *email_address; const gchar *server; const gchar *password; const gchar *username; const gchar *provider_type; gint response; memset (&data, 0, sizeof (AddAccountData)); data.cancellable = g_cancellable_new (); data.loop = g_main_loop_new (NULL, FALSE); data.dialog = dialog; data.error = NULL; create_account_details_ui (provider, dialog, vbox, TRUE, &data); gtk_widget_show_all (GTK_WIDGET (vbox)); g_signal_connect (dialog, "response", G_CALLBACK (dialog_response_cb), &data); ews_client = goa_ews_client_new (); ews_again: response = gtk_dialog_run (dialog); if (response != GTK_RESPONSE_OK) { g_set_error (&data.error, GOA_ERROR, GOA_ERROR_DIALOG_DISMISSED, _("Dialog was dismissed")); goto out; } email_address = gtk_entry_get_text (GTK_ENTRY (data.email_address)); password = gtk_entry_get_text (GTK_ENTRY (data.password)); username = gtk_entry_get_text (GTK_ENTRY (data.username)); server = gtk_entry_get_text (GTK_ENTRY (data.server)); /* See if there's already an account of this type with the * given identity */ provider_type = goa_provider_get_provider_type (provider); if (!goa_utils_check_duplicate (client, username, email_address, provider_type, (GoaPeekInterfaceFunc) goa_object_peek_password_based, &data.error)) goto out; g_cancellable_reset (data.cancellable); goa_ews_client_autodiscover (ews_client, email_address, password, username, server, accept_ssl_errors, data.cancellable, autodiscover_cb, &data); gtk_widget_set_sensitive (data.connect_button, FALSE); gtk_widget_show (data.progress_grid); g_main_loop_run (data.loop); if (g_cancellable_is_cancelled (data.cancellable)) { g_prefix_error (&data.error, _("Dialog was dismissed (%s, %d): "), g_quark_to_string (data.error->domain), data.error->code); data.error->domain = GOA_ERROR; data.error->code = GOA_ERROR_DIALOG_DISMISSED; goto out; } else if (data.error != NULL) { gchar *markup; if (data.error->code == GOA_ERROR_SSL) { gtk_button_set_label (GTK_BUTTON (data.connect_button), _("_Ignore")); accept_ssl_errors = TRUE; } else { gtk_button_set_label (GTK_BUTTON (data.connect_button), _("_Try Again")); accept_ssl_errors = FALSE; } markup = g_strdup_printf ("%s:\n%s", _("Error connecting to Microsoft Exchange server"), data.error->message); g_clear_error (&data.error); gtk_label_set_markup (GTK_LABEL (data.cluebar_label), markup); g_free (markup); gtk_expander_set_expanded (GTK_EXPANDER (data.expander), TRUE); gtk_widget_set_no_show_all (data.cluebar, FALSE); gtk_widget_show_all (data.cluebar); goto ews_again; } gtk_widget_hide (GTK_WIDGET (dialog)); g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&credentials, "{sv}", "password", g_variant_new_string (password)); g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); g_variant_builder_add (&details, "{ss}", "MailEnabled", "true"); g_variant_builder_add (&details, "{ss}", "CalendarEnabled", "true"); g_variant_builder_add (&details, "{ss}", "ContactsEnabled", "true"); g_variant_builder_add (&details, "{ss}", "Host", server); g_variant_builder_add (&details, "{ss}", "AcceptSslErrors", (accept_ssl_errors) ? "true" : "false"); /* OK, everything is dandy, add the account */ /* we want the GoaClient to update before this method returns (so it * can create a proxy for the new object) so run the mainloop while * waiting for this to complete */ goa_manager_call_add_account (goa_client_get_manager (client), goa_provider_get_provider_type (provider), username, email_address, g_variant_builder_end (&credentials), g_variant_builder_end (&details), NULL, /* GCancellable* */ (GAsyncReadyCallback) add_account_cb, &data); g_main_loop_run (data.loop); if (data.error != NULL) goto out; ret = GOA_OBJECT (g_dbus_object_manager_get_object (goa_client_get_object_manager (client), data.account_object_path)); out: /* We might have an object even when data.error is set. * eg., if we failed to store the credentials in the keyring. */ if (data.error != NULL) g_propagate_error (error, data.error); else g_assert (ret != NULL); g_signal_handlers_disconnect_by_func (dialog, dialog_response_cb, &data); g_free (data.account_object_path); g_clear_pointer (&data.loop, (GDestroyNotify) g_main_loop_unref); g_clear_object (&data.cancellable); g_clear_object (&ews_client); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean refresh_account (GoaProvider *provider, GoaClient *client, GoaObject *object, GtkWindow *parent, GError **error) { AddAccountData data; GVariantBuilder builder; GoaAccount *account; GoaEwsClient *ews_client = NULL; GoaExchange *exchange; GtkWidget *dialog; GtkWidget *vbox; gboolean accept_ssl_errors; gboolean ret = FALSE; const gchar *email_address; const gchar *server; const gchar *password; const gchar *username; gint response; g_return_val_if_fail (GOA_IS_EXCHANGE_PROVIDER (provider), FALSE); g_return_val_if_fail (GOA_IS_CLIENT (client), FALSE); g_return_val_if_fail (GOA_IS_OBJECT (object), FALSE); g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); dialog = gtk_dialog_new_with_buttons (NULL, parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR, NULL, NULL); gtk_container_set_border_width (GTK_CONTAINER (dialog), 12); gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); gtk_box_set_spacing (GTK_BOX (vbox), 12); memset (&data, 0, sizeof (AddAccountData)); data.cancellable = g_cancellable_new (); data.loop = g_main_loop_new (NULL, FALSE); data.dialog = GTK_DIALOG (dialog); data.error = NULL; create_account_details_ui (provider, GTK_DIALOG (dialog), GTK_BOX (vbox), FALSE, &data); account = goa_object_peek_account (object); email_address = goa_account_get_presentation_identity (account); gtk_entry_set_text (GTK_ENTRY (data.email_address), email_address); gtk_editable_set_editable (GTK_EDITABLE (data.email_address), FALSE); gtk_widget_show_all (dialog); g_signal_connect (dialog, "response", G_CALLBACK (dialog_response_cb), &data); ews_client = goa_ews_client_new (); ews_again: response = gtk_dialog_run (GTK_DIALOG (dialog)); if (response != GTK_RESPONSE_OK) { g_set_error (&data.error, GOA_ERROR, GOA_ERROR_DIALOG_DISMISSED, _("Dialog was dismissed")); goto out; } password = gtk_entry_get_text (GTK_ENTRY (data.password)); username = goa_account_get_identity (account); exchange = goa_object_peek_exchange (object); accept_ssl_errors = goa_exchange_get_accept_ssl_errors (exchange); server = goa_exchange_get_host (exchange); g_cancellable_reset (data.cancellable); goa_ews_client_autodiscover (ews_client, email_address, password, username, server, accept_ssl_errors, data.cancellable, autodiscover_cb, &data); gtk_widget_set_sensitive (data.connect_button, FALSE); gtk_widget_show (data.progress_grid); g_main_loop_run (data.loop); if (g_cancellable_is_cancelled (data.cancellable)) { g_prefix_error (&data.error, _("Dialog was dismissed (%s, %d): "), g_quark_to_string (data.error->domain), data.error->code); data.error->domain = GOA_ERROR; data.error->code = GOA_ERROR_DIALOG_DISMISSED; goto out; } else if (data.error != NULL) { gchar *markup; markup = g_strdup_printf ("%s:\n%s", _("Error connecting to Microsoft Exchange server"), data.error->message); g_clear_error (&data.error); gtk_label_set_markup (GTK_LABEL (data.cluebar_label), markup); g_free (markup); gtk_button_set_label (GTK_BUTTON (data.connect_button), _("_Try Again")); gtk_widget_set_no_show_all (data.cluebar, FALSE); gtk_widget_show_all (data.cluebar); goto ews_again; } /* TODO: run in worker thread */ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&builder, "{sv}", "password", g_variant_new_string (password)); if (!goa_utils_store_credentials_for_object_sync (provider, object, g_variant_builder_end (&builder), NULL, /* GCancellable */ &data.error)) goto out; goa_account_call_ensure_credentials (account, NULL, /* GCancellable */ NULL, NULL); /* callback, user_data */ ret = TRUE; out: if (data.error != NULL) g_propagate_error (error, data.error); gtk_widget_destroy (dialog); g_clear_pointer (&data.loop, (GDestroyNotify) g_main_loop_unref); g_clear_object (&data.cancellable); g_clear_object (&ews_client); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static void goa_exchange_provider_init (GoaExchangeProvider *self) { } static void goa_exchange_provider_class_init (GoaExchangeProviderClass *klass) { GoaProviderClass *provider_class; provider_class = GOA_PROVIDER_CLASS (klass); provider_class->get_provider_type = get_provider_type; provider_class->get_provider_name = get_provider_name; provider_class->get_provider_group = get_provider_group; provider_class->get_provider_features = get_provider_features; provider_class->add_account = add_account; provider_class->refresh_account = refresh_account; provider_class->build_object = build_object; provider_class->ensure_credentials_sync = ensure_credentials_sync; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean on_handle_get_password (GoaPasswordBased *interface, GDBusMethodInvocation *invocation, const gchar *id, /* unused */ gpointer user_data) { GoaObject *object; GoaAccount *account; GoaProvider *provider; GError *error; const gchar *account_id; const gchar *method_name; const gchar *provider_type; gchar *password = NULL; /* TODO: maybe log what app is requesting access */ object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (interface))); account = goa_object_peek_account (object); account_id = goa_account_get_id (account); provider_type = goa_account_get_provider_type (account); method_name = g_dbus_method_invocation_get_method_name (invocation); g_debug ("Handling %s for account (%s, %s)", method_name, provider_type, account_id); provider = goa_provider_get_for_provider_type (provider_type); error = NULL; if (!goa_utils_get_credentials (provider, object, "password", NULL, &password, NULL, &error)) { g_dbus_method_invocation_take_error (invocation, error); goto out; } goa_password_based_complete_get_password (interface, invocation, password); out: g_free (password); g_object_unref (provider); return TRUE; /* invocation was handled */ }