[shotwell/wip/pluggable-auth: 23/23] wip: flickr: Extract authenticator



commit 12329c57a306a94df47f2bca9222c65002e15847
Author: Jens Georg <mail jensge org>
Date:   Thu Dec 22 21:05:41 2016 +0100

    wip: flickr: Extract authenticator

 plugins/shotwell-publishing/FlickrPublishing.vala  |  319 ++------------------
 .../FlickrPublishingAuthenticator.vala             |  320 ++++++++++++++++++++
 publish.am                                         |    1 +
 3 files changed, 353 insertions(+), 287 deletions(-)
---
diff --git a/plugins/shotwell-publishing/FlickrPublishing.vala 
b/plugins/shotwell-publishing/FlickrPublishing.vala
index 1702288..c8951f8 100644
--- a/plugins/shotwell-publishing/FlickrPublishing.vala
+++ b/plugins/shotwell-publishing/FlickrPublishing.vala
@@ -56,8 +56,6 @@ public class FlickrService : Object, Spit.Pluggable, Spit.Publishing.Service {
 namespace Publishing.Flickr {
 
 internal const string SERVICE_NAME = "Flickr";
-internal const string SERVICE_WELCOME_MESSAGE = 
-    _("You are not currently logged into Flickr.\n\nClick Log in to log into Flickr in your Web browser. You 
will have to authorize Shotwell Connect to link to your Flickr account.");
 internal const string RESTART_ERROR_MESSAGE = 
     _("You have already logged in and out of Flickr during this Shotwell session.\nTo continue publishing to 
Flickr, quit and restart Shotwell, then try publishing again.");
 internal const string ENDPOINT_URL = "https://api.flickr.com/services/rest";;
@@ -104,6 +102,7 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
     private bool was_started = false;
     private Session session = null;
     private PublishingOptionsPane publishing_options_pane = null;
+    private Publishing.Authenticator.Flickr authenticator;
    
     private PublishingParameters parameters = null;
 
@@ -114,12 +113,13 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
         this.host = host;
         this.session = new Session();
         this.parameters = new PublishingParameters();
-        
-        session.authenticated.connect(on_session_authenticated);
+        this.authenticator = new Publishing.Authenticator.Flickr (host);
+
+        this.authenticator.authenticated.connect(on_session_authenticated);
     }
     
     ~FlickrPublisher() {
-        session.authenticated.disconnect(on_session_authenticated);
+        this.authenticator.authenticated.disconnect(on_session_authenticated);
     }
     
     private void invalidate_persistent_session() {
@@ -166,106 +166,36 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
         host.set_config_bool("strip_metadata", strip_metadata);
     }    
 
-    private void on_welcome_pane_login_clicked() {
-        if (!running)
-            return;
-
-        debug("EVENT: user clicked 'Login' button in the welcome pane");
-        
-        do_run_authentication_request_transaction();
-    }
-
-    private void on_auth_request_txn_completed(Publishing.RESTSupport.Transaction txn) {
-        txn.completed.disconnect(on_auth_request_txn_completed);
-        txn.network_error.disconnect(on_auth_request_txn_error);
-
-        if (!is_running())
-            return;
-
-        debug("EVENT: OAuth authentication request transaction completed; response = '%s'",
-            txn.get_response());
-
-        do_parse_token_info_from_auth_request(txn.get_response());
-    }
-
-    private void on_auth_request_txn_error(Publishing.RESTSupport.Transaction txn,
-        Spit.Publishing.PublishingError err) {
-        txn.completed.disconnect(on_auth_request_txn_completed);
-        txn.network_error.disconnect(on_auth_request_txn_error);
-
+    private void on_session_authenticated() {
         if (!is_running())
             return;
 
-        debug("EVENT: OAuth authentication request transaction caused a network error");
-        host.post_error(err);
-    }
-    
-    private void on_authentication_token_available(string token, string token_secret) {
-        debug("EVENT: OAuth authentication token (%s) and token secret (%s) available",
-            token, token_secret);
-
-        session.set_request_phase_credentials(token, token_secret);
-
-        do_launch_system_browser(token);
-    }
-    
-    private void on_system_browser_launched() {
-        if (!is_running())
-            return;
+        debug("EVENT: a fully authenticated session has become available");
 
-        debug("EVENT: system browser launched.");
-        
-        do_show_pin_entry_pane();
-    }
-    
-    private void on_pin_entry_proceed(PinEntryPane sender, string pin) {
-        sender.proceed.disconnect(on_pin_entry_proceed);
-        
-        if (!is_running())
-            return;
+        var params = this.authenticator.get_authentication_parameter();
+        Variant request_token = null;
+        Variant request_token_secret = null;
+        Variant auth_token = null;
+        Variant auth_token_secret = null;
+        Variant username = null;
 
-        debug("EVENT: user clicked 'Continue' in PIN entry pane.");
-        
-        do_verify_pin(pin);
-    }
+        params.lookup_extended("RequestToken", null, out request_token);
+        params.lookup_extended("RequestTokenSecret", null, out request_token_secret);
+        session.set_request_phase_credentials(request_token.get_string(),
+                request_token_secret.get_string());
 
-    private void on_access_token_fetch_txn_completed(Publishing.RESTSupport.Transaction txn) {
-        txn.completed.disconnect(on_access_token_fetch_txn_completed);
-        txn.network_error.disconnect(on_access_token_fetch_error);
-        
-        if (!is_running())
-            return;
-        
-        debug("EVENT: fetching OAuth access token over the network succeeded");
-        
-        do_extract_access_phase_credentials_from_reponse(txn.get_response());
-    }
+        params.lookup_extended("AuthToken", null, out auth_token);
+        params.lookup_extended("AuthTokenSecret", null, out auth_token_secret);
+        params.lookup_extended("Username", null, out username);
+        session.set_access_phase_credentials(auth_token.get_string(),
+                auth_token_secret.get_string(), username.get_string());
 
-    private void on_access_token_fetch_error(Publishing.RESTSupport.Transaction txn,
-        Spit.Publishing.PublishingError err) {
-        txn.completed.disconnect(on_access_token_fetch_txn_completed);
-        txn.network_error.disconnect(on_access_token_fetch_error);
-        
-        if (!is_running())
-            return;
-
-        debug("EVENT: fetching OAuth access token over the network caused an error.");
-        
-        host.post_error(err);
-    }
-    
-    private void on_session_authenticated() {
-        if (!is_running())
-            return;
-
-        debug("EVENT: a fully authenticated session has become available");
-        
         parameters.username = session.get_username();
-        
+
         set_persistent_access_phase_token(session.get_access_phase_token());
         set_persistent_access_phase_token_secret(session.get_access_phase_token_secret());
         set_persistent_access_phase_username(session.get_username());
-        
+
         do_fetch_account_info();
     }
 
@@ -360,126 +290,6 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
         host.post_error(err);
     }
 
-    private void do_show_login_welcome_pane() {
-        debug("ACTION: installing login welcome pane");
-
-        host.set_service_locked(false);
-        host.install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_welcome_pane_login_clicked);
-    }
-    
-    private void do_run_authentication_request_transaction() {
-        debug("ACTION: running authentication request transaction");
-
-        host.set_service_locked(true);
-        host.install_static_message_pane(_("Preparing for login…"));
-
-        AuthenticationRequestTransaction txn = new AuthenticationRequestTransaction(session);
-        txn.completed.connect(on_auth_request_txn_completed);
-        txn.network_error.connect(on_auth_request_txn_error);
-        
-        try {
-            txn.execute();
-        } catch (Spit.Publishing.PublishingError err) {
-            host.post_error(err);
-        }
-    }
-    
-    private void do_parse_token_info_from_auth_request(string response) {
-        debug("ACTION: parsing authorization request response '%s' into token and secret", response);
-        
-        string? oauth_token = null;
-        string? oauth_token_secret = null;
-
-        var data = Soup.Form.decode(response);
-        data.lookup_extended("oauth_token", null, out oauth_token);
-        data.lookup_extended("oauth_token_secret", null, out oauth_token_secret);
-        
-        if (oauth_token == null || oauth_token_secret == null)
-            host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
-                "'%s' isn't a valid response to an OAuth authentication request", response));
-        
-        
-        on_authentication_token_available(oauth_token, oauth_token_secret);
-    }
-    
-    private void do_launch_system_browser(string token) {
-        string login_uri = "https://www.flickr.com/services/oauth/authorize?oauth_token="; + token +
-            "&perms=write";
-        
-        debug("ACTION: launching system browser with uri = '%s'", login_uri);
-        
-        try {
-            Process.spawn_command_line_async("xdg-open " + login_uri);
-        } catch (SpawnError e) {
-            host.post_error(new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
-                "couldn't launch system web browser to complete Flickr login"));
-            return;
-        }
-        
-        on_system_browser_launched();
-    }
-    
-    private void do_show_pin_entry_pane() {
-        debug("ACTION: showing PIN entry pane");
-        
-        Gtk.Builder builder = new Gtk.Builder();
-        
-        try {
-            builder.add_from_resource (Resources.RESOURCE_PATH + "/" +
-                    "flickr_pin_entry_pane.ui");
-        } catch (Error e) {
-            warning("Could not parse UI file! Error: %s.", e.message);
-            host.post_error(
-                new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
-                    _("A file required for publishing is unavailable. Publishing to Flickr can’t 
continue.")));
-            return;        
-        }
-        
-        PinEntryPane pin_entry_pane = new PinEntryPane(builder);
-        pin_entry_pane.proceed.connect(on_pin_entry_proceed);
-        host.install_dialog_pane(pin_entry_pane);
-    }
-    
-    private void do_verify_pin(string pin) {
-        debug("ACTION: validating authorization PIN %s", pin);
-        
-        host.set_service_locked(true);
-        host.install_static_message_pane(_("Verifying authorization…"));
-        
-        AccessTokenFetchTransaction txn = new AccessTokenFetchTransaction(session, pin);
-        txn.completed.connect(on_access_token_fetch_txn_completed);
-        txn.network_error.connect(on_access_token_fetch_error);
-        
-        try {
-            txn.execute();
-        } catch (Spit.Publishing.PublishingError err) {
-            host.post_error(err);
-        }
-    }
-    
-    private void do_extract_access_phase_credentials_from_reponse(string response) {
-        debug("ACTION: extracting access phase credentials from '%s'", response);
-
-        string? token = null;
-        string? token_secret = null;
-        string? username = null;
-
-        var data = Soup.Form.decode(response);
-        data.lookup_extended("oauth_token", null, out token);
-        data.lookup_extended("oauth_token_secret", null, out token_secret);
-        data.lookup_extended("username", null, out username);
-
-        debug("access phase credentials: { token = '%s'; token_secret = '%s'; username = '%s' }",
-            token, token_secret, username);
-
-        if (token == null || token_secret == null || username == null)
-            host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE("expected " +
-                "access phase credentials to contain token, token secret, and username but at " +
-                "least one of these is absent"));
-
-        session.set_access_phase_credentials(token, token_secret, username);
-    }
-
     private void do_fetch_account_info() {
         debug("ACTION: running network transaction to fetch account information");
 
@@ -655,10 +465,10 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
 
             session.authenticate_from_persistent_credentials(get_persistent_access_phase_token(),
                 get_persistent_access_phase_token_secret(), get_persistent_access_phase_username());
+            do_fetch_account_info();
         } else {
             debug("attempt start: no persistent session available; showing login welcome pane");
-
-            do_show_login_welcome_pane();
+            authenticator.authenticate();
         }
     }
     
@@ -684,60 +494,6 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
     }
 }
 
-internal class PinEntryPane : Spit.Publishing.DialogPane, GLib.Object {
-    private Gtk.Box pane_widget = null;
-    private Gtk.Button continue_button = null;
-    private Gtk.Entry pin_entry = null;
-    private Gtk.Label pin_entry_caption = null;
-    private Gtk.Label explanatory_text = null;
-    private Gtk.Builder builder = null;
-
-    public signal void proceed(PinEntryPane sender, string authorization_pin);
-
-    public PinEntryPane(Gtk.Builder builder) {
-        this.builder = builder;
-        assert(builder != null);
-        assert(builder.get_objects().length() > 0);        
-        
-        explanatory_text = builder.get_object("explanatory_text") as Gtk.Label;
-        pin_entry_caption = builder.get_object("pin_entry_caption") as Gtk.Label;
-        pin_entry = builder.get_object("pin_entry") as Gtk.Entry;
-        continue_button = builder.get_object("continue_button") as Gtk.Button;
-        
-        pane_widget = builder.get_object("pane_widget") as Gtk.Box;
-        
-        pane_widget.show_all();
-        
-        on_pin_entry_contents_changed();
-    }
-    
-    private void on_continue_clicked() {
-        proceed(this, pin_entry.get_text());
-    }
-    
-    private void on_pin_entry_contents_changed() {
-        continue_button.set_sensitive(pin_entry.text_length > 0);
-    }
-
-    public Gtk.Widget get_widget() {
-        return pane_widget;
-    }
-    
-    public Spit.Publishing.DialogPane.GeometryOptions get_preferred_geometry() {
-        return Spit.Publishing.DialogPane.GeometryOptions.NONE;
-    }
-
-    public void on_pane_installed() {
-        continue_button.clicked.connect(on_continue_clicked);
-        pin_entry.changed.connect(on_pin_entry_contents_changed);
-    }
-
-    public void on_pane_uninstalled() {
-        continue_button.clicked.disconnect(on_continue_clicked);
-        pin_entry.changed.disconnect(on_pin_entry_contents_changed);
-    }
-}
-
 internal class Transaction : Publishing.RESTSupport.Transaction {
     public Transaction(Session session, Publishing.RESTSupport.HttpMethod method =
         Publishing.RESTSupport.HttpMethod.POST) {
@@ -818,22 +574,6 @@ internal class Transaction : Publishing.RESTSupport.Transaction {
     }
 }
 
-internal class AuthenticationRequestTransaction : Transaction {
-    public AuthenticationRequestTransaction(Session session) {
-        base.with_uri(session, "https://www.flickr.com/services/oauth/request_token";,
-            Publishing.RESTSupport.HttpMethod.GET);
-    }
-}
-
-internal class AccessTokenFetchTransaction : Transaction {
-    public AccessTokenFetchTransaction(Session session, string user_verifier) {
-        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());
-    }
-}
-
 internal class AccountInfoFetchTransaction : Transaction {
     public AccountInfoFetchTransaction(Session session) {
         base(session, Publishing.RESTSupport.HttpMethod.GET);
@@ -1045,7 +785,12 @@ internal class Session : Publishing.RESTSupport.Session {
         assert(request_phase_token != null);
         return request_phase_token;
     }
-    
+
+    public string get_request_phase_token_secret() {
+        assert(request_phase_token_secret != null);
+        return request_phase_token_secret;
+    }
+
     public string get_access_phase_token() {
         assert(access_phase_token != null);
         return access_phase_token;
diff --git a/plugins/shotwell-publishing/FlickrPublishingAuthenticator.vala 
b/plugins/shotwell-publishing/FlickrPublishingAuthenticator.vala
new file mode 100644
index 0000000..e3c5f28
--- /dev/null
+++ b/plugins/shotwell-publishing/FlickrPublishingAuthenticator.vala
@@ -0,0 +1,320 @@
+/* Copyright 2016 Software Freedom Conservancy Inc.
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+namespace Publishing.Authenticator {
+    internal const string SERVICE_WELCOME_MESSAGE =
+        _("You are not currently logged into Flickr.\n\nClick Log in to log into Flickr in your Web browser. 
You will have to authorize Shotwell Connect to link to your Flickr account.");
+
+    internal class AuthenticationRequestTransaction : Publishing.Flickr.Transaction {
+        public AuthenticationRequestTransaction(Publishing.Flickr.Session session) {
+            base.with_uri(session, "https://www.flickr.com/services/oauth/request_token";,
+                    Publishing.RESTSupport.HttpMethod.GET);
+        }
+    }
+
+    internal class AccessTokenFetchTransaction : Publishing.Flickr.Transaction {
+        public AccessTokenFetchTransaction(Publishing.Flickr.Session session, string user_verifier) {
+            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());
+        }
+    }
+
+    internal class PinEntryPane : Spit.Publishing.DialogPane, GLib.Object {
+        private Gtk.Box pane_widget = null;
+        private Gtk.Button continue_button = null;
+        private Gtk.Entry pin_entry = null;
+        private Gtk.Label pin_entry_caption = null;
+        private Gtk.Label explanatory_text = null;
+        private Gtk.Builder builder = null;
+
+        public signal void proceed(PinEntryPane sender, string authorization_pin);
+
+        public PinEntryPane(Gtk.Builder builder) {
+            this.builder = builder;
+            assert(builder != null);
+            assert(builder.get_objects().length() > 0);
+
+            explanatory_text = builder.get_object("explanatory_text") as Gtk.Label;
+            pin_entry_caption = builder.get_object("pin_entry_caption") as Gtk.Label;
+            pin_entry = builder.get_object("pin_entry") as Gtk.Entry;
+            continue_button = builder.get_object("continue_button") as Gtk.Button;
+
+            pane_widget = builder.get_object("pane_widget") as Gtk.Box;
+
+            pane_widget.show_all();
+
+            on_pin_entry_contents_changed();
+        }
+
+        private void on_continue_clicked() {
+            proceed(this, pin_entry.get_text());
+        }
+
+        private void on_pin_entry_contents_changed() {
+            continue_button.set_sensitive(pin_entry.text_length > 0);
+        }
+
+        public Gtk.Widget get_widget() {
+            return pane_widget;
+        }
+
+        public Spit.Publishing.DialogPane.GeometryOptions get_preferred_geometry() {
+            return Spit.Publishing.DialogPane.GeometryOptions.NONE;
+        }
+
+        public void on_pane_installed() {
+            continue_button.clicked.connect(on_continue_clicked);
+            pin_entry.changed.connect(on_pin_entry_contents_changed);
+        }
+
+        public void on_pane_uninstalled() {
+            continue_button.clicked.disconnect(on_continue_clicked);
+            pin_entry.changed.disconnect(on_pin_entry_contents_changed);
+        }
+    }
+
+
+    public class Flickr : GLib.Object, Spit.Publishing.Authenticator {
+        private GLib.HashTable<string, Variant> params;
+        private Publishing.Flickr.Session session;
+        private Spit.Publishing.PluginHost host;
+
+        public Flickr(Spit.Publishing.PluginHost host) {
+            base();
+
+            this.host = host;
+            params = new GLib.HashTable<string, Variant>(str_hash, str_equal);
+
+            session = new Publishing.Flickr.Session();
+            session.authenticated.connect(on_session_authenticated);
+        }
+
+        ~Flickr() {
+            session.authenticated.disconnect(on_session_authenticated);
+        }
+
+        public void authenticate() {
+            do_show_login_welcome_pane();
+        }
+
+        public bool can_logout() {
+            return true;
+        }
+
+        public GLib.HashTable<string, Variant> get_authentication_parameter() {
+            return this.params;
+        }
+
+        public void invalidate_persistent_session() {
+        }
+
+        public void logout () {
+        }
+
+        private void do_show_login_welcome_pane() {
+            debug("ACTION: installing login welcome pane");
+
+            host.set_service_locked(false);
+            host.install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_welcome_pane_login_clicked);
+        }
+
+        private void on_welcome_pane_login_clicked() {
+            debug("EVENT: user clicked 'Login' button in the welcome pane");
+
+            do_run_authentication_request_transaction();
+        }
+
+        private void do_run_authentication_request_transaction() {
+            debug("ACTION: running authentication request transaction");
+
+            host.set_service_locked(true);
+            host.install_static_message_pane(_("Preparing for login…"));
+
+            AuthenticationRequestTransaction txn = new AuthenticationRequestTransaction(session);
+            txn.completed.connect(on_auth_request_txn_completed);
+            txn.network_error.connect(on_auth_request_txn_error);
+
+            try {
+                txn.execute();
+            } catch (Spit.Publishing.PublishingError err) {
+                host.post_error(err);
+            }
+        }
+
+        private void on_auth_request_txn_completed(Publishing.RESTSupport.Transaction txn) {
+            txn.completed.disconnect(on_auth_request_txn_completed);
+            txn.network_error.disconnect(on_auth_request_txn_error);
+
+            debug("EVENT: OAuth authentication request transaction completed; response = '%s'",
+                txn.get_response());
+
+            do_parse_token_info_from_auth_request(txn.get_response());
+        }
+
+        private void on_auth_request_txn_error(Publishing.RESTSupport.Transaction txn,
+                Spit.Publishing.PublishingError err) {
+            txn.completed.disconnect(on_auth_request_txn_completed);
+            txn.network_error.disconnect(on_auth_request_txn_error);
+
+            debug("EVENT: OAuth authentication request transaction caused a network error");
+            host.post_error(err);
+
+            this.authentication_failed();
+        }
+
+        private void do_parse_token_info_from_auth_request(string response) {
+            debug("ACTION: parsing authorization request response '%s' into token and secret", response);
+
+            string? oauth_token = null;
+            string? oauth_token_secret = null;
+
+            var data = Soup.Form.decode(response);
+            data.lookup_extended("oauth_token", null, out oauth_token);
+            data.lookup_extended("oauth_token_secret", null, out oauth_token_secret);
+
+            if (oauth_token == null || oauth_token_secret == null)
+                host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
+                            "'%s' isn't a valid response to an OAuth authentication request", response));
+
+
+            on_authentication_token_available(oauth_token, oauth_token_secret);
+        }
+
+        private void on_authentication_token_available(string token, string token_secret) {
+            debug("EVENT: OAuth authentication token (%s) and token secret (%s) available",
+                    token, token_secret);
+
+            session.set_request_phase_credentials(token, token_secret);
+
+            do_launch_system_browser(token);
+        }
+
+        private void on_system_browser_launched() {
+            debug("EVENT: system browser launched.");
+
+            do_show_pin_entry_pane();
+        }
+
+        private void on_pin_entry_proceed(PinEntryPane sender, string pin) {
+            sender.proceed.disconnect(on_pin_entry_proceed);
+
+            debug("EVENT: user clicked 'Continue' in PIN entry pane.");
+
+            do_verify_pin(pin);
+        }
+
+        private void do_launch_system_browser(string token) {
+            string login_uri = "https://www.flickr.com/services/oauth/authorize?oauth_token="; + token +
+                "&perms=write";
+
+            debug("ACTION: launching system browser with uri = '%s'", login_uri);
+
+            try {
+                Process.spawn_command_line_async("xdg-open " + login_uri);
+            } catch (SpawnError e) {
+                host.post_error(new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
+                            "couldn't launch system web browser to complete Flickr login"));
+                return;
+            }
+
+            on_system_browser_launched();
+        }
+
+        private void do_show_pin_entry_pane() {
+            debug("ACTION: showing PIN entry pane");
+
+            Gtk.Builder builder = new Gtk.Builder();
+
+            try {
+                builder.add_from_resource (Resources.RESOURCE_PATH + "/" +
+                        "flickr_pin_entry_pane.ui");
+            } catch (Error e) {
+                warning("Could not parse UI file! Error: %s.", e.message);
+                host.post_error(
+                        new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
+                            _("A file required for publishing is unavailable. Publishing to Flickr can’t 
continue.")));
+                return;
+            }
+
+            PinEntryPane pin_entry_pane = new PinEntryPane(builder);
+            pin_entry_pane.proceed.connect(on_pin_entry_proceed);
+            host.install_dialog_pane(pin_entry_pane);
+        }
+
+        private void do_verify_pin(string pin) {
+            debug("ACTION: validating authorization PIN %s", pin);
+
+            host.set_service_locked(true);
+            host.install_static_message_pane(_("Verifying authorization…"));
+
+            AccessTokenFetchTransaction txn = new AccessTokenFetchTransaction(session, pin);
+            txn.completed.connect(on_access_token_fetch_txn_completed);
+            txn.network_error.connect(on_access_token_fetch_error);
+
+            try {
+                txn.execute();
+            } catch (Spit.Publishing.PublishingError err) {
+                host.post_error(err);
+            }
+        }
+
+        private void on_access_token_fetch_txn_completed(Publishing.RESTSupport.Transaction txn) {
+            txn.completed.disconnect(on_access_token_fetch_txn_completed);
+            txn.network_error.disconnect(on_access_token_fetch_error);
+
+            debug("EVENT: fetching OAuth access token over the network succeeded");
+
+            do_extract_access_phase_credentials_from_reponse(txn.get_response());
+        }
+
+        private void on_access_token_fetch_error(Publishing.RESTSupport.Transaction txn,
+                Spit.Publishing.PublishingError err) {
+            txn.completed.disconnect(on_access_token_fetch_txn_completed);
+            txn.network_error.disconnect(on_access_token_fetch_error);
+
+            debug("EVENT: fetching OAuth access token over the network caused an error.");
+
+            host.post_error(err);
+            this.authentication_failed();
+        }
+
+        private void do_extract_access_phase_credentials_from_reponse(string response) {
+            debug("ACTION: extracting access phase credentials from '%s'", response);
+
+            string? token = null;
+            string? token_secret = null;
+            string? username = null;
+
+            var data = Soup.Form.decode(response);
+            data.lookup_extended("oauth_token", null, out token);
+            data.lookup_extended("oauth_token_secret", null, out token_secret);
+            data.lookup_extended("username", null, out username);
+
+            debug("access phase credentials: { token = '%s'; token_secret = '%s'; username = '%s' }",
+                    token, token_secret, username);
+
+            if (token == null || token_secret == null || username == null) {
+                host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE("expected " +
+                            "access phase credentials to contain token, token secret, and username but at " +
+                            "least one of these is absent"));
+                this.authentication_failed();
+            } else {
+                session.set_access_phase_credentials(token, token_secret, username);
+            }
+        }
+
+        private void on_session_authenticated() {
+            params.insert("RequestToken", session.get_request_phase_token());
+            params.insert("RequestTokenSecret", session.get_request_phase_token_secret());
+            params.insert("AuthToken", session.get_access_phase_token());
+            params.insert("AuthTokenSecret", session.get_access_phase_token_secret());
+            params.insert("Username", session.get_username());
+            this.authenticated();
+        }
+    }
+}
diff --git a/publish.am b/publish.am
index a6ab780..a5c2d87 100644
--- a/publish.am
+++ b/publish.am
@@ -22,6 +22,7 @@ plugins_shotwell_publishing_shotwell_publishing_la_SOURCES = \
        plugins/shotwell-publishing/FacebookPublishingAuthenticator.vala \
        plugins/shotwell-publishing/PicasaPublishing.vala \
        plugins/shotwell-publishing/FlickrPublishing.vala \
+       plugins/shotwell-publishing/FlickrPublishingAuthenticator.vala \
        plugins/shotwell-publishing/YouTubePublishing.vala \
        plugins/shotwell-publishing/PiwigoPublishing.vala \
        plugins/shotwell-plugin-common.vapi


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]