=== modified file 'Makefile'
--- old/Makefile	2012-08-21 19:18:25 +0000
+++ new/Makefile	2012-08-21 19:18:37 +0000
@@ -140,6 +140,7 @@
 	trash.ui 
 
 SYS_INTEGRATION_FILES = \
+	shotwell.application \
 	shotwell.desktop.head \
 	shotwell-viewer.desktop.head \
 	org.yorba.shotwell.gschema.xml \
@@ -536,6 +537,8 @@
 	mkdir -p $(DESTDIR)$(PREFIX)/bin
 	$(INSTALL_PROGRAM) $(PROGRAM) $(DESTDIR)$(PREFIX)/bin
 	$(INSTALL_PROGRAM) $(THUMBNAILER_BIN) $(DESTDIR)$(PREFIX)/bin
+	mkdir -p $(DESTDIR)$(PREFIX)/share/accounts/applications
+	$(INSTALL_DATA) misc/shotwell.application $(DESTDIR)$(PREFIX)/share/accounts/applications
 	mkdir -p $(DESTDIR)$(PREFIX)/share/shotwell/icons
 	$(INSTALL_DATA) icons/* $(DESTDIR)$(PREFIX)/share/shotwell/icons
 	mkdir -p $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps

=== modified file 'misc/org.yorba.shotwell.gschema.xml'
--- old/misc/org.yorba.shotwell.gschema.xml	2012-08-21 19:17:33 +0000
+++ new/misc/org.yorba.shotwell.gschema.xml	2012-08-21 19:18:37 +0000
@@ -356,6 +356,12 @@
         <description>The authorization token for the currently logged in Picasa Web Albums user, if any</description>
     </key>
     
+    <key name="auth-method" type="s">
+        <default>""</default>
+        <summary>auth method</summary>
+        <description>The authorization method for the currently logged in Picasa Web Albums user, if any</description>
+    </key>
+
     <key name="user-name" type="s">
         <default>""</default>
         <summary>username</summary>

=== added file 'misc/shotwell.application'
--- old/misc/shotwell.application	1970-01-01 00:00:00 +0000
+++ new/misc/shotwell.application	2012-08-21 19:18:37 +0000
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<application id="shotwell">
+  <description>Shotwell</description>
+	<desktop-entry>shotwell.desktop</desktop-entry>
+
+  <services>
+		<service id="picasa">
+			<description>Publish your pictures to Picasa</description>
+		</service>
+		<service id="facebook-sharing">
+			<description>Publish your pictures to Facebook</description>
+		</service>
+		<service id="flickr-sharing">
+			<description>Publish your pictures to Flickr</description>
+		</service>
+  </services>
+
+</application>

=== modified file 'plugins/Makefile'
--- old/plugins/Makefile	2012-08-21 19:17:33 +0000
+++ new/plugins/Makefile	2012-08-21 19:18:37 +0000
@@ -4,7 +4,10 @@
 DIST_FILES := \
 	Makefile \
 	Makefile.plugin.mk \
-	plugins.mk
+	plugins.mk \
+	Accounts.vapi \
+	Accounts.deps \
+	Signon.vapi
 
 .PHONY: all
 all: $(ALL_PLUGINS)

=== modified file 'plugins/Makefile.plugin.mk'
--- old/plugins/Makefile.plugin.mk	2012-08-21 19:17:33 +0000
+++ new/plugins/Makefile.plugin.mk	2012-08-21 19:19:28 +0000
@@ -27,6 +27,8 @@
 # automatically include the shotwell-plugin-dev-1.0 package as a local dependency
 EXT_PKGS := $(PKGS)
 PKGS := shotwell-plugin-dev-1.0 $(PKGS) $(PLUGIN_PKGS)
+EXT_PKGS := libsignon-glib libaccounts-glib $(EXT_PKGS)
+PKGS := signon accounts $(PKGS)
 
 # automatically include the Resources.vala common file
 SRC_FILES := ../common/Resources.vala $(SRC_FILES)

=== modified file 'plugins/common/RESTSupport.vala'
--- old/plugins/common/RESTSupport.vala	2012-08-21 19:17:33 +0000
+++ new/plugins/common/RESTSupport.vala	2012-08-21 19:18:37 +0000
@@ -314,7 +314,7 @@
             old_url = message.get_uri().to_string(false);
             url_with_query = get_endpoint_url() + "?" + formdata_string;
             message.set_uri(new Soup.URI(url_with_query));
-        } else {
+        } else if (get_method() == HttpMethod.POST) {
             message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY,
                 formdata_string.data);
         }

=== added file 'plugins/common/accounts.vala'
--- old/plugins/common/accounts.vala	1970-01-01 00:00:00 +0000
+++ new/plugins/common/accounts.vala	2012-08-21 19:18:37 +0000
@@ -0,0 +1,54 @@
+/* Copyright 2009-2011 Yorba Foundation
+ *
+ * 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.Accounts {
+
+public class SharingAccount {
+    private Ag.AccountService account_service = null;
+    
+    public SharingAccount(Ag.AccountService account_service) {
+        this.account_service = account_service;
+    }
+
+    public Signon.AuthSession create_auth_session() throws GLib.Error {
+        var auth_data = account_service.get_auth_data();
+
+        debug("Signon-id: %u", auth_data.get_credentials_id());
+
+        return new Signon.AuthSession(auth_data.get_credentials_id(),
+                                      auth_data.get_method());
+    }
+
+    public HashTable<string,Value?> get_session_parameters(out string mechanism) {
+        var auth_data = account_service.get_auth_data();
+        mechanism = auth_data.get_mechanism();
+        return auth_data.get_parameters();
+    }
+}
+
+public class SharingAccounts {
+    private Ag.Manager manager = null;
+    
+    public SharingAccounts() {
+        manager = new Ag.Manager.for_service_type("sharing");
+    }
+
+    public SharingAccount? get_account_for_provider(string provider) {
+        GLib.List<Ag.AccountService> accounts =
+            manager.get_enabled_account_services();
+
+        foreach (Ag.AccountService account_service in accounts) {
+            Ag.Account account = account_service.get_account();
+            if (account.get_provider_name() == provider) {
+                return new SharingAccount(account_service);
+            }
+        }
+        return null;
+    }
+}
+
+}
+

=== modified file 'plugins/shotwell-publishing/FacebookPublishing.vala'
--- old/plugins/shotwell-publishing/FacebookPublishing.vala	2012-08-21 19:17:33 +0000
+++ new/plugins/shotwell-publishing/FacebookPublishing.vala	2012-08-21 19:18:37 +0000
@@ -4,6 +4,8 @@
  * (version 2.1 or later).  See the COPYING file in this distribution.
  */
 
+using Publishing.Accounts;
+
 public class FacebookService : Object, Spit.Pluggable, Spit.Publishing.Service {
     private const string ICON_FILENAME = "facebook.png";
 
@@ -50,6 +52,11 @@
         return (Spit.Publishing.Publisher.MediaType.PHOTO |
             Spit.Publishing.Publisher.MediaType.VIDEO);
     }
+
+    public bool is_enabled() {
+        SharingAccounts accounts = new SharingAccounts();
+        return accounts.get_account_for_provider("facebook") != null;
+    }
 }
 
 namespace Publishing.Facebook {
@@ -163,13 +170,102 @@
     private int publish_to_album = NO_ALBUM;
     private weak Spit.Publishing.PluginHost host = null;
     private FacebookRESTSession session = null;
-    private WebAuthenticationPane web_auth_pane = null;
     private Spit.Publishing.ProgressCallback progress_reporter = null;
     private weak Spit.Publishing.Service service = null;
     private bool running = false;
+    private Signon.AuthSession auth_session = null;
 
     private Resolution target_resolution = Resolution.HIGH;
 
+    private struct LocaleLookup {
+        public string prefix;
+        public string translation;
+        public string? exception_code;
+        public string? exception_translation;
+        public string? exception_code_2;
+        public string? exception_translation_2;
+    }
+
+    private const LocaleLookup[] locale_lookup_table = {
+        { "es", "es-la", "ES", "es-es" },
+        { "en", "en-gb", "US", "en-us" },
+        { "fr", "fr-fr", "CA", "fr-ca" },
+        { "pt", "pt-br", "PT", "pt-pt" },
+        { "zh", "zh-cn", "HK", "zh-hk", "TW", "zh-tw" },
+        { "af", "af-za" },
+        { "ar", "ar-ar" },
+        { "nb", "nb-no" },
+        { "no", "nb-no" },
+        { "id", "id-id" },
+        { "ms", "ms-my" },
+        { "ca", "ca-es" },
+        { "cs", "cs-cz" },
+        { "cy", "cy-gb" },
+        { "da", "da-dk" },
+        { "de", "de-de" },
+        { "tl", "tl-ph" },
+        { "ko", "ko-kr" },
+        { "hr", "hr-hr" },
+        { "it", "it-it" },
+        { "lt", "lt-lt" },
+        { "hu", "hu-hu" },
+        { "nl", "nl-nl" },
+        { "ja", "ja-jp" },
+        { "nb", "nb-no" },
+        { "no", "nb-no" },
+        { "pl", "pl-pl" },
+        { "ro", "ro-ro" },
+        { "ru", "ru-ru" },
+        { "sk", "sk-sk" },
+        { "sl", "sl-sl" },
+        { "sv", "sv-se" },
+        { "th", "th-th" },
+        { "vi", "vi-vn" },
+        { "tr", "tr-tr" },
+        { "el", "el-gr" },
+        { "bg", "bg-bg" },
+        { "sr", "sr-rs" },
+        { "he", "he-il" },
+        { "hi", "hi-in" },
+        { "bn", "bn-in" },
+        { "pa", "pa-in" },
+        { "ta", "ta-in" },
+        { "te", "te-in" },
+        { "ml", "ml-in" }
+    };
+
+    private string get_system_locale_as_facebook_locale() {
+        unowned string? raw_system_locale = Intl.setlocale(LocaleCategory.ALL, "");
+        if (raw_system_locale == null || raw_system_locale == "")
+            return "www";
+
+        string system_locale = raw_system_locale.split(".")[0];
+
+        foreach (LocaleLookup locale_lookup in locale_lookup_table) {
+            if (!system_locale.has_prefix(locale_lookup.prefix))
+                continue;
+
+            if (locale_lookup.exception_code != null) {
+                assert(locale_lookup.exception_translation != null);
+
+                if (system_locale.contains(locale_lookup.exception_code))
+                    return locale_lookup.exception_translation;
+            }
+
+            if (locale_lookup.exception_code_2 != null) {
+                assert(locale_lookup.exception_translation_2 != null);
+
+                if (system_locale.contains(locale_lookup.exception_code_2))
+                    return locale_lookup.exception_translation_2;
+            }
+
+            return locale_lookup.translation;
+        }
+
+        // default
+        return "www";
+    }
+
     public FacebookPublisher(Spit.Publishing.Service service,
         Spit.Publishing.PluginHost host) {
         debug("FacebookPublisher instantiated.");
@@ -377,37 +473,80 @@
         start();
     }
 
+    public void on_processed(Signon.AuthSession self, owned HashTable<string,Value?> session_data, GLib.Error error){
+        /* TODO: handle errors */
+
+        host.set_service_locked(true);
+        host.install_account_fetch_wait_pane();
+
+        session.authenticated.connect(on_session_authenticated);
+        session.authentication_failed.connect(on_session_authentication_failed);
+
+        string token = session_data.lookup("AccessToken").get_string();
+        debug("Access Token: %s", token);
+        session.authenticate_with_parameters(token, null, null);
+    }
+
     private void do_hosted_web_authentication() {
         debug("ACTION: doing hosted web authentication.");
 
+        HashTable<string,Value?> data = null;
+        string mechanism = "user_agent";
+
+        SharingAccounts accounts =
+            new SharingAccounts();
+
+        SharingAccount? account = accounts.get_account_for_provider("facebook");
+        if (account != null) {
+            try {
+                auth_session = account.create_auth_session();
+                data = account.get_session_parameters(out mechanism);
+                debug("Got account data");
+            } catch (GLib.Error e) {
+                debug("EVENT: couldn't create session for account: %s",
+                      e.message);
+            }
+        }
+
+        if (auth_session == null) {
+            debug("Creating unbound session");
+            try {
+                auth_session = new Signon.AuthSession(0, "oauth2");
+            } catch (GLib.Error e) {
+                debug("EVENT: AuthSession creation failed: %s", e.message);
+                return;
+            }
+        }
+
+        if (data == null) {
+            debug("Using own params");
+            data = new HashTable<string,Value?>(str_hash, str_equal);
+            string facebook_locale = get_system_locale_as_facebook_locale();
+            string host_name = "%s.facebook.com".printf(facebook_locale);
+            data.insert("Host", host_name);
+            data.insert("AuthPath", "/dialog/oauth");
+            data.insert("ClientId", APPLICATION_ID);
+            data.insert("RedirectUri", "https://www.facebook.com/connect/login_success.html");
+            string[] scopes = {
+                "offline_access",
+                "publish_stream",
+                "user_photos",
+                "user_videos"
+            };
+            data.insert("Scope", scopes);
+            string[] response_type = {
+                "token"
+            };
+            data.insert("ResponseType", response_type);
+        }
+
+        var windowId = host.get_dialog_xid();
+        if (windowId != 0) {
+            data.insert("WindowId", windowId);
+        }
+
+        auth_session.process(data, mechanism, on_processed);
         host.set_service_locked(false);
-
-        web_auth_pane = new WebAuthenticationPane();
-        web_auth_pane.login_succeeded.connect(on_web_auth_pane_login_succeeded);
-        web_auth_pane.login_failed.connect(on_web_auth_pane_login_failed);
-
-        host.install_dialog_pane(web_auth_pane,
-            Spit.Publishing.PluginHost.ButtonMode.CANCEL);
-    }
-
-    private void do_authenticate_session(string success_url) {
-        debug("ACTION: preparing to extract session information encoded in url = '%s'",
-            success_url);
-
-        host.set_service_locked(true);
-        host.install_account_fetch_wait_pane();
-
-        session.authenticated.connect(on_session_authenticated);
-        session.authentication_failed.connect(on_session_authentication_failed);
-
-        try {
-            session.authenticate_from_uri(success_url);
-        } catch (Spit.Publishing.PublishingError err) {
-            // only post an error if we're running; errors tend to come in groups, so it's possible
-            // another error has already posted and caused us to stop
-            if (is_running())
-                host.post_error(err);
-        }
     }
 
     private void do_save_session_information() {
@@ -534,30 +673,6 @@
         host.post_error(err);
     }
 
-    private void on_web_auth_pane_login_succeeded(string success_url) {
-        if (!is_running())
-            return;
-
-        debug("EVENT: hosted web login succeeded.");
-
-        do_authenticate_session(success_url);
-    }
-
-    private void on_web_auth_pane_login_failed() {
-        if (!is_running())
-            return;
-
-        debug("EVENT: hosted web login failed.");
-
-        // In this case, "failed" doesn't mean that the user didn't enter the right username and
-        // password -- Facebook handles that case inside the Facebook Connect web control. Instead,
-        // it means that no session was initiated in response to our login request. The only
-        // way this happens is if the user clicks the "Cancel" button that appears inside
-        // the web control. In this case, the correct behavior is to return the user to the
-        // service welcome pane so that they can start the web interaction again.
-        do_show_service_welcome_pane();
-    }
-
     private void on_session_authenticated() {
         if (!is_running())
             return;
@@ -745,14 +860,8 @@
                 get_persistent_uid(), get_persistent_user_name());
             on_session_authenticated();
         } else {
-            if (WebAuthenticationPane.is_cache_dirty()) {
-                host.set_service_locked(false);
-                host.install_static_message_pane(RESTART_ERROR_MESSAGE,
-                    Spit.Publishing.PluginHost.ButtonMode.CANCEL);
-            } else {
-                session = new FacebookRESTSession(PHOTO_ENDPOINT_URL, USER_AGENT);
-                do_show_service_welcome_pane();
-            }
+            session = new FacebookRESTSession(PHOTO_ENDPOINT_URL, USER_AGENT);
+            do_show_service_welcome_pane();
         }
     }
 
@@ -881,39 +990,14 @@
         return (access_token != null && uid != null && user_name != null);
     }
 
-    public void authenticate_with_parameters(string access_token, string uid, string user_name) {
-        this.access_token = access_token;
-        this.uid = uid;
-        this.user_name = user_name;
-    }
-
-    public void authenticate_from_uri(string good_login_uri) throws Spit.Publishing.PublishingError {
-        // the raw uri is percent-encoded, so decode it
-        string decoded_uri = Soup.URI.decode(good_login_uri);
-
-        // locate the access token within the URI
-        string? access_token = null;
-        int index = decoded_uri.index_of("#access_token=");
-        if (index >= 0)
-            access_token = decoded_uri[index:decoded_uri.length];
-        if (access_token == null)
-            throw new Spit.Publishing.PublishingError.MALFORMED_RESPONSE("Server redirect URL contained no access token");
-
-        // remove any trailing parameters from the session description string
-        string? trailing_params = null;
-        index = access_token.index_of_char('&');
-        if (index >= 0)
-            trailing_params = access_token[index:access_token.length];
-        if (trailing_params != null)
-            access_token = access_token.replace(trailing_params, "");
-
-        // remove the key from the session description string
-        access_token = access_token.replace("#access_token=", "");
-        
-        // we've got an access token!
-        this.access_token = access_token;
-        
-        do_user_id_fetch_transaction();
+    public void authenticate_with_parameters(string access_token, string? uid, string? user_name) {
+        this.access_token = access_token;
+        if (uid == null) {
+            do_user_id_fetch_transaction();
+        } else {
+            this.uid = uid;
+            this.user_name = user_name;
+        }
     }
 
     public string get_endpoint_url() {
@@ -1307,190 +1391,6 @@
     }
 }
 
-internal class WebAuthenticationPane : Spit.Publishing.DialogPane, Object {
-    private WebKit.WebView webview = null;
-    private Gtk.Box pane_widget = null;
-    private Gtk.ScrolledWindow webview_frame = null;
-    private static bool cache_dirty = false;
-
-    public signal void login_succeeded(string success_url);
-    public signal void login_failed();
-
-    public WebAuthenticationPane() {
-        pane_widget = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
-
-        webview_frame = new Gtk.ScrolledWindow(null, null);
-        webview_frame.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
-        webview_frame.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
-
-        webview = new WebKit.WebView();
-        webview.get_settings().enable_plugins = false;
-        webview.get_settings().enable_default_context_menu = false;
-
-        webview.load_finished.connect(on_page_load);
-        webview.load_started.connect(on_load_started);
-
-        webview_frame.add(webview);
-        pane_widget.pack_start(webview_frame, true, true, 0);
-    }
-    
-    private class LocaleLookup {
-        public string prefix;
-        public string translation;
-        public string? exception_code;
-        public string? exception_translation;
-        public string? exception_code_2;
-        public string? exception_translation_2;
-        
-        public LocaleLookup(string prefix, string translation, string? exception_code = null, 
-            string? exception_translation  = null, string? exception_code_2  = null, 
-            string? exception_translation_2 = null) {
-            this.prefix = prefix;
-            this.translation = translation;
-            this.exception_code = exception_code;
-            this.exception_translation = exception_translation;
-            this.exception_code_2 = exception_code_2;
-            this.exception_translation_2 = exception_translation_2;
-        }
-        
-    }
-    
-    private LocaleLookup[] locale_lookup_table = {
-        new LocaleLookup( "es", "es-la", "ES", "es-es" ),
-        new LocaleLookup( "en", "en-gb", "US", "en-us" ),
-        new LocaleLookup( "fr", "fr-fr", "CA", "fr-ca" ),
-        new LocaleLookup( "pt", "pt-br", "PT", "pt-pt" ),
-        new LocaleLookup( "zh", "zh-cn", "HK", "zh-hk", "TW", "zh-tw" ),
-        new LocaleLookup( "af", "af-za" ),
-        new LocaleLookup( "ar", "ar-ar" ),
-        new LocaleLookup( "nb", "nb-no" ),
-        new LocaleLookup( "no", "nb-no" ),
-        new LocaleLookup( "id", "id-id" ),
-        new LocaleLookup( "ms", "ms-my" ),
-        new LocaleLookup( "ca", "ca-es" ),
-        new LocaleLookup( "cs", "cs-cz" ),
-        new LocaleLookup( "cy", "cy-gb" ),
-        new LocaleLookup( "da", "da-dk" ),
-        new LocaleLookup( "de", "de-de" ),
-        new LocaleLookup( "tl", "tl-ph" ),
-        new LocaleLookup( "ko", "ko-kr" ),
-        new LocaleLookup( "hr", "hr-hr" ),
-        new LocaleLookup( "it", "it-it" ),
-        new LocaleLookup( "lt", "lt-lt" ),
-        new LocaleLookup( "hu", "hu-hu" ),
-        new LocaleLookup( "nl", "nl-nl" ),
-        new LocaleLookup( "ja", "ja-jp" ),
-        new LocaleLookup( "nb", "nb-no" ),
-        new LocaleLookup( "no", "nb-no" ),
-        new LocaleLookup( "pl", "pl-pl" ),
-        new LocaleLookup( "ro", "ro-ro" ),
-        new LocaleLookup( "ru", "ru-ru" ),
-        new LocaleLookup( "sk", "sk-sk" ),
-        new LocaleLookup( "sl", "sl-si" ),
-        new LocaleLookup( "sv", "sv-se" ),
-        new LocaleLookup( "th", "th-th" ),
-        new LocaleLookup( "vi", "vi-vn" ),
-        new LocaleLookup( "tr", "tr-tr" ),
-        new LocaleLookup( "el", "el-gr" ),
-        new LocaleLookup( "bg", "bg-bg" ),
-        new LocaleLookup( "sr", "sr-rs" ),
-        new LocaleLookup( "he", "he-il" ),
-        new LocaleLookup( "hi", "hi-in" ),
-        new LocaleLookup( "bn", "bn-in" ),
-        new LocaleLookup( "pa", "pa-in" ),
-        new LocaleLookup( "ta", "ta-in" ),
-        new LocaleLookup( "te", "te-in" ),
-        new LocaleLookup( "ml", "ml-in" )
-    };
-    
-    private string get_system_locale_as_facebook_locale() {
-        unowned string? raw_system_locale = Intl.setlocale(LocaleCategory.ALL, "");
-        if (raw_system_locale == null || raw_system_locale == "")
-            return "www";
-        
-        string system_locale = raw_system_locale.split(".")[0];
-        
-        foreach (LocaleLookup locale_lookup in locale_lookup_table) {
-            if (!system_locale.has_prefix(locale_lookup.prefix))
-                continue;
-            
-            if (locale_lookup.exception_code != null) {
-                assert(locale_lookup.exception_translation != null);
-                
-                if (system_locale.contains(locale_lookup.exception_code))
-                    return locale_lookup.exception_translation;
-            }
-            
-            if (locale_lookup.exception_code_2 != null) {
-                assert(locale_lookup.exception_translation_2 != null);
-                
-                if (system_locale.contains(locale_lookup.exception_code_2))
-                    return locale_lookup.exception_translation_2;
-            }
-            
-            return locale_lookup.translation;
-        }
-        
-        // default
-        return "www";
-    }
-
-    private string get_login_url() {
-        string facebook_locale = get_system_locale_as_facebook_locale();
-
-        return "https://%s.facebook.com/dialog/oauth?client_id=%s&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=offline_access,publish_stream,user_photos,user_videos&response_type=token".printf(facebook_locale, APPLICATION_ID);
-    }
-
-    private void on_page_load(WebKit.WebFrame origin_frame) {
-        pane_widget.get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.LEFT_PTR));
-
-        string loaded_url = origin_frame.get_uri().dup();
-
-        // strip parameters from the loaded url
-        if (loaded_url.contains("?")) {
-            int index = loaded_url.index_of_char('?');
-            string params = loaded_url[index:loaded_url.length];
-            loaded_url = loaded_url.replace(params, "");
-        }
-
-        // were we redirected to the facebook login success page?
-        if (loaded_url.contains("login_success")) {
-            cache_dirty = true;
-            login_succeeded(origin_frame.get_uri());
-            return;
-        }
-
-        // were we redirected to the login total failure page?
-        if (loaded_url.contains("login_failure")) {
-            login_failed();
-            return;
-        }
-    }
-
-    private void on_load_started(WebKit.WebFrame frame) {
-        pane_widget.get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.WATCH));
-    }
-
-    public static bool is_cache_dirty() {
-        return cache_dirty;
-    }
-
-    public Gtk.Widget get_widget() {
-        return pane_widget;
-    }
-
-    public Spit.Publishing.DialogPane.GeometryOptions get_preferred_geometry() {
-        return Spit.Publishing.DialogPane.GeometryOptions.COLOSSAL_SIZE;
-    }
-
-    public void on_pane_installed() {
-        webview.open(get_login_url());
-    }
-
-    public void on_pane_uninstalled() {
-    }
-}
-
 internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
     private LegacyPublishingOptionsPane wrapped = null;
 

=== modified file 'plugins/shotwell-publishing/FlickrPublishing.vala'
--- old/plugins/shotwell-publishing/FlickrPublishing.vala	2012-08-21 19:17:33 +0000
+++ new/plugins/shotwell-publishing/FlickrPublishing.vala	2012-08-21 19:18:37 +0000
@@ -4,6 +4,8 @@
  * (version 2.1 or later).  See the COPYING file in this distribution. 
  */
 
+using Publishing.Accounts;
+
 extern string hmac_sha1(string key, string message);
 
 public class FlickrService : Object, Spit.Pluggable, Spit.Publishing.Service {
@@ -52,6 +54,11 @@
         return (Spit.Publishing.Publisher.MediaType.PHOTO |
             Spit.Publishing.Publisher.MediaType.VIDEO);
     }
+
+    public bool is_enabled() {
+        SharingAccounts accounts = new SharingAccounts();
+        return accounts.get_account_for_provider("flickr") != null;
+    }
 }
 
 namespace Publishing.Flickr {
@@ -105,6 +112,7 @@
     private bool was_started = false;
     private Session session = null;
     private PublishingOptionsPane publishing_options_pane = null;
+    private Signon.AuthSession auth_session = null;
    
     private PublishingParameters parameters = null;
 
@@ -165,88 +173,54 @@
 
         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);
-        
-        if (!is_running())
-            return;
-
-        debug("EVENT: user clicked 'Continue' in PIN entry pane.");
-        
-        do_verify_pin(pin);
-    }
-
-    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());
-    }
-
-    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);
-    }
-    
+        HashTable<string,Value?> data = null;
+        string mechanism = null;
+        SharingAccounts accounts = new SharingAccounts();
+
+        SharingAccount? account = accounts.get_account_for_provider("flickr");
+        if (account != null) {
+            try {
+                auth_session = account.create_auth_session();
+                data = account.get_session_parameters(out mechanism);
+                debug("Got account data");
+            } catch (GLib.Error e) {
+                debug("EVENT: couldn't create session for account: %s",
+                      e.message);
+            }
+        }
+
+        if (data == null) {
+            warning ("No account authentication data");
+            host.post_error(new Spit.Publishing.PublishingError.SERVICE_ERROR(
+                "Error while accessing the account"));
+            return;
+        }
+
+        session.set_api_credentials(data.lookup("ConsumerKey").get_string(),
+                                    data.lookup("ConsumerSecret").get_string());
+
+        var windowId = host.get_dialog_xid();
+        if (windowId != 0) {
+            data.insert("WindowId", windowId);
+        }
+
+        auth_session.process(data, mechanism, on_processed);
+        host.set_service_locked(false);
+    }
+
+    public void on_processed(Signon.AuthSession self, owned HashTable<string,Value?> session_data, GLib.Error error){
+        /* TODO: handle errors */
+
+        host.set_service_locked(true);
+        host.install_account_fetch_wait_pane();
+
+        string token = session_data.lookup("AccessToken").get_string();
+        string token_secret = session_data.lookup("TokenSecret").get_string();
+        string username = session_data.lookup("username").get_string();
+        debug("Access Token: %s, %s, %s", token, token_secret, username);
+        session.set_access_phase_credentials(token, token_secret, username);
+    }
+
     private void on_session_authenticated() {
         if (!is_running())
             return;
@@ -360,129 +334,6 @@
         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;
-        
-        string[] key_value_pairs = response.split("&");
-        foreach (string pair in key_value_pairs) {
-            string[] split_pair = pair.split("=");
-            
-            if (split_pair.length != 2)
-                host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
-                    "'%s' isn't a valid response to an OAuth authentication request"));
-
-            if (split_pair[0] == "oauth_token")
-                oauth_token = split_pair[1];
-            else if (split_pair[0] == "oauth_token_secret")
-                oauth_token_secret = split_pair[1];
-        }
-        
-        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"));
-        
-        
-        on_authentication_token_available(oauth_token, oauth_token_secret);
-    }
-    
-    private void do_launch_system_browser(string token) {
-        string login_uri = "http://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");
-        
-        PinEntryPane pin_entry_pane = new PinEntryPane();
-        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[] key_value_pairs = response.split("&");
-
-        string? token = null;
-        string? token_secret = null;
-        string? username = null;
-        foreach (string key_value_pair in key_value_pairs) {
-            string[] split_pair = key_value_pair.split("=");
-
-            if (split_pair.length != 2)
-                continue;
-
-            string key = split_pair[0];
-            string value = split_pair[1];
-            
-            if (key == "oauth_token")
-                token = value;
-            else if (key == "oauth_token_secret")
-                token_secret = value;
-            else if (key == "username")
-                username = value;
-        }
-        
-        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");
 
@@ -752,7 +603,7 @@
         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_api_key());
     }
     
     public Transaction.with_uri(Session session, string uri,
@@ -764,7 +615,7 @@
         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_api_key());
     }
 
     public override void execute() throws Spit.Publishing.PublishingError {
@@ -822,22 +673,6 @@
     }
 }
 
-internal class AuthenticationRequestTransaction : Transaction {
-    public AuthenticationRequestTransaction(Session session) {
-        base.with_uri(session, "http://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, "http://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);
@@ -864,7 +699,7 @@
         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_api_key());
         add_authorization_header_field("oauth_token", session.get_access_phase_token());
         
         add_argument("is_public", ("%d".printf(parameters.visibility_specification.everyone_level)));
@@ -919,11 +754,11 @@
 }
 
 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? api_key = API_KEY;
+    private string? api_secret = API_SECRET;
 
     public Session() {
         base(ENDPOINT_URL);
@@ -983,16 +818,12 @@
         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 = api_secret + "&" + access_phase_token_secret;
         } else {
-            debug("neither access phase nor request phase token secrets available; using API " +
+            debug("access token secret not available; using API " +
                 "key as signing key");
 
-            signing_key = API_SECRET + "&";
+            signing_key = api_secret + "&";
         }
         
         string signature_base_string = http_method + "&" + Soup.URI.encode(
@@ -1015,11 +846,11 @@
             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_api_credentials(string api_key, string api_secret) {
+        this.api_key = api_key;
+        this.api_secret = api_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,11 +871,11 @@
         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_api_key() {
+        assert(api_key != null);
+        return api_key;
     }
-    
+
     public string get_access_phase_token() {
         assert(access_phase_token != null);
         return access_phase_token;

=== modified file 'plugins/shotwell-publishing/Makefile'
--- old/plugins/shotwell-publishing/Makefile	2012-08-21 19:17:33 +0000
+++ new/plugins/shotwell-publishing/Makefile	2012-08-21 19:18:37 +0000
@@ -16,6 +16,7 @@
 	PicasaPublishing.vala \
 	FlickrPublishing.vala \
 	YouTubePublishing.vala \
+	../common/accounts.vala \
 	../common/RESTSupport.vala \
 	../common/ui.vala
 

=== modified file 'plugins/shotwell-publishing/PicasaPublishing.vala'
--- old/plugins/shotwell-publishing/PicasaPublishing.vala	2012-08-21 19:17:33 +0000
+++ new/plugins/shotwell-publishing/PicasaPublishing.vala	2012-08-21 19:18:37 +0000
@@ -4,6 +4,8 @@
  * (version 2.1 or later).  See the COPYING file in this distribution. 
  */
 
+using Publishing.Accounts;
+
 public class PicasaService : Object, Spit.Pluggable, Spit.Publishing.Service {
     private const string ICON_FILENAME = "picasa.png";
 
@@ -50,6 +52,11 @@
     
     public void activation(bool enabled) {
     }
+
+    public bool is_enabled() {
+        SharingAccounts accounts = new SharingAccounts();
+        return accounts.get_account_for_provider("google") != null;
+    }
 }
 
 namespace Publishing.Picasa {
@@ -68,6 +75,7 @@
     private Album[] albums = null;
     private PublishingParameters parameters = null;
     private Spit.Publishing.Publisher.MediaType media_type = Spit.Publishing.Publisher.MediaType.NONE;
+    private Signon.AuthSession auth_session = null;
 
     public PicasaPublisher(Spit.Publishing.Service service,
         Spit.Publishing.PluginHost host) {
@@ -123,6 +131,29 @@
         return result;
     }
     
+    private string? extract_username(Xml.Node* document_root) throws Spit.Publishing.PublishingError {
+        Xml.Node* doc_node_iter = null;
+        if (document_root->name == "feed")
+            doc_node_iter = document_root->children;
+        else
+            throw new Spit.Publishing.PublishingError.MALFORMED_RESPONSE("response root node " +
+                "isn't a <feed>");
+
+        for ( ; doc_node_iter != null; doc_node_iter = doc_node_iter->next) {
+            if (doc_node_iter->name != "author")
+                continue;
+
+            Xml.Node* author_node_iter = doc_node_iter->children;
+            for ( ; author_node_iter != null; author_node_iter = author_node_iter->next) {
+                if (author_node_iter->name == "name")
+                    return author_node_iter->get_content();
+            }
+
+        }
+
+        return null;
+    }
+
     internal string? get_persistent_username() {
         return host.get_config_string("user_name", null);
     }
@@ -131,6 +162,10 @@
         return host.get_config_string("auth_token", null);
     }
     
+    internal string? get_persistent_auth_method() {
+        return host.get_config_string("auth_method", null);
+    }
+
     internal void set_persistent_username(string username) {
         host.set_config_string("user_name", username);
     }
@@ -139,11 +174,16 @@
         host.set_config_string("auth_token", auth_token);
     }
 
+    internal void set_persistent_auth_method(string auth_method) {
+        host.set_config_string("auth_method", auth_method);
+    }
+
     internal void invalidate_persistent_session() {
         debug("invalidating persisted Picasa Web Albums session.");
 
-        host.unset_config_key("user_name");
-        host.unset_config_key("auth_token");
+        set_persistent_username("");
+        set_persistent_auth_token("");
+        set_persistent_auth_method("");
     }
     
     internal bool is_persistent_session_available() {
@@ -164,76 +204,89 @@
         
         debug("EVENT: user clicked 'Login' in welcome pane.");
 
-        do_show_credentials_pane(CredentialsPane.Mode.INTRO);
-    }
-
-    private void on_credentials_go_back() {
-        if (!is_running())
-            return;
-            
-        debug("EVENT: user clicked 'Go Back' in credentials pane.");
-
-        do_show_service_welcome_pane();
-    }
-
-    private void on_credentials_login(string username, string password) {
-        if (!is_running())
-            return;    
-    
-        debug("EVENT: user clicked 'Login' in credentials pane.");
-
-        this.username = username;
-
-        do_network_login(username, password);
-    }
-
-    private void on_token_fetch_complete(Publishing.RESTSupport.Transaction txn) {
-        txn.completed.disconnect(on_token_fetch_complete);
-        txn.network_error.disconnect(on_token_fetch_error);
-
-        if (!is_running())
-            return;
-
-        if (session.is_authenticated()) // ignore these events if the session is already auth'd
-            return;
-
-        debug("EVENT: network transaction to fetch token for login completed successfully.");
-        
-        int index = txn.get_response().index_of("Auth=");
-        string auth_substring = (index >= 0) ? txn.get_response()[index:txn.get_response().length] : "";
-        auth_substring = auth_substring.chomp();
-        string auth_token = auth_substring.substring(5);
+        do_hosted_web_authentication();
+    }
+
+    public void on_processed(Signon.AuthSession self, owned HashTable<string,Value?>? session_data, GLib.Error error){
+        if (error != null) {
+            debug("got error: %s", error.message);
+            /* TODO: handle errors */
+            return;
+        }
+
+        host.set_service_locked(true);
+        host.install_account_fetch_wait_pane();
 
         session.authenticated.connect(on_session_authenticated);
-        session.authenticate(auth_token, username);
+
+        if (session_data.lookup("AccessToken") != null) {
+            string token = session_data.lookup("AccessToken").get_string();
+            debug("OAuth Access Token: %s", token);
+            session.authenticate(token, null, "OAuth2");
+        } else if (session_data.lookup("AuthToken") != null) {
+            string token = session_data.lookup("AuthToken").get_string();
+            debug("ClientLogin Access Token: %s", token);
+            session.authenticate(token, null, "ClientLogin");
+        } else {
+            debug("Access token not present!");
+        }
     }
 
-    private void on_token_fetch_error(Publishing.RESTSupport.Transaction bad_txn,
-        Spit.Publishing.PublishingError err) {
-        bad_txn.completed.disconnect(on_token_fetch_complete);
-        bad_txn.network_error.disconnect(on_token_fetch_error);
-
-        if (!is_running())
-            return;
-
-        if (session.is_authenticated()) // ignore these events if the session is already auth'd
-            return;
-
-        debug("EVENT: network transaction to fetch token for login failed; response = '%s'.",
-            bad_txn.get_response());
-
-        // HTTP error 403 is invalid authentication -- if we get this error during token fetch
-        // then we can just show the login screen again with a retry message; if we get any error
-        // other than 403 though, we can't recover from it, so just post the error to the user
-        if (bad_txn.get_status_code() == 403) {
-            if (bad_txn.get_response().contains("CaptchaRequired"))
-                do_show_credentials_pane(CredentialsPane.Mode.ADDITIONAL_SECURITY);
-            else
-                do_show_credentials_pane(CredentialsPane.Mode.FAILED_RETRY);
-        }
-        else {
-            host.post_error(err);
-        }
+    private void do_hosted_web_authentication() {
+        debug("ACTION: doing hosted web authentication.");
+
+        HashTable<string,Value?> data = null;
+        string mechanism = "user_agent";
+
+        SharingAccounts accounts =
+            new SharingAccounts();
+
+        SharingAccount? account = accounts.get_account_for_provider("google");
+        if (account != null) {
+            try {
+                auth_session = account.create_auth_session();
+                data = account.get_session_parameters(out mechanism);
+                debug("Got account data");
+            } catch (GLib.Error e) {
+                debug("EVENT: couldn't create session for account: %s",
+                      e.message);
+            }
+        }
+
+        if (auth_session == null) {
+            debug("Creating unbound session");
+            try {
+                auth_session = new Signon.AuthSession(0, "oauth2");
+            } catch (GLib.Error e) {
+                debug("EVENT: AuthSession creation failed: %s", e.message);
+                return;
+            }
+        }
+
+        if (data == null) {
+            debug("Using own params");
+            data = new HashTable<string,Value?>(str_hash, str_equal);
+            data.insert("Host", "accounts.google.com");
+            data.insert("AuthPath", "o/oauth2/auth");
+            data.insert("ClientId", "1041829795610-htf69c529db58qcq8jvf58bijn1ie3oi.apps.googleusercontent.com");
+            data.insert("RedirectUri", "http://www.mardy.it/oauth2callback");
+            string[] scopes = {
+                "https://picasaweb.google.com/data/"
+            };
+            data.insert("Scope", scopes);
+            string[] response_type = {
+                "token"
+            };
+            data.insert("ResponseType", response_type);
+        }
+
+        var windowId = host.get_dialog_xid();
+        if (windowId != 0) {
+            data.insert("WindowId", windowId);
+        }
+
+        auth_session.process(data, mechanism, on_processed);
+        host.set_service_locked(false);
     }
 
     private void on_session_authenticated() {
@@ -271,25 +324,7 @@
         debug("EVENT: fetching account and album information failed; response = '%s'.",
             bad_txn.get_response());
 
-        if (bad_txn.get_status_code() == 404) {
-            // if we get a 404 error (resource not found) on the initial album fetch, then the
-            // user's album feed doesn't exist -- this occurs when the user has a valid Google
-            // account but it hasn't yet been set up for use with Picasa. In this case, we
-            // re-display the credentials capture pane with an "account not set up" message.
-            // In addition, we deauthenticate the session. Deauth is neccessary because we
-            // did previously auth the user's account. If we get any other kind of error, we can't
-            // recover, so just post it to the user
-            session.deauthenticate();
-            do_show_credentials_pane(CredentialsPane.Mode.NOT_SET_UP);
-        } else if (bad_txn.get_status_code() == 403) {
-            // if we get a 403 error (authentication failed) then we need to return to the login
-            // screen because the user's auth token is no longer valid and he or she needs to
-            // login again to obtain a new one
-            session.deauthenticate();
-            do_show_credentials_pane(CredentialsPane.Mode.INTRO);
-        } else {
-            host.post_error(err);
-        }
+        host.post_error(err);
     }
 
     private void on_publishing_options_logout() {
@@ -414,34 +449,6 @@
         host.install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_service_welcome_login);
     }
     
-    private void do_show_credentials_pane(CredentialsPane.Mode mode) {
-        debug("ACTION: showing credentials capture pane in %s mode.", mode.to_string());
-        
-        CredentialsPane creds_pane = new CredentialsPane(host, mode);
-        creds_pane.go_back.connect(on_credentials_go_back);
-        creds_pane.login.connect(on_credentials_login);
-
-        host.install_dialog_pane(creds_pane);
-    }
-
-    private void do_network_login(string username, string password) {
-        debug("ACTION: running network login transaction for user = '%s'.", username);
-        
-        host.install_login_wait_pane();
-
-        TokenFetchTransaction fetch_trans = new TokenFetchTransaction(session, username, password);
-        fetch_trans.network_error.connect(on_token_fetch_error);
-        fetch_trans.completed.connect(on_token_fetch_complete);
-
-        try {
-            fetch_trans.execute();
-        } catch (Spit.Publishing.PublishingError err) {
-            // 403 errors are recoverable, so don't post the error to our host immediately;
-            // instead, try to recover from it
-            on_token_fetch_error(fetch_trans, err);
-        }
-    }
-    
     private void do_save_auth_info() {
         debug("ACTION: saving authentication information to configuration system.");
         
@@ -449,6 +456,7 @@
         
         set_persistent_auth_token(session.get_auth_token());
         set_persistent_username(session.get_username());
+        set_persistent_auth_method(session.get_auth_method());
     }
 
     private void do_fetch_account_information() {
@@ -485,6 +493,8 @@
 
         try {
             albums = extract_albums(response_doc.get_root_node());
+            username = extract_username(response_doc.get_root_node());
+            set_persistent_username(username);
         } catch (Spit.Publishing.PublishingError err) {
             host.post_error(err);
             return;
@@ -567,7 +577,8 @@
 
         if (is_persistent_session_available()) {
             username = get_persistent_username();
-            session.authenticate(get_persistent_auth_token(), get_persistent_username());
+            session.authenticate(get_persistent_auth_token(), get_persistent_username(),
+                                 get_persistent_auth_method());
             do_fetch_account_information();
         } else {
             do_show_service_welcome_pane();
@@ -598,6 +609,7 @@
 internal class Session : Publishing.RESTSupport.Session {
     private string? auth_token = null;
     private string? username = null;
+    private string? auth_method = null;
 
     public Session() {
     }
@@ -606,9 +618,10 @@
         return (auth_token != null);
     }
 
-    public void authenticate(string auth_token, string username) {
+    public void authenticate(string auth_token, string? username, string auth_method) {
         this.auth_token = auth_token;
         this.username = username;
+        this.auth_method = auth_method;
         
         notify_authenticated();
     }
@@ -616,6 +629,7 @@
     public void deauthenticate() {
         auth_token = null;
         username = null;
+        auth_method = null;
     }
 
     public string? get_username() {
@@ -625,19 +639,9 @@
     public string? get_auth_token() {
         return auth_token;
     }
-}
-
-internal class TokenFetchTransaction : Publishing.RESTSupport.Transaction {
-    private const string ENDPOINT_URL = "https://www.google.com/accounts/ClientLogin";
-
-    public TokenFetchTransaction(Session session, string username, string password) {
-        base.with_endpoint_url(session, ENDPOINT_URL);
-
-        add_argument("accountType", "HOSTED_OR_GOOGLE");
-        add_argument("Email", username);
-        add_argument("Passwd", password);
-        add_argument("service", "lh2");
-        add_argument("source", "yorba-shotwell-" + _VERSION);
+
+    public string? get_auth_method() {
+        return auth_method;
     }
 }
 
@@ -652,12 +656,19 @@
         base.with_endpoint_url(session, endpoint_url, method);
         assert(session.is_authenticated());
 
-        add_header("Authorization", "GoogleLogin auth=%s".printf(session.get_auth_token()));
+        if (session.get_auth_method() == "ClientLogin") {
+            add_header("Authorization",
+                       "GoogleLogin auth=%s".printf(session.get_auth_token()));
+        } else {
+            add_argument("access_token", session.get_auth_token());
+            add_header("Authorization",
+                       "Bearer %s".printf(session.get_auth_token()));
+        }
     }
 }
 
 internal class AlbumDirectoryTransaction : AuthenticatedTransaction {
-    private const string ENDPOINT_URL = "http://picasaweb.google.com/data/feed/api/user/" +
+    private const string ENDPOINT_URL = "https://picasaweb.google.com/data/feed/api/user/" +
         "default";
 
     public AlbumDirectoryTransaction(Session session) {
@@ -674,7 +685,7 @@
 }
 
 private class AlbumCreationTransaction : AuthenticatedTransaction {
-    private const string ENDPOINT_URL = "http://picasaweb.google.com/data/feed/api/user/" +
+    private const string ENDPOINT_URL = "https://picasaweb.google.com/data/feed/api/user/" +
         "default";
     private const string ALBUM_ENTRY_TEMPLATE = "<?xml version='1.0' encoding='utf-8'?><entry xmlns='http://www.w3.org/2005/Atom' xmlns:gphoto='http://schemas.google.com/photos/2007'><title type='text'>%s</title><gphoto:access>%s</gphoto:access><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/photos/2007#album'></category></entry>";
     
@@ -747,8 +758,14 @@
         // that we've been building up
         Soup.Message outbound_message =
             soup_form_request_new_from_multipart(get_endpoint_url(), message_parts);
-        outbound_message.request_headers.append("Authorization", "GoogleLogin auth=%s".printf(session.get_auth_token()));
         set_message(outbound_message);
+        if (session.get_auth_method() == "ClientLogin") {
+            add_header("Authorization",
+                       "GoogleLogin auth=%s".printf(session.get_auth_token()));
+        } else {
+            add_header("Authorization",
+                       "Bearer %s".printf(session.get_auth_token()));
+        }
 
         // send the message and get its response
         set_is_executed(true);
@@ -756,199 +773,6 @@
     }
 }
 
-internal class CredentialsPane : Spit.Publishing.DialogPane, GLib.Object {
-    public enum Mode {
-        INTRO,
-        FAILED_RETRY,
-        NOT_SET_UP,
-        ADDITIONAL_SECURITY;
-
-        public string to_string() {
-            switch (this) {
-                case Mode.INTRO:
-                    return "INTRO";
-
-                case Mode.FAILED_RETRY:
-                    return "FAILED_RETRY";
-
-                case Mode.NOT_SET_UP:
-                    return "NOT_SET_UP";
-
-                case Mode.ADDITIONAL_SECURITY:
-                    return "ADDITIONAL_SECURITY";
-
-                default:
-                    error("unrecognized CredentialsPane.Mode enumeration value");
-            }
-        }
-    }
-
-    private LegacyCredentialsPane wrapped = null;
-
-    public signal void go_back();
-    public signal void login(string email, string password);
-
-    public CredentialsPane(Spit.Publishing.PluginHost host, Mode mode = Mode.INTRO,
-        string? username = null) {
-            wrapped = new LegacyCredentialsPane(host, mode, username);
-    }
-    
-    protected void notify_go_back() {
-        go_back();
-    }
-    
-    protected void notify_login(string email, string password) {
-        login(email, password);
-    }
-
-    public Gtk.Widget get_widget() {
-        return wrapped;
-    }
-    
-    public Spit.Publishing.DialogPane.GeometryOptions get_preferred_geometry() {
-        return Spit.Publishing.DialogPane.GeometryOptions.NONE;
-    }
-    
-    public void on_pane_installed() {        
-        wrapped.go_back.connect(notify_go_back);
-        wrapped.login.connect(notify_login);
-        
-        wrapped.installed();
-    }
-    
-    public void on_pane_uninstalled() {
-        wrapped.go_back.disconnect(notify_go_back);
-        wrapped.login.disconnect(notify_login);
-    }
-}
-
-internal class LegacyCredentialsPane : Gtk.VBox {
-    private const string INTRO_MESSAGE = _("Enter the email address and password associated with your Picasa Web Albums account.");
-    private const string FAILED_RETRY_MESSAGE = _("Picasa Web Albums didn't recognize the email address and password you entered. To try again, re-enter your email address and password below.");
-    private const string NOT_SET_UP_MESSAGE = _("The email address and password you entered correspond to a Google account that isn't set up for use with Picasa Web Albums. You can set up most accounts by using your browser to log into the Picasa Web Albums site at least once. To try again, re-enter your email address and password below.");
-    private const string ADDITIONAL_SECURITY_MESSAGE = _("The email address and password you entered correspond to a Google account that has been tagged as requiring additional security. You can clear this tag by using your browser to log into Picasa Web Albums. To try again, re-enter your email address and password below.");
-    
-    private const int UNIFORM_ACTION_BUTTON_WIDTH = 102;
-    private const int VERTICAL_SPACE_HEIGHT = 32;
-    public const int STANDARD_CONTENT_LABEL_WIDTH = 500;
-
-    private weak Spit.Publishing.PluginHost host = null;
-    private Gtk.Entry email_entry;
-    private Gtk.Entry password_entry;
-    private Gtk.Button login_button;
-    private Gtk.Button go_back_button;
-    private string? username = null;
-
-    public signal void go_back();
-    public signal void login(string email, string password);
-
-    public LegacyCredentialsPane(Spit.Publishing.PluginHost host, CredentialsPane.Mode mode =
-        CredentialsPane.Mode.INTRO, string? username = null) {
-        this.host = host;
-        this.username = username;
-
-        add(gtk_vspacer(VERTICAL_SPACE_HEIGHT));
-
-        Gtk.Label intro_message_label = new Gtk.Label("");
-        intro_message_label.set_line_wrap(true);
-        add(intro_message_label);
-        intro_message_label.set_size_request(STANDARD_CONTENT_LABEL_WIDTH, -1);
-        intro_message_label.set_alignment(0.5f, 0.0f);
-        switch (mode) {
-            case CredentialsPane.Mode.INTRO:
-                intro_message_label.set_text(INTRO_MESSAGE);
-            break;
-
-            case CredentialsPane.Mode.FAILED_RETRY:
-                intro_message_label.set_markup("<b>%s</b>\n\n%s".printf(_(
-                    "Unrecognized User"), FAILED_RETRY_MESSAGE));
-            break;
-
-            case CredentialsPane.Mode.NOT_SET_UP:
-                intro_message_label.set_markup("<b>%s</b>\n\n%s".printf(_("Account Not Ready"),
-                    NOT_SET_UP_MESSAGE));
-                add(gtk_vspacer(VERTICAL_SPACE_HEIGHT));
-            break;
-
-            case CredentialsPane.Mode.ADDITIONAL_SECURITY:
-                intro_message_label.set_markup("<b>%s</b>\n\n%s".printf(_("Additional Security Required"),
-                    ADDITIONAL_SECURITY_MESSAGE));
-                add(gtk_vspacer(VERTICAL_SPACE_HEIGHT));
-            break;
-        }
-
-        Gtk.Alignment entry_widgets_table_aligner = new Gtk.Alignment(0.5f, 0.5f, 0.0f, 0.0f);
-        Gtk.Table entry_widgets_table = new Gtk.Table(3,2, false);
-        Gtk.Label email_entry_label = new Gtk.Label.with_mnemonic(_("_Email address:"));
-        email_entry_label.set_alignment(0.0f, 0.5f);
-        Gtk.Label password_entry_label = new Gtk.Label.with_mnemonic(_("_Password:"));
-        password_entry_label.set_alignment(0.0f, 0.5f);
-        email_entry = new Gtk.Entry();
-        if (username != null)
-            email_entry.set_text(username);
-        email_entry.changed.connect(on_email_changed);
-        password_entry = new Gtk.Entry();
-        password_entry.set_visibility(false);
-        entry_widgets_table.attach(email_entry_label, 0, 1, 0, 1,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 6, 6);
-        entry_widgets_table.attach(password_entry_label, 0, 1, 1, 2,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 6, 6);
-        entry_widgets_table.attach(email_entry, 1, 2, 0, 1,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 6, 6);
-        entry_widgets_table.attach(password_entry, 1, 2, 1, 2,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 6, 6);
-        go_back_button = new Gtk.Button.with_mnemonic(_("Go _Back"));
-        go_back_button.clicked.connect(on_go_back_button_clicked);
-        Gtk.Alignment go_back_button_aligner = new Gtk.Alignment(0.0f, 0.5f, 0.0f, 0.0f);
-        go_back_button_aligner.add(go_back_button);
-        go_back_button.set_size_request(UNIFORM_ACTION_BUTTON_WIDTH, -1);
-        login_button = new Gtk.Button.with_mnemonic(_("_Login"));
-        login_button.clicked.connect(on_login_button_clicked);
-        login_button.set_sensitive(username != null);
-        Gtk.Alignment login_button_aligner = new Gtk.Alignment(1.0f, 0.5f, 0.0f, 0.0f);
-        login_button_aligner.add(login_button);
-        login_button.set_size_request(UNIFORM_ACTION_BUTTON_WIDTH, -1);
-        entry_widgets_table.attach(go_back_button_aligner, 0, 1, 2, 3,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 6, VERTICAL_SPACE_HEIGHT);
-        entry_widgets_table.attach(login_button_aligner, 1, 2, 2, 3,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
-            Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 6, VERTICAL_SPACE_HEIGHT);
-        entry_widgets_table_aligner.add(entry_widgets_table);
-        add(entry_widgets_table_aligner);
-
-        email_entry_label.set_mnemonic_widget(email_entry);
-        password_entry_label.set_mnemonic_widget(password_entry);
-
-        add(gtk_vspacer(VERTICAL_SPACE_HEIGHT));
-    }
-
-    private void on_login_button_clicked() {
-        login(email_entry.get_text(), password_entry.get_text());
-    }
-
-    private void on_go_back_button_clicked() {
-        go_back();
-    }
-
-    private void on_email_changed() {
-        login_button.set_sensitive(email_entry.get_text() != "");
-    }
-
-    public void installed() {
-        host.set_service_locked(false);
-
-        email_entry.grab_focus();
-        password_entry.set_activates_default(true);
-        login_button.can_default = true;
-        host.set_dialog_default_widget(login_button);
-    }
-}
-
 internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
     private LegacyPublishingOptionsPane wrapped = null;
 

=== modified file 'src/plugins/PublishingInterfaces.vala'
--- old/src/plugins/PublishingInterfaces.vala	2012-08-21 19:17:33 +0000
+++ new/src/plugins/PublishingInterfaces.vala	2012-08-21 19:18:37 +0000
@@ -494,6 +494,7 @@
      */
     public abstract Spit.Publishing.Publisher.MediaType get_publishable_media_type();
     
+    public abstract ulong get_dialog_xid();
     //
     // For future expansion.
     //
@@ -586,10 +587,14 @@
      */
     public abstract Spit.Publishing.Publisher.MediaType get_supported_media();
     
+    /**
+     * Checks whether the service is enabled.
+     */
+    public virtual bool is_enabled() { return true; }
+
     //
     // For future expansion.
     //
-    protected virtual void reserved0() {}
     protected virtual void reserved1() {}
     protected virtual void reserved2() {}
     protected virtual void reserved3() {}

=== modified file 'src/publishing/PublishingPluginHost.vala'
--- old/src/publishing/PublishingPluginHost.vala	2012-08-21 19:17:33 +0000
+++ new/src/publishing/PublishingPluginHost.vala	2012-08-21 19:18:37 +0000
@@ -232,6 +232,13 @@
         
         return report_plugin_upload_progress;
     }
+
+    public ulong get_dialog_xid() {
+        if (dialog == null) return 0;
+
+        Gdk.Window window = dialog.get_window();
+        return Gdk.X11Window.get_xid(window);
+    }
 }
 
 }

=== modified file 'src/publishing/PublishingUI.vala'
--- old/src/publishing/PublishingUI.vala	2012-08-21 19:17:33 +0000
+++ new/src/publishing/PublishingUI.vala	2012-08-21 19:18:37 +0000
@@ -326,6 +326,7 @@
         Spit.Publishing.Service[] all_services = load_all_services();
 
         foreach (Spit.Publishing.Service service in all_services) {
+            if (!service.is_enabled()) continue;
             
             if (has_photos && !has_videos) {
                 if ((service.get_supported_media() & Spit.Publishing.Publisher.MediaType.PHOTO) != 0)

