From 5178cc0743214c42feeb587e45935b33a6404036 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Fri, 31 May 2024 16:40:41 +0000 Subject: [PATCH] Import from AlmaLinux stable repository --- .gnome-online-accounts.metadata | 1 - ...002-Drop-dependency-on-WebKitGTK-139.patch | 4922 +++++++++++++++++ SPECS/gnome-online-accounts.spec | 36 +- 3 files changed, 4950 insertions(+), 9 deletions(-) delete mode 100644 .gnome-online-accounts.metadata create mode 100644 SOURCES/0002-Drop-dependency-on-WebKitGTK-139.patch diff --git a/.gnome-online-accounts.metadata b/.gnome-online-accounts.metadata deleted file mode 100644 index 4cb487b..0000000 --- a/.gnome-online-accounts.metadata +++ /dev/null @@ -1 +0,0 @@ -382053cff81f8b054d4beb02c0430ee4accb65ed SOURCES/gnome-online-accounts-3.28.2.tar.xz diff --git a/SOURCES/0002-Drop-dependency-on-WebKitGTK-139.patch b/SOURCES/0002-Drop-dependency-on-WebKitGTK-139.patch new file mode 100644 index 0000000..09be406 --- /dev/null +++ b/SOURCES/0002-Drop-dependency-on-WebKitGTK-139.patch @@ -0,0 +1,4922 @@ +diff --git a/configure.ac b/configure.ac +index ddf98f1..f997d36 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -124,10 +124,6 @@ if test "$enable_backend" != "no"; then + AC_SUBST(GTK_CFLAGS) + AC_SUBST(GTK_LIBS) + +- PKG_CHECK_MODULES(WEBKIT_GTK, [webkit2gtk-4.0 >= 2.12.0]) +- AC_SUBST(WEBKIT_GTK_CFLAGS) +- AC_SUBST(WEBKIT_GTK_LIBS) +- + PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.42]) + AC_SUBST(LIBSOUP_CFLAGS) + AC_SUBST(LIBSOUP_LIBS) +@@ -176,14 +172,6 @@ if test "$enable_backend" != "no"; then + fi + fi + +-AC_ARG_ENABLE([inspector], +- [AS_HELP_STRING([--enable-inspector], [Enable a WebKitWebInspector for the embedded web view])], +- [], +- [enable_inspector=no]) +-if test "$enable_inspector" != "no"; then +- AC_DEFINE(GOA_INSPECTOR_ENABLED, 1, [Enable a WebKitWebInspector for the embedded web view]) +-fi +- + AC_ARG_WITH(template-file, + [AS_HELP_STRING([--with-template-file], [Path to the template file])], + [], +@@ -271,7 +259,11 @@ AC_DEFINE_UNQUOTED(GOA_GOOGLE_CLIENT_ID, ["$with_google_client_id"], [Google OAu + AC_DEFINE_UNQUOTED(GOA_GOOGLE_CLIENT_SECRET, ["$with_google_client_secret"], [Google OAuth 2.0 client secret]) + if test "$enable_google" != "no"; then + AC_DEFINE(GOA_GOOGLE_ENABLED, 1, [Enable Google data provider]) ++ if test "$with_google_client_id" != "44438659992-7kgjeitenc16ssihbtdjbgguch7ju55s.apps.googleusercontent.com"; then ++ AC_MSG_ERROR([Unexpected Google OAuth2 Client ID, correct it here and in data/Makefile.am in oauth2_schemes, to be reverse-DNS of the new Client ID]) ++ fi + fi ++AM_CONDITIONAL(GOOGLE_ENABLED, [test x$enable_google != xno]) + + # IMAP/SMTP + AC_DEFINE(GOA_IMAP_SMTP_NAME, ["imap_smtp"], [ProviderType and extension point name]) +diff --git a/data/Makefile.am b/data/Makefile.am +index 286dcd0..61b69e1 100644 +--- a/data/Makefile.am ++++ b/data/Makefile.am +@@ -20,16 +20,30 @@ service_DATA = $(service_in_files:.service.in=.service) + @sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ + endif + ++desktopdir = $(datadir)/applications ++desktop_in_files = org.gnome.OnlineAccounts.OAuth2.desktop.in ++desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) ++if GOOGLE_ENABLED ++ oauth2_schemes=x-scheme-handler/com.googleusercontent.apps.44438659992-7kgjeitenc16ssihbtdjbgguch7ju55s; ++else ++ oauth2_schemes= ++endif ++ ++%.desktop: %.desktop.in Makefile ++ @sed -e "s|\@libexecdir\@|$(libexecdir)|" -e "s|\@oauth2_schemes\@|$(oauth2_schemes)|" $< > $@ ++ + EXTRA_DIST = \ + $(gsettings_SCHEMAS) \ + dbus-interfaces.xml \ + org.gnome.Identity.service.in \ + org.gnome.OnlineAccounts.service.in \ ++ org.gnome.OnlineAccounts.OAuth2.desktop.in \ + $(NULL) + + CLEANFILES = \ + org.gnome.OnlineAccounts.service \ + org.gnome.Identity.service \ ++ org.gnome.OnlineAccounts.OAuth2.desktop \ + $(NULL) + + clean-local : +diff --git a/data/org.gnome.OnlineAccounts.OAuth2.desktop.in b/data/org.gnome.OnlineAccounts.OAuth2.desktop.in +new file mode 100644 +index 0000000..d0478aa +--- /dev/null ++++ b/data/org.gnome.OnlineAccounts.OAuth2.desktop.in +@@ -0,0 +1,6 @@ ++[Desktop Entry] ++Name=GNOME OAuth2 Handler ++Exec=@libexecdir@/goa-oauth2-handler %u ++Type=Application ++MimeType=x-scheme-handler/goa-oauth2;@oauth2_schemes@ ++NoDisplay=true +diff --git a/doc/goa-docs.xml b/doc/goa-docs.xml +index 0abb53a..a9d45e1 100644 +--- a/doc/goa-docs.xml ++++ b/doc/goa-docs.xml +@@ -171,7 +171,6 @@ + Core + + +- + + + +diff --git a/doc/goa-sections.txt b/doc/goa-sections.txt +index 306846e..a27d942 100644 +--- a/doc/goa-sections.txt ++++ b/doc/goa-sections.txt +@@ -498,36 +498,6 @@ GoaOAuth2ProviderPrivate + goa_oauth2_provider_get_type + + +-
+-goaoauthprovider +-GoaOAuthProvider +-GoaOAuthProviderClass +-goa_oauth_provider_get_request_uri +-goa_oauth_provider_get_request_uri_params +-goa_oauth_provider_get_authorization_uri +-goa_oauth_provider_get_token_uri +-goa_oauth_provider_get_callback_uri +-goa_oauth_provider_get_consumer_key +-goa_oauth_provider_get_consumer_secret +-goa_oauth_provider_build_authorization_uri +-goa_oauth_provider_get_use_mobile_browser +-goa_oauth_provider_is_deny_node +-goa_oauth_provider_is_identity_node +-goa_oauth_provider_is_password_node +-goa_oauth_provider_add_account_key_values +-goa_oauth_provider_get_identity_sync +-goa_oauth_provider_get_access_token_sync +-goa_oauth_provider_parse_request_token_error +- +-GOA_OAUTH_PROVIDER +-GOA_OAUTH_PROVIDER_CLASS +-GOA_OAUTH_PROVIDER_GET_CLASS +-GOA_IS_OAUTH_PROVIDER +-GOA_IS_OAUTH_PROVIDER_CLASS +-GOA_TYPE_OAUTH_PROVIDER +-goa_oauth_provider_get_type +-
+- +
+ GoaMail + GoaMail +diff --git a/doc/goa.types b/doc/goa.types +index 56ba3c4..d8d7325 100644 +--- a/doc/goa.types ++++ b/doc/goa.types +@@ -62,5 +62,4 @@ goa_printers_proxy_get_type + goa_printers_skeleton_get_type + + goa_provider_get_type +-goa_oauth_provider_get_type + goa_oauth2_provider_get_type +diff --git a/po/POTFILES.in b/po/POTFILES.in +index b65650c..8ee89ae 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -22,7 +22,6 @@ src/goabackend/goasmtpauth.c + src/goabackend/goatelepathyprovider.c + src/goabackend/goatodoistprovider.c + src/goabackend/goautils.c +-src/goabackend/goawebview.c + src/goabackend/goawindowsliveprovider.c + src/goaidentity/goaidentityservice.c + src/goaidentity/goakerberosidentity.c +diff --git a/src/goabackend/Makefile.am b/src/goabackend/Makefile.am +index c254594..7f15518 100644 +--- a/src/goabackend/Makefile.am ++++ b/src/goabackend/Makefile.am +@@ -19,7 +19,6 @@ AM_CPPFLAGS = \ + -DPACKAGE_LOCALSTATE_DIR=\""$(localstatedir)"\" \ + -DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \ + -DPACKAGE_LIB_DIR=\""$(libdir)"\" \ +- -DPACKAGE_WEB_EXTENSIONS_DIR=\""$(libdir)/goa-1.0/web-extensions"\" \ + -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT \ + $(WARN_CFLAGS) \ + $(NULL) +@@ -81,17 +80,13 @@ libgoa_backend_1_0_la_SOURCES = \ + goasouplogger.h goasouplogger.c \ + goamailclient.h goamailclient.c \ + goaexchangeprovider.h goaexchangeprovider.c \ +- goaoauthprovider.h goaoauthprovider.c \ + goaoauth2provider.h goaoauth2provider-priv.h \ +- goaoauth2provider-web-extension.h \ +- goaoauth2provider-web-view.h \ + goaoauth2provider.c \ + goagoogleprovider.h goagoogleprovider.c \ + goafacebookprovider.h goafacebookprovider.c \ + goaimapsmtpprovider.h goaimapsmtpprovider.c \ + goamediaserverprovider.h goamediaserverprovider.c \ + goaowncloudprovider.h goaowncloudprovider.c \ +- goaflickrprovider.h goaflickrprovider.c \ + goafoursquareprovider.h goafoursquareprovider.c \ + goawindowsliveprovider.h goawindowsliveprovider.c \ + goapocketprovider.h goapocketprovider.c \ +@@ -99,7 +94,6 @@ libgoa_backend_1_0_la_SOURCES = \ + goatodoistprovider.h goatodoistprovider.c \ + goaobjectskeletonutils.h goaobjectskeletonutils.c \ + goautils.h goautils.c \ +- goawebview.h goawebview.c \ + nautilus-floating-bar.h nautilus-floating-bar.c \ + $(top_builddir)/src/goaidentity/org.gnome.Identity.c \ + $(top_srcdir)/src/goaidentity/goaidentitymanagererror.c \ +@@ -119,7 +113,6 @@ libgoa_backend_1_0_la_SOURCES += \ + endif + + libgoa_backend_1_0_la_CFLAGS = \ +- $(WEBKIT_GTK_CFLAGS) \ + $(JSON_GLIB_CFLAGS) \ + $(GCR_CFLAGS) \ + $(GLIB_CFLAGS) \ +@@ -134,7 +127,6 @@ libgoa_backend_1_0_la_CFLAGS = \ + + libgoa_backend_1_0_la_LIBADD = \ + $(top_builddir)/src/goa/libgoa-1.0.la \ +- $(WEBKIT_GTK_LIBS) \ + $(JSON_GLIB_LIBS) \ + $(GCR_LIBS) \ + $(GLIB_LIBS) \ +@@ -154,39 +146,29 @@ libgoa_backend_1_0_la_LDFLAGS = \ + + # ---------------------------------------------------------------------------------------------------- + +-webextension_LTLIBRARIES = libgoawebextension.la ++libexec_PROGRAMS = goa-oauth2-handler + +-webextensiondir = $(libdir)/goa-1.0/web-extensions +- +-libgoawebextension_la_SOURCES = \ +- goawebextension.h goawebextension.c \ +- goawebextensionmain.c \ ++goa_oauth2_handler_SOURCES = \ ++ goaoauth2handler.c \ + $(NULL) + +-libgoawebextension_la_CFLAGS = \ +- $(REST_CFLAGS) \ +- $(WEBKIT_GTK_CFLAGS) \ ++goa_oauth2_handler_CFLAGS = \ ++ $(GLIB_CFLAGS) \ ++ $(LIBSOUP_CFLAGS) \ ++ $(SECRET_CFLAGS) \ ++ -DG_LOG_DOMAIN=\"goa-oauth2-handler\" \ + $(NULL) + +-libgoawebextension_la_LIBADD = \ +- libgoa-backend-1.0.la \ +- $(REST_LIBS) \ +- $(WEBKIT_GTK_LIBS) \ ++goa_oauth2_handler_LDADD = \ ++ $(GLIB_LIBS) \ ++ $(LIBSOUP_LIBS) \ ++ $(SECRET_LIBS) \ + $(NULL) + +-libgoawebextension_la_LDFLAGS = \ +- -avoid-version \ +- -module \ +- -no-undefined \ ++goa_oauth2_handler_LDFLAGS = \ ++ $(WARN_LDFLAGS) \ + $(NULL) + +-# Force installation order: libgoa-backend-1.0 must be installed first, othwerwise +-# libtool will incorrectly relink libgoawebextension.la under parallel make install. +-# Requires ugly automake syntax - see http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 +- +-installwebextensionLTLIBRARIES = install-webextensionLTLIBRARIES +-$(installwebextensionLTLIBRARIES): install-libLTLIBRARIES +- + # ---------------------------------------------------------------------------------------------------- + + BUILT_SOURCES = \ +diff --git a/src/goabackend/goafacebookprovider.c b/src/goabackend/goafacebookprovider.c +index c6033fb..c1d35d0 100644 +--- a/src/goabackend/goafacebookprovider.c ++++ b/src/goabackend/goafacebookprovider.c +@@ -243,31 +243,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, + + /* ---------------------------------------------------------------------------------------------------- */ + +-static gboolean +-is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) +-{ +- gboolean ret = FALSE; +- gchar *element_type = NULL; +- gchar *name = NULL; +- +- g_object_get (element, "type", &element_type, NULL); +- if (g_strcmp0 (element_type, "text") != 0) +- goto out; +- +- name = webkit_dom_html_input_element_get_name (element); +- if (g_strcmp0 (name, "email") != 0) +- goto out; +- +- ret = TRUE; +- +- out: +- g_free (element_type); +- g_free (name); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- + static gboolean + build_object (GoaProvider *provider, + GoaObjectSkeleton *object, +@@ -367,6 +342,5 @@ goa_facebook_provider_class_init (GoaFacebookProviderClass *klass) + oauth2_class->get_client_id = get_client_id; + oauth2_class->get_client_secret = get_client_secret; + oauth2_class->get_identity_sync = get_identity_sync; +- oauth2_class->is_identity_node = is_identity_node; + oauth2_class->add_account_key_values = add_account_key_values; + } +diff --git a/src/goabackend/goaflickrprovider.c b/src/goabackend/goaflickrprovider.c +deleted file mode 100644 +index 702ed1e..0000000 +--- a/src/goabackend/goaflickrprovider.c ++++ /dev/null +@@ -1,364 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +-/* +- * Copyright (C) 2011 Willem van Engen +- * 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 +-#include +- +-#include "goaprovider.h" +-#include "goaprovider-priv.h" +-#include "goaflickrprovider.h" +-#include "goaobjectskeletonutils.h" +-#include "goasouplogger.h" +- +-struct _GoaFlickrProvider +-{ +- GoaOAuthProvider parent_instance; +-}; +- +-G_DEFINE_TYPE_WITH_CODE (GoaFlickrProvider, goa_flickr_provider, GOA_TYPE_OAUTH_PROVIDER, +- goa_provider_ensure_extension_points_registered (); +- g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, +- g_define_type_id, +- GOA_FLICKR_NAME, +- 0)); +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static const gchar * +-get_provider_type (GoaProvider *provider) +-{ +- return GOA_FLICKR_NAME; +-} +- +-static gchar * +-get_provider_name (GoaProvider *provider, +- GoaObject *object) +-{ +- return g_strdup (_("Flickr")); +-} +- +-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_PHOTOS; +-} +- +-static const gchar * +-get_consumer_key (GoaOAuthProvider *oauth_provider) +-{ +- return GOA_FLICKR_CONSUMER_KEY; +-} +- +-static const gchar * +-get_consumer_secret (GoaOAuthProvider *oauth_provider) +-{ +- return GOA_FLICKR_CONSUMER_SECRET; +-} +- +-static const gchar * +-get_request_uri (GoaOAuthProvider *oauth_provider) +-{ +- return "https://www.flickr.com/services/oauth/request_token"; +-} +- +-static const gchar * +-get_authorization_uri (GoaOAuthProvider *oauth_provider) +-{ +- return "https://www.flickr.com/services/oauth/authorize"; +-} +- +-static const gchar * +-get_token_uri (GoaOAuthProvider *oauth_provider) +-{ +- return "https://www.flickr.com/services/oauth/access_token"; +-} +- +-static const gchar * +-get_callback_uri (GoaOAuthProvider *oauth_provider) +-{ +- /* Should match the URI specified in the Flickr App +- * Garden in order to detect when the user denied access via +- * the OAuth1 web page. +- */ +- return "https://www.gnome.org/"; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gchar * +-get_identity_sync (GoaOAuthProvider *oauth_provider, +- const gchar *access_token, +- const gchar *access_token_secret, +- gchar **out_presentation_identity, +- GCancellable *cancellable, +- GError **error) +-{ +- GError *identity_error = NULL; +- RestProxy *proxy = NULL; +- RestProxyCall *call = NULL; +- JsonParser *parser = NULL; +- JsonObject *json_object; +- SoupLogger *logger = NULL; +- gchar *ret = NULL; +- gchar *id = NULL; +- gchar *presentation_identity = NULL; +- +- /* TODO: cancellable */ +- +- proxy = oauth_proxy_new_with_token (goa_oauth_provider_get_consumer_key (oauth_provider), +- goa_oauth_provider_get_consumer_secret (oauth_provider), +- access_token, +- access_token_secret, +- "https://api.flickr.com/services/rest", +- FALSE); +- logger = goa_soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); +- rest_proxy_add_soup_feature (proxy, SOUP_SESSION_FEATURE (logger)); +- +- call = rest_proxy_new_call (proxy); +- rest_proxy_call_add_param (call, "method", "flickr.test.login"); +- rest_proxy_call_add_param (call, "format", "json"); +- rest_proxy_call_add_param (call, "nojsoncallback", "1"); +- rest_proxy_call_set_method (call, "GET"); +- +- if (!rest_proxy_call_sync (call, error)) +- goto out; +- if (rest_proxy_call_get_status_code (call) != 200) +- { +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- _("Expected status 200 when requesting your identity, instead got status %d (%s)"), +- rest_proxy_call_get_status_code (call), +- rest_proxy_call_get_status_message (call)); +- goto out; +- } +- +- parser = json_parser_new (); +- if (!json_parser_load_from_data (parser, +- rest_proxy_call_get_payload (call), +- rest_proxy_call_get_payload_length (call), +- &identity_error)) +- { +- g_warning ("json_parser_load_from_data() failed: %s (%s, %d)", +- identity_error->message, +- g_quark_to_string (identity_error->domain), +- identity_error->code); +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- _("Could not parse response")); +- goto out; +- } +- +- json_object = json_node_get_object (json_parser_get_root (parser)); +- if (!json_object_has_member (json_object, "user")) +- { +- g_warning ("Did not find user in JSON data"); +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- _("Could not parse response")); +- goto out; +- } +- +- json_object = json_object_get_object_member (json_object, "user"); +- if (!json_object_has_member (json_object, "id")) +- { +- g_warning ("Did not find user.id in JSON data"); +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- _("Could not parse response")); +- goto out; +- } +- if (!json_object_has_member (json_object, "username")) +- { +- g_warning ("Did not find user.username in JSON data"); +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- _("Could not parse response")); +- goto out; +- } +- +- id = g_strdup (json_object_get_string_member (json_object, "id")); +- +- json_object = json_object_get_object_member (json_object, "username"); +- if (!json_object_has_member (json_object, "_content")) +- { +- g_warning ("Did not find user.username._content in JSON data"); +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- _("Could not parse response")); +- goto out; +- } +- +- presentation_identity = g_strdup (json_object_get_string_member (json_object, "_content")); +- +- ret = id; +- id = NULL; +- if (out_presentation_identity != NULL) +- { +- *out_presentation_identity = presentation_identity; +- presentation_identity = NULL; +- } +- +- out: +- g_clear_object (&parser); +- g_clear_error (&identity_error); +- g_clear_object (&call); +- g_clear_object (&proxy); +- g_clear_object (&logger); +- g_free (id); +- g_free (presentation_identity); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gboolean +-is_identity_node (GoaOAuthProvider *oauth_provider, WebKitDOMHTMLInputElement *element) +-{ +- /* Flickr does not provide a way to query the string used by the +- * user to log in via the web interface. The user id and username +- * returned by flickr.test.login [1] are not what we are looking +- * for. +- * +- * [1] http://www.flickr.com/services/api/flickr.test.login.html +- */ +- return FALSE; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gchar * +-parse_request_token_error (GoaOAuthProvider *oauth_provider, RestProxyCall *call) +-{ +- const gchar *payload; +- gchar *msg = NULL; +- guint status; +- +- payload = rest_proxy_call_get_payload (call); +- status = rest_proxy_call_get_status_code (call); +- +- if (status == 401 && g_strcmp0 (payload, "oauth_problem=timestamp_refused") == 0) +- msg = g_strdup (_("Your system time is invalid. Check your date and time settings.")); +- +- return msg; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gboolean +-build_object (GoaProvider *provider, +- GoaObjectSkeleton *object, +- GKeyFile *key_file, +- const gchar *group, +- GDBusConnection *connection, +- gboolean just_added, +- GError **error) +-{ +- GoaAccount *account = NULL; +- gboolean photos_enabled; +- gboolean ret = FALSE; +- +- /* Chain up */ +- if (!GOA_PROVIDER_CLASS (goa_flickr_provider_parent_class)->build_object (provider, +- object, +- key_file, +- group, +- connection, +- just_added, +- error)) +- goto out; +- +- account = goa_object_get_account (GOA_OBJECT (object)); +- +- /* Photos */ +- photos_enabled = g_key_file_get_boolean (key_file, group, "PhotosEnabled", NULL); +- goa_object_skeleton_attach_photos (object, photos_enabled); +- +- if (just_added) +- { +- goa_account_set_photos_disabled (account, !photos_enabled); +- +- g_signal_connect (account, +- "notify::photos-disabled", +- G_CALLBACK (goa_util_account_notify_property_cb), +- (gpointer) "PhotosEnabled"); +- } +- +- ret = TRUE; +- +- out: +- g_clear_object (&account); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static void +-add_account_key_values (GoaOAuthProvider *oauth_provider, +- GVariantBuilder *builder) +-{ +- g_variant_builder_add (builder, "{ss}", "PhotosEnabled", "true"); +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static void +-goa_flickr_provider_init (GoaFlickrProvider *self) +-{ +-} +- +-static void +-goa_flickr_provider_class_init (GoaFlickrProviderClass *klass) +-{ +- GoaProviderClass *provider_class; +- GoaOAuthProviderClass *oauth_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->build_object = build_object; +- +- oauth_class = GOA_OAUTH_PROVIDER_CLASS (klass); +- oauth_class->get_identity_sync = get_identity_sync; +- oauth_class->is_identity_node = is_identity_node; +- oauth_class->get_consumer_key = get_consumer_key; +- oauth_class->get_consumer_secret = get_consumer_secret; +- oauth_class->get_request_uri = get_request_uri; +- oauth_class->get_authorization_uri = get_authorization_uri; +- oauth_class->get_token_uri = get_token_uri; +- oauth_class->get_callback_uri = get_callback_uri; +- oauth_class->parse_request_token_error = parse_request_token_error; +- oauth_class->add_account_key_values = add_account_key_values; +-} +diff --git a/src/goabackend/goaflickrprovider.h b/src/goabackend/goaflickrprovider.h +deleted file mode 100644 +index f08a8a6..0000000 +--- a/src/goabackend/goaflickrprovider.h ++++ /dev/null +@@ -1,37 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +-/* +- * Copyright © 2012 Willem van Engen +- * +- * 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 . +- */ +- +-#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION) +-#error "Only can be included directly." +-#endif +- +-#ifndef __GOA_FLICKR_PROVIDER_H__ +-#define __GOA_FLICKR_PROVIDER_H__ +- +-#include +- +-#include "goaoauthprovider.h" +- +-G_BEGIN_DECLS +- +-#define GOA_TYPE_FLICKR_PROVIDER (goa_flickr_provider_get_type ()) +-G_DECLARE_FINAL_TYPE (GoaFlickrProvider, goa_flickr_provider, GOA, FLICKR_PROVIDER, GoaOAuthProvider); +- +-G_END_DECLS +- +-#endif /* __GOA_FLICKR_PROVIDER_H__ */ +diff --git a/src/goabackend/goafoursquareprovider.c b/src/goabackend/goafoursquareprovider.c +index c1e4146..def21cb 100644 +--- a/src/goabackend/goafoursquareprovider.c ++++ b/src/goabackend/goafoursquareprovider.c +@@ -251,31 +251,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, + + /* ---------------------------------------------------------------------------------------------------- */ + +-static gboolean +-is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) +-{ +- gboolean ret = FALSE; +- gchar *element_type = NULL; +- gchar *name = NULL; +- +- g_object_get (element, "type", &element_type, NULL); +- if (g_strcmp0 (element_type, "email") != 0) +- goto out; +- +- name = webkit_dom_html_input_element_get_name (element); +- if (g_strcmp0 (name, "emailOrPhone") != 0) +- goto out; +- +- ret = TRUE; +- +- out: +- g_free (element_type); +- g_free (name); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- + static gboolean + build_object (GoaProvider *provider, + GoaObjectSkeleton *object, +@@ -366,6 +341,5 @@ goa_foursquare_provider_class_init (GoaFoursquareProviderClass *klass) + oauth2_class->get_client_secret = get_client_secret; + oauth2_class->get_use_mobile_browser = get_use_mobile_browser; + oauth2_class->get_identity_sync = get_identity_sync; +- oauth2_class->is_identity_node = is_identity_node; + oauth2_class->add_account_key_values = add_account_key_values; + } +diff --git a/src/goabackend/goagoogleprovider.c b/src/goabackend/goagoogleprovider.c +index 6e4ace2..6f951e5 100644 +--- a/src/goabackend/goagoogleprovider.c ++++ b/src/goabackend/goagoogleprovider.c +@@ -32,6 +32,7 @@ + struct _GoaGoogleProvider + { + GoaOAuth2Provider parent_instance; ++ gchar *redirect_uri; + }; + + G_DEFINE_TYPE_WITH_CODE (GoaGoogleProvider, goa_google_provider, GOA_TYPE_OAUTH2_PROVIDER, +@@ -81,19 +82,50 @@ get_provider_features (GoaProvider *provider) + static const gchar * + get_authorization_uri (GoaOAuth2Provider *oauth2_provider) + { +- return "https://accounts.google.com/o/oauth2/auth"; ++ return "https://accounts.google.com/o/oauth2/v2/auth"; + } + + static const gchar * + get_token_uri (GoaOAuth2Provider *oauth2_provider) + { +- return "https://accounts.google.com/o/oauth2/token"; ++ return "https://oauth2.googleapis.com/token"; + } + + static const gchar * + get_redirect_uri (GoaOAuth2Provider *oauth2_provider) + { +- return "http://localhost"; ++ G_LOCK_DEFINE_STATIC (redirect_uri); ++ GoaGoogleProvider *self = GOA_GOOGLE_PROVIDER (oauth2_provider); ++ ++ G_LOCK (redirect_uri); ++ ++ if (!self->redirect_uri) { ++ GPtrArray *array; ++ gchar **strv; ++ gchar *joinstr; ++ guint ii; ++ ++ strv = g_strsplit (GOA_GOOGLE_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 */ ++ self->redirect_uri = g_strconcat (joinstr, ":/oauth2redirect", NULL); ++ ++ g_ptr_array_free (array, TRUE); ++ g_strfreev (strv); ++ g_free (joinstr); ++ } ++ ++ G_UNLOCK (redirect_uri); ++ ++ return self->redirect_uri; + } + + static const gchar * +@@ -241,37 +273,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, + + /* ---------------------------------------------------------------------------------------------------- */ + +-static gboolean +-is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) +-{ +- gboolean ret = FALSE; +- gchar *element_type = NULL; +- gchar *id = NULL; +- gchar *name = NULL; +- +- g_object_get (element, "type", &element_type, NULL); +- if (g_strcmp0 (element_type, "email") != 0) +- goto out; +- +- id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (element)); +- if (g_strcmp0 (id, "identifierId") != 0) +- goto out; +- +- name = webkit_dom_html_input_element_get_name (element); +- if (g_strcmp0 (name, "identifier") != 0) +- goto out; +- +- ret = TRUE; +- +- out: +- g_free (element_type); +- g_free (id); +- g_free (name); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- + static gboolean + build_object (GoaProvider *provider, + GoaObjectSkeleton *object, +@@ -446,6 +447,16 @@ add_account_key_values (GoaOAuth2Provider *oauth2_provider, + + /* ---------------------------------------------------------------------------------------------------- */ + ++static void ++goa_google_finalize (GObject *object) ++{ ++ GoaGoogleProvider *self = GOA_GOOGLE_PROVIDER (object); ++ ++ g_free (self->redirect_uri); ++ ++ G_OBJECT_CLASS (goa_google_provider_parent_class)->finalize (object); ++} ++ + static void + goa_google_provider_init (GoaGoogleProvider *self) + { +@@ -456,6 +467,10 @@ goa_google_provider_class_init (GoaGoogleProviderClass *klass) + { + GoaProviderClass *provider_class; + GoaOAuth2ProviderClass *oauth2_class; ++ GObjectClass *object_class; ++ ++ object_class = G_OBJECT_CLASS (klass); ++ object_class->finalize = goa_google_finalize; + + provider_class = GOA_PROVIDER_CLASS (klass); + provider_class->get_provider_type = get_provider_type; +@@ -472,7 +487,6 @@ goa_google_provider_class_init (GoaGoogleProviderClass *klass) + oauth2_class->get_identity_sync = get_identity_sync; + oauth2_class->get_redirect_uri = get_redirect_uri; + oauth2_class->get_scope = get_scope; +- oauth2_class->is_identity_node = is_identity_node; + oauth2_class->get_token_uri = get_token_uri; + oauth2_class->add_account_key_values = add_account_key_values; + } +diff --git a/src/goabackend/goaoauth2handler.c b/src/goabackend/goaoauth2handler.c +new file mode 100644 +index 0000000..c5a86cb +--- /dev/null ++++ b/src/goabackend/goaoauth2handler.c +@@ -0,0 +1,173 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* ++ * Copyright © 2023 GNOME Foundation Inc. ++ * Contributor: Andy Holmes ++ * ++ * 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 ++#include ++ ++ ++static const SecretSchema oauth2_schema = ++{ ++ .name = "org.gnome.OnlineAccounts.OAuth2", ++ .flags = SECRET_SCHEMA_NONE, ++ .attributes = { ++ { ++ .name = "goa-oauth2-client", ++ .type = SECRET_SCHEMA_ATTRIBUTE_STRING, ++ }, ++ { ++ .name = "goa-oauth2-provider", ++ .type = SECRET_SCHEMA_ATTRIBUTE_STRING, ++ }, ++ { "NULL", 0 } ++ } ++}; ++ ++static struct ++{ ++ const char *client_id; ++ const char *provider; ++} ++oauth2_providers[] = ++{ ++#ifdef GOA_GOOGLE_ENABLED ++ { ++ .client_id = GOA_GOOGLE_CLIENT_ID, ++ .provider = GOA_GOOGLE_NAME, ++ }, ++#endif ++#ifdef GOA_WINDOWS_LIVE_ENABLED ++ { ++ .client_id = GOA_WINDOWS_LIVE_CLIENT_ID, ++ .provider = GOA_WINDOWS_LIVE_NAME, ++ }, ++#endif ++ { NULL, NULL }, ++}; ++ ++static gboolean ++get_oauth2_provider (const char *needle, ++ const char **client_out, ++ const char **provider_out) ++{ ++ g_return_val_if_fail (needle != NULL, FALSE); ++ ++ for (unsigned int i = 0; oauth2_providers[i].client_id != NULL; i++) ++ { ++ if (g_str_equal (needle, oauth2_providers[i].client_id)) ++ { ++ if (client_out) ++ *client_out = oauth2_providers[i].client_id; ++ ++ if (provider_out) ++ *provider_out = oauth2_providers[i].provider; ++ ++ return TRUE; ++ } ++ } ++ ++ return FALSE; ++} ++ ++int ++main (int argc, ++ char **argv) ++{ ++ SoupURI *uri = NULL; ++ const char *scheme = NULL; ++ const char *path = NULL; ++ const char *client_id = NULL; ++ const char *provider_type = NULL; ++ GError *error = NULL; ++ ++ if (argc < 2) ++ { ++ g_printerr ("%s: Missing URI\n", argv[0]); ++ return EXIT_FAILURE; ++ } ++ ++ uri = soup_uri_new (argv[1]); ++ if (uri == NULL) ++ { ++ g_printerr ("%s: Invalid URI: %s\n", argv[0], argv[1]); ++ return EXIT_FAILURE; ++ } ++ ++ /* Google apps may use a reverse-DNS form of the client ID as the URI scheme ++ * See: https://developers.google.com/identity/protocols/oauth2/native-app ++ */ ++ scheme = soup_uri_get_scheme (uri); ++ if (scheme != NULL) ++ { ++ g_auto (GStrv) strv = g_strsplit (scheme, ".", -1); ++ g_autoptr (GString) tmp = g_string_new (""); ++ ++ for (unsigned int i = 0; strv[i] != NULL; i++) ++ { ++ if (i > 0) ++ g_string_prepend_c (tmp, '.'); ++ g_string_prepend (tmp, strv[i]); ++ } ++ ++ get_oauth2_provider (tmp->str, &client_id, &provider_type); ++ } ++ ++ /* Windows Live uses goa-oauth2:// with the client ID as the first path segment ++ */ ++ if (client_id == NULL) ++ { ++ path = soup_uri_get_path (uri); ++ if (path != NULL && *path != '\0') ++ { ++ g_auto (GStrv) strv = NULL; ++ ++ strv = g_strsplit (*path == '/' ? path +1 : path, "/", 1); ++ get_oauth2_provider (strv[0], &client_id, &provider_type); ++ } ++ } ++ ++ if (client_id == NULL) ++ { ++ g_printerr ("%s: Unknown provider\n", argv[0]); ++ soup_uri_free (uri); ++ return EXIT_FAILURE; ++ } ++ ++ if (!secret_password_store_sync (&oauth2_schema, ++ SECRET_COLLECTION_SESSION, ++ "GNOME Online Accounts OAuth2 URI", ++ argv[1], /* Secret */ ++ NULL, ++ &error, ++ "goa-oauth2-client", client_id, ++ "goa-oauth2-provider", provider_type, ++ NULL)) ++ { ++ if (error != NULL) ++ g_printerr ("%s: Failed to store OAuth2 URI: %s\n", argv[0], error->message); ++ ++ soup_uri_free (uri); ++ g_clear_error (&error); ++ return EXIT_FAILURE; ++ } ++ ++ return EXIT_SUCCESS; ++} +diff --git a/src/goabackend/goaoauth2provider-priv.h b/src/goabackend/goaoauth2provider-priv.h +index de1b808..4b00a24 100644 +--- a/src/goabackend/goaoauth2provider-priv.h ++++ b/src/goabackend/goaoauth2provider-priv.h +@@ -26,8 +26,6 @@ + #include + #include + #include +-#include +-#include + + G_BEGIN_DECLS + +@@ -51,11 +49,7 @@ G_BEGIN_DECLS + * @build_authorization_uri: Virtual function for goa_oauth2_provider_build_authorization_uri(). + * @get_use_mobile_browser: Virtual function for goa_oauth2_provider_get_use_mobile_browser(). + * @add_account_key_values: Virtual function for goa_oauth2_provider_add_account_key_values(). +- * @decide_navigation_policy: Virtual function for goa_oauth2_provider_decide_navigation_policy(). + * @process_redirect_url: Virtual function for goa_oauth2_provider_process_redirect_url(). +- * @is_deny_node: Virtual function for goa_oauth2_provider_is_deny_node(). +- * @is_identity_node: Virtual function for goa_oauth2_provider_is_identity_node(). +- * @is_password_node: Virtual function for goa_oauth2_provider_is_password_node(). + * + * Class structure for #GoaOAuth2Provider. + */ +@@ -86,18 +80,7 @@ struct _GoaOAuth2ProviderClass + void (*add_account_key_values) (GoaOAuth2Provider *provider, + GVariantBuilder *builder); + +- /* pure virtual */ +- gboolean (*is_identity_node) (GoaOAuth2Provider *provider, +- WebKitDOMHTMLInputElement *element); +- + /* virtual but with default implementation */ +- gboolean (*is_deny_node) (GoaOAuth2Provider *provider, +- WebKitDOMNode *node); +- gboolean (*is_password_node) (GoaOAuth2Provider *provider, +- WebKitDOMHTMLInputElement *element); +- gboolean (*decide_navigation_policy) (GoaOAuth2Provider *provider, +- WebKitWebView *web_view, +- WebKitNavigationPolicyDecision *decision); + gboolean (*process_redirect_url) (GoaOAuth2Provider *provider, + const gchar *redirect_url, + gchar **access_token, +diff --git a/src/goabackend/goaoauth2provider-web-extension.h b/src/goabackend/goaoauth2provider-web-extension.h +deleted file mode 100644 +index baac005..0000000 +--- a/src/goabackend/goaoauth2provider-web-extension.h ++++ /dev/null +@@ -1,40 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +-/* +- * Copyright © 2016 – 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 . +- */ +- +-#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION) +-#error "Only can be included directly." +-#endif +- +-#ifndef __GOA_OAUTH2_PROVIDER_WEB_EXTENSION_H__ +-#define __GOA_OAUTH2_PROVIDER_WEB_EXTENSION_H__ +- +-#include +-#include +- +-G_BEGIN_DECLS +- +-gboolean goa_oauth2_provider_is_deny_node (GoaOAuth2Provider *provider, +- WebKitDOMNode *node); +-gboolean goa_oauth2_provider_is_identity_node (GoaOAuth2Provider *provider, +- WebKitDOMHTMLInputElement *element); +-gboolean goa_oauth2_provider_is_password_node (GoaOAuth2Provider *provider, +- WebKitDOMHTMLInputElement *element); +- +-G_END_DECLS +- +-#endif /* __GOA_OAUTH2_PROVIDER_WEB_EXTENSION_H__ */ +diff --git a/src/goabackend/goaoauth2provider-web-view.h b/src/goabackend/goaoauth2provider-web-view.h +deleted file mode 100644 +index f2dae5e..0000000 +--- a/src/goabackend/goaoauth2provider-web-view.h ++++ /dev/null +@@ -1,37 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +-/* +- * Copyright © 2016 – 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 . +- */ +- +-#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION) +-#error "Only can be included directly." +-#endif +- +-#ifndef __GOA_OAUTH2_PROVIDER_WEB_VIEW_H__ +-#define __GOA_OAUTH2_PROVIDER_WEB_VIEW_H__ +- +-#include +-#include +- +-G_BEGIN_DECLS +- +-gboolean goa_oauth2_provider_decide_navigation_policy (GoaOAuth2Provider *provider, +- WebKitWebView *web_view, +- WebKitNavigationPolicyDecision *decision); +- +-G_END_DECLS +- +-#endif /* __GOA_OAUTH2_PROVIDER_WEB_VIEW_H__ */ +diff --git a/src/goabackend/goaoauth2provider.c b/src/goabackend/goaoauth2provider.c +index 9092605..1ceacb5 100644 +--- a/src/goabackend/goaoauth2provider.c ++++ b/src/goabackend/goaoauth2provider.c +@@ -22,16 +22,13 @@ + + #include + #include ++#include + #include +-#include + + #include "goaprovider.h" + #include "goautils.h" +-#include "goawebview.h" + #include "goaoauth2provider.h" + #include "goaoauth2provider-priv.h" +-#include "goaoauth2provider-web-extension.h" +-#include "goaoauth2provider-web-view.h" + #include "goarestproxy.h" + + /** +@@ -81,6 +78,8 @@ struct _GoaOAuth2ProviderPrivate + gchar *identity; + gchar *presentation_identity; + gchar *password; ++ gchar *request_uri; ++ SecretCollection *session; + }; + + G_LOCK_DEFINE_STATIC (provider_lock); +@@ -134,70 +133,6 @@ goa_oauth2_provider_get_use_mobile_browser (GoaOAuth2Provider *self) + + /* ---------------------------------------------------------------------------------------------------- */ + +-static gboolean +-goa_oauth2_provider_is_deny_node_default (GoaOAuth2Provider *self, WebKitDOMNode *node) +-{ +- return FALSE; +-} +- +-/** +- * goa_oauth2_provider_is_deny_node: +- * @self: A #GoaOAuth2Provider. +- * @node: A WebKitDOMNode. +- * +- * Checks whether @node is the HTML UI element that the user can use +- * to deny permission to access his account. Usually they are either a +- * WebKitDOMHTMLButtonElement or a WebKitDOMHTMLInputElement. +- * +- * Please note that providers may have multiple such elements in their +- * UI and this method should catch all of them. +- * +- * This is a virtual method where the default implementation returns +- * %FALSE. +- * +- * Returns: %TRUE if the @node can be used to deny permission. +- */ +-gboolean +-goa_oauth2_provider_is_deny_node (GoaOAuth2Provider *self, WebKitDOMNode *node) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH2_PROVIDER (self), FALSE); +- return GOA_OAUTH2_PROVIDER_GET_CLASS (self)->is_deny_node (self, node); +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gboolean +-goa_oauth2_provider_is_password_node_default (GoaOAuth2Provider *self, WebKitDOMHTMLInputElement *element) +-{ +- return FALSE; +-} +- +-/** +- * goa_oauth2_provider_is_password_node: +- * @self: A #GoaOAuth2Provider. +- * @element: A WebKitDOMHTMLInputElement +- * +- * Checks whether @element is the HTML UI element that the user can +- * use to enter her password. This can be used to offer a +- * #GoaPasswordBased interface by saving the user's +- * password. Providers usually frown upon doing this, so this is not +- * recommended. +- * +- * This is a virtual method where the default implementation returns +- * %FALSE. +- * +- * Returns: %TRUE if @element can be used to enter the password. +- */ +-gboolean +-goa_oauth2_provider_is_password_node (GoaOAuth2Provider *self, WebKitDOMHTMLInputElement *element) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH2_PROVIDER (self), FALSE); +- g_return_val_if_fail (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element), FALSE); +- return GOA_OAUTH2_PROVIDER_GET_CLASS (self)->is_password_node (self, element); +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- + static void + goa_oauth2_provider_add_account_key_values_default (GoaOAuth2Provider *self, + GVariantBuilder *builder) +@@ -287,45 +222,6 @@ goa_oauth2_provider_build_authorization_uri (GoaOAuth2Provider *self, + + /* ---------------------------------------------------------------------------------------------------- */ + +-static gboolean +-goa_oauth2_provider_decide_navigation_policy_default (GoaOAuth2Provider *self, +- WebKitWebView *web_view, +- WebKitNavigationPolicyDecision *decision) +-{ +- return FALSE; +-} +- +-/* +- * goa_oauth2_provider_decide_navigation_policy_default: +- * @self: A #GoaOAuth2Provider. +- * @decision: A #WebKitNavigationPolicyDecision +- * +- * Certain OAuth2-like, but not exactly OAuth2, +- * providers may not send us to the redirect URI, as expected. They +- * might need some special handling for that. This is a provider +- * specific hook to accommodate them. +- * +- * This is a virtual method where the default implementation returns +- * %FALSE. +- * +- * Returns: %TRUE if @provider decided what to do with @decision, +- * %FALSE otherwise. +- */ +-gboolean +-goa_oauth2_provider_decide_navigation_policy (GoaOAuth2Provider *self, +- WebKitWebView *web_view, +- WebKitNavigationPolicyDecision *decision) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH2_PROVIDER (self), FALSE); +- g_return_val_if_fail (WEBKIT_IS_WEB_VIEW (web_view), FALSE); +- g_return_val_if_fail (WEBKIT_IS_NAVIGATION_POLICY_DECISION (decision), FALSE); +- +- return GOA_OAUTH2_PROVIDER_GET_CLASS (self)->decide_navigation_policy (self, web_view, decision); +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- + /** + * goa_oauth2_provider_process_redirect_url: + * @self: A #GoaOAuth2Provider. +@@ -551,26 +447,6 @@ goa_oauth2_provider_get_identity_sync (GoaOAuth2Provider *self, + error); + } + +-/** +- * goa_oauth2_provider_is_identity_node: +- * @self: A #GoaOAuth2Provider. +- * @element: A WebKitDOMHTMLInputElement. +- * +- * Checks whether @element is the HTML UI element that the user can +- * use to identify herself at the provider. +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: %TRUE if the @element can be used to deny permission. +- */ +-gboolean +-goa_oauth2_provider_is_identity_node (GoaOAuth2Provider *self, WebKitDOMHTMLInputElement *element) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH2_PROVIDER (self), FALSE); +- return GOA_OAUTH2_PROVIDER_GET_CLASS (self)->is_identity_node (self, element); +-} +- + /* ---------------------------------------------------------------------------------------------------- */ + + static gchar * +@@ -730,72 +606,42 @@ get_tokens_sync (GoaOAuth2Provider *self, + + /* ---------------------------------------------------------------------------------------------------- */ + +-static void +-on_web_view_deny_click (GoaWebView *web_view, gpointer user_data) +-{ +- GoaOAuth2Provider *self = GOA_OAUTH2_PROVIDER (user_data); +- GoaOAuth2ProviderPrivate *priv; +- +- priv = goa_oauth2_provider_get_instance_private (self); +- gtk_dialog_response (priv->dialog, GTK_RESPONSE_CANCEL); +-} +- +-static void +-on_web_view_password_submit (GoaWebView *web_view, const gchar *password, gpointer user_data) +-{ +- GoaOAuth2Provider *self = GOA_OAUTH2_PROVIDER (user_data); +- GoaOAuth2ProviderPrivate *priv; +- +- priv = goa_oauth2_provider_get_instance_private (self); +- +- g_free (priv->password); +- priv->password = g_strdup (password); +-} +- + static gboolean +-on_web_view_decide_policy (WebKitWebView *web_view, +- WebKitPolicyDecision *decision, +- WebKitPolicyDecisionType decision_type, +- gpointer user_data) ++parse_requested_uri (GoaOAuth2Provider *self, ++ const char *requested_uri) + { +- GoaOAuth2Provider *self = GOA_OAUTH2_PROVIDER (user_data); +- GoaOAuth2ProviderPrivate *priv; ++ GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); + GHashTable *key_value_pairs; +- WebKitNavigationAction *action; +- WebKitURIRequest *request; + SoupURI *uri; + const gchar *fragment; + const gchar *oauth2_error; + const gchar *query; + const gchar *redirect_uri; +- const gchar *requested_uri; +- gint response_id = GTK_RESPONSE_NONE; +- +- priv = goa_oauth2_provider_get_instance_private (self); +- +- if (decision_type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION) +- goto default_behaviour; +- +- if (goa_oauth2_provider_decide_navigation_policy (self, +- web_view, +- WEBKIT_NAVIGATION_POLICY_DECISION (decision))) +- { +- response_id = 0; +- goto ignore_request; +- } + + /* TODO: use oauth2_proxy_extract_access_token() */ ++ g_assert (priv->error == NULL); + +- action = webkit_navigation_policy_decision_get_navigation_action (WEBKIT_NAVIGATION_POLICY_DECISION (decision)); +- request = webkit_navigation_action_get_request (action); +- requested_uri = webkit_uri_request_get_uri (request); + redirect_uri = goa_oauth2_provider_get_redirect_uri (self); + if (!g_str_has_prefix (requested_uri, redirect_uri)) +- goto default_behaviour; ++ { ++ g_set_error (&priv->error, ++ GOA_ERROR, ++ GOA_ERROR_FAILED, ++ "Invalid URI: %s", ++ requested_uri); ++ return FALSE; ++ } + + uri = soup_uri_new (requested_uri); +- fragment = soup_uri_get_fragment (uri); +- query = soup_uri_get_query (uri); ++ if (uri == NULL) ++ { ++ g_set_error (&priv->error, ++ GOA_ERROR, ++ GOA_ERROR_FAILED, ++ "Invalid URI: %s", ++ requested_uri); ++ return FALSE; ++ } + + /* Three cases: + * 1) we can either have the backend handle the URI for us, or +@@ -806,23 +652,23 @@ on_web_view_decide_policy (WebKitWebView *web_view, + */ + if (GOA_OAUTH2_PROVIDER_GET_CLASS (self)->process_redirect_url) + { +- gchar *url; ++ g_autofree char *url = NULL; + + url = soup_uri_to_string (uri, FALSE); ++ soup_uri_free (uri); + if (!goa_oauth2_provider_process_redirect_url (self, url, &priv->access_token, &priv->error)) + { + g_prefix_error (&priv->error, _("Authorization response: ")); + priv->error->domain = GOA_ERROR; + priv->error->code = GOA_ERROR_NOT_AUTHORIZED; +- response_id = GTK_RESPONSE_CLOSE; ++ ++ return FALSE; + } +- else +- response_id = GTK_RESPONSE_OK; + +- g_free (url); +- goto ignore_request; ++ return TRUE; + } + ++ fragment = soup_uri_get_fragment (uri); + if (fragment != NULL) + { + /* fragment is encoded into a key/value pairs for the token and +@@ -846,57 +692,175 @@ on_web_view_decide_policy (WebKitWebView *web_view, + priv->access_token_expires_in = atoi (expires_in_str); + + priv->refresh_token = g_strdup (g_hash_table_lookup (key_value_pairs, "refresh_token")); +- +- response_id = GTK_RESPONSE_OK; + } + g_hash_table_unref (key_value_pairs); +- } + +- if (priv->access_token != NULL) +- goto ignore_request; ++ if (priv->access_token != NULL) ++ { ++ soup_uri_free (uri); ++ return TRUE; ++ } ++ } + ++ query = soup_uri_get_query (uri); + if (query != NULL) + { + key_value_pairs = soup_form_decode (query); + + priv->authorization_code = g_strdup (g_hash_table_lookup (key_value_pairs, "code")); +- if (priv->authorization_code != NULL) +- response_id = GTK_RESPONSE_OK; +- + g_hash_table_unref (key_value_pairs); ++ if (priv->authorization_code != NULL) ++ { ++ soup_uri_free (uri); ++ return TRUE; ++ } + } + +- if (priv->authorization_code != NULL) +- goto ignore_request; +- + /* In case we don't find the access_token or auth code, then look + * for the error in the query part of the URI. + */ + key_value_pairs = soup_form_decode (query); + oauth2_error = (const gchar *) g_hash_table_lookup (key_value_pairs, "error"); + if (g_strcmp0 (oauth2_error, GOA_OAUTH2_ACCESS_DENIED) == 0) +- response_id = GTK_RESPONSE_CANCEL; +- else + { + g_set_error (&priv->error, + GOA_ERROR, + GOA_ERROR_NOT_AUTHORIZED, + _("Authorization response: %s"), + oauth2_error); +- response_id = GTK_RESPONSE_CLOSE; ++ } ++ else ++ { ++ g_set_error_literal (&priv->error, ++ GOA_ERROR, ++ GOA_ERROR_FAILED, ++ _("Failed to authenticate")); + } + g_hash_table_unref (key_value_pairs); +- goto ignore_request; ++ soup_uri_free (uri); ++ return FALSE; ++} + +- ignore_request: +- g_assert (response_id != GTK_RESPONSE_NONE); +- if (response_id < 0) +- gtk_dialog_response (priv->dialog, response_id); +- webkit_policy_decision_ignore (decision); +- return TRUE; ++/* ---------------------------------------------------------------------------------------------------- */ + +- default_behaviour: +- return FALSE; ++static const SecretSchema oauth2_schema = ++{ ++ .name = "org.gnome.OnlineAccounts.OAuth2", ++ .flags = SECRET_SCHEMA_NONE, ++ .attributes = { ++ { ++ .name = "goa-oauth2-client", ++ .type = SECRET_SCHEMA_ATTRIBUTE_STRING, ++ }, ++ { ++ .name = "goa-oauth2-provider", ++ .type = SECRET_SCHEMA_ATTRIBUTE_STRING, ++ }, ++ { "NULL", 0 } ++ } ++}; ++ ++static void ++on_secrets_changed (SecretCollection *collection, ++ GParamSpec *pspec, ++ GoaOAuth2Provider *self) ++{ ++ GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); ++ const char *client_id = NULL; ++ const char *provider_type = NULL; ++ g_autofree char *requested_uri = NULL; ++ GtkResponseType response_id = GTK_RESPONSE_NONE; ++ ++ client_id = goa_oauth2_provider_get_client_id (self); ++ provider_type = goa_provider_get_provider_type (GOA_PROVIDER (self)); ++ requested_uri = secret_password_lookup_sync (&oauth2_schema, NULL, NULL, ++ "goa-oauth2-client", client_id, ++ "goa-oauth2-provider", provider_type, ++ NULL); ++ ++ if (requested_uri != NULL) ++ { ++ if (parse_requested_uri (self, requested_uri)) ++ response_id = GTK_RESPONSE_OK; ++ else ++ response_id = GTK_RESPONSE_CANCEL; ++ } ++ ++ if (response_id != GTK_RESPONSE_NONE) ++ { ++ g_signal_handlers_disconnect_by_func (collection, on_secrets_changed, self); ++ gtk_dialog_response (priv->dialog, response_id); ++ } ++} ++ ++static void ++secret_service_get_cb (GObject *object, ++ GAsyncResult *result, ++ GoaOAuth2Provider *self) ++{ ++ GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); ++ SecretService *service = NULL; ++ GList *collections = NULL; ++ ++ service = secret_service_get_finish (result, &priv->error); ++ if (service == NULL) ++ goto out; ++ ++ collections = secret_service_get_collections (service); ++ for (const GList *iter = collections; iter != NULL; iter = iter->next) ++ { ++ g_autofree char *label = secret_collection_get_label (iter->data); ++ ++ /* The session collection is an empty string (?) */ ++ if (g_strcmp0 (label, "") == 0) ++ { ++ const char *client_id = NULL; ++ const char *provider_type = NULL; ++ ++ /* Ensure there's no dangling entry */ ++ client_id = goa_oauth2_provider_get_client_id (self); ++ provider_type = goa_provider_get_provider_type (GOA_PROVIDER (self)); ++ secret_password_clear_sync (&oauth2_schema, NULL, NULL, ++ "goa-oauth2-client", client_id, ++ "goa-oauth2-provider", provider_type, ++ NULL); ++ ++ /* Watch the session collection for the requested URI */ ++ priv->session = g_object_ref (iter->data); ++ g_signal_connect_object (priv->session, ++ "notify::items", ++ G_CALLBACK (on_secrets_changed), ++ self, ++ 0); ++ goto out; ++ } ++ } ++ ++ if (priv->session == NULL && priv->error == NULL) ++ { ++ g_set_error (&priv->error, ++ GOA_ERROR, ++ GOA_ERROR_FAILED, ++ "Failed to connect to session keyring"); ++ goto out; ++ } ++ ++out: ++ g_clear_object (&service); ++ g_list_free_full (collections, g_object_unref); ++ g_main_loop_quit (priv->loop); ++} ++ ++static void ++on_continue_in_browser (GtkButton *button, ++ GoaOAuth2Provider *self) ++{ ++ GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); ++ ++ if (!g_app_info_launch_default_for_uri (priv->request_uri, NULL, &priv->error)) ++ gtk_dialog_response (priv->dialog, GTK_RESPONSE_CANCEL); ++ else ++ gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE); + } + + static gboolean +@@ -906,12 +870,13 @@ get_tokens_and_identity (GoaOAuth2Provider *self, + GtkDialog *dialog, + GtkBox *vbox) + { +- GoaOAuth2ProviderPrivate *priv; ++ GoaOAuth2ProviderPrivate *priv = goa_oauth2_provider_get_instance_private (self); + gboolean ret = FALSE; +- gchar *url; +- GtkWidget *embed; ++ int response_id = GTK_RESPONSE_NONE; + GtkWidget *grid; +- GtkWidget *web_view; ++ GtkWidget *image; ++ GtkWidget *label; ++ GtkWidget *button; + const gchar *scope; + gchar *escaped_redirect_uri = NULL; + gchar *escaped_client_id = NULL; +@@ -923,7 +888,6 @@ get_tokens_and_identity (GoaOAuth2Provider *self, + g_return_val_if_fail (GTK_IS_DIALOG (dialog), FALSE); + g_return_val_if_fail (GTK_IS_BOX (vbox), FALSE); + +- priv = goa_oauth2_provider_get_instance_private (self); + g_return_val_if_fail (priv->error == NULL, FALSE); + + /* TODO: check with NM whether we're online, if not - return error */ +@@ -937,6 +901,8 @@ get_tokens_and_identity (GoaOAuth2Provider *self, + g_clear_pointer (&priv->authorization_code, g_free); + g_clear_pointer (&priv->access_token, g_free); + g_clear_pointer (&priv->refresh_token, g_free); ++ g_clear_pointer (&priv->request_uri, g_free); ++ g_clear_object (&priv->session); + + /* TODO: use oauth2_proxy_build_login_url_full() */ + escaped_redirect_uri = g_uri_escape_string (goa_oauth2_provider_get_redirect_uri (self), NULL, TRUE); +@@ -946,40 +912,71 @@ get_tokens_and_identity (GoaOAuth2Provider *self, + escaped_scope = g_uri_escape_string (goa_oauth2_provider_get_scope (self), NULL, TRUE); + else + escaped_scope = NULL; +- url = goa_oauth2_provider_build_authorization_uri (self, +- goa_oauth2_provider_get_authorization_uri (self), +- escaped_redirect_uri, +- escaped_client_id, +- escaped_scope); ++ priv->request_uri = goa_oauth2_provider_build_authorization_uri (self, ++ goa_oauth2_provider_get_authorization_uri (self), ++ escaped_redirect_uri, ++ escaped_client_id, ++ escaped_scope); + + goa_utils_set_dialog_title (GOA_PROVIDER (self), dialog, add_account); + +- grid = gtk_grid_new (); +- gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL); +- gtk_grid_set_row_spacing (GTK_GRID (grid), 12); ++ grid = g_object_new (GTK_TYPE_BOX, ++ "orientation", GTK_ORIENTATION_VERTICAL, ++ "spacing", 18, ++ "halign", GTK_ALIGN_CENTER, ++ "hexpand", TRUE, ++ "valign", GTK_ALIGN_CENTER, ++ "vexpand", TRUE, ++ "margin", 12, ++ NULL); + gtk_container_add (GTK_CONTAINER (vbox), grid); + +- web_view = goa_web_view_new (GOA_PROVIDER (self), existing_identity); +- gtk_widget_set_hexpand (web_view, TRUE); +- gtk_widget_set_vexpand (web_view, TRUE); +- embed = goa_web_view_get_view (GOA_WEB_VIEW (web_view)); +- +- if (goa_oauth2_provider_get_use_mobile_browser (self)) +- goa_web_view_fake_mobile (GOA_WEB_VIEW (web_view)); +- +- webkit_web_view_load_uri (WEBKIT_WEB_VIEW (embed), url); +- g_signal_connect (embed, +- "decide-policy", +- G_CALLBACK (on_web_view_decide_policy), +- self); +- g_signal_connect (web_view, "deny-click", G_CALLBACK (on_web_view_deny_click), self); +- g_signal_connect (web_view, "password-submit", G_CALLBACK (on_web_view_password_submit), self); ++ image = g_object_new (GTK_TYPE_IMAGE, ++ "icon-name", "web-browser-symbolic", ++ "pixel-size", 128, ++ NULL); ++ gtk_style_context_add_class (gtk_widget_get_style_context (image), "dim-label"); ++ gtk_container_add (GTK_CONTAINER (grid), image); ++ ++ label = gtk_label_new (_("Sign in with your browser to setup an account.")); ++ gtk_style_context_add_class (gtk_widget_get_style_context (label), "heading"); ++ gtk_container_add (GTK_CONTAINER (grid), label); ++ ++ button = gtk_button_new_with_label (_("Continue")); ++ gtk_widget_set_halign (button, GTK_ALIGN_CENTER); ++ gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action"); ++ g_signal_connect_object (button, ++ "clicked", ++ G_CALLBACK (on_continue_in_browser), ++ self, 0); ++ gtk_container_add (GTK_CONTAINER (grid), button); ++ gtk_dialog_add_button (priv->dialog, _("_Cancel"), GTK_RESPONSE_CANCEL); + +- gtk_container_add (GTK_CONTAINER (grid), web_view); ++ gtk_widget_show_all (GTK_WIDGET (vbox)); + gtk_window_set_default_size (GTK_WINDOW (dialog), -1, -1); + +- gtk_widget_show_all (GTK_WIDGET (vbox)); +- gtk_dialog_run (GTK_DIALOG (dialog)); ++ /* Watch the session secret collection for the OAuth2 URI */ ++ secret_service_get (SECRET_SERVICE_LOAD_COLLECTIONS | SECRET_SERVICE_OPEN_SESSION, ++ NULL, ++ (GAsyncReadyCallback) secret_service_get_cb, ++ self); ++ g_main_loop_run (priv->loop); ++ if (priv->error != NULL) ++ goto out; ++ ++ /* Inform the user authentication should be completed in the browser */ ++ response_id = gtk_dialog_run (GTK_DIALOG (dialog)); ++ if (response_id != GTK_RESPONSE_OK) ++ { ++ if (priv->error == NULL) ++ { ++ g_set_error (&priv->error, ++ GOA_ERROR, ++ GOA_ERROR_DIALOG_DISMISSED, ++ _("Dialog was dismissed")); ++ } ++ goto out; ++ } + + /* We can have either the auth code, with which we'll obtain the token, or + * the token directly if we are using a client side flow, since we don't +@@ -1038,12 +1035,14 @@ get_tokens_and_identity (GoaOAuth2Provider *self, + } + + ret = TRUE; ++ gtk_dialog_response (dialog, GTK_RESPONSE_OK); + + out: +- g_free (url); + g_free (escaped_redirect_uri); + g_free (escaped_client_id); + g_free (escaped_scope); ++ g_clear_pointer (&priv->request_uri, g_free); ++ g_clear_object (&priv->session); + return ret; + } + +@@ -1131,6 +1130,7 @@ goa_oauth2_provider_add_account (GoaProvider *provider, + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + priv = goa_oauth2_provider_get_instance_private (self); ++ priv->loop = g_main_loop_new (NULL, FALSE); + + if (!get_tokens_and_identity (self, TRUE, NULL, dialog, vbox)) + goto out; +@@ -1165,7 +1165,6 @@ goa_oauth2_provider_add_account (GoaProvider *provider, + NULL, /* GCancellable* */ + (GAsyncReadyCallback) add_account_cb, + self); +- priv->loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (priv->loop); + if (priv->error != NULL) + goto out; +@@ -1215,6 +1214,7 @@ goa_oauth2_provider_refresh_account (GoaProvider *provider, + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + priv = goa_oauth2_provider_get_instance_private (self); ++ priv->loop = g_main_loop_new (NULL, FALSE); + + dialog = gtk_dialog_new_with_buttons (NULL, + parent, +@@ -1625,6 +1625,8 @@ goa_oauth2_provider_finalize (GObject *object) + g_free (priv->authorization_code); + g_free (priv->access_token); + g_free (priv->refresh_token); ++ g_clear_pointer (&priv->request_uri, g_free); ++ g_clear_object (&priv->session); + + G_OBJECT_CLASS (goa_oauth2_provider_parent_class)->finalize (object); + } +@@ -1650,12 +1652,9 @@ goa_oauth2_provider_class_init (GoaOAuth2ProviderClass *klass) + provider_class->ensure_credentials_sync = goa_oauth2_provider_ensure_credentials_sync; + + klass->build_authorization_uri = goa_oauth2_provider_build_authorization_uri_default; +- klass->decide_navigation_policy = goa_oauth2_provider_decide_navigation_policy_default; + klass->get_token_uri = goa_oauth2_provider_get_token_uri_default; + klass->get_scope = goa_oauth2_provider_get_scope_default; + klass->get_use_mobile_browser = goa_oauth2_provider_get_use_mobile_browser_default; +- klass->is_deny_node = goa_oauth2_provider_is_deny_node_default; +- klass->is_password_node = goa_oauth2_provider_is_password_node_default; + klass->add_account_key_values = goa_oauth2_provider_add_account_key_values_default; + } + +diff --git a/src/goabackend/goaoauthprovider.c b/src/goabackend/goaoauthprovider.c +deleted file mode 100644 +index 71bcad6..0000000 +--- a/src/goabackend/goaoauthprovider.c ++++ /dev/null +@@ -1,1662 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +-/* +- * Copyright © 2011 – 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 +- +-#include +-#include +-#include +-#include +- +-#include "goaprovider.h" +-#include "goautils.h" +-#include "goawebview.h" +-#include "goaoauthprovider.h" +-#include "goasouplogger.h" +- +-/** +- * SECTION:goaoauthprovider +- * @title: GoaOAuthProvider +- * @short_description: Abstract base class for OAuth 1.0a providers +- * +- * #GoaOAuthProvider is an abstract base class for OAuth 1.0a +- * compliant implementations as defined by RFC +- * 5849. Additionally, the code works with providers +- * implementing OAuth +- * Session 1.0 Draft 1 for refreshing access tokens. +- * +- * Subclasses must implement +- * #GoaOAuthProviderClass.get_consumer_key, +- * #GoaOAuthProviderClass.get_consumer_secret, +- * #GoaOAuthProviderClass.get_request_uri, +- * #GoaOAuthProviderClass.get_authorization_uri, +- * #GoaOAuthProviderClass.get_token_uri, +- * #GoaOAuthProviderClass.get_callback_uri and +- * #GoaOAuthProviderClass.get_identity_sync methods. +- * +- * Additionally, the +- * #GoaProviderClass.get_provider_type, +- * #GoaProviderClass.get_provider_name, +- * #GoaProviderClass.build_object (this should chain up to its +- * parent class) methods must be implemented. +- * +- * Note that the #GoaProviderClass.add_account, +- * #GoaProviderClass.refresh_account and +- * #GoaProviderClass.ensure_credentials_sync methods do not +- * need to be implemented - this type implements these methods. +- */ +- +-G_LOCK_DEFINE_STATIC (provider_lock); +- +-G_DEFINE_ABSTRACT_TYPE (GoaOAuthProvider, goa_oauth_provider, GOA_TYPE_PROVIDER); +- +-static gboolean +-is_authorization_error (GError *error) +-{ +- gboolean ret; +- +- g_return_val_if_fail (error != NULL, FALSE); +- +- ret = FALSE; +- if (error->domain == REST_PROXY_ERROR || error->domain == SOUP_HTTP_ERROR) +- { +- if (SOUP_STATUS_IS_CLIENT_ERROR (error->code)) +- ret = TRUE; +- } +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gboolean +-goa_oauth_provider_get_use_mobile_browser_default (GoaOAuthProvider *provider) +-{ +- return FALSE; +-} +- +-/** +- * goa_oauth_provider_get_use_mobile_browser: +- * @provider: A #GoaOAuthProvider. +- * +- * Returns whether there is a need for the embedded browser to identify +- * itself as running on a mobile phone in order to get a more compact +- * version of the approval page. +- * +- * This is a virtual method where the default implementation returns +- * %FALSE. +- * +- * Returns: %TRUE if the embedded browser should identify itself as +- * running on a mobile platform, %FALSE otherwise. +- */ +-gboolean +-goa_oauth_provider_get_use_mobile_browser (GoaOAuthProvider *provider) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_use_mobile_browser (provider); +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gboolean +-goa_oauth_provider_is_deny_node_default (GoaOAuthProvider *provider, WebKitDOMNode *node) +-{ +- return FALSE; +-} +- +-/** +- * goa_oauth_provider_is_deny_node: +- * @provider: A #GoaOAuthProvider. +- * @node: A WebKitDOMNode. +- * +- * Checks whether @node is the HTML UI element that the user can use +- * to deny permission to access his account. Usually they are either a +- * WebKitDOMHTMLButtonElement or a WebKitDOMHTMLInputElement. +- * +- * Please note that providers may have multiple such elements in their +- * UI and this method should catch all of them. +- * +- * This is a virtual method where the default implementation returns +- * %FALSE. +- * +- * Returns: %TRUE if the @node can be used to deny permission. +- */ +-gboolean +-goa_oauth_provider_is_deny_node (GoaOAuthProvider *provider, WebKitDOMNode *node) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->is_deny_node (provider, node); +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gboolean +-goa_oauth_provider_is_password_node_default (GoaOAuthProvider *provider, WebKitDOMHTMLInputElement *element) +-{ +- return FALSE; +-} +- +-/** +- * goa_oauth_provider_is_password_node: +- * @provider: A #GoaOAuthProvider. +- * @element: A WebKitDOMHTMLInputElement +- * +- * Checks whether @element is the HTML UI element that the user can +- * use to enter her password. This can be used to offer a +- * #GoaPasswordBased interface by saving the user's +- * password. Providers usually frown upon doing this, so this is not +- * recommended. +- * +- * This is a virtual method where the default implementation returns +- * %FALSE. +- * +- * Returns: %TRUE if @element can be used to enter the password. +- */ +-gboolean +-goa_oauth_provider_is_password_node (GoaOAuthProvider *provider, WebKitDOMHTMLInputElement *element) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); +- g_return_val_if_fail (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element), FALSE); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->is_password_node (provider, element); +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static void +-goa_oauth_provider_add_account_key_values_default (GoaOAuthProvider *provider, +- GVariantBuilder *builder) +-{ +- /* do nothing */ +-} +- +-/** +- * goa_oauth_provider_add_account_key_values: +- * @provider: A #GoaProvider. +- * @builder: A #GVariantBuilder for a a{ss} variant. +- * +- * Hook for implementations to add key/value pairs to the key-file +- * when creating an account. +- * +- * This is a virtual method where the default implementation does nothing. +- */ +-void +-goa_oauth_provider_add_account_key_values (GoaOAuthProvider *provider, +- GVariantBuilder *builder) +-{ +- g_return_if_fail (GOA_IS_OAUTH_PROVIDER (provider)); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->add_account_key_values (provider, builder); +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gchar * +-goa_oauth_provider_build_authorization_uri_default (GoaOAuthProvider *provider, +- const gchar *authorization_uri, +- const gchar *escaped_oauth_token) +-{ +- return g_strdup_printf ("%s" +- "?oauth_token=%s", +- authorization_uri, +- escaped_oauth_token); +-} +- +-/** +- * goa_oauth_provider_build_authorization_uri: +- * @provider: A #GoaOAuthProvider. +- * @authorization_uri: An authorization URI. +- * @escaped_oauth_token: An escaped oauth token. +- * +- * Builds the URI that can be opened in a web browser (or embedded web +- * browser widget) to start authenticating an user. +- * +- * The default implementation just returns the expected URI +- * (e.g. http://example.com/dialog/oauth?auth_token=1234567890) +- * - override (and chain up) if you e.g. need to to pass additional +- * parameters. +- * +- * The @authorization_uri parameter originate from the result of the +- * the goa_oauth_provider_get_authorization_uri() method. The +- * @escaped_oauth_token parameter is the temporary credentials identifier +- * escaped using g_uri_escape_string(). +- * +- * Returns: (transfer full): An authorization URI that must be freed with g_free(). +- */ +-gchar * +-goa_oauth_provider_build_authorization_uri (GoaOAuthProvider *provider, +- const gchar *authorization_uri, +- const gchar *escaped_oauth_token) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- g_return_val_if_fail (authorization_uri != NULL, NULL); +- g_return_val_if_fail (escaped_oauth_token != NULL, NULL); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->build_authorization_uri (provider, +- authorization_uri, +- escaped_oauth_token); +-} +- +-/** +- * goa_oauth_provider_get_consumer_key: +- * @provider: A #GoaOAuthProvider. +- * +- * Gets the consumer key identifying the client. +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: (transfer none): A string owned by @provider - do not free. +- */ +-const gchar * +-goa_oauth_provider_get_consumer_key (GoaOAuthProvider *provider) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_consumer_key (provider); +-} +- +-/** +- * goa_oauth_provider_get_consumer_secret: +- * @provider: A #GoaOAuthProvider. +- * +- * Gets the consumer secret identifying the client. +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: (transfer none): A string owned by @provider - do not free. +- */ +-const gchar * +-goa_oauth_provider_get_consumer_secret (GoaOAuthProvider *provider) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_consumer_secret (provider); +-} +- +-/** +- * goa_oauth_provider_get_request_uri: +- * @provider: A #GoaOAuthProvider. +- * +- * Gets the request uri. +- * +- * http://tools.ietf.org/html/rfc5849#section-2.1 +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: (transfer none): A string owned by @provider - do not free. +- */ +-const gchar * +-goa_oauth_provider_get_request_uri (GoaOAuthProvider *provider) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_request_uri (provider); +-} +- +-/** +- * goa_oauth_provider_get_request_uri_params: +- * @provider: A #GoaOAuthProvider. +- * +- * Gets additional parameters for the request URI. +- * +- * http://tools.ietf.org/html/rfc5849#section-2.1 +- * +- * This is a virtual method where the default implementation returns +- * %NULL. +- * +- * Returns: (transfer full): %NULL (for no parameters) or a +- * %NULL-terminated array of (key, value) pairs that will be added to +- * the URI. The caller will free the returned value with g_strfreev(). +- */ +-gchar ** +-goa_oauth_provider_get_request_uri_params (GoaOAuthProvider *provider) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_request_uri_params (provider); +-} +- +-static gchar ** +-goa_oauth_provider_get_request_uri_params_default (GoaOAuthProvider *provider) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- return NULL; +-} +- +-/** +- * goa_oauth_provider_get_authorization_uri: +- * @provider: A #GoaOAuthProvider. +- * +- * Gets the authorization uri. +- * +- * http://tools.ietf.org/html/rfc5849#section-2.2 +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: (transfer none): A string owned by @provider - do not free. +- */ +-const gchar * +-goa_oauth_provider_get_authorization_uri (GoaOAuthProvider *provider) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_authorization_uri (provider); +-} +- +-/** +- * goa_oauth_provider_get_token_uri: +- * @provider: A #GoaOAuthProvider. +- * +- * Gets the token uri. +- * +- * http://tools.ietf.org/html/rfc5849#section-2.3 +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: (transfer none): A string owned by @provider - do not free. +- */ +-const gchar * +-goa_oauth_provider_get_token_uri (GoaOAuthProvider *provider) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_token_uri (provider); +-} +- +-/** +- * goa_oauth_provider_get_callback_uri: +- * @provider: A #GoaOAuthProvider. +- * +- * Gets the callback uri. +- * +- * http://tools.ietf.org/html/rfc5849#section-2.1 +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: (transfer none): A string owned by @provider - do not free. +- */ +-const gchar * +-goa_oauth_provider_get_callback_uri (GoaOAuthProvider *provider) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_callback_uri (provider); +-} +- +-/** +- * goa_oauth_provider_get_identity_sync: +- * @provider: A #GoaOAuthProvider. +- * @access_token: A valid OAuth 1.0 access token. +- * @access_token_secret: The valid secret for @access_token. +- * @out_presentation_identity: (out): Return location for presentation identity or %NULL. +- * @cancellable: (allow-none): A #GCancellable or %NULL. +- * @error: Return location for error or %NULL. +- * +- * Method that returns the identity corresponding to @access_token and +- * @access_token_secret. +- * +- * The identity is needed because all authentication happens out of +- * band. In addition to the identity, an implementation also returns a +- * presentation identity that is more suitable +- * for presentation (the identity could be a GUID for example). +- * +- * The calling thread is blocked while the identity is obtained. +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: The identity or %NULL if error is set. The returned string +- * must be freed with g_free(). +- */ +-gchar * +-goa_oauth_provider_get_identity_sync (GoaOAuthProvider *provider, +- const gchar *access_token, +- const gchar *access_token_secret, +- gchar **out_presentation_identity, +- GCancellable *cancellable, +- GError **error) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- g_return_val_if_fail (access_token != NULL, NULL); +- g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); +- g_return_val_if_fail (error == NULL || *error == NULL, NULL); +- +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->get_identity_sync (provider, +- access_token, +- access_token_secret, +- out_presentation_identity, +- cancellable, +- error); +-} +- +-/** +- * goa_oauth_provider_is_identity_node: +- * @provider: A #GoaOAuthProvider. +- * @element: A WebKitDOMHTMLInputElement. +- * +- * Checks whether @element is the HTML UI element that the user can +- * use to identify herself at the provider. +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: %TRUE if the @element can be used to deny permission. +- */ +-gboolean +-goa_oauth_provider_is_identity_node (GoaOAuthProvider *provider, WebKitDOMHTMLInputElement *element) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->is_identity_node (provider, element); +-} +- +-/** +- * goa_oauth_provider_parse_request_token_error: +- * @provider: A #GoaOAuthProvider. +- * @call: The #RestProxyCall that was used to fetch the request token. +- * +- * Tries to parse the headers and payload within @call to provide a +- * human readable error message in case the request token could not +- * be fetched. +- * +- * This is a pure virtual method - a subclass must provide an +- * implementation. +- * +- * Returns: A human readable error message or %NULL if the cause of the +- * error could not be determined. The returned string must be freed with +- * g_free(). +- */ +-gchar * +-goa_oauth_provider_parse_request_token_error (GoaOAuthProvider *provider, RestProxyCall *call) +-{ +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- return GOA_OAUTH_PROVIDER_GET_CLASS (provider)->parse_request_token_error (provider, call); +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gchar * +-get_tokens_sync (GoaOAuthProvider *provider, +- const gchar *token, +- const gchar *token_secret, +- const gchar *session_handle, /* may be NULL */ +- const gchar *verifier, /* may be NULL */ +- gchar **out_access_token_secret, +- gint *out_access_token_expires_in, +- gchar **out_session_handle, +- gint *out_session_handle_expires_in, +- GCancellable *cancellable, +- GError **error) +-{ +- RestProxy *proxy; +- RestProxyCall *call; +- SoupLogger *logger = NULL; +- gchar *ret = NULL; +- guint status_code; +- GHashTable *f; +- const gchar *expires_in_str; +- gchar *ret_access_token = NULL; +- gchar *ret_access_token_secret = NULL; +- gint ret_access_token_expires_in = 0; +- gchar *ret_session_handle = NULL; +- gint ret_session_handle_expires_in = 0; +- +- proxy = oauth_proxy_new (goa_oauth_provider_get_consumer_key (provider), +- goa_oauth_provider_get_consumer_secret (provider), +- goa_oauth_provider_get_token_uri (provider), +- FALSE); +- logger = goa_soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); +- rest_proxy_add_soup_feature (proxy, SOUP_SESSION_FEATURE (logger)); +- oauth_proxy_set_token (OAUTH_PROXY (proxy), token); +- oauth_proxy_set_token_secret (OAUTH_PROXY (proxy), token_secret); +- call = rest_proxy_new_call (proxy); +- rest_proxy_call_set_method (call, "POST"); +- if (verifier != NULL) +- rest_proxy_call_add_param (call, "oauth_verifier", verifier); +- if (session_handle != NULL) +- rest_proxy_call_add_param (call, "oauth_session_handle", session_handle); +- /* TODO: cancellable support? */ +- if (!rest_proxy_call_sync (call, error)) +- goto out; +- +- status_code = rest_proxy_call_get_status_code (call); +- if (status_code != 200) +- { +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- /* Translators: the %d is a HTTP status code and the %s is a textual description of it */ +- _("Expected status 200 when requesting access token, instead got status %d (%s)"), +- status_code, +- rest_proxy_call_get_status_message (call)); +- goto out; +- } +- +- f = soup_form_decode (rest_proxy_call_get_payload (call)); +- ret_access_token = g_strdup (g_hash_table_lookup (f, "oauth_token")); +- ret_access_token_secret = g_strdup (g_hash_table_lookup (f, "oauth_token_secret")); +- ret_session_handle = g_strdup (g_hash_table_lookup (f, "oauth_session_handle")); +- expires_in_str = g_hash_table_lookup (f, "oauth_expires_in"); +- if (expires_in_str != NULL) +- ret_access_token_expires_in = atoi (expires_in_str); +- expires_in_str = g_hash_table_lookup (f, "oauth_authorization_expires_in"); +- if (expires_in_str != NULL) +- ret_session_handle_expires_in = atoi (expires_in_str); +- g_hash_table_unref (f); +- +- if (ret_access_token == NULL || ret_access_token_secret == NULL) +- { +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- _("Missing access_token or access_token_secret headers in response")); +- goto out; +- } +- +- ret = ret_access_token; ret_access_token = NULL; +- if (out_access_token_secret != NULL) +- { +- *out_access_token_secret = ret_access_token_secret; +- ret_access_token_secret = NULL; +- } +- if (out_access_token_expires_in != NULL) +- *out_access_token_expires_in = ret_access_token_expires_in; +- if (out_session_handle != NULL) +- { +- *out_session_handle = ret_session_handle; +- ret_session_handle = NULL; +- } +- if (out_session_handle_expires_in != NULL) +- *out_session_handle_expires_in = ret_session_handle_expires_in; +- +- out: +- g_free (ret_access_token); +- g_free (ret_access_token_secret); +- g_free (ret_session_handle); +- g_clear_object (&call); +- g_clear_object (&proxy); +- g_clear_object (&logger); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-typedef struct +-{ +- GoaOAuthProvider *provider; +- GtkDialog *dialog; +- GError *error; +- GMainLoop *loop; +- +- gchar *password; +- +- gchar *oauth_verifier; +- +- const gchar *existing_identity; +- +- gchar *identity; +- gchar *presentation_identity; +- +- gchar *request_token; +- gchar *request_token_secret; +- gchar *access_token; +- gchar *access_token_secret; +- gint access_token_expires_in; +- gchar *session_handle; +- gint session_handle_expires_in; +-} IdentifyData; +- +-static void +-on_web_view_deny_click (GoaWebView *web_view, gpointer user_data) +-{ +- IdentifyData *data = user_data; +- gtk_dialog_response (data->dialog, GTK_RESPONSE_CANCEL); +-} +- +-static void +-on_web_view_password_submit (GoaWebView *web_view, const gchar *password, gpointer user_data) +-{ +- IdentifyData *data = user_data; +- +- g_free (data->password); +- data->password = g_strdup (password); +-} +- +-static gboolean +-on_web_view_decide_policy (WebKitWebView *web_view, +- WebKitPolicyDecision *decision, +- WebKitPolicyDecisionType decision_type, +- gpointer user_data) +-{ +- GHashTable *key_value_pairs; +- IdentifyData *data = user_data; +- SoupURI *uri; +- WebKitNavigationAction *action; +- WebKitURIRequest *request; +- const gchar *query; +- const gchar *redirect_uri; +- const gchar *requested_uri; +- gint response_id = GTK_RESPONSE_NONE; +- +- if (decision_type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION) +- return FALSE; +- +- /* TODO: use oauth_proxy_extract_access_token() */ +- +- action = webkit_navigation_policy_decision_get_navigation_action (WEBKIT_NAVIGATION_POLICY_DECISION (decision)); +- request = webkit_navigation_action_get_request (action); +- requested_uri = webkit_uri_request_get_uri (request); +- redirect_uri = goa_oauth_provider_get_callback_uri (data->provider); +- +- if (!g_str_has_prefix (requested_uri, redirect_uri)) +- goto default_behaviour; +- +- uri = soup_uri_new (requested_uri); +- query = soup_uri_get_query (uri); +- +- if (query != NULL) +- { +- key_value_pairs = soup_form_decode (query); +- +- data->oauth_verifier = g_strdup (g_hash_table_lookup (key_value_pairs, "oauth_verifier")); +- if (data->oauth_verifier != NULL) +- response_id = GTK_RESPONSE_OK; +- +- g_hash_table_unref (key_value_pairs); +- } +- +- if (data->oauth_verifier != NULL) +- goto ignore_request; +- +- /* TODO: The only OAuth1 provider is Flickr. It doesn't send any +- * error code and only redirects to the URI specified in the Flickr +- * App Garden. Re-evaluate when the situation changes. +- */ +- response_id = GTK_RESPONSE_CANCEL; +- goto ignore_request; +- +- ignore_request: +- g_assert (response_id != GTK_RESPONSE_NONE); +- gtk_dialog_response (data->dialog, response_id); +- webkit_policy_decision_ignore (decision); +- return TRUE; +- +- default_behaviour: +- return FALSE; +-} +- +-static void +-rest_proxy_call_cb (RestProxyCall *call, const GError *error, GObject *weak_object, gpointer user_data) +-{ +- IdentifyData *data = user_data; +- g_main_loop_quit (data->loop); +-} +- +-static gboolean +-get_tokens_and_identity (GoaOAuthProvider *provider, +- gboolean add_account, +- const gchar *existing_identity, +- GtkDialog *dialog, +- GtkBox *vbox, +- gchar **out_access_token, +- gchar **out_access_token_secret, +- gint *out_access_token_expires_in, +- gchar **out_session_handle, +- gint *out_session_handle_expires_in, +- gchar **out_identity, +- gchar **out_presentation_identity, +- gchar **out_password, +- GError **error) +-{ +- gboolean ret = FALSE; +- gchar *url = NULL; +- IdentifyData data; +- gchar *escaped_request_token = NULL; +- RestProxy *proxy = NULL; +- RestProxyCall *call = NULL; +- SoupLogger *logger = NULL; +- GHashTable *f; +- GtkWidget *embed; +- GtkWidget *grid; +- GtkWidget *spinner; +- GtkWidget *web_view; +- gchar **request_params = NULL; +- guint n; +- +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), FALSE); +- g_return_val_if_fail ((!add_account && existing_identity != NULL && existing_identity[0] != '\0') +- || (add_account && existing_identity == NULL), FALSE); +- g_return_val_if_fail (GTK_IS_DIALOG (dialog), FALSE); +- g_return_val_if_fail (GTK_IS_BOX (vbox), FALSE); +- g_return_val_if_fail (error == NULL || *error == NULL, FALSE); +- +- /* TODO: check with NM whether we're online, if not - return error */ +- +- memset (&data, '\0', sizeof (IdentifyData)); +- data.provider = provider; +- data.dialog = dialog; +- data.loop = g_main_loop_new (NULL, FALSE); +- data.existing_identity = existing_identity; +- +- proxy = oauth_proxy_new (goa_oauth_provider_get_consumer_key (provider), +- goa_oauth_provider_get_consumer_secret (provider), +- goa_oauth_provider_get_request_uri (provider), FALSE); +- logger = goa_soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); +- rest_proxy_add_soup_feature (proxy, SOUP_SESSION_FEATURE (logger)); +- +- call = rest_proxy_new_call (proxy); +- rest_proxy_call_set_method (call, "POST"); +- rest_proxy_call_add_param (call, "oauth_callback", goa_oauth_provider_get_callback_uri (provider)); +- +- request_params = goa_oauth_provider_get_request_uri_params (provider); +- if (request_params != NULL) +- { +- g_assert (g_strv_length (request_params) % 2 == 0); +- for (n = 0; request_params[n] != NULL; n += 2) +- rest_proxy_call_add_param (call, request_params[n], request_params[n+1]); +- } +- if (!rest_proxy_call_async (call, rest_proxy_call_cb, NULL, &data, &data.error)) +- { +- g_prefix_error (&data.error, _("Error getting a Request Token: ")); +- goto out; +- } +- +- goa_utils_set_dialog_title (GOA_PROVIDER (provider), dialog, add_account); +- +- grid = gtk_grid_new (); +- gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL); +- gtk_grid_set_row_spacing (GTK_GRID (grid), 12); +- gtk_container_add (GTK_CONTAINER (vbox), grid); +- +- spinner = gtk_spinner_new (); +- gtk_widget_set_hexpand (spinner, TRUE); +- gtk_widget_set_halign (spinner, GTK_ALIGN_CENTER); +- gtk_widget_set_vexpand (spinner, TRUE); +- gtk_widget_set_valign (spinner, GTK_ALIGN_CENTER); +- gtk_widget_set_size_request (GTK_WIDGET (spinner), 24, 24); +- gtk_spinner_start (GTK_SPINNER (spinner)); +- gtk_container_add (GTK_CONTAINER (grid), spinner); +- gtk_widget_show_all (GTK_WIDGET (vbox)); +- +- g_main_loop_run (data.loop); +- gtk_container_remove (GTK_CONTAINER (grid), spinner); +- +- if (rest_proxy_call_get_status_code (call) != 200) +- { +- gchar *msg; +- +- msg = goa_oauth_provider_parse_request_token_error (provider, call); +- if (msg == NULL) +- /* Translators: the %d is a HTTP status code and the %s is a textual description of it */ +- msg = g_strdup_printf (_("Expected status 200 for getting a Request Token, instead got status %d (%s)"), +- rest_proxy_call_get_status_code (call), +- rest_proxy_call_get_status_message (call)); +- +- g_set_error_literal (&data.error, GOA_ERROR, GOA_ERROR_FAILED, msg); +- g_free (msg); +- goto out; +- } +- f = soup_form_decode (rest_proxy_call_get_payload (call)); +- data.request_token = g_strdup (g_hash_table_lookup (f, "oauth_token")); +- data.request_token_secret = g_strdup (g_hash_table_lookup (f, "oauth_token_secret")); +- g_hash_table_unref (f); +- if (data.request_token == NULL || data.request_token_secret == NULL) +- { +- g_set_error (&data.error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- _("Missing request_token or request_token_secret headers in response")); +- goto out; +- } +- +- escaped_request_token = g_uri_escape_string (data.request_token, NULL, TRUE); +- url = goa_oauth_provider_build_authorization_uri (provider, +- goa_oauth_provider_get_authorization_uri (provider), +- escaped_request_token); +- +- web_view = goa_web_view_new (GOA_PROVIDER (provider), existing_identity); +- gtk_widget_set_hexpand (web_view, TRUE); +- gtk_widget_set_vexpand (web_view, TRUE); +- embed = goa_web_view_get_view (GOA_WEB_VIEW (web_view)); +- +- if (goa_oauth_provider_get_use_mobile_browser (provider)) +- goa_web_view_fake_mobile (GOA_WEB_VIEW (web_view)); +- +- webkit_web_view_load_uri (WEBKIT_WEB_VIEW (embed), url); +- g_signal_connect (embed, +- "decide-policy", +- G_CALLBACK (on_web_view_decide_policy), +- &data); +- g_signal_connect (web_view, "deny-click", G_CALLBACK (on_web_view_deny_click), &data); +- g_signal_connect (web_view, "password-submit", G_CALLBACK (on_web_view_password_submit), &data); +- +- gtk_container_add (GTK_CONTAINER (grid), web_view); +- gtk_window_set_default_size (GTK_WINDOW (dialog), -1, -1); +- +- gtk_widget_show_all (GTK_WIDGET (vbox)); +- gtk_dialog_run (GTK_DIALOG (dialog)); +- +- if (data.oauth_verifier == NULL) +- { +- if (data.error == NULL) +- { +- g_set_error (&data.error, +- GOA_ERROR, +- GOA_ERROR_DIALOG_DISMISSED, +- _("Dialog was dismissed")); +- } +- goto out; +- } +- g_assert (data.error == NULL); +- +- /* OK, we are done interacting with the user ... but before we can +- * make up our mind, there are two more RPC calls to make and these +- * call may take some time. So hide the dialog immediately. +- */ +- gtk_widget_hide (GTK_WIDGET (dialog)); +- +- /* OK, we now have the request token... we can exchange that for a +- * (short-lived) access token and session_handle (used to refresh the +- * access token).. +- */ +- +- /* TODO: run in worker thread */ +- data.access_token = get_tokens_sync (provider, +- data.request_token, +- data.request_token_secret, +- NULL, /* session_handle */ +- data.oauth_verifier, +- &data.access_token_secret, +- &data.access_token_expires_in, +- &data.session_handle, +- &data.session_handle_expires_in, +- NULL, /* GCancellable */ +- &data.error); +- if (data.access_token == NULL) +- { +- g_prefix_error (&data.error, _("Error getting an Access Token: ")); +- goto out; +- } +- +- /* TODO: run in worker thread */ +- data.identity = goa_oauth_provider_get_identity_sync (provider, +- data.access_token, +- data.access_token_secret, +- &data.presentation_identity, +- NULL, /* TODO: GCancellable */ +- &data.error); +- if (data.identity == NULL) +- { +- g_prefix_error (&data.error, _("Error getting identity: ")); +- goto out; +- } +- +- ret = TRUE; +- +- out: +- g_clear_object (&call); +- +- if (ret) +- { +- g_warn_if_fail (data.error == NULL); +- if (out_access_token != NULL) +- *out_access_token = g_strdup (data.access_token); +- if (out_access_token_secret != NULL) +- *out_access_token_secret = g_strdup (data.access_token_secret); +- if (out_access_token_expires_in != NULL) +- *out_access_token_expires_in = data.access_token_expires_in; +- if (out_session_handle != NULL) +- *out_session_handle = g_strdup (data.session_handle); +- if (out_session_handle_expires_in != NULL) +- *out_session_handle_expires_in = data.session_handle_expires_in; +- if (out_identity != NULL) +- *out_identity = g_strdup (data.identity); +- if (out_presentation_identity != NULL) +- *out_presentation_identity = g_strdup (data.presentation_identity); +- if (out_password != NULL) +- *out_password = g_strdup (data.password); +- } +- else +- { +- g_warn_if_fail (data.error != NULL); +- g_propagate_error (error, data.error); +- } +- +- g_free (data.password); +- g_free (data.presentation_identity); +- g_free (data.identity); +- g_free (url); +- +- g_free (data.oauth_verifier); +- g_clear_pointer (&data.loop, (GDestroyNotify) g_main_loop_unref); +- g_free (data.access_token); +- g_free (data.access_token_secret); +- g_free (escaped_request_token); +- +- g_free (data.request_token); +- g_free (data.request_token_secret); +- +- g_strfreev (request_params); +- g_clear_object (&proxy); +- g_clear_object (&logger); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-typedef struct +-{ +- GError *error; +- GMainLoop *loop; +- gchar *account_object_path; +-} AddData; +- +-static void +-add_account_cb (GoaManager *manager, +- GAsyncResult *res, +- gpointer user_data) +-{ +- AddData *data = user_data; +- goa_manager_call_add_account_finish (manager, +- &data->account_object_path, +- res, +- &data->error); +- g_main_loop_quit (data->loop); +-} +- +-static gint64 +-duration_to_abs_usec (gint duration_sec) +-{ +- gint64 ret; +- GTimeVal now; +- +- g_get_current_time (&now); +- ret = ((gint64) now.tv_sec) * 1000L * 1000L + ((gint64) now.tv_usec); +- ret += ((gint64) duration_sec) * 1000L * 1000L; +- return ret; +-} +- +-static gint +-abs_usec_to_duration (gint64 abs_usec) +-{ +- gint64 ret; +- GTimeVal now; +- +- g_get_current_time (&now); +- ret = abs_usec - (((gint64) now.tv_sec) * 1000L * 1000L + ((gint64) now.tv_usec)); +- ret /= 1000L * 1000L; +- return ret; +-} +- +-static GoaObject * +-goa_oauth_provider_add_account (GoaProvider *_provider, +- GoaClient *client, +- GtkDialog *dialog, +- GtkBox *vbox, +- GError **error) +-{ +- GoaOAuthProvider *provider = GOA_OAUTH_PROVIDER (_provider); +- GoaObject *ret = NULL; +- gchar *access_token = NULL; +- gchar *access_token_secret = NULL; +- gint access_token_expires_in; +- gchar *session_handle = NULL; +- gint session_handle_expires_in; +- gchar *identity = NULL; +- gchar *presentation_identity = NULL; +- gchar *password = NULL; +- AddData data; +- GVariantBuilder credentials; +- GVariantBuilder details; +- +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- g_return_val_if_fail (GOA_IS_CLIENT (client), NULL); +- g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); +- g_return_val_if_fail (GTK_IS_BOX (vbox), NULL); +- g_return_val_if_fail (error == NULL || *error == NULL, NULL); +- +- memset (&data, '\0', sizeof (AddData)); +- data.loop = g_main_loop_new (NULL, FALSE); +- +- if (!get_tokens_and_identity (provider, +- TRUE, +- NULL, +- dialog, +- vbox, +- &access_token, +- &access_token_secret, +- &access_token_expires_in, +- &session_handle, +- &session_handle_expires_in, +- &identity, +- &presentation_identity, +- &password, +- &data.error)) +- goto out; +- +- /* OK, got the identity... see if there's already an account +- * of this type with the given identity +- */ +- if (!goa_utils_check_duplicate (client, +- identity, +- presentation_identity, +- goa_provider_get_provider_type (GOA_PROVIDER (provider)), +- (GoaPeekInterfaceFunc) goa_object_peek_oauth_based, +- &data.error)) +- goto out; +- +- g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT); +- g_variant_builder_add (&credentials, "{sv}", "access_token", g_variant_new_string (access_token)); +- g_variant_builder_add (&credentials, "{sv}", "access_token_secret", g_variant_new_string (access_token_secret)); +- if (access_token_expires_in > 0) +- g_variant_builder_add (&credentials, "{sv}", "access_token_expires_at", +- g_variant_new_int64 (duration_to_abs_usec (access_token_expires_in))); +- if (session_handle != NULL) +- g_variant_builder_add (&credentials, "{sv}", "session_handle", g_variant_new_string (session_handle)); +- if (session_handle_expires_in > 0) +- g_variant_builder_add (&credentials, "{sv}", "session_handle_expires_at", +- g_variant_new_int64 (duration_to_abs_usec (session_handle_expires_in))); +- if (password != NULL) +- g_variant_builder_add (&credentials, "{sv}", "password", g_variant_new_string (password)); +- +- g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); +- goa_oauth_provider_add_account_key_values (provider, &details); +- +- /* 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 (GOA_PROVIDER (provider)), +- identity, +- presentation_identity, +- 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_free (identity); +- g_free (presentation_identity); +- g_free (password); +- g_free (access_token); +- g_free (access_token_secret); +- g_free (session_handle); +- g_free (data.account_object_path); +- g_clear_pointer (&data.loop, (GDestroyNotify) g_main_loop_unref); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gboolean +-goa_oauth_provider_refresh_account (GoaProvider *_provider, +- GoaClient *client, +- GoaObject *object, +- GtkWindow *parent, +- GError **error) +-{ +- GoaOAuthProvider *provider = GOA_OAUTH_PROVIDER (_provider); +- GoaAccount *account; +- GtkWidget *dialog; +- gchar *access_token = NULL; +- gchar *access_token_secret = NULL; +- gchar *password = NULL; +- gint access_token_expires_in; +- gchar *session_handle = NULL; +- gint session_handle_expires_in; +- gchar *identity = NULL; +- const gchar *existing_identity; +- const gchar *existing_presentation_identity; +- GVariantBuilder builder; +- gboolean ret = FALSE; +- +- g_return_val_if_fail (GOA_IS_OAUTH_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_resizable (GTK_WINDOW (dialog), FALSE); +- gtk_widget_show_all (dialog); +- +- account = goa_object_peek_account (object); +- +- /* We abuse presentation identity here because for some providers +- * identity can be a machine readable ID, which can not be used to +- * log in via the provider's web interface. +- */ +- existing_presentation_identity = goa_account_get_presentation_identity (account); +- if (!get_tokens_and_identity (provider, +- FALSE, +- existing_presentation_identity, +- GTK_DIALOG (dialog), +- GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), +- &access_token, +- &access_token_secret, +- &access_token_expires_in, +- &session_handle, +- &session_handle_expires_in, +- &identity, +- NULL, /* out_presentation_identity */ +- &password, +- error)) +- goto out; +- +- /* Changes made to the web interface by the providers can break our +- * DOM parsing. So we should still query and check the identity +- * afterwards. +- */ +- existing_identity = goa_account_get_identity (account); +- if (g_strcmp0 (identity, existing_identity) != 0) +- { +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_FAILED, +- _("Was asked to log in as %s, but logged in as %s"), +- existing_identity, +- identity); +- goto out; +- } +- +- g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); +- g_variant_builder_add (&builder, "{sv}", "access_token", g_variant_new_string (access_token)); +- g_variant_builder_add (&builder, "{sv}", "access_token_secret", g_variant_new_string (access_token_secret)); +- if (access_token_expires_in > 0) +- g_variant_builder_add (&builder, "{sv}", "access_token_expires_at", +- g_variant_new_int64 (duration_to_abs_usec (access_token_expires_in))); +- if (session_handle != NULL) +- g_variant_builder_add (&builder, "{sv}", "session_handle", g_variant_new_string (session_handle)); +- if (session_handle_expires_in > 0) +- g_variant_builder_add (&builder, "{sv}", "session_handle_expires_at", +- g_variant_new_int64 (duration_to_abs_usec (session_handle_expires_in))); +- if (password != NULL) +- g_variant_builder_add (&builder, "{sv}", "password", g_variant_new_string (password)); +- /* TODO: run in worker thread */ +- if (!goa_utils_store_credentials_for_object_sync (GOA_PROVIDER (provider), +- object, +- g_variant_builder_end (&builder), +- NULL, /* GCancellable */ +- error)) +- goto out; +- +- goa_account_call_ensure_credentials (goa_object_peek_account (object), +- NULL, /* GCancellable */ +- NULL, NULL); /* callback, user_data */ +- +- ret = TRUE; +- +- out: +- gtk_widget_destroy (dialog); +- +- g_free (identity); +- g_free (access_token); +- g_free (access_token_secret); +- g_free (password); +- g_free (session_handle); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static void +-free_mutex (GMutex *mutex) +-{ +- g_mutex_clear (mutex); +- g_slice_free (GMutex, mutex); +-} +- +-/** +- * goa_oauth_provider_get_access_token_sync: +- * @provider: A #GoaOAuthProvider. +- * @object: A #GoaObject. +- * @force_refresh: If set to %TRUE, forces a refresh of the access token, if possible. +- * @out_access_token_secret: (out): The secret for the return access token. +- * @out_access_token_expires_in: (out): Return location for how many seconds the returned token is valid for (0 if unknown) or %NULL. +- * @cancellable: (allow-none): A #GCancellable or %NULL. +- * @error: Return location for error or %NULL. +- * +- * Synchronously gets an access token for @object. The calling thread +- * is blocked while the operation is pending. +- * +- * The resulting token is typically read from the local cache so most +- * of the time only a local roundtrip to the storage for the token +- * cache (e.g. gnome-keyring-daemon) is +- * needed. However, the operation may involve refreshing the token +- * with the service provider so a full network round-trip may be +- * needed. +- * +- * Note that multiple calls are serialized to avoid multiple +- * outstanding requests to the service provider. +- * +- * This operation may fail if e.g. unable to refresh the credentials +- * or if network connectivity is not available. Note that even if a +- * token is returned, the returned token isn't guaranteed to work - +- * use goa_provider_ensure_credentials_sync() if you need +- * stronger guarantees. +- * +- * Returns: The access token or %NULL if error is set. The returned +- * string must be freed with g_free(). +- */ +-gchar * +-goa_oauth_provider_get_access_token_sync (GoaOAuthProvider *provider, +- GoaObject *object, +- gboolean force_refresh, +- gchar **out_access_token_secret, +- gint *out_access_token_expires_in, +- GCancellable *cancellable, +- GError **error) +-{ +- GVariant *credentials = NULL; +- GVariantIter iter; +- const gchar *key; +- GVariant *value; +- gchar *access_token = NULL; +- gchar *access_token_secret = NULL; +- gchar *session_handle = NULL; +- gchar *access_token_for_refresh = NULL; +- gchar *access_token_secret_for_refresh = NULL; +- gchar *session_handle_for_refresh = NULL; +- gchar *password = NULL; +- gint access_token_expires_in = 0; +- gint session_handle_expires_in = 0; +- gboolean success = FALSE; +- GVariantBuilder builder; +- gchar *ret = NULL; +- GMutex *lock; +- +- g_return_val_if_fail (GOA_IS_OAUTH_PROVIDER (provider), NULL); +- g_return_val_if_fail (GOA_IS_OBJECT (object), NULL); +- g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); +- g_return_val_if_fail (error == NULL || *error == NULL, NULL); +- +- /* provider_lock is too coarse, use a per-object lock instead */ +- G_LOCK (provider_lock); +- lock = g_object_get_data (G_OBJECT (object), "-goa-oauth-provider-get-access-token-lock"); +- if (lock == NULL) +- { +- lock = g_slice_new0 (GMutex); +- g_mutex_init (lock); +- g_object_set_data_full (G_OBJECT (object), +- "-goa-oauth-provider-get-access-token-lock", +- lock, +- (GDestroyNotify) free_mutex); +- } +- G_UNLOCK (provider_lock); +- +- g_mutex_lock (lock); +- +- /* First, get the credentials from the keyring */ +- credentials = goa_utils_lookup_credentials_sync (GOA_PROVIDER (provider), +- object, +- cancellable, +- error); +- if (credentials == NULL) +- { +- if (error != NULL) +- { +- (*error)->domain = GOA_ERROR; +- (*error)->code = GOA_ERROR_NOT_AUTHORIZED; +- } +- goto out; +- } +- +- g_variant_iter_init (&iter, credentials); +- while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) +- { +- if (g_strcmp0 (key, "access_token") == 0) +- access_token = g_variant_dup_string (value, NULL); +- else if (g_strcmp0 (key, "access_token_secret") == 0) +- access_token_secret = g_variant_dup_string (value, NULL); +- else if (g_strcmp0 (key, "access_token_expires_at") == 0) +- access_token_expires_in = abs_usec_to_duration (g_variant_get_int64 (value)); +- else if (g_strcmp0 (key, "session_handle") == 0) +- session_handle = g_variant_dup_string (value, NULL); +- else if (g_strcmp0 (key, "session_handle_expires_at") == 0) +- session_handle_expires_in = abs_usec_to_duration (g_variant_get_int64 (value)); +- else if (g_strcmp0 (key, "password") == 0) +- password = g_variant_dup_string (value, NULL); +- g_variant_unref (value); +- } +- +- if (access_token == NULL || access_token_secret == NULL) +- { +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_NOT_AUTHORIZED, +- _("Credentials do not contain access_token or access_token_secret")); +- goto out; +- } +- +- /* if we can't refresh the token, just return it no matter what */ +- if (session_handle == NULL) +- { +- g_debug ("Returning locally cached credentials that cannot be refreshed"); +- success = TRUE; +- goto out; +- } +- +- /* If access_token is still "fresh enough" (e.g. more than ten +- * minutes of life left in it), just return it unless we've been +- * asked to forcibly refresh it +- */ +- if (!force_refresh && access_token_expires_in > 10*60) +- { +- g_debug ("Returning locally cached credentials (expires in %d seconds)", access_token_expires_in); +- success = TRUE; +- goto out; +- } +- +- g_debug ("Refreshing locally cached credentials (expires in %d seconds, force_refresh=%d)", access_token_expires_in, force_refresh); +- +- /* Otherwise, refresh it */ +- access_token_for_refresh = access_token; access_token = NULL; +- access_token_secret_for_refresh = access_token_secret; access_token_secret = NULL; +- session_handle_for_refresh = session_handle; session_handle = NULL; +- access_token = get_tokens_sync (provider, +- access_token_for_refresh, +- access_token_secret_for_refresh, +- session_handle_for_refresh, +- NULL, /* verifier */ +- &access_token_secret, +- &access_token_expires_in, +- &session_handle, +- &session_handle_expires_in, +- cancellable, +- error); +- if (access_token == NULL) +- { +- if (error != NULL) +- { +- g_prefix_error (error, _("Failed to refresh access token (%s, %d): "), +- g_quark_to_string ((*error)->domain), (*error)->code); +- (*error)->code = is_authorization_error (*error) ? GOA_ERROR_NOT_AUTHORIZED : GOA_ERROR_FAILED; +- (*error)->domain = GOA_ERROR; +- } +- goto out; +- } +- +- /* Good. Now update the keyring with the refreshed credentials */ +- g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); +- g_variant_builder_add (&builder, "{sv}", "access_token", g_variant_new_string (access_token)); +- g_variant_builder_add (&builder, "{sv}", "access_token_secret", g_variant_new_string (access_token_secret)); +- if (access_token_expires_in > 0) +- g_variant_builder_add (&builder, "{sv}", "access_token_expires_at", +- g_variant_new_int64 (duration_to_abs_usec (access_token_expires_in))); +- if (session_handle != NULL) +- g_variant_builder_add (&builder, "{sv}", "session_handle", g_variant_new_string (session_handle)); +- if (session_handle_expires_in > 0) +- g_variant_builder_add (&builder, "{sv}", "session_handle_expires_at", +- g_variant_new_int64 (duration_to_abs_usec (session_handle_expires_in))); +- if (password != NULL) +- g_variant_builder_add (&builder, "{sv}", "password", g_variant_new_string (password)); +- +- /* TODO: run in worker thread */ +- if (!goa_utils_store_credentials_for_object_sync (GOA_PROVIDER (provider), +- object, +- g_variant_builder_end (&builder), +- cancellable, +- error)) +- { +- if (error != NULL) +- { +- (*error)->domain = GOA_ERROR; +- (*error)->code = GOA_ERROR_NOT_AUTHORIZED; +- } +- goto out; +- } +- +- success = TRUE; +- +- out: +- if (success) +- { +- ret = access_token; access_token = NULL; +- g_assert (ret != NULL); +- if (out_access_token_secret != NULL) +- { +- *out_access_token_secret = access_token_secret; access_token_secret = NULL; +- } +- if (out_access_token_expires_in != NULL) +- *out_access_token_expires_in = access_token_expires_in; +- } +- g_free (access_token); +- g_free (access_token_secret); +- g_free (session_handle); +- g_free (access_token_for_refresh); +- g_free (access_token_secret_for_refresh); +- g_free (session_handle_for_refresh); +- g_free (password); +- g_clear_pointer (&credentials, (GDestroyNotify) g_variant_unref); +- +- g_mutex_unlock (lock); +- +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gboolean on_handle_get_access_token (GoaOAuthBased *object, +- GDBusMethodInvocation *invocation, +- gpointer user_data); +- +-static gboolean +-goa_oauth_provider_build_object (GoaProvider *provider, +- GoaObjectSkeleton *object, +- GKeyFile *key_file, +- const gchar *group, +- GDBusConnection *connection, +- gboolean just_added, +- GError **error) +-{ +- GoaOAuthBased *oauth_based; +- gchar *identity; +- +- identity = NULL; +- +- oauth_based = goa_object_get_oauth_based (GOA_OBJECT (object)); +- if (oauth_based != NULL) +- goto out; +- +- oauth_based = goa_oauth_based_skeleton_new (); +- goa_oauth_based_set_consumer_key (oauth_based, +- goa_oauth_provider_get_consumer_key (GOA_OAUTH_PROVIDER (provider))); +- goa_oauth_based_set_consumer_secret (oauth_based, +- goa_oauth_provider_get_consumer_secret (GOA_OAUTH_PROVIDER (provider))); +- /* Ensure D-Bus method invocations run in their own thread */ +- g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (oauth_based), +- G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); +- goa_object_skeleton_set_oauth_based (object, oauth_based); +- g_signal_connect (oauth_based, +- "handle-get-access-token", +- G_CALLBACK (on_handle_get_access_token), +- NULL); +- +- out: +- g_object_unref (oauth_based); +- g_free (identity); +- return TRUE; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static gboolean +-goa_oauth_provider_ensure_credentials_sync (GoaProvider *_provider, +- GoaObject *object, +- gint *out_expires_in, +- GCancellable *cancellable, +- GError **error) +-{ +- GoaOAuthProvider *provider = GOA_OAUTH_PROVIDER (_provider); +- gboolean ret = FALSE; +- gchar *access_token = NULL; +- gchar *access_token_secret = NULL; +- gint access_token_expires_in; +- gchar *identity = NULL; +- gboolean force_refresh = FALSE; +- +- again: +- access_token = goa_oauth_provider_get_access_token_sync (provider, +- object, +- force_refresh, +- &access_token_secret, +- &access_token_expires_in, +- cancellable, +- error); +- if (access_token == NULL) +- goto out; +- +- identity = goa_oauth_provider_get_identity_sync (provider, +- access_token, +- access_token_secret, +- NULL, /* out_presentation_identity */ +- cancellable, +- error); +- if (identity == NULL) +- { +- /* OK, try again, with forcing the locally cached credentials to be refreshed */ +- if (!force_refresh) +- { +- force_refresh = TRUE; +- g_free (access_token); access_token = NULL; +- g_free (access_token_secret); access_token_secret = NULL; +- g_clear_error (error); +- goto again; +- } +- else +- { +- goto out; +- } +- } +- +- /* TODO: maybe check with the identity we have */ +- ret = TRUE; +- if (out_expires_in != NULL) +- *out_expires_in = access_token_expires_in; +- +- out: +- g_free (identity); +- g_free (access_token); +- g_free (access_token_secret); +- return ret; +-} +- +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static void +-goa_oauth_provider_init (GoaOAuthProvider *client) +-{ +-} +- +-static void +-goa_oauth_provider_class_init (GoaOAuthProviderClass *klass) +-{ +- GoaProviderClass *provider_class; +- +- provider_class = GOA_PROVIDER_CLASS (klass); +- provider_class->add_account = goa_oauth_provider_add_account; +- provider_class->refresh_account = goa_oauth_provider_refresh_account; +- provider_class->build_object = goa_oauth_provider_build_object; +- provider_class->ensure_credentials_sync = goa_oauth_provider_ensure_credentials_sync; +- +- klass->build_authorization_uri = goa_oauth_provider_build_authorization_uri_default; +- klass->get_use_mobile_browser = goa_oauth_provider_get_use_mobile_browser_default; +- klass->is_deny_node = goa_oauth_provider_is_deny_node_default; +- klass->is_password_node = goa_oauth_provider_is_password_node_default; +- klass->get_request_uri_params = goa_oauth_provider_get_request_uri_params_default; +- klass->add_account_key_values = goa_oauth_provider_add_account_key_values_default; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- +-/* runs in a thread dedicated to handling @invocation */ +-static gboolean +-on_handle_get_access_token (GoaOAuthBased *interface, +- GDBusMethodInvocation *invocation, +- gpointer user_data) +-{ +- GoaObject *object; +- GoaAccount *account; +- GoaProvider *provider; +- GError *error; +- const gchar *id; +- const gchar *method_name; +- const gchar *provider_type; +- gchar *access_token = NULL; +- gchar *access_token_secret = NULL; +- gint access_token_expires_in; +- +- /* 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); +- +- 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, id); +- +- provider = goa_provider_get_for_provider_type (provider_type); +- +- error = NULL; +- access_token = goa_oauth_provider_get_access_token_sync (GOA_OAUTH_PROVIDER (provider), +- object, +- FALSE, /* force_refresh */ +- &access_token_secret, +- &access_token_expires_in, +- NULL, /* GCancellable* */ +- &error); +- if (access_token == NULL) +- { +- g_dbus_method_invocation_take_error (invocation, error); +- } +- else +- { +- goa_oauth_based_complete_get_access_token (interface, +- invocation, +- access_token, +- access_token_secret, +- access_token_expires_in); +- } +- g_free (access_token); +- g_free (access_token_secret); +- g_object_unref (provider); +- return TRUE; /* invocation was handled */ +-} +diff --git a/src/goabackend/goaoauthprovider.h b/src/goabackend/goaoauthprovider.h +deleted file mode 100644 +index d4ffa3c..0000000 +--- a/src/goabackend/goaoauthprovider.h ++++ /dev/null +@@ -1,143 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +-/* +- * Copyright © 2011 – 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 . +- */ +- +-#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION) +-#error "Only can be included directly." +-#endif +- +-#ifndef __GOA_OAUTH_PROVIDER_H__ +-#define __GOA_OAUTH_PROVIDER_H__ +- +-#include +-#include +-#include +-#include +- +-G_BEGIN_DECLS +- +-#define GOA_TYPE_OAUTH_PROVIDER (goa_oauth_provider_get_type ()) +-G_DECLARE_DERIVABLE_TYPE (GoaOAuthProvider, goa_oauth_provider, GOA, OAUTH_PROVIDER, GoaProvider); +- +-/** +- * GoaOAuthProvider: +- * +- * The #GoaOAuthProvider structure contains only private data and should +- * only be accessed using the provided API. +- */ +- +-/** +- * GoaOAuthProviderClass: +- * @parent_class: The parent class. +- * @get_consumer_key: Virtual function for goa_oauth_provider_get_consumer_key(). +- * @get_consumer_secret: Virtual function for goa_oauth_provider_get_consumer_secret(). +- * @get_request_uri: Virtual function for goa_oauth_provider_get_request_uri(). +- * @get_authorization_uri: Virtual function for goa_oauth_provider_get_authorization_uri(). +- * @get_token_uri: Virtual function for goa_oauth_provider_get_token_uri(). +- * @get_callback_uri: Virtual function for goa_oauth_provider_get_callback_uri(). +- * @get_identity_sync: Virtual function for goa_oauth_provider_get_identity_sync(). +- * @parse_request_token_error: Virtual function for goa_oauth_provider_parse_request_token_error(). +- * @build_authorization_uri: Virtual function for goa_oauth_provider_build_authorization_uri(). +- * @get_use_mobile_browser: Virtual function for goa_oauth_provider_get_use_mobile_browser(). +- * @get_request_uri_params: Virtual function for goa_oauth_provider_get_request_uri_params(). +- * @add_account_key_values: Virtual function for goa_oauth_provider_add_account_key_values(). +- * @is_deny_node: Virtual function for goa_oauth_provider_is_deny_node(). +- * @is_identity_node: Virtual function for goa_oauth_provider_is_identity_node(). +- * @is_password_node: Virtual function for goa_oauth_provider_is_password_node(). +- * +- * Class structure for #GoaOAuthProvider. +- */ +-struct _GoaOAuthProviderClass +-{ +- GoaProviderClass parent_class; +- +- /* pure virtual */ +- const gchar *(*get_consumer_key) (GoaOAuthProvider *provider); +- const gchar *(*get_consumer_secret) (GoaOAuthProvider *provider); +- const gchar *(*get_request_uri) (GoaOAuthProvider *provider); +- const gchar *(*get_authorization_uri) (GoaOAuthProvider *provider); +- const gchar *(*get_token_uri) (GoaOAuthProvider *provider); +- const gchar *(*get_callback_uri) (GoaOAuthProvider *provider); +- +- gchar *(*get_identity_sync) (GoaOAuthProvider *provider, +- const gchar *access_token, +- const gchar *access_token_secret, +- gchar **out_presentation_identity, +- GCancellable *cancellable, +- GError **error); +- +- gchar *(*parse_request_token_error) (GoaOAuthProvider *provider, +- RestProxyCall *call); +- +- /* virtual but with default implementation */ +- gchar *(*build_authorization_uri) (GoaOAuthProvider *provider, +- const gchar *authorization_uri, +- const gchar *escaped_oauth_token); +- gboolean (*get_use_mobile_browser) (GoaOAuthProvider *provider); +- gchar **(*get_request_uri_params) (GoaOAuthProvider *provider); +- void (*add_account_key_values) (GoaOAuthProvider *provider, +- GVariantBuilder *builder); +- +- /* pure virtual */ +- gboolean (*is_identity_node) (GoaOAuthProvider *provider, +- WebKitDOMHTMLInputElement *element); +- +- /* virtual but with default implementation */ +- gboolean (*is_deny_node) (GoaOAuthProvider *provider, +- WebKitDOMNode *node); +- gboolean (*is_password_node) (GoaOAuthProvider *provider, +- WebKitDOMHTMLInputElement *element); +-}; +- +-const gchar *goa_oauth_provider_get_consumer_key (GoaOAuthProvider *provider); +-const gchar *goa_oauth_provider_get_consumer_secret (GoaOAuthProvider *provider); +-const gchar *goa_oauth_provider_get_request_uri (GoaOAuthProvider *provider); +-gchar **goa_oauth_provider_get_request_uri_params (GoaOAuthProvider *provider); +-const gchar *goa_oauth_provider_get_authorization_uri (GoaOAuthProvider *provider); +-const gchar *goa_oauth_provider_get_token_uri (GoaOAuthProvider *provider); +-const gchar *goa_oauth_provider_get_callback_uri (GoaOAuthProvider *provider); +-gchar *goa_oauth_provider_get_identity_sync (GoaOAuthProvider *provider, +- const gchar *access_token, +- const gchar *access_token_secret, +- gchar **out_presentation_identity, +- GCancellable *cancellable, +- GError **error); +-gboolean goa_oauth_provider_is_deny_node (GoaOAuthProvider *provider, +- WebKitDOMNode *node); +-gboolean goa_oauth_provider_is_identity_node (GoaOAuthProvider *provider, +- WebKitDOMHTMLInputElement *element); +-gboolean goa_oauth_provider_is_password_node (GoaOAuthProvider *provider, +- WebKitDOMHTMLInputElement *element); +-gchar *goa_oauth_provider_parse_request_token_error (GoaOAuthProvider *provider, +- RestProxyCall *call); +-gchar *goa_oauth_provider_get_access_token_sync (GoaOAuthProvider *provider, +- GoaObject *object, +- gboolean force_refresh, +- gchar **out_access_token_secret, +- gint *out_access_token_expires_in, +- GCancellable *cancellable, +- GError **error); +-gchar *goa_oauth_provider_build_authorization_uri (GoaOAuthProvider *provider, +- const gchar *authorization_uri, +- const gchar *escaped_oauth_token); +-gboolean goa_oauth_provider_get_use_mobile_browser (GoaOAuthProvider *provider); +-void goa_oauth_provider_add_account_key_values (GoaOAuthProvider *provider, +- GVariantBuilder *builder); +- +-G_END_DECLS +- +-#endif /* __GOA_OAUTH_PROVIDER_H__ */ +diff --git a/src/goabackend/goapocketprovider.c b/src/goabackend/goapocketprovider.c +index 38f9863..0d1a8ce 100644 +--- a/src/goabackend/goapocketprovider.c ++++ b/src/goabackend/goapocketprovider.c +@@ -185,32 +185,6 @@ build_authorization_uri (GoaOAuth2Provider *oauth2_provider, + return url; + } + +-static gboolean +-decide_navigation_policy (GoaOAuth2Provider *oauth2_provider, +- WebKitWebView *web_view, +- WebKitNavigationPolicyDecision *decision) +-{ +- GoaPocketProvider *self = GOA_POCKET_PROVIDER (oauth2_provider); +- WebKitNavigationAction *action; +- WebKitURIRequest *request; +- gboolean ret = FALSE; +- const gchar *uri; +- +- action = webkit_navigation_policy_decision_get_navigation_action (decision); +- request = webkit_navigation_action_get_request (action); +- uri = webkit_uri_request_get_uri (request); +- if (!g_str_has_prefix (uri, "https://getpocket.com/a/")) +- goto out; +- +- webkit_uri_request_set_uri (request, self->authorization_uri); +- webkit_web_view_load_request (web_view, request); +- +- ret = TRUE; +- +- out: +- return ret; +-} +- + static gboolean + process_redirect_url (GoaOAuth2Provider *oauth2_provider, + const gchar *redirect_url, +@@ -279,68 +253,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, + + /* ---------------------------------------------------------------------------------------------------- */ + +-static gboolean +-is_deny_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMNode *node) +-{ +- WebKitDOMElement *element; +- gboolean ret = FALSE; +- gchar *id = NULL; +- gchar *class = NULL; +- gchar *text = NULL; +- +- if (!WEBKIT_DOM_IS_ELEMENT (node)) +- goto out; +- +- element = WEBKIT_DOM_ELEMENT (node); +- +- /* Desktop version */ +- id = webkit_dom_element_get_id (element); +- if (g_strcmp0 (id, "denyButton") == 0) +- { +- ret = TRUE; +- goto out; +- } +- +- /* Mobile version */ +- class = webkit_dom_element_get_class_name (element); +- if (g_strcmp0 (class, "toolbarButton") != 0) +- goto out; +- +- /* FIXME: This only seems to work if we don't click on the "Sign Up" +- * button, does the check need to be done again? */ +- text = webkit_dom_node_get_text_content (node); +- if (g_strcmp0 (text, "Cancel") != 0) +- goto out; +- +- ret = TRUE; +- +- out: +- g_free (id); +- g_free (class); +- g_free (text); +- return ret; +-} +- +-static gboolean +-is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) +-{ +- gboolean ret = FALSE; +- gchar *name; +- +- name = webkit_dom_html_input_element_get_name (element); +- if (g_strcmp0 (name, "feed_id") != 0) +- goto out; +- +- ret = TRUE; +- +-out: +- g_free (name); +- return ret; +- +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- + static gboolean + build_object (GoaProvider *provider, + GoaObjectSkeleton *object, +@@ -431,15 +343,12 @@ goa_pocket_provider_class_init (GoaPocketProviderClass *klass) + provider_class->build_object = build_object; + + oauth2_class->build_authorization_uri = build_authorization_uri; +- oauth2_class->decide_navigation_policy = decide_navigation_policy; + oauth2_class->get_authorization_uri = get_authorization_uri; + oauth2_class->get_token_uri = get_token_uri; + oauth2_class->get_redirect_uri = get_redirect_uri; + oauth2_class->get_client_id = get_client_id; + oauth2_class->get_client_secret = get_client_secret; + oauth2_class->get_identity_sync = get_identity_sync; +- oauth2_class->is_deny_node = is_deny_node; +- oauth2_class->is_identity_node = is_identity_node; + oauth2_class->add_account_key_values = add_account_key_values; + oauth2_class->process_redirect_url = process_redirect_url; + } +diff --git a/src/goabackend/goaprovider.c b/src/goabackend/goaprovider.c +index c8f7f0d..fe8ce22 100644 +--- a/src/goabackend/goaprovider.c ++++ b/src/goabackend/goaprovider.c +@@ -28,7 +28,6 @@ + #include "goafacebookprovider.h" + #include "goaimapsmtpprovider.h" + #include "goaowncloudprovider.h" +-#include "goaflickrprovider.h" + #include "goafoursquareprovider.h" + #include "goawindowsliveprovider.h" + #include "goatelepathyfactory.h" +@@ -968,9 +967,6 @@ static struct + #ifdef GOA_WINDOWS_LIVE_ENABLED + { GOA_WINDOWS_LIVE_NAME, goa_windows_live_provider_get_type }, + #endif +-#ifdef GOA_FLICKR_ENABLED +- { GOA_FLICKR_NAME, goa_flickr_provider_get_type }, +-#endif + #ifdef GOA_POCKET_ENABLED + { GOA_POCKET_NAME, goa_pocket_provider_get_type }, + #endif +diff --git a/src/goabackend/goatodoistprovider.c b/src/goabackend/goatodoistprovider.c +index d97c33c..7f17a59 100644 +--- a/src/goabackend/goatodoistprovider.c ++++ b/src/goabackend/goatodoistprovider.c +@@ -133,33 +133,6 @@ build_authorization_uri (GoaOAuth2Provider *oauth2_provider, + + /* ---------------------------------------------------------------------------------------------------- */ + +-static gboolean +-is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) +-{ +- gboolean ret = FALSE; +- gchar *element_type = NULL; +- gchar *id = NULL; +- gchar *name = NULL; +- +- g_object_get (element, "type", &element_type, NULL); +- if (g_strcmp0 (element_type, "email") != 0) +- goto out; +- +- id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (element)); +- if (g_strcmp0 (id, "email") != 0) +- goto out; +- +- ret = TRUE; +- +- out: +- g_free (element_type); +- g_free (id); +- g_free (name); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- + static gchar * + get_identity_sync (GoaOAuth2Provider *oauth2_provider, + const gchar *access_token, +@@ -337,6 +310,5 @@ goa_todoist_provider_class_init (GoaTodoistProviderClass *klass) + oauth2_class->get_client_secret = get_client_secret; + oauth2_class->get_scope = get_scope; + oauth2_class->get_identity_sync = get_identity_sync; +- oauth2_class->is_identity_node = is_identity_node; + oauth2_class->add_account_key_values = add_account_key_values; + } +diff --git a/src/goabackend/goawebextension.c b/src/goabackend/goawebextension.c +deleted file mode 100644 +index 6a25ab9..0000000 +--- a/src/goabackend/goawebextension.c ++++ /dev/null +@@ -1,273 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +-/* +- * Copyright © 2015 Damián Nohales +- * Copyright © 2015 – 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 "goaoauthprovider.h" +-#include "goaoauth2provider.h" +-#include "goaoauth2provider-web-extension.h" +-#include "goaprovider.h" +-#include "goawebextension.h" +- +-struct _GoaWebExtension +-{ +- GObject parent; +- GoaProvider *provider; +- WebKitWebExtension *wk_extension; +- gchar *existing_identity; +- gchar *provider_type; +-}; +- +-struct _GoaWebExtensionClass +-{ +- GObjectClass parent; +-}; +- +-enum +-{ +- PROP_0, +- PROP_EXISTING_IDENTITY, +- PROP_PROVIDER_TYPE, +- PROP_WK_EXTENSION +-}; +- +-G_DEFINE_TYPE (GoaWebExtension, goa_web_extension, G_TYPE_OBJECT) +- +-static void +-web_extension_dom_node_deny_click_cb (WebKitDOMNode *element, WebKitDOMEvent *event, gpointer user_data) +-{ +- WebKitDOMDOMWindow *dom_window = WEBKIT_DOM_DOM_WINDOW (user_data); +- webkit_dom_dom_window_webkit_message_handlers_post_message (dom_window, "deny-click", ""); +-} +- +-static void +-web_extension_dom_node_password_submit_cb (WebKitDOMNode *element, WebKitDOMEvent *event, gpointer user_data) +-{ +- WebKitDOMDOMWindow *dom_window = WEBKIT_DOM_DOM_WINDOW (user_data); +- WebKitDOMHTMLInputElement *password_node; +- gchar *password; +- +- password_node = WEBKIT_DOM_HTML_INPUT_ELEMENT (g_object_get_data (G_OBJECT (dom_window), "goa-password-node")); +- password = webkit_dom_html_input_element_get_value (password_node); +- webkit_dom_dom_window_webkit_message_handlers_post_message (dom_window, "password-submit", password); +- g_free (password); +-} +- +-static void +-web_extension_document_loaded_cb (WebKitWebPage *web_page, gpointer user_data) +-{ +- GoaWebExtension *self = GOA_WEB_EXTENSION (user_data); +- WebKitDOMDocument *document; +- WebKitDOMDOMWindow *dom_window; +- WebKitDOMHTMLCollection *elements = NULL; +- gulong element_count; +- gulong i; +- +- document = webkit_web_page_get_dom_document (web_page); +- elements = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "*"); +- element_count = webkit_dom_html_collection_get_length (elements); +- +- dom_window = webkit_dom_document_get_default_view (document); +- +- for (i = 0; i < element_count; i++) +- { +- WebKitDOMNode *element = webkit_dom_html_collection_item (elements, i); +- +- if ((GOA_IS_OAUTH_PROVIDER (self->provider) +- && goa_oauth_provider_is_deny_node (GOA_OAUTH_PROVIDER (self->provider), element)) +- || (GOA_IS_OAUTH2_PROVIDER (self->provider) +- && goa_oauth2_provider_is_deny_node (GOA_OAUTH2_PROVIDER (self->provider), element))) +- { +- webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (element), +- "click", +- G_CALLBACK (web_extension_dom_node_deny_click_cb), +- FALSE, +- dom_window); +- } +- else if (self->existing_identity != NULL +- && self->existing_identity[0] != '\0' +- && WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element) +- && ((GOA_IS_OAUTH_PROVIDER (self->provider) +- && goa_oauth_provider_is_identity_node (GOA_OAUTH_PROVIDER (self->provider), +- WEBKIT_DOM_HTML_INPUT_ELEMENT (element))) +- || (GOA_IS_OAUTH2_PROVIDER (self->provider) +- && goa_oauth2_provider_is_identity_node (GOA_OAUTH2_PROVIDER (self->provider), +- WEBKIT_DOM_HTML_INPUT_ELEMENT (element))))) +- { +- webkit_dom_html_input_element_set_value (WEBKIT_DOM_HTML_INPUT_ELEMENT (element), +- self->existing_identity); +- webkit_dom_html_input_element_set_read_only (WEBKIT_DOM_HTML_INPUT_ELEMENT (element), TRUE); +- } +- else if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element) +- && ((GOA_IS_OAUTH_PROVIDER (self->provider) +- && goa_oauth_provider_is_password_node (GOA_OAUTH_PROVIDER (self->provider), +- WEBKIT_DOM_HTML_INPUT_ELEMENT (element))) +- || (GOA_IS_OAUTH2_PROVIDER (self->provider) +- && goa_oauth2_provider_is_password_node (GOA_OAUTH2_PROVIDER (self->provider), +- WEBKIT_DOM_HTML_INPUT_ELEMENT (element))))) +- { +- WebKitDOMHTMLFormElement *form; +- +- form = webkit_dom_html_input_element_get_form (WEBKIT_DOM_HTML_INPUT_ELEMENT (element)); +- if (form != NULL) +- { +- g_object_set_data_full (G_OBJECT (dom_window), +- "goa-password-node", +- g_object_ref (element), +- g_object_unref); +- webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (form), +- "submit", +- G_CALLBACK (web_extension_dom_node_password_submit_cb), +- FALSE, +- dom_window); +- } +- } +- } +- +- g_clear_object (&elements); +-} +- +-static void +-web_extension_page_created_cb (GoaWebExtension *self, WebKitWebPage *web_page) +-{ +- g_signal_connect_object (web_page, "document-loaded", G_CALLBACK (web_extension_document_loaded_cb), self, 0); +-} +- +-static void +-goa_web_extension_constructed (GObject *object) +-{ +- GoaWebExtension *self = GOA_WEB_EXTENSION (object); +- +- G_OBJECT_CLASS (goa_web_extension_parent_class)->constructed (object); +- +- self->provider = goa_provider_get_for_provider_type (self->provider_type); +- +- g_signal_connect_object (self->wk_extension, +- "page-created", +- G_CALLBACK (web_extension_page_created_cb), +- self, +- G_CONNECT_SWAPPED); +-} +- +-static void +-goa_web_extension_dispose (GObject *object) +-{ +- GoaWebExtension *self = GOA_WEB_EXTENSION (object); +- +- g_clear_object (&self->provider); +- g_clear_object (&self->wk_extension); +- +- G_OBJECT_CLASS (goa_web_extension_parent_class)->dispose (object); +-} +- +-static void +-goa_web_extension_finalize (GObject *object) +-{ +- GoaWebExtension *self = GOA_WEB_EXTENSION (object); +- +- g_free (self->existing_identity); +- g_free (self->provider_type); +- +- G_OBJECT_CLASS (goa_web_extension_parent_class)->finalize (object); +-} +- +-static void +-goa_web_extension_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +-{ +- GoaWebExtension *self = GOA_WEB_EXTENSION (object); +- +- switch (prop_id) +- { +- case PROP_EXISTING_IDENTITY: +- self->existing_identity = g_value_dup_string (value); +- break; +- +- case PROP_PROVIDER_TYPE: +- self->provider_type = g_value_dup_string (value); +- break; +- +- case PROP_WK_EXTENSION: +- self->wk_extension = WEBKIT_WEB_EXTENSION (g_value_dup_object (value)); +- break; +- +- default: +- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +- break; +- } +-} +- +-static void +-goa_web_extension_class_init (GoaWebExtensionClass *klass) +-{ +- GObjectClass *object_class = G_OBJECT_CLASS (klass); +- +- object_class->constructed = goa_web_extension_constructed; +- object_class->dispose = goa_web_extension_dispose; +- object_class->finalize = goa_web_extension_finalize; +- object_class->set_property = goa_web_extension_set_property; +- +- g_object_class_install_property (object_class, +- PROP_EXISTING_IDENTITY, +- g_param_spec_string ("existing-identity", +- "A GoaAccount identity", +- "The user name with which we want to prefill the form", +- NULL, +- G_PARAM_WRITABLE | +- G_PARAM_CONSTRUCT_ONLY | +- G_PARAM_STATIC_STRINGS)); +- +- g_object_class_install_property (object_class, +- PROP_PROVIDER_TYPE, +- g_param_spec_string ("provider-type", +- "A GoaProvider type", +- "The provider type that is represented by this view", +- NULL, +- G_PARAM_WRITABLE | +- G_PARAM_CONSTRUCT_ONLY | +- G_PARAM_STATIC_STRINGS)); +- +- g_object_class_install_property (object_class, +- PROP_WK_EXTENSION, +- g_param_spec_object ("wk-extension", +- "A WebKitWebExtension", +- "The associated WebKitWebExtension", +- WEBKIT_TYPE_WEB_EXTENSION, +- G_PARAM_WRITABLE | +- G_PARAM_CONSTRUCT_ONLY | +- G_PARAM_STATIC_STRINGS)); +-} +- +-static void +-goa_web_extension_init (GoaWebExtension *self) +-{ +-} +- +-GoaWebExtension * +-goa_web_extension_new (WebKitWebExtension *wk_extension, +- const gchar *provider_type, +- const gchar *existing_identity) +-{ +- return g_object_new (GOA_TYPE_WEB_EXTENSION, +- "existing-identity", existing_identity, +- "provider-type", provider_type, +- "wk-extension", wk_extension, +- NULL); +-} +diff --git a/src/goabackend/goawebextension.h b/src/goabackend/goawebextension.h +deleted file mode 100644 +index 4a8cf8f..0000000 +--- a/src/goabackend/goawebextension.h ++++ /dev/null +@@ -1,42 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +-/* +- * Copyright © 2015 Damián Nohales +- * Copyright © 2015 – 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 . +- */ +- +-#ifndef __GOA_WEB_EXTENSION_H__ +-#define __GOA_WEB_EXTENSION_H__ +- +-#include +-#include +- +-G_BEGIN_DECLS +- +-#define GOA_TYPE_WEB_EXTENSION (goa_web_extension_get_type()) +-#define GOA_WEB_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOA_TYPE_WEB_EXTENSION, GoaWebExtension)) +-#define GOA_IS_WEB_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOA_TYPE_WEB_EXTENSION)) +- +-typedef struct _GoaWebExtension GoaWebExtension; +-typedef struct _GoaWebExtensionClass GoaWebExtensionClass; +- +-GType goa_web_extension_get_type (void); +-GoaWebExtension *goa_web_extension_new (WebKitWebExtension *wk_extension, +- const gchar *provider_type, +- const gchar *existing_identity); +- +-G_END_DECLS +- +-#endif /* __GOA_WEB_EXTENSION_H__ */ +diff --git a/src/goabackend/goawebextensionmain.c b/src/goabackend/goawebextensionmain.c +deleted file mode 100644 +index 4fc91e5..0000000 +--- a/src/goabackend/goawebextensionmain.c ++++ /dev/null +@@ -1,53 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +-/* +- * Copyright © 2015 Damián Nohales +- * Copyright © 2015 – 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 +- +-#include "goawebextension.h" +- +-static GoaWebExtension *the_extension; +- +-/* Silence -Wmissing-prototypes */ +-void webkit_web_extension_initialize (WebKitWebExtension *wk_extension); +-void webkit_web_extension_initialize_with_user_data (WebKitWebExtension *wk_extension, GVariant *user_data); +- +-G_MODULE_EXPORT void +-webkit_web_extension_initialize (WebKitWebExtension *wk_extension) +-{ +- g_warning ("Error initializing web extension: user data not set"); +-} +- +-G_MODULE_EXPORT void +-webkit_web_extension_initialize_with_user_data (WebKitWebExtension *wk_extension, GVariant *user_data) +-{ +- const gchar *existing_identity; +- const gchar *provider_type; +- +- g_variant_get (user_data, "(&s&s)", &provider_type, &existing_identity); +- the_extension = goa_web_extension_new (wk_extension, provider_type, existing_identity); +-} +- +-static void __attribute__((destructor)) +-goa_web_extension_shutdown (void) +-{ +- g_clear_object (&the_extension); +-} +diff --git a/src/goabackend/goawebview.c b/src/goabackend/goawebview.c +deleted file mode 100644 +index b7af122..0000000 +--- a/src/goabackend/goawebview.c ++++ /dev/null +@@ -1,501 +0,0 @@ +-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- +- * +- * Copyright © 2015 Damián Nohales +- * 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 . +- */ +- +-/* Based on code by the Epiphany team. +- */ +- +-#include "config.h" +- +-#include +-#include +-#include +-#include +-#include +- +-#include "goawebview.h" +-#include "nautilus-floating-bar.h" +- +-struct _GoaWebView +-{ +- GtkOverlay parent_instance; +- GoaProvider *provider; +- GtkWidget *floating_bar; +- GtkWidget *progress_bar; +- GtkWidget *web_view; +- WebKitUserContentManager *user_content_manager; +- WebKitWebContext *context; +- gchar *existing_identity; +- gulong clear_notify_progress_id; +- gulong notify_load_status_id; +- gulong notify_progress_id; +-}; +- +-struct _GoaWebViewClass +-{ +- GtkOverlayClass parent_class; +-}; +- +-enum +-{ +- PROP_0, +- PROP_EXISTING_IDENTITY, +- PROP_PROVIDER +-}; +- +-enum +-{ +- DENY_CLICK, +- PASSWORD_SUBMIT, +- LAST_SIGNAL +-}; +- +-static guint signals[LAST_SIGNAL] = { 0 }; +- +-G_DEFINE_TYPE (GoaWebView, goa_web_view, GTK_TYPE_OVERLAY) +- +-static gboolean +-web_view_clear_notify_progress_cb (gpointer user_data) +-{ +- GoaWebView *self = GOA_WEB_VIEW (user_data); +- +- gtk_widget_hide (self->progress_bar); +- self->clear_notify_progress_id = 0; +- return FALSE; +-} +- +-static char * +-web_view_create_loading_title (const gchar *url) +-{ +- SoupURI *uri; +- const gchar *hostname; +- gchar *title; +- +- g_return_val_if_fail (url != NULL && url[0] != '\0', NULL); +- +- uri = soup_uri_new (url); +- hostname = soup_uri_get_host (uri); +- /* translators: %s here is the address of the web page */ +- title = g_strdup_printf (_("Loading “%s”…"), hostname); +- soup_uri_free (uri); +- +- return title; +-} +- +-static void +-web_view_floating_bar_update (GoaWebView *self, const gchar *text) +-{ +- nautilus_floating_bar_set_label (NAUTILUS_FLOATING_BAR (self->floating_bar), text); +- +- if (text == NULL || text[0] == '\0') +- { +- gtk_widget_hide (self->floating_bar); +- gtk_widget_set_halign (self->floating_bar, GTK_ALIGN_START); +- } +- else +- gtk_widget_show (self->floating_bar); +-} +- +-static void +-web_view_initialize_web_extensions_cb (GoaWebView *self) +-{ +- GVariant *data; +- const gchar *existing_identity; +- const gchar *provider_type; +- +- webkit_web_context_set_web_extensions_directory (self->context, PACKAGE_WEB_EXTENSIONS_DIR); +- +- if (self->provider == NULL) +- return; +- +- provider_type = goa_provider_get_provider_type (self->provider); +- existing_identity = (self->existing_identity == NULL) ? "" : self->existing_identity; +- data = g_variant_new ("(ss)", provider_type, existing_identity); +- webkit_web_context_set_web_extensions_initialization_user_data (self->context, data); +-} +- +-#ifdef GOA_INSPECTOR_ENABLED +-static void +-web_view_inspector_closed_cb (WebKitWebInspector *inspector) +-{ +- GtkWidget *window; +- WebKitWebViewBase *inspector_web_view; +- +- inspector_web_view = webkit_web_inspector_get_web_view (inspector); +- window = gtk_widget_get_toplevel (GTK_WIDGET (inspector_web_view)); +- if (gtk_widget_is_toplevel (window)) +- gtk_widget_destroy (window); +-} +- +-static gboolean +-web_view_inspector_open_window_cb (WebKitWebInspector *inspector) +-{ +- GtkWidget *window; +- GtkWindowGroup *group; +- WebKitWebViewBase *inspector_web_view; +- +- group = gtk_window_group_new (); +- +- window = gtk_window_new (GTK_WINDOW_TOPLEVEL); +- gtk_window_resize (GTK_WINDOW (window), 800, 600); +- gtk_window_group_add_window (group, GTK_WINDOW (window)); +- g_object_unref (group); +- +- inspector_web_view = webkit_web_inspector_get_web_view (inspector); +- gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (inspector_web_view)); +- +- gtk_widget_show_all (window); +- gtk_window_present (GTK_WINDOW (window)); +- +- return GDK_EVENT_STOP; +-} +-#endif /* GOA_INSPECTOR_ENABLED */ +- +-static void +-web_view_load_changed_cb (WebKitWebView *web_view, +- WebKitLoadEvent load_event, +- gpointer user_data) +-{ +- GoaWebView *self = GOA_WEB_VIEW (user_data); +- +- switch (load_event) +- { +- case WEBKIT_LOAD_STARTED: +- case WEBKIT_LOAD_COMMITTED: +- { +- const gchar *uri; +- gchar *title; +- +- uri = webkit_web_view_get_uri (web_view); +- title = web_view_create_loading_title (uri); +- +- web_view_floating_bar_update (self, title); +- g_free (title); +- break; +- } +- +- case WEBKIT_LOAD_REDIRECTED: +- /* TODO: Update the loading uri */ +- break; +- +- case WEBKIT_LOAD_FINISHED: +- web_view_floating_bar_update (self, NULL); +- break; +- +- default: +- break; +- } +-} +- +-static void +-web_view_notify_estimated_load_progress_cb (GObject *object, +- GParamSpec *pspec, +- gpointer user_data) +-{ +- GoaWebView *self = GOA_WEB_VIEW (user_data); +- WebKitWebView *web_view = WEBKIT_WEB_VIEW (object); +- gboolean loading; +- const gchar *uri; +- gdouble progress; +- +- if (self->clear_notify_progress_id != 0) +- { +- g_source_remove (self->clear_notify_progress_id); +- self->clear_notify_progress_id = 0; +- } +- +- uri = webkit_web_view_get_uri (web_view); +- if (!uri || g_str_equal (uri, "about:blank")) +- return; +- +- progress = webkit_web_view_get_estimated_load_progress (web_view); +- loading = webkit_web_view_is_loading (web_view); +- +- if (progress == 1.0 || !loading) +- self->clear_notify_progress_id = g_timeout_add (500, web_view_clear_notify_progress_cb, self); +- else +- gtk_widget_show (self->progress_bar); +- +- gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->progress_bar), +- (loading || progress == 1.0) ? progress : 0.0); +-} +- +-static void +-web_view_script_message_received_deny_click_cb (GoaWebView *self) +-{ +- g_signal_emit (self, signals[DENY_CLICK], 0); +-} +- +-static void +-web_view_script_message_received_password_submit_cb (GoaWebView *self, WebKitJavascriptResult *js_result) +-{ +- JSGlobalContextRef js_context; +- JSStringRef js_string; +- JSValueRef js_value; +- gsize max_size; +- +- js_value = webkit_javascript_result_get_value (js_result); +- js_context = webkit_javascript_result_get_global_context (js_result); +- js_string = JSValueToStringCopy (js_context, js_value, NULL); +- max_size = JSStringGetMaximumUTF8CStringSize (js_string); +- if (max_size > 0) +- { +- gchar *password; +- +- password = g_malloc0 (max_size); +- JSStringGetUTF8CString (js_string, password, max_size); +- g_signal_emit (self, signals[PASSWORD_SUBMIT], 0, password); +- g_free (password); +- } +- +- JSStringRelease (js_string); +-} +- +-static void +-goa_web_view_constructed (GObject *object) +-{ +- GoaWebView *self = GOA_WEB_VIEW (object); +- const gchar *const *language_names; +- +- G_OBJECT_CLASS (goa_web_view_parent_class)->constructed (object); +- +- self->context = webkit_web_context_new (); +- language_names = g_get_language_names (); +- webkit_web_context_set_preferred_languages (self->context, language_names); +- g_signal_connect_swapped (self->context, +- "initialize-web-extensions", +- G_CALLBACK (web_view_initialize_web_extensions_cb), +- self); +- +- self->user_content_manager = webkit_user_content_manager_new (); +- g_signal_connect_swapped (self->user_content_manager, +- "script-message-received::deny-click", +- G_CALLBACK (web_view_script_message_received_deny_click_cb), +- self); +- g_signal_connect_swapped (self->user_content_manager, +- "script-message-received::password-submit", +- G_CALLBACK (web_view_script_message_received_password_submit_cb), +- self); +- webkit_user_content_manager_register_script_message_handler (self->user_content_manager, "deny-click"); +- webkit_user_content_manager_register_script_message_handler (self->user_content_manager, "password-submit"); +- +- self->web_view = GTK_WIDGET (g_object_new (WEBKIT_TYPE_WEB_VIEW, +- "user-content-manager", self->user_content_manager, +- "web-context", self->context, +- NULL)); +- gtk_widget_set_size_request (self->web_view, 500, 400); +- gtk_container_add (GTK_CONTAINER (self), self->web_view); +- +-#ifdef GOA_INSPECTOR_ENABLED +- { +- WebKitSettings *settings; +- WebKitWebInspector *inspector; +- +- /* Setup the inspector */ +- settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (self->web_view)); +- g_object_set (settings, "enable-developer-extras", TRUE, NULL); +- +- inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (self->web_view)); +- g_signal_connect (inspector, "closed", G_CALLBACK (web_view_inspector_closed_cb), NULL); +- g_signal_connect (inspector, "open-window", G_CALLBACK (web_view_inspector_open_window_cb), NULL); +- } +-#endif /* GOA_INSPECTOR_ENABLED */ +- +- /* statusbar is hidden by default */ +- self->floating_bar = nautilus_floating_bar_new (NULL, FALSE); +- gtk_widget_set_halign (self->floating_bar, GTK_ALIGN_START); +- gtk_widget_set_valign (self->floating_bar, GTK_ALIGN_END); +- gtk_widget_set_no_show_all (self->floating_bar, TRUE); +- gtk_overlay_add_overlay (GTK_OVERLAY (self), self->floating_bar); +- +- self->progress_bar = gtk_progress_bar_new (); +- gtk_style_context_add_class (gtk_widget_get_style_context (self->progress_bar), +- GTK_STYLE_CLASS_OSD); +- gtk_widget_set_halign (self->progress_bar, GTK_ALIGN_FILL); +- gtk_widget_set_valign (self->progress_bar, GTK_ALIGN_START); +- gtk_overlay_add_overlay (GTK_OVERLAY (self), self->progress_bar); +- +- self->notify_progress_id = g_signal_connect (self->web_view, +- "notify::estimated-load-progress", +- G_CALLBACK (web_view_notify_estimated_load_progress_cb), +- self); +- self->notify_load_status_id = g_signal_connect (self->web_view, +- "load_changed", +- G_CALLBACK (web_view_load_changed_cb), +- self); +-} +- +-static void +-goa_web_view_dispose (GObject *object) +-{ +- GoaWebView *self = GOA_WEB_VIEW (object); +- +- g_clear_object (&self->user_content_manager); +- g_clear_object (&self->context); +- +- if (self->clear_notify_progress_id != 0) +- { +- g_source_remove (self->clear_notify_progress_id); +- self->clear_notify_progress_id = 0; +- } +- +- if (self->notify_load_status_id != 0) +- { +- g_signal_handler_disconnect (self->web_view, self->notify_load_status_id); +- self->notify_load_status_id = 0; +- } +- +- if (self->notify_progress_id != 0) +- { +- g_signal_handler_disconnect (self->web_view, self->notify_progress_id); +- self->notify_progress_id = 0; +- } +- +- G_OBJECT_CLASS (goa_web_view_parent_class)->dispose (object); +-} +- +-static void +-goa_web_view_finalize (GObject *object) +-{ +- GoaWebView *self = GOA_WEB_VIEW (object); +- +- g_free (self->existing_identity); +- +- if (self->provider != NULL) +- g_object_remove_weak_pointer (G_OBJECT (self->provider), (gpointer *) &self->provider); +- +- G_OBJECT_CLASS (goa_web_view_parent_class)->finalize (object); +-} +- +-static void +-goa_web_view_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +-{ +- GoaWebView *self = GOA_WEB_VIEW (object); +- +- switch (prop_id) +- { +- case PROP_EXISTING_IDENTITY: +- self->existing_identity = g_value_dup_string (value); +- break; +- +- case PROP_PROVIDER: +- self->provider = GOA_PROVIDER (g_value_get_object (value)); +- if (self->provider != NULL) +- g_object_add_weak_pointer (G_OBJECT (self->provider), (gpointer *) &self->provider); +- break; +- +- default: +- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +- break; +- } +-} +- +-static void +-goa_web_view_init (GoaWebView *self) +-{ +-} +- +-static void +-goa_web_view_class_init (GoaWebViewClass *klass) +-{ +- GObjectClass *object_class; +- +- object_class = G_OBJECT_CLASS (klass); +- object_class->constructed = goa_web_view_constructed; +- object_class->dispose = goa_web_view_dispose; +- object_class->finalize = goa_web_view_finalize; +- object_class->set_property = goa_web_view_set_property; +- +- g_object_class_install_property (object_class, +- PROP_EXISTING_IDENTITY, +- g_param_spec_string ("existing-identity", +- "A GoaAccount identity", +- "The user name with which we want to prefill the form", +- NULL, +- G_PARAM_WRITABLE | +- G_PARAM_CONSTRUCT_ONLY | +- G_PARAM_STATIC_STRINGS)); +- +- g_object_class_install_property (object_class, +- PROP_PROVIDER, +- g_param_spec_object ("provider", +- "A GoaProvider", +- "The provider that is represented by this view", +- GOA_TYPE_PROVIDER, +- G_PARAM_WRITABLE | +- G_PARAM_CONSTRUCT_ONLY | +- G_PARAM_STATIC_STRINGS)); +- +- signals[DENY_CLICK] = g_signal_new ("deny-click", +- G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, +- 0, +- NULL, +- NULL, +- g_cclosure_marshal_VOID__VOID, +- G_TYPE_NONE, +- 0); +- +- signals[PASSWORD_SUBMIT] = g_signal_new ("password-submit", +- G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, +- 0, +- NULL, +- NULL, +- g_cclosure_marshal_VOID__STRING, +- G_TYPE_NONE, +- 1, +- G_TYPE_STRING); +-} +- +-GtkWidget * +-goa_web_view_new (GoaProvider *provider, const gchar *existing_identity) +-{ +- return g_object_new (GOA_TYPE_WEB_VIEW, "provider", provider, "existing-identity", existing_identity, NULL); +-} +- +-GtkWidget * +-goa_web_view_get_view (GoaWebView *self) +-{ +- return self->web_view; +-} +- +-void +-goa_web_view_fake_mobile (GoaWebView *self) +-{ +- WebKitSettings *settings; +- +- settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (self->web_view)); +- +- /* This is based on the HTC Wildfire's user agent. Some +- * providers, like Google, refuse to provide the mobile +- * version of their authentication pages otherwise. eg., +- * in Google's case, passing btmpl=mobile does not help. +- * +- * The actual user agent used by a HTC Wildfire is: +- * Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; HTC Wildfire +- * Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 +- * Mobile Safari/533.1 +- * +- * Also note that the user agents of some mobile browsers may +- * not work. eg., Nokia N9. +- */ +- webkit_settings_set_user_agent (settings, +- "Mozilla/5.0 (GNOME; not Android) " +- "AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile"); +-} +diff --git a/src/goabackend/goawebview.h b/src/goabackend/goawebview.h +deleted file mode 100644 +index 4b415cd..0000000 +--- a/src/goabackend/goawebview.h ++++ /dev/null +@@ -1,43 +0,0 @@ +-/* -*- 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 . +- */ +- +-#ifndef __GOA_WEB_VIEW_H__ +-#define __GOA_WEB_VIEW_H__ +- +-#include +- +-#include "goaprovider.h" +- +-G_BEGIN_DECLS +- +-#define GOA_TYPE_WEB_VIEW (goa_web_view_get_type ()) +-#define GOA_WEB_VIEW(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GOA_TYPE_WEB_VIEW, GoaWebView)) +-#define GOA_IS_WEB_VIEW(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GOA_TYPE_WEB_VIEW)) +- +-typedef struct _GoaWebView GoaWebView; +-typedef struct _GoaWebViewClass GoaWebViewClass; +- +-GType goa_web_view_get_type (void) G_GNUC_CONST; +-GtkWidget *goa_web_view_new (GoaProvider *provider, +- const gchar *existing_identity); +-GtkWidget *goa_web_view_get_view (GoaWebView *self); +-void goa_web_view_fake_mobile (GoaWebView *self); +- +-G_END_DECLS +- +-#endif /* __GOA_WEB_VIEW_H__ */ +diff --git a/src/goabackend/goawindowsliveprovider.c b/src/goabackend/goawindowsliveprovider.c +index 10c2dcf..2135fbd 100644 +--- a/src/goabackend/goawindowsliveprovider.c ++++ b/src/goabackend/goawindowsliveprovider.c +@@ -94,7 +94,8 @@ get_token_uri (GoaOAuth2Provider *oauth2_provider) + static const gchar * + get_redirect_uri (GoaOAuth2Provider *oauth2_provider) + { +- return "https://login.live.com/oauth20_desktop.srf"; ++ /* See: https://learn.microsoft.com/en-us/entra/identity-platform/reply-url */ ++ return "goa-oauth2://localhost/"GOA_WINDOWS_LIVE_CLIENT_ID; + } + + static const gchar * +@@ -234,36 +235,6 @@ get_identity_sync (GoaOAuth2Provider *oauth2_provider, + + /* ---------------------------------------------------------------------------------------------------- */ + +-static gboolean +-is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element) +-{ +- gboolean ret = FALSE; +- gchar *element_type = NULL; +- gchar *name = NULL; +- +- /* FIXME: This does not show up in +- * webkit_dom_document_get_elements_by_tag_name, but can be +- * seen in the inspector. Needs further investigation. +- */ +- +- g_object_get (element, "type", &element_type, NULL); +- if (g_strcmp0 (element_type, "email") != 0) +- goto out; +- +- name = webkit_dom_html_input_element_get_name (element); +- if (g_strcmp0 (name, "login") != 0) +- goto out; +- +- ret = TRUE; +- +- out: +- g_free (element_type); +- g_free (name); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- + static gboolean + build_object (GoaProvider *provider, + GoaObjectSkeleton *object, +@@ -390,6 +361,5 @@ goa_windows_live_provider_class_init (GoaWindowsLiveProviderClass *klass) + oauth2_class->get_client_id = get_client_id; + oauth2_class->get_client_secret = get_client_secret; + oauth2_class->get_identity_sync = get_identity_sync; +- oauth2_class->is_identity_node = is_identity_node; + oauth2_class->add_account_key_values = add_account_key_values; + } diff --git a/SPECS/gnome-online-accounts.spec b/SPECS/gnome-online-accounts.spec index 291f7b7..a7d2d7c 100644 --- a/SPECS/gnome-online-accounts.spec +++ b/SPECS/gnome-online-accounts.spec @@ -2,11 +2,10 @@ %global glib2_version 2.52 %global gtk3_version 3.19.12 %global libsoup_version 2.42 -%global webkit2gtk3_version 2.12.0 Name: gnome-online-accounts Version: 3.28.2 -Release: 4%{?dist} +Release: 7%{?dist} Summary: Single sign-on framework for GNOME License: LGPLv2+ @@ -14,6 +13,9 @@ URL: https://wiki.gnome.org/Projects/GnomeOnlineAccounts Source0: https://download.gnome.org/sources/gnome-online-accounts/3.28/%{name}-%{version}.tar.xz Patch01: 0001-mute-debug-prints.patch +Patch02: 0002-Drop-dependency-on-WebKitGTK-139.patch + +Obsoletes: gnome-online-accounts-oauth2 < 3.28.2-6 BuildRequires: pkgconfig(gcr-3) BuildRequires: pkgconfig(gio-2.0) >= %{glib2_version} @@ -24,7 +26,6 @@ BuildRequires: pkgconfig(gobject-introspection-1.0) BuildRequires: gettext >= %{gettext_version} BuildRequires: gtk-doc BuildRequires: krb5-devel -BuildRequires: pkgconfig(webkit2gtk-4.0) >= %{webkit2gtk3_version} BuildRequires: pkgconfig(json-glib-1.0) BuildRequires: pkgconfig(libsecret-1) >= 0.7 BuildRequires: pkgconfig(libsoup-2.4) >= %{libsoup_version} @@ -34,16 +35,17 @@ BuildRequires: pkgconfig(telepathy-glib) %endif BuildRequires: pkgconfig(libxml-2.0) BuildRequires: vala +BuildRequires: autoconf +BuildRequires: automake Requires: glib2%{?_isa} >= %{glib2_version} Requires: gtk3%{?_isa} >= %{gtk3_version} Requires: libsoup%{?_isa} >= %{libsoup_version} -Requires: webkit2gtk3%{?_isa} >= %{webkit2gtk3_version} %description GNOME Online Accounts provides interfaces so that applications and libraries in GNOME can access the user's online accounts. It has providers for Google, -ownCloud, Facebook, Flickr, Foursquare, Microsoft Account, Pocket, Microsoft +ownCloud, Facebook, Foursquare, Microsoft Account, Pocket, Microsoft Exchange, IMAP/SMTP and Kerberos. %package devel @@ -57,10 +59,19 @@ developing applications that use %{name}. %prep %setup -q %patch01 -p1 -b .mute-debug-prints +%patch02 -p1 -b .no-webkitgtk %build +aclocal -I m4 +autoheader +automake --add-missing +libtoolize +#intltoolize --force +autoconf + %configure \ --disable-facebook \ + --disable-flickr \ --disable-foursquare \ --disable-lastfm \ --disable-media-server \ @@ -69,7 +80,6 @@ developing applications that use %{name}. --disable-telepathy \ --disable-todoist \ --enable-exchange \ - --enable-flickr \ --enable-google \ --enable-gtk-doc \ --enable-imap-smtp \ @@ -106,10 +116,10 @@ find $RPM_BUILD_ROOT -name '*.la' -delete %{_libdir}/libgoa-backend-1.0.so.1 %{_libdir}/libgoa-backend-1.0.so.1.0.0 %dir %{_libdir}/goa-1.0 -%dir %{_libdir}/goa-1.0/web-extensions -%{_libdir}/goa-1.0/web-extensions/libgoawebextension.so %{_prefix}/libexec/goa-daemon %{_prefix}/libexec/goa-identity-service +%{_prefix}/libexec/goa-oauth2-handler +%{_datadir}/applications/org.gnome.OnlineAccounts.OAuth2.desktop %{_datadir}/dbus-1/services/org.gnome.OnlineAccounts.service %{_datadir}/dbus-1/services/org.gnome.Identity.service %{_datadir}/icons/hicolor/*/apps/goa-*.png @@ -137,6 +147,16 @@ find $RPM_BUILD_ROOT -name '*.la' -delete %{_datadir}/vala/ %changelog +* Wed Nov 15 2023 Milan Crha - 3.28.2-7 +- Related: RHEL-10493 (Add margin around OAuth2 prompt content) + +* Thu Nov 09 2023 Milan Crha - 3.28.2-6 +- Resolves: RHEL-10493 (Move account types that depend on WebKitGTK into separate optional subpackage) +- backport upstream fix to use external browser for OAuth2 + +* Wed Oct 11 2023 Milan Crha - 3.28.2-5 +- Resolves: RHEL-10493 (Move account types that depend on WebKitGTK into separate optional subpackage) + * Fri Sep 02 2022 Milan Crha - 3.28.2-4 - Resolves: #2068010 (Turn runtime warnings around libsecret into debug prints)