summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala111
-rw-r--r--plugins/authenticator/shotwell/GoogleAuthenticator.vala122
-rw-r--r--plugins/authenticator/shotwell/meson.build2
-rw-r--r--plugins/common/WebAuthenticationPane.vala129
-rw-r--r--plugins/meson.build2
-rw-r--r--plugins/shotwell-publishing/meson.build2
-rw-r--r--plugins/shotwell-transitions/meson.build2
7 files changed, 96 insertions, 274 deletions
diff --git a/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala b/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala
index 23de183..e381ae9 100644
--- a/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala
+++ b/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala
@@ -18,86 +18,26 @@ namespace Publishing.Authenticator.Shotwell.Flickr {
internal const string SERVICE_DISCLAIMER = "<b>This product uses the Flickr API but is not endorsed or certified by SmugMug, Inc.</b>";
internal class AuthenticationRequestTransaction : Publishing.RESTSupport.OAuth1.Transaction {
- public AuthenticationRequestTransaction(Publishing.RESTSupport.OAuth1.Session session) {
+ public AuthenticationRequestTransaction(Publishing.RESTSupport.OAuth1.Session session, string cookie) {
base.with_uri(session, "https://www.flickr.com/services/oauth/request_token",
Publishing.RESTSupport.HttpMethod.GET);
- add_argument("oauth_callback", "shotwell-auth://local-callback");
+ add_argument("oauth_callback", "shotwell-oauth2://localhost?sw_auth_cookie=%s".printf(cookie));
}
}
internal class AccessTokenFetchTransaction : Publishing.RESTSupport.OAuth1.Transaction {
- public AccessTokenFetchTransaction(Publishing.RESTSupport.OAuth1.Session session, string user_verifier) {
+ public AccessTokenFetchTransaction(Publishing.RESTSupport.OAuth1.Session session, string user_verifier, string cookie) {
base.with_uri(session, "https://www.flickr.com/services/oauth/access_token",
Publishing.RESTSupport.HttpMethod.GET);
add_argument("oauth_verifier", user_verifier);
add_argument("oauth_token", session.get_request_phase_token());
- add_argument("oauth_callback", "shotwell-auth://local-callback");
- }
- }
-
- internal class WebAuthenticationPane : Common.WebAuthenticationPane {
- private string? auth_code = null;
- private const string LOGIN_URI = "https://www.flickr.com/services/oauth/authorize?oauth_token=%s&perms=write";
-
- public signal void authorized(string auth_code);
- public signal void error();
-
- public WebAuthenticationPane(string token) {
- Object(login_uri : LOGIN_URI.printf(token));
- }
-
- public override void constructed() {
- base.constructed();
-
- var ctx = WebKit.WebContext.get_default();
- ctx.register_uri_scheme("shotwell-auth", this.on_shotwell_auth_request_cb);
-
- var mgr = ctx.get_security_manager();
- mgr.register_uri_scheme_as_secure("shotwell-auth");
- mgr.register_uri_scheme_as_cors_enabled("shotwell-auth");
- }
-
- public override void on_page_load() {
- if (this.load_error != null) {
- this.error();
-
- return;
- }
-
- try {
- var uri = GLib.Uri.parse(get_view().get_uri(), GLib.UriFlags.NONE);
- if (uri.get_scheme() == "shotwell-auth" && this.auth_code == null) {
- var form_data = Soup.Form.decode (uri.get_query());
- this.auth_code = form_data.lookup("oauth_verifier");
- }
- } catch (Error err) {
- this.error();
-
- return;
- }
-
- if (this.auth_code != null) {
- this.authorized(this.auth_code);
- }
- }
-
- private void on_shotwell_auth_request_cb(WebKit.URISchemeRequest request) {
- try {
- var uri = GLib.Uri.parse(request.get_uri(), GLib.UriFlags.NONE);
- var form_data = Soup.Form.decode (uri.get_query());
- this.auth_code = form_data.lookup("oauth_verifier");
- } catch (Error err) {
- debug ("Failed to parse URI %s: %s", request.get_uri(), err.message);
- }
-
- var response = "";
- var mins = new MemoryInputStream.from_data(response.data);
- request.finish(mins, -1, "text/plain");
+ add_argument("oauth_callback", "shotwell-oauth2://localhost?sw_auth_cookie=%s".printf(cookie));
}
}
internal class Flickr : Publishing.Authenticator.Shotwell.OAuth1.Authenticator {
- private WebAuthenticationPane pane;
+ private Common.ExternalWebPane pane;
+ private string auth_cookie = Uuid.string_random();
public Flickr(Spit.Publishing.PluginHost host) {
base("Flickr", API_KEY, API_SECRET, host);
@@ -147,7 +87,7 @@ namespace Publishing.Authenticator.Shotwell.Flickr {
host.set_service_locked(true);
host.install_static_message_pane(_("Preparing for login…"));
- AuthenticationRequestTransaction txn = new AuthenticationRequestTransaction(session);
+ AuthenticationRequestTransaction txn = new AuthenticationRequestTransaction(session, auth_cookie);
try {
yield txn.execute_async();
debug("EVENT: OAuth authentication request transaction completed; response = '%s'",
@@ -185,22 +125,33 @@ namespace Publishing.Authenticator.Shotwell.Flickr {
session.set_request_phase_credentials(token, token_secret);
- do_web_authentication(token);
+ do_web_authentication.begin(token);
}
- private void do_web_authentication(string token) {
- pane = new WebAuthenticationPane(token);
- host.install_dialog_pane(pane);
- pane.authorized.connect((pin) => { this.do_verify_pin.begin(pin); });
- pane.error.connect(this.on_web_login_error);
- }
+ private class AuthCallback : Spit.Publishing.AuthenticatedCallback, Object {
+ public signal void auth(GLib.HashTable<string, string> params);
- private void on_web_login_error() {
- if (pane.load_error != null) {
- host.post_error(pane.load_error);
- return;
+ public void authenticated(GLib.HashTable<string, string> params) {
+ auth(params);
}
- host.post_error(new Spit.Publishing.PublishingError.PROTOCOL_ERROR(_("Flickr authorization failed")));
+ }
+
+ private async void do_web_authentication(string token) {
+ var uri = "https://www.flickr.com/services/oauth/authorize?oauth_token=%s&perms=write".printf(token);
+ pane = new Common.ExternalWebPane(uri);
+ host.install_dialog_pane(pane);
+ var auth_callback = new AuthCallback();
+ string? web_auth_code = null;
+ auth_callback.auth.connect((prm) => {
+ if ("oauth_verifier" in prm) {
+ web_auth_code = prm["oauth_verifier"];
+ }
+ do_web_authentication.callback();
+ });
+ host.register_auth_callback(auth_cookie, auth_callback);
+ yield;
+ host.unregister_auth_callback(auth_cookie);
+ yield do_verify_pin(web_auth_code);
}
private async void do_verify_pin(string pin) {
@@ -209,7 +160,7 @@ namespace Publishing.Authenticator.Shotwell.Flickr {
host.set_service_locked(true);
host.install_static_message_pane(_("Verifying authorization…"));
- AccessTokenFetchTransaction txn = new AccessTokenFetchTransaction(session, pin);
+ AccessTokenFetchTransaction txn = new AccessTokenFetchTransaction(session, pin, auth_cookie);
try {
yield txn.execute_async();
diff --git a/plugins/authenticator/shotwell/GoogleAuthenticator.vala b/plugins/authenticator/shotwell/GoogleAuthenticator.vala
index 9fc5b27..5a0d934 100644
--- a/plugins/authenticator/shotwell/GoogleAuthenticator.vala
+++ b/plugins/authenticator/shotwell/GoogleAuthenticator.vala
@@ -5,73 +5,11 @@ namespace Publishing.Authenticator.Shotwell.Google {
private const string OAUTH_CLIENT_ID = "534227538559-hvj2e8bj0vfv2f49r7gvjoq6jibfav67.apps.googleusercontent.com";
private const string REVERSE_CLIENT_ID = "com.googleusercontent.apps.534227538559-hvj2e8bj0vfv2f49r7gvjoq6jibfav67";
private const string OAUTH_CLIENT_SECRET = "pwpzZ7W1TCcD5uIfYCu8sM7x";
- private const string OAUTH_CALLBACK_URI = REVERSE_CLIENT_ID + ":/auth-callback";
+ private const string OAUTH_CALLBACK_URI = REVERSE_CLIENT_ID + ":/localhost";
private const string SCHEMA_KEY_PROFILE_ID = "shotwell-profile-id";
private const string SCHEMA_KEY_ACCOUNTNAME = "accountname";
- private class WebAuthenticationPane : Common.WebAuthenticationPane {
- public static bool cache_dirty = false;
- private string? auth_code = null;
-
- public signal void error();
-
- public override void constructed() {
- base.constructed();
-
- var ctx = WebKit.WebContext.get_default();
- ctx.register_uri_scheme(REVERSE_CLIENT_ID, this.on_shotwell_auth_request_cb);
- }
-
- public override void on_page_load() {
- if (this.load_error != null) {
- this.error ();
-
- return;
- }
-
- try {
- var uri = GLib.Uri.parse(get_view().get_uri(), UriFlags.NONE);
- if (uri.get_scheme() == REVERSE_CLIENT_ID && this.auth_code == null) {
- var form_data = Soup.Form.decode (uri.get_query());
- this.auth_code = form_data.lookup("code");
- }
- } catch (Error err) {
- debug ("Failed to parse auth code from URI %s: %s", get_view().get_uri(),
- err.message);
- }
-
- if (this.auth_code != null) {
- this.authorized(this.auth_code);
- }
- }
-
- private void on_shotwell_auth_request_cb(WebKit.URISchemeRequest request) {
- try {
- var uri = GLib.Uri.parse(request.get_uri(), GLib.UriFlags.NONE);
- debug("URI: %s", request.get_uri());
- var form_data = Soup.Form.decode (uri.get_query());
- this.auth_code = form_data.lookup("code");
- } catch (Error err) {
- debug("Failed to parse request URI: %s", err.message);
- }
-
- var response = "";
- var mins = new MemoryInputStream.from_data(response.data);
- request.finish(mins, -1, "text/plain");
- }
-
- public signal void authorized(string auth_code);
-
- public WebAuthenticationPane(string auth_sequence_start_url) {
- Object (login_uri : auth_sequence_start_url);
- }
-
- public static bool is_cache_dirty() {
- return cache_dirty;
- }
- }
-
private class Session : Publishing.RESTSupport.Session {
public string access_token = null;
public string refresh_token = null;
@@ -132,7 +70,6 @@ namespace Publishing.Authenticator.Shotwell.Google {
private string accountname = "default";
private Spit.Publishing.PluginHost host = null;
private GLib.HashTable<string, Variant> params = null;
- private WebAuthenticationPane web_auth_pane = null;
private Session session = null;
private string welcome_message = null;
private Secret.Schema? schema = null;
@@ -166,14 +103,7 @@ namespace Publishing.Authenticator.Shotwell.Google {
return;
}
- // FIXME: Find a way for a proper logout
- if (WebAuthenticationPane.is_cache_dirty()) {
- host.set_service_locked(false);
-
- host.install_static_message_pane(_("You have already logged in and out of a Google service during this Shotwell session.\n\nTo continue publishing to Google services, quit and restart Shotwell, then try publishing again."));
- } else {
- this.do_show_service_welcome_pane();
- }
+ this.do_show_service_welcome_pane();
}
public bool can_logout() {
@@ -202,8 +132,15 @@ namespace Publishing.Authenticator.Shotwell.Google {
public void set_accountname(string accountname) {
this.accountname = accountname;
}
+ private class AuthCallback : Spit.Publishing.AuthenticatedCallback, Object {
+ public signal void auth(GLib.HashTable<string, string> params);
+
+ public void authenticated(GLib.HashTable<string, string> params) {
+ auth(params);
+ }
+ }
- private void do_hosted_web_authentication() {
+ private async void do_hosted_web_authentication() {
debug("ACTION: running OAuth authentication flow in hosted web pane.");
string user_authorization_url = "https://accounts.google.com/o/oauth2/auth?" +
@@ -216,23 +153,26 @@ namespace Publishing.Authenticator.Shotwell.Google {
"access_type=offline&" +
"approval_prompt=force";
- web_auth_pane = new WebAuthenticationPane(user_authorization_url);
- web_auth_pane.authorized.connect(on_web_auth_pane_authorized);
- web_auth_pane.error.connect(on_web_auth_pane_error);
-
- host.install_dialog_pane(web_auth_pane);
- }
-
- private void on_web_auth_pane_authorized(string auth_code) {
- web_auth_pane.authorized.disconnect(on_web_auth_pane_authorized);
-
- debug("EVENT: user authorized scope %s with auth_code %s", scope, auth_code);
-
- do_get_access_tokens.begin(auth_code);
- }
+ var auth_callback = new AuthCallback();
+ string? web_auth_code = null;
+ auth_callback.auth.connect((prm) => {
+ if ("code" in prm) {
+ web_auth_code = prm["code"];
+ }
+ do_hosted_web_authentication.callback();
+ });
+ host.register_auth_callback(REVERSE_CLIENT_ID, auth_callback);
+ try {
+ AppInfo.launch_default_for_uri(user_authorization_url, null);
+ host.install_login_wait_pane();
+ yield;
- private void on_web_auth_pane_error() {
- host.post_error(web_auth_pane.load_error);
+ yield do_get_access_tokens(web_auth_code);
+ } catch (Error err) {
+ host.post_error(err);
+ } finally {
+ host.unregister_auth_callback(REVERSE_CLIENT_ID);
+ }
}
private async void do_get_access_tokens(string auth_code) {
@@ -384,7 +324,6 @@ namespace Publishing.Authenticator.Shotwell.Google {
}
this.authenticated();
- web_auth_pane.clear();
}
private async void do_exchange_refresh_token_for_access_token() {
@@ -421,7 +360,6 @@ namespace Publishing.Authenticator.Shotwell.Google {
Idle.add (() => { this.authenticate(); return false; });
}
- web_auth_pane.clear();
host.post_error(err);
}
}
@@ -435,7 +373,7 @@ namespace Publishing.Authenticator.Shotwell.Google {
private void on_service_welcome_login() {
debug("EVENT: user clicked 'Login' in welcome pane.");
- this.do_hosted_web_authentication();
+ this.do_hosted_web_authentication.begin();
}
}
}
diff --git a/plugins/authenticator/shotwell/meson.build b/plugins/authenticator/shotwell/meson.build
index 037ec3b..a6475e0 100644
--- a/plugins/authenticator/shotwell/meson.build
+++ b/plugins/authenticator/shotwell/meson.build
@@ -11,7 +11,7 @@ authenticator_shotwell_resources = gnome.compile_resources('authenticator-resour
source_dir : meson.project_source_root())
authenticator_shotwell_deps = [gee, gtk, gio, soup, json_glib, sw_plugin,
- sw_plugin_common_dep, json_glib, xml, webkit, secret]
+ sw_plugin_common_dep, json_glib, xml, secret]
authenticator = library('shotwell-authenticator',
authenticator_shotwell_sources + authenticator_shotwell_resources,
diff --git a/plugins/common/WebAuthenticationPane.vala b/plugins/common/WebAuthenticationPane.vala
index b9f7280..f745252 100644
--- a/plugins/common/WebAuthenticationPane.vala
+++ b/plugins/common/WebAuthenticationPane.vala
@@ -6,109 +6,44 @@
using Spit.Publishing;
namespace Shotwell.Plugins.Common {
- public abstract class WebAuthenticationPane : Spit.Publishing.DialogPane, Object {
+ public class ExternalWebPane : Spit.Publishing.DialogPane, Object {
public DialogPane.GeometryOptions preferred_geometry {
get; construct; default = DialogPane.GeometryOptions.COLOSSAL_SIZE;
}
-
public string login_uri { owned get; construct; }
- public Error load_error { get; private set; default = null; }
-
- private WebKit.WebView webview;
- private Gtk.Widget widget;
- private Gtk.Entry entry;
+ public Gtk.Widget widget;
- public void clear() {
- debug("Clearing the data of WebKit...");
- this.webview.get_website_data_manager().clear.begin(WebKit.WebsiteDataTypes.ALL, (GLib.TimeSpan)0);
+ public ExternalWebPane(string uri) {
+ Object(login_uri: uri);
}
+ public signal void browser_toggled();
+
public override void constructed () {
base.constructed ();
- var ctx = WebKit.WebContext.get_default();
- if (!ctx.get_sandbox_enabled()) {
- ctx.set_sandbox_enabled(true);
- }
-
- var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 4);
- this.widget = box;
- this.entry = new Gtk.Entry();
- this.entry.editable = false;
- this.entry.get_style_context().add_class("flat");
- this.entry.get_style_context().add_class("read-only");
- box.pack_start (entry, false, false, 6);
-
- this.webview = new WebKit.WebView ();
-
- this.webview.load_changed.connect (this.on_page_load_changed);
- this.webview.load_failed.connect (this.on_page_load_failed);
- this.webview.context_menu.connect ( () => { return false; });
- this.webview.decide_policy.connect (this.on_decide_policy);
- this.webview.bind_property("uri", this.entry, "text", GLib.BindingFlags.DEFAULT);
- box.pack_end (this.webview);
- }
-
- private bool on_decide_policy(WebKit.PolicyDecision decision, WebKit.PolicyDecisionType type) {
- switch (type) {
- case WebKit.PolicyDecisionType.NEW_WINDOW_ACTION: {
- var navigation = (WebKit.NavigationPolicyDecision) decision;
- var action = navigation.get_navigation_action();
- var uri = action.get_request().uri;
- decision.ignore();
- AppInfo.launch_default_for_uri_async.begin(uri, null);
- return true;
- }
- default:
- break;
- }
-
- return false;
- }
-
- public abstract void on_page_load ();
-
- protected void set_cursor (Gdk.CursorType type) {
- var window = webview.get_window ();
- if (window == null)
- return;
-
- var display = window.get_display ();
- if (display == null)
- return;
-
- var cursor = new Gdk.Cursor.for_display (display, type);
- window.set_cursor (cursor);
- }
-
- private bool on_page_load_failed (WebKit.LoadEvent load_event, string uri, Error error) {
- // OAuth call-back scheme. Produces a load error because it is not HTTP(S)
- // Do not set the load_error, but continue the error handling
- if (uri.has_prefix ("shotwell-auth://"))
- return false;
-
- critical ("Failed to load uri %s: %s", uri, error.message);
- this.load_error = error;
-
- return false;
- }
-
- private void on_page_load_changed (WebKit.LoadEvent load_event) {
- switch (load_event) {
- case WebKit.LoadEvent.STARTED:
- case WebKit.LoadEvent.REDIRECTED:
- this.set_cursor (Gdk.CursorType.WATCH);
- break;
- case WebKit.LoadEvent.FINISHED:
- this.set_cursor (Gdk.CursorType.LEFT_PTR);
- this.on_page_load ();
- break;
- default:
- break;
- }
- }
-
- public WebKit.WebView get_view () {
- return this.webview;
+ var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 18);
+ box.set_halign(Gtk.Align.CENTER);
+ box.hexpand = true;
+ box.set_valign(Gtk.Align.CENTER);
+ box.vexpand = true;
+ var image = new Gtk.Image.from_icon_name ("web-browser-symbolic", Gtk.IconSize.DIALOG);
+ image.get_style_context().add_class("dim-label");
+ image.set_pixel_size(128);
+ box.add(image);
+
+ var label = new Gtk.Label(_("Sign in with your browser to setup an account"));
+ label.get_style_context().add_class("heading");
+ box.add(label);
+ var button = new Gtk.Button.with_label (_("Continue"));
+ button.set_halign(Gtk.Align.CENTER);
+ button.get_style_context().add_class ("suggested-action");
+ button.clicked.connect(() => {
+ AppInfo.launch_default_for_uri_async.begin(login_uri, null);
+ browser_toggled();
+ });
+ box.pack_end(button);
+
+ widget = box;
}
public DialogPane.GeometryOptions get_preferred_geometry() {
@@ -120,11 +55,9 @@ namespace Shotwell.Plugins.Common {
}
public void on_pane_installed () {
- this.get_view ().load_uri (this.login_uri);
}
public void on_pane_uninstalled() {
- this.clear();
- }
- }
+ }
+ }
}
diff --git a/plugins/meson.build b/plugins/meson.build
index e9c0e49..bd4ac9e 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -7,7 +7,7 @@ sw_plugin_common = library('shotwell-plugin-common',
'common/BuilderPane.vala',
'common/OAuth1Support.vala'],
version: meson.project_version(),
- dependencies : [gtk, gee, webkit, soup, xml, sw_plugin],
+ dependencies : [gtk, gee, soup, xml, sw_plugin],
vala_header : 'shotwell-plugin-common.h',
vala_vapi : 'shotwell-plugin-common.vapi',
include_directories : config_incdir,
diff --git a/plugins/shotwell-publishing/meson.build b/plugins/shotwell-publishing/meson.build
index a93726b..962195c 100644
--- a/plugins/shotwell-publishing/meson.build
+++ b/plugins/shotwell-publishing/meson.build
@@ -18,7 +18,7 @@ shotwell_publishing_resources = gnome.compile_resources('publishing-resource',
shared_module('shotwell-publishing',
shotwell_publishing_sources + shotwell_publishing_resources,
dependencies : [gtk, soup, gexiv2, gee, sw_plugin, json_glib,
- webkit, sw_plugin_common_dep, xml, gcr,
+ sw_plugin_common_dep, xml, gcr,
gcr_ui, authenticator_dep, secret],
c_args : ['-DPLUGIN_RESOURCE_PATH="/org/gnome/Shotwell/Publishing"',
'-DGCR_API_SUBJECT_TO_CHANGE'],
diff --git a/plugins/shotwell-transitions/meson.build b/plugins/shotwell-transitions/meson.build
index 8134292..f809ade 100644
--- a/plugins/shotwell-transitions/meson.build
+++ b/plugins/shotwell-transitions/meson.build
@@ -20,7 +20,7 @@ libm = cc.find_library('m', required : false)
shared_module('shotwell-transitions',
shotwell_transitions_sources + shotwell_transitions_resources,
dependencies : [gio, gdk_pixbuf, cairo, gtk, gdk, xml, sw_plugin,
- sw_plugin_common_dep, gee, soup, webkit, libm],
+ sw_plugin_common_dep, gee, soup, libm],
vala_args : [
'--gresources', 'org.gnome.Shotwell.Transitions.gresource.xml',
],