[tracker/miner-web] libtracker-miner: Add TrackerMinerWeb class



commit 9dcabd24ec2ee657391b5b6830d9f5d6a76968b8
Author: Carlos Garnacho <carlos lanedo com>
Date:   Fri Nov 6 15:25:19 2009 +0100

    libtracker-miner: Add TrackerMinerWeb class
    
    The TrackerMinerWeb class is a thin layer above TrackerMiner which adds
    functions commonly used by miners fetching data from remote services.

 configure.ac                                       |   99 +++
 ....freedesktop.Tracker1.Miner.Facebook.service.in |    3 +
 data/dbus/tracker-miner-web.xml                    |   21 +
 data/miners/Makefile.am                            |    6 +-
 data/miners/tracker-miner-facebook.desktop.in      |    7 +
 examples/libtracker-miner/Makefile.am              |   17 +-
 examples/libtracker-miner/password-provider-test.c |   56 ++
 src/Makefile.am                                    |    3 +
 src/libtracker-miner/Makefile.am                   |   48 ++-
 src/libtracker-miner/gnome-password-provider.c     |  304 +++++++++
 src/libtracker-miner/keyfile-password-provider.c   |  393 +++++++++++
 src/libtracker-miner/tracker-miner-0.7.vapi        |   82 +++
 src/libtracker-miner/tracker-miner-web-0.7.vapi    |   40 ++
 src/libtracker-miner/tracker-miner-web-dbus.h      |   47 ++
 src/libtracker-miner/tracker-miner-web.c           |  249 +++++++
 src/libtracker-miner/tracker-miner-web.deps.in     |    1 +
 src/libtracker-miner/tracker-miner-web.h           |  138 ++++
 src/libtracker-miner/tracker-miner.c               |    4 +-
 src/libtracker-miner/tracker-password-provider.c   |  131 ++++
 src/libtracker-miner/tracker-password-provider.h   |   84 +++
 .../tracker-password-provider.vala                 |   32 +
 src/libtracker-miner/vapi/rebuild_vapi.sh          |    6 +
 .../vapi/tracker-miner-0.7-custom.vala             |   46 ++
 src/libtracker-miner/vapi/tracker-miner-0.7.deps   |    1 +
 src/libtracker-miner/vapi/tracker-miner-0.7.files  |    1 +
 .../vapi/tracker-miner-0.7.metadata                |   37 +
 .../vapi/tracker-miner-0.7.namespace               |    1 +
 src/tracker-miner-facebook/Makefile.am             |   66 ++
 src/tracker-miner-facebook/facebook.vala           |  719 ++++++++++++++++++++
 src/tracker-preferences/Makefile.am                |    2 +-
 src/tracker-preferences/tracker-preferences.ui     |  125 ++++-
 src/tracker-preferences/tracker-preferences.vala   |  149 ++++
 32 files changed, 2900 insertions(+), 18 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index c000bf1..3c5219b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -157,6 +157,8 @@ EDS_REQUIRED=2.25.5
 LIBSTREAMANALYZER_REQUIRED=0.7.0
 GEE_REQUIRED=0.3
 ID3LIB_REQUIRED=3.8.3
+GNOME_KEYRING_REQUIRED=2.26
+LIBREST_REQUIRED=0.6.1
 
 # Library Checks
 PKG_CHECK_MODULES(GLIB2, [glib-2.0 >= $GLIB_REQUIRED])
@@ -753,6 +755,67 @@ fi
 
 AM_CONDITIONAL(HAVE_UNAC, test "x$have_unac" = "xyes")
 
+##################################################################
+# Enable Gnome Keyring support?
+##################################################################
+
+AC_ARG_ENABLE(gnome_keyring, 
+	      AS_HELP_STRING([--enable-gnome-keyring],
+			     [enable Gnome Keyring support to store credentials [[default=auto]]]),,
+	      [enable_gnome_keyring=auto])
+
+if test "x$enable_gnome_keyring" != "xno"; then
+	PKG_CHECK_MODULES(GNOME_KEYRING,
+					  [ gnome-keyring-1 >= $GNOME_KEYRING_REQUIRED ],
+					  [have_gnome_keyring=yes],
+					  [have_gnome_keyring=no])
+	AC_SUBST(GNOME_KEYRING_LIBS)
+	AC_SUBST(GNOME_KEYRING_CFLAGS)
+
+	if test "x$have_gnome_keyring" = "xyes"; then
+		AC_DEFINE(HAVE_GNOME_KEYRING, [], [Define if we have Gnome Keyring for password storage])
+	fi
+fi
+
+if test "x$enable_gnome_keyring" = "xyes"; then
+   if test "x$have_gnome_keyring" != "xyes"; then
+      AC_MSG_ERROR([Couldn't find Gnome Keyring >= $GNOME_KEYRING_REQUIRED.])
+   fi
+fi
+
+AM_CONDITIONAL(HAVE_GNOME_KEYRING, test "x$have_gnome_keyring" = "xyes")
+
+##################################################################
+# Enable librest support?
+##################################################################
+
+AC_ARG_ENABLE(librest,
+	      AS_HELP_STRING([--enable-librest],
+			     [enable librest (necessary for web miners) [[default=auto]]]),,
+	      [enable_librest=auto])
+
+if test "x$enable_librest" != "xno"; then
+	PKG_CHECK_MODULES(LIBREST,
+					  [ rest-0.6 >= $LIBREST_REQUIRED ],
+					  [have_librest=yes],
+					  [have_librest=no])
+	AC_SUBST(LIBREST_LIBS)
+	AC_SUBST(LIBREST_CFLAGS)
+
+	if test "x$have_librest" = "xyes"; then
+		AC_DEFINE(HAVE_LIBREST, [], [Define if we have librest for web miners])
+	fi
+fi
+
+if test "x$enable_librest" = "xyes"; then
+   if test "x$have_librest" != "xyes"; then
+      AC_MSG_ERROR([Couldn't find librest >= $LIBREST_REQUIRED.])
+   fi
+fi
+
+AM_CONDITIONAL(HAVE_LIBREST, test "x$have_librest" = "xyes")
+
+
 ####################################################################
 # Mail miners
 ####################################################################
@@ -868,6 +931,19 @@ PKG_CHECK_MODULES(TRACKER_VALA,
 AC_SUBST(TRACKER_VALA_CFLAGS)
 AC_SUBST(TRACKER_VALA_LIBS)
 
+PKG_CHECK_MODULES(GNOMEKEYRING,
+                  [ gnome-keyring-1 >= $GNOMEKEYRING_REQUIRED ],
+                  [have_gnomekeyring=yes],
+                  [have_gnomekeyring=no])
+AC_SUBST(GNOMEKEYRING_LIBS)
+AC_SUBST(GNOMEKEYRING_CFLAGS)
+
+if test "x$have_gnomekeyring" = "xyes"; then
+	AC_DEFINE(HAVE_GNOMEKEYRING, [], [Define if we have Gnome Keyring for password storage])
+fi
+
+AM_CONDITIONAL(HAVE_GNOMEKEYRING, test "x$have_gnomekeyring" = "xyes")
+
 ##################################################################
 # Enable building tracker-status-icon?
 ##################################################################
@@ -952,6 +1028,27 @@ fi
 AM_CONDITIONAL(HAVE_TRACKER_SEARCH_BAR, test "$have_tracker_search_bar" = "yes")
 
 ##################################################################
+# Check for dependencies to build tracker facebook miner
+##################################################################
+
+AC_ARG_ENABLE([miner-facebook],
+	      AS_HELP_STRING([--enable-miner-facebook],
+	                     [enable tracker-miner-facebook[[default=no]]]),,
+	      [enable_tracker_miner_facebook=no])
+
+if test "x$enable_tracker_miner_facebook" != "xno" ; then
+   if test "x$have_librest" != "xyes" ; then
+      AC_MSG_ERROR([Couldn't find tracker-miner-facebook dependencies ($LIBREST_REQUIRED).])
+   else
+      have_tracker_miner_facebook="yes"
+   fi
+else
+   have_tracker_miner_facebook="no  (disabled)"
+fi
+
+AM_CONDITIONAL(HAVE_TRACKER_MINER_FACEBOOK, test "$have_tracker_miner_facebook" = "yes")
+
+##################################################################
 # Check for GNOME/GTK dependencies to build tracker search tool
 ##################################################################
 
@@ -1639,6 +1736,7 @@ AC_CONFIG_FILES([
 	src/tracker-control/Makefile
 	src/tracker-extract/Makefile
 	src/tracker-miner-fs/Makefile
+	src/tracker-miner-facebook/Makefile
 	src/tracker-preferences/Makefile
 	src/tracker-preferences/tracker-preferences.desktop.in
 	src/tracker-search-bar/Makefile
@@ -1741,6 +1839,7 @@ Metadata Extractors:
 	Support helix formats (RPM/RM/etc):     $have_gstreamer_helix
 	Support MP3 album art:                  $selected_for_albumart
 	Support playlists (w/ Totem):           $have_playlist
+	Facebook miner:				$have_tracker_miner_facebook
 
 Plugins/Extensions:
 
diff --git a/data/dbus/org.freedesktop.Tracker1.Miner.Facebook.service.in b/data/dbus/org.freedesktop.Tracker1.Miner.Facebook.service.in
new file mode 100644
index 0000000..cd01265
--- /dev/null
+++ b/data/dbus/org.freedesktop.Tracker1.Miner.Facebook.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.Tracker1.Miner.Facebook
+Exec= libexecdir@/tracker-miner-facebook
diff --git a/data/dbus/tracker-miner-web.xml b/data/dbus/tracker-miner-web.xml
new file mode 100644
index 0000000..5c17a6a
--- /dev/null
+++ b/data/dbus/tracker-miner-web.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<node name="/">
+  <interface name="org.freedesktop.Tracker1.Miner.Web">
+    <method name="Authenticate">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+    </method>
+    <method name="GetAssociationData">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+	  <arg name="result" type="a{ss}" direction="out"/>
+	</method>
+    <method name="Associate">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+	  <arg name="data" type="a{ss}" direction="in"/>
+	</method>
+    <method name="Dissociate">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+	</method>
+	<property name="AssociationStatus" type="u" access="read"/>
+  </interface>
+</node>
diff --git a/data/miners/Makefile.am b/data/miners/Makefile.am
index cdb1db4..4ea7bd3 100644
--- a/data/miners/Makefile.am
+++ b/data/miners/Makefile.am
@@ -2,13 +2,15 @@ include $(top_srcdir)/Makefile.decl
 
 desktop_in_files = \
 	tracker-miner-applications.desktop.in \
-	tracker-miner-files.desktop.in
+	tracker-miner-files.desktop.in        \
+	tracker-miner-facebook.desktop.in
 
 tracker_minersdir = $(datadir)/tracker/miners
 
 tracker_miners_DATA = \
 	tracker-miner-applications.desktop \
-	tracker-miner-files.desktop
+	tracker-miner-files.desktop        \
+	tracker-miner-facebook.desktop
 
 @INTLTOOL_DESKTOP_RULE@
 
diff --git a/data/miners/tracker-miner-facebook.desktop.in b/data/miners/tracker-miner-facebook.desktop.in
new file mode 100644
index 0000000..0ecf121
--- /dev/null
+++ b/data/miners/tracker-miner-facebook.desktop.in
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Facebook
+_Comment=Index your data on Facebook
+DBusName=org.freedesktop.Tracker1.Miner.Facebook
+DBusPath=/org/freedesktop/Tracker1/Miner/Facebook
+X-Tracker-Bridge-AuthScheme=Token
diff --git a/examples/libtracker-miner/Makefile.am b/examples/libtracker-miner/Makefile.am
index 5f753ce..f612330 100644
--- a/examples/libtracker-miner/Makefile.am
+++ b/examples/libtracker-miner/Makefile.am
@@ -17,7 +17,7 @@ INCLUDES =							\
 	$(DBUS_CFLAGS)						\
 	$(UNAC_CFLAGS)
 
-noinst_PROGRAMS = tracker-miner-test
+noinst_PROGRAMS = tracker-miner-test password-provider-test
 
 tracker_miner_test_SOURCES =   					\
 	tracker-miner-test.c    				\
@@ -36,3 +36,18 @@ tracker_miner_test_LDADD =                             		\
        $(GLIB2_LIBS)                                   		\
        -lz                                             		\
        -lm
+
+password_provider_test_SOURCES =      \
+	password-provider-test.c
+
+password_provider_test_LDADD =        \
+	$(top_builddir)/src/libtracker-miner/libtracker-miner- TRACKER_API_VERSION@.la	\
+	$(DBUS_LIBS)                                    		\
+	$(GMODULE_LIBS)                                 		\
+	$(GTHREAD_LIBS)                                 		\
+	$(GIO_LIBS)                                     		\
+	$(GCOV_LIBS)                                    		\
+	$(GLIB2_LIBS)                                   		\
+	-lz                                             		\
+	-lm
+
diff --git a/examples/libtracker-miner/password-provider-test.c b/examples/libtracker-miner/password-provider-test.c
new file mode 100644
index 0000000..12a8097
--- /dev/null
+++ b/examples/libtracker-miner/password-provider-test.c
@@ -0,0 +1,56 @@
+#include "config.h"
+#include <libtracker-miner/tracker-password-provider.h>
+
+#define SERVICE_NAME "TestService"
+
+int main (int argc, char **argv)
+{
+	g_type_init ();
+	g_set_application_name ("PasswordBackendTest");
+
+	TrackerPasswordProvider *p = tracker_get_password_provider ();
+
+	GError *error = NULL;
+	tracker_password_provider_store_password (p, SERVICE_NAME, "This is the test service", "testUser", "testPass", &error);
+
+	if (error) {
+		g_critical ("tracker_password_provider_store: %s", error->message);
+		g_error_free (error);
+
+		return 1;
+	}
+
+	gchar *username = NULL;
+
+	gchar *password = tracker_password_provider_get_password (p, SERVICE_NAME, &username, &error);
+
+	if (error) {
+		g_critical ("tracker_password_provider_get: %s", error->message);
+		g_error_free (error);
+
+		return 1;
+	} else {
+		g_message ("Username: %s , Password:%s", username, password);
+	}
+
+	g_free (username);
+	username = NULL;
+	g_free (password);
+
+	// Also test without getting the username
+	password = tracker_password_provider_get_password (p, SERVICE_NAME, NULL, &error);
+
+	if (error) {
+		g_critical ("tracker_password_provider_get: %s", error->message);
+		g_error_free (error);
+
+		return 1;
+	} else {
+		g_message ("Username: %s , Password:%s", username, password);
+	}
+
+	g_free (username);
+	g_free (password);
+	g_free (p);
+	return 0;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index 2f5ff33..e6ee672 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,3 +39,6 @@ if HAVE_TRACKER_EXPLORER
 SUBDIRS += tracker-explorer
 endif
 
+if HAVE_TRACKER_MINER_FACEBOOK
+SUBDIRS += tracker-miner-facebook
+endif
diff --git a/src/libtracker-miner/Makefile.am b/src/libtracker-miner/Makefile.am
index 973126a..dd7689c 100644
--- a/src/libtracker-miner/Makefile.am
+++ b/src/libtracker-miner/Makefile.am
@@ -17,7 +17,8 @@ INCLUDES =						\
 	$(HAL_CFLAGS)					\
 	$(DEVKIT_POWER_CFLAGS)				\
 	$(DBUS_CFLAGS)					\
-	$(UNAC_CFLAGS)
+	$(UNAC_CFLAGS)                  \
+	--include $(top_srcdir)/config.h
 
 lib_LTLIBRARIES = libtracker-miner- TRACKER_API_VERSION@.la
 
@@ -36,13 +37,16 @@ libtracker_miner_ TRACKER_API_VERSION@_la_SOURCES = 	\
 	tracker-miner-dbus.h				\
 	tracker-miner-fs.c				\
 	tracker-miner-fs.h				\
+	tracker-miner-web.c				\
+	tracker-miner-web.h				\
 	tracker-miner-manager.c				\
 	tracker-miner-manager.h				\
 	tracker-monitor.c				\
 	tracker-monitor.h				\
 	tracker-utils.c					\
-	tracker-utils.h					\
-	tracker-thumbnailer.c
+	tracker-thumbnailer.c           \
+	tracker-utils.h                 \
+	tracker-password-provider.c
 
 libtracker_minerinclude_HEADERS = 			\
 	tracker-crawler.h				\
@@ -51,6 +55,8 @@ libtracker_minerinclude_HEADERS = 			\
 	tracker-miner-fs.h				\
 	tracker-miner-manager.h				\
 	tracker-thumbnailer.h
+	tracker-miner-web.h             \
+	tracker-password-provider.h
 
 libtracker_miner_ TRACKER_API_VERSION@_la_LDFLAGS = 	\
 	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
@@ -71,6 +77,28 @@ libtracker_miner_ TRACKER_API_VERSION@_la_LIBADD = 	\
 	$(GLIB2_LIBS)					\
 	$(GDKPIXBUF_LIBS)
 
+if HAVE_GNOME_KEYRING
+libtracker_miner_ TRACKER_API_VERSION@_la_SOURCES += 	\
+	gnome-password-provider.c
+
+libtracker_miner_ TRACKER_API_VERSION@_la_LIBADD +=       \
+	$(GNOME_KEYRING_LIBS)
+
+INCLUDES +=                         \
+	$(GNOME_KEYRING_CFLAGS)
+else
+libtracker_miner_ TRACKER_API_VERSION@_la_SOURCES += 	\
+	keyfile-password-provider.c
+endif
+
+vapidir=$(datadir)/vala/vapi
+vapi_DATA =                     \
+	tracker-miner-$(TRACKER_API_VERSION).vapi
+
+tracker-miner-web-$(TRACKER_API_VERSION).deps: tracker-miner-web.deps.in
+	@sed -e "s|@VERSION[ ]|${TRACKER_API_VERSION}|" $< > $@
+
+
 tracker-marshal.h: tracker-marshal.list
 	$(AM_V_GEN)$(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --header > $@
 
@@ -84,15 +112,27 @@ marshal_sources = 					\
 
 dbus_sources = 						\
 	tracker-miner-glue.h				\
+	tracker-miner-web-glue.h				\
 	tracker-miner-client.h
 
+# Custom rule to avoid API duplication. There is also a workaround for bug
+# in dbus-binding-tool where it generates bad code when two files are passed
+# on the command line (though the man page says it supports it)
+# This bug is reported at https://bugs.freedesktop.org/show_bug.cgi?id=25056
+tracker-miner-web-glue.h: $(top_srcdir)/data/dbus/tracker-miner-web.xml $(top_srcdir)/data/dbus/tracker-miner.xml
+	echo '<?xml version="1.0" encoding="UTF-8"?>'        > $(top_srcdir)/src/libtracker-miner/tracker-miner-web-full.xml
+	echo '<node name="/">'                              >> $(top_srcdir)/src/libtracker-miner/tracker-miner-web-full.xml
+	cat $^ | grep -v -e '<node' -e '<?xml' -e '</node>' >> $(top_srcdir)/src/libtracker-miner/tracker-miner-web-full.xml
+	echo '</node>'                                      >> $(top_srcdir)/src/libtracker-miner/tracker-miner-web-full.xml
+	$(AM_V_GEN)$(DBUSBINDINGTOOL) --mode=glib-server --output=$@ --prefix=tracker_miner_web_dbus $(top_srcdir)/src/libtracker-miner/tracker-miner-web-full.xml
+
 %-glue.h: $(top_srcdir)/data/dbus/%.xml
 	$(AM_V_GEN)$(DBUSBINDINGTOOL) --mode=glib-server --output=$@ --prefix=$(subst -,_,$*) $^
 
 %-client.h: $(top_srcdir)/data/dbus/%.xml
 	$(AM_V_GEN)$(DBUSBINDINGTOOL) --mode=glib-client --output=$@ --prefix=$(subst -,_,$*) $^
 
-BUILT_SOURCES = $(dbus_sources) $(marshal_sources)
+BUILT_SOURCES = $(dbus_sources) $(marshal_sources) tracker-miner-web-full.xml
 
 CLEANFILES = $(BUILT_SOURCES)
 
diff --git a/src/libtracker-miner/gnome-password-provider.c b/src/libtracker-miner/gnome-password-provider.c
new file mode 100644
index 0000000..96dbb89
--- /dev/null
+++ b/src/libtracker-miner/gnome-password-provider.c
@@ -0,0 +1,304 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, Adrien Bustany (abustany gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <glib-object.h>
+#include <gnome-keyring.h>
+
+#include "tracker-password-provider.h"
+
+#define TRACKER_TYPE_PASSWORD_PROVIDER_GNOME         (tracker_password_provider_gnome_get_type())
+#define TRACKER_PASSWORD_PROVIDER_GNOME(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_PASSWORD_PROVIDER_GNOME, TrackerPasswordProviderGnome))
+#define TRACKER_PASSWORD_PROVIDER_GNOME_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),    TRACKER_TYPE_PASSWORD_PROVIDER_GNOME, TrackerPasswordProviderGnomeClass))
+#define TRACKER_IS_PASSWORD_PROVIDER_GNOME(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_PASSWORD_PROVIDER_GNOME))
+#define TRACKER_IS_PASSWORD_PROVIDER_GNOME_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),    TRACKER_TYPE_PASSWORD_PROVIDER_GNOME))
+#define TRACKER_PASSWORD_PROVIDER_GNOME_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_PASSWORD_PROVIDER_GNOME, TrackerPasswordProviderGnomeClass))
+
+#define TRACKER_PASSWORD_PROVIDER_GNOME_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_PASSWORD_PROVIDER_GNOME, TrackerPasswordProviderGnomePrivate))
+
+#define PASSWORD_PROVIDER_GNOME_NAME "Gnome keyring"
+
+typedef struct TrackerPasswordProviderGnome TrackerPasswordProviderGnome;
+typedef struct TrackerPasswordProviderGnomeClass TrackerPasswordProviderGnomeClass;
+typedef struct TrackerPasswordProviderGnomePrivate TrackerPasswordProviderGnomePrivate;
+
+struct TrackerPasswordProviderGnome {
+	GObject parent_instance;
+	TrackerPasswordProviderGnomePrivate *private;
+};
+
+struct TrackerPasswordProviderGnomeClass {
+	GObjectClass parent_class;
+};
+
+struct TrackerPasswordProviderGnomePrivate {
+	gchar    *name;
+};
+
+const GnomeKeyringPasswordSchema password_schema = {
+	GNOME_KEYRING_ITEM_GENERIC_SECRET,
+	{
+		{ "service", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+		{ "username", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+		{ NULL, 0 }
+	}
+};
+
+enum {
+	PROP_0,
+	PROP_NAME
+};
+
+GType           tracker_password_provider_gnome_get_type (void) G_GNUC_CONST;
+
+static void     tracker_password_provider_iface_init     (TrackerPasswordProviderIface    *iface);
+static void     password_provider_gnome_constructed      (GObject                         *object);
+static void     password_provider_gnome_finalize         (GObject                         *object);
+static void     password_provider_set_property           (GObject                         *object,
+                                                          guint                            prop_id,
+                                                          const GValue                    *value,
+                                                          GParamSpec                      *pspec);
+static void     password_provider_get_property           (GObject                         *object,
+                                                          guint                            prop_id,
+                                                          GValue                          *value,
+                                                          GParamSpec                      *pspec);
+
+void            password_provider_gnome_store            (TrackerPasswordProvider         *provider,
+                                                          const gchar                     *service,
+                                                          const gchar                     *description,
+                                                          const gchar                     *username,
+                                                          const gchar                     *password,
+                                                          GError                         **error);
+gchar*          password_provider_gnome_get              (TrackerPasswordProvider         *provider,
+                                                          const gchar                     *service,
+                                                          gchar                          **username,
+                                                          GError                         **error);
+void            password_provider_gnome_forget           (TrackerPasswordProvider         *provider,
+                                                          const gchar                     *service,
+                                                          GError                         **error);
+
+G_DEFINE_TYPE_WITH_CODE (TrackerPasswordProviderGnome,
+                         tracker_password_provider_gnome,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (TRACKER_TYPE_PASSWORD_PROVIDER,
+                                                tracker_password_provider_iface_init))
+
+static void
+tracker_password_provider_gnome_class_init (TrackerPasswordProviderGnomeClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize     = password_provider_gnome_finalize;
+	object_class->constructed  = password_provider_gnome_constructed;
+	object_class->set_property = password_provider_set_property;
+	object_class->get_property = password_provider_get_property;
+
+	g_object_class_override_property (object_class,
+	                                  PROP_NAME,
+	                                  "name");
+	g_type_class_add_private (object_class,
+	                          sizeof (TrackerPasswordProviderGnomePrivate));
+}
+
+static void
+tracker_password_provider_gnome_init (TrackerPasswordProviderGnome *provider)
+{
+	TrackerPasswordProviderGnomePrivate *priv;
+
+	priv = TRACKER_PASSWORD_PROVIDER_GNOME_GET_PRIVATE (provider);
+}
+
+static void
+tracker_password_provider_iface_init (TrackerPasswordProviderIface *iface)
+{
+	iface->store_password = password_provider_gnome_store;
+	iface->get_password = password_provider_gnome_get;
+	iface->forget_password = password_provider_gnome_forget;
+}
+
+static void
+password_provider_gnome_constructed (GObject *object)
+{
+}
+
+static void
+password_provider_gnome_finalize (GObject *object)
+{
+	G_OBJECT_CLASS (tracker_password_provider_gnome_parent_class)->finalize (object);
+}
+
+static void
+password_provider_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+	TrackerPasswordProviderGnome *gnome_provider = TRACKER_PASSWORD_PROVIDER_GNOME (object);
+	TrackerPasswordProviderGnomePrivate *priv = TRACKER_PASSWORD_PROVIDER_GNOME_GET_PRIVATE (gnome_provider);
+
+	switch (prop_id) {
+		case PROP_NAME:
+			g_free (priv->name);
+			priv->name = g_value_dup_string (value);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	};
+}
+
+static void
+password_provider_get_property (GObject      *object,
+                                guint         prop_id,
+                                GValue       *value,
+                                GParamSpec   *pspec)
+{
+	TrackerPasswordProviderGnome *gnome_provider = TRACKER_PASSWORD_PROVIDER_GNOME (object);
+	TrackerPasswordProviderGnomePrivate *priv = TRACKER_PASSWORD_PROVIDER_GNOME_GET_PRIVATE (gnome_provider);
+
+	switch (prop_id) {
+		case PROP_NAME:
+			g_value_set_string (value, priv->name);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	};
+}
+
+void
+password_provider_gnome_store (TrackerPasswordProvider  *provider,
+                                 const gchar              *service,
+                                 const gchar              *description,
+                                 const gchar              *username,
+                                 const gchar              *password,
+                                 GError                  **error)
+{
+	GnomeKeyringResult r = gnome_keyring_store_password_sync (&password_schema,
+	                                                          NULL,
+	                                                          description,
+	                                                          password,
+	                                                          "service", service,
+	                                                          "username", username,
+	                                                          NULL);
+	if (r != GNOME_KEYRING_RESULT_OK) {
+		g_set_error_literal (error,
+                             TRACKER_PASSWORD_PROVIDER_ERROR,
+                             TRACKER_PASSWORD_PROVIDER_ERROR_SERVICE,
+                             "Cannot store password in Gnome keyring");
+	}
+}
+
+gchar*
+password_provider_gnome_get (TrackerPasswordProvider  *provider,
+                             const gchar              *service,
+                             gchar                   **username,
+                             GError                  **error)
+{
+	gchar *password;
+	GnomeKeyringAttributeList *search_attributes;
+	GList *found_items = NULL;
+
+	GnomeKeyringAttribute service_attr = {
+		"service",
+		GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+		{ service }
+	};
+
+	search_attributes = g_array_sized_new (FALSE,
+	                                       FALSE,
+	                                       sizeof (GnomeKeyringAttribute),
+	                                       1);
+
+	g_array_append_val (search_attributes, service_attr);
+
+	GnomeKeyringResult r = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+	                                                      search_attributes,
+	                                                      &found_items);
+
+	if (r != GNOME_KEYRING_RESULT_OK) {
+		if (r == GNOME_KEYRING_RESULT_NO_MATCH) {
+			g_set_error_literal (error,
+	                             TRACKER_PASSWORD_PROVIDER_ERROR,
+	                             TRACKER_PASSWORD_PROVIDER_ERROR_NOTFOUND,
+	                             "Password not found");
+		} else {
+			g_set_error (error,
+	                     TRACKER_PASSWORD_PROVIDER_ERROR,
+	                     TRACKER_PASSWORD_PROVIDER_ERROR_SERVICE,
+	                     "Keyring error: %s",
+	                     gnome_keyring_result_to_message (r));
+		}
+
+		gnome_keyring_found_list_free (found_items);
+		return NULL;
+	}
+
+	GnomeKeyringFound *found = (GnomeKeyringFound *)(g_list_first (found_items));
+
+	/* Walk through all attributes and select the ones we're interested in */
+	gint i;
+	for (i = 0 ; i < found->attributes->len ; ++i) {
+		GnomeKeyringAttribute *attr = &g_array_index (found->attributes, GnomeKeyringAttribute, i);
+		if (g_strcmp0 (attr->name, "username") && username) {
+			*username = g_strdup (attr->value.string);
+		}
+	}
+
+	password = g_strdup (found->secret);
+
+	gnome_keyring_found_list_free (found_items);
+
+	return password;
+}
+
+void
+password_provider_gnome_forget (TrackerPasswordProvider  *provider,
+                                  const gchar              *service,
+                                  GError                  **error)
+{
+	GnomeKeyringResult r = gnome_keyring_delete_password_sync (&password_schema,
+	                                                           "service", service,
+	                                                           NULL);
+
+	if (r != GNOME_KEYRING_RESULT_OK) {
+		g_set_error (error,
+                     TRACKER_PASSWORD_PROVIDER_ERROR,
+                     TRACKER_PASSWORD_PROVIDER_ERROR_SERVICE,
+                     "Cannot delete password: %s",
+                     gnome_keyring_result_to_message (r));
+	}
+}
+
+const TrackerPasswordProvider*
+tracker_get_password_provider (void)
+{
+	static TrackerPasswordProvider *instance = NULL;
+
+	if (instance == NULL) {
+		instance = g_object_new (TRACKER_TYPE_PASSWORD_PROVIDER_GNOME,
+		                         "name", PASSWORD_PROVIDER_GNOME_NAME,
+		                         NULL);
+	}
+
+	g_assert (instance != NULL);
+
+	return instance;
+}
+
diff --git a/src/libtracker-miner/keyfile-password-provider.c b/src/libtracker-miner/keyfile-password-provider.c
new file mode 100644
index 0000000..d52c9e2
--- /dev/null
+++ b/src/libtracker-miner/keyfile-password-provider.c
@@ -0,0 +1,393 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, Adrien Bustany (abustany gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include <glib-object.h>
+
+#include "tracker-password-provider.h"
+
+#define TRACKER_TYPE_PASSWORD_PROVIDER_KEYFILE         (tracker_password_provider_keyfile_get_type())
+#define TRACKER_PASSWORD_PROVIDER_KEYFILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_PASSWORD_PROVIDER_KEYFILE, TrackerPasswordProviderKeyfile))
+#define TRACKER_PASSWORD_PROVIDER_KEYFILE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),    TRACKER_TYPE_PASSWORD_PROVIDER_KEYFILE, TrackerPasswordProviderKeyfileClass))
+#define TRACKER_IS_PASSWORD_PROVIDER_KEYFILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_PASSWORD_PROVIDER_KEYFILE))
+#define TRACKER_IS_PASSWORD_PROVIDER_KEYFILE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),    TRACKER_TYPE_PASSWORD_PROVIDER_KEYFILE))
+#define TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_PASSWORD_PROVIDER_KEYFILE, TrackerPasswordProviderKeyfileClass))
+
+#define TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_PASSWORD_PROVIDER_KEYFILE, TrackerPasswordProviderKeyfilePrivate))
+
+#define PASSWORD_PROVIDER_KEYFILE_NAME "KeyFile"
+
+/* GKeyFile settings */
+#define KEYFILE_FILENAME "passwords.cfg"
+#define GROUP_GENERAL    "General"
+
+typedef struct TrackerPasswordProviderKeyfile TrackerPasswordProviderKeyfile;
+typedef struct TrackerPasswordProviderKeyfileClass TrackerPasswordProviderKeyfileClass;
+typedef struct TrackerPasswordProviderKeyfilePrivate TrackerPasswordProviderKeyfilePrivate;
+
+struct TrackerPasswordProviderKeyfile {
+	GObject parent_instance;
+	TrackerPasswordProviderKeyfilePrivate *private;
+};
+
+struct TrackerPasswordProviderKeyfileClass {
+	GObjectClass parent_class;
+};
+
+struct TrackerPasswordProviderKeyfilePrivate {
+	gchar    *name;
+	GKeyFile *password_file;
+};
+
+enum {
+	PROP_0,
+	PROP_NAME
+};
+
+GType           tracker_password_provider_keyfile_get_type (void) G_GNUC_CONST;
+
+static void     tracker_password_provider_iface_init       (TrackerPasswordProviderIface    *iface);
+static void     password_provider_keyfile_constructed      (GObject                         *object);
+static void     password_provider_keyfile_finalize         (GObject                         *object);
+static void     password_provider_set_property             (GObject                         *object,
+                                                            guint                            prop_id,
+                                                            const GValue                    *value,
+                                                            GParamSpec                      *pspec);
+static void     password_provider_get_property             (GObject                         *object,
+                                                            guint                            prop_id,
+                                                            GValue                          *value,
+                                                            GParamSpec                      *pspec);
+
+void            password_provider_keyfile_store            (TrackerPasswordProvider         *provider,
+                                                            const gchar                     *service,
+                                                            const gchar                     *description,
+                                                            const gchar                     *username,
+                                                            const gchar                     *password,
+                                                            GError                         **error);
+gchar*          password_provider_keyfile_get              (TrackerPasswordProvider         *provider,
+                                                            const gchar                     *service,
+                                                            gchar                          **username,
+                                                            GError                         **error);
+void            password_provider_keyfile_forget           (TrackerPasswordProvider         *provider,
+                                                            const gchar                     *service,
+                                                            GError                         **error);
+
+static void     load_password_file                         (TrackerPasswordProviderKeyfile  *kf);
+static gboolean save_password_file                         (TrackerPasswordProviderKeyfile  *kf);
+
+G_DEFINE_TYPE_WITH_CODE (TrackerPasswordProviderKeyfile,
+                         tracker_password_provider_keyfile,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (TRACKER_TYPE_PASSWORD_PROVIDER,
+                                                tracker_password_provider_iface_init))
+
+static void
+tracker_password_provider_keyfile_class_init (TrackerPasswordProviderKeyfileClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize     = password_provider_keyfile_finalize;
+	object_class->constructed  = password_provider_keyfile_constructed;
+	object_class->set_property = password_provider_set_property;
+	object_class->get_property = password_provider_get_property;
+
+	g_object_class_override_property (object_class,
+	                                  PROP_NAME,
+	                                  "name");
+	g_type_class_add_private (object_class,
+	                          sizeof (TrackerPasswordProviderKeyfilePrivate));
+}
+
+static void
+tracker_password_provider_keyfile_init (TrackerPasswordProviderKeyfile *provider)
+{
+	TrackerPasswordProviderKeyfilePrivate *priv;
+
+	priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (provider);
+}
+
+static void
+tracker_password_provider_iface_init (TrackerPasswordProviderIface *iface)
+{
+	iface->store_password = password_provider_keyfile_store;
+	iface->get_password = password_provider_keyfile_get;
+	iface->forget_password = password_provider_keyfile_forget;
+}
+
+static void
+password_provider_keyfile_constructed (GObject *object)
+{
+	TrackerPasswordProviderKeyfile *kf = TRACKER_PASSWORD_PROVIDER_KEYFILE (object);
+	TrackerPasswordProviderKeyfilePrivate *priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (kf);
+
+	priv->password_file = g_key_file_new ();
+
+	load_password_file (kf);
+}
+
+static void
+password_provider_keyfile_finalize (GObject *object)
+{
+	TrackerPasswordProviderKeyfile *kf = TRACKER_PASSWORD_PROVIDER_KEYFILE (object);
+	TrackerPasswordProviderKeyfilePrivate *priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (kf);
+
+	g_key_file_free (priv->password_file);
+
+	G_OBJECT_CLASS (tracker_password_provider_keyfile_parent_class)->finalize (object);
+}
+
+static void
+password_provider_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+	TrackerPasswordProviderKeyfile *kf = TRACKER_PASSWORD_PROVIDER_KEYFILE (object);
+	TrackerPasswordProviderKeyfilePrivate *priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (kf);
+
+	switch (prop_id) {
+		case PROP_NAME:
+			g_free (priv->name);
+			priv->name = g_value_dup_string (value);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	};
+}
+
+static void
+password_provider_get_property (GObject      *object,
+                                guint         prop_id,
+                                GValue       *value,
+                                GParamSpec   *pspec)
+{
+	TrackerPasswordProviderKeyfile *kf = TRACKER_PASSWORD_PROVIDER_KEYFILE (object);
+	TrackerPasswordProviderKeyfilePrivate *priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (kf);
+
+	switch (prop_id) {
+		case PROP_NAME:
+			g_value_set_string (value, priv->name);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	};
+}
+
+void
+password_provider_keyfile_store (TrackerPasswordProvider  *provider,
+                                 const gchar              *service,
+                                 const gchar              *description,
+                                 const gchar              *username,
+                                 const gchar              *password,
+                                 GError                  **error)
+{
+	TrackerPasswordProviderKeyfile *kf = TRACKER_PASSWORD_PROVIDER_KEYFILE (provider);
+	TrackerPasswordProviderKeyfilePrivate *priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (kf);
+
+	g_key_file_set_string (priv->password_file,
+	                       service,
+	                       "description",
+	                       description);
+	g_key_file_set_string (priv->password_file,
+	                       service,
+	                       "username",
+	                       username);
+	g_key_file_set_string (priv->password_file,
+	                       service,
+	                       "password",
+	                       password);
+
+	if (!save_password_file (kf)) {
+		g_set_error_literal (error,
+                             TRACKER_PASSWORD_PROVIDER_ERROR,
+                             TRACKER_PASSWORD_PROVIDER_ERROR_SERVICE,
+                             "Cannot write password file");
+	}
+}
+
+gchar*
+password_provider_keyfile_get (TrackerPasswordProvider  *provider,
+                               const gchar              *service,
+                               gchar                   **username,
+                               GError                  **error)
+{
+	TrackerPasswordProviderKeyfile *kf = TRACKER_PASSWORD_PROVIDER_KEYFILE (provider);
+	TrackerPasswordProviderKeyfilePrivate *priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (kf);
+
+	gchar *password;
+
+	GError *e1 = NULL;
+	GError *e2 = NULL;
+
+	password = g_key_file_get_string (priv->password_file,
+	                                  service,
+						              "password",
+						              &e1);
+
+	if (username) {
+		*username = g_key_file_get_string (priv->password_file,
+		                                   service,
+		                                   "username",
+		                                   &e2);
+	}
+
+	if (e1 || e2) {
+		g_set_error_literal (error,
+                             TRACKER_PASSWORD_PROVIDER_ERROR,
+                             TRACKER_PASSWORD_PROVIDER_ERROR_NOTFOUND,
+                             "Password not found");
+	}
+
+	if (e1)
+		g_error_free (e1);
+	if (e2)
+		g_error_free (e2);
+
+	return password;
+}
+
+void
+password_provider_keyfile_forget (TrackerPasswordProvider  *provider,
+                                  const gchar              *service,
+                                  GError                  **error)
+{
+	TrackerPasswordProviderKeyfile *kf = TRACKER_PASSWORD_PROVIDER_KEYFILE (provider);
+	TrackerPasswordProviderKeyfilePrivate *priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (kf);
+
+	GError *e = NULL;
+
+	if (!g_key_file_remove_group (priv->password_file, service, &e)) {
+		g_warning ("Cannot remove group '%s' from password file: %s",
+		           service,
+		           e->message);
+		g_error_free (e);
+
+		g_set_error_literal (error,
+                             TRACKER_PASSWORD_PROVIDER_ERROR,
+                             TRACKER_PASSWORD_PROVIDER_ERROR_NOTFOUND,
+                             "Service not found");
+	}
+}
+
+const TrackerPasswordProvider*
+tracker_get_password_provider (void)
+{
+	static TrackerPasswordProvider *instance = NULL;
+
+	if (instance == NULL) {
+		instance = g_object_new (TRACKER_TYPE_PASSWORD_PROVIDER_KEYFILE,
+		                         "name", PASSWORD_PROVIDER_KEYFILE_NAME,
+		                         NULL);
+	}
+
+	g_assert (instance != NULL);
+
+	return instance;
+}
+
+/* Copied from tracker-config-file.c */
+static gchar *
+config_dir_ensure_exists_and_return (void)
+{
+	gchar *directory;
+
+	directory = g_build_filename (g_get_user_config_dir (),
+			"tracker",
+			NULL);
+
+	if (!g_file_test (directory, G_FILE_TEST_EXISTS)) {
+		g_print ("Creating config directory:'%s'\n", directory);
+
+		if (g_mkdir_with_parents (directory, 0700) == -1) {
+			g_critical ("Could not create configuration directory");
+			g_free (directory);
+			return NULL;
+		}
+	}
+
+	return directory;
+}
+
+static void
+load_password_file (TrackerPasswordProviderKeyfile *kf)
+{
+	TrackerPasswordProviderKeyfilePrivate *priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (kf);
+	gchar *filename;
+	gchar *directory;
+
+	GError *error = NULL;
+
+	directory = config_dir_ensure_exists_and_return ();
+	if (!directory) {
+		return;
+	}
+
+	filename = g_build_filename (directory, KEYFILE_FILENAME, NULL);
+	g_free (directory);
+
+	if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+		return;
+	}
+
+	g_key_file_load_from_file (priv->password_file,
+	                           filename,
+	                           G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+	                           &error);
+
+	if (error) {
+		g_critical ("Cannot load password file: %s", error->message);
+		g_error_free (error);
+	}
+}
+
+static gboolean
+save_password_file (TrackerPasswordProviderKeyfile *kf)
+{
+	TrackerPasswordProviderKeyfilePrivate *priv = TRACKER_PASSWORD_PROVIDER_KEYFILE_GET_PRIVATE (kf);
+	gchar *filename;
+	gchar *directory;
+	gchar *data;
+	gsize size;
+
+	GError *error = NULL;
+
+	directory = config_dir_ensure_exists_and_return ();
+	if (!directory) {
+		return FALSE;
+	}
+
+	filename = g_build_filename (directory, KEYFILE_FILENAME, NULL);
+	g_free (directory);
+
+	data = g_key_file_to_data (priv->password_file, &size, NULL);
+
+	g_file_set_contents (filename, data, size, &error);
+	g_free (data);
+	g_free (filename);
+
+	if (error) {
+		g_warning ("Couldn't write password file : %s", error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
diff --git a/src/libtracker-miner/tracker-miner-0.7.vapi b/src/libtracker-miner/tracker-miner-0.7.vapi
new file mode 100644
index 0000000..eae382d
--- /dev/null
+++ b/src/libtracker-miner/tracker-miner-0.7.vapi
@@ -0,0 +1,82 @@
+/* tracker-miner-0.7.vapi generated by vapigen, do not modify. */
+
+[CCode (cprefix = "Tracker", lower_case_cprefix = "tracker_")]
+namespace Tracker {
+	[CCode (cheader_filename = "libtracker-miner/tracker-miner.h")]
+	public class Miner : GLib.Object {
+		public async void commit (GLib.Cancellable? cancellable = null) throws GLib.Error;
+		public static GLib.Quark error_quark ();
+		public async void execute_batch_update (string sparql, GLib.Cancellable? cancellable = null) throws GLib.Error;
+		public async unowned GLib.PtrArray execute_sparql (string sparql, GLib.Cancellable? cancellable = null) throws GLib.Error;
+		public async void execute_update (string sparql, GLib.Cancellable? cancellable = null) throws GLib.Error;
+		public bool is_started ();
+		public int pause (string reason) throws GLib.Error;
+		public virtual void paused ();
+		public bool resume (int cookie) throws GLib.Error;
+		public virtual void resumed ();
+		public void start ();
+		public virtual void started ();
+		public void stop ();
+		public virtual void stopped ();
+		public virtual void writeback ([CCode (array_length = false)] string[] subjects);
+		public signal void error (GLib.Error e);
+	}
+	[CCode (ref_function = "tracker_miner_fs_ref", unref_function = "tracker_miner_fs_unref", cheader_filename = "libtracker-miner/tracker-miner-fs.h")]
+	public class MinerFS {
+		public void add_directory (GLib.File file, bool recurse);
+		public void add_file (GLib.File file);
+		public virtual bool check_directory (GLib.File directory);
+		public virtual bool check_directory_contents (GLib.File directory, GLib.List<GLib.File> children);
+		public virtual bool check_file (GLib.File file);
+		public double get_throttle ();
+		public virtual bool monitor_directory (GLib.File directory);
+		public void notify_file (GLib.File file, GLib.Error error);
+		public bool remove_directory (GLib.File file);
+		public void set_throttle (double throttle);
+		public signal void finished (double elapsed, uint directories_found, uint directories_ignored, uint files_found, uint files_ignored);
+	}
+	[CCode (cheader_filename = "libtracker-miner/tracker-miner-web.h")]
+	public class MinerWeb : Tracker.Miner {
+		public virtual void associate (GLib.HashTable association_data) throws Tracker.MinerWebError;
+		public virtual Tracker.MinerWebAssociationStatus authenticate () throws Tracker.MinerWebError;
+		public virtual void dissociate () throws Tracker.MinerWebError;
+		public static GLib.Quark error_quark ();
+		public virtual GLib.HashTable get_association_data () throws Tracker.MinerWebError;
+	}
+	[CCode (cheader_filename = "libtracker-miner/tracker-password-provider.h")]
+	public interface PasswordProvider : GLib.Object {
+		public void forget_password (string service) throws GLib.Error;
+		public unowned string get_name ();
+		public string get_password (string service, out string username) throws GLib.Error;
+		public void store_password (string service, string description, string username, string password) throws GLib.Error;
+	}
+	[CCode (cprefix = "TRACKER_MINER_WEB_", has_type_id = "0", cheader_filename = "libtracker-miner/tracker-miner-web.h")]
+	public enum MinerWebAssociationStatus {
+		UNASSOCIATED,
+		ASSOCIATED
+	}
+	[CCode (cprefix = "TRACKER_MINER_WEB_ERROR_", cheader_filename = "libtracker-miner/tracker-miner-web.h")]
+	public errordomain MinerWebError {
+		WRONG_CREDENTIALS,
+		TOKEN_EXPIRED,
+		NO_CREDENTIALS,
+		KEYRING,
+		SERVICE,
+		TRACKER,
+	}
+	[CCode (cprefix = "TRACKER_PASSWORD_PROVIDER_ERROR_", cheader_filename = "libtracker-miner/tracker-password-provider.h")]
+	public errordomain PasswordProviderError {
+		SERVICE,
+		NOTFOUND,
+	}
+	[CCode (cheader_filename = "libtracker-miner/tracker-miner.h")]
+	public const string MINER_DBUS_INTERFACE;
+	[CCode (cheader_filename = "libtracker-miner/tracker-miner.h")]
+	public const string MINER_DBUS_NAME_PREFIX;
+	[CCode (cheader_filename = "libtracker-miner/tracker-miner.h")]
+	public const string MINER_DBUS_PATH_PREFIX;
+	[CCode (cheader_filename = "libtracker-miner/tracker-miner.h")]
+	public const string MINER_WEB_DBUS_INTERFACE;
+	[CCode (cheader_filename = "libtracker-miner/tracker-password-provider.h")]
+	public static unowned Tracker.PasswordProvider get_password_provider ();
+}
diff --git a/src/libtracker-miner/tracker-miner-web-0.7.vapi b/src/libtracker-miner/tracker-miner-web-0.7.vapi
new file mode 100644
index 0000000..55b550a
--- /dev/null
+++ b/src/libtracker-miner/tracker-miner-web-0.7.vapi
@@ -0,0 +1,40 @@
+[CCode (cprefix = "Tracker", lower_case_cprefix = "tracker_")]
+namespace Tracker {
+
+	public errordomain MinerWebError {
+		WRONG_CREDENTIALS,
+		TOKEN_EXPIRED,
+		NO_CREDENTIALS,
+		KEYRING,
+		SERVICE,
+		TRACKER
+	}
+
+	[Compact]
+	[CCode (cheader_filename = "libtracker-miner/tracker-miner-web.h")]
+	public abstract class MinerWeb : Miner {
+		public abstract void authenticate () throws GLib.Error;
+		public abstract GLib.HashTable<string, string> get_association_data () throws GLib.Error;
+		public abstract void associate (GLib.HashTable<string, string> association_data) throws GLib.Error;
+		public abstract void dissociate () throws GLib.Error;
+
+		[CCode (cprefix = "TRACKER_MINER_WEB_")]
+		public enum AssociationStatus {
+			UNASSOCIATED,
+			ASSOCIATED
+		}
+		public AssociationStatus association_status {
+			get {
+				AssociationStatus s;
+				this.get ("association-status", out s);
+				return s;
+			}
+			set {
+				this.set ("association-status", association_status);
+			}
+		}
+	}
+
+	public const string MINER_WEB_DBUS_INTERFACE;
+	public const string MINER_WEB_ERROR_DOMAIN;
+}
diff --git a/src/libtracker-miner/tracker-miner-web-dbus.h b/src/libtracker-miner/tracker-miner-web-dbus.h
new file mode 100644
index 0000000..cefce86
--- /dev/null
+++ b/src/libtracker-miner/tracker-miner-web-dbus.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, Nokia (urho konttori nokia com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKERMINER_WEB_DBUS_H__
+#define __LIBTRACKERMINER_WEB_DBUS_H__
+
+#include <glib-object.h>
+
+#include "tracker-miner-dbus.h"
+#include "tracker-miner-web.h"
+
+G_BEGIN_DECLS
+
+void tracker_miner_web_dbus_authenticate         (TrackerMinerWeb        *miner,
+                                                  DBusGMethodInvocation  *context,
+                                                  GError                **error);
+void tracker_miner_web_dbus_get_association_data (TrackerMinerWeb        *miner,
+                                                  DBusGMethodInvocation  *context,
+                                                  GError                **error);
+void tracker_miner_web_dbus_associate            (TrackerMinerWeb        *miner,
+                                                  const GHashTable       *association_data,
+                                                  DBusGMethodInvocation  *context,
+                                                  GError                **error);
+void tracker_miner_web_dbus_dissociate           (TrackerMinerWeb        *miner,
+                                                  DBusGMethodInvocation  *context,
+                                                  GError                **error);
+
+G_END_DECLS
+
+#endif /* __LIBTRACKERMINER_DBUS_H__ */
diff --git a/src/libtracker-miner/tracker-miner-web.c b/src/libtracker-miner/tracker-miner-web.c
new file mode 100644
index 0000000..a948074
--- /dev/null
+++ b/src/libtracker-miner/tracker-miner-web.c
@@ -0,0 +1,249 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, Adrien Bustany (abustany gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "tracker-miner-web.h"
+#include "tracker-dbus.h"
+#include "tracker-miner-web-dbus.h"
+#include "tracker-miner-web-glue.h"
+
+/**
+ * SECTION:tracker-miner-web
+ * @short_description: Abstract base class for miners using web services
+ * @include: libtracker-miner/tracker-miner-web.h
+ *
+ * #TrackerMinerWeb is an abstract base class for miners retrieving data
+ * from web services. It's a very thin layer above #TrackerMiner, only
+ * adding virtual methods needed to handle association with the remote
+ * service.
+ **/
+
+#define TRACKER_MINER_WEB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_MINER_WEB, TrackerMinerWebPrivate))
+
+struct TrackerMinerWebPrivate {
+	TrackerMinerWebAssociationStatus association_status;
+};
+
+enum {
+	PROP_0,
+	PROP_ASSOCIATION_STATUS
+};
+
+static void miner_web_set_property (GObject *     object,
+                                    guint         param_id,
+									const GValue *value,
+									GParamSpec   *pspec);
+static void miner_web_get_property (GObject *     object,
+                                    guint         param_id,
+									GValue       *value,
+									GParamSpec   *pspec);
+static void miner_web_constructed  (GObject *object);
+static void miner_web_finalize     (GObject *object);
+
+G_DEFINE_ABSTRACT_TYPE (TrackerMinerWeb, tracker_miner_web, TRACKER_TYPE_MINER)
+
+static void
+tracker_miner_web_class_init (TrackerMinerWebClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->set_property = miner_web_set_property;
+	object_class->get_property = miner_web_get_property;
+	object_class->constructed  = miner_web_constructed;
+	object_class->finalize     = miner_web_finalize;
+
+	g_object_class_install_property (object_class,
+	                                 PROP_ASSOCIATION_STATUS,
+	                                 g_param_spec_uint ("association-status",
+	                                                    "Association status",
+	                                                    "Tells if the miner is associated with the remote service",
+	                                                    TRACKER_MINER_WEB_UNASSOCIATED, /* min value */
+	                                                    TRACKER_MINER_WEB_ASSOCIATED,   /* max value */
+	                                                    TRACKER_MINER_WEB_UNASSOCIATED, /* default value */
+														G_PARAM_READWRITE));
+
+	g_type_class_add_private (object_class, sizeof (TrackerMinerWebPrivate));
+}
+
+static void
+tracker_miner_web_init (TrackerMinerWeb *miner)
+{
+	TrackerMinerWebPrivate *priv;
+	miner->private = priv = TRACKER_MINER_WEB_GET_PRIVATE (miner);
+	priv->association_status = TRACKER_MINER_WEB_UNASSOCIATED;
+}
+
+static void
+miner_web_set_property (GObject      *object,
+                        guint         param_id,
+						const GValue *value,
+						GParamSpec   *pspec)
+{
+	TrackerMinerWeb *miner = TRACKER_MINER_WEB (object);
+	switch (param_id) {
+		case PROP_ASSOCIATION_STATUS:
+			miner->private->association_status = g_value_get_uint (value);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+			break;
+	}
+}
+
+static void
+miner_web_get_property (GObject      *object,
+                        guint         param_id,
+						GValue       *value,
+						GParamSpec   *pspec)
+{
+	TrackerMinerWeb *miner = TRACKER_MINER_WEB (object);
+	switch (param_id) {
+		case PROP_ASSOCIATION_STATUS:
+			g_value_set_uint (value, miner->private->association_status);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+			break;
+	}
+}
+
+static void
+miner_web_constructed (GObject *object)
+{
+	tracker_miner_dbus_init (TRACKER_MINER (object),
+	                         &dbus_glib_tracker_miner_web_dbus_object_info);
+
+	G_OBJECT_CLASS (tracker_miner_web_parent_class)->constructed (object);
+}
+
+static void
+miner_web_finalize (GObject *object)
+{
+	/* TrackerMinerWeb *miner = TRACKER_MINER_WEB (object); */
+
+	G_OBJECT_CLASS (tracker_miner_web_parent_class)->finalize (object);
+}
+
+GQuark
+tracker_miner_web_error_quark (void)
+{
+	return g_quark_from_static_string (TRACKER_MINER_WEB_ERROR_DOMAIN);
+}
+
+void
+tracker_miner_web_dbus_authenticate (TrackerMinerWeb        *miner,
+                                     DBusGMethodInvocation  *context,
+                                     GError                **error)
+{
+	g_assert (TRACKER_IS_MINER_WEB (miner));
+
+	GError *local_error = NULL;
+	TRACKER_MINER_WEB_GET_CLASS (miner)->authenticate (miner, &local_error);
+
+	if (local_error != NULL) {
+		dbus_g_method_return_error (context, local_error);
+	} else {
+		dbus_g_method_return (context);
+	}
+}
+
+void
+tracker_miner_web_dbus_get_association_data (TrackerMinerWeb        *miner,
+                                        DBusGMethodInvocation  *context,
+                                        GError                **error)
+{
+	g_assert (TRACKER_IS_MINER_WEB (miner));
+
+	GError *local_error = NULL;
+	GHashTable *association_data = TRACKER_MINER_WEB_GET_CLASS (miner)->get_association_data (miner, &local_error);
+
+	if (local_error != NULL) {
+		dbus_g_method_return_error (context, local_error);
+	} else {
+		dbus_g_method_return (context, association_data);
+	}
+}
+
+void
+tracker_miner_web_dbus_associate (TrackerMinerWeb        *miner,
+                             const GHashTable       *association_data,
+                             DBusGMethodInvocation  *context,
+                             GError                **error)
+{
+	g_assert (TRACKER_IS_MINER_WEB (miner));
+
+	GError *local_error = NULL;
+	TRACKER_MINER_WEB_GET_CLASS (miner)->associate (miner, association_data, &local_error);
+
+	if (local_error != NULL) {
+		dbus_g_method_return_error (context, local_error);
+	} else {
+		dbus_g_method_return (context);
+	}
+}
+
+void
+tracker_miner_web_dbus_dissociate (TrackerMinerWeb        *miner,
+                                   DBusGMethodInvocation  *context,
+                                   GError                **error)
+{
+	g_assert (TRACKER_IS_MINER_WEB (miner));
+
+	GError *local_error = NULL;
+	TRACKER_MINER_WEB_GET_CLASS (miner)->dissociate (miner, &local_error);
+
+	if (local_error != NULL) {
+		dbus_g_method_return_error (context, local_error);
+	} else {
+		dbus_g_method_return (context);
+	}
+}
+
+TrackerMinerWebAssociationStatus
+tracker_miner_web_authenticate (TrackerMinerWeb     *miner,
+                                GError             **error)
+{
+	g_assert (TRACKER_IS_MINER_WEB (miner));
+	return TRACKER_MINER_WEB_GET_CLASS (miner)->authenticate (miner, error);
+}
+
+GHashTable*
+tracker_miner_web_get_association_data (TrackerMinerWeb     *miner,
+                                        GError             **error)
+{
+	g_assert (TRACKER_IS_MINER_WEB (miner));
+	return TRACKER_MINER_WEB_GET_CLASS (miner)->get_association_data (miner, error);
+}
+
+void
+tracker_miner_web_associate (TrackerMinerWeb   *miner,
+                             const GHashTable  *association_data,
+                             GError           **error)
+{
+	g_assert (TRACKER_IS_MINER_WEB (miner));
+	TRACKER_MINER_WEB_GET_CLASS (miner)->associate (miner, association_data, error);
+}
+
+void
+tracker_miner_web_dissociate (TrackerMinerWeb   *miner,
+                              GError           **error)
+{
+	g_assert (TRACKER_IS_MINER_WEB (miner));
+	TRACKER_MINER_WEB_GET_CLASS (miner)->dissociate (miner, error);
+}
diff --git a/src/libtracker-miner/tracker-miner-web.deps.in b/src/libtracker-miner/tracker-miner-web.deps.in
new file mode 100644
index 0000000..0f1fc53
--- /dev/null
+++ b/src/libtracker-miner/tracker-miner-web.deps.in
@@ -0,0 +1 @@
+tracker-miner- VERSION@
diff --git a/src/libtracker-miner/tracker-miner-web.h b/src/libtracker-miner/tracker-miner-web.h
new file mode 100644
index 0000000..7ee7501
--- /dev/null
+++ b/src/libtracker-miner/tracker-miner-web.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, Adrien Bustany (abustany gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_MINER_WEB_H__
+#define __TRACKER_MINER_WEB_H__
+
+#include <libtracker-miner/tracker-miner.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_MINER_WEB	   (tracker_miner_web_get_type())
+#define TRACKER_MINER_WEB(o)	   (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_MINER_WEB, TrackerMinerWeb))
+#define TRACKER_MINER_WEB_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),    TRACKER_TYPE_MINER_WEB, TrackerMinerWebClass))
+#define TRACKER_IS_MINER_WEB(o)	   (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_MINER_WEB))
+#define TRACKER_IS_MINER_WEB_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),    TRACKER_TYPE_MINER_WEB))
+#define TRACKER_MINER_WEB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_MINER_WEB, TrackerMinerWebClass))
+
+typedef struct TrackerMinerWeb TrackerMinerWeb;
+typedef struct TrackerMinerWebPrivate TrackerMinerWebPrivate;
+
+/**
+ * The name of the DBus interface exposed by the web miners
+ **/
+#define TRACKER_MINER_WEB_DBUS_INTERFACE "org.freedesktop.Tracker1.MinerWeb"
+
+/**
+ * TrackerMinerWebError:
+ * @TRACKER_MINER_WEB_ERROR_WRONG_CREDENTIALS: The stored credentials are refused by the remote service
+ * @TRACKER_MINER_WEB_ERROR_TOKEN_EXPIRED    : The service says the stored token has expired
+ * @TRACKER_MINER_WEB_ERROR_NO_CREDENTIALS   : There are currenty no credentials stored for this service
+ * @TRACKER_MINER_WEB_ERROR_KEYRING          : Error while contacting the credentials storage
+ * @TRACKER_MINER_WEB_ERROR_SERVICE          : Error while contacting the remote service
+ * @TRACKER_MINER_WEB_ERROR_TRACKER          : Error while contacting Tracker
+ *
+ * Describes the different errors that can occur while operating with the remote service.
+ **/
+typedef enum {
+	TRACKER_MINER_WEB_ERROR_WRONG_CREDENTIALS,
+	TRACKER_MINER_WEB_ERROR_TOKEN_EXPIRED,
+	TRACKER_MINER_WEB_ERROR_NO_CREDENTIALS,
+	TRACKER_MINER_WEB_ERROR_KEYRING,
+	TRACKER_MINER_WEB_ERROR_SERVICE,
+	TRACKER_MINER_WEB_ERROR_TRACKER
+} TrackerMinerWebError;
+#define TRACKER_MINER_WEB_ERROR        tracker_miner_web_error_quark ()
+#define TRACKER_MINER_WEB_ERROR_DOMAIN "TrackerMinerWeb"
+
+/**
+ * TrackerMinerWebAssociationStatus:
+ * @TRACKER_MINER_WEB_UNASSOCIATED : The miner is currently unassociated with the remote service
+ * @TRACKER_MINER_WEB_ASSOCIATED   : The miner is currently associated with the remote service
+ *
+ * Describes if the miner can currently communicate (import data) with the web service.
+ **/
+typedef enum {
+	TRACKER_MINER_WEB_UNASSOCIATED,
+	TRACKER_MINER_WEB_ASSOCIATED
+} TrackerMinerWebAssociationStatus;
+
+struct TrackerMinerWeb {
+	TrackerMiner            parent_instance;
+	/*< private >*/
+	TrackerMinerWebPrivate *private;
+};
+
+/**
+ * TrackerMinerWebClass
+ * @parent_class        : parent object class
+ * @authenticate        : called when the miner is told to authenticate against the remote service
+ * @get_association_data: called when one requests the miner's association data.
+ *                        In the case of a user/pass based authentication, this should return
+ *                        an empty hash table.
+ *                        In the case of a token based authentication, the following keys must
+ *                        be defined in the returned hash table:
+ *                        - url: the url to point the user too
+ *                        Additionally, the miner can define the following keys :
+ *                        - post_message: a message to display after the user completes the
+ *                                        association process
+ *                        - post_url: a url to point the user after he completes the association
+ *
+ *                        If both post_message and post_url are defined, the message will be
+ *                        displayed before the url is opened.
+ * @associate           : called when the miner is told to associate with the web service.
+ *                        In the case of a user/pass based authentication, the following keys must be defined
+ *                        - username: the provided username
+ *                        - password: the provided password
+ *                        In the case of a token based authentication, the hash table can be ignored
+ *                        since it will be empty.
+ * @dissociate          : called when the miner is told to forget any user credentials it has stored
+ **/
+typedef struct {
+        TrackerMinerClass parent_class;
+
+        /* vmethods */
+        TrackerMinerWebAssociationStatus (* authenticate)         (TrackerMinerWeb     *miner,
+		                                                           GError             **error);
+		GHashTable*                      (* get_association_data) (TrackerMinerWeb     *miner,
+		                                                           GError             **error);
+		void                             (* associate)            (TrackerMinerWeb     *miner,
+		                                                           const GHashTable    *association_data,
+		                                                           GError             **error);
+		void                             (* dissociate)           (TrackerMinerWeb     *miner,
+		                                                           GError             **error);
+} TrackerMinerWebClass;
+
+GType                            tracker_miner_web_get_type              (void) G_GNUC_CONST;
+GQuark                           tracker_miner_web_error_quark           (void);
+
+TrackerMinerWebAssociationStatus tracker_miner_web_authenticate          (TrackerMinerWeb     *miner,
+                                                                          GError             **error);
+GHashTable*                      tracker_miner_web_get_association_data  (TrackerMinerWeb     *miner,
+                                                                          GError             **error);
+void                             tracker_miner_web_associate             (TrackerMinerWeb     *miner,
+                                                                          const GHashTable    *association_data,
+                                                                          GError             **error);
+void                             tracker_miner_web_dissociate            (TrackerMinerWeb     *miner,
+                                                                          GError             **error);
+
+G_END_DECLS
+
+#endif /* __TRACKER_MINER_WEB_H__ */
diff --git a/src/libtracker-miner/tracker-miner.c b/src/libtracker-miner/tracker-miner.c
index cd7f04e..7194296 100644
--- a/src/libtracker-miner/tracker-miner.c
+++ b/src/libtracker-miner/tracker-miner.c
@@ -433,8 +433,8 @@ miner_constructed (GObject *object)
 
 static void
 store_name_monitor_cb (TrackerMiner *miner,
-		       const gchar  *name,
-		       gboolean      available)
+                       const gchar  *name,
+                       gboolean      available)
 {
 	GError *error = NULL;
 
diff --git a/src/libtracker-miner/tracker-password-provider.c b/src/libtracker-miner/tracker-password-provider.c
new file mode 100644
index 0000000..1172911
--- /dev/null
+++ b/src/libtracker-miner/tracker-password-provider.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2009, Adrien Bustany (abustany gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "tracker-password-provider.h"
+
+#include "config.h"
+
+/* for mlock */
+#include <sys/mman.h>
+
+static void
+tracker_password_provider_init (gpointer object_class)
+{
+	static gboolean is_initialized = FALSE;
+
+	if (!is_initialized) {
+		g_object_interface_install_property (object_class,
+		                                     g_param_spec_string ("name",
+		                                                          "Password provider name",
+		                                                          "Password provider name",
+		                                                          NULL,
+		                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		is_initialized = TRUE;
+	}
+}
+
+GType
+tracker_password_provider_get_type (void)
+{
+	static GType iface_type = 0;
+
+	if (iface_type == 0) {
+		static const GTypeInfo info = {
+			sizeof (TrackerPasswordProviderIface),
+			tracker_password_provider_init,
+			NULL
+		};
+
+		iface_type = g_type_register_static (G_TYPE_INTERFACE,
+		                                     "TrackerPasswordProvider",
+		                                     &info,
+		                                     0);
+	}
+
+	return iface_type;
+}
+
+GQuark
+tracker_password_provider_error_quark (void)
+{
+	return g_quark_from_static_string (TRACKER_PASSWORD_PROVIDER_ERROR_DOMAIN);
+}
+
+gchar*
+tracker_password_provider_get_name (TrackerPasswordProvider *provider)
+{
+	g_assert (TRACKER_IS_PASSWORD_PROVIDER (provider));
+
+	gchar *name;
+	g_object_get (provider,
+	              "name", &name,
+	              NULL);
+
+	return name;
+}
+
+void
+tracker_password_provider_store_password (TrackerPasswordProvider   *provider,
+                                           const gchar               *service,
+                                           const gchar               *description,
+                                           const gchar               *username,
+                                           const gchar               *password,
+                                           GError                   **error)
+{
+	g_assert (TRACKER_IS_PASSWORD_PROVIDER (provider));
+
+	TRACKER_PASSWORD_PROVIDER_GET_INTERFACE (provider)->store_password (provider,
+	                                                                    service,
+	                                                                    description,
+	                                                                    username,
+	                                                                    password,
+	                                                                    error);
+}
+
+gchar*
+tracker_password_provider_get_password (TrackerPasswordProvider   *provider,
+                                        const gchar               *service,
+                                        gchar                    **username,
+                                        GError                   **error)
+{
+	g_assert (TRACKER_IS_PASSWORD_PROVIDER (provider));
+
+	gchar *password = TRACKER_PASSWORD_PROVIDER_GET_INTERFACE (provider)->get_password (provider,
+	                                                                                    service,
+	                                                                                    username,
+	                                                                                    error);
+	/* This locking is not perfect since the process can be swapped before the mlock call...
+	 * gnome-keyring has a better implementation using a pool of locked memory to allocate the
+	 * password, but it's much more complex :-/
+	 */
+	mlock (password, sizeof (password));
+	return password;
+}
+
+void
+tracker_password_provider_forget_password (TrackerPasswordProvider   *provider,
+                                           const gchar               *service,
+                                           GError                   **error)
+{
+	g_assert (TRACKER_IS_PASSWORD_PROVIDER (provider));
+
+	TRACKER_PASSWORD_PROVIDER_GET_INTERFACE (provider)->forget_password (provider,
+	                                                                     service,
+	                                                                     error);
+}
diff --git a/src/libtracker-miner/tracker-password-provider.h b/src/libtracker-miner/tracker-password-provider.h
new file mode 100644
index 0000000..ff6ae66
--- /dev/null
+++ b/src/libtracker-miner/tracker-password-provider.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2009, Adrien Bustany (abustany gnome org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TRACKER_PASSWORD_PROVIDER_H__
+#define __TRACKER_PASSWORD_PROVIDER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_PASSWORD_PROVIDER         (tracker_password_provider_get_type())
+#define TRACKER_PASSWORD_PROVIDER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_PASSWORD_PROVIDER, TrackerPasswordProvider))
+#define TRACKER_IS_PASSWORD_PROVIDER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_PASSWORD_PROVIDER))
+#define TRACKER_PASSWORD_PROVIDER_GET_INTERFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o),  TRACKER_TYPE_PASSWORD_PROVIDER, TrackerPasswordProviderIface))
+
+#define TRACKER_PASSWORD_PROVIDER_ERROR_DOMAIN  "TrackerPasswordProvider"
+#define TRACKER_PASSWORD_PROVIDER_ERROR         tracker_password_provider_error_quark()
+
+typedef struct TrackerPasswordProvider TrackerPasswordProvider;
+typedef struct TrackerPasswordProviderIface TrackerPasswordProviderIface;
+
+typedef enum {
+	TRACKER_PASSWORD_PROVIDER_ERROR_SERVICE,
+	TRACKER_PASSWORD_PROVIDER_ERROR_NOTFOUND
+} TrackerPasswordProviderError;
+
+struct TrackerPasswordProviderIface
+{
+	GTypeInterface parent_iface;
+
+	void     (* store_password)        (TrackerPasswordProvider  *provider,
+	                                    const gchar              *service,
+	                                    const gchar              *description,
+	                                    const gchar              *username,
+	                                    const gchar              *password,
+	                                    GError                  **error);
+	gchar*   (* get_password)          (TrackerPasswordProvider  *provider,
+	                                    const gchar              *service,
+	                                    gchar                   **username,
+	                                    GError                  **error);
+	void     (* forget_password)       (TrackerPasswordProvider  *provider,
+	                                    const gchar              *service,
+	                                    GError                  **error);
+};
+
+GType                          tracker_password_provider_get_type        (void) G_GNUC_CONST;
+GQuark                         tracker_password_provider_error_quark     (void);
+
+gchar*                         tracker_password_provider_get_name        (TrackerPasswordProvider   *provider);
+/* Must be defined by the selected implementation */
+const TrackerPasswordProvider* tracker_get_password_provider             (void);
+void                           tracker_password_provider_store_password  (TrackerPasswordProvider   *provider,
+	                                                                      const gchar              *service,
+	                                                                      const gchar              *description,
+	                                                                      const gchar              *username,
+	                                                                      const gchar              *password,
+	                                                                      GError                  **error);
+
+gchar*                         tracker_password_provider_get_password    (TrackerPasswordProvider   *provider,
+	                                                                      const gchar              *service,
+	                                                                      gchar                   **username,
+	                                                                      GError                  **error);
+void                           tracker_password_provider_forget_password (TrackerPasswordProvider   *provider,
+	                                                                      const gchar              *service,
+	                                                                      GError                  **error);
+G_END_DECLS
+
+#endif
diff --git a/src/libtracker-miner/tracker-password-provider.vala b/src/libtracker-miner/tracker-password-provider.vala
new file mode 100644
index 0000000..97acca0
--- /dev/null
+++ b/src/libtracker-miner/tracker-password-provider.vala
@@ -0,0 +1,32 @@
+namespace Tracker {
+	[CCode (cheader_filename = "libtracker-miner/tracker-password-provider.h")]
+	public errordomain PasswordProviderError {
+		SERVICE, // The service couldn't be contacted
+		NOTFOUND, // No results were found during a call to get
+	}
+
+	private static PasswordProvider _password_provider_instance = null;
+
+	[CCode (cheader_filename = "libtracker-miner/tracker-password-provider.h")]
+	public interface PasswordProvider : GLib.Object {
+		public abstract string name { get; }
+		public abstract void store_password (string service, string description, string username, string password) throws PasswordProviderError;
+		public abstract string @get_password (string service, out string? username) throws PasswordProviderError;
+		public abstract void forget_password (string service) throws PasswordProviderError;
+
+		public static PasswordProvider password_provider {
+			get {
+				if (_password_provider_instance == null) {
+#if HAVE_GNOME_KEYRING
+					_password_provider_instance = new GnomePasswordProvider ();
+#else
+					_password_provider_instance = new KeyFilePasswordProvider ();
+#endif
+				}
+
+				assert (_password_provider_instance != null);
+				return _password_provider_instance;
+			}
+		}
+	}
+}
diff --git a/src/libtracker-miner/vapi/rebuild_vapi.sh b/src/libtracker-miner/vapi/rebuild_vapi.sh
new file mode 100755
index 0000000..5da0280
--- /dev/null
+++ b/src/libtracker-miner/vapi/rebuild_vapi.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+cd $(dirname $0)/..
+echo vala-gen-introspect
+vala-gen-introspect tracker-miner-0.7 vapi
+echo vapigen
+vapigen --library tracker-miner-0.7 --metadata vapi/tracker-miner-0.7.metadata vapi/tracker-miner-0.7.gi vapi/tracker-miner-0.7-custom.vala
diff --git a/src/libtracker-miner/vapi/tracker-miner-0.7-custom.vala b/src/libtracker-miner/vapi/tracker-miner-0.7-custom.vala
new file mode 100644
index 0000000..baa973f
--- /dev/null
+++ b/src/libtracker-miner/vapi/tracker-miner-0.7-custom.vala
@@ -0,0 +1,46 @@
+namespace Tracker {
+	[CCode (cheader_filename="libtracker-miner/tracker-miner.h")]
+	public class Miner : GLib.Object {
+		public async void commit (GLib.Cancellable? cancellable = null) throws GLib.Error;
+		public async void execute_batch_update (string sparql, GLib.Cancellable? cancellable = null) throws GLib.Error;
+		public async unowned GLib.PtrArray execute_sparql (string sparql, GLib.Cancellable? cancellable = null) throws GLib.Error;
+		public async void execute_update (string sparql, GLib.Cancellable? cancellable = null) throws GLib.Error;
+
+		public virtual void started ();
+		public virtual void stopped ();
+		public virtual void paused ();
+		public virtual void resumed ();
+		public virtual void writeback ([CCode (array_length = false, array_null_terminated = true)] string[] subjects);
+
+		public signal void error (GLib.Error e);
+	}
+
+	[CCode (cheader_filename = "libtracker-miner/tracker-miner-fs.h")]
+	public class MinerFS {
+		public virtual bool check_file (GLib.File file);
+		public virtual bool check_directory (GLib.File directory);
+		public virtual bool check_directory_contents (GLib.File directory, GLib.List<GLib.File> children);
+		public virtual bool monitor_directory (GLib.File directory);
+		// Commented out since we don't have a Tracker.SparqlBuilder binding yet
+		//public virtual bool process_file (GLib.File file, Tracker.SparqlBuilder builder, GLib.Cancellable cancellable);
+		//public virtual bool writeback_file (GLib.File file, Tracker.SparqlBuilder builder, GLib.Cancellable cancellable);
+
+		public signal void finished (double elapsed, uint directories_found, uint directories_ignored, uint files_found, uint files_ignored);
+	}
+
+	[CCode (cheader_filename="libtracker-miner/tracker-miner-web.h")]
+	public class MinerWeb : Miner {
+		public virtual void associate (GLib.HashTable association_data) throws MinerWebError;
+		public virtual Tracker.MinerWebAssociationStatus authenticate () throws MinerWebError;
+		public virtual void dissociate () throws MinerWebError;
+		public virtual GLib.HashTable get_association_data () throws MinerWebError;
+	}
+
+	[CCode (cheader_filename = "libtracker-miner/tracker-password-provider.h")]
+	public interface PasswordProvider : GLib.Object {
+		public void store_password (string service, string description, string username, string password) throws GLib.Error;
+		public string get_password (string service, out string username) throws GLib.Error;
+		public void forget_password (string service) throws GLib.Error;
+		public unowned string get_name ();
+	}
+}
diff --git a/src/libtracker-miner/vapi/tracker-miner-0.7.deps b/src/libtracker-miner/vapi/tracker-miner-0.7.deps
new file mode 100644
index 0000000..cd10dfd
--- /dev/null
+++ b/src/libtracker-miner/vapi/tracker-miner-0.7.deps
@@ -0,0 +1 @@
+gio-2.0
diff --git a/src/libtracker-miner/vapi/tracker-miner-0.7.files b/src/libtracker-miner/vapi/tracker-miner-0.7.files
new file mode 100644
index 0000000..0dc1d20
--- /dev/null
+++ b/src/libtracker-miner/vapi/tracker-miner-0.7.files
@@ -0,0 +1 @@
+include/tracker-0.7/libtracker-miner/
diff --git a/src/libtracker-miner/vapi/tracker-miner-0.7.metadata b/src/libtracker-miner/vapi/tracker-miner-0.7.metadata
new file mode 100644
index 0000000..f1ec439
--- /dev/null
+++ b/src/libtracker-miner/vapi/tracker-miner-0.7.metadata
@@ -0,0 +1,37 @@
+TrackerMiner cheader_filename="libtracker-miner/tracker-miner.h"
+TrackerMinerWeb cheader_filename="libtracker-miner/tracker-miner-web.h"
+TrackerMinerWebAssociationStatus cheader_filename="libtracker-miner/tracker-miner-web.h"
+TRACKER_MINER_* cheader_filename="libtracker-miner/tracker-miner.h"
+TrackerMinerFS cheader_filename="libtracker-miner/tracker-miner-fs.h"
+TrackerMiner.parent_instance hidden="1"
+TrackerMinerFS.parent hidden="1"
+TrackerMinerClass hidden="1"
+tracker_miner_commit hidden="1"
+tracker_miner_commit_finish hidden="1"
+tracker_miner_execute_batch_update hidden="1"
+tracker_miner_execute_batch_update_finish hidden="1"
+tracker_miner_execute_sparql hidden="1"
+tracker_miner_execute_sparql_finish hidden="1"
+tracker_miner_execute_update hidden="1"
+tracker_miner_execute_update_finish hidden="1"
+tracker_miner_writeback hidden="1"
+TrackerMinerFS.private hidden="1"
+TrackerMinerFSClass hidden="1"
+TrackerMinerFSDoneCb hidden="1"
+tracker_miner_dbus_* hidden="1"
+TrackerMiner.private hidden="1"
+TrackerMinerWeb.private hidden="1"
+TrackerMinerWeb.parent_instance hidden="1"
+TrackerMinerWebClass hidden="1"
+TrackerMinerWebError errordomain="1" cheader_filename="libtracker-miner/tracker-miner-web.h"
+tracker_miner_web_associate hidden="1"
+tracker_miner_web_dissociate hidden="1"
+tracker_miner_web_authenticate hidden="1"
+tracker_miner_web_get_association_data hidden="1"
+TrackerPasswordProviderIface hidden="1"
+TrackerPasswordProviderError errordomain="1" cheader_filename="libtracker-miner/tracker-password-provider.h"
+TrackerPasswordProvider hidden="1"
+TRACKER_MINER_ERROR_DOMAIN hidden="1"
+TRACKER_MINER_WEB_ERROR_DOMAIN hidden="1"
+TRACKER_PASSWORD_PROVIDER_ERROR_DOMAIN hidden="1"
+tracker_get_password_provider cheader_filename="libtracker-miner/tracker-password-provider.h"
diff --git a/src/libtracker-miner/vapi/tracker-miner-0.7.namespace b/src/libtracker-miner/vapi/tracker-miner-0.7.namespace
new file mode 100644
index 0000000..7bd7aa0
--- /dev/null
+++ b/src/libtracker-miner/vapi/tracker-miner-0.7.namespace
@@ -0,0 +1 @@
+Tracker
diff --git a/src/tracker-miner-facebook/Makefile.am b/src/tracker-miner-facebook/Makefile.am
new file mode 100644
index 0000000..5a67729
--- /dev/null
+++ b/src/tracker-miner-facebook/Makefile.am
@@ -0,0 +1,66 @@
+include $(top_srcdir)/Makefile.decl
+
+AM_CPPFLAGS = \
+	-include $(CONFIG_HEADER)
+
+VALAINCLUDES= \
+	--vapidir $(top_srcdir)/src/libtracker-miner \
+	--pkg posix \
+	--pkg dbus-glib-1 \
+	--pkg tracker-miner-0.7 \
+	--pkg rest \
+	--pkg uuid \
+	--thread
+
+libexec_PROGRAMS=tracker-miner-facebook
+
+tracker_miner_facebook_VALASOURCES= \
+	facebook.vala
+
+tracker_miner_facebook_SOURCES= \
+	$(tracker_miner_facebook_VALASOURCES:.vala=.c)
+
+tracker-miner-facebook.vala.stamp: $(tracker_miner_facebook_VALASOURCES)
+	$(VALAC) -C $(VALAINCLUDES) $(VALAFLAGS) $^
+	touch $@
+
+INCLUDES = \
+	-Wall								\
+	-DSHAREDIR=\""$(datadir)"\"					\
+	-DPKGLIBDIR=\""$(libdir)/tracker"\"				\
+	-DLOCALEDIR=\""$(localedir)"\" 					\
+	-DLIBEXEC_PATH=\""$(libexecdir)"\"				\
+	-DG_LOG_DOMAIN=\"Tracker\"					\
+	-DTRACKER_COMPILATION						\
+	-I$(top_srcdir)/src						\
+	$(WARN_CFLAGS)                          \
+	$(GMODULE_CFLAGS)                          \
+	$(GLIB2_CFLAGS)                         \
+	$(GOBJECT_CFLAGS)                       \
+	$(GTHREAD_CFLAGS)                       \
+	$(DBUS_CFLAGS)                          \
+	$(LIBREST_CFLAGS)                          \
+	$(UUID_CFLAGS)
+
+tracker_miner_facebook_LDADD= \
+	$(top_builddir)/src/libtracker-miner/libtracker-miner-0.7.la	\
+	$(GLIB2_LIBS)                         \
+	$(GOBJECT_LIBS)                       \
+	$(GTHREAD_LIBS)                       \
+	$(DBUS_LIBS)                          \
+	$(LIBREST_LIBS)                          \
+	$(UUID_LIBS)
+
+BUILT_SOURCES= \
+	tracker-miner-facebook.vala.stamp
+
+CLEANFILES= $(BUILT_SOURCES)
+
+EXTRA_DIST= \
+	tracker-miner-facebook.vala.stamp \
+	$(tracker_miner_facebook_SOURCES) \
+	$(tracker_miner_facebook_VALASOURCES)
+
+MAINTAINERCLEANFILES= \
+	$(tracker_miner_facebook_SOURCES) \
+	tracker-miner-facebook.vala.stamp
diff --git a/src/tracker-miner-facebook/facebook.vala b/src/tracker-miner-facebook/facebook.vala
new file mode 100644
index 0000000..9c31778
--- /dev/null
+++ b/src/tracker-miner-facebook/facebook.vala
@@ -0,0 +1,719 @@
+using Tracker;
+using Rest;
+
+public class FacebookMiner : Tracker.MinerWeb {
+	private const string SERVICE_NAME = "Facebook";
+	private const string SERVICE_DESCRIPTION = "Authentication token for Facebook miner";
+	private const string API_KEY = "a07366931355e51525938ade2d0df2fb";
+	private const string SHARED_SECRET = "dd34c9d53460953bfd3b5aa87c09b538";
+	private const string FACEBOOK_REST = "https://api.facebook.com/restserver.php";;
+	private const string REST_ERRORMSG = "Error during REST call : %s";
+	private const string MINER_DATASOURCE_URN = "urn:nepomuk:datasource:40d8b787-3de2-46d3-984c-1b021a996ef9";
+
+	private const uint update_interval = 600; // in seconds
+
+	private unowned PasswordProvider password_provider;
+
+	private Proxy rest;
+	private string auth_token;
+	private string session = null;
+	private string secret = SHARED_SECRET;
+
+	private string username = null; // Is actually the numeric user ID
+
+	private uint timeout_handle = 0;
+
+	construct {
+		// Set name for Tracker.Miner
+		set ("name", SERVICE_NAME);
+
+		password_provider = get_password_provider ();
+	}
+
+	public FacebookMiner ()
+	{
+		rest = new Proxy (FACEBOOK_REST, false);
+	}
+
+	// Tracker.Miner functions
+	public override void started ()
+	{
+		message ("Initializing miner...");
+
+		try {
+			authenticate ();
+		} catch (Error e) {
+			warning ("Error while authenticating : %s", e.message);
+		}
+	}
+
+	public override void stopped ()
+	{
+	}
+
+	public override void paused ()
+	{
+	}
+
+	public override void resumed ()
+	{
+	}
+
+	public override void writeback ([CCode (array_length = false, array_null_terminated = true)] string [] subjects)
+	{
+	}
+
+	// Tracker.MinerWeb
+	public override HashTable<string, string> get_association_data () throws MinerWebError
+	{
+		var ret = new HashTable<string, string> (str_hash, str_equal);
+		ProxyCall c = rest.new_call ();
+		c.add_param ("method", "auth.createToken");
+		XmlNode node;
+		try {
+			node = runCall (c);
+		} catch (MinerWebError e) {
+			warning ("Error during REST call : %s", e.message);
+			throw e;
+		}
+
+		if (node.name != "auth_createToken_response") {
+#if DEBUG
+			warning (_("Got answer %s\n", c.get_payload ()));
+			warning (_("Couldn't get authentication token"));
+			//error (new MinerWebError.SERVICE (_("Couldn't get authentication token")));
+#endif
+			return ret;
+		}
+
+		auth_token = node.content;
+		string url = "http://www.facebook.com/login.php?api_key=%s&v=1.0&auth_token=%s".printf (API_KEY, auth_token);
+		ret.insert ("url", url);
+
+		ret.insert ("post_message",
+		          _("A last browser window will now open, which will allow you to grant"
+		          + "Tracker permanent access to your Facebook account, as well as access"
+		          + "to your stream (statuses of your friends etc.). You're not obliged"
+		          + "to do so, but if you choose not to grant Tracker these permissions,"
+		          + "you'll need to login again every 24 hours, and Tracker will only"
+		          + "index your photo albums."));
+		ret.insert ("post_url", "http://www.facebook.com/connect/prompt_permissions.php?api_key=%s&v=1.0&next=http://www.facebook.com/connect/login_success.html&display=popup&ext_perm=read_stream,offline_access".printf (API_KEY));
+		return ret;
+	}
+
+	// This supposes we have a valid auth_token. Else, well, it'll just fail...
+	public override void associate (HashTable<string, string> data) throws MinerWebError
+	{
+		ProxyCall c = rest.new_call ();
+		c.add_param ("method", "auth.getSession");
+		c.add_param ("auth_token", auth_token);
+
+		{
+			XmlNode node = runCall (c);
+
+			if (node.name != "auth_getSession_response") {
+#if DEBUG
+				warning ("Got answer %s\n", c.get_payload ());
+				warning ("Couldn't get session token");
+#endif
+				throw new MinerWebError.SERVICE (_(REST_ERRORMSG), node.find ("error_msg").content);
+			}
+
+			username = node.find ("uid").content;
+			secret = node.find ("secret").content;
+			session = node.find ("session_key").content;
+		}
+
+		try {
+			password_provider.store_password (SERVICE_NAME, SERVICE_DESCRIPTION, session, secret);
+		} catch (Error e) {
+			warning ("Couldn't store credentials in the keyring : %s", e.message);
+			throw new MinerWebError.KEYRING (e.message);
+		}
+	}
+
+	public override void dissociate () throws Tracker.MinerWebError
+	{
+		message ("Dissociate not implemented");
+	}
+
+	public override MinerWebAssociationStatus authenticate () throws MinerWebError
+	{
+		message ("Trying to authenticate");
+		string secret;
+
+		uint association_status = MinerWebAssociationStatus.UNASSOCIATED;
+		try {
+			secret = password_provider.get_password (SERVICE_NAME, out session);
+		} catch (Error e) {
+			if (e is PasswordProviderError.NOTFOUND) {
+				message ("No credentials stored in the keyring");
+				throw new MinerWebError.NO_CREDENTIALS (_("Association needed"));
+			} else {
+				warning ("Couldn't access the keyring : %s", e.message);
+				throw new MinerWebError.KEYRING (e.message);
+			}
+		}
+
+		ProxyCall c = rest.new_call ();
+		c.add_param ("method", "users.getLoggedInUser");
+		XmlNode node;
+		try {
+			node = runCall (c);
+		} catch (MinerWebError e) {
+			throw e;
+		}
+
+		if (node.name != "users_getLoggedInUser_response") {
+#if DEBUG
+			warning ("Couldn't get user info\nGot answer %s\n", c.get_payload ());
+#endif
+			if (node.find ("error_code").content == "102") { // Session key invalid or no longer valid
+				session = null;
+				secret = SHARED_SECRET;
+				message ("Authentication token has expired");
+				throw new MinerWebError.TOKEN_EXPIRED (_("Please associate again"));
+			}
+		} else {
+			username = node.content;
+			association_status = MinerWebAssociationStatus.ASSOCIATED;
+			message ("Authentication successful!");
+		}
+
+		set ("association_status", association_status);
+
+		return (MinerWebAssociationStatus)association_status;
+	}
+
+	public async bool pull ()
+	{
+		uint association_status;
+		get ("association_status", out association_status);
+		// Only accept new work if we're idle
+		if (association_status != MinerWebAssociationStatus.ASSOCIATED && timeout_handle != 0) {
+			timeout_handle = 0;
+			return false;
+		}
+
+
+		// Be smart, only pull what's necessary (we're smart, aren't we ?)
+		string photos_pull_from = "1980-01-01T00:00:00Z,";
+		string stream_pull_from = "1980-01-01T00:00:00Z,";
+
+		try {
+			unowned GLib.PtrArray results = yield execute_sparql ("select  ?date where { ?album a nfo:MediaList ; nie:dataSource <%s> ; nie:isStoredAs ?uri ; nie:contentLastModified ?date } ORDER BY DESC(?date) LIMIT 1".printf (MINER_DATASOURCE_URN));
+			unowned string[][] res = (string[][]) results.pdata;
+			if (results.len > 0) {
+				photos_pull_from = res[0][0];
+			}
+
+			results = yield execute_sparql ("select  ?date where { ?message a mfo:FeedMessage ; nie:dataSource <%s> ; nie:isStoredAs ?uri ; nmo:receivedDate ?date } ORDER BY DESC(?date) LIMIT 1");
+			res = (string[][]) results.pdata;
+			if (results.len > 0) {
+				stream_pull_from = res[0][0];
+			}
+
+			message ("Pulling photos starting from %s and stream starting from %s", photos_pull_from, stream_pull_from);
+		} catch (Error e) {
+			critical ("Error contacting Tracker : %s", e.message);
+			return true;
+		}
+
+		// Pull photos
+		var c = rest.new_call ();
+		c.add_params ("method", "fql.multiquery",
+					  "queries", """{
+									"connections" : "SELECT target_id FROM connection WHERE source_id='%s' AND is_following=1",
+									"friends" : "SELECT uid, name FROM user WHERE uid IN (SELECT target_id FROM #connections)",
+									"photos" : "SELECT pid, src_big, caption, aid, owner, created FROM photo WHERE aid IN (SELECT aid FROM album WHERE owner IN (SELECT target_id FROM #connections)) AND modified > '%2$s'",
+									"albums" : "SELECT aid, name, description, link, owner, modified FROM album WHERE owner IN (SELECT target_id FROM #connections) AND modified > '%2$s'"
+									} """.printf (username, timestamp_from_iso8601 (photos_pull_from)));
+		runCall_async (c, pull_photos_call_cb);
+
+		// Pull streams
+		c = rest.new_call ();
+		c.add_params ("method", "fql.multiquery",
+					  "queries", """{
+									"stream":"SELECT post_id, actor_id, target_id, message, attachment, permalink, created_time FROM stream WHERE source_id IN (SELECT target_id FROM connection WHERE source_id='%s' AND is_following=1) AND created_time > '%s'",
+									"actors":"SELECT uid, name, pic_square FROM user WHERE uid IN (SELECT actor_id FROM #stream)"
+									}""".printf (username, timestamp_from_iso8601 (stream_pull_from)));
+		runCall_async (c, pull_stream_call_cb);
+
+		return true;
+	}
+
+	private void pull_photos_call_cb (ProxyCall call, GLib.Error err, Object weak_object)
+	{
+		if (err != null) {
+			warning ("Error while pulling pictures : %s", err.message);
+			error (new MinerWebError.SERVICE (err.message));
+		} else {
+			pull_photos.begin (call);
+		}
+
+	}
+
+	private async void pull_photos (ProxyCall call)
+	{
+		message ("Pulling pictures");
+
+		var parser = new XmlParser ();
+		XmlNode root = parser.parse_from_data (call.get_payload (), call.get_payload_length ());
+
+		weak XmlNode connections_node = null;
+		weak XmlNode friends_node = null;
+		weak XmlNode albums_node = null;
+		weak XmlNode photos_node = null;
+
+		{ // Assign the results groups to the right variables
+			XmlNode current = root.find ("fql_result");
+
+			if (current == null) {
+				warning ("Error in request : \n%s", call.get_payload ());
+				//error (new MinerWebError.SERVICE (_(REST_ERRORMSG), root.find ("error_msg").content));
+				return;
+			}
+
+			while (current != null) {
+				switch (current.find ("name").content) {
+					case "connections":
+						connections_node = current;
+						break;
+					case "friends":
+						friends_node = current;
+						break;
+					case "albums":
+						albums_node = current;
+						break;
+					case "photos":
+						photos_node = current;
+						break;
+					default:
+						break;
+				}
+
+				current = current.next;
+			}
+		}
+
+		// Maps the friend's uid to their uri
+		var friend_urn = new HashTable<string, string> (str_hash, str_equal);
+		{ // List contacts
+			weak XmlNode current_friend = friends_node.find ("fql_result_set").find ("user");
+
+			while (current_friend != null) {
+				friend_urn.insert (current_friend.find ("uid").content, yield get_contact (current_friend.find ("name").content));
+
+				current_friend = current_friend.next;
+			}
+		}
+
+		// Maps the album's aid to their uri
+		var album_urn = new HashTable<string, string> (str_hash, str_equal);
+		{ // List albums
+			weak XmlNode current_album = albums_node.find ("fql_result_set").find ("album");
+
+			while (current_album != null) {
+				string current_album_uri = current_album.find ("link").content;
+				string current_album_urn = yield get_resource ("nfo:MediaList", current_album_uri);
+
+				album_urn.insert (current_album.find ("aid").content, current_album_urn);
+
+				try {
+					string name = current_album.find ("name").content;
+					if (name != null) {
+						message ("Will index album %s", name);
+						yield execute_batch_update ("insert into <%s> {<%s> rdfs:label \"%s\"}"
+						                            .printf (MINER_DATASOURCE_URN,
+						                                     current_album_urn,
+						                                     escape_string (name)));
+					}
+
+					string comment = current_album.find ("description").content;
+					if (comment != null) {
+						yield execute_batch_update ("insert into <%s> {<%s> rdfs:comment \"%s\"}"
+						                            .printf (MINER_DATASOURCE_URN,
+						                                     current_album_urn,
+						                                     escape_string (comment)));
+					}
+
+					yield execute_batch_update ("insert into <%s> {<%s> nie:contentLastModified '%s' ; nco:creator <%s>"
+					                           .printf(MINER_DATASOURCE_URN,
+					                                   current_album_urn,
+					                                   timestamp_to_iso8601 (current_album.find ("modified").content),
+					                                   friend_urn.lookup    (current_album.find ("owner").content)));
+					yield commit ();
+				} catch (Error update_album_error) {
+					critical ("Error while contacting Tracker : %s", update_album_error.message);
+					//error (new MinerWebError.TRACKER (e.message));
+					return;
+				}
+
+				current_album = current_album.next;
+			}
+		}
+
+		{ // And finally, list photos (remember, that's why whe're here for)
+			weak XmlNode current_photo = photos_node.find ("fql_result_set").find ("photo");
+
+			while (current_photo != null) {
+				string uri = current_photo.find ("src_big").content;
+				if (uri == null) {
+					uri = current_photo.find ("src").content;
+				}
+
+				if (uri == null) {
+					current_photo = current_photo.next;
+					continue;
+				}
+				string urn = yield get_resource ("nmm:Photo", uri);
+
+				message ("Indexing photo %s with urn %s", uri, urn);
+
+				try {
+					string caption = current_photo.find ("caption").content;
+					if (caption != null) {
+						yield execute_batch_update ("insert into <%s> {<%s> rdfs:label \"%s\"}"
+						                            .printf (MINER_DATASOURCE_URN,
+						                                     urn,
+						                                     escape_string (caption)));
+					}
+
+					string date = timestamp_to_iso8601 (current_photo.find ("created").content);
+					yield execute_batch_update ("insert into <%s> {<%s> nie:contentCreated '%s'}"
+					                            .printf (MINER_DATASOURCE_URN,
+					                                     urn,
+					                                     date));
+
+					if (album_urn.lookup (current_photo.find ("aid").content) != null) {
+						yield execute_batch_update ("insert into <%s> {<%s> a nfo:MediaFileListEntry . <%s> nfo:mediaListEntry <%1$s>}"
+						                           .printf (MINER_DATASOURCE_URN,
+						                                    urn,
+						                                    album_urn.lookup (current_photo.find ("aid").content)));
+					} else {
+						warning ("Unknown album for picture %s", uri);
+					}
+
+					yield execute_batch_update ("insert into <%s> {<%s> nco:creator <%s>}"
+					                            .printf (MINER_DATASOURCE_URN,
+					                                     urn,
+					                                     friend_urn.lookup (current_photo.find ("owner").content)));
+					yield commit ();
+				} catch (Error update_photo_error) {
+					critical ("Error while contacting Tracker : %s", update_photo_error.message);
+					//error (new MinerWebError.TRACKER (e.message));
+					return;
+				}
+
+				current_photo = current_photo.next;
+			}
+		}
+
+		message ("Photos pulled");
+	}
+
+	private void pull_stream_call_cb (ProxyCall call, GLib.Error err, Object weak_object)
+	{
+		if (err != null) {
+			warning ("Error while pulling pictures : %s", err.message);
+			error (new MinerWebError.SERVICE (err.message));
+			return;
+		} else {
+			pull_stream.begin (call);
+		}
+	}
+
+	private async void pull_stream (ProxyCall call)
+	{
+
+		var parser = new XmlParser ();
+		XmlNode root = parser.parse_from_data (call.get_payload (), call.get_payload_length ());
+		XmlNode stream_result;
+		XmlNode actors_result;
+		{
+			XmlNode result_1 = root.find ("fql_result");
+			if (result_1 == null) {
+				warning ("Error in request : \n%s", call.get_payload ());
+				//error (new MinerWebError.SERVICE (_(REST_ERRORMSG), root.find ("error_msg").content));
+				return;
+			}
+
+			if (result_1.find ("name").content == "stream") {
+				stream_result = result_1;
+				actors_result = result_1.next;
+			} else {
+				stream_result = result_1.next;
+				actors_result = result_1;
+			}
+		}
+
+		// Maps Facebook ids to nco urns
+		var authors = new HashTable<string, string>(str_hash, str_equal);
+
+		XmlNode current = actors_result.find ("fql_result_set").find ("user");
+		while (current != null) {
+			string facebook_id = current.find ("uid").content;
+			var author = current.find ("name").content;
+			try {
+				var authorUid = yield get_contact (author);
+
+				var avatarUrl = current.find ("pic_square").content;
+				var avatarUid = yield get_resource ("nfo:Image", avatarUrl);
+				yield execute_update ("insert into <%s> {<%s> nco:photo <%s>}"
+				                      .printf (MINER_DATASOURCE_URN,
+				                               authorUid,
+				                               avatarUid));
+
+				authors.insert (facebook_id, authorUid);
+				break;
+			} catch (Error insert_contact_error) {
+				critical ("Error contacting Tracker : %s", insert_contact_error.message);
+				//error (new MinerWebError.TRACKER (e.message));
+			}
+			current = current.next;
+		}
+
+		// Pull the stream
+		current = stream_result.find ("fql_result_set").find ("stream_post");
+		while (current != null) {
+			try {
+				string uri = current.find ("permalink").content;
+				string urn = yield get_resource ("mfo:FeedMessage", uri);
+
+				string date = timestamp_to_iso8601 (current.find ("created_time").content);
+				yield execute_batch_update ("insert into <%s> {<%s> nmo:from <%s> ; nmo:receivedDate \"%s\"}"
+				                      .printf (MINER_DATASOURCE_URN,
+				                               urn,
+				                               authors.lookup (current.find ("actor_id").content),
+				                               date));
+
+				if (current.find ("message").content != null) {
+					yield execute_batch_update ("insert into <%s> {<%s> nmo:plainTextMessageContent \"%s\"}"
+					                            .printf (MINER_DATASOURCE_URN,
+					                                     urn,
+					                                     escape_string (current.find ("message").content)));
+				}
+
+				// Deal with any attachment we might have
+				// We are obliged to first list the pictures, then do a query to get their src_big attribute
+				if (current.find ("attachment").children.size () > 0) {
+					// Maps the photo pid to the nmo:Message urn
+					var pictures = new List<string> ();
+
+					XmlNode current_media = current.find ("stream_media");
+					while (current_media != null) {
+						switch (current_media.find ("type").content) {
+							case "photo":
+								pictures.append (current_media.find ("photo").find ("pid").content);
+								break;
+							default:
+								warning ("Media type %s not handled yet", current_media.find ("type").content);
+								break;
+						}
+						current_media = current_media.next;
+					}
+
+					if (pictures != null) {
+						weak List<string> current_pic = pictures.first ();
+						var query = new StringBuilder ("SELECT src_big FROM photo WHERE pid = '%s'".printf (current_pic.data));
+
+						while ((current_pic = current_pic.next) != null) {
+							query.append_printf (" OR pid = '%s'", current_pic.data);
+						}
+
+						var c = rest.new_call ();
+						c.add_params ("method", "fql.query",
+									  "query", query.str);
+
+						// here we can block, since we don't bother anyone
+						XmlNode photos_node;
+						try {
+							photos_node = runCall (c);
+						} catch (MinerWebError e) {
+							warning ("REST call failed!");
+							return;
+						}
+
+						XmlNode current_photo = photos_node.find ("photo");
+						while (current_photo != null) {
+							var picture_uri = current_photo.find ("src_big").content;
+							var enclosure_urn = yield get_resource ("nmm:Photo", picture_uri);
+
+							yield execute_batch_update ("insert into <%s> {<%s> a mfo:Enclosure ; mfo:remoteLink <%s> . <%s> mfo:enclosureList <%1$s>}"
+							                            .printf (MINER_DATASOURCE_URN,
+							                                     enclosure_urn,
+							                                     picture_uri,
+							                                     urn));
+
+							// The rest of the photo indexing will be done by pull_photos
+
+							current_photo = current_photo.next;
+						}
+					}
+				}
+
+				yield commit ();
+			} catch (Error update_message_error) {
+				critical ("Error while inserting data into Tracker : %s", update_message_error.message);
+				//error (new MinerWebError.TRACKER (e.message));
+			}
+			current = current.next;
+		}
+		message ("Stream pulled");
+	}
+
+	// Private functions
+
+	private XmlNode? runCall (ProxyCall c) throws MinerWebError
+	{
+		signCall (c);
+
+		try {
+			c.run (null);
+		} catch (Error e) {
+			warning ("Error in REST call : %s\n", e.message);
+			//error (new MinerWebError.SERVICE (_(REST_ERRORMSG), e.message));
+			throw new MinerWebError.SERVICE (e.message);
+		}
+
+		var parser = new XmlParser ();
+		weak XmlNode ret = parser.parse_from_data (c.get_payload (), c.get_payload_length ());
+		return ret;
+	}
+
+
+	private void runCall_async (ProxyCall c, ProxyCallAsyncCallback callback)
+	{
+		signCall (c);
+
+		try {
+			c.run_async (callback, this);
+		} catch (Error e) {
+			warning ("Error in REST call : %s\n", e.message);
+			//error (new MinerWebError.SERVICE (_(REST_ERRORMSG), e.message));
+			throw new MinerWebError.SERVICE (e.message);
+		}
+	}
+
+	// Add version, api_key, call_id and sig
+	private void signCall (ProxyCall c)
+	{
+		var time = TimeVal ();
+		var callid = "%ld".printf(1000000*(time.tv_sec) + time.tv_usec);
+		c.add_params ("v", "1.0",
+					  "api_key", API_KEY,
+					  "call_id", callid,
+					  null);
+
+		if (session != null)
+			c.add_param ("session_key", session);
+
+		string sig = "";
+		var par = c.get_params ();
+		List<weak string> keys = par.get_keys ().copy ();
+		keys.sort ((CompareFunc)strcmp);
+
+		for (int i = 0 ; i < keys.length () ; ++i) {
+			sig += keys.nth_data (i) + "=" + par.lookup (keys.nth_data (i));
+		}
+		sig += secret;
+
+		sig = Checksum.compute_for_string (ChecksumType.MD5, sig);
+
+		c.add_param ("sig", sig);
+	}
+
+	// tries to find a resource with the given url as a nie:isStoredAs property. If none is foud, creates one (return value is urn)
+	private async string? get_resource (string klass, string stored_as)
+	{
+		unowned GLib.PtrArray results;
+		try {
+			results = yield execute_sparql ("select ?r where { ?r a %s . ?r nie:isStoredAs \"%s\" }".printf (klass, stored_as));
+		} catch (Error e) {
+			critical ("Couldn't contact Tracker : %s", e.message);
+			//error (new MinerWebError.TRACKER (e.message));
+			return null;
+		}
+
+		unowned string [][] res = (string[][])results.pdata;
+		switch (results.len) {
+			case 0:
+				string urn = "urn:uuid:%s".printf (uuid_generate_string ());
+				yield execute_update ("insert {<%s> a nfo:RemoteDataObject . <%s> a %s ; nie:isStoredAs <%1$s> ; nie:dataSource <%s>}"
+				                      .printf (stored_as,
+				                               urn,
+				                               klass,
+				                               MINER_DATASOURCE_URN));
+				return urn;
+			case 1:
+				return res[0][0];
+			default:
+				warning ("More than one object of type %s and with nie:isStoredAs '%s'. Returning the first one", klass, stored_as);
+				return res[0][0];
+		}
+	}
+
+	// tries to find a nco:Contact with the given nco:fullname. If none is foud, creates one (return value is urn)
+	private async string? get_contact (string fullname)
+	{
+		unowned GLib.PtrArray results;
+		string escaped_fullname = escape_string (fullname);
+		try {
+			results = yield execute_sparql ("select ?r where { ?r a nco:Contact . ?r nco:fullname \"%s\" }".printf (escaped_fullname));
+		} catch (Error e) {
+			critical ("Couldn't contact Tracker : %s", e.message);
+			//error (new MinerWebError.TRACKER (e.message));
+			return null;
+		}
+
+		unowned string[][] res = (string[][])results.pdata;
+		switch (results.len) {
+			case 0:
+				string urn = "urn:uuid:%s".printf (uuid_generate_string ());
+				yield execute_update ("insert {<%s> a nco:Contact ; nco:fullname \"%s\" ; nie:dataSource <%s>}".printf (urn, escaped_fullname, MINER_DATASOURCE_URN));
+				return urn;
+			case 1:
+				return res[0][0];
+			default:
+				warning ("More than one nco:Contact with nco:fullname '%s'. Returning the first one", fullname);
+				return res[0][0];
+		}
+	}
+
+	private string escape_string(string str) {
+		return str.escape ("").replace ("'", "\\'").replace ("\"", "\\\"");
+	}
+
+
+	private string timestamp_to_iso8601 (string timestamp)
+	{
+		GLib.TimeVal timeval = GLib.TimeVal ();
+		timeval.tv_sec = timestamp.to_long ();
+		return timeval.to_iso8601 ();
+	}
+
+	private string? timestamp_from_iso8601 (string isodate)
+	{
+		GLib.TimeVal timeval = GLib.TimeVal ();
+		if (!timeval.from_iso8601 (isodate))
+			return null;
+
+		return "%ld".printf (timeval.tv_sec);
+	}
+
+}
+
+MainLoop loop;
+
+void main ()
+{
+	loop = new MainLoop (null, false);
+
+	Environment.set_application_name ("FacebookMiner");
+
+	var miner = new FacebookMiner ();
+	miner.start ();
+	message ("Miner started");
+
+	loop.run ();
+}
diff --git a/src/tracker-preferences/Makefile.am b/src/tracker-preferences/Makefile.am
index c5b1e08..b45d5f8 100644
--- a/src/tracker-preferences/Makefile.am
+++ b/src/tracker-preferences/Makefile.am
@@ -56,7 +56,7 @@ EXTRA_DIST = 								\
 	$(tracker_preferences_VALASOURCES)
 
 tracker-preferences.vala.stamp: $(tracker_preferences_VALASOURCES) tracker-config.vapi tracker-status-icon-config.vapi config.vapi
-	$(AM_V_GEN)$(VALAC) $(GCOV_VALAFLAGS) --pkg gtk+-2.0 --pkg gmodule-2.0 -C $(VALAFLAGS) $^
+	$(AM_V_GEN)$(VALAC) $(GCOV_VALAFLAGS) --pkg gtk+-2.0 --pkg gmodule-2.0 --pkg dbus-glib-1 --vapidir $(top_srcdir)/src/libtracker-miner --pkg tracker-miner-$(TRACKER_API_VERSION) -C $(VALAFLAGS) $^
 	$(AM_V_GEN)touch $@
 
 CLEANFILES = $(desktop_DATA)
diff --git a/src/tracker-preferences/tracker-preferences.ui b/src/tracker-preferences/tracker-preferences.ui
index e624891..4ac93c4 100644
--- a/src/tracker-preferences/tracker-preferences.ui
+++ b/src/tracker-preferences/tracker-preferences.ui
@@ -32,6 +32,16 @@
       <column type="gchararray"/>
     </columns>
   </object>
+  <object class="GtkListStore" id="liststore_miners">
+    <columns>
+      <!-- column-name icon -->
+      <column type="gchararray"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name auth_type -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
   <object class="GtkDialog" id="tracker-preferences">
     <property name="width_request">500</property>
     <property name="border_width">5</property>
@@ -253,7 +263,7 @@
                     <property name="label_xalign">0</property>
                     <property name="shadow_type">none</property>
                     <child>
-                      <object class="GtkAlignment" id="alignment3">
+                      <object class="GtkAlignment" id="alignment4">
                         <property name="visible">True</property>
                         <property name="top_padding">6</property>
                         <property name="left_padding">12</property>
@@ -296,7 +306,7 @@
                               </packing>
                             </child>
                             <child>
-                              <object class="GtkAlignment" id="alignment4">
+                              <object class="GtkAlignment" id="alignment7">
                                 <property name="visible">True</property>
                                 <property name="left_padding">12</property>
                                 <child>
@@ -376,7 +386,7 @@
                     <property name="label_xalign">0</property>
                     <property name="shadow_type">none</property>
                     <child>
-                      <object class="GtkAlignment" id="alignment7">
+                      <object class="GtkAlignment" id="alignment8">
                         <property name="visible">True</property>
                         <property name="top_padding">6</property>
                         <property name="left_padding">12</property>
@@ -1218,6 +1228,109 @@ Most commonly this is used to ignore directories like *~, *.o, *.la, etc</proper
                 <property name="tab_fill">False</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkFrame" id="frame2">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment9">
+                    <property name="visible">True</property>
+                    <property name="top_padding">6</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox12">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkComboBox" id="combo_miners">
+                            <property name="visible">True</property>
+                            <property name="model">liststore_miners</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox7">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkImage" id="image_miner_status">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-dialog-question</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="padding">6</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="label_miner_status">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Miner status unknown</property>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHButtonBox" id="hbuttonbox1">
+                                <property name="visible">True</property>
+                                <child>
+                                  <object class="GtkButton" id="button_miner_associate">
+                                    <property name="label" translatable="yes">Associate</property>
+                                    <property name="visible">True</property>
+                                    <property name="sensitive">False</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="padding">6</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label14">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Miner&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">4</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="label13">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Miners</property>
+              </object>
+              <packing>
+                <property name="position">4</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
           </object>
           <packing>
             <property name="position">1</property>
@@ -1277,10 +1390,6 @@ Most commonly this is used to ignore directories like *~, *.o, *.la, etc</proper
     <property name="page_increment">2</property>
     <property name="page_size">5</property>
   </object>
-  <object class="GtkImage" id="image1">
-    <property name="visible">True</property>
-    <property name="stock">gtk-missing-image</property>
-  </object>
   <object class="GtkAdjustment" id="adjustment_disk_space_limit">
     <property name="lower">-1</property>
     <property name="upper">100</property>
@@ -1295,8 +1404,8 @@ Most commonly this is used to ignore directories like *~, *.o, *.la, etc</proper
   </object>
   <object class="GtkSizeGroup" id="sizegroup_indexing">
     <widgets>
-      <widget name="label_throttle"/>
       <widget name="label_disk_space_limit"/>
+      <widget name="label_throttle"/>
     </widgets>
   </object>
 </interface>
diff --git a/src/tracker-preferences/tracker-preferences.vala b/src/tracker-preferences/tracker-preferences.vala
index 5f9dfb0..bbe60e9 100644
--- a/src/tracker-preferences/tracker-preferences.vala
+++ b/src/tracker-preferences/tracker-preferences.vala
@@ -26,8 +26,12 @@ using Tracker;
 
 public static Config config = null;
 public static IconConfig icon_config = null;
+public static DBus.Connection dbus_conn;
 
 public const string HOME_STRING = "$HOME";
+public const string DESKTOP_GROUP = "Desktop Entry";
+public const string MINER_ON_ICON = "gtk-yes";
+public const string MINER_OFF_ICON = "gtk-no";
 
 public static Window window;
 public static CheckButton checkbutton_enable_index_on_battery_first_time;
@@ -52,6 +56,19 @@ public static ToggleButton togglebutton_home;
 public static RadioButton radiobutton_display_never;
 public static RadioButton radiobutton_display_active;
 public static RadioButton radiobutton_display_always;
+public static ListStore liststore_miners;
+public static ComboBox combo_miners;
+public static Image image_miner_status;
+public static Label label_miner_status;
+public static Button button_miner_associate;
+
+public struct MinerInfo
+{
+	public string name;
+	public DBus.Object proxy;
+	public MinerWebAssociationStatus association_status;
+}
+public static MinerInfo? current_miner = null;
 
 public static void radiobutton_visibility_toggled_cb (RadioButton source) {
 	if (radiobutton_display_never.active) {
@@ -322,6 +339,120 @@ setup_standard_treeview (TreeView view, string title)
 	view.append_column (column);
 }
 
+static void
+setup_combo_miners ()
+{
+	var icon_renderer = new CellRendererPixbuf ();
+	var name_renderer = new CellRendererText ();
+	combo_miners.pack_start (icon_renderer, false);
+	combo_miners.pack_start (name_renderer, true);
+	combo_miners.add_attribute (icon_renderer,
+	                            "icon-name", 0);
+	combo_miners.add_attribute (name_renderer,
+	                            "text", 1);
+}
+
+static void
+fill_in_miners_model ()
+{
+	string miners_folder_path = Path.build_filename (TRACKER_DATADIR, "miners");
+	try {
+		Dir miners_dir = Dir.open (miners_folder_path);
+		int position = 0;
+		string current_filename;
+		while ((current_filename = miners_dir.read_name ()) != null) {
+			string desktop_path = Path.build_filename (TRACKER_DATADIR, "miners", current_filename);
+			KeyFile desktop_file = new KeyFile ();
+			try {
+				desktop_file.load_from_file (desktop_path, KeyFileFlags.NONE);
+				if (!desktop_file.has_key (DESKTOP_GROUP, "AuthScheme")) {
+					// Only web miners should have this key defined
+					continue;
+				}
+				string icon_name;
+				try {
+					icon_name = desktop_file.get_string (DESKTOP_GROUP, "Icon");
+				} catch (Error e) {
+					icon_name = null;
+				}
+				liststore_miners.insert_with_values (null, position++,
+				                                     0 /* Icon */, icon_name,
+				                                     1 /* Name */, desktop_file.get_string (DESKTOP_GROUP, "Name"),
+				                                     2 /* Association type */, desktop_file.get_string (DESKTOP_GROUP, "AuthScheme"));
+			} catch (Error e) {
+				warning ("Couldn't load miner %s : %s", current_filename, e.message);
+			}
+		}
+	} catch (Error e) {
+		warning ("Couldn't list miners : %s", e.message);
+	}
+}
+
+static void
+update_miner_status_panel (string name, string association_type)
+{
+//	string dbus_name = "org.freedesktop.Tracker1.Miner.%s".printf (name);
+//	string dbus_path = "/org/freedesktop/Tracker1/Miner/Web/%s".printf (name);
+//
+//	dynamic DBus.Object miner = dbus_conn.get_object (dbus_name, dbus_path, MINER_WEB_DBUS_INTERFACE);
+//
+//	try {
+//		MinerWebAssociationStatus miner_status = miner.GetAssociationStatus ();
+//
+//		switch (miner_status) {
+//			case MinerWebAssociationStatus.ASSOCIATED:
+//				image_miner_status.set_from_stock (STOCK_YES, IconSize.MENU);
+//				label_miner_status.set_text (_("Running"));
+//				button_miner_associate.set_label (_("Associate as another user"));
+//				break;
+//			case MinerWebAssociationStatus.UNASSOCIATED:
+//				image_miner_status.set_from_stock (STOCK_NO, IconSize.MENU);
+//				label_miner_status.set_text (_("Needs association"));
+//				button_miner_associate.set_label (_("Associate"));
+//				break;
+//		}
+//		frame_miner_association.show ();
+//
+//		switch (association_type) {
+//			case "Token":
+//				vbox_miner_tokenassociation.show ();
+//				table_miner_userassociation.hide ();
+//				break;
+//			case "UserPass":
+//				vbox_miner_tokenassociation.hide ();
+//				table_miner_userassociation.show ();
+//				break;
+//			default:
+//				break;
+//		}
+//	} catch (Error e) {
+//		warning ("Error while contacting miner : %s", e.message);
+//		image_miner_status.set_from_stock (STOCK_DIALOG_ERROR, IconSize.MENU);
+//		label_miner_status.set_text (_("Error while contacting the miner"));
+//		frame_miner_association.hide ();
+//	}
+//
+//	vbox_miners.show ();
+}
+
+static void
+combo_miners_changed ()
+{
+	TreeIter active_iter;
+	string name;
+
+	combo_miners.get_active_iter (out active_iter);
+	liststore_miners.get (active_iter,
+	                      1, out name);
+
+	message ("Active miner is now %s", name);
+}
+
+static void
+button_miner_associate_clicked (Button source)
+{
+}
+
 static int main (string[] args) {
 	Gtk.init (ref args);
 
@@ -364,11 +495,18 @@ static int main (string[] args) {
 		treeview_ignored_directories_with_content = builder.get_object ("treeview_ignored_directories_with_content") as TreeView;
 		treeview_ignored_files = builder.get_object ("treeview_ignored_files") as TreeView;
 
+		combo_miners = builder.get_object ("combo_miners") as ComboBox;
+		image_miner_status = builder.get_object ("image_miner_status") as Image;
+		label_miner_status = builder.get_object ("label_miner_status") as Label;
+		button_miner_associate = builder.get_object ("button_miner_associate") as Button;
+
 		setup_standard_treeview (treeview_index_recursively, _("Directory"));
 		setup_standard_treeview (treeview_index_single, _("Directory"));
 		setup_standard_treeview (treeview_ignored_directories, _("Directory"));
 		setup_standard_treeview (treeview_ignored_directories_with_content, _("Directory"));
 		setup_standard_treeview (treeview_ignored_files, _("File"));
+		setup_combo_miners ();
+		combo_miners.changed.connect (combo_miners_changed);
 
 		liststore_index_recursively = builder.get_object ("liststore_index_recursively") as ListStore;
 		fill_in_model (liststore_index_recursively, config.index_recursive_directories);
@@ -387,9 +525,20 @@ static int main (string[] args) {
 		liststore_gnored_directories_with_content = builder.get_object ("liststore_gnored_directories_with_content") as ListStore;
 		fill_in_model (liststore_gnored_directories_with_content, config.ignored_directories_with_content);
 
+		liststore_miners = builder.get_object ("liststore_miners") as ListStore;
+		fill_in_miners_model ();
+		combo_miners.set_active (0);
+
 		builder.connect_signals (null);
 
+		try {
+			dbus_conn = DBus.Bus.get (DBus.BusType.SESSION);
+		} catch (Error e) {
+			critical ("Couldn't connect to session bus : %s", e.message);
+		}
+
 		window.show_all ();
+
 		Gtk.main ();
 	} catch (Error e) {
 		stderr.printf ("Could not load UI: %s\n", e.message);



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