diff options
Diffstat (limited to 'plugins/shotwell-publishing/FlickrPublishing.vala')
-rw-r--r-- | plugins/shotwell-publishing/FlickrPublishing.vala | 427 |
1 files changed, 49 insertions, 378 deletions
diff --git a/plugins/shotwell-publishing/FlickrPublishing.vala b/plugins/shotwell-publishing/FlickrPublishing.vala index 1702288..24b2b61 100644 --- a/plugins/shotwell-publishing/FlickrPublishing.vala +++ b/plugins/shotwell-publishing/FlickrPublishing.vala @@ -56,13 +56,7 @@ 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"; -internal const string API_KEY = "60dd96d4a2ad04888b09c9e18d82c26f"; -internal const string API_SECRET = "d0960565e03547c1"; internal const int ORIGINAL_SIZE = -1; internal const string EXPIRED_SESSION_ERROR_CODE = "98"; internal const string ENCODE_RFC_3986_EXTRA = "!*'();:@&=+$,/?%#[] \\"; @@ -104,168 +98,63 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object { private bool was_started = false; private Session session = null; private PublishingOptionsPane publishing_options_pane = null; + private Spit.Publishing.Authenticator authenticator = null; private PublishingParameters parameters = null; public FlickrPublisher(Spit.Publishing.Service service, - Spit.Publishing.PluginHost host) { + Spit.Publishing.PluginHost host) { debug("FlickrPublisher instantiated."); this.service = service; this.host = host; this.session = new Session(); this.parameters = new PublishingParameters(); - - session.authenticated.connect(on_session_authenticated); + this.authenticator = Publishing.Authenticator.Factory.get_instance().create("flickr", host); + + this.authenticator.authenticated.connect(on_session_authenticated); } ~FlickrPublisher() { - session.authenticated.disconnect(on_session_authenticated); - } - - private void invalidate_persistent_session() { - set_persistent_access_phase_token(""); - set_persistent_access_phase_token_secret(""); - set_persistent_access_phase_username(""); - } - - private bool is_persistent_session_valid() { - return (get_persistent_access_phase_username() != null && - get_persistent_access_phase_token() != null && - get_persistent_access_phase_token_secret() != null); - } - - private string? get_persistent_access_phase_username() { - return host.get_config_string("access_phase_username", null); - } - - private void set_persistent_access_phase_username(string username) { - host.set_config_string("access_phase_username", username); + this.authenticator.authenticated.disconnect(on_session_authenticated); } - private string? get_persistent_access_phase_token() { - return host.get_config_string("access_phase_token", null); - } - - private void set_persistent_access_phase_token(string token) { - host.set_config_string("access_phase_token", token); - } - - private string? get_persistent_access_phase_token_secret() { - return host.get_config_string("access_phase_token_secret", null); - } - - private void set_persistent_access_phase_token_secret(string secret) { - host.set_config_string("access_phase_token_secret", secret); + public Spit.Publishing.Authenticator get_authenticator() { + return this.authenticator; } private bool get_persistent_strip_metadata() { return host.get_config_bool("strip_metadata", false); } - + private void set_persistent_strip_metadata(bool strip_metadata) { 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); - - 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: 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); - + private void on_session_authenticated() { if (!is_running()) return; - debug("EVENT: user clicked 'Continue' in PIN entry pane."); - - do_verify_pin(pin); - } + debug("EVENT: a fully authenticated session has become available"); - 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()); - } + var params = this.authenticator.get_authentication_parameter(); + Variant consumer_key = null; + Variant consumer_secret = null; + Variant auth_token = null; + Variant auth_token_secret = null; + Variant username = null; - 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; + params.lookup_extended("ConsumerKey", null, out consumer_key); + params.lookup_extended("ConsumerSecret", null, out consumer_secret); + session.set_api_credentials(consumer_key.get_string(), consumer_secret.get_string()); - 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; + 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()); - 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 +249,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"); @@ -544,8 +313,9 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object { private void do_logout() { debug("ACTION: logging user out, deauthenticating session, and erasing stored credentials"); - session.deauthenticate(); - invalidate_persistent_session(); + if (authenticator.can_logout()) { + authenticator.logout(); + } running = false; @@ -650,16 +420,7 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object { running = true; was_started = true; - if (is_persistent_session_valid()) { - debug("attempt start: a persistent session is available; using it"); - - session.authenticate_from_persistent_credentials(get_persistent_access_phase_token(), - get_persistent_access_phase_token_secret(), get_persistent_access_phase_username()); - } else { - debug("attempt start: no persistent session available; showing login welcome pane"); - - do_show_login_welcome_pane(); - } + authenticator.authenticate(); } public void start() { @@ -684,60 +445,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) { @@ -748,7 +455,7 @@ internal class Transaction : Publishing.RESTSupport.Transaction { add_argument("oauth_version", "1.0"); add_argument("oauth_callback", "oob"); add_argument("oauth_timestamp", session.get_oauth_timestamp()); - add_argument("oauth_consumer_key", API_KEY); + add_argument("oauth_consumer_key", session.get_consumer_key()); } public Transaction.with_uri(Session session, string uri, @@ -760,7 +467,7 @@ internal class Transaction : Publishing.RESTSupport.Transaction { add_argument("oauth_version", "1.0"); add_argument("oauth_callback", "oob"); add_argument("oauth_timestamp", session.get_oauth_timestamp()); - add_argument("oauth_consumer_key", API_KEY); + add_argument("oauth_consumer_key", session.get_consumer_key()); } public override void execute() throws Spit.Publishing.PublishingError { @@ -818,22 +525,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); @@ -860,7 +551,7 @@ private class UploadTransaction : Publishing.RESTSupport.UploadTransaction { add_authorization_header_field("oauth_version", "1.0"); add_authorization_header_field("oauth_callback", "oob"); add_authorization_header_field("oauth_timestamp", session.get_oauth_timestamp()); - add_authorization_header_field("oauth_consumer_key", API_KEY); + add_authorization_header_field("oauth_consumer_key", session.get_consumer_key()); add_authorization_header_field("oauth_token", session.get_access_phase_token()); add_argument("is_public", ("%d".printf(parameters.visibility_specification.everyone_level))); @@ -920,11 +611,11 @@ private class UploadTransaction : Publishing.RESTSupport.UploadTransaction { } internal class Session : Publishing.RESTSupport.Session { - private string? request_phase_token = null; - private string? request_phase_token_secret = null; private string? access_phase_token = null; private string? access_phase_token_secret = null; private string? username = null; + private string? consumer_key = null; + private string? consumer_secret = null; public Session() { base(ENDPOINT_URL); @@ -935,19 +626,9 @@ internal class Session : Publishing.RESTSupport.Session { username != null); } - public void authenticate_from_persistent_credentials(string token, string secret, - string username) { - this.access_phase_token = token; - this.access_phase_token_secret = secret; - this.username = username; - - authenticated(); - } - - public void deauthenticate() { - access_phase_token = null; - access_phase_token_secret = null; - username = null; + public void set_api_credentials(string consumer_key, string consumer_secret) { + this.consumer_key = consumer_key; + this.consumer_secret = consumer_secret; } public void sign_transaction(Publishing.RESTSupport.Transaction txn) { @@ -984,16 +665,12 @@ internal class Session : Publishing.RESTSupport.Session { if (access_phase_token_secret != null) { debug("access phase token secret available; using it as signing key"); - signing_key = API_SECRET + "&" + access_phase_token_secret; - } else if (request_phase_token_secret != null) { - debug("request phase token secret available; using it as signing key"); - - signing_key = API_SECRET + "&" + request_phase_token_secret; + signing_key = consumer_secret + "&" + access_phase_token_secret; } else { debug("neither access phase nor request phase token secrets available; using API " + "key as signing key"); - signing_key = API_SECRET + "&"; + signing_key = consumer_secret + "&"; } string signature_base_string = http_method + "&" + Soup.URI.encode( @@ -1016,11 +693,6 @@ internal class Session : Publishing.RESTSupport.Session { txn.add_argument("oauth_signature", signature); } - public void set_request_phase_credentials(string token, string secret) { - this.request_phase_token = token; - this.request_phase_token_secret = secret; - } - public void set_access_phase_credentials(string token, string secret, string username) { this.access_phase_token = token; this.access_phase_token_secret = secret; @@ -1040,22 +712,17 @@ internal class Session : Publishing.RESTSupport.Session { public string get_oauth_timestamp() { return GLib.get_real_time().to_string().substring(0, 10); } - - public string get_request_phase_token() { - assert(request_phase_token != null); - return request_phase_token; + + public string get_consumer_key() { + assert(consumer_key != null); + return consumer_key; } - + public string get_access_phase_token() { assert(access_phase_token != null); return access_phase_token; } - public string get_access_phase_token_secret() { - assert(access_phase_token_secret != null); - return access_phase_token_secret; - } - public string get_username() { assert(is_authenticated()); return username; @@ -1119,6 +786,10 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object { size_label = (Gtk.Label) this.builder.get_object("size_label"); strip_metadata_check = (Gtk.CheckButton) this.builder.get_object("strip_metadata_check"); + if (!publisher.get_authenticator().can_logout()) { + logout_button.parent.remove(logout_button); + } + this.parameters = parameters; this.publisher = publisher; this.media_type = media_type; |