[gthumb: 3/22] [picasaweb] read the album list



commit a52a67ac522268f607d0f7ca4cc359cdc57edbab
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sun Jan 24 18:06:37 2010 +0100

    [picasaweb] read the album list

 extensions/picasaweb/Makefile.am                   |    6 +-
 extensions/picasaweb/data/ui/Makefile.am           |    8 +-
 .../picasaweb/data/ui/export-to-picasaweb.ui       |   59 ++--
 .../data/ui/picasa-web-account-manager.ui          |  134 ++++++++
 .../data/ui/picasa-web-account-properties.ui       |  143 ++++++++
 extensions/picasaweb/data/ui/picasaweb-account.ui  |  358 --------------------
 extensions/picasaweb/dlg-export-to-picasaweb.c     |  356 +++++++++++---------
 extensions/picasaweb/google-connection.c           |  258 +++++++++++++-
 extensions/picasaweb/google-connection.h           |   40 ++-
 .../picasaweb/gth-account-properties-dialog.c      |  114 ++++++-
 .../picasaweb/gth-account-properties-dialog.h      |    3 +-
 extensions/picasaweb/picasa-web-album.c            |  284 ++++++++++++++++
 extensions/picasaweb/picasa-web-album.h            |   85 +++++
 extensions/picasaweb/picasa-web-service.c          |  292 ++++++++++++++++-
 extensions/picasaweb/picasa-web-service.h          |   34 ++-
 15 files changed, 1562 insertions(+), 612 deletions(-)
---
diff --git a/extensions/picasaweb/Makefile.am b/extensions/picasaweb/Makefile.am
index 4a86d7a..d91c56b 100644
--- a/extensions/picasaweb/Makefile.am
+++ b/extensions/picasaweb/Makefile.am
@@ -19,13 +19,15 @@ libpicasaweb_la_SOURCES = 		\
 	gth-account-properties-dialog.c	\
 	gth-account-properties-dialog.h	\
 	main.c				\
+	picasa-web-album.c		\
+	picasa-web-album.h		\
 	picasa-web-service.c		\
 	picasa-web-service.h		\
 	preferences.h
 
-libpicasaweb_la_CPPFLAGS = $(GTHUMB_CFLAGS) $(LIBSOUP_CFLAGS) $(GNOME_KEYRING_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb 
+libpicasaweb_la_CFLAGS = $(GTHUMB_CFLAGS) $(LIBSOUP_CFLAGS) $(GNOME_KEYRING_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb 
 libpicasaweb_la_LDFLAGS = $(EXTENSION_LIBTOOL_FLAGS)
-libpicasaweb_la_LIBADD = $(GTHUMB_LIBS)
+libpicasaweb_la_LIBADD = $(GTHUMB_LIBS) 
 libpicasaweb_la_DEPENDENCIES = $(top_builddir)/gthumb/gthumb$(EXEEXT)
 
 extensioninidir = $(extensiondir)
diff --git a/extensions/picasaweb/data/ui/Makefile.am b/extensions/picasaweb/data/ui/Makefile.am
index c5ff2c3..c8d5702 100644
--- a/extensions/picasaweb/data/ui/Makefile.am
+++ b/extensions/picasaweb/data/ui/Makefile.am
@@ -1,7 +1,9 @@
 uidir = $(pkgdatadir)/ui
-ui_DATA = 			\
-	export-to-picasaweb.ui	\
-	picasaweb-account.ui
+ui_DATA = 					\
+	export-to-picasaweb.ui			\
+	picasa-web-account-manager.ui		\
+	picasa-web-account-properties.ui	\
+	picasa-web-album-properties.ui
 EXTRA_DIST = $(ui_DATA)
 
 -include $(top_srcdir)/git.mk
diff --git a/extensions/picasaweb/data/ui/export-to-picasaweb.ui b/extensions/picasaweb/data/ui/export-to-picasaweb.ui
index 9ee5b76..2c7e1c1 100644
--- a/extensions/picasaweb/data/ui/export-to-picasaweb.ui
+++ b/extensions/picasaweb/data/ui/export-to-picasaweb.ui
@@ -2,6 +2,34 @@
 <interface>
   <requires lib="gtk+" version="2.16"/>
   <!-- interface-naming-policy project-wide -->
+  <object class="GtkListStore" id="account_liststore">
+    <columns>
+      <!-- column-name account -->
+      <column type="gchararray"/>
+      <!-- column-name type -->
+      <column type="gint"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name icon -->
+      <column type="gchararray"/>
+      <!-- column-name sensitive -->
+      <column type="gboolean"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="album_liststore">
+    <columns>
+      <!-- column-name album -->
+      <column type="GObject"/>
+      <!-- column-name type -->
+      <column type="gint"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name icon -->
+      <column type="gchararray"/>
+      <!-- column-name sensitive -->
+      <column type="gboolean"/>
+    </columns>
+  </object>
   <object class="GtkDialog" id="export_dialog">
     <property name="border_width">5</property>
     <property name="title" translatable="yes">Upload to Picasa Web Albums</property>
@@ -59,6 +87,7 @@
                 </child>
               </object>
               <packing>
+                <property name="expand">False</property>
                 <property name="position">0</property>
               </packing>
             </child>
@@ -74,6 +103,7 @@
                     <property name="use_underline">True</property>
                   </object>
                   <packing>
+                    <property name="expand">False</property>
                     <property name="position">0</property>
                   </packing>
                 </child>
@@ -161,7 +191,6 @@
             </child>
           </object>
           <packing>
-            <property name="expand">False</property>
             <property name="position">1</property>
           </packing>
         </child>
@@ -232,34 +261,6 @@
     <property name="visible">True</property>
     <property name="stock">gtk-goto-top</property>
   </object>
-  <object class="GtkListStore" id="album_liststore">
-    <columns>
-      <!-- column-name album data -->
-      <column type="gpointer"/>
-      <!-- column-name type -->
-      <column type="gint"/>
-      <!-- column-name name -->
-      <column type="gchararray"/>
-      <!-- column-name icon name -->
-      <column type="gchararray"/>
-      <!-- column-name sensitive -->
-      <column type="gboolean"/>
-    </columns>
-  </object>
-  <object class="GtkListStore" id="account_liststore">
-    <columns>
-      <!-- column-name account data -->
-      <column type="gpointer"/>
-      <!-- column-name type -->
-      <column type="gint"/>
-      <!-- column-name name -->
-      <column type="gchararray"/>
-      <!-- column-name icon name -->
-      <column type="gchararray"/>
-      <!-- column-name sensitive -->
-      <column type="gboolean"/>
-    </columns>
-  </object>
   <object class="GtkDialog" id="choose_account_dialog">
     <property name="border_width">5</property>
     <property name="type_hint">normal</property>
diff --git a/extensions/picasaweb/data/ui/picasa-web-account-manager.ui b/extensions/picasaweb/data/ui/picasa-web-account-manager.ui
new file mode 100644
index 0000000..ea42ea6
--- /dev/null
+++ b/extensions/picasaweb/data/ui/picasa-web-account-manager.ui
@@ -0,0 +1,134 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkListStore" id="accounts_liststore">
+    <columns>
+      <!-- column-name account -->
+      <column type="gpointer"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkVBox" id="account_manager">
+    <property name="visible">True</property>
+    <property name="border_width">5</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkLabel" id="label3">
+        <property name="visible">True</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">Accounts:</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHBox" id="hbox1">
+        <property name="visible">True</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="hscrollbar_policy">automatic</property>
+            <property name="vscrollbar_policy">automatic</property>
+            <property name="shadow_type">in</property>
+            <child>
+              <object class="GtkTreeView" id="account_treeview">
+                <property name="width_request">300</property>
+                <property name="height_request">150</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="model">accounts_liststore</property>
+                <property name="headers_visible">False</property>
+                <property name="headers_clickable">False</property>
+                <property name="search_column">1</property>
+                <child>
+                  <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+                    <child>
+                      <object class="GtkCellRendererText" id="cellrenderertext1"/>
+                      <attributes>
+                        <attribute name="text">1</attribute>
+                      </attributes>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox3">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkButton" id="button6">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <child>
+                  <object class="GtkImage" id="image1">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-add</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button7">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <child>
+                  <object class="GtkImage" id="image2">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-properties</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button8">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <child>
+                  <object class="GtkImage" id="image3">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-delete</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/extensions/picasaweb/data/ui/picasa-web-account-properties.ui b/extensions/picasaweb/data/ui/picasa-web-account-properties.ui
new file mode 100644
index 0000000..de138f3
--- /dev/null
+++ b/extensions/picasaweb/data/ui/picasa-web-account-properties.ui
@@ -0,0 +1,143 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkListStore" id="accounts_liststore">
+    <columns>
+      <!-- column-name account -->
+      <column type="gpointer"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkVBox" id="account_properties">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkTable" id="table1">
+        <property name="visible">True</property>
+        <property name="border_width">5</property>
+        <property name="n_rows">2</property>
+        <property name="n_columns">2</property>
+        <property name="column_spacing">6</property>
+        <property name="row_spacing">6</property>
+        <child>
+          <object class="GtkLabel" id="label1">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">_E-Mail:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">email_entry</property>
+          </object>
+          <packing>
+            <property name="x_options">GTK_FILL</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="email_entry">
+            <property name="width_request">300</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">&#x25CF;</property>
+            <property name="activates_default">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox2">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkEntry" id="password_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+                <property name="activates_default">True</property>
+                <property name="invisible_char_set">True</property>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="challange_box">
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="challenge_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Type the characters you see in the picture below. Letters are not case-sensitive.</property>
+                    <property name="wrap">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkImage" id="challenge_image">
+                    <property name="visible">True</property>
+                    <property name="ypad">12</property>
+                    <property name="stock">gtk-missing-image</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="challenge_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                    <property name="activates_default">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkAlignment" id="alignment1">
+            <property name="visible">True</property>
+            <property name="top_padding">5</property>
+            <child>
+              <object class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="yalign">0</property>
+                <property name="label" translatable="yes">_Password:</property>
+                <property name="use_underline">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+            <property name="x_options">GTK_FILL</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/extensions/picasaweb/dlg-export-to-picasaweb.c b/extensions/picasaweb/dlg-export-to-picasaweb.c
index 4b237ff..599ba53 100644
--- a/extensions/picasaweb/dlg-export-to-picasaweb.c
+++ b/extensions/picasaweb/dlg-export-to-picasaweb.c
@@ -29,6 +29,7 @@
 #include "dlg-export-to-picasaweb.h"
 #include "gth-account-chooser-dialog.h"
 #include "gth-account-properties-dialog.h"
+#include "picasa-web-album.h"
 #include "picasa-web-service.h"
 
 
@@ -68,6 +69,7 @@ typedef struct {
 	GList            *accounts;
 	char             *email;
 	char             *password;
+	char             *challange;
 	GList            *albums;
 	GoogleConnection *conn;
 	PicasaWebService *picasaweb;
@@ -79,10 +81,15 @@ static void
 export_dialog_destroy_cb (GtkWidget  *widget,
 			  DialogData *data)
 {
+	picasa_web_accounts_save_to_file (data->albums);
+	if (data->conn != NULL)
+		gth_task_completed (GTH_TASK (data->conn), NULL);
+
 	_g_object_unref (data->cancellable);
 	_g_object_unref (data->picasaweb);
 	_g_object_unref (data->conn);
 	_g_object_list_unref (data->albums);
+	g_free (data->challange);
 	g_free (data->password);
 	g_free (data->email);
 	_g_string_list_free (data->accounts);
@@ -103,6 +110,7 @@ export_dialog_response_cb (GtkDialog *dialog,
 		show_help_dialog (GTK_WINDOW (data->browser), "export-to-picasaweb");
 		break;
 
+	case GTK_RESPONSE_DELETE_EVENT:
 	case GTK_RESPONSE_CANCEL:
 		gtk_widget_destroy (data->dialog);
 		break;
@@ -119,7 +127,66 @@ export_dialog_response_cb (GtkDialog *dialog,
 static void
 show_export_dialog (DialogData *data)
 {
-	/* FIXME: update widgets data */
+	GtkTreeIter  iter;
+	int          current_account;
+	int          idx;
+	GList       *scan;
+
+	/* Accounts */
+
+	gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("account_liststore")));
+
+	current_account = 0;
+	for (scan = data->accounts, idx = 0; scan; scan = scan->next, idx++) {
+		char *account = scan->data;
+
+		if (g_strcmp0 (account, data->email) == 0)
+			current_account = idx;
+		gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter);
+		gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter,
+				    ACCOUNT_DATA_COLUMN, account,
+				    ACCOUNT_TYPE_COLUMN, ITEM_TYPE_ENTRY,
+				    ACCOUNT_NAME_COLUMN, account,
+				    ACCOUNT_SENSITIVE_COLUMN, TRUE,
+				    -1);
+	}
+
+	gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter);
+	gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter,
+			    ACCOUNT_TYPE_COLUMN, ITEM_TYPE_SEPARATOR,
+			    ACCOUNT_SENSITIVE_COLUMN, TRUE,
+			    -1);
+
+	gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter);
+	gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter,
+			    ACCOUNT_DATA_COLUMN, NULL,
+			    ACCOUNT_TYPE_COLUMN, ITEM_TYPE_COMMAND,
+			    ACCOUNT_ICON_COLUMN, GTK_STOCK_EDIT,
+			    ACCOUNT_NAME_COLUMN, _("Edit Accounts..."),
+			    ACCOUNT_SENSITIVE_COLUMN, TRUE,
+			    -1);
+
+	gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("account_combobox")), current_account);
+
+	/* Albums */
+
+	gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("album_liststore")));
+
+	for (scan = data->albums; scan; scan = scan->next) {
+		PicasaWebAlbum *album = scan->data;
+
+		gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter);
+		gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter,
+				    ALBUM_DATA_COLUMN, album,
+				    ALBUM_TYPE_COLUMN, ITEM_TYPE_ENTRY,
+				    ALBUM_NAME_COLUMN, album->title,
+				    ALBUM_SENSITIVE_COLUMN, TRUE,
+				    -1);
+	}
+
+	gtk_widget_set_sensitive (GET_WIDGET ("upload_button"), data->albums != NULL);
+
+	/**/
 
 	gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (data->browser));
 	gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
@@ -149,17 +216,43 @@ list_albums_ready_cb (GObject      *source_object,
 
 
 static void
+get_album_list (DialogData *data)
+{
+	if (data->picasaweb == NULL)
+		data->picasaweb = picasa_web_service_new (data->conn);
+	picasa_web_service_list_albums (data->picasaweb,
+				        "default",
+				        data->cancellable,
+				        list_albums_ready_cb,
+				        data);
+}
+
+
+#ifdef HAVE_GNOME_KEYRING
+static void
+store_password_done_cb (GnomeKeyringResult result,
+			gpointer           user_data)
+{
+	get_album_list ((DialogData *) user_data);
+}
+#endif
+
+
+static void challange_account_dialog (DialogData *data);
+
+
+static void
 connection_ready_cb (GObject      *source_object,
 		     GAsyncResult *result,
 		     gpointer      user_data)
 {
 	DialogData       *data = user_data;
 	GoogleConnection *conn = GOOGLE_CONNECTION (source_object);
-	GError           *error;
+	GError           *error = NULL;
 
 	if (! google_connection_connect_finish (conn, result, &error)) {
 		if (g_error_matches (error, GOOGLE_CONNECTION_ERROR, GOOGLE_CONNECTION_ERROR_CAPTCHA_REQUIRED)) {
-			/* FIXME: request to insert the captcha text */
+			challange_account_dialog (data);
 		}
 		else {
 			_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
@@ -168,13 +261,27 @@ connection_ready_cb (GObject      *source_object,
 		return;
 	}
 
-	if (data->picasaweb == NULL)
-		data->picasaweb = picasa_web_service_new (conn);
-	picasa_web_service_list_albums (data->picasaweb,
-				        "default",
-				        data->cancellable,
-				        list_albums_ready_cb,
-				        data);
+	if (! g_list_find_custom (data->accounts, data->email, (GCompareFunc) strcmp))
+		data->accounts = g_list_append (data->accounts, g_strdup (data->email));
+
+#ifdef HAVE_GNOME_KEYRING
+	if (gnome_keyring_is_available ()) {
+		gnome_keyring_store_password (GNOME_KEYRING_NETWORK_PASSWORD,
+					      GNOME_KEYRING_SESSION,
+					      _("Picasa Web Album"),
+					      data->password,
+					      store_password_done_cb,
+					      data,
+					      NULL,
+					      "user", data->email,
+					      "server", "picasaweb.google.com",
+					      "protocol", "http",
+					      NULL);
+		return;
+	}
+#endif
+
+	get_album_list (data);
 }
 
 
@@ -187,24 +294,78 @@ connect_to_server (DialogData *data)
 		gth_progress_dialog_add_task (GTH_PROGRESS_DIALOG (data->progress_dialog), GTH_TASK (data->conn));
 	}
 
+#ifdef HAVE_GNOME_KEYRING
+	if (data->password == NULL) {
+		if (gnome_keyring_is_available ()) {
+			gnome_keyring_find_password_sync (GNOME_KEYRING_NETWORK_PASSWORD,
+							  &data->password,
+							  "user", data->email,
+							  "server", "picasaweb.google.com",
+							  "protocol", "http",
+							  NULL);
+		}
+	}
+#endif
+
 	google_connection_connect (data->conn,
 				   data->email,
 				   data->password,
-				   NULL,
+				   data->challange,
 				   data->cancellable,
 				   connection_ready_cb,
 				   data);
 }
 
 
-#ifdef HAVE_GNOME_KEYRING
 static void
-store_password_done_cb (GnomeKeyringResult result,
-			gpointer           user_data)
+challange_account_dialog_response_cb (GtkDialog *dialog,
+				      int        response_id,
+				      gpointer   user_data)
 {
-	connect_to_server ((DialogData *) user_data);
+	DialogData *data = user_data;
+
+	switch (response_id) {
+	case GTK_RESPONSE_HELP:
+		show_help_dialog (GTK_WINDOW (dialog), "challange-picasaweb-account");
+		break;
+
+	case GTK_RESPONSE_DELETE_EVENT:
+	case GTK_RESPONSE_CANCEL:
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		gtk_widget_destroy (data->dialog);
+		break;
+
+	case GTK_RESPONSE_OK:
+		g_free (data->email);
+		g_free (data->password);
+		data->email = g_strdup (gth_account_properties_dialog_get_email (GTH_ACCOUNT_PROPERTIES_DIALOG (dialog)));
+		data->password = g_strdup (gth_account_properties_dialog_get_password (GTH_ACCOUNT_PROPERTIES_DIALOG (dialog)));
+		data->challange = g_strdup (gth_account_properties_dialog_get_challange (GTH_ACCOUNT_PROPERTIES_DIALOG (dialog)));
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		connect_to_server (data);
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+static void
+challange_account_dialog (DialogData *data)
+{
+	GtkWidget *dialog;
+
+	dialog = gth_account_properties_dialog_new (data->email, data->password, google_connection_get_challange_url (data->conn));
+	g_signal_connect (dialog,
+			  "response",
+			  G_CALLBACK (challange_account_dialog_response_cb),
+			  data);
+
+	gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->browser));
+	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+	gtk_window_present (GTK_WINDOW (dialog));
 }
-#endif
 
 
 static void
@@ -219,35 +380,21 @@ new_account_dialog_response_cb (GtkDialog *dialog,
 		show_help_dialog (GTK_WINDOW (dialog), "new-picasaweb-account");
 		break;
 
+	case GTK_RESPONSE_DELETE_EVENT:
 	case GTK_RESPONSE_CANCEL:
 		gtk_widget_destroy (GTK_WIDGET (dialog));
 		gtk_widget_destroy (data->dialog);
 		break;
 
 	case GTK_RESPONSE_OK:
-		{
-			g_free (data->email);
-			g_free (data->password);
-			data->email = g_strdup (gth_account_properties_dialog_get_email (GTH_ACCOUNT_PROPERTIES_DIALOG (dialog)));
-			data->password = g_strdup (gth_account_properties_dialog_get_password (GTH_ACCOUNT_PROPERTIES_DIALOG (dialog)));
-			data->accounts = g_list_append (data->accounts, g_strdup (data->email));
-#ifdef HAVE_GNOME_KEYRING
-			if (gnome_keyring_is_available ())
-				gnome_keyring_store_password (GNOME_KEYRING_NETWORK_PASSWORD,
-							      GNOME_KEYRING_SESSION,
-							      _("Picasa Web Album"),
-							      data->password,
-							      store_password_done_cb,
-							      data,
-							      NULL,
-							      "user", data->email,
-							      "server", "picasaweb.google.com",
-							      "protocol", "http",
-							      NULL);
-			else
-#endif
-				connect_to_server (data);
-		}
+		g_free (data->email);
+		g_free (data->password);
+		g_free (data->challange);
+		data->email = g_strdup (gth_account_properties_dialog_get_email (GTH_ACCOUNT_PROPERTIES_DIALOG (dialog)));
+		data->password = g_strdup (gth_account_properties_dialog_get_password (GTH_ACCOUNT_PROPERTIES_DIALOG (dialog)));
+		data->challange = NULL;
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		connect_to_server (data);
 		break;
 
 	default:
@@ -261,12 +408,13 @@ new_account_dialog (DialogData *data)
 {
 	GtkWidget *dialog;
 
-	dialog = gth_account_properties_dialog_new (NULL, NULL);
+	dialog = gth_account_properties_dialog_new (NULL, NULL, NULL);
 	g_signal_connect (dialog,
 			  "response",
 			  G_CALLBACK (new_account_dialog_response_cb),
 			  data);
 
+	gtk_window_set_title (GTK_WINDOW (dialog), _("New Account"));
 	gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->browser));
 	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
 	gtk_window_present (GTK_WINDOW (dialog));
@@ -289,6 +437,8 @@ account_chooser_dialog_response_cb (GtkDialog *dialog,
 	case GTK_RESPONSE_OK:
 		g_free (data->password);
 		data->password = NULL;
+		g_free (data->challange);
+		data->challange = NULL;
 		g_free (data->email);
 		data->email = g_strdup (gth_account_chooser_dialog_get_active (GTH_ACCOUNT_CHOOSER_DIALOG (dialog)));
 		if (data->email != NULL) {
@@ -321,70 +471,10 @@ account_combobox_row_separator_func (GtkTreeModel *model,
 }
 
 
-static gboolean
-album_combobox_row_separator_func (GtkTreeModel *model,
-				   GtkTreeIter  *iter,
-				   gpointer      data)
-{
-	int item_type;
-
-	gtk_tree_model_get (model, iter, ALBUM_TYPE_COLUMN, &item_type, -1);
-
-	return item_type == ITEM_TYPE_SEPARATOR;
-}
-
-
-static GList *
-picasaweb_accounts_read_from_file (void)
-{
-	GList       *accounts = NULL;
-	char        *filename;
-	char        *buffer;
-	gsize        len;
-	GError      *error;
-	DomDocument *doc;
-
-	filename = gth_user_dir_get_file (GTH_DIR_CONFIG, GTHUMB_DIR, "accounts", "picasaweb.xml", NULL);
-	g_file_get_contents (filename, &buffer, &len, &error);
-
-	doc = dom_document_new ();
-	if (dom_document_load (doc, buffer, len, &error)) {
-		DomElement *node;
-
-		node = DOM_ELEMENT (doc)->first_child;
-		if ((node != NULL) && (g_strcmp0 (node->tag_name, "accounts") == 0)) {
-			DomElement *child;
-
-			for (child = node->first_child;
-			     child != NULL;
-			     child = child->next_sibling)
-			{
-				if (strcmp (child->tag_name, "account") == 0) {
-					const char *value;
-
-					value = dom_element_get_attribute (child, "email");
-					if (value != NULL)
-						accounts = g_list_prepend (accounts, g_strdup (value));
-				}
-			}
-
-			accounts = g_list_reverse (accounts);
-		}
-	}
-
-	g_object_unref (doc);
-	g_free (buffer);
-	g_free (filename);
-
-	return accounts;
-}
-
-
 void
 dlg_export_to_picasaweb (GthBrowser *browser)
 {
-	DialogData  *data;
-	GtkTreeIter  iter;
+	DialogData *data;
 
 	data = g_new0 (DialogData, 1);
 	data->browser = browser;
@@ -398,70 +488,6 @@ dlg_export_to_picasaweb (GthBrowser *browser)
 					      account_combobox_row_separator_func,
 					      data,
 					      NULL);
-	gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (GET_WIDGET ("album_combobox")),
-					      album_combobox_row_separator_func,
-					      data,
-					      NULL);
-
-	/* Account */
-
-	data->accounts = picasaweb_accounts_read_from_file ();
-
-	/*
-	gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter);
-	gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter,
-			    ACCOUNT_DATA_COLUMN, NULL,
-			    ACCOUNT_TYPE_COLUMN, ITEM_TYPE_ENTRY,
-			    ACCOUNT_NAME_COLUMN, "",
-			    ACCOUNT_SENSITIVE_COLUMN, FALSE,
-			    -1);
-
-	gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter);
-	gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter,
-			    ACCOUNT_TYPE_COLUMN, ITEM_TYPE_SEPARATOR,
-			    ACCOUNT_SENSITIVE_COLUMN, TRUE,
-			    -1);
-
-	gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter);
-	gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter,
-			    ACCOUNT_DATA_COLUMN, NULL,
-			    ACCOUNT_TYPE_COLUMN, ITEM_TYPE_COMMAND,
-			    ACCOUNT_ICON_COLUMN, GTK_STOCK_EDIT,
-			    ACCOUNT_NAME_COLUMN, _("Edit Accounts..."),
-			    ACCOUNT_SENSITIVE_COLUMN, TRUE,
-			    -1);
-
-	gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("account_combobox")), 0);
-	*/
-
-	/* Album */
-
-	/*
-	gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter);
-	gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter,
-			    ALBUM_DATA_COLUMN, NULL,
-			    ALBUM_TYPE_COLUMN, ITEM_TYPE_ENTRY,
-			    ALBUM_NAME_COLUMN, _("(Empty)"),
-			    ALBUM_SENSITIVE_COLUMN, FALSE,
-			    -1);
-
-	gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter);
-	gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter,
-			    ALBUM_TYPE_COLUMN, ITEM_TYPE_SEPARATOR,
-			    ALBUM_SENSITIVE_COLUMN, TRUE,
-			    -1);
-
-	gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter);
-	gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter,
-			    ALBUM_DATA_COLUMN, NULL,
-			    ALBUM_TYPE_COLUMN, ITEM_TYPE_COMMAND,
-			    ALBUM_ICON_COLUMN, GTK_STOCK_EDIT,
-			    ALBUM_NAME_COLUMN, _("Edit Albums..."),
-			    ALBUM_SENSITIVE_COLUMN, TRUE,
-			    -1);
-
-	gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("album_combobox")), 0);
-	*/
 
 	/* Set the signals handlers. */
 
diff --git a/extensions/picasaweb/google-connection.c b/extensions/picasaweb/google-connection.c
index 3a2a7bd..db78092 100644
--- a/extensions/picasaweb/google-connection.c
+++ b/extensions/picasaweb/google-connection.c
@@ -27,6 +27,10 @@
 #include "google-connection.h"
 
 
+#define SOUP_LOG_LEVEL SOUP_LOGGER_LOG_BODY /* FIXME: set to SOUP_LOGGER_LOG_NONE when done */
+#define GTHUMB_SOURCE ("GNOME-" PACKAGE "-" VERSION)
+
+
 GQuark
 google_connection_error_quark (void)
 {
@@ -39,10 +43,17 @@ google_connection_error_quark (void)
 }
 
 
+/* -- GoogleConnection -- */
+
+
 struct _GoogleConnectionPrivate
 {
-	char *service;
-	char *token;
+	char               *service;
+	SoupSession        *session;
+	char               *token;
+	char               *challange_url;
+	GCancellable       *cancellable;
+	GSimpleAsyncResult *result;
 };
 
 
@@ -55,8 +66,13 @@ google_connection_finalize (GObject *object)
 	GoogleConnection *self;
 
 	self = GOOGLE_CONNECTION (object);
-	g_free (self->priv->service);
+
+	_g_object_unref (self->priv->result);
+	_g_object_unref (self->priv->cancellable);
+	g_free (self->priv->challange_url);
 	g_free (self->priv->token);
+	_g_object_unref (self->priv->session);
+	g_free (self->priv->service);
 
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -99,7 +115,12 @@ google_connection_init (GoogleConnection *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GOOGLE_TYPE_CONNECTION, GoogleConnectionPrivate);
 	self->priv->service = NULL;
+	self->priv->session = NULL;
 	self->priv->token = NULL;
+	self->priv->challange_url = NULL;
+	self->priv->cancellable = NULL;
+	self->priv->result = NULL;
+
 }
 
 
@@ -144,15 +165,218 @@ google_connection_new (const char *service)
 
 
 void
-google_connection_connect (GoogleConnection     *conn,
-			   const char           *email,
-			   const char           *password,
-			   const char           *challange,
-			   GCancellable         *cancellable,
-			   GAsyncReadyCallback   callback,
-			   gpointer              user_data)
-{
-	/* FIXME */
+google_connection_send_message (GoogleConnection    *self,
+				SoupMessage         *msg,
+				GCancellable        *cancellable,
+				GAsyncReadyCallback  callback,
+				gpointer             user_data,
+				gpointer             source_tag,
+				SoupSessionCallback  soup_session_cb,
+				gpointer             soup_session_cb_data)
+{
+	char *value;
+
+	_g_object_unref (self->priv->cancellable);
+	self->priv->cancellable = _g_object_ref (cancellable);
+
+	_g_object_unref (self->priv->result);
+	self->priv->result = g_simple_async_result_new (G_OBJECT (soup_session_cb_data),
+							callback,
+							user_data,
+							source_tag);
+
+	value = g_strconcat ("GoogleLogin auth=", self->priv->token, NULL);
+	soup_message_headers_replace (msg->request_headers, "Authorization", value);
+	g_free (value);
+
+	soup_message_headers_replace (msg->request_headers, "GData-Version", "2");
+
+	soup_session_queue_message (self->priv->session,
+				    msg,
+				    soup_session_cb,
+				    soup_session_cb_data);
+}
+
+
+GSimpleAsyncResult *
+google_connection_get_result (GoogleConnection *self)
+{
+	return self->priv->result;
+}
+
+
+static GHashTable *
+get_keys_from_message_body (SoupBuffer *body)
+{
+	GHashTable  *keys;
+	char       **lines;
+	int          i;
+
+	keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+	lines = g_strsplit (body->data, "\n", -1);
+	for (i = 0; lines[i] != NULL; i++) {
+		char **pair;
+
+		pair = g_strsplit (lines[i], "=", 2);
+		if ((pair[0] != NULL) && (pair[1] != NULL))
+			g_hash_table_insert (keys, g_strdup (pair[0]), g_strdup (pair[1]));
+
+		g_strfreev (pair);
+	}
+
+	g_strfreev (lines);
+
+	return keys;
+}
+
+
+static void
+connect_cb (SoupSession *session,
+            SoupMessage *msg,
+            gpointer     user_data)
+{
+	GoogleConnection *self = user_data;
+	SoupBuffer       *body;
+	GHashTable       *keys;
+
+	body = soup_message_body_flatten (msg->response_body);
+	keys = get_keys_from_message_body (body);
+
+	g_free (self->priv->token);
+	self->priv->token = NULL;
+
+	if (msg->status_code == 403) {
+		char   *error_name;
+		GError *error;
+		int     error_code;
+		char   *error_message;
+
+		error_name = g_hash_table_lookup (keys, "Error");
+		error_code = GOOGLE_CONNECTION_ERROR_UNKNOWN;
+		error_message = "The error is unknown or unspecified; the request contained invalid input or was malformed.";
+
+		if (error_name == NULL) {
+			/* void */
+		}
+		else if (strcmp (error_name, "BadAuthentication") == 0) {
+			error_code = GOOGLE_CONNECTION_ERROR_BAD_AUTHENTICATION;
+			error_message = "The login request used a username or password that is not recognized.";
+		}
+		else if (strcmp (error_name, "NotVerified") == 0) {
+			error_code = GOOGLE_CONNECTION_ERROR_NOT_VERIFIED;
+			error_message = "The account email address has not been verified. The user will need to access their Google account directly to resolve the issue before logging in using a non-Google application.";
+		}
+		else if (strcmp (error_name, "TermsNotAgreed") == 0) {
+			error_code = GOOGLE_CONNECTION_ERROR_TERMS_NOT_AGREED;
+			error_message = "The user has not agreed to terms. The user will need to access their Google account directly to resolve the issue before logging in using a non-Google application.";
+		}
+		else if (strcmp (error_name, "CaptchaRequired") == 0) {
+			error_code = GOOGLE_CONNECTION_ERROR_CAPTCHA_REQUIRED;
+			error_message = "A CAPTCHA is required.";
+		}
+		else if (strcmp (error_name, "AccountDeleted") == 0) {
+			error_code = GOOGLE_CONNECTION_ERROR_ACCOUNT_DELETED;
+			error_message = "The user account has been deleted.";
+		}
+		else if (strcmp (error_name, "AccountDisabled") == 0) {
+			error_code = GOOGLE_CONNECTION_ERROR_ACCOUNT_DISABLED;
+			error_message = "The user account has been disabled.";
+		}
+		else if (strcmp (error_name, "ServiceDisabled") == 0) {
+			error_code = GOOGLE_CONNECTION_ERROR_SERVICE_DISABLED;
+			error_message = "The user's access to the specified service has been disabled.";
+		}
+		else if (strcmp (error_name, "ServiceUnavailable") == 0) {
+			error_code = GOOGLE_CONNECTION_ERROR_SERVICE_UNAVAILABLE;
+			error_message = "The service is not available; try again later.";
+		}
+
+		error = g_error_new_literal (GOOGLE_CONNECTION_ERROR, error_code, error_message);
+		if (error_code == GOOGLE_CONNECTION_ERROR_CAPTCHA_REQUIRED) {
+			g_free (self->priv->challange_url);
+			self->priv->token = g_strdup (g_hash_table_lookup (keys, "CaptchaToken"));
+			self->priv->challange_url = g_strdup (g_hash_table_lookup (keys, "CaptchaUrl"));
+		}
+
+		g_simple_async_result_set_from_error (self->priv->result, error);
+		g_error_free (error);
+	}
+	else if (msg->status_code == 200) {
+		self->priv->token = g_strdup (g_hash_table_lookup (keys, "Auth"));
+		g_simple_async_result_set_op_res_gboolean (self->priv->result, TRUE);
+	}
+	else {
+		g_simple_async_result_set_error (self->priv->result,
+						 SOUP_HTTP_ERROR,
+						 msg->status_code,
+						 "%s",
+						 soup_status_get_phrase (msg->status_code));
+		g_simple_async_result_complete_in_idle (self->priv->result);
+	}
+
+	g_simple_async_result_complete_in_idle (self->priv->result);
+
+	g_hash_table_destroy (keys);
+	soup_buffer_free (body);
+}
+
+
+void
+google_connection_connect (GoogleConnection    *self,
+			   const char          *email,
+			   const char          *password,
+			   const char          *challange,
+			   GCancellable        *cancellable,
+			   GAsyncReadyCallback  callback,
+			   gpointer             user_data)
+{
+	SoupMessage *msg;
+	GHashTable  *data_set;
+
+	g_return_if_fail (email != NULL);
+	g_return_if_fail (password != NULL);
+
+	if (self->priv->session == NULL) {
+		SoupLogger *logger;
+
+		self->priv->session = soup_session_async_new_with_options (
+#ifdef HAVE_LIBSOUP_GNOME
+			SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_GNOME,
+#endif
+			NULL);
+
+		logger = soup_logger_new (SOUP_LOG_LEVEL, -1);
+		soup_session_add_feature (self->priv->session, SOUP_SESSION_FEATURE (logger));
+
+		g_object_unref (logger);
+	}
+
+	_g_object_unref (self->priv->cancellable);
+	self->priv->cancellable = _g_object_ref (cancellable);
+
+	_g_object_unref (self->priv->result);
+	self->priv->result = g_simple_async_result_new (G_OBJECT (self),
+							callback,
+							user_data,
+							google_connection_connect);
+
+	data_set = g_hash_table_new (g_str_hash, g_str_equal);
+	g_hash_table_insert (data_set, "accountType", "HOSTED_OR_GOOGLE");
+	g_hash_table_insert (data_set, "service", self->priv->service);
+	g_hash_table_insert (data_set, "Email", (char *) email);
+	g_hash_table_insert (data_set, "Passwd", (char *) password);
+	g_hash_table_insert (data_set, "source", GTHUMB_SOURCE);
+	if (self->priv->token != NULL)
+		g_hash_table_insert (data_set, "logintoken", self->priv->token);
+	if (challange != NULL)
+		g_hash_table_insert (data_set, "logincaptcha", (char *) challange);
+	msg = soup_form_request_new_from_hash ("POST",
+					       "https://www.google.com/accounts/ClientLogin";,
+					       data_set);
+	soup_session_queue_message (self->priv->session, msg, connect_cb, self);
+
+	g_object_unref (msg);
+	g_hash_table_destroy (data_set);
 }
 
 
@@ -161,13 +385,15 @@ google_connection_connect_finish (GoogleConnection  *conn,
 				  GAsyncResult      *result,
 				  GError           **error)
 {
-	/* FIXME */
-	return FALSE;
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+	else
+		return TRUE;
 }
 
 
 const char *
-google_connection_get_token (GoogleConnection*conn)
+google_connection_get_challange_url (GoogleConnection *self)
 {
-	return conn->priv->token;
+	return self->priv->challange_url;
 }
diff --git a/extensions/picasaweb/google-connection.h b/extensions/picasaweb/google-connection.h
index 4227379..948f0d9 100644
--- a/extensions/picasaweb/google-connection.h
+++ b/extensions/picasaweb/google-connection.h
@@ -24,6 +24,11 @@
 #define GOOGLE_CONNECTION_H
 
 #include <glib-object.h>
+#ifdef HAVE_LIBSOUP_GNOME
+#include <libsoup/soup-gnome.h>
+#else
+#include <libsoup/soup.h>
+#endif /* HAVE_LIBSOUP_GNOME */
 #include <gthumb.h>
 
 #define GOOGLE_SERVICE_PICASA_WEB_ALBUM "lh2"
@@ -65,18 +70,27 @@ struct _GoogleConnectionClass
 	GthTaskClass __parent_class;
 };
 
-GType                google_connection_get_type        (void) G_GNUC_CONST;
-GoogleConnection *   google_connection_new             (const char           *service);
-void                 google_connection_connect         (GoogleConnection     *conn,
-						        const char           *email,
-						        const char           *password,
-						        const char           *challange,
-						        GCancellable         *cancellable,
-						        GAsyncReadyCallback   callback,
-						        gpointer              user_data);
-gboolean             google_connection_connect_finish  (GoogleConnection     *conn,
-						        GAsyncResult         *result,
-						        GError              **error);
-const char *         google_connection_get_token       (GoogleConnection     *conn);
+GType                google_connection_get_type          (void) G_GNUC_CONST;
+GoogleConnection *   google_connection_new               (const char           *service);
+void		     google_connection_send_message      (GoogleConnection     *self,
+							  SoupMessage          *msg,
+							  GCancellable         *cancellable,
+							  GAsyncReadyCallback   callback,
+							  gpointer              user_data,
+							  gpointer              source_tag,
+							  SoupSessionCallback   soup_session_cb,
+							  gpointer              soup_session_cb_data);
+GSimpleAsyncResult * google_connection_get_result        (GoogleConnection     *self);
+void                 google_connection_connect           (GoogleConnection     *self,
+						          const char           *email,
+						          const char           *password,
+						          const char           *challange,
+						          GCancellable         *cancellable,
+						          GAsyncReadyCallback   callback,
+						          gpointer              user_data);
+gboolean             google_connection_connect_finish    (GoogleConnection     *self,
+						          GAsyncResult         *result,
+						          GError              **error);
+const char *         google_connection_get_challange_url (GoogleConnection     *self);
 
 #endif /* GOOGLE_CONNECTION_H */
diff --git a/extensions/picasaweb/gth-account-properties-dialog.c b/extensions/picasaweb/gth-account-properties-dialog.c
index c8c4ad4..0ed1924 100644
--- a/extensions/picasaweb/gth-account-properties-dialog.c
+++ b/extensions/picasaweb/gth-account-properties-dialog.c
@@ -25,13 +25,18 @@
 #include "gth-account-properties-dialog.h"
 
 
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+
+
 static gpointer parent_class = NULL;
 
 
 struct _GthAccountPropertiesDialogPrivate {
-	char *email;
-	char *password;
-	char *challange;
+	GtkBuilder   *builder;
+	char         *email;
+	char         *password;
+	char         *challange_url;
+	GCancellable *cancellable;
 };
 
 
@@ -41,9 +46,11 @@ gth_account_properties_dialog_finalize (GObject *object)
 	GthAccountPropertiesDialog *self;
 
 	self = GTH_ACCOUNT_PROPERTIES_DIALOG (object);
+	_g_object_unref (self->priv->builder);
 	g_free (self->priv->email);
 	g_free (self->priv->password);
-	g_free (self->priv->challange);
+	g_free (self->priv->challange_url);
+	g_object_unref (self->priv->cancellable);
 
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -65,10 +72,32 @@ gth_account_properties_dialog_class_init (GthAccountPropertiesDialogClass *klass
 static void
 gth_account_properties_dialog_init (GthAccountPropertiesDialog *self)
 {
+	GtkWidget *content;
+
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_ACCOUNT_PROPERTIES_DIALOG, GthAccountPropertiesDialogPrivate);
 	self->priv->email = NULL;
 	self->priv->password = NULL;
-	self->priv->challange = NULL;
+	self->priv->challange_url = NULL;
+	self->priv->cancellable = g_cancellable_new ();
+	self->priv->builder = _gtk_builder_new_from_file ("picasa-web-account-properties.ui", "picasaweb");
+
+	gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
+	gtk_dialog_set_has_separator (GTK_DIALOG (self), FALSE);
+	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), 5);
+	gtk_container_set_border_width (GTK_CONTAINER (self), 5);
+
+	content = _gtk_builder_get_widget (self->priv->builder, "account_properties");
+	gtk_container_set_border_width (GTK_CONTAINER (content), 0);
+  	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), content, TRUE, TRUE, 0);
+
+  	gtk_entry_set_visibility (GTK_ENTRY (GET_WIDGET ("password_entry")), FALSE);
+
+	gtk_dialog_add_buttons (GTK_DIALOG (self),
+				GTK_STOCK_HELP, GTK_RESPONSE_HELP,
+				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+				GTK_STOCK_OK, GTK_RESPONSE_OK,
+				NULL);
+	gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
 }
 
 
@@ -100,15 +129,78 @@ gth_account_properties_dialog_get_type (void)
 }
 
 
+static void
+image_buffer_ready_cb (void     *buffer,
+		       gsize     count,
+		       GError   *error,
+		       gpointer  user_data)
+{
+	GthAccountPropertiesDialog *self = user_data;
+	GInputStream               *stream;
+	GdkPixbuf                  *pixbuf;
+
+	if (error != NULL) {
+		/* FIXME: show the error dialog */
+		return;
+	}
+
+	stream = g_memory_input_stream_new_from_data (buffer, count, NULL);
+	pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
+	if (pixbuf != NULL) {
+		gtk_widget_show (GET_WIDGET ("challange_box"));
+		gtk_image_set_from_pixbuf (GTK_IMAGE (GET_WIDGET ("challenge_image")), pixbuf);
+		g_object_unref (pixbuf);
+	}
+
+	g_object_unref (stream);
+}
+
+
+static void
+gth_account_properties_dialog_construct (GthAccountPropertiesDialog *self,
+					 const char                 *email,
+					 const char                 *password,
+					 const char                 *challange_url)
+{
+	if (email != NULL) {
+		self->priv->email = g_strdup (email);
+		gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("email_entry")), self->priv->email);
+	}
+
+	if (password != NULL) {
+		self->priv->password = g_strdup (password);
+		gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("password_entry")), self->priv->password);
+	}
+
+	if (challange_url != NULL) {
+		char  *url;
+		GFile *file;
+
+		self->priv->challange_url = g_strdup (challange_url);
+
+		url = g_strconcat ("http://www.google.com/accounts/";, challange_url, NULL);
+		file = g_file_new_for_uri (url);
+		g_load_file_async (file,
+				   G_PRIORITY_DEFAULT,
+				   self->priv->cancellable,
+				   image_buffer_ready_cb,
+				   self);
+
+		g_object_unref (file);
+		g_free (url);
+	}
+}
+
+
 GtkWidget *
 gth_account_properties_dialog_new (const char *email,
-			           const char *password)
+			           const char *password,
+				   const char *challange_url)
 {
 	GthAccountPropertiesDialog *self;
 
 	self = g_object_new (GTH_TYPE_ACCOUNT_PROPERTIES_DIALOG, NULL);
-	self->priv->email = g_strdup (email);
-	self->priv->password = g_strdup (password);
+	gth_account_properties_dialog_construct (self, email, password, challange_url);
 
 	return (GtkWidget *) self;
 }
@@ -117,19 +209,19 @@ gth_account_properties_dialog_new (const char *email,
 const char *
 gth_account_properties_dialog_get_email (GthAccountPropertiesDialog *self)
 {
-	return self->priv->email;
+	return gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("email_entry")));
 }
 
 
 const char *
 gth_account_properties_dialog_get_password (GthAccountPropertiesDialog *self)
 {
-	return self->priv->password;
+	return gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("password_entry")));
 }
 
 
 const char *
 gth_account_properties_dialog_get_challange (GthAccountPropertiesDialog *self)
 {
-	return self->priv->challange;
+	return gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("challenge_entry")));
 }
diff --git a/extensions/picasaweb/gth-account-properties-dialog.h b/extensions/picasaweb/gth-account-properties-dialog.h
index 9be0ada..571fa92 100644
--- a/extensions/picasaweb/gth-account-properties-dialog.h
+++ b/extensions/picasaweb/gth-account-properties-dialog.h
@@ -50,7 +50,8 @@ struct _GthAccountPropertiesDialogClass {
 
 GType          gth_account_properties_dialog_get_type      (void);
 GtkWidget *    gth_account_properties_dialog_new           (const char *email,
-							    const char *password);
+							    const char *password,
+							    const char *challange);
 const char *   gth_account_properties_dialog_get_email     (GthAccountPropertiesDialog *self);
 const char *   gth_account_properties_dialog_get_password  (GthAccountPropertiesDialog *self);
 const char *   gth_account_properties_dialog_get_challange (GthAccountPropertiesDialog *self);
diff --git a/extensions/picasaweb/picasa-web-album.c b/extensions/picasaweb/picasa-web-album.c
new file mode 100644
index 0000000..e135782
--- /dev/null
+++ b/extensions/picasaweb/picasa-web-album.c
@@ -0,0 +1,284 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gthumb.h>
+#include "picasa-web-album.h"
+
+
+static gpointer picasa_web_album_parent_class = NULL;
+
+
+static void
+picasa_web_album_finalize (GObject *obj)
+{
+	PicasaWebAlbum *self;
+
+	self = PICASA_WEB_ALBUM (obj);
+
+	g_free (self->id);
+	g_free (self->title);
+	g_free (self->summary);
+	g_free (self->edit_url);
+
+	G_OBJECT_CLASS (picasa_web_album_parent_class)->finalize (obj);
+}
+
+
+static void
+picasa_web_album_class_init (PicasaWebAlbumClass *klass)
+{
+	picasa_web_album_parent_class = g_type_class_peek_parent (klass);
+	G_OBJECT_CLASS (klass)->finalize = picasa_web_album_finalize;
+}
+
+
+static DomElement*
+picasa_web_album_create_element (DomDomizable *base,
+				 DomDocument  *doc)
+{
+	PicasaWebAlbum *self;
+	DomElement     *element;
+	char           *value;
+
+	self = PICASA_WEB_ALBUM (base);
+
+	element = dom_document_create_element (doc, "entry",
+					       "xmlns", "http://www.w3.org/2005/Atom";,
+					       "xmlns:media", "http://search.yahoo.com/mrss/";,
+					       "xmlns:gphoto", "http://schemas.google.com/photos/2007";,
+					       NULL);
+	if (self->id != NULL)
+		dom_element_append_child (element, dom_document_create_element_with_text (doc, self->id, "id", NULL));
+	if (self->title != NULL)
+		dom_element_append_child (element, dom_document_create_element_with_text (doc, self->title, "title", "type", "text", NULL));
+	if (self->summary != NULL)
+		dom_element_append_child (element, dom_document_create_element_with_text (doc, self->summary, "summary", "type", "text", NULL));
+
+	switch (self->access) {
+	case PICASA_WEB_ACCESS_ALL:
+		value = "all";
+		break;
+	case PICASA_WEB_ACCESS_PRIVATE:
+		value = "private";
+		break;
+	case PICASA_WEB_ACCESS_PUBLIC:
+		value = "public";
+		break;
+	case PICASA_WEB_ACCESS_VISIBLE:
+		value = "visible";
+		break;
+	}
+	dom_element_append_child (element, dom_document_create_element_with_text (doc, value, "gphoto:access", NULL));
+
+	dom_element_append_child (element,
+				  dom_document_create_element (doc, "category",
+							       "scheme", "http://schemas.google.com/g/2005#kind";,
+							       "term", "http://schemas.google.com/photos/2007#album";,
+							       NULL));
+
+	return element;
+}
+
+
+static void
+picasa_web_album_load_from_element (DomDomizable *base,
+				    DomElement   *element)
+{
+	PicasaWebAlbum *self;
+	DomElement     *node;
+
+	self = PICASA_WEB_ALBUM (base);
+
+	picasa_web_album_set_id (self, NULL);
+	picasa_web_album_set_title (self, NULL);
+	picasa_web_album_set_summary (self, NULL);
+	picasa_web_album_set_edit_url (self, NULL);
+	picasa_web_album_set_access (self, NULL);
+	self->n_photos = 0;
+
+	picasa_web_album_set_etag (self, dom_element_get_attribute (element, "gd:etag"));
+	for (node = element->first_child; node; node = node->next_sibling) {
+		if (g_strcmp0 (node->tag_name, "gphoto:id") == 0) {
+			picasa_web_album_set_id (self, dom_element_get_inner_text (node));
+		}
+		else if (g_strcmp0 (node->tag_name, "title") == 0) {
+			picasa_web_album_set_title (self, dom_element_get_inner_text (node));
+		}
+		else if (g_strcmp0 (node->tag_name, "summary") == 0) {
+			picasa_web_album_set_summary (self, dom_element_get_inner_text (node));
+		}
+		else if (g_strcmp0 (node->tag_name, "link") == 0) {
+			if (g_strcmp0 (dom_element_get_attribute (node, "rel"), "edit") == 0)
+				picasa_web_album_set_edit_url (self, dom_element_get_attribute (node, "href"));
+		}
+		else if (g_strcmp0 (node->tag_name, "gphoto:access") == 0) {
+			picasa_web_album_set_access (self, dom_element_get_inner_text (node));
+		}
+		else if (g_strcmp0 (node->tag_name, "gphoto:numphotos") == 0) {
+			picasa_web_album_set_n_photos (self, dom_element_get_inner_text (node));
+		}
+	}
+}
+
+
+static void
+picasa_web_album_dom_domizable_interface_init (DomDomizableIface *iface)
+{
+	iface->create_element = picasa_web_album_create_element;
+	iface->load_from_element = picasa_web_album_load_from_element;
+}
+
+
+static void
+picasa_web_album_instance_init (PicasaWebAlbum *self)
+{
+}
+
+
+GType
+picasa_web_album_get_type (void)
+{
+	static GType picasa_web_album_type_id = 0;
+
+	if (picasa_web_album_type_id == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (PicasaWebAlbumClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) picasa_web_album_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (PicasaWebAlbum),
+			0,
+			(GInstanceInitFunc) picasa_web_album_instance_init,
+			NULL
+		};
+		static const GInterfaceInfo dom_domizable_info = {
+			(GInterfaceInitFunc) picasa_web_album_dom_domizable_interface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL
+		};
+
+		picasa_web_album_type_id = g_type_register_static (G_TYPE_OBJECT,
+								   "PicasaWebAlbum",
+								   &g_define_type_info,
+								   0);
+		g_type_add_interface_static (picasa_web_album_type_id, DOM_TYPE_DOMIZABLE, &dom_domizable_info);
+	}
+
+	return picasa_web_album_type_id;
+}
+
+
+PicasaWebAlbum *
+picasa_web_album_new (void)
+{
+	return g_object_new (PICASA_WEB_TYPE_ALBUM, NULL);
+}
+
+
+void
+picasa_web_album_set_etag (PicasaWebAlbum *self,
+			   const char     *value)
+{
+	g_free (self->etag);
+	self->etag = NULL;
+	if (value != NULL)
+		self->etag = g_strdup (value);
+}
+
+
+void
+picasa_web_album_set_id (PicasaWebAlbum *self,
+			 const char     *value)
+{
+	g_free (self->id);
+	self->id = NULL;
+	if (value != NULL)
+		self->id = g_strdup (value);
+}
+
+
+void
+picasa_web_album_set_title (PicasaWebAlbum *self,
+			    const char     *value)
+{
+	g_free (self->title);
+	self->title = NULL;
+	if (value != NULL)
+		self->title = g_strdup (value);
+}
+
+
+void
+picasa_web_album_set_summary (PicasaWebAlbum *self,
+			      const char     *value)
+{
+	g_free (self->summary);
+	self->summary = NULL;
+	if (value != NULL)
+		self->summary = g_strdup (value);
+}
+
+
+void
+picasa_web_album_set_edit_url (PicasaWebAlbum *self,
+			       const char     *value)
+{
+	g_free (self->edit_url);
+	self->edit_url = NULL;
+	if (value != NULL)
+		self->edit_url = g_strdup (value);
+}
+
+
+void
+picasa_web_album_set_access (PicasaWebAlbum *self,
+			     const char     *value)
+{
+	if (value == NULL)
+		self->access = PICASA_WEB_ACCESS_PRIVATE;
+	else if (strcmp (value, "all"))
+		self->access = PICASA_WEB_ACCESS_ALL;
+	else if (strcmp (value, "private"))
+		self->access = PICASA_WEB_ACCESS_PRIVATE;
+	else if (strcmp (value, "public"))
+		self->access = PICASA_WEB_ACCESS_PUBLIC;
+	else if (strcmp (value, "visible"))
+		self->access = PICASA_WEB_ACCESS_VISIBLE;
+	else
+		self->access = PICASA_WEB_ACCESS_PRIVATE;
+}
+
+
+void
+picasa_web_album_set_n_photos (PicasaWebAlbum *self,
+			       const char     *value)
+{
+	if (value != NULL)
+		self->n_photos = atoi (value);
+	else
+		self->n_photos = 0;
+}
diff --git a/extensions/picasaweb/picasa-web-album.h b/extensions/picasaweb/picasa-web-album.h
new file mode 100644
index 0000000..d4afe7b
--- /dev/null
+++ b/extensions/picasaweb/picasa-web-album.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PICASA_WEB_ALBUM_H
+#define PICASA_WEB_ALBUM_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define PICASA_WEB_TYPE_ALBUM            (picasa_web_album_get_type ())
+#define PICASA_WEB_ALBUM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PICASA_WEB_TYPE_ALBUM, PicasaWebAlbum))
+#define PICASA_WEB_ALBUM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PICASA_WEB_TYPE_ALBUM, PicasaWebAlbumClass))
+#define PICASA_WEB_IS_ALBUM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PICASA_WEB_TYPE_ALBUM))
+#define PICASA_WEB_IS_ALBUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PICASA_WEB_TYPE_ALBUM))
+#define PICASA_WEB_ALBUM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PICASA_WEB_TYPE_ALBUM, PicasaWebAlbumClass))
+
+typedef struct _PicasaWebAlbum PicasaWebAlbum;
+typedef struct _PicasaWebAlbumClass PicasaWebAlbumClass;
+typedef struct _PicasaWebAlbumPrivate PicasaWebAlbumPrivate;
+
+typedef enum  {
+	PICASA_WEB_ACCESS_ALL,
+	PICASA_WEB_ACCESS_PRIVATE,
+	PICASA_WEB_ACCESS_PUBLIC,
+	PICASA_WEB_ACCESS_VISIBLE
+} PicasaWebAccess;
+
+struct _PicasaWebAlbum {
+	GObject parent_instance;
+	PicasaWebAlbumPrivate *priv;
+
+	char            *etag;
+	char            *id;
+	char            *title;
+	char            *summary;
+	char            *edit_url;
+	PicasaWebAccess  access;
+	int              n_photos;
+};
+
+struct _PicasaWebAlbumClass {
+	GObjectClass parent_class;
+};
+
+GType             picasa_web_album_get_type     (void);
+PicasaWebAlbum *  picasa_web_album_new          (void);
+void              picasa_web_album_set_etag     (PicasaWebAlbum *self,
+						 const char     *value);
+void              picasa_web_album_set_id       (PicasaWebAlbum *self,
+						 const char     *value);
+void              picasa_web_album_set_title    (PicasaWebAlbum *self,
+						 const char     *value);
+void              picasa_web_album_set_summary  (PicasaWebAlbum *self,
+						 const char     *value);
+void              picasa_web_album_set_edit_url (PicasaWebAlbum *self,
+						 const char     *value);
+void              picasa_web_album_set_access   (PicasaWebAlbum *self,
+						 const char     *value);
+void              picasa_web_album_set_n_photos (PicasaWebAlbum *self,
+						 const char     *value);
+
+G_END_DECLS
+
+#endif /* PICASA_WEB_ALBUM_H */
diff --git a/extensions/picasaweb/picasa-web-service.c b/extensions/picasaweb/picasa-web-service.c
index 5173fed..4f23058 100644
--- a/extensions/picasaweb/picasa-web-service.c
+++ b/extensions/picasaweb/picasa-web-service.c
@@ -24,12 +24,14 @@
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gthumb.h>
+#include "picasa-web-album.h"
 #include "picasa-web-service.h"
 
 
 struct _PicasaWebServicePrivate
 {
 	GoogleConnection *conn;
+	char             *user_id;
 };
 
 
@@ -43,6 +45,7 @@ picasa_web_service_finalize (GObject *object)
 
 	self = PICASA_WEB_SERVICE (object);
 	_g_object_unref (self->priv->conn);
+	g_free (self->priv->user_id);
 
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -66,6 +69,7 @@ picasa_web_service_init (PicasaWebService *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, PICASA_TYPE_WEB_SERVICE, PicasaWebServicePrivate);
 	self->priv->conn = NULL;
+	self->priv->user_id = g_strdup ("default");
 }
 
 
@@ -109,14 +113,108 @@ picasa_web_service_new (GoogleConnection *conn)
 }
 
 
+/* -- picasa_web_service_list_albums -- */
+
+
+static void
+list_albums_ready_cb (SoupSession *session,
+		      SoupMessage *msg,
+		      gpointer     user_data)
+{
+	PicasaWebService   *self = user_data;
+	GSimpleAsyncResult *result;
+	SoupBuffer         *body;
+	DomDocument        *doc;
+	GError             *error = NULL;
+
+	result = google_connection_get_result (self->priv->conn);
+
+	if (msg->status_code != 200) {
+		g_simple_async_result_set_error (result,
+						 SOUP_HTTP_ERROR,
+						 msg->status_code,
+						 "%s",
+						 soup_status_get_phrase (msg->status_code));
+		g_simple_async_result_complete_in_idle (result);
+		return;
+	}
+
+	body = soup_message_body_flatten (msg->response_body);
+	doc = dom_document_new ();
+	if (dom_document_load (doc, body->data, body->length, &error)) {
+		DomElement *feed_node;
+		GList      *albums = NULL;
+
+		feed_node = DOM_ELEMENT (doc)->first_child;
+		while ((feed_node != NULL) && g_strcmp0 (feed_node->tag_name, "feed") != 0)
+			feed_node = feed_node->next_sibling;
+
+		if (feed_node != NULL) {
+			DomElement     *node;
+			PicasaWebAlbum *album = NULL;
+
+			for (node = feed_node->first_child;
+			     node != NULL;
+			     node = node->next_sibling)
+			{
+				if (g_strcmp0 (node->tag_name, "id") == 0) { /* get the user id */
+					char *user_id;
+
+					user_id = strrchr (dom_element_get_inner_text (node), '/');
+					if (user_id != NULL) {
+						g_free (self->priv->user_id);
+						self->priv->user_id = g_strdup (user_id + 1);
+					}
+				}
+				else if (g_strcmp0 (node->tag_name, "entry") == 0) { /* read the album data */
+					if (album != NULL)
+						albums = g_list_prepend (albums, album);
+					album = picasa_web_album_new ();
+					dom_domizable_load_from_element (DOM_DOMIZABLE (album), node);
+				}
+			}
+			if (album != NULL)
+				albums = g_list_prepend (albums, album);
+		}
+		albums = g_list_reverse (albums);
+		g_simple_async_result_set_op_res_gpointer (result, albums, (GDestroyNotify) _g_object_list_unref);
+	}
+	else {
+		g_simple_async_result_set_from_error (result, error);
+		g_error_free (error);
+	}
+	g_simple_async_result_complete_in_idle (result);
+
+	g_object_unref (doc);
+	soup_buffer_free (body);
+}
+
+
 void
-picasa_web_service_list_albums (PicasaWebService    *service,
+picasa_web_service_list_albums (PicasaWebService    *self,
 			        const char          *user_id,
 			        GCancellable        *cancellable,
 			        GAsyncReadyCallback  callback,
 			        gpointer             user_data)
 {
-	/* FIXME */
+	char        *url;
+	SoupMessage *msg;
+
+	g_return_if_fail (user_id != NULL);
+
+	url = g_strconcat ("http://picasaweb.google.com/data/feed/api/user/";, user_id, NULL);
+	msg = soup_message_new ("GET", url);
+	google_connection_send_message (self->priv->conn,
+					msg,
+					cancellable,
+					callback,
+					user_data,
+					picasa_web_service_list_albums,
+					list_albums_ready_cb,
+					self);
+
+	g_object_unref (msg);
+	g_free (url);
 }
 
 
@@ -125,6 +223,192 @@ picasa_web_service_list_albums_finish (PicasaWebService  *service,
 				       GAsyncResult      *result,
 				       GError           **error)
 {
-	/* FIXME */
-	return NULL;
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return NULL;
+	else
+		return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+
+/* -- picasa_web_service_create_album -- */
+
+
+static void
+create_album_ready_cb (SoupSession *session,
+		       SoupMessage *msg,
+		       gpointer     user_data)
+{
+	PicasaWebService   *self = user_data;
+	GSimpleAsyncResult *result;
+	SoupBuffer         *body;
+	DomDocument        *doc;
+	GError             *error = NULL;
+
+	result = google_connection_get_result (self->priv->conn);
+
+	if (msg->status_code != 201) {
+		g_simple_async_result_set_error (result,
+						 SOUP_HTTP_ERROR,
+						 msg->status_code,
+						 "%s",
+						 soup_status_get_phrase (msg->status_code));
+		g_simple_async_result_complete_in_idle (result);
+		return;
+	}
+
+	body = soup_message_body_flatten (msg->response_body);
+	doc = dom_document_new ();
+	if (dom_document_load (doc, body->data, body->length, &error)) {
+		PicasaWebAlbum *album;
+
+		album = picasa_web_album_new ();
+		dom_domizable_load_from_element (DOM_DOMIZABLE (album), DOM_ELEMENT (doc)->first_child);
+		g_simple_async_result_set_op_res_gpointer (result, album, (GDestroyNotify) _g_object_list_unref);
+	}
+	else {
+		g_simple_async_result_set_from_error (result, error);
+		g_error_free (error);
+	}
+	g_simple_async_result_complete_in_idle (result);
+
+	g_object_unref (doc);
+	soup_buffer_free (body);
+}
+
+
+void
+picasa_web_service_create_album (PicasaWebService     *self,
+				 PicasaWebAlbum       *album,
+				 GCancellable         *cancellable,
+				 GAsyncReadyCallback   callback,
+				 gpointer              user_data)
+{
+	DomDocument *doc;
+	DomElement  *entry;
+	char        *buffer;
+	gsize        len;
+	char        *url;
+	SoupMessage *msg;
+
+	g_return_if_fail (self->priv->user_id != NULL);
+
+	doc = dom_document_new ();
+	entry = dom_domizable_create_element (DOM_DOMIZABLE (album), doc);
+	dom_element_set_attribute (entry, "xmlns", "http://www.w3.org/2005/Atom";);
+	dom_element_set_attribute (entry, "xmlns:media", "http://search.yahoo.com/mrss/";);
+	dom_element_set_attribute (entry, "xmlns:gphoto", "http://schemas.google.com/photos/2007";);
+	dom_element_append_child (DOM_ELEMENT (doc), entry);
+	buffer = dom_document_dump (doc, &len);
+
+	url = g_strconcat ("http://picasaweb.google.com/data/feed/api/user/";, self->priv->user_id, NULL);
+	msg = soup_message_new ("POST", url);
+	soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE, buffer, len);
+	google_connection_send_message (self->priv->conn,
+					msg,
+					cancellable,
+					callback,
+					user_data,
+					picasa_web_service_create_album,
+					create_album_ready_cb,
+					self);
+
+	g_object_unref (msg);
+	g_free (url);
+	g_object_unref (doc);
+}
+
+
+PicasaWebAlbum *
+picasa_web_service_create_album_finish (PicasaWebService  *service,
+					GAsyncResult      *result,
+					GError           **error)
+{
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return NULL;
+	else
+		return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+
+/* utilities */
+
+
+GList *
+picasa_web_accounts_load_from_file (void)
+{
+	GList       *accounts = NULL;
+	char        *filename;
+	char        *buffer;
+	gsize        len;
+	DomDocument *doc;
+
+	filename = gth_user_dir_get_file (GTH_DIR_CONFIG, GTHUMB_DIR, "accounts", "picasaweb.xml", NULL);
+	if (! g_file_get_contents (filename, &buffer, &len, NULL)) {
+		g_free (filename);
+		return NULL;
+	}
+
+	doc = dom_document_new ();
+	if (dom_document_load (doc, buffer, len, NULL)) {
+		DomElement *node;
+
+		node = DOM_ELEMENT (doc)->first_child;
+		if ((node != NULL) && (g_strcmp0 (node->tag_name, "accounts") == 0)) {
+			DomElement *child;
+
+			for (child = node->first_child;
+			     child != NULL;
+			     child = child->next_sibling)
+			{
+				if (strcmp (child->tag_name, "account") == 0) {
+					const char *value;
+
+					value = dom_element_get_attribute (child, "email");
+					if (value != NULL)
+						accounts = g_list_prepend (accounts, g_strdup (value));
+				}
+			}
+
+			accounts = g_list_reverse (accounts);
+		}
+	}
+
+	g_object_unref (doc);
+	g_free (buffer);
+	g_free (filename);
+
+	return accounts;
+}
+
+
+void
+picasa_web_accounts_save_to_file (GList *accounts)
+{
+	DomDocument *doc;
+	DomElement  *root;
+	GList       *scan;
+	char        *buffer;
+	gsize        len;
+	char        *filename;
+	GFile       *file;
+
+	doc = dom_document_new ();
+	root = dom_document_create_element (doc, "accounts", NULL);
+	dom_element_append_child (DOM_ELEMENT (doc), root);
+	for (scan = accounts; scan; scan = scan->next)
+		dom_element_append_child (root,
+					  dom_document_create_element (doc, "account",
+								       "email", (char *) scan->data,
+								       NULL));
+
+	gth_user_dir_make_dir_for_file (GTH_DIR_CONFIG, GTHUMB_DIR, "accounts", "picasaweb.xml", NULL);
+	filename = gth_user_dir_get_file (GTH_DIR_CONFIG, GTHUMB_DIR, "accounts", "picasaweb.xml", NULL);
+	file = g_file_new_for_path (filename);
+	buffer = dom_document_dump (doc, &len);
+	g_write_file (file, FALSE, 0, buffer, len, NULL, NULL);
+
+	g_free (buffer);
+	g_object_unref (file);
+	g_free (filename);
+	g_object_unref (doc);
 }
diff --git a/extensions/picasaweb/picasa-web-service.h b/extensions/picasaweb/picasa-web-service.h
index 2a774e4..d99e5b8 100644
--- a/extensions/picasaweb/picasa-web-service.h
+++ b/extensions/picasaweb/picasa-web-service.h
@@ -25,6 +25,7 @@
 
 #include <glib-object.h>
 #include "google-connection.h"
+#include "picasa-web-album.h"
 
 #define PICASA_TYPE_WEB_SERVICE         (picasa_web_service_get_type ())
 #define PICASA_WEB_SERVICE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), PICASA_TYPE_WEB_SERVICE, PicasaWebService))
@@ -48,15 +49,28 @@ struct _PicasaWebServiceClass
 	GObjectClass __parent_class;
 };
 
-GType                picasa_web_service_get_type           (void) G_GNUC_CONST;
-PicasaWebService *   picasa_web_service_new                (GoogleConnection     *conn);
-void                 picasa_web_service_list_albums        (PicasaWebService     *service,
-						            const char           *user_id,
-						            GCancellable         *cancellable,
-						            GAsyncReadyCallback   callback,
-						            gpointer              user_data);
-GList *              picasa_web_service_list_albums_finish (PicasaWebService     *service,
-						            GAsyncResult         *result,
-						            GError              **error);
+GType                picasa_web_service_get_type            (void) G_GNUC_CONST;
+PicasaWebService *   picasa_web_service_new                 (GoogleConnection     *conn);
+void                 picasa_web_service_list_albums         (PicasaWebService     *service,
+						             const char           *user_id,
+						             GCancellable         *cancellable,
+						             GAsyncReadyCallback   callback,
+						             gpointer              user_data);
+GList *              picasa_web_service_list_albums_finish  (PicasaWebService     *service,
+						             GAsyncResult         *result,
+						             GError              **error);
+void                 picasa_web_service_create_album        (PicasaWebService     *service,
+							     PicasaWebAlbum       *album,
+							     GCancellable         *cancellable,
+							     GAsyncReadyCallback   callback,
+							     gpointer              user_data);
+PicasaWebAlbum *     picasa_web_service_create_album_finish (PicasaWebService     *service,
+							     GAsyncResult         *result,
+							     GError              **error);
+
+/* utilities */
+
+GList *              picasa_web_accounts_load_from_file    (void);
+void                 picasa_web_accounts_save_to_file      (GList *accounts);
 
 #endif /* PICASA_WEB_SERVICE_H */



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