[gthumb: 2/11] [flicker] added account manager, authorization process



commit fdd6756982fd5807c0fb5611c76a488facd4fc5f
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Mon Mar 8 23:33:13 2010 +0100

    [flicker] added account manager, authorization process

 configure.ac                                       |    2 +
 extensions/flicker/Makefile.am                     |   19 +
 extensions/flicker/actions.c                       |    4 +-
 extensions/flicker/callbacks.c                     |    4 +-
 extensions/flicker/data/Makefile.am                |    3 +
 extensions/flicker/data/ui/Makefile.am             |   11 +
 extensions/flicker/data/ui/export-to-flickr.ui     |  300 +++++++
 .../flicker/data/ui/flicker-account-chooser.ui     |   56 ++
 .../flicker/data/ui/flicker-account-manager.ui     |  103 +++
 .../flicker/data/ui/flicker-ask-authorization.ui   |   78 ++
 .../data/ui/flicker-complete-authorization.ui      |   79 ++
 extensions/flicker/dlg-export-to-flickr.c          |  642 +++++++++++++
 extensions/flicker/dlg-export-to-flickr.h          |   31 +
 extensions/flicker/flickr-account-chooser-dialog.c |  184 ++++
 extensions/flicker/flickr-account-chooser-dialog.h |   61 ++
 extensions/flicker/flickr-account-manager-dialog.c |  247 +++++
 extensions/flicker/flickr-account-manager-dialog.h |   59 ++
 extensions/flicker/flickr-account.c                |  180 ++++
 extensions/flicker/flickr-account.h                |   66 ++
 extensions/flicker/flickr-connection.c             |  552 ++++++++++++
 extensions/flicker/flickr-connection.h             |  106 +++
 extensions/flicker/flickr-photoset.c               |  266 ++++++
 extensions/flicker/flickr-photoset.h               |   81 ++
 extensions/flicker/flickr-service.c                |  939 ++++++++++++++++++++
 extensions/flicker/flickr-service.h                |  105 +++
 extensions/flicker/flickr-types.h                  |   43 +
 extensions/flicker/flickr-user.c                   |  245 +++++
 extensions/flicker/flickr-user.h                   |   84 ++
 extensions/picasaweb/dlg-export-to-picasaweb.c     |    2 +
 extensions/picasaweb/picasa-web-service.c          |    2 +-
 30 files changed, 4549 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 46d1681..cb3a60a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -433,6 +433,8 @@ extensions/file_tools/data/Makefile
 extensions/file_tools/data/ui/Makefile
 extensions/file_viewer/Makefile
 extensions/flicker/Makefile
+extensions/flicker/data/Makefile
+extensions/flicker/data/ui/Makefile
 extensions/gstreamer_tools/Makefile
 extensions/gstreamer_tools/data/Makefile
 extensions/gstreamer_tools/data/ui/Makefile
diff --git a/extensions/flicker/Makefile.am b/extensions/flicker/Makefile.am
index 34d4c4f..86c7e36 100644
--- a/extensions/flicker/Makefile.am
+++ b/extensions/flicker/Makefile.am
@@ -1,5 +1,7 @@
 if ENABLE_WEB_SERVICES
 
+SUBDIRS = data
+
 extensiondir = $(pkglibdir)/extensions
 extension_LTLIBRARIES = libflicker.la
 
@@ -8,6 +10,23 @@ libflicker_la_SOURCES = 			\
 	actions.h				\
 	callbacks.c				\
 	callbacks.h				\
+	dlg-export-to-flickr.c			\
+	dlg-export-to-flickr.h			\
+	flickr-account.c			\
+	flickr-account.h			\
+	flickr-account-chooser-dialog.c		\
+	flickr-account-chooser-dialog.h		\
+	flickr-account-manager-dialog.c		\
+	flickr-account-manager-dialog.h		\
+	flickr-connection.c			\
+	flickr-connection.h			\
+	flickr-photoset.c			\
+	flickr-photoset.h			\
+	flickr-service.c			\
+	flickr-service.h			\
+	flickr-types.h				\
+	flickr-user.c				\
+	flickr-user.h				\
 	main.c
 
 libflicker_la_CFLAGS = $(GTHUMB_CFLAGS) $(LIBSOUP_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb 
diff --git a/extensions/flicker/actions.c b/extensions/flicker/actions.c
index acb9eda..4333b42 100644
--- a/extensions/flicker/actions.c
+++ b/extensions/flicker/actions.c
@@ -24,6 +24,7 @@
 #include <config.h>
 #include <glib/gi18n.h>
 #include <gthumb.h>
+#include "dlg-export-to-flickr.h"
 
 
 void
@@ -37,8 +38,7 @@ gth_browser_activate_action_export_flicker (GtkAction  *action,
 	file_list = gth_file_list_get_files (GTH_FILE_LIST (gth_browser_get_file_list (browser)), items);
 	if (file_list == NULL)
 		file_list = gth_file_store_get_visibles (gth_browser_get_file_store (browser));
-
-	/* dlg_export_to_picasaweb (browser, file_list); FIXME */
+	dlg_export_to_flickr (browser, file_list);
 
 	_g_object_list_unref (file_list);
 	_gtk_tree_path_list_free (items);
diff --git a/extensions/flicker/callbacks.c b/extensions/flicker/callbacks.c
index 33892cd..1275e41 100644
--- a/extensions/flicker/callbacks.c
+++ b/extensions/flicker/callbacks.c
@@ -47,8 +47,8 @@ static const char *ui_info =
 
 static GtkActionEntry action_entries[] = {
 	{ "File_Export_Flicker", NULL,
-	  N_("_Flicker..."), NULL,
-	  N_("Upload photos to Flicker"),
+	  N_("_Flickr..."), NULL,
+	  N_("Upload photos to Flickr"),
 	  G_CALLBACK (gth_browser_activate_action_export_flicker) },
 };
 
diff --git a/extensions/flicker/data/Makefile.am b/extensions/flicker/data/Makefile.am
new file mode 100644
index 0000000..c1713cf
--- /dev/null
+++ b/extensions/flicker/data/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = ui
+
+-include $(top_srcdir)/git.mk
diff --git a/extensions/flicker/data/ui/Makefile.am b/extensions/flicker/data/ui/Makefile.am
new file mode 100644
index 0000000..d8e958c
--- /dev/null
+++ b/extensions/flicker/data/ui/Makefile.am
@@ -0,0 +1,11 @@
+uidir = $(pkgdatadir)/ui
+ui_DATA = 					\
+	export-to-flickr.ui			\
+	flicker-account-chooser.ui		\
+	flicker-account-manager.ui		\
+	flicker-ask-authorization.ui		\
+	flicker-complete-authorization.ui
+
+EXTRA_DIST = $(ui_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/extensions/flicker/data/ui/export-to-flickr.ui b/extensions/flicker/data/ui/export-to-flickr.ui
new file mode 100644
index 0000000..8a30ddd
--- /dev/null
+++ b/extensions/flicker/data/ui/export-to-flickr.ui
@@ -0,0 +1,300 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkDialog" id="export_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Export to Flickr</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox7">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkVBox" id="images_box">
+                <property name="width_request">460</property>
+                <property name="height_request">220</property>
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">3</property>
+                <child>
+                  <object class="GtkLabel" id="images_info_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkTable" id="table2">
+                    <property name="visible">True</property>
+                    <property name="n_rows">2</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">6</property>
+                    <property name="row_spacing">5</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment1">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <child>
+                          <object class="GtkLabel" id="label3">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="yalign">0</property>
+                            <property name="label" translatable="yes">A_ccount:</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="x_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVBox" id="vbox4">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">3</property>
+                        <child>
+                          <object class="GtkHBox" id="hbox4">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkComboBox" id="account_combobox">
+                                <property name="width_request">300</property>
+                                <property name="visible">True</property>
+                                <property name="model">account_liststore</property>
+                                <child>
+                                  <object class="GtkCellRendererText" id="cellrenderertext4"/>
+                                  <attributes>
+                                    <attribute name="text">1</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkButton" id="edit_accounts_button">
+                                <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-edit</property>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox3">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkLabel" id="label4">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Free space:</property>
+                                <attributes>
+                                  <attribute name="absolute-size" value="10000"/>
+                                </attributes>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="free_space_label">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="yalign">0.51999998092651367</property>
+                                <attributes>
+                                  <attribute name="absolute-size" value="10000"/>
+                                </attributes>
+                              </object>
+                              <packing>
+                                <property name="position">1</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>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label1">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Album:</property>
+                        <property name="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkComboBoxEntry" id="album_comboboxentry">
+                        <property name="visible">True</property>
+                        <property name="model">album_liststore</property>
+                        <child>
+                          <object class="GtkCellRendererPixbuf" id="cellrenderertext1"/>
+                          <attributes>
+                            <attribute name="icon-name">2</attribute>
+                          </attributes>
+                        </child>
+                        <child>
+                          <object class="GtkCellRendererText" id="cellrenderertext2"/>
+                          <attributes>
+                            <attribute name="text">1</attribute>
+                          </attributes>
+                        </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>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">0</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>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area7">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="close_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="upload_button">
+                <property name="label" translatable="yes">_Upload</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="image">upload_image</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button1">
+                <property name="label">gtk-help</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">close_button</action-widget>
+      <action-widget response="-5">upload_button</action-widget>
+      <action-widget response="-11">button1</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkImage" id="upload_image">
+    <property name="visible">True</property>
+    <property name="stock">gtk-goto-top</property>
+  </object>
+  <object class="GtkListStore" id="album_liststore">
+    <columns>
+      <!-- column-name data -->
+      <column type="GObject"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name icon -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="account_liststore">
+    <columns>
+      <!-- column-name account -->
+      <column type="GObject"/>
+      <!-- column-name username -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+</interface>
diff --git a/extensions/flicker/data/ui/flicker-account-chooser.ui b/extensions/flicker/data/ui/flicker-account-chooser.ui
new file mode 100644
index 0000000..54abe69
--- /dev/null
+++ b/extensions/flicker/data/ui/flicker-account-chooser.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkVBox" id="account_chooser">
+    <property name="visible">True</property>
+    <property name="border_width">5</property>
+    <property name="orientation">vertical</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkHBox" id="hbox3">
+        <property name="visible">True</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkLabel" id="label1">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">A_ccount:</property>
+            <property name="use_underline">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkComboBox" id="account_combobox">
+            <property name="width_request">300</property>
+            <property name="visible">True</property>
+            <property name="model">account_liststore</property>
+            <child>
+              <object class="GtkCellRendererText" id="cellrenderertext1"/>
+              <attributes>
+                <attribute name="text">1</attribute>
+              </attributes>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+  <object class="GtkListStore" id="account_liststore">
+    <columns>
+      <!-- column-name account -->
+      <column type="GObject"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+</interface>
diff --git a/extensions/flicker/data/ui/flicker-account-manager.ui b/extensions/flicker/data/ui/flicker-account-manager.ui
new file mode 100644
index 0000000..2106973
--- /dev/null
+++ b/extensions/flicker/data/ui/flicker-account-manager.ui
@@ -0,0 +1,103 @@
+<?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 data -->
+      <column type="GObject"/>
+      <!-- column-name username -->
+      <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">A_ccounts:</property>
+        <property name="use_underline">True</property>
+        <property name="mnemonic_widget">account_treeview</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="reorderable">True</property>
+                <property name="search_column">1</property>
+                <child>
+                  <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+                    <child>
+                      <object class="GtkCellRendererText" id="account_cellrenderertext"/>
+                      <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="delete_button">
+                <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">0</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/flicker/data/ui/flicker-ask-authorization.ui b/extensions/flicker/data/ui/flicker-ask-authorization.ui
new file mode 100644
index 0000000..55d7428
--- /dev/null
+++ b/extensions/flicker/data/ui/flicker-ask-authorization.ui
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkMessageDialog" id="ask_authorization_messagedialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Upload to Flickr</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="type_hint">normal</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="message_type">other</property>
+    <property name="text" translatable="yes">Authorization required</property>
+    <property name="secondary_text" translatable="yes">Authorizing is a simple process which takes place in your web browser. When you're finished, return to this window to complete authorization.</property>
+    <property name="image">icon_image</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="authorize_button">
+                <property name="label" translatable="yes">_Authorize...</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="image">button_image</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancel_button</action-widget>
+      <action-widget response="-5">authorize_button</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkImage" id="button_image">
+    <property name="visible">True</property>
+    <property name="stock">gtk-dialog-authentication</property>
+  </object>
+  <object class="GtkImage" id="icon_image">
+    <property name="visible">True</property>
+    <property name="yalign">0</property>
+    <property name="stock">gtk-dialog-authentication</property>
+    <property name="icon-size">6</property>
+  </object>
+</interface>
diff --git a/extensions/flicker/data/ui/flicker-complete-authorization.ui b/extensions/flicker/data/ui/flicker-complete-authorization.ui
new file mode 100644
index 0000000..89a28ba
--- /dev/null
+++ b/extensions/flicker/data/ui/flicker-complete-authorization.ui
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkMessageDialog" id="complete_authorization_messagedialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Upload to Flickr</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="type_hint">normal</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="message_type">other</property>
+    <property name="text" translatable="yes">Complete authorization</property>
+    <property name="secondary_text" translatable="yes">Return to this window after you have finished the authorization process on Flickr.com, and click the 'Complete' button below.</property>
+    <property name="image">icon_image1</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox4">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area4">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel_button1">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+                <property name="xalign">0.50999999046325684</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="complete_button">
+                <property name="label" translatable="yes">C_omplete</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="image">button_image1</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancel_button1</action-widget>
+      <action-widget response="-5">complete_button</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkImage" id="icon_image1">
+    <property name="visible">True</property>
+    <property name="yalign">0</property>
+    <property name="stock">gtk-dialog-authentication</property>
+    <property name="icon-size">6</property>
+  </object>
+  <object class="GtkImage" id="button_image1">
+    <property name="visible">True</property>
+    <property name="stock">gtk-dialog-authentication</property>
+  </object>
+</interface>
diff --git a/extensions/flicker/dlg-export-to-flickr.c b/extensions/flicker/dlg-export-to-flickr.c
new file mode 100644
index 0000000..10c4cde
--- /dev/null
+++ b/extensions/flicker/dlg-export-to-flickr.c
@@ -0,0 +1,642 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2010 The 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 <gtk/gtk.h>
+#include <gthumb.h>
+#include "dlg-export-to-flickr.h"
+#include "flickr-account-chooser-dialog.h"
+#include "flickr-account-manager-dialog.h"
+#include "flickr-photoset.h"
+#include "flickr-service.h"
+#include "flickr-user.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (data->builder, (x)))
+
+
+enum {
+	ACCOUNT_DATA_COLUMN,
+	ACCOUNT_NAME_COLUMN
+};
+
+
+typedef struct {
+	GthBrowser       *browser;
+	GthFileData      *location;
+	GList            *file_list;
+	GtkBuilder       *builder;
+	GtkWidget        *dialog;
+	GtkWidget        *progress_dialog;
+	GList            *accounts;
+	FlickrAccount    *account;
+	FlickrUser       *user;
+	FlickrConnection *conn;
+	FlickrService    *service;
+	GCancellable     *cancellable;
+} DialogData;
+
+
+static void
+export_dialog_destroy_cb (GtkWidget  *widget,
+			  DialogData *data)
+{
+	if (data->conn != NULL)
+		gth_task_completed (GTH_TASK (data->conn), NULL);
+	_g_object_unref (data->cancellable);
+	_g_object_unref (data->service);
+	_g_object_unref (data->conn);
+	_g_object_list_unref (data->accounts);
+	_g_object_unref (data->account);
+	_g_object_unref (data->user);
+	_g_object_unref (data->builder);
+	_g_object_list_unref (data->file_list);
+	_g_object_unref (data->location);
+	g_free (data);
+}
+
+
+static void
+export_dialog_response_cb (GtkDialog *dialog,
+			   int        response_id,
+			   gpointer   user_data)
+{
+	DialogData *data = user_data;
+
+	switch (response_id) {
+	case GTK_RESPONSE_HELP:
+		show_help_dialog (GTK_WINDOW (data->browser), "export-to-flickr");
+		break;
+
+	case GTK_RESPONSE_DELETE_EVENT:
+	case GTK_RESPONSE_CANCEL:
+		flickr_accounts_save_to_file (data->accounts, data->account);
+		gtk_widget_destroy (data->dialog);
+		break;
+
+	case GTK_RESPONSE_OK:
+		{
+			/* FIXME
+			GtkTreeModel   *tree_model;
+			GtkTreeIter     iter;
+			PicasaWebAlbum *album;
+			GList          *file_list;
+
+			if (! gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (GET_WIDGET ("albums_treeview"))), &tree_model, &iter)) {
+				gtk_widget_set_sensitive (GET_WIDGET ("upload_button"), FALSE);
+				return;
+			}
+
+			gtk_tree_model_get (tree_model, &iter,
+					    ALBUM_DATA_COLUMN, &album,
+					    -1);
+
+			gth_task_dialog (GTH_TASK (data->conn), FALSE);
+
+			file_list = gth_file_data_list_to_file_list (data->file_list);
+			flickr_service_post_photos (data->picasaweb,
+						    album,
+						    file_list,
+						    data->cancellable,
+						    post_photos_ready_cb,
+						    data);
+
+			_g_object_list_unref (file_list);
+			g_object_unref (album);
+			*/
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+static void
+update_account_list (DialogData *data)
+{
+	GtkTreeIter  iter;
+	int          current_account;
+	int          idx;
+	GList       *scan;
+	char        *free_space;
+
+	current_account = 0;
+	gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("account_liststore")));
+	for (scan = data->accounts, idx = 0; scan; scan = scan->next, idx++) {
+		FlickrAccount *account = scan->data;
+
+		if ((data->account != NULL) && (g_strcmp0 (data->account->username, account->username) == 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_NAME_COLUMN, account->username,
+				    -1);
+	}
+	gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("account_combobox")), current_account);
+
+	free_space = g_format_size_for_display (data->user->max_bandwidth - data->user->used_bandwidth);
+	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("free_space_label")), free_space);
+	g_free (free_space);
+}
+
+
+static void
+upload_status_ready_cb (GObject      *source_object,
+			GAsyncResult *res,
+			gpointer      user_data)
+{
+	DialogData *data = user_data;
+	GError     *error = NULL;
+
+	data->user = flickr_service_get_upload_status_finish (FLICKR_SERVICE (source_object), res, &error);
+	if (data->user == NULL) {
+		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+		gtk_widget_destroy (data->dialog);
+		return;
+	}
+
+	gth_task_dialog (GTH_TASK (data->conn), TRUE);
+	update_account_list (data);
+
+	gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (data->browser));
+	gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
+	gtk_window_present (GTK_WINDOW (data->dialog));
+}
+
+
+static void
+connect_to_server (DialogData *data)
+{
+	g_return_if_fail (data->account != NULL);
+
+	flickr_connection_set_auth_token (data->conn, data->account->token);
+	if (data->service == NULL)
+		data->service = flickr_service_new (data->conn);
+	flickr_service_get_upload_status (data->service,
+					  data->cancellable,
+					  upload_status_ready_cb,
+					  data);
+}
+
+
+static void
+connection_token_ready_cb (GObject      *source_object,
+			   GAsyncResult *res,
+			   gpointer      user_data)
+{
+	DialogData *data = user_data;
+	GError     *error = NULL;
+	GList      *link;
+
+	if (! flickr_connection_get_token_finish (FLICKR_CONNECTION (source_object), res, &error)) {
+		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+		gtk_widget_destroy (data->dialog);
+		return;
+	}
+
+	_g_object_unref (data->account);
+	data->account = flickr_account_new ();
+	flickr_account_set_username (data->account, flickr_connection_get_username (data->conn));
+	flickr_account_set_token (data->account, flickr_connection_get_auth_token (data->conn));
+
+	link = g_list_find_custom (data->accounts, data->account, (GCompareFunc) flickr_account_cmp);
+	if (link != NULL) {
+		data->accounts = g_list_remove_link (data->accounts, link);
+		_g_object_list_unref (link);
+	}
+	data->accounts = g_list_prepend (data->accounts, g_object_ref (data->account));
+
+	connect_to_server (data);
+}
+
+
+static void
+complete_authorization_messagedialog_response_cb (GtkDialog *dialog,
+						  int        response_id,
+						  gpointer   user_data)
+{
+	DialogData *data = user_data;
+
+	switch (response_id) {
+	case GTK_RESPONSE_HELP:
+		show_help_dialog (GTK_WINDOW (dialog), "flicker-complete-authorization");
+		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:
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		gth_task_dialog (GTH_TASK (data->conn), FALSE);
+		flickr_connection_get_token (data->conn,
+					     data->cancellable,
+					     connection_token_ready_cb,
+					     data);
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+static void
+complete_authorization (DialogData *data)
+{
+	GtkBuilder *builder;
+	GtkWidget  *dialog;
+
+	gth_task_dialog (GTH_TASK (data->conn), TRUE);
+
+	builder = _gtk_builder_new_from_file ("flicker-complete-authorization.ui", "flicker");
+	dialog = _gtk_builder_get_widget (builder, "complete_authorization_messagedialog");
+	g_object_set_data_full (G_OBJECT (dialog), "builder", builder, g_object_unref);
+	g_signal_connect (dialog,
+			  "response",
+			  G_CALLBACK (complete_authorization_messagedialog_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));
+}
+
+
+static void
+ask_authorization_messagedialog_response_cb (GtkDialog *dialog,
+					     int        response_id,
+					     gpointer   user_data)
+{
+	DialogData *data = user_data;
+
+	switch (response_id) {
+	case GTK_RESPONSE_HELP:
+		show_help_dialog (GTK_WINDOW (dialog), "flicker-ask-authorization");
+		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:
+		{
+			char   *url;
+			GError *error = NULL;
+
+			gtk_widget_destroy (GTK_WIDGET (dialog));
+
+			url = flickr_connection_get_login_link (data->conn, FLICKR_ACCESS_WRITE);
+			if (gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)), url, 0, &error)) {
+				complete_authorization (data);
+			}
+			else {
+				_gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+				gtk_widget_destroy (data->dialog);
+			}
+
+			g_free (url);
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+static void
+ask_authorization (DialogData *data)
+{
+	GtkBuilder *builder;
+	GtkWidget  *dialog;
+
+	gth_task_dialog (GTH_TASK (data->conn), TRUE);
+
+	builder = _gtk_builder_new_from_file ("flicker-ask-authorization.ui", "flicker");
+	dialog = _gtk_builder_get_widget (builder, "ask_authorization_messagedialog");
+	g_object_set_data_full (G_OBJECT (dialog), "builder", builder, g_object_unref);
+	g_signal_connect (dialog,
+			  "response",
+			  G_CALLBACK (ask_authorization_messagedialog_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));
+}
+
+
+static void
+connection_frob_ready_cb (GObject      *source_object,
+			  GAsyncResult *res,
+			  gpointer      user_data)
+{
+	DialogData *data = user_data;
+	GError     *error = NULL;
+
+	if (! flickr_connection_get_frob_finish (FLICKR_CONNECTION (source_object), res, &error)) {
+		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+		gtk_widget_destroy (data->dialog);
+		return;
+	}
+
+	ask_authorization (data);
+}
+
+
+static void
+start_authorization_process (DialogData *data)
+{
+	flickr_connection_get_frob (data->conn,
+				    data->cancellable,
+				    connection_frob_ready_cb,
+				    data);
+}
+
+
+static void
+account_chooser_dialog_response_cb (GtkDialog *dialog,
+				    int        response_id,
+				    gpointer   user_data)
+{
+	DialogData *data = user_data;
+
+	switch (response_id) {
+	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_object_unref (data->account);
+		data->account = flickr_account_chooser_dialog_get_active (FLICKR_ACCOUNT_CHOOSER_DIALOG (dialog));
+		if (data->account != NULL) {
+			gtk_widget_destroy (GTK_WIDGET (dialog));
+			connect_to_server (data);
+		}
+
+		break;
+
+	case FLICKR_ACCOUNT_CHOOSER_RESPONSE_NEW:
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		start_authorization_process (data);
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+static void
+auto_select_account (DialogData *data)
+{
+	gtk_widget_hide (data->dialog);
+	gth_task_dialog (GTH_TASK (data->conn), FALSE);
+
+	if (data->accounts != NULL) {
+		if (data->account != NULL) {
+			connect_to_server (data);
+		}
+		else if (data->accounts->next == NULL) {
+			data->account = g_object_ref (data->accounts->data);
+			connect_to_server (data);
+		}
+		else {
+			GtkWidget *dialog;
+
+			gth_task_dialog (GTH_TASK (data->conn), TRUE);
+			dialog = flickr_account_chooser_dialog_new (data->accounts, data->account);
+			g_signal_connect (dialog,
+					  "response",
+					  G_CALLBACK (account_chooser_dialog_response_cb),
+					  data);
+
+			gtk_window_set_title (GTK_WINDOW (dialog), _("Choose 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));
+		}
+	}
+	else
+		start_authorization_process (data);
+}
+
+
+static void
+account_manager_dialog_response_cb (GtkDialog *dialog,
+			            int        response_id,
+			            gpointer   user_data)
+{
+	DialogData *data = user_data;
+
+	switch (response_id) {
+	case GTK_RESPONSE_DELETE_EVENT:
+	case GTK_RESPONSE_CANCEL:
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		break;
+
+	case GTK_RESPONSE_OK:
+		_g_object_list_unref (data->accounts);
+		data->accounts = flickr_account_manager_dialog_get_accounts (FLICKR_ACCOUNT_MANAGER_DIALOG (dialog));
+		if (! g_list_find_custom (data->accounts, data->account, (GCompareFunc) flickr_account_cmp)) {
+			_g_object_unref (data->account);
+			data->account = NULL;
+			auto_select_account (data);
+		}
+		else
+			update_account_list (data);
+		flickr_accounts_save_to_file (data->accounts, data->account);
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+static void
+edit_accounts_button_clicked_cb (GtkButton *button,
+			         gpointer   user_data)
+{
+	DialogData *data = user_data;
+	GtkWidget  *dialog;
+
+	dialog = flickr_account_manager_dialog_new (data->accounts);
+	g_signal_connect (dialog,
+			  "response",
+			  G_CALLBACK (account_manager_dialog_response_cb),
+			  data);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), _("Edit Accounts"));
+	gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->dialog));
+	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+	gtk_window_present (GTK_WINDOW (dialog));
+}
+
+
+static void
+account_combobox_changed_cb (GtkComboBox *widget,
+			     gpointer     user_data)
+{
+	DialogData    *data = user_data;
+	GtkTreeIter    iter;
+	FlickrAccount *account;
+
+	if (! gtk_combo_box_get_active_iter (widget, &iter))
+		return;
+
+	gtk_tree_model_get (gtk_combo_box_get_model (widget),
+			    &iter,
+			    ACCOUNT_DATA_COLUMN, &account,
+			    -1);
+
+	if (flickr_account_cmp (account, data->account) != 0) {
+		_g_object_unref (data->account);
+		data->account = account;
+		auto_select_account (data);
+	}
+	else
+		g_object_unref (account);
+}
+
+
+void
+dlg_export_to_flickr (GthBrowser *browser,
+		      GList      *file_list)
+{
+	DialogData       *data;
+	GList            *scan;
+	int               n_total;
+	goffset           total_size;
+	char             *total_size_formatted;
+	char             *text;
+	GtkWidget        *list_view;
+	/*GtkTreeSelection *selection;*/
+
+	data = g_new0 (DialogData, 1);
+	data->browser = browser;
+	data->location = gth_file_data_dup (gth_browser_get_location_data (browser));
+	data->builder = _gtk_builder_new_from_file ("export-to-flickr.ui", "flicker");
+	data->dialog = _gtk_builder_get_widget (data->builder, "export_dialog");
+	data->cancellable = g_cancellable_new ();
+
+	data->file_list = NULL;
+	n_total = 0;
+	total_size = 0;
+	for (scan = file_list; scan; scan = scan->next) {
+		GthFileData *file_data = scan->data;
+		const char  *mime_type;
+
+		mime_type = gth_file_data_get_mime_type (file_data);
+		if (g_content_type_equals (mime_type, "image/bmp")
+		    || g_content_type_equals (mime_type, "image/gif")
+		    || g_content_type_equals (mime_type, "image/jpeg")
+		    || g_content_type_equals (mime_type, "image/png"))
+		{
+			GthFileData *new_file_data;
+
+			new_file_data = gth_file_data_dup (file_data);
+			new_file_data->thumb_loaded = FALSE;
+			total_size += g_file_info_get_size (new_file_data->info);
+			n_total++;
+			data->file_list = g_list_prepend (data->file_list, new_file_data);
+		}
+	}
+
+	if (data->file_list == NULL) {
+		GError *error;
+
+		error = g_error_new_literal (GTH_ERROR, GTH_ERROR_GENERIC, _("No valid file selected."));
+		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (browser), _("Could not export the files"), &error);
+		gtk_widget_destroy (data->dialog);
+		return;
+	}
+
+	total_size_formatted = g_format_size_for_display (total_size);
+	text = g_strdup_printf (g_dngettext (NULL, "%d file (%s)", "%d files (%s)", n_total), n_total, total_size_formatted);
+	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("images_info_label")), text);
+	g_free (text);
+	g_free (total_size_formatted);
+
+	/* Set the widget data */
+
+	list_view = gth_file_list_new (GTH_FILE_LIST_TYPE_NO_SELECTION);
+	gth_file_list_set_thumb_size (GTH_FILE_LIST (list_view), 112);
+	gth_file_view_set_spacing (GTH_FILE_VIEW (gth_file_list_get_view (GTH_FILE_LIST (list_view))), 0);
+	gth_file_list_enable_thumbs (GTH_FILE_LIST (list_view), TRUE);
+	gth_file_list_set_caption (GTH_FILE_LIST (list_view), "none");
+	gth_file_list_set_sort_func (GTH_FILE_LIST (list_view), gth_main_get_sort_type ("file::name")->cmp_func, FALSE);
+	gtk_widget_show (list_view);
+	gtk_box_pack_start (GTK_BOX (GET_WIDGET ("images_box")), list_view, TRUE, TRUE, 0);
+	gth_file_list_set_files (GTH_FILE_LIST (list_view), data->file_list);
+
+	gtk_widget_set_sensitive (GET_WIDGET ("upload_button"), FALSE);
+
+	/* Set the signals handlers. */
+
+	g_signal_connect (G_OBJECT (data->dialog),
+			  "destroy",
+			  G_CALLBACK (export_dialog_destroy_cb),
+			  data);
+
+	g_signal_connect (data->dialog,
+			  "response",
+			  G_CALLBACK (export_dialog_response_cb),
+			  data);
+	g_signal_connect (GET_WIDGET ("edit_accounts_button"),
+			  "clicked",
+			  G_CALLBACK (edit_accounts_button_clicked_cb),
+			  data);
+	g_signal_connect (GET_WIDGET ("account_combobox"),
+			  "changed",
+			  G_CALLBACK (account_combobox_changed_cb),
+			  data);
+
+	/* FIXME
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (GET_WIDGET ("albums_treeview")));
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+	g_signal_connect (selection,
+			  "changed",
+			  G_CALLBACK (albums_treeview_selection_changed_cb),
+			  data);
+
+	data->accounts = flickr_accounts_load_from_file (&data->username);
+	auto_select_account (data);
+	*/
+
+	data->conn = flickr_connection_new ();
+	data->accounts = flickr_accounts_load_from_file ();
+	data->account = flickr_accounts_find_default (data->accounts);
+	auto_select_account (data);
+}
diff --git a/extensions/flicker/dlg-export-to-flickr.h b/extensions/flicker/dlg-export-to-flickr.h
new file mode 100644
index 0000000..c7f0c4d
--- /dev/null
+++ b/extensions/flicker/dlg-export-to-flickr.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2010 The 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 DLG_EXPORT_TO_FLICKR_H
+#define DLG_EXPORT_TO_FLICKR_H
+
+#include <gthumb.h>
+
+void dlg_export_to_flickr (GthBrowser *browser,
+			   GList      *file_list);
+
+#endif /* DLG_EXPORT_TO_FLICKR_H */
diff --git a/extensions/flicker/flickr-account-chooser-dialog.c b/extensions/flicker/flickr-account-chooser-dialog.c
new file mode 100644
index 0000000..fa48273
--- /dev/null
+++ b/extensions/flicker/flickr-account-chooser-dialog.c
@@ -0,0 +1,184 @@
+/* -*- 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 <glib/gi18n.h>
+#include "flickr-account-chooser-dialog.h"
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+
+
+enum {
+	ACCOUNT_DATA_COLUMN,
+	ACCOUNT_NAME_COLUMN
+};
+
+
+static gpointer parent_class = NULL;
+
+
+struct _FlickrAccountChooserDialogPrivate {
+	GtkBuilder *builder;
+};
+
+
+static void
+flickr_account_chooser_dialog_finalize (GObject *object)
+{
+	FlickrAccountChooserDialog *self;
+
+	self = FLICKR_ACCOUNT_CHOOSER_DIALOG (object);
+
+	_g_object_unref (self->priv->builder);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+flickr_account_chooser_dialog_class_init (FlickrAccountChooserDialogClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (FlickrAccountChooserDialogPrivate));
+
+	object_class = (GObjectClass*) klass;
+	object_class->finalize = flickr_account_chooser_dialog_finalize;
+}
+
+
+static void
+flickr_account_chooser_dialog_init (FlickrAccountChooserDialog *self)
+{
+	GtkWidget *content;
+
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FLICKR_TYPE_ACCOUNT_CHOOSER_DIALOG, FlickrAccountChooserDialogPrivate);
+	self->priv->builder = _gtk_builder_new_from_file ("flicker-account-chooser.ui", "flicker");
+
+	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_chooser");
+	gtk_container_set_border_width (GTK_CONTAINER (content), 5);
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), content, TRUE, TRUE, 0);
+
+	gtk_dialog_add_button (GTK_DIALOG (self),
+			       GTK_STOCK_NEW,
+			       FLICKR_ACCOUNT_CHOOSER_RESPONSE_NEW);
+	gtk_dialog_add_button (GTK_DIALOG (self),
+			       GTK_STOCK_CANCEL,
+			       GTK_RESPONSE_CANCEL);
+	gtk_dialog_add_button (GTK_DIALOG (self),
+			       GTK_STOCK_OK,
+			       GTK_RESPONSE_OK);
+	gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
+}
+
+
+GType
+flickr_account_chooser_dialog_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (FlickrAccountChooserDialogClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) flickr_account_chooser_dialog_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (FlickrAccountChooserDialog),
+			0,
+			(GInstanceInitFunc) flickr_account_chooser_dialog_init,
+			NULL
+		};
+		type = g_type_register_static (GTK_TYPE_DIALOG,
+					       "FlickrAccountChooserDialog",
+					       &g_define_type_info,
+					       0);
+	}
+
+	return type;
+}
+
+
+static void
+flickr_account_chooser_dialog_construct (FlickrAccountChooserDialog *self,
+				         GList                      *accounts,
+				         FlickrAccount              *default_account)
+{
+	GtkTreeIter  iter;
+	GList       *scan;
+	int          active = 0;
+	int          idx;
+
+	gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("account_liststore")));
+
+	for (scan = accounts, idx = 0; scan; scan = scan->next, idx++) {
+		FlickrAccount *account = scan->data;
+
+		if ((default_account != NULL) && (g_strcmp0 (account->username, default_account->username) == 0))
+			active = 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_NAME_COLUMN, account->username,
+				    -1);
+	}
+
+	gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("account_combobox")), active);
+}
+
+
+GtkWidget *
+flickr_account_chooser_dialog_new (GList         *accounts,
+				   FlickrAccount *default_account)
+{
+	FlickrAccountChooserDialog *self;
+
+	self = g_object_new (FLICKR_TYPE_ACCOUNT_CHOOSER_DIALOG, NULL);
+	flickr_account_chooser_dialog_construct (self, accounts, default_account);
+
+	return (GtkWidget *) self;
+}
+
+
+FlickrAccount *
+flickr_account_chooser_dialog_get_active (FlickrAccountChooserDialog *self)
+{
+	GtkTreeIter    iter;
+	FlickrAccount *account;
+
+	if (! gtk_combo_box_get_active_iter (GTK_COMBO_BOX (GET_WIDGET ("account_combobox")), &iter))
+		return NULL;
+
+	gtk_tree_model_get (GTK_TREE_MODEL (GET_WIDGET ("account_liststore")), &iter,
+			    ACCOUNT_DATA_COLUMN, &account,
+			    -1);
+
+	return account;
+}
diff --git a/extensions/flicker/flickr-account-chooser-dialog.h b/extensions/flicker/flickr-account-chooser-dialog.h
new file mode 100644
index 0000000..a288527
--- /dev/null
+++ b/extensions/flicker/flickr-account-chooser-dialog.h
@@ -0,0 +1,61 @@
+/* -*- 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 FLICKR_ACCOUNT_CHOOSER_DIALOG_H
+#define FLICKR_ACCOUNT_CHOOSER_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <gthumb.h>
+#include "flickr-account.h"
+
+G_BEGIN_DECLS
+
+#define FLICKR_ACCOUNT_CHOOSER_RESPONSE_NEW 1
+
+#define FLICKR_TYPE_ACCOUNT_CHOOSER_DIALOG            (flickr_account_chooser_dialog_get_type ())
+#define FLICKR_ACCOUNT_CHOOSER_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLICKR_TYPE_ACCOUNT_CHOOSER_DIALOG, FlickrAccountChooserDialog))
+#define FLICKR_ACCOUNT_CHOOSER_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FLICKR_TYPE_ACCOUNT_CHOOSER_DIALOG, FlickrAccountChooserDialogClass))
+#define FLICKR_IS_ACCOUNT_CHOOSER_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLICKR_TYPE_ACCOUNT_CHOOSER_DIALOG))
+#define FLICKR_IS_ACCOUNT_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FLICKR_TYPE_ACCOUNT_CHOOSER_DIALOG))
+#define FLICKR_ACCOUNT_CHOOSER_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FLICKR_TYPE_ACCOUNT_CHOOSER_DIALOG, FlickrAccountChooserDialogClass))
+
+typedef struct _FlickrAccountChooserDialog FlickrAccountChooserDialog;
+typedef struct _FlickrAccountChooserDialogClass FlickrAccountChooserDialogClass;
+typedef struct _FlickrAccountChooserDialogPrivate FlickrAccountChooserDialogPrivate;
+
+struct _FlickrAccountChooserDialog {
+	GtkDialog parent_instance;
+	FlickrAccountChooserDialogPrivate *priv;
+};
+
+struct _FlickrAccountChooserDialogClass {
+	GtkDialogClass parent_class;
+};
+
+GType           flickr_account_chooser_dialog_get_type    (void);
+GtkWidget *     flickr_account_chooser_dialog_new         (GList                      *accounts,
+							   FlickrAccount              *default_account);
+FlickrAccount * flickr_account_chooser_dialog_get_active  (FlickrAccountChooserDialog *self);
+
+G_END_DECLS
+
+#endif /* FLICKR_ACCOUNT_CHOOSER_DIALOG_H */
diff --git a/extensions/flicker/flickr-account-manager-dialog.c b/extensions/flicker/flickr-account-manager-dialog.c
new file mode 100644
index 0000000..6b14f96
--- /dev/null
+++ b/extensions/flicker/flickr-account-manager-dialog.c
@@ -0,0 +1,247 @@
+/* -*- 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 <glib/gi18n.h>
+#include "flickr-account.h"
+#include "flickr-account-manager-dialog.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+
+
+enum {
+	ACCOUNT_DATA_COLUMN,
+	ACCOUNT_NAME_COLUMN,
+};
+
+
+static gpointer parent_class = NULL;
+
+
+struct _FlickrAccountManagerDialogPrivate {
+	GtkBuilder *builder;
+};
+
+
+static void
+flickr_account_manager_dialog_finalize (GObject *object)
+{
+	FlickrAccountManagerDialog *self;
+
+	self = FLICKR_ACCOUNT_MANAGER_DIALOG (object);
+
+	_g_object_unref (self->priv->builder);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+flickr_account_manager_dialog_class_init (FlickrAccountManagerDialogClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (FlickrAccountManagerDialogPrivate));
+
+	object_class = (GObjectClass*) klass;
+	object_class->finalize = flickr_account_manager_dialog_finalize;
+}
+
+
+static void
+delete_button_clicked_cb (GtkWidget *button,
+		          gpointer   user_data)
+{
+	FlickrAccountManagerDialog *self = user_data;
+	GtkTreeModel               *tree_model;
+	GtkTreeIter                 iter;
+
+	if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (GET_WIDGET ("account_treeview"))),
+					     &tree_model,
+					     &iter))
+	{
+		gtk_list_store_remove (GTK_LIST_STORE (tree_model), &iter);
+	}
+}
+
+
+static void
+text_renderer_edited_cb (GtkCellRendererText *renderer,
+			 char                *path,
+			 char                *new_text,
+			 gpointer             user_data)
+{
+	FlickrAccountManagerDialog *self = user_data;
+	GtkTreePath                *tree_path;
+	GtkTreeIter                 iter;
+	FlickrAccount              *account;
+
+	tree_path = gtk_tree_path_new_from_string (path);
+	tree_path = gtk_tree_path_new_from_string (path);
+	if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (GET_WIDGET ("accounts_liststore")),
+				       &iter,
+				       tree_path))
+	{
+		gtk_tree_path_free (tree_path);
+		return;
+	}
+	gtk_tree_path_free (tree_path);
+
+	gtk_tree_model_get (GTK_TREE_MODEL (GET_WIDGET ("accounts_liststore")), &iter,
+			    ACCOUNT_DATA_COLUMN, &account,
+			    -1);
+	flickr_account_set_username (account, new_text);
+
+	gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("accounts_liststore")), &iter,
+			    ACCOUNT_DATA_COLUMN, account,
+			    ACCOUNT_NAME_COLUMN, new_text,
+			    -1);
+
+	g_object_unref (account);
+}
+
+
+static void
+flickr_account_manager_dialog_init (FlickrAccountManagerDialog *self)
+{
+	GtkWidget *content;
+
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FLICKR_TYPE_ACCOUNT_MANAGER_DIALOG, FlickrAccountManagerDialogPrivate);
+	self->priv->builder = _gtk_builder_new_from_file ("flicker-account-manager.ui", "flicker");
+
+	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_manager");
+	gtk_container_set_border_width (GTK_CONTAINER (content), 5);
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), content, TRUE, TRUE, 0);
+
+	gtk_dialog_add_button (GTK_DIALOG (self),
+			       GTK_STOCK_CANCEL,
+			       GTK_RESPONSE_CANCEL);
+	gtk_dialog_add_button (GTK_DIALOG (self),
+			       GTK_STOCK_OK,
+			       GTK_RESPONSE_OK);
+
+	g_object_set (GET_WIDGET ("account_cellrenderertext"), "editable", TRUE, NULL);
+        g_signal_connect (GET_WIDGET ("account_cellrenderertext"),
+                          "edited",
+                          G_CALLBACK (text_renderer_edited_cb),
+                          self);
+
+	g_signal_connect (GET_WIDGET ("delete_button"),
+			  "clicked",
+			  G_CALLBACK (delete_button_clicked_cb),
+			  self);
+}
+
+
+GType
+flickr_account_manager_dialog_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (FlickrAccountManagerDialogClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) flickr_account_manager_dialog_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (FlickrAccountManagerDialog),
+			0,
+			(GInstanceInitFunc) flickr_account_manager_dialog_init,
+			NULL
+		};
+		type = g_type_register_static (GTK_TYPE_DIALOG,
+					       "FlickrAccountManagerDialog",
+					       &g_define_type_info,
+					       0);
+	}
+
+	return type;
+}
+
+
+static void
+flickr_account_manager_dialog_construct (FlickrAccountManagerDialog *self,
+				         GList                      *accounts)
+{
+	GtkListStore *list_store;
+	GtkTreeIter   iter;
+	GList        *scan;
+
+	list_store = GTK_LIST_STORE (GET_WIDGET ("accounts_liststore"));
+	gtk_list_store_clear (list_store);
+	for (scan = accounts; scan; scan = scan->next) {
+		FlickrAccount *account = scan->data;
+
+		gtk_list_store_append (list_store, &iter);
+		gtk_list_store_set (list_store, &iter,
+				    ACCOUNT_DATA_COLUMN, account,
+				    ACCOUNT_NAME_COLUMN, account->username,
+				    -1);
+	}
+}
+
+
+GtkWidget *
+flickr_account_manager_dialog_new (GList *accounts)
+{
+	FlickrAccountManagerDialog *self;
+
+	self = g_object_new (FLICKR_TYPE_ACCOUNT_MANAGER_DIALOG, NULL);
+	flickr_account_manager_dialog_construct (self, accounts);
+
+	return (GtkWidget *) self;
+}
+
+
+GList *
+flickr_account_manager_dialog_get_accounts (FlickrAccountManagerDialog *self)
+{
+	GList        *accounts;
+	GtkTreeModel *tree_model;
+	GtkTreeIter   iter;
+
+	tree_model = (GtkTreeModel *) GET_WIDGET ("accounts_liststore");
+	if (! gtk_tree_model_get_iter_first (tree_model, &iter))
+		return NULL;
+
+	accounts = NULL;
+	do {
+		FlickrAccount *account;
+
+		gtk_tree_model_get (tree_model, &iter,
+				    ACCOUNT_DATA_COLUMN, &account,
+				    -1);
+		accounts = g_list_prepend (accounts, account);
+	}
+	while (gtk_tree_model_iter_next (tree_model, &iter));
+
+	return g_list_reverse (accounts);
+}
diff --git a/extensions/flicker/flickr-account-manager-dialog.h b/extensions/flicker/flickr-account-manager-dialog.h
new file mode 100644
index 0000000..5db8d52
--- /dev/null
+++ b/extensions/flicker/flickr-account-manager-dialog.h
@@ -0,0 +1,59 @@
+/* -*- 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 FLICKR_ACCOUNT_MANAGER_DIALOG_H
+#define FLICKR_ACCOUNT_MANAGER_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+#define FLICKR_ACCOUNT_MANAGER_RESPONSE_NEW 1
+
+#define FLICKR_TYPE_ACCOUNT_MANAGER_DIALOG            (flickr_account_manager_dialog_get_type ())
+#define FLICKR_ACCOUNT_MANAGER_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLICKR_TYPE_ACCOUNT_MANAGER_DIALOG, FlickrAccountManagerDialog))
+#define FLICKR_ACCOUNT_MANAGER_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FLICKR_TYPE_ACCOUNT_MANAGER_DIALOG, FlickrAccountManagerDialogClass))
+#define FLICKR_IS_ACCOUNT_MANAGER_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLICKR_TYPE_ACCOUNT_MANAGER_DIALOG))
+#define FLICKR_IS_ACCOUNT_MANAGER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FLICKR_TYPE_ACCOUNT_MANAGER_DIALOG))
+#define FLICKR_ACCOUNT_MANAGER_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FLICKR_TYPE_ACCOUNT_MANAGER_DIALOG, FlickrAccountManagerDialogClass))
+
+typedef struct _FlickrAccountManagerDialog FlickrAccountManagerDialog;
+typedef struct _FlickrAccountManagerDialogClass FlickrAccountManagerDialogClass;
+typedef struct _FlickrAccountManagerDialogPrivate FlickrAccountManagerDialogPrivate;
+
+struct _FlickrAccountManagerDialog {
+	GtkDialog parent_instance;
+	FlickrAccountManagerDialogPrivate *priv;
+};
+
+struct _FlickrAccountManagerDialogClass {
+	GtkDialogClass parent_class;
+};
+
+GType          flickr_account_manager_dialog_get_type     (void);
+GtkWidget *    flickr_account_manager_dialog_new          (GList                      *accounts);
+GList *        flickr_account_manager_dialog_get_accounts (FlickrAccountManagerDialog *dialog);
+
+G_END_DECLS
+
+#endif /* FLICKR_ACCOUNT_MANAGER_DIALOG_H */
diff --git a/extensions/flicker/flickr-account.c b/extensions/flicker/flickr-account.c
new file mode 100644
index 0000000..1242e11
--- /dev/null
+++ b/extensions/flicker/flickr-account.c
@@ -0,0 +1,180 @@
+/* -*- 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 "flickr-account.h"
+
+
+static gpointer flickr_account_parent_class = NULL;
+
+
+static void
+flickr_account_finalize (GObject *obj)
+{
+	FlickrAccount *self;
+
+	self = FLICKR_ACCOUNT (obj);
+
+	g_free (self->username);
+	g_free (self->token);
+
+	G_OBJECT_CLASS (flickr_account_parent_class)->finalize (obj);
+}
+
+
+static void
+flickr_account_class_init (FlickrAccountClass *klass)
+{
+	flickr_account_parent_class = g_type_class_peek_parent (klass);
+	G_OBJECT_CLASS (klass)->finalize = flickr_account_finalize;
+}
+
+
+static DomElement*
+flickr_account_create_element (DomDomizable *base,
+			       DomDocument  *doc)
+{
+	FlickrAccount *self;
+	DomElement *element;
+
+	self = FLICKR_ACCOUNT (base);
+
+	element = dom_document_create_element (doc, "account", NULL);
+	if (self->username != NULL)
+		dom_element_set_attribute (element, "username", self->username);
+	if (self->token != NULL)
+		dom_element_set_attribute (element, "token", self->token);
+	if (self->is_default)
+		dom_element_set_attribute (element, "default", "1");
+
+	return element;
+}
+
+
+static void
+flickr_account_load_from_element (DomDomizable *base,
+			          DomElement   *element)
+{
+	FlickrAccount *self;
+
+	self = FLICKR_ACCOUNT (base);
+
+	flickr_account_set_username (self, dom_element_get_attribute (element, "username"));
+	flickr_account_set_token (self, dom_element_get_attribute (element, "token"));
+	self->is_default = (g_strcmp0 (dom_element_get_attribute (element, "default"), "1") == 0);
+}
+
+
+static void
+flickr_account_dom_domizable_interface_init (DomDomizableIface *iface)
+{
+	iface->create_element = flickr_account_create_element;
+	iface->load_from_element = flickr_account_load_from_element;
+}
+
+
+static void
+flickr_account_instance_init (FlickrAccount *self)
+{
+}
+
+
+GType
+flickr_account_get_type (void)
+{
+	static GType flickr_account_type_id = 0;
+
+	if (flickr_account_type_id == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (FlickrAccountClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) flickr_account_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (FlickrAccount),
+			0,
+			(GInstanceInitFunc) flickr_account_instance_init,
+			NULL
+		};
+		static const GInterfaceInfo dom_domizable_info = {
+			(GInterfaceInitFunc) flickr_account_dom_domizable_interface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL
+		};
+
+		flickr_account_type_id = g_type_register_static (G_TYPE_OBJECT,
+								   "FlickrAccount",
+								   &g_define_type_info,
+								   0);
+		g_type_add_interface_static (flickr_account_type_id, DOM_TYPE_DOMIZABLE, &dom_domizable_info);
+	}
+
+	return flickr_account_type_id;
+}
+
+
+FlickrAccount *
+flickr_account_new (void)
+{
+	return g_object_new (FLICKR_TYPE_ACCOUNT, NULL);
+}
+
+
+void
+flickr_account_set_username (FlickrAccount *self,
+			     const char    *value)
+{
+	g_free (self->username);
+	self->username = NULL;
+	if (value != NULL)
+		self->username = g_strdup (value);
+}
+
+
+void
+flickr_account_set_token (FlickrAccount *self,
+			  const char    *value)
+{
+	g_free (self->token);
+	self->token = NULL;
+	if (value != NULL)
+		self->token = g_strdup (value);
+}
+
+
+int
+flickr_account_cmp (FlickrAccount *a,
+		    FlickrAccount *b)
+{
+	if ((a == NULL) && (b == NULL))
+		return 0;
+	else if (a == NULL)
+		return 1;
+	else if (b == NULL)
+		return -1;
+	else
+		return g_strcmp0 (a->username, b->username);
+}
diff --git a/extensions/flicker/flickr-account.h b/extensions/flicker/flickr-account.h
new file mode 100644
index 0000000..6c9954c
--- /dev/null
+++ b/extensions/flicker/flickr-account.h
@@ -0,0 +1,66 @@
+/* -*- 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 FLICKR_ACCOUNT_H
+#define FLICKR_ACCOUNT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FLICKR_TYPE_ACCOUNT            (flickr_account_get_type ())
+#define FLICKR_ACCOUNT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLICKR_TYPE_ACCOUNT, FlickrAccount))
+#define FLICKR_ACCOUNT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FLICKR_TYPE_ACCOUNT, FlickrAccountClass))
+#define FLICKR_IS_ACCOUNT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLICKR_TYPE_ACCOUNT))
+#define FLICKR_IS_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FLICKR_TYPE_ACCOUNT))
+#define FLICKR_ACCOUNT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FLICKR_TYPE_ACCOUNT, FlickrAccountClass))
+
+typedef struct _FlickrAccount FlickrAccount;
+typedef struct _FlickrAccountClass FlickrAccountClass;
+typedef struct _FlickrAccountPrivate FlickrAccountPrivate;
+
+struct _FlickrAccount {
+	GObject parent_instance;
+	FlickrAccountPrivate *priv;
+
+	char     *username;
+	char     *token;
+	gboolean  is_default;
+};
+
+struct _FlickrAccountClass {
+	GObjectClass parent_class;
+};
+
+GType             flickr_account_get_type       (void);
+FlickrAccount *   flickr_account_new            (void);
+void              flickr_account_set_username   (FlickrAccount *self,
+						 const char    *value);
+void              flickr_account_set_token      (FlickrAccount *self,
+						 const char    *value);
+int               flickr_account_cmp            (FlickrAccount *a,
+						 FlickrAccount *b);
+
+G_END_DECLS
+
+#endif /* FLICKR_ACCOUNT_H */
diff --git a/extensions/flicker/flickr-connection.c b/extensions/flicker/flickr-connection.c
new file mode 100644
index 0000000..b977cbb
--- /dev/null
+++ b/extensions/flicker/flickr-connection.c
@@ -0,0 +1,552 @@
+/* -*- 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 <glib.h>
+#include <glib/gi18n.h>
+#include <gthumb.h>
+#include "flickr-connection.h"
+#include "flickr-user.h"
+
+
+#define DEBUG_FLICKR_CONNECTION
+#define GTHUMB_FLICKR_API_KEY "8960706ee7f4151e893b11837e9c24ce"
+#define GTHUMB_FLICKR_SHARED_SECRET "1ff8d1e45c873423"
+
+
+GQuark
+flickr_connection_error_quark (void)
+{
+	static GQuark quark;
+
+        if (!quark)
+                quark = g_quark_from_static_string ("flickr_connection");
+
+        return quark;
+}
+
+
+/* -- FlickrConnection -- */
+
+
+struct _FlickrConnectionPrivate
+{
+	SoupSession        *session;
+	char               *frob;
+	char               *token;
+	char               *username;
+	char               *user_id;
+	GCancellable       *cancellable;
+	GSimpleAsyncResult *result;
+	GChecksum          *checksum;
+};
+
+
+static gpointer parent_class = NULL;
+
+
+static void
+flickr_connection_finalize (GObject *object)
+{
+	FlickrConnection *self;
+
+	self = FLICKR_CONNECTION (object);
+
+	g_checksum_free (self->priv->checksum);
+	_g_object_unref (self->priv->result);
+	_g_object_unref (self->priv->cancellable);
+	g_free (self->priv->user_id);
+	g_free (self->priv->username);
+	g_free (self->priv->token);
+	g_free (self->priv->frob);
+	_g_object_unref (self->priv->session);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+flickr_connection_exec (GthTask *base)
+{
+	/* void */
+}
+
+
+static void
+flickr_connection_cancelled (GthTask *base)
+{
+	/* void */
+}
+
+
+static void
+flickr_connection_class_init (FlickrConnectionClass *klass)
+{
+	GObjectClass *object_class;
+	GthTaskClass *task_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (FlickrConnectionPrivate));
+
+	object_class = (GObjectClass*) klass;
+	object_class->finalize = flickr_connection_finalize;
+
+	task_class = (GthTaskClass*) klass;
+	task_class->exec = flickr_connection_exec;
+	task_class->cancelled = flickr_connection_cancelled;
+}
+
+
+static void
+flickr_connection_init (FlickrConnection *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FLICKR_TYPE_CONNECTION, FlickrConnectionPrivate);
+	self->priv->session = NULL;
+	self->priv->username = NULL;
+	self->priv->user_id = NULL;
+	self->priv->token = NULL;
+	self->priv->frob = NULL;
+	self->priv->cancellable = NULL;
+	self->priv->result = NULL;
+	self->priv->checksum = g_checksum_new (G_CHECKSUM_MD5);
+}
+
+
+GType
+flickr_connection_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo type_info = {
+			sizeof (FlickrConnectionClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) flickr_connection_class_init,
+			NULL,
+			NULL,
+			sizeof (FlickrConnection),
+			0,
+			(GInstanceInitFunc) flickr_connection_init
+		};
+
+		type = g_type_register_static (GTH_TYPE_TASK,
+					       "FlickrConnection",
+					       &type_info,
+					       0);
+	}
+
+	return type;
+}
+
+
+FlickrConnection *
+flickr_connection_new (void)
+{
+	return (FlickrConnection *) g_object_new (FLICKR_TYPE_CONNECTION, NULL);
+}
+
+
+void
+flickr_connection_send_message (FlickrConnection    *self,
+				SoupMessage         *msg,
+				GCancellable        *cancellable,
+				GAsyncReadyCallback  callback,
+				gpointer             user_data,
+				gpointer             source_tag,
+				SoupSessionCallback  soup_session_cb,
+				gpointer             soup_session_cb_data)
+{
+	if (self->priv->session == NULL) {
+		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);
+
+#ifdef DEBUG_FLICKR_CONNECTION
+		{
+			SoupLogger *logger;
+
+			logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
+			soup_session_add_feature (self->priv->session, SOUP_SESSION_FEATURE (logger));
+
+			g_object_unref (logger);
+		}
+#endif
+	}
+
+	_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);
+
+	soup_session_queue_message (self->priv->session,
+				    msg,
+				    soup_session_cb,
+				    soup_session_cb_data);
+}
+
+
+GSimpleAsyncResult *
+flickr_connection_get_result (FlickrConnection *self)
+{
+	return self->priv->result;
+}
+
+
+void
+flickr_connection_add_api_sig (FlickrConnection *self,
+			       GHashTable       *data_set)
+{
+	GList *keys;
+	GList *scan;
+
+	g_hash_table_insert (data_set, "api_key", GTHUMB_FLICKR_API_KEY);
+	if (self->priv->token != NULL)
+		g_hash_table_insert (data_set, "auth_token", self->priv->token);
+
+	g_checksum_reset (self->priv->checksum);
+	g_checksum_update (self->priv->checksum, (guchar *) GTHUMB_FLICKR_SHARED_SECRET, -1);
+
+	keys = g_hash_table_get_keys (data_set);
+	keys = g_list_sort (keys, (GCompareFunc) strcmp);
+	for (scan = keys; scan; scan = scan->next) {
+		char *key = scan->data;
+
+		g_checksum_update (self->priv->checksum, (guchar *) key, -1);
+		g_checksum_update (self->priv->checksum, g_hash_table_lookup (data_set, key), -1);
+	}
+	g_hash_table_insert (data_set, "api_sig", (gpointer) g_checksum_get_string (self->priv->checksum));
+
+	g_list_free (keys);
+}
+
+
+static void
+connection_frob_ready_cb (SoupSession *session,
+			  SoupMessage *msg,
+			  gpointer     user_data)
+{
+	FlickrConnection *self = user_data;
+	SoupBuffer       *body;
+	DomDocument      *doc = NULL;
+	GError           *error = NULL;
+
+	g_free (self->priv->frob);
+	self->priv->frob = NULL;
+
+	body = soup_message_body_flatten (msg->response_body);
+	if (flickr_utils_parse_response (body, &doc, &error)) {
+		DomElement *root;
+		DomElement *child;
+
+		root = DOM_ELEMENT (doc)->first_child;
+		for (child = root->first_child; child; child = child->next_sibling)
+			if (g_strcmp0 (child->tag_name, "frob") == 0)
+				self->priv->frob = g_strdup (dom_element_get_inner_text (child));
+
+		if (self->priv->frob == NULL) {
+			error = g_error_new_literal (FLICKR_CONNECTION_ERROR, 0, _("Unknown error"));
+			g_simple_async_result_set_from_error (self->priv->result, error);
+		}
+		else
+			g_simple_async_result_set_op_res_gboolean (self->priv->result, TRUE);
+
+		g_object_unref (doc);
+	}
+	else
+		g_simple_async_result_set_from_error (self->priv->result, error);
+
+	g_simple_async_result_complete_in_idle (self->priv->result);
+
+	soup_buffer_free (body);
+}
+
+
+void
+flickr_connection_get_frob (FlickrConnection    *self,
+			    GCancellable        *cancellable,
+			    GAsyncReadyCallback  callback,
+			    gpointer             user_data)
+{
+	GHashTable  *data_set;
+	SoupMessage *msg;
+
+	gth_task_progress (GTH_TASK (self), _("Connecting to the server"), NULL, TRUE, 0.0);
+
+	data_set = g_hash_table_new (g_str_hash, g_str_equal);
+	g_hash_table_insert (data_set, "method", "flickr.auth.getFrob");
+	flickr_connection_add_api_sig (self, data_set);
+	msg = soup_form_request_new_from_hash ("GET", "http://api.flickr.com/services/rest";, data_set);
+	flickr_connection_send_message (self,
+					msg,
+					cancellable,
+					callback,
+					user_data,
+					flickr_connection_get_frob,
+					connection_frob_ready_cb,
+					self);
+
+	g_hash_table_destroy (data_set);
+}
+
+
+gboolean
+flickr_connection_get_frob_finish (FlickrConnection  *self,
+				   GAsyncResult      *result,
+				   GError           **error)
+{
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+	else
+		return TRUE;
+}
+
+
+static char *
+get_access_type_name (FlickrAccessType access_type)
+{
+	char *name = NULL;
+
+	switch (access_type) {
+	case FLICKR_ACCESS_READ:
+		name = "read";
+		break;
+
+	case FLICKR_ACCESS_WRITE:
+		name = "write";
+		break;
+
+	case FLICKR_ACCESS_DELETE:
+		name = "delete";
+		break;
+	}
+
+	return name;
+}
+
+char *
+flickr_connection_get_login_link (FlickrConnection *self,
+				  FlickrAccessType  access_type)
+{
+	GHashTable *data_set;
+	GString    *link;
+	GList      *keys;
+	GList      *scan;
+
+	g_return_val_if_fail (self->priv->frob != NULL, NULL);
+
+	data_set = g_hash_table_new (g_str_hash, g_str_equal);
+	g_hash_table_insert (data_set, "frob", self->priv->frob);
+	g_hash_table_insert (data_set, "perms", get_access_type_name (access_type));
+	flickr_connection_add_api_sig (self, data_set);
+
+	link = g_string_new ("http://www.flickr.com/services/auth/?";);
+	keys = g_hash_table_get_keys (data_set);
+	for (scan = keys; scan; scan = scan->next) {
+		char *key = scan->data;
+
+		if (scan != keys)
+			g_string_append (link, "&");
+		g_string_append (link, key);
+		g_string_append (link, "=");
+		g_string_append (link, g_hash_table_lookup (data_set, key));
+	}
+
+	g_list_free (keys);
+	g_hash_table_destroy (data_set);
+
+	return g_string_free (link, FALSE);
+}
+
+
+static void
+connection_token_ready_cb (SoupSession *session,
+			   SoupMessage *msg,
+			   gpointer     user_data)
+{
+	FlickrConnection *self = user_data;
+	SoupBuffer       *body;
+	DomDocument      *doc = NULL;
+	GError           *error = NULL;
+
+	body = soup_message_body_flatten (msg->response_body);
+	if (flickr_utils_parse_response (body, &doc, &error)) {
+		DomElement *response;
+		DomElement *auth;
+
+		response = DOM_ELEMENT (doc)->first_child;
+		for (auth = response->first_child; auth; auth = auth->next_sibling) {
+			if (g_strcmp0 (auth->tag_name, "auth") == 0) {
+				DomElement *node;
+
+				for (node = auth->first_child; node; node = node->next_sibling) {
+					if (g_strcmp0 (node->tag_name, "token") == 0) {
+						self->priv->token = g_strdup (dom_element_get_inner_text (node));
+					}
+					else if (g_strcmp0 (node->tag_name, "user") == 0) {
+						self->priv->username = g_strdup (dom_element_get_attribute (node, "username"));
+						self->priv->user_id = g_strdup (dom_element_get_attribute (node, "nsid"));
+					}
+				}
+			}
+		}
+
+		if (self->priv->token == NULL) {
+			error = g_error_new_literal (FLICKR_CONNECTION_ERROR, 0, _("Unknown error"));
+			g_simple_async_result_set_from_error (self->priv->result, error);
+		}
+		else
+			g_simple_async_result_set_op_res_gboolean (self->priv->result, TRUE);
+
+		g_object_unref (doc);
+	}
+	else
+		g_simple_async_result_set_from_error (self->priv->result, error);
+
+	g_simple_async_result_complete_in_idle (self->priv->result);
+
+	soup_buffer_free (body);
+}
+
+
+void
+flickr_connection_get_token (FlickrConnection    *self,
+			     GCancellable        *cancellable,
+			     GAsyncReadyCallback  callback,
+			     gpointer             user_data)
+{
+	GHashTable  *data_set;
+	SoupMessage *msg;
+
+	gth_task_progress (GTH_TASK (self), _("Connecting to the server"), NULL, TRUE, 0.0);
+
+	g_free (self->priv->token);
+	self->priv->token = NULL;
+
+	data_set = g_hash_table_new (g_str_hash, g_str_equal);
+	g_hash_table_insert (data_set, "method", "flickr.auth.getToken");
+	g_hash_table_insert (data_set, "frob", self->priv->frob);
+	flickr_connection_add_api_sig (self, data_set);
+	msg = soup_form_request_new_from_hash ("GET", "http://api.flickr.com/services/rest";, data_set);
+	flickr_connection_send_message (self,
+					msg,
+					cancellable,
+					callback,
+					user_data,
+					flickr_connection_get_token,
+					connection_token_ready_cb,
+					self);
+
+	g_hash_table_destroy (data_set);
+}
+
+
+gboolean
+flickr_connection_get_token_finish (FlickrConnection  *self,
+				    GAsyncResult      *result,
+				    GError           **error)
+{
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+	else
+		return TRUE;
+}
+
+
+void
+flickr_connection_set_auth_token (FlickrConnection *self,
+				  const char       *value)
+{
+	g_free (self->priv->token);
+	self->priv->token = NULL;
+	if (value != NULL)
+		self->priv->token = g_strdup (value);
+}
+
+
+const char *
+flickr_connection_get_auth_token (FlickrConnection *self)
+{
+	return self->priv->token;
+}
+
+
+const char *
+flickr_connection_get_username (FlickrConnection *self)
+{
+	return self->priv->username;
+}
+
+
+const char *
+flickr_connection_get_user_id (FlickrConnection *self)
+{
+	return self->priv->user_id;
+}
+
+
+/* utilities */
+
+
+gboolean
+flickr_utils_parse_response (SoupBuffer   *body,
+			     DomDocument **doc_p,
+			     GError      **error)
+{
+	DomDocument *doc;
+	DomElement  *node;
+
+	doc = dom_document_new ();
+	if (! dom_document_load (doc, body->data, body->length, error)) {
+		g_object_unref (doc);
+		return FALSE;
+	}
+
+	for (node = DOM_ELEMENT (doc)->first_child; node; node = node->next_sibling) {
+		if (g_strcmp0 (node->tag_name, "rsp") == 0) {
+			if (g_strcmp0 (dom_element_get_attribute (node, "stat"), "ok") != 0) {
+				DomElement *child;
+
+				for (child = node->first_child; child; child = child->next_sibling) {
+					if (g_strcmp0 (child->tag_name, "err") == 0) {
+						*error = g_error_new_literal (FLICKR_CONNECTION_ERROR,
+									      atoi (dom_element_get_attribute (child, "code")),
+									      dom_element_get_attribute (child, "msg"));
+					}
+				}
+
+				g_object_unref (doc);
+				return FALSE;
+			}
+		}
+	}
+
+	*doc_p = doc;
+
+	return TRUE;
+}
diff --git a/extensions/flicker/flickr-connection.h b/extensions/flicker/flickr-connection.h
new file mode 100644
index 0000000..6c9d232
--- /dev/null
+++ b/extensions/flicker/flickr-connection.h
@@ -0,0 +1,106 @@
+/* -*- 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 FLICKR_CONNECTION_H
+#define FLICKR_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>
+
+typedef enum {
+	FLICKR_ACCESS_READ,
+	FLICKR_ACCESS_WRITE,
+	FLICKR_ACCESS_DELETE
+} FlickrAccessType;
+
+#define FLICKR_CONNECTION_ERROR flickr_connection_error_quark ()
+GQuark flickr_connection_error_quark (void);
+
+#define FLICKR_TYPE_CONNECTION         (flickr_connection_get_type ())
+#define FLICKR_CONNECTION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), FLICKR_TYPE_CONNECTION, FlickrConnection))
+#define FLICKR_CONNECTION_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), FLICKR_TYPE_CONNECTION, FlickrConnectionClass))
+#define FLICKR_IS_CONNECTION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), FLICKR_TYPE_CONNECTION))
+#define FLICKR_IS_CONNECTION_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), FLICKR_TYPE_CONNECTION))
+#define FLICKR_CONNECTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), FLICKR_TYPE_CONNECTION, FlickrConnectionClass))
+
+typedef struct _FlickrConnection         FlickrConnection;
+typedef struct _FlickrConnectionPrivate  FlickrConnectionPrivate;
+typedef struct _FlickrConnectionClass    FlickrConnectionClass;
+
+struct _FlickrConnection
+{
+	GthTask __parent;
+	FlickrConnectionPrivate *priv;
+};
+
+struct _FlickrConnectionClass
+{
+	GthTaskClass __parent_class;
+};
+
+GType                flickr_connection_get_type           (void) G_GNUC_CONST;
+FlickrConnection *   flickr_connection_new                (void);
+void		     flickr_connection_send_message       (FlickrConnection      *self,
+						           SoupMessage           *msg,
+						           GCancellable          *cancellable,
+						           GAsyncReadyCallback    callback,
+						           gpointer               user_data,
+						           gpointer               source_tag,
+						           SoupSessionCallback    soup_session_cb,
+						           gpointer               soup_session_cb_data);
+GSimpleAsyncResult * flickr_connection_get_result         (FlickrConnection      *self);
+void                 flickr_connection_add_api_sig        (FlickrConnection      *self,
+						           GHashTable            *data_set);
+void                 flickr_connection_get_frob           (FlickrConnection      *self,
+						           GCancellable          *cancellable,
+						           GAsyncReadyCallback    callback,
+						           gpointer               user_data);
+gboolean             flickr_connection_get_frob_finish    (FlickrConnection      *self,
+							   GAsyncResult          *result,
+							   GError               **error);
+char *               flickr_connection_get_login_link     (FlickrConnection      *self,
+							   FlickrAccessType       access_type);
+void                 flickr_connection_get_token          (FlickrConnection      *self,
+						           GCancellable          *cancellable,
+						           GAsyncReadyCallback    callback,
+						           gpointer               user_data);
+gboolean             flickr_connection_get_token_finish   (FlickrConnection      *self,
+							   GAsyncResult          *result,
+							   GError               **error);
+void                 flickr_connection_set_auth_token     (FlickrConnection      *self,
+							   const char            *value);
+const char *         flickr_connection_get_auth_token     (FlickrConnection      *self);
+const char *         flickr_connection_get_username       (FlickrConnection      *self);
+const char *         flickr_connection_get_user_id        (FlickrConnection      *self);
+
+/* utilities */
+
+gboolean             flickr_utils_parse_response          (SoupBuffer            *body,
+							   DomDocument          **doc_p,
+							   GError               **error);
+
+#endif /* FLICKR_CONNECTION_H */
diff --git a/extensions/flicker/flickr-photoset.c b/extensions/flicker/flickr-photoset.c
new file mode 100644
index 0000000..7975e78
--- /dev/null
+++ b/extensions/flicker/flickr-photoset.c
@@ -0,0 +1,266 @@
+/* -*- 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 "flickr-photoset.h"
+
+
+static gpointer flickr_photoset_parent_class = NULL;
+
+
+static void
+flickr_photoset_finalize (GObject *obj)
+{
+	FlickrPhotoset *self;
+
+	self = FLICKR_PHOTOSET (obj);
+
+	g_free (self->id);
+	g_free (self->title);
+	g_free (self->description);
+	g_free (self->primary);
+	g_free (self->secret);
+	g_free (self->server);
+	g_free (self->farm);
+
+	G_OBJECT_CLASS (flickr_photoset_parent_class)->finalize (obj);
+}
+
+
+static void
+flickr_photoset_class_init (FlickrPhotosetClass *klass)
+{
+	flickr_photoset_parent_class = g_type_class_peek_parent (klass);
+	G_OBJECT_CLASS (klass)->finalize = flickr_photoset_finalize;
+}
+
+
+static DomElement*
+flickr_photoset_create_element (DomDomizable *base,
+				DomDocument  *doc)
+{
+	FlickrPhotoset *self;
+	DomElement     *element;
+	char           *value;
+
+	self = FLICKR_PHOTOSET (base);
+
+	element = dom_document_create_element (doc, "photoset", NULL);
+	if (self->id != NULL)
+		dom_element_set_attribute (element, "id", self->id);
+	if (self->primary != NULL)
+		dom_element_set_attribute (element, "primary", self->primary);
+	if (self->secret != NULL)
+		dom_element_set_attribute (element, "secret", self->secret);
+	if (self->server != NULL)
+		dom_element_set_attribute (element, "server", self->server);
+	if (self->n_photos >= 0) {
+		value = g_strdup_printf ("%d", self->n_photos);
+		dom_element_set_attribute (element, "photos", value);
+		g_free (value);
+	}
+	if (self->farm != NULL)
+		dom_element_set_attribute (element, "farm", self->farm);
+
+	if (self->title != NULL)
+		dom_element_append_child (element, dom_document_create_element_with_text (doc, self->title, "title", NULL));
+	if (self->description != NULL)
+		dom_element_append_child (element, dom_document_create_element_with_text (doc, self->description, "description", NULL));
+
+	return element;
+}
+
+
+static void
+flickr_photoset_load_from_element (DomDomizable *base,
+				   DomElement   *element)
+{
+	FlickrPhotoset *self;
+	DomElement     *node;
+
+	self = FLICKR_PHOTOSET (base);
+
+	flickr_photoset_set_id (self, dom_element_get_attribute (element, "id"));
+	flickr_photoset_set_title (self, NULL);
+	flickr_photoset_set_description (self, NULL);
+	flickr_photoset_set_n_photos (self, dom_element_get_attribute (element, "photos"));
+	flickr_photoset_set_primary (self, dom_element_get_attribute (element, "primary"));
+	flickr_photoset_set_secret (self, dom_element_get_attribute (element, "secret"));
+	flickr_photoset_set_server (self, dom_element_get_attribute (element, "server"));
+	flickr_photoset_set_farm (self, dom_element_get_attribute (element, "farm"));
+
+	for (node = element->first_child; node; node = node->next_sibling) {
+		if (g_strcmp0 (node->tag_name, "title") == 0) {
+			flickr_photoset_set_title (self, dom_element_get_inner_text (node));
+		}
+		else if (g_strcmp0 (node->tag_name, "description") == 0) {
+			flickr_photoset_set_description (self, dom_element_get_inner_text (node));
+		}
+	}
+}
+
+
+static void
+flickr_photoset_dom_domizable_interface_init (DomDomizableIface *iface)
+{
+	iface->create_element = flickr_photoset_create_element;
+	iface->load_from_element = flickr_photoset_load_from_element;
+}
+
+
+static void
+flickr_photoset_instance_init (FlickrPhotoset *self)
+{
+}
+
+
+GType
+flickr_photoset_get_type (void)
+{
+	static GType flickr_photoset_type_id = 0;
+
+	if (flickr_photoset_type_id == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (FlickrPhotosetClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) flickr_photoset_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (FlickrPhotoset),
+			0,
+			(GInstanceInitFunc) flickr_photoset_instance_init,
+			NULL
+		};
+		static const GInterfaceInfo dom_domizable_info = {
+			(GInterfaceInitFunc) flickr_photoset_dom_domizable_interface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL
+		};
+
+		flickr_photoset_type_id = g_type_register_static (G_TYPE_OBJECT,
+								   "FlickrPhotoset",
+								   &g_define_type_info,
+								   0);
+		g_type_add_interface_static (flickr_photoset_type_id, DOM_TYPE_DOMIZABLE, &dom_domizable_info);
+	}
+
+	return flickr_photoset_type_id;
+}
+
+
+FlickrPhotoset *
+flickr_photoset_new (void)
+{
+	return g_object_new (FLICKR_TYPE_PHOTOSET, NULL);
+}
+
+
+void
+flickr_photoset_set_id (FlickrPhotoset *self,
+			const char     *value)
+{
+	g_free (self->id);
+	self->id = NULL;
+	if (value != NULL)
+		self->id = g_strdup (value);
+}
+
+
+void
+flickr_photoset_set_title (FlickrPhotoset *self,
+			   const char     *value)
+{
+	g_free (self->title);
+	self->title = NULL;
+	if (value != NULL)
+		self->title = g_strdup (value);
+}
+
+
+void
+flickr_photoset_set_description (FlickrPhotoset *self,
+			         const char     *value)
+{
+	g_free (self->description);
+	self->description = NULL;
+	if (value != NULL)
+		self->description = g_strdup (value);
+}
+
+
+void
+flickr_photoset_set_n_photos (FlickrPhotoset *self,
+			      const char     *value)
+{
+	if (value != NULL)
+		self->n_photos = atoi (value);
+	else
+		self->n_photos = 0;
+}
+
+
+void
+flickr_photoset_set_primary (FlickrPhotoset *self,
+			     const char     *value)
+{
+	g_free (self->primary);
+	self->primary = NULL;
+	if (value != NULL)
+		self->primary = g_strdup (value);
+}
+
+
+void
+flickr_photoset_set_secret (FlickrPhotoset *self,
+			    const char     *value)
+{
+	g_free (self->secret);
+	self->secret = NULL;
+	if (value != NULL)
+		self->secret = g_strdup (value);
+}
+
+
+void
+flickr_photoset_set_server (FlickrPhotoset *self,
+			    const char     *value)
+{
+	g_free (self->server);
+	self->server = NULL;
+	if (value != NULL)
+		self->server = g_strdup (value);
+}
+
+
+void
+flickr_photoset_set_farm (FlickrPhotoset *self,
+			  const char     *value)
+{
+	g_free (self->farm);
+	self->farm = NULL;
+	if (value != NULL)
+		self->farm = g_strdup (value);
+}
diff --git a/extensions/flicker/flickr-photoset.h b/extensions/flicker/flickr-photoset.h
new file mode 100644
index 0000000..bc0c3bb
--- /dev/null
+++ b/extensions/flicker/flickr-photoset.h
@@ -0,0 +1,81 @@
+/* -*- 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 FLICKR_PHOTOSET_H
+#define FLICKR_PHOTOSET_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FLICKR_TYPE_PHOTOSET            (flickr_photoset_get_type ())
+#define FLICKR_PHOTOSET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLICKR_TYPE_PHOTOSET, FlickrPhotoset))
+#define FLICKR_PHOTOSET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FLICKR_TYPE_PHOTOSET, FlickrPhotosetClass))
+#define FLICKR_IS_PHOTOSET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLICKR_TYPE_PHOTOSET))
+#define FLICKR_IS_PHOTOSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FLICKR_TYPE_PHOTOSET))
+#define FLICKR_PHOTOSET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FLICKR_TYPE_PHOTOSET, FlickrPhotosetClass))
+
+typedef struct _FlickrPhotoset FlickrPhotoset;
+typedef struct _FlickrPhotosetClass FlickrPhotosetClass;
+typedef struct _FlickrPhotosetPrivate FlickrPhotosetPrivate;
+
+struct _FlickrPhotoset {
+	GObject parent_instance;
+	FlickrPhotosetPrivate *priv;
+
+	char *id;
+	char *title;
+	char *description;
+	int   n_photos;
+	char *primary;
+	char *secret;
+	char *server;
+	char *farm;
+};
+
+struct _FlickrPhotosetClass {
+	GObjectClass parent_class;
+};
+
+GType             flickr_photoset_get_type          (void);
+FlickrPhotoset *  flickr_photoset_new               (void);
+void              flickr_photoset_set_id            (FlickrPhotoset *self,
+						     const char     *value);
+void              flickr_photoset_set_title         (FlickrPhotoset *self,
+						     const char     *value);
+void              flickr_photoset_set_description   (FlickrPhotoset *self,
+						     const char     *value);
+void              flickr_photoset_set_n_photos      (FlickrPhotoset *self,
+						     const char     *value);
+void              flickr_photoset_set_primary       (FlickrPhotoset *self,
+						     const char     *value);
+void              flickr_photoset_set_secret        (FlickrPhotoset *self,
+						     const char     *value);
+void              flickr_photoset_set_server        (FlickrPhotoset *self,
+						     const char     *value);
+void              flickr_photoset_set_farm          (FlickrPhotoset *self,
+						     const char     *value);
+
+G_END_DECLS
+
+#endif /* FLICKR_PHOTOSET_H */
diff --git a/extensions/flicker/flickr-service.c b/extensions/flicker/flickr-service.c
new file mode 100644
index 0000000..54fb92c
--- /dev/null
+++ b/extensions/flicker/flickr-service.c
@@ -0,0 +1,939 @@
+/* -*- 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 <glib.h>
+#include <glib/gi18n.h>
+#include <gthumb.h>
+#include "flickr-account.h"
+#include "flickr-connection.h"
+#include "flickr-photoset.h"
+#include "flickr-service.h"
+#include "flickr-user.h"
+
+
+typedef struct {
+	FlickrPhotoset      *album;
+	GList               *file_list;
+	GCancellable        *cancellable;
+        GAsyncReadyCallback  callback;
+        gpointer             user_data;
+	GList               *current;
+	goffset              total_size;
+	goffset              uploaded_size;
+	int                  n_files;
+	int                  uploaded_files;
+} PostPhotosData;
+
+
+static void
+post_photos_data_free (PostPhotosData *post_photos)
+{
+	if (post_photos == NULL)
+		return;
+	_g_object_unref (post_photos->cancellable);
+	_g_object_list_unref (post_photos->file_list);
+	g_object_unref (post_photos->album);
+	g_free (post_photos);
+}
+
+
+struct _FlickrServicePrivate
+{
+	FlickrConnection *conn;
+	FlickrUser       *user;
+	PostPhotosData   *post_photos;
+};
+
+
+static gpointer parent_class = NULL;
+
+
+static void
+flickr_service_finalize (GObject *object)
+{
+	FlickrService *self;
+
+	self = FLICKR_SERVICE (object);
+
+	_g_object_unref (self->priv->conn);
+	_g_object_unref (self->priv->user);
+	post_photos_data_free (self->priv->post_photos);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+flickr_service_class_init (FlickrServiceClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (FlickrServicePrivate));
+
+	object_class = (GObjectClass*) klass;
+	object_class->finalize = flickr_service_finalize;
+}
+
+
+static void
+flickr_service_init (FlickrService *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FLICKR_TYPE_SERVICE, FlickrServicePrivate);
+	self->priv->conn = NULL;
+	self->priv->user = NULL;
+	self->priv->post_photos = NULL;
+}
+
+
+GType
+flickr_service_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo type_info = {
+			sizeof (FlickrServiceClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) flickr_service_class_init,
+			NULL,
+			NULL,
+			sizeof (FlickrService),
+			0,
+			(GInstanceInitFunc) flickr_service_init
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT,
+					       "FlickrService",
+					       &type_info,
+					       0);
+	}
+
+	return type;
+}
+
+
+FlickrService *
+flickr_service_new (FlickrConnection *conn)
+{
+	FlickrService *self;
+
+	self = (FlickrService *) g_object_new (FLICKR_TYPE_SERVICE, NULL);
+	self->priv->conn = g_object_ref (conn);
+
+	return self;
+}
+
+
+/* -- flickr_service_get_upload_status -- */
+
+
+static void
+get_upload_status_ready_cb (SoupSession *session,
+			    SoupMessage *msg,
+			    gpointer     user_data)
+{
+	FlickrService      *self = user_data;
+	GSimpleAsyncResult *result;
+	SoupBuffer         *body;
+	DomDocument        *doc = NULL;
+	GError             *error = NULL;
+
+	result = flickr_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);
+	if (flickr_utils_parse_response (body, &doc, &error)) {
+		DomElement *response;
+		DomElement *node;
+		FlickrUser *user = NULL;
+
+		response = DOM_ELEMENT (doc)->first_child;
+		for (node = response->first_child; node; node = node->next_sibling) {
+			if (g_strcmp0 (node->tag_name, "user") == 0) {
+				user = flickr_user_new ();
+				dom_domizable_load_from_element (DOM_DOMIZABLE (user), node);
+				g_simple_async_result_set_op_res_gpointer (result, user, (GDestroyNotify) g_object_unref);
+			}
+		}
+
+		if (user == NULL) {
+			error = g_error_new_literal (FLICKR_CONNECTION_ERROR, 0, _("Unknown error"));
+			g_simple_async_result_set_from_error (result, error);
+		}
+
+		g_object_unref (doc);
+	}
+	else
+		g_simple_async_result_set_from_error (result, error);
+
+	g_simple_async_result_complete_in_idle (result);
+
+	soup_buffer_free (body);
+}
+
+
+void
+flickr_service_get_upload_status (FlickrService       *self,
+				  GCancellable        *cancellable,
+				  GAsyncReadyCallback  callback,
+				  gpointer             user_data)
+{
+	GHashTable  *data_set;
+	SoupMessage *msg;
+
+	gth_task_progress (GTH_TASK (self->priv->conn), _("Connecting to the server"), _("Getting account information"), TRUE, 0.0);
+
+	data_set = g_hash_table_new (g_str_hash, g_str_equal);
+	g_hash_table_insert (data_set, "method", "flickr.people.getUploadStatus");
+	flickr_connection_add_api_sig (self->priv->conn, data_set);
+	msg = soup_form_request_new_from_hash ("GET", "http://api.flickr.com/services/rest";, data_set);
+	flickr_connection_send_message (self->priv->conn,
+					msg,
+					cancellable,
+					callback,
+					user_data,
+					flickr_service_get_upload_status,
+					get_upload_status_ready_cb,
+					self);
+
+	g_hash_table_destroy (data_set);
+}
+
+
+FlickrUser *
+flickr_service_get_upload_status_finish (FlickrService  *self,
+					 GAsyncResult   *result,
+					 GError        **error)
+{
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return NULL;
+	else
+		return g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+}
+
+
+#if 0
+/* -- flickr_service_list_albums -- */
+
+
+static void
+list_albums_ready_cb (SoupSession *session,
+		      SoupMessage *msg,
+		      gpointer     user_data)
+{
+	FlickrService   *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;
+			FlickrPhotoset *album;
+
+			self->priv->user = flickr_user_new ();
+			dom_domizable_load_from_element (DOM_DOMIZABLE (self->priv->user), feed_node);
+
+			album = NULL;
+			for (node = feed_node->first_child;
+			     node != NULL;
+			     node = node->next_sibling)
+			{
+				if (g_strcmp0 (node->tag_name, "entry") == 0) { /* read the album data */
+					if (album != NULL)
+						albums = g_list_prepend (albums, album);
+					album = flickr_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
+flickr_service_list_albums (FlickrService       *self,
+			    const char          *user_id,
+			    GCancellable        *cancellable,
+			    GAsyncReadyCallback  callback,
+			    gpointer             user_data)
+{
+	char        *url;
+	SoupMessage *msg;
+
+	g_return_if_fail (user_id != NULL);
+
+	gth_task_progress (GTH_TASK (self->priv->conn), _("Getting the album list"), NULL, TRUE, 0.0);
+
+	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,
+					flickr_service_list_albums,
+					list_albums_ready_cb,
+					self);
+
+	g_free (url);
+}
+
+
+GList *
+flickr_service_list_albums_finish (FlickrService  *service,
+				   GAsyncResult   *result,
+				   GError        **error)
+{
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return NULL;
+	else
+		return _g_object_list_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+}
+
+
+/* -- flickr_service_create_album -- */
+
+
+static void
+create_album_ready_cb (SoupSession *session,
+		       SoupMessage *msg,
+		       gpointer     user_data)
+{
+	FlickrService      *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)) {
+		FlickrPhotoset *album;
+
+		album = flickr_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_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
+flickr_service_create_album (FlickrService        *self,
+			     FlickrPhotoset       *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 != NULL);
+
+	gth_task_progress (GTH_TASK (self->priv->conn), _("Creating the new album"), NULL, TRUE, 0.0);
+
+	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_request (msg, ATOM_ENTRY_MIME_TYPE, SOUP_MEMORY_TAKE, buffer, len);
+	google_connection_send_message (self->priv->conn,
+					msg,
+					cancellable,
+					callback,
+					user_data,
+					flickr_service_create_album,
+					create_album_ready_cb,
+					self);
+
+	g_free (url);
+	g_object_unref (doc);
+}
+
+
+FlickrPhotoset *
+flickr_service_create_album_finish (FlickrService  *service,
+				    GAsyncResult   *result,
+				    GError        **error)
+{
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return NULL;
+	else
+		return g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+}
+
+
+/* -- flickr_service_post_photos -- */
+
+
+static void
+post_photos_done (FlickrService *self,
+		  GError        *error)
+{
+	GSimpleAsyncResult *result;
+
+	result = google_connection_get_result (self->priv->conn);
+	if (error == NULL)
+		g_simple_async_result_set_op_res_gboolean (result, TRUE);
+	else
+		g_simple_async_result_set_from_error (result, error);
+	g_simple_async_result_complete_in_idle (result);
+}
+
+
+static void picasa_wev_service_post_current_file (FlickrService *self);
+
+
+static void
+post_photo_ready_cb (SoupSession *session,
+		     SoupMessage *msg,
+		     gpointer     user_data)
+{
+	FlickrService *self = user_data;
+
+	if (msg->status_code != 201) {
+		GError *error;
+
+		error = g_error_new (SOUP_HTTP_ERROR, msg->status_code, "%s", soup_status_get_phrase (msg->status_code));
+		post_photos_done (self, error);
+		g_error_free (error);
+
+		return;
+	}
+
+	self->priv->post_photos->current = self->priv->post_photos->current->next;
+	picasa_wev_service_post_current_file (self);
+}
+
+
+static void
+post_photo_file_buffer_ready_cb (void     **buffer,
+				 gsize      count,
+				 GError    *error,
+				 gpointer   user_data)
+{
+	FlickrService   *self = user_data;
+	GthFileData        *file_data;
+	SoupMultipart      *multipart;
+	const char         *filename;
+	char               *value;
+	GObject            *metadata;
+	DomDocument        *doc;
+	DomElement         *entry;
+	char               *entry_buffer;
+	gsize               entry_len;
+	SoupMessageHeaders *headers;
+	SoupBuffer         *body;
+	char               *details;
+	char               *url;
+	SoupMessage        *msg;
+
+	if (error != NULL) {
+		post_photos_done (self, error);
+		return;
+	}
+
+	file_data = self->priv->post_photos->current->data;
+	multipart = soup_multipart_new ("multipart/related");
+
+	/* the metadata part */
+
+	doc = dom_document_new ();
+	entry = dom_document_create_element (doc, "entry",
+					     "xmlns", "http://www.w3.org/2005/Atom";,
+					     "xmlns:gphoto", "http://schemas.google.com/photos/2007";,
+					     "xmlns:media", "http://search.yahoo.com/mrss/";,
+					     NULL);
+
+	filename = g_file_info_get_display_name (file_data->info);
+	dom_element_append_child (entry,
+				  dom_document_create_element_with_text (doc, filename, "title", NULL));
+
+	value = gth_file_data_get_attribute_as_string (file_data, "general::description");
+	if (value == NULL)
+		value = gth_file_data_get_attribute_as_string (file_data, "general::title");
+	dom_element_append_child (entry,
+				  dom_document_create_element_with_text (doc, value, "summary", NULL));
+
+	value = gth_file_data_get_attribute_as_string (file_data, "general::location");
+	if (value != NULL)
+		dom_element_append_child (entry,
+					  dom_document_create_element_with_text (doc, value, "gphoto:location", NULL));
+
+	metadata = g_file_info_get_attribute_object (file_data->info, "general::tags");
+	if ((metadata != NULL) && GTH_IS_STRING_LIST (metadata))
+		value = gth_string_list_join (GTH_STRING_LIST (metadata), ", ");
+	if (value != NULL) {
+		DomElement *group;
+
+		group = dom_document_create_element (doc, "media:group", NULL);
+		dom_element_append_child (group,
+					  dom_document_create_element_with_text (doc, value, "media:keywords", NULL));
+		dom_element_append_child (entry, group);
+
+		g_free (value);
+	}
+
+	dom_element_append_child (entry,
+				  dom_document_create_element (doc, "category",
+							       "scheme", "http://schemas.google.com/g/2005#kind";,
+							       "term", "http://schemas.google.com/photos/2007#photo";,
+							       NULL));
+	dom_element_append_child (DOM_ELEMENT (doc), entry);
+	entry_buffer = dom_document_dump (doc, &entry_len);
+
+	headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
+	soup_message_headers_append (headers, "Content-Type", "application/atom+xml");
+	body = soup_buffer_new (SOUP_MEMORY_TAKE, entry_buffer, entry_len);
+	soup_multipart_append_part (multipart, headers, body);
+
+	soup_buffer_free (body);
+	soup_message_headers_free (headers);
+	g_object_unref (doc);
+
+	/* the file part */
+
+	body = soup_buffer_new (SOUP_MEMORY_TEMPORARY, *buffer, count);
+	soup_multipart_append_form_file (multipart,
+					 "file",
+					 NULL,
+					 gth_file_data_get_mime_type (file_data),
+					 body);
+
+	soup_buffer_free (body);
+
+	/* send the file */
+
+	/* Translators: %s is a filename */
+	details = g_strdup_printf (_("Uploading '%s'"), filename);
+	gth_task_progress (GTH_TASK (self->priv->conn), NULL, details, TRUE, 0.0);
+	g_free (details);
+
+	url = g_strconcat ("http://picasaweb.google.com/data/feed/api/user/";,
+			   self->priv->user->id,
+			   "/albumid/",
+			   self->priv->post_photos->album->id,
+			   NULL);
+	msg = soup_form_request_new_from_multipart (url, multipart);
+	google_connection_send_message (self->priv->conn,
+					msg,
+					self->priv->post_photos->cancellable,
+					self->priv->post_photos->callback,
+					self->priv->post_photos->user_data,
+					flickr_service_post_photos,
+					post_photo_ready_cb,
+					self);
+
+	g_free (url);
+	soup_multipart_free (multipart);
+}
+
+
+static void
+picasa_wev_service_post_current_file (FlickrService *self)
+{
+	GthFileData *file_data;
+
+	if (self->priv->post_photos->current == NULL) {
+		post_photos_done (self, NULL);
+		return;
+	}
+
+	file_data = self->priv->post_photos->current->data;
+	g_load_file_async (file_data->file,
+			   G_PRIORITY_DEFAULT,
+			   self->priv->post_photos->cancellable,
+			   post_photo_file_buffer_ready_cb,
+			   self);
+}
+
+
+static void
+post_photos_info_ready_cb (GList    *files,
+		           GError   *error,
+		           gpointer  user_data)
+{
+	FlickrService *self = user_data;
+
+	if (error != NULL) {
+		post_photos_done (self, error);
+		return;
+	}
+
+	self->priv->post_photos->file_list = _g_object_list_ref (files);
+	self->priv->post_photos->current = self->priv->post_photos->file_list;
+	picasa_wev_service_post_current_file (self);
+}
+
+
+void
+flickr_service_post_photos (FlickrService       *self,
+			    FlickrPhotoset      *album,
+			    GList               *file_list, /* GFile list */
+			    GCancellable        *cancellable,
+			    GAsyncReadyCallback  callback,
+			    gpointer             user_data)
+{
+	GList *scan;
+
+	g_return_if_fail (album != NULL);
+	g_return_if_fail (self->priv->post_photos == NULL);
+
+	gth_task_progress (GTH_TASK (self->priv->conn), _("Uploading the files to the server"), NULL, TRUE, 0.0);
+
+	self->priv->post_photos = g_new0 (PostPhotosData, 1);
+	self->priv->post_photos->album = g_object_ref (album);
+	self->priv->post_photos->cancellable = _g_object_ref (cancellable);
+	self->priv->post_photos->callback = callback;
+	self->priv->post_photos->user_data = user_data;
+	self->priv->post_photos->total_size = 0;
+	self->priv->post_photos->n_files = 0;
+	for (scan = self->priv->post_photos->file_list; scan; scan = scan->next) {
+		GthFileData *file_data = scan->data;
+
+		self->priv->post_photos->total_size += g_file_info_get_size (file_data->info);
+		self->priv->post_photos->n_files += 1;
+	}
+
+	_g_query_all_metadata_async (file_list,
+				     FALSE,
+				     TRUE,
+				     "*",
+				     self->priv->post_photos->cancellable,
+				     post_photos_info_ready_cb,
+				     self);
+}
+
+
+gboolean
+flickr_service_post_photos_finish (FlickrService  *self,
+				   GAsyncResult   *result,
+				   GError        **error)
+{
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+	else
+		return TRUE;
+}
+
+
+/* -- flickr_service_list_photos -- */
+
+
+static void
+list_photos_ready_cb (SoupSession *session,
+		      SoupMessage *msg,
+		      gpointer     user_data)
+{
+	FlickrService   *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      *photos = 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;
+			PicasaWebPhoto *photo;
+
+			self->priv->user = flickr_user_new ();
+			dom_domizable_load_from_element (DOM_DOMIZABLE (self->priv->user), feed_node);
+
+			photo = NULL;
+			for (node = feed_node->first_child;
+			     node != NULL;
+			     node = node->next_sibling)
+			{
+				if (g_strcmp0 (node->tag_name, "entry") == 0) { /* read the photo data */
+					if (photo != NULL)
+						photos = g_list_prepend (photos, photo);
+					photo = flickr_photo_new ();
+					dom_domizable_load_from_element (DOM_DOMIZABLE (photo), node);
+				}
+			}
+			if (photo != NULL)
+				photos = g_list_prepend (photos, photo);
+		}
+		photos = g_list_reverse (photos);
+		g_simple_async_result_set_op_res_gpointer (result, photos, (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
+flickr_service_list_photos (FlickrService       *self,
+			    FlickrPhotoset      *album,
+			    GCancellable        *cancellable,
+			    GAsyncReadyCallback  callback,
+			    gpointer             user_data)
+{
+	char        *url;
+	SoupMessage *msg;
+
+	g_return_if_fail (album != NULL);
+
+	gth_task_progress (GTH_TASK (self->priv->conn), _("Getting the photo list"), NULL, TRUE, 0.0);
+
+	url = g_strconcat ("http://picasaweb.google.com/data/feed/api/user/";,
+			   self->priv->user->id,
+			   "/albumid/",
+			   album->id,
+			   NULL);
+	msg = soup_message_new ("GET", url);
+	google_connection_send_message (self->priv->conn,
+					msg,
+					cancellable,
+					callback,
+					user_data,
+					flickr_service_list_photos,
+					list_photos_ready_cb,
+					self);
+
+	g_free (url);
+}
+
+
+GList *
+flickr_service_list_photos_finish (FlickrService  *self,
+				   GAsyncResult   *result,
+				   GError        **error)
+{
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return NULL;
+	else
+		return _g_object_list_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+}
+
+#endif
+
+
+/* utilities */
+
+
+GList *
+flickr_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", "flickr.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) {
+					FlickrAccount *account;
+
+					account = flickr_account_new ();
+					dom_domizable_load_from_element (DOM_DOMIZABLE (account), child);
+
+					accounts = g_list_prepend (accounts, account);
+				}
+			}
+
+			accounts = g_list_reverse (accounts);
+		}
+	}
+
+	g_object_unref (doc);
+	g_free (buffer);
+	g_free (filename);
+
+	return accounts;
+}
+
+
+FlickrAccount *
+flickr_accounts_find_default (GList *accounts)
+{
+	GList *scan;
+
+	for (scan = accounts; scan; scan = scan->next) {
+		FlickrAccount *account = scan->data;
+
+		if (account->is_default)
+			return g_object_ref (account);
+	}
+
+	return NULL;
+}
+
+
+void
+flickr_accounts_save_to_file (GList         *accounts,
+			      FlickrAccount *default_account)
+{
+	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) {
+		FlickrAccount *account = scan->data;
+		DomElement    *node;
+
+		if ((default_account != NULL) && g_strcmp0 (account->username, default_account->username) == 0)
+			account->is_default = TRUE;
+		else
+			account->is_default = FALSE;
+		node = dom_domizable_create_element (DOM_DOMIZABLE (account), doc);
+		dom_element_append_child (root, node);
+	}
+
+	gth_user_dir_make_dir_for_file (GTH_DIR_CONFIG, GTHUMB_DIR, "accounts", "flickr.xml", NULL);
+	filename = gth_user_dir_get_file (GTH_DIR_CONFIG, GTHUMB_DIR, "accounts", "flickr.xml", NULL);
+	file = g_file_new_for_path (filename);
+	buffer = dom_document_dump (doc, &len);
+	g_write_file (file, FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, buffer, len, NULL, NULL);
+
+	g_free (buffer);
+	g_object_unref (file);
+	g_free (filename);
+	g_object_unref (doc);
+}
diff --git a/extensions/flicker/flickr-service.h b/extensions/flicker/flickr-service.h
new file mode 100644
index 0000000..941bef7
--- /dev/null
+++ b/extensions/flicker/flickr-service.h
@@ -0,0 +1,105 @@
+/* -*- 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 FLICKR_SERVICE_H
+#define FLICKR_SERVICE_H
+
+#include <glib-object.h>
+#include "flickr-account.h"
+#include "flickr-connection.h"
+#include "flickr-photoset.h"
+#include "flickr-user.h"
+
+#define FLICKR_TYPE_SERVICE         (flickr_service_get_type ())
+#define FLICKR_SERVICE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), FLICKR_TYPE_SERVICE, FlickrService))
+#define FLICKR_SERVICE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), FLICKR_TYPE_SERVICE, FlickrServiceClass))
+#define FLICKR_IS_SERVICE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), FLICKR_TYPE_SERVICE))
+#define FLICKR_IS_SERVICE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), FLICKR_TYPE_SERVICE))
+#define FLICKR_SERVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), FLICKR_TYPE_SERVICE, FlickrServiceClass))
+
+typedef struct _FlickrService         FlickrService;
+typedef struct _FlickrServicePrivate  FlickrServicePrivate;
+typedef struct _FlickrServiceClass    FlickrServiceClass;
+
+struct _FlickrService
+{
+	GObject __parent;
+	FlickrServicePrivate *priv;
+};
+
+struct _FlickrServiceClass
+{
+	GObjectClass __parent_class;
+};
+
+GType             flickr_service_get_type                 (void) G_GNUC_CONST;
+FlickrService *   flickr_service_new                      (FlickrConnection     *conn);
+void              flickr_service_get_upload_status        (FlickrService        *self,
+							   GCancellable         *cancellable,
+							   GAsyncReadyCallback   callback,
+							   gpointer              user_data);
+FlickrUser *      flickr_service_get_upload_status_finish (FlickrService        *self,
+						           GAsyncResult         *result,
+						           GError              **error);
+#if 0
+void              flickr_service_list_photosets           (FlickrService        *self,
+						           GCancellable         *cancellable,
+						           GAsyncReadyCallback   callback,
+						           gpointer              user_data);
+GList *           flickr_service_list_photosets_finish    (FlickrService        *self,
+						           GAsyncResult         *result,
+						           GError              **error);
+void              flickr_service_create_photoset          (FlickrService        *self,
+						           FlickrPhotoset       *photoset,
+						           GCancellable         *cancellable,
+						           GAsyncReadyCallback   callback,
+						           gpointer              user_data);
+FlickrPhotoset *  flickr_service_create_photoset_finish   (FlickrService        *self,
+						           GAsyncResult         *result,
+						           GError              **error);
+void              flickr_service_post_photos              (FlickrService        *self,
+							   FlickrPhotoset       *photoset,
+						           GList                *file_list, /* GFile list */
+						           GCancellable         *cancellable,
+						           GAsyncReadyCallback   callback,
+						           gpointer              user_data);
+gboolean          flickr_service_post_photos_finish       (FlickrService        *self,
+						           GAsyncResult         *result,
+						           GError              **error);
+void              flickr_service_list_photos              (FlickrService        *self,
+							   FlickrPhotoset       *photoset,
+						           GCancellable         *cancellable,
+						           GAsyncReadyCallback   callback,
+						           gpointer              user_data);
+GList *           flickr_service_list_photos_finish       (FlickrService        *self,
+						           GAsyncResult         *result,
+						           GError              **error);
+#endif
+
+/* utilities */
+
+GList *          flickr_accounts_load_from_file  (void);
+FlickrAccount *  flickr_accounts_find_default    (GList         *accounts);
+void             flickr_accounts_save_to_file    (GList         *accounts,
+						  FlickrAccount *default_account);
+
+#endif /* FLICKR_SERVICE_H */
diff --git a/extensions/flicker/flickr-types.h b/extensions/flicker/flickr-types.h
new file mode 100644
index 0000000..9cd5662
--- /dev/null
+++ b/extensions/flicker/flickr-types.h
@@ -0,0 +1,43 @@
+/* -*- 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 FLICKR_TYPES_H
+#define FLICKR_TYPES_H
+
+typedef enum  {
+	FLICKR_SAFETY_LEVEL_SAFE = 1,
+	FLICKR_SAFETY_LEVEL_MODERATE = 2,
+	FLICKR_SAFETY_LEVEL_RESTRICTED = 3
+} FlickrSafetyLevel;
+
+typedef enum  {
+	FLICKR_CONTENT_TYPE_PHOTO = 1,
+	FLICKR_CONTENT_TYPE_SCREENSHOT = 2,
+	FLICKR_CONTENT_TYPE_OTHER = 3
+} FlickrContentType;
+
+typedef enum  {
+	FLICKR_HIDDEN_PUBLIC = 1,
+	FLICKR_HIDDEN_HIDDEN = 2,
+} FlickrHiddenType;
+
+#endif /* FLICKR_TYPES_H */
diff --git a/extensions/flicker/flickr-user.c b/extensions/flicker/flickr-user.c
new file mode 100644
index 0000000..41e4a1e
--- /dev/null
+++ b/extensions/flicker/flickr-user.c
@@ -0,0 +1,245 @@
+/* -*- 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 "flickr-user.h"
+
+
+static gpointer flickr_user_parent_class = NULL;
+
+
+static void
+flickr_user_finalize (GObject *obj)
+{
+	FlickrUser *self;
+
+	self = FLICKR_USER (obj);
+
+	g_free (self->id);
+	g_free (self->username);
+
+	G_OBJECT_CLASS (flickr_user_parent_class)->finalize (obj);
+}
+
+
+static void
+flickr_user_class_init (FlickrUserClass *klass)
+{
+	flickr_user_parent_class = g_type_class_peek_parent (klass);
+	G_OBJECT_CLASS (klass)->finalize = flickr_user_finalize;
+}
+
+
+static DomElement*
+flickr_user_create_element (DomDomizable *base,
+			    DomDocument  *doc)
+{
+	FlickrUser *self;
+	DomElement *element;
+
+	self = FLICKR_USER (base);
+
+	element = dom_document_create_element (doc, "user", NULL);
+	if (self->id != NULL)
+		dom_element_set_attribute (element, "id", self->id);
+
+	return element;
+}
+
+
+static void
+flickr_user_load_from_element (DomDomizable *base,
+			       DomElement   *element)
+{
+	FlickrUser *self;
+	DomElement *node;
+
+	self = FLICKR_USER (base);
+
+	flickr_user_set_id (self, dom_element_get_attribute (element, "id"));
+	flickr_user_set_is_pro (self, dom_element_get_attribute (element, "ispro"));
+
+	for (node = element->first_child; node; node = node->next_sibling) {
+		if (g_strcmp0 (node->tag_name, "username") == 0) {
+			flickr_user_set_username (self, dom_element_get_inner_text (node));
+		}
+		else if (g_strcmp0 (node->tag_name, "bandwidth") == 0) {
+			flickr_user_set_max_bandwidth (self, dom_element_get_attribute (node, "maxbytes"));
+			flickr_user_set_used_bandwidth (self, dom_element_get_attribute (node, "usedbytes"));
+		}
+		else if (g_strcmp0 (node->tag_name, "filesize") == 0) {
+			flickr_user_set_max_filesize (self, dom_element_get_attribute (node, "maxbytes"));
+		}
+		else if (g_strcmp0 (node->tag_name, "videosize") == 0) {
+			flickr_user_set_max_videosize (self, dom_element_get_attribute (node, "maxbytes"));
+		}
+		else if (g_strcmp0 (node->tag_name, "sets") == 0) {
+			flickr_user_set_n_sets (self, dom_element_get_attribute (node, "created"));
+		}
+		else if (g_strcmp0 (node->tag_name, "videos") == 0) {
+			flickr_user_set_n_videos (self, dom_element_get_attribute (node, "uploaded"));
+		}
+	}
+}
+
+
+static void
+flickr_user_dom_domizable_interface_init (DomDomizableIface *iface)
+{
+	iface->create_element = flickr_user_create_element;
+	iface->load_from_element = flickr_user_load_from_element;
+}
+
+
+static void
+flickr_user_instance_init (FlickrUser *self)
+{
+}
+
+
+GType
+flickr_user_get_type (void)
+{
+	static GType flickr_user_type_id = 0;
+
+	if (flickr_user_type_id == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (FlickrUserClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) flickr_user_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (FlickrUser),
+			0,
+			(GInstanceInitFunc) flickr_user_instance_init,
+			NULL
+		};
+		static const GInterfaceInfo dom_domizable_info = {
+			(GInterfaceInitFunc) flickr_user_dom_domizable_interface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL
+		};
+
+		flickr_user_type_id = g_type_register_static (G_TYPE_OBJECT,
+								   "FlickrUser",
+								   &g_define_type_info,
+								   0);
+		g_type_add_interface_static (flickr_user_type_id, DOM_TYPE_DOMIZABLE, &dom_domizable_info);
+	}
+
+	return flickr_user_type_id;
+}
+
+
+FlickrUser *
+flickr_user_new (void)
+{
+	return g_object_new (FLICKR_TYPE_USER, NULL);
+}
+
+
+void
+flickr_user_set_id (FlickrUser *self,
+		    const char *value)
+{
+	g_free (self->id);
+	self->id = NULL;
+	if (value != NULL)
+		self->id = g_strdup (value);
+}
+
+
+void
+flickr_user_set_is_pro (FlickrUser *self,
+			const char *value)
+{
+	self->is_pro = (g_strcmp0 (value, "1") == 0);
+}
+
+
+void
+flickr_user_set_username(FlickrUser *self,
+			 const char *value)
+{
+	g_free (self->username);
+	self->username = NULL;
+	if (value != NULL)
+		self->username = g_strdup (value);
+}
+
+
+void
+flickr_user_set_max_bandwidth (FlickrUser *self,
+			       const char *value)
+{
+	self->max_bandwidth = g_ascii_strtoull (value, NULL, 10);
+}
+
+
+void
+flickr_user_set_used_bandwidth (FlickrUser *self,
+				const char *value)
+{
+	self->used_bandwidth = g_ascii_strtoull (value, NULL, 10);
+}
+
+
+void
+flickr_user_set_max_filesize (FlickrUser *self,
+			      const char *value)
+{
+	self->max_filesize = g_ascii_strtoull (value, NULL, 10);
+}
+
+
+void
+flickr_user_set_max_videosize (FlickrUser *self,
+			       const char *value)
+{
+	self->max_videosize = g_ascii_strtoull (value, NULL, 10);
+}
+
+
+void
+flickr_user_set_n_sets (FlickrUser *self,
+			const char *value)
+{
+	if (value != NULL)
+		self->n_sets = atoi (value);
+	else
+		self->n_sets = 0;
+}
+
+
+void
+flickr_user_set_n_videos (FlickrUser *self,
+			  const char *value)
+{
+	if (value != NULL)
+		self->n_videos = atoi (value);
+	else
+		self->n_videos = 0;
+}
diff --git a/extensions/flicker/flickr-user.h b/extensions/flicker/flickr-user.h
new file mode 100644
index 0000000..0c6a77d
--- /dev/null
+++ b/extensions/flicker/flickr-user.h
@@ -0,0 +1,84 @@
+/* -*- 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 FLICKR_USER_H
+#define FLICKR_USER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FLICKR_TYPE_USER            (flickr_user_get_type ())
+#define FLICKR_USER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLICKR_TYPE_USER, FlickrUser))
+#define FLICKR_USER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FLICKR_TYPE_USER, FlickrUserClass))
+#define FLICKR_IS_USER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLICKR_TYPE_USER))
+#define FLICKR_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FLICKR_TYPE_USER))
+#define FLICKR_USER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FLICKR_TYPE_USER, FlickrUserClass))
+
+typedef struct _FlickrUser FlickrUser;
+typedef struct _FlickrUserClass FlickrUserClass;
+typedef struct _FlickrUserPrivate FlickrUserPrivate;
+
+struct _FlickrUser {
+	GObject parent_instance;
+	FlickrUserPrivate *priv;
+
+	char     *id;
+	gboolean  is_pro;
+	char     *username;
+	goffset   max_bandwidth;
+	goffset   used_bandwidth;
+	goffset   max_filesize;
+	goffset   max_videosize;
+	int       n_sets;
+	int       n_videos;
+};
+
+struct _FlickrUserClass {
+	GObjectClass parent_class;
+};
+
+GType             flickr_user_get_type             (void);
+FlickrUser *      flickr_user_new                  (void);
+void              flickr_user_set_id               (FlickrUser *self,
+						    const char *value);
+void              flickr_user_set_is_pro           (FlickrUser *self,
+						    const char *value);
+void              flickr_user_set_username         (FlickrUser *self,
+						    const char *value);
+void              flickr_user_set_max_bandwidth    (FlickrUser *self,
+						    const char *value);
+void              flickr_user_set_used_bandwidth   (FlickrUser *self,
+						    const char *value);
+void              flickr_user_set_max_filesize     (FlickrUser *self,
+						    const char *value);
+void              flickr_user_set_max_videosize    (FlickrUser *self,
+						    const char *value);
+void              flickr_user_set_n_sets           (FlickrUser *self,
+						    const char *value);
+void              flickr_user_set_n_videos         (FlickrUser *self,
+						    const char *value);
+
+G_END_DECLS
+
+#endif /* FLICKR_USER_H */
diff --git a/extensions/picasaweb/dlg-export-to-picasaweb.c b/extensions/picasaweb/dlg-export-to-picasaweb.c
index 406b47c..413e255 100644
--- a/extensions/picasaweb/dlg-export-to-picasaweb.c
+++ b/extensions/picasaweb/dlg-export-to-picasaweb.c
@@ -653,6 +653,7 @@ static void
 auto_select_account (DialogData *data)
 {
 	gtk_widget_hide (data->dialog);
+	gth_task_dialog (GTH_TASK (data->conn), FALSE);
 
 	if (data->accounts != NULL) {
 		if (data->email != NULL) {
@@ -665,6 +666,7 @@ auto_select_account (DialogData *data)
 		else {
 			GtkWidget *dialog;
 
+			gth_task_dialog (GTH_TASK (data->conn), TRUE);
 			dialog = picasa_account_chooser_dialog_new (data->accounts, data->email);
 			g_signal_connect (dialog,
 					  "response",
diff --git a/extensions/picasaweb/picasa-web-service.c b/extensions/picasaweb/picasa-web-service.c
index 35c1e91..9559152 100644
--- a/extensions/picasaweb/picasa-web-service.c
+++ b/extensions/picasaweb/picasa-web-service.c
@@ -820,7 +820,7 @@ picasa_web_accounts_save_to_file (GList      *accounts,
 	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, G_FILE_CREATE_PRIVATE|G_FILE_CREATE_REPLACE_DESTINATION, buffer, len, NULL, NULL);
+	g_write_file (file, FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, buffer, len, NULL, NULL);
 
 	g_free (buffer);
 	g_object_unref (file);



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