/* * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com) * * 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. * * 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 "evolution-data-server-config.h" #include #include "e-data-server-util.h" #include "e-oauth2-service.h" #include "e-oauth2-service-base.h" #include "e-oauth2-service-google.h" /* https://developers.google.com/identity/protocols/OAuth2InstalledApp */ /* https://developers.google.com/identity/protocols/oauth2/native-app */ /* Forward Declarations */ static void e_oauth2_service_google_oauth2_service_init (EOAuth2ServiceInterface *iface); G_DEFINE_TYPE_WITH_CODE (EOAuth2ServiceGoogle, e_oauth2_service_google, E_TYPE_OAUTH2_SERVICE_BASE, G_IMPLEMENT_INTERFACE (E_TYPE_OAUTH2_SERVICE, e_oauth2_service_google_oauth2_service_init)) static gboolean eos_google_guess_can_process (EOAuth2Service *service, const gchar *protocol, const gchar *hostname) { return hostname && ( e_util_utf8_strstrcase (hostname, ".google.com") || e_util_utf8_strstrcase (hostname, ".googlemail.com") || e_util_utf8_strstrcase (hostname, ".googleusercontent.com") || e_util_utf8_strstrcase (hostname, ".gmail.com")); } static const gchar * eos_google_get_name (EOAuth2Service *service) { return "Google"; } static const gchar * eos_google_get_display_name (EOAuth2Service *service) { /* Translators: This is a user-visible string, display name of an OAuth2 service. */ return C_("OAuth2Service", "Google"); } static const gchar * eos_google_read_settings (EOAuth2Service *service, const gchar *key_name) { G_LOCK_DEFINE_STATIC (user_settings); gchar *value; G_LOCK (user_settings); value = g_object_get_data (G_OBJECT (service), key_name); if (!value) { GSettings *settings; settings = g_settings_new ("org.gnome.evolution-data-server"); value = g_settings_get_string (settings, key_name); g_object_unref (settings); if (value && *value) { g_object_set_data_full (G_OBJECT (service), key_name, value, g_free); } else { g_free (value); value = (gchar *) ""; g_object_set_data (G_OBJECT (service), key_name, value); } } G_UNLOCK (user_settings); return value; } static const gchar * eos_google_get_client_id (EOAuth2Service *service, ESource *source) { static gchar glob_buff[128] = {0, }; const gchar *client_id; client_id = eos_google_read_settings (service, "oauth2-google-client-id"); if (client_id && *client_id) return client_id; return e_oauth2_service_util_compile_value (GOOGLE_CLIENT_ID, glob_buff, sizeof (glob_buff)); } static const gchar * eos_google_get_client_secret (EOAuth2Service *service, ESource *source) { static gchar glob_buff[128] = {0, }; const gchar *client_secret; client_secret = eos_google_read_settings (service, "oauth2-google-client-secret"); if (client_secret && *client_secret) return client_secret; return e_oauth2_service_util_compile_value (GOOGLE_CLIENT_SECRET, glob_buff, sizeof (glob_buff)); } static const gchar * eos_google_get_authentication_uri (EOAuth2Service *service, ESource *source) { return "https://accounts.google.com/o/oauth2/v2/auth"; } static const gchar * eos_google_get_refresh_uri (EOAuth2Service *service, ESource *source) { return "https://oauth2.googleapis.com/token"; } static const gchar * eos_google_get_redirect_uri (EOAuth2Service *service, ESource *source) { G_LOCK_DEFINE_STATIC (redirect_uri); const gchar *key_name = "oauth2-google-redirect-uri"; gchar *value; G_LOCK (redirect_uri); value = g_object_get_data (G_OBJECT (service), key_name); if (!value) { const gchar *client_id = eos_google_get_client_id (service, source); if (client_id) { GPtrArray *array; gchar **strv; gchar *joinstr; guint ii; strv = g_strsplit (client_id, ".", -1); array = g_ptr_array_new (); for (ii = 0; strv[ii]; ii++) { g_ptr_array_insert (array, 0, strv[ii]); } g_ptr_array_add (array, NULL); joinstr = g_strjoinv (".", (gchar **) array->pdata); /* Use reverse-DNS of the client ID with the below path */ value = g_strconcat (joinstr, ":/oauth2redirect", NULL); g_ptr_array_free (array, TRUE); g_strfreev (strv); g_free (joinstr); g_object_set_data_full (G_OBJECT (service), key_name, value, g_free); } } G_UNLOCK (redirect_uri); return value; } static void eos_google_prepare_authentication_uri_query (EOAuth2Service *service, ESource *source, GHashTable *uri_query) { const gchar *GOOGLE_SCOPE = /* GMail IMAP and SMTP access */ "https://mail.google.com/ " /* Google Calendar API (CalDAV and GData) */ "https://www.googleapis.com/auth/calendar " /* Google Contacts API (GData) */ "https://www.google.com/m8/feeds/ " /* Google Contacts API (CardDAV) - undocumented */ "https://www.googleapis.com/auth/carddav " /* Google Tasks - undocumented */ "https://www.googleapis.com/auth/tasks"; g_return_if_fail (uri_query != NULL); e_oauth2_service_util_set_to_form (uri_query, "scope", GOOGLE_SCOPE); e_oauth2_service_util_set_to_form (uri_query, "include_granted_scopes", "false"); } static gboolean eos_google_extract_authorization_code (EOAuth2Service *service, ESource *source, const gchar *page_title, const gchar *page_uri, const gchar *page_content, gchar **out_authorization_code) { gchar *error_code = NULL, *error_message = NULL; gboolean success; g_return_val_if_fail (out_authorization_code != NULL, FALSE); *out_authorization_code = NULL; if (page_title && *page_title) { /* Known response, but no authorization code */ if (g_ascii_strncasecmp (page_title, "Denied ", 7) == 0) return TRUE; if (g_ascii_strncasecmp (page_title, "Success code=", 13) == 0) { *out_authorization_code = g_strdup (page_title + 13); return TRUE; } } /* It is an acceptable response when either the authorization code or the error information is set. */ success = e_oauth2_service_util_extract_from_uri (page_uri, out_authorization_code, &error_code, &error_message); g_free (error_code); g_free (error_message); return success; } static void e_oauth2_service_google_oauth2_service_init (EOAuth2ServiceInterface *iface) { iface->guess_can_process = eos_google_guess_can_process; iface->get_name = eos_google_get_name; iface->get_display_name = eos_google_get_display_name; iface->get_client_id = eos_google_get_client_id; iface->get_client_secret = eos_google_get_client_secret; iface->get_authentication_uri = eos_google_get_authentication_uri; iface->get_refresh_uri = eos_google_get_refresh_uri; iface->get_redirect_uri = eos_google_get_redirect_uri; iface->prepare_authentication_uri_query = eos_google_prepare_authentication_uri_query; iface->extract_authorization_code = eos_google_extract_authorization_code; } static void e_oauth2_service_google_class_init (EOAuth2ServiceGoogleClass *klass) { } static void e_oauth2_service_google_init (EOAuth2ServiceGoogle *oauth2_google) { }