[tracker/libtracker-miner: 3/3] Raw port of handy code from tracker-miner-fs that will be needed for TrackerMinerCrawler.



commit 99e6630f704b5efb56d3144ca98c5bb42bc5e395
Author: Carlos Garnacho <carlos lanedo com>
Date:   Thu Jul 30 18:13:15 2009 +0200

    Raw port of handy code from tracker-miner-fs that will be needed for TrackerMinerCrawler.
    
    Things are broken, and dependency on config, modules config, and possibly
    status, should be removed all along the code.

 src/libtracker-miner/Makefile.am             |   33 +-
 src/libtracker-miner/tracker-config.c        | 1065 ++++++++++++++++
 src/libtracker-miner/tracker-config.h        |  119 ++
 src/libtracker-miner/tracker-crawler.c       | 1236 ++++++++++++++++++
 src/libtracker-miner/tracker-crawler.h       |   70 +
 src/libtracker-miner/tracker-marshal.list    |    8 +
 src/libtracker-miner/tracker-miner-crawler.c |   23 +-
 src/libtracker-miner/tracker-monitor.c       | 1632 +++++++++++++++++++++++
 src/libtracker-miner/tracker-monitor.h       |   77 ++
 src/libtracker-miner/tracker-processor.c     | 1774 ++++++++++++++++++++++++++
 src/libtracker-miner/tracker-processor.h     |   64 +
 src/libtracker-miner/tracker-status.c        | 1097 ++++++++++++++++
 src/libtracker-miner/tracker-status.h        |   96 ++
 src/libtracker-miner/tracker-utils.c         |   43 +
 src/libtracker-miner/tracker-utils.h         |   31 +
 15 files changed, 7365 insertions(+), 3 deletions(-)
---
diff --git a/src/libtracker-miner/Makefile.am b/src/libtracker-miner/Makefile.am
index 08b181c..15a56f8 100644
--- a/src/libtracker-miner/Makefile.am
+++ b/src/libtracker-miner/Makefile.am
@@ -22,13 +22,27 @@ libtracker_minerincludedir=$(includedir)/tracker-$(TRACKER_API_VERSION)/libtrack
 libtracker_miner_LTLIBRARIES = libtracker-miner.la
 
 libtracker_miner_la_SOURCES = 				\
+	tracker-config.c				\
+	tracker-config.h				\
+	tracker-crawler.c				\
+	tracker-crawler.h				\
 	tracker-dbus.c					\
 	tracker-dbus.h					\
+	tracker-marshal.c				\
+	tracker-marshal.h				\
 	tracker-miner-dbus.h				\
 	tracker-miner.c					\
 	tracker-miner.h					\
 	tracker-miner-crawler.c				\
-	tracker-miner-crawler.h
+	tracker-miner-crawler.h				\
+	tracker-monitor.c				\
+	tracker-monitor.h				\
+	tracker-processor.c				\
+	tracker-processor.h				\
+	tracker-status.c				\
+	tracker-status.h				\
+	tracker-utils.c					\
+	tracker-utils.h
 
 libtracker_minerinclude_HEADERS = 			\
 	tracker-miner.h 				\
@@ -39,7 +53,9 @@ libtracker_miner_la_LDFLAGS = \
 
 libtracker_miner_la_LIBADD = 				\
 	$(top_builddir)/src/libstemmer/libstemmer.la	\
+	$(top_builddir)/src/libtracker-common/libtracker-common.la \
 	$(top_builddir)/src/libtracker/libtrackerclient- TRACKER_API_VERSION@.la \
+	$(top_builddir)/src/libinotify/libinotify.la	\
 	$(HAL_LIBS)					\
 	$(DEVKIT_POWER_LIBS)				\
 	$(DBUS_LIBS)					\
@@ -50,15 +66,28 @@ libtracker_miner_la_LIBADD = 				\
 	$(GLIB2_LIBS)					\
 	$(GDKPIXBUF_LIBS)
 
+tracker-marshal.h: tracker-marshal.list
+	$(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --header > $@
+
+tracker-marshal.c: tracker-marshal.list
+	(echo "#include \"tracker-marshal.h\""; \
+	 $(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --body) > $@
+
+marshal_sources = 					\
+	tracker-marshal.c				\
+	tracker-marshal.h
+
 dbus_sources = tracker-miner-glue.h
 
 %-glue.h: $(top_srcdir)/data/dbus/%.xml
 	$(DBUSBINDINGTOOL) --mode=glib-server --output=$@ --prefix=$(subst -,_,$*) $^
 
-BUILT_SOURCES = $(dbus_sources)
+BUILT_SOURCES = $(dbus_sources) $(marshal_sources)
 
 CLEANFILES = $(BUILT_SOURCES)
 
+EXTRA_DIST = tracker-marshal.list
+
 libexec_PROGRAMS = tracker-miner-test
 
 tracker_miner_test_SOURCES = 	\
diff --git a/src/libtracker-miner/tracker-config.c b/src/libtracker-miner/tracker-config.c
new file mode 100644
index 0000000..00d17c7
--- /dev/null
+++ b/src/libtracker-miner/tracker-config.c
@@ -0,0 +1,1065 @@
+/* -*- 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 General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-keyfile-object.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-type-utils.h>
+
+#include "tracker-config.h"
+
+#define TRACKER_CONFIG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_CONFIG, TrackerConfigPrivate))
+
+/* GKeyFile defines */
+#define GROUP_GENERAL				 "General"
+#define GROUP_MONITORS				 "Monitors"
+#define GROUP_INDEXING				 "Indexing"
+
+/* Default values */
+#define DEFAULT_VERBOSITY			 0
+#define DEFAULT_INITIAL_SLEEP			 15	  /* 0->1000 */
+#define DEFAULT_ENABLE_WATCHES			 TRUE
+#define DEFAULT_THROTTLE			 0	  /* 0->20 */
+#define DEFAULT_ENABLE_THUMBNAILS		 TRUE
+#define DEFAULT_DISABLE_INDEXING_ON_BATTERY	 TRUE
+#define DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT FALSE
+#define DEFAULT_INDEX_MOUNTED_DIRECTORIES	 TRUE
+#define DEFAULT_INDEX_REMOVABLE_DEVICES		 TRUE
+#define DEFAULT_LOW_DISK_SPACE_LIMIT		 1	  /* 0->100 / -1 */
+
+typedef struct {
+	/* General */
+	gint	  verbosity;
+	gint	  initial_sleep;
+
+	/* Watches */
+	GSList	 *watch_directory_roots;
+	GSList	 *crawl_directory_roots;
+	GSList	 *no_watch_directory_roots;
+	gboolean  enable_watches;
+
+	/* Indexing */
+	gint	  throttle;
+	gboolean  enable_thumbnails;
+	GSList   *disabled_modules;
+
+	gboolean  disable_indexing_on_battery;
+	gboolean  disable_indexing_on_battery_init;
+	gint	  low_disk_space_limit;
+	gboolean  index_mounted_directories;
+	gboolean  index_removable_devices;
+} TrackerConfigPrivate;
+
+typedef struct {
+	GType  type;
+	gchar *property;
+	gchar *group;
+	gchar *key;
+} ObjectToKeyFile;
+
+static void     config_set_property         (GObject       *object,
+					     guint          param_id,
+					     const GValue  *value,
+					     GParamSpec    *pspec);
+static void     config_get_property         (GObject       *object,
+					     guint          param_id,
+					     GValue        *value,
+					     GParamSpec    *pspec);
+static void     config_finalize             (GObject       *object);
+static void     config_constructed          (GObject       *object);
+static void     config_load                 (TrackerConfig *config);
+static gboolean config_save                 (TrackerConfig *config);
+static void     config_create_with_defaults (TrackerConfig *config,
+					     GKeyFile      *key_file,
+					     gboolean       overwrite);
+
+enum {
+	PROP_0,
+
+	/* General */
+	PROP_VERBOSITY,
+	PROP_INITIAL_SLEEP,
+
+	/* Watches */
+	PROP_ENABLE_WATCHES,
+	PROP_WATCH_DIRECTORY_ROOTS,
+	PROP_CRAWL_DIRECTORY_ROOTS,
+	PROP_NO_WATCH_DIRECTORY_ROOTS,
+
+	/* Indexing */
+	PROP_THROTTLE,
+	PROP_ENABLE_THUMBNAILS,
+	PROP_DISABLED_MODULES,
+	PROP_DISABLE_INDEXING_ON_BATTERY,
+	PROP_DISABLE_INDEXING_ON_BATTERY_INIT,
+	PROP_LOW_DISK_SPACE_LIMIT,
+	PROP_INDEX_MOUNTED_DIRECTORIES,
+	PROP_INDEX_REMOVABLE_DEVICES,
+};
+
+
+static ObjectToKeyFile conversions[] = {
+	{ G_TYPE_INT,     "verbosity",                        GROUP_GENERAL,  "Verbosity"               },
+	{ G_TYPE_INT,     "initial-sleep",                    GROUP_GENERAL,  "InitialSleep"            },
+	{ G_TYPE_BOOLEAN, "enable-watches",                   GROUP_MONITORS, "EnableWatches"           },
+	{ G_TYPE_POINTER, "watch-directory-roots",            GROUP_MONITORS, "WatchDirectoryRoots"     },
+	{ G_TYPE_POINTER, "crawl-directory-roots",            GROUP_MONITORS, "CrawlDirectoryRoots"     },
+	{ G_TYPE_POINTER, "no-watch-directory-roots",         GROUP_MONITORS, "NoWatchDirectory"        },
+	{ G_TYPE_INT,     "throttle",                         GROUP_INDEXING, "Throttle"                },
+	{ G_TYPE_BOOLEAN, "enable-thumbnails",                GROUP_INDEXING, "EnableThumbnails"        },
+	{ G_TYPE_POINTER, "disabled-modules",                 GROUP_INDEXING, "DisabledModules"         },
+	{ G_TYPE_BOOLEAN, "disable-indexing-on-battery",      GROUP_INDEXING, "BatteryIndex"            },
+	{ G_TYPE_BOOLEAN, "disable-indexing-on-battery-init", GROUP_INDEXING, "BatteryIndexInitial"     },
+	{ G_TYPE_INT,     "low-disk-space-limit",             GROUP_INDEXING, "LowDiskSpaceLimit"       },
+	{ G_TYPE_BOOLEAN, "index-mounted-directories",        GROUP_INDEXING, "IndexMountedDirectories" },
+	{ G_TYPE_BOOLEAN, "index-removable-devices",          GROUP_INDEXING, "IndexRemovableMedia"     },
+};
+
+G_DEFINE_TYPE (TrackerConfig, tracker_config, TRACKER_TYPE_CONFIG_FILE);
+
+static void
+tracker_config_class_init (TrackerConfigClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->set_property = config_set_property;
+	object_class->get_property = config_get_property;
+	object_class->finalize	   = config_finalize;
+	object_class->constructed  = config_constructed;
+
+	/* General */
+	g_object_class_install_property (object_class,
+					 PROP_VERBOSITY,
+					 g_param_spec_int ("verbosity",
+							   "Log verbosity",
+							   " Log verbosity (0=errors, 1=minimal, 2=detailed, 3=debug)",
+							   0,
+							   3,
+							   DEFAULT_VERBOSITY,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_INITIAL_SLEEP,
+					 g_param_spec_int ("initial-sleep",
+							   "Initial sleep",
+							   " Time in seconds before crawling filesystem (0->1000)",
+							   0,
+							   1000,
+							   DEFAULT_INITIAL_SLEEP,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	/* Monitors */
+	g_object_class_install_property (object_class,
+					 PROP_ENABLE_WATCHES,
+					 g_param_spec_boolean ("enable-watches",
+							       "Enable watches",
+							       " Set to false to completely disable any watching",
+							       DEFAULT_ENABLE_WATCHES,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_WATCH_DIRECTORY_ROOTS,
+					 g_param_spec_pointer ("watch-directory-roots",
+							       "Watched directory roots",
+							       " List of directory roots to index and watch (separator=;)",
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_CRAWL_DIRECTORY_ROOTS,
+					 g_param_spec_pointer ("crawl-directory-roots",
+							       "Crawl directory roots",
+							       " List of directory roots to index but NOT watch (separator=;)",
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_NO_WATCH_DIRECTORY_ROOTS,
+					 g_param_spec_pointer ("no-watch-directory-roots",
+							       "Not watched directory roots",
+							       " List of directory roots NOT to index and NOT to watch (separator=;)",
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	/* Indexing */
+	g_object_class_install_property (object_class,
+					 PROP_THROTTLE,
+					 g_param_spec_int ("throttle",
+							   "Throttle",
+							   " Sets the indexing speed (0->20, where 20=slowest speed)",
+							   0,
+							   20,
+							   DEFAULT_THROTTLE,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_ENABLE_THUMBNAILS,
+					 g_param_spec_boolean ("enable-thumbnails",
+							       "Enable thumbnails",
+							       " Set to false to completely disable thumbnail generation",
+							       DEFAULT_ENABLE_THUMBNAILS,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_DISABLED_MODULES,
+					 g_param_spec_pointer ("disabled-modules",
+							       "Disabled modules",
+							       " List of disabled modules (separator=;)\n"
+							       " The modules that are indexed are kept in $prefix/lib/tracker/indexer-modules",
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	;
+	g_object_class_install_property (object_class,
+					 PROP_DISABLE_INDEXING_ON_BATTERY,
+					 g_param_spec_boolean ("disable-indexing-on-battery",
+							       "Disable indexing on battery",
+							       " Set to true to disable indexing when running on battery",
+							       DEFAULT_DISABLE_INDEXING_ON_BATTERY,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_DISABLE_INDEXING_ON_BATTERY_INIT,
+					 g_param_spec_boolean ("disable-indexing-on-battery-init",
+							       "Disable indexing on battery",
+							       " Set to true to disable initial indexing when running on battery",
+							       DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_LOW_DISK_SPACE_LIMIT,
+					 g_param_spec_int ("low-disk-space-limit",
+							   "Low disk space limit",
+							   " Pause indexer when disk space is <= this value\n"
+							   " (0->100, value is in % of $HOME file system, -1=disable pausing)",
+							   -1,
+							   100,
+							   DEFAULT_LOW_DISK_SPACE_LIMIT,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_INDEX_MOUNTED_DIRECTORIES,
+					 g_param_spec_boolean ("index-mounted-directories",
+							       "Index mounted directories",
+							       " Set to true to enable traversing mounted directories on other file systems\n"
+							       " (this excludes removable devices)",
+							       DEFAULT_INDEX_MOUNTED_DIRECTORIES,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_INDEX_REMOVABLE_DEVICES,
+					 g_param_spec_boolean ("index-removable-devices",
+							       "index removable devices",
+							       " Set to true to enable traversing mounted directories for removable devices",
+							       DEFAULT_INDEX_REMOVABLE_DEVICES,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_type_class_add_private (object_class, sizeof (TrackerConfigPrivate));
+}
+
+static void
+tracker_config_init (TrackerConfig *object)
+{
+}
+
+static void
+config_set_property (GObject	  *object,
+		     guint	   param_id,
+		     const GValue *value,
+		     GParamSpec	  *pspec)
+{
+	switch (param_id) {
+		/* General */
+	case PROP_VERBOSITY:
+		tracker_config_set_verbosity (TRACKER_CONFIG (object),
+					      g_value_get_int (value));
+		break;
+	case PROP_INITIAL_SLEEP:
+		tracker_config_set_initial_sleep (TRACKER_CONFIG (object),
+						  g_value_get_int (value));
+		break;
+
+		/* Watches */
+	case PROP_WATCH_DIRECTORY_ROOTS:    
+		tracker_config_set_watch_directory_roots (TRACKER_CONFIG (object),
+							  g_value_get_pointer (value));
+		break;
+
+	case PROP_CRAWL_DIRECTORY_ROOTS:    
+		tracker_config_set_crawl_directory_roots (TRACKER_CONFIG (object),
+							  g_value_get_pointer (value));
+		break;
+	case PROP_NO_WATCH_DIRECTORY_ROOTS: 
+		tracker_config_set_no_watch_directory_roots (TRACKER_CONFIG (object),
+							     g_value_get_pointer (value));
+		break;
+	case PROP_ENABLE_WATCHES:
+		tracker_config_set_enable_watches (TRACKER_CONFIG (object),
+						   g_value_get_boolean (value));
+		break;
+
+		/* Indexing */
+	case PROP_THROTTLE:
+		tracker_config_set_throttle (TRACKER_CONFIG (object),
+					     g_value_get_int (value));
+		break;
+	case PROP_ENABLE_THUMBNAILS:
+		tracker_config_set_enable_thumbnails (TRACKER_CONFIG (object),
+						      g_value_get_boolean (value));
+		break;
+	case PROP_DISABLED_MODULES:
+		tracker_config_set_disabled_modules (TRACKER_CONFIG (object),
+						     g_value_get_pointer (value));
+		break;
+	case PROP_DISABLE_INDEXING_ON_BATTERY:
+		tracker_config_set_disable_indexing_on_battery (TRACKER_CONFIG (object),
+								g_value_get_boolean (value));
+		break;
+	case PROP_DISABLE_INDEXING_ON_BATTERY_INIT:
+		tracker_config_set_disable_indexing_on_battery_init (TRACKER_CONFIG (object),
+								     g_value_get_boolean (value));
+		break;
+	case PROP_LOW_DISK_SPACE_LIMIT:
+		tracker_config_set_low_disk_space_limit (TRACKER_CONFIG (object),
+							 g_value_get_int (value));
+		break;
+	case PROP_INDEX_MOUNTED_DIRECTORIES:
+		tracker_config_set_index_mounted_directories (TRACKER_CONFIG (object),
+							      g_value_get_boolean (value));
+		break;
+	case PROP_INDEX_REMOVABLE_DEVICES:
+		tracker_config_set_index_removable_devices (TRACKER_CONFIG (object),
+							    g_value_get_boolean (value));
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+config_get_property (GObject	*object,
+		     guint	 param_id,
+		     GValue	*value,
+		     GParamSpec *pspec)
+{
+	TrackerConfigPrivate *priv;
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (object);
+
+	switch (param_id) {
+		/* General */
+	case PROP_VERBOSITY:
+		g_value_set_int (value, priv->verbosity);
+		break;
+	case PROP_INITIAL_SLEEP:
+		g_value_set_int (value, priv->initial_sleep);
+		break;
+
+		/* Watches */
+	case PROP_WATCH_DIRECTORY_ROOTS:
+		g_value_set_pointer (value, priv->watch_directory_roots);
+		break;
+	case PROP_CRAWL_DIRECTORY_ROOTS:
+		g_value_set_pointer (value, priv->crawl_directory_roots);
+		break;
+	case PROP_NO_WATCH_DIRECTORY_ROOTS:
+		g_value_set_pointer (value, priv->no_watch_directory_roots);
+		break;
+	case PROP_ENABLE_WATCHES:
+		g_value_set_boolean (value, priv->enable_watches);
+		break;
+
+		/* Indexing */
+	case PROP_THROTTLE:
+		g_value_set_int (value, priv->throttle);
+		break;
+	case PROP_ENABLE_THUMBNAILS:
+		g_value_set_boolean (value, priv->enable_thumbnails);
+		break;
+	case PROP_DISABLED_MODULES:
+		g_value_set_pointer (value, priv->disabled_modules);
+		break;
+	case PROP_DISABLE_INDEXING_ON_BATTERY:
+		g_value_set_boolean (value, priv->disable_indexing_on_battery);
+		break;
+	case PROP_DISABLE_INDEXING_ON_BATTERY_INIT:
+		g_value_set_boolean (value, priv->disable_indexing_on_battery_init);
+		break;
+	case PROP_LOW_DISK_SPACE_LIMIT:
+		g_value_set_int (value, priv->low_disk_space_limit);
+		break;
+	case PROP_INDEX_MOUNTED_DIRECTORIES:
+		g_value_set_boolean (value, priv->index_mounted_directories);
+		break;
+	case PROP_INDEX_REMOVABLE_DEVICES:
+		g_value_set_boolean (value, priv->index_removable_devices);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+config_finalize (GObject *object)
+{
+	TrackerConfigPrivate *priv;
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (object);
+
+	g_slist_foreach (priv->watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->watch_directory_roots);
+
+	g_slist_foreach (priv->crawl_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->crawl_directory_roots);
+
+	g_slist_foreach (priv->no_watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->no_watch_directory_roots);
+
+	g_slist_foreach (priv->disabled_modules, (GFunc) g_free, NULL);
+	g_slist_free (priv->disabled_modules);
+
+	(G_OBJECT_CLASS (tracker_config_parent_class)->finalize) (object);
+}
+
+static void
+config_constructed (GObject *object)
+{
+	(G_OBJECT_CLASS (tracker_config_parent_class)->constructed) (object);
+
+	config_load (TRACKER_CONFIG (object));
+}
+
+static void
+config_create_with_defaults (TrackerConfig *config,
+			     GKeyFile      *key_file, 
+			     gboolean       overwrite)
+{
+	gint i;
+
+	g_message ("Loading defaults into GKeyFile...");
+	
+	for (i = 0; i < G_N_ELEMENTS (conversions); i++) {
+		gboolean has_key;
+		
+		has_key = g_key_file_has_key (key_file, 
+					      conversions[i].group, 
+					      conversions[i].key, 
+					      NULL);
+		if (!overwrite && has_key) {
+			continue;
+		}
+		
+		switch (conversions[i].type) {
+		case G_TYPE_INT:
+			g_key_file_set_integer (key_file, 
+						conversions[i].group, 
+						conversions[i].key, 
+						tracker_keyfile_object_default_int (config, 
+										    conversions[i].property));
+			break;
+
+		case G_TYPE_BOOLEAN:
+			g_key_file_set_boolean (key_file, 
+						conversions[i].group, 
+						conversions[i].key, 
+						tracker_keyfile_object_default_boolean (config, 
+											conversions[i].property));
+			break;
+
+		case G_TYPE_POINTER:
+			/* Special case string lists */
+			if (g_strcmp0 (conversions[i].property, "watch-directory-roots") == 0) {
+				const gchar *string_list[] = { NULL, NULL };
+
+				string_list[0] = g_get_home_dir ();
+
+				g_key_file_set_string_list (key_file, 
+							    conversions[i].group, 
+							    conversions[i].key, 
+							    string_list, 
+							    G_N_ELEMENTS (string_list));
+			} else {
+				const gchar *string_list[] = { NULL };
+
+				g_key_file_set_string_list (key_file, 
+							    conversions[i].group, 
+							    conversions[i].key, 
+							    string_list, 
+							    G_N_ELEMENTS (string_list));
+			}
+
+			break;
+
+		default:
+			g_assert_not_reached ();
+		}
+
+		g_key_file_set_comment (key_file, 
+					conversions[i].group, 
+					conversions[i].key, 
+					tracker_keyfile_object_blurb (config,
+								      conversions[i].property), 
+					NULL);
+	}
+}
+
+static void
+config_load (TrackerConfig *config)
+{
+	TrackerConfigFile *file;
+	gint i;
+
+	file = TRACKER_CONFIG_FILE (config);
+	config_create_with_defaults (config, file->key_file, FALSE);
+
+	if (!file->file_exists) {
+		tracker_config_file_save (file);
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (conversions); i++) {
+		gboolean has_key;
+		gboolean is_directory_list;
+		
+		has_key = g_key_file_has_key (file->key_file, 
+					      conversions[i].group, 
+					      conversions[i].key, 
+					      NULL);
+	
+		switch (conversions[i].type) {
+		case G_TYPE_INT:
+			tracker_keyfile_object_load_int (G_OBJECT (file), 
+							 conversions[i].property,
+							 file->key_file,
+							 conversions[i].group, 
+							 conversions[i].key);
+			break;
+
+		case G_TYPE_BOOLEAN:
+			tracker_keyfile_object_load_boolean (G_OBJECT (file), 
+							     conversions[i].property,
+							     file->key_file,
+							     conversions[i].group, 
+							     conversions[i].key);
+			break;
+
+		case G_TYPE_POINTER:
+			if (g_strcmp0 (conversions[i].property, "disabled-modules") == 0) {
+				is_directory_list = FALSE;
+			} else {
+				is_directory_list = TRUE;
+			}
+
+			tracker_keyfile_object_load_string_list (G_OBJECT (file), 
+								 conversions[i].property,
+								 file->key_file, 
+								 conversions[i].group, 
+								 conversions[i].key,
+								 is_directory_list);
+			break;
+		}
+	}
+}
+
+static gboolean
+config_save (TrackerConfig *config)
+{
+	TrackerConfigFile *file;
+	gint i;
+
+	file = TRACKER_CONFIG_FILE (config);
+
+	if (!file->key_file) {
+		g_critical ("Could not save config, GKeyFile was NULL, has the config been loaded?");
+
+		return FALSE;
+	}
+
+	g_message ("Setting details to GKeyFile object...");
+
+	for (i = 0; i < G_N_ELEMENTS (conversions); i++) {
+		switch (conversions[i].type) {
+		case G_TYPE_INT:
+			tracker_keyfile_object_save_int (file,
+							 conversions[i].property, 
+							 file->key_file,
+							 conversions[i].group, 
+							 conversions[i].key);
+			break;
+
+		case G_TYPE_BOOLEAN:
+			tracker_keyfile_object_save_boolean (file,
+							     conversions[i].property, 
+							     file->key_file,
+							     conversions[i].group, 
+							     conversions[i].key);
+			break;
+
+		case G_TYPE_POINTER:
+			tracker_keyfile_object_save_string_list (file,
+								 conversions[i].property, 
+								 file->key_file,
+								 conversions[i].group, 
+								 conversions[i].key);
+			break;
+
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+	}
+
+	return tracker_config_file_save (file);
+}
+
+TrackerConfig *
+tracker_config_new (void)
+{
+	return g_object_new (TRACKER_TYPE_CONFIG,
+			     "domain", "foo",
+			     NULL);
+}
+
+gboolean
+tracker_config_save (TrackerConfig *config)
+{
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), FALSE);
+
+	return config_save (config);
+}
+
+gint
+tracker_config_get_verbosity (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_VERBOSITY);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->verbosity;
+}
+
+gint
+tracker_config_get_initial_sleep (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_INITIAL_SLEEP);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->initial_sleep;
+}
+
+GSList *
+tracker_config_get_watch_directory_roots (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->watch_directory_roots;
+}
+
+GSList *
+tracker_config_get_crawl_directory_roots (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->crawl_directory_roots;
+}
+
+GSList *
+tracker_config_get_no_watch_directory_roots (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->no_watch_directory_roots;
+}
+
+gboolean
+tracker_config_get_enable_watches (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_WATCHES);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->enable_watches;
+}
+
+gint
+tracker_config_get_throttle (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_THROTTLE);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->throttle;
+}
+
+gboolean
+tracker_config_get_enable_thumbnails (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_THUMBNAILS);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->enable_thumbnails;
+}
+
+GSList *
+tracker_config_get_disabled_modules (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->disabled_modules;
+}
+
+gboolean
+tracker_config_get_disable_indexing_on_battery (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_DISABLE_INDEXING_ON_BATTERY);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->disable_indexing_on_battery;
+}
+
+gboolean
+tracker_config_get_disable_indexing_on_battery_init (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->disable_indexing_on_battery_init;
+}
+
+gint
+tracker_config_get_low_disk_space_limit (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_LOW_DISK_SPACE_LIMIT);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->low_disk_space_limit;
+}
+
+gboolean
+tracker_config_get_index_mounted_directories (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_INDEX_MOUNTED_DIRECTORIES);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->index_mounted_directories;
+}
+
+gboolean
+tracker_config_get_index_removable_devices (TrackerConfig *config)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_INDEX_REMOVABLE_DEVICES);
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	return priv->index_removable_devices;
+}
+
+void
+tracker_config_set_verbosity (TrackerConfig *config,
+			      gint	     value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!tracker_keyfile_object_validate_int (config, "verbosity", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->verbosity = value;
+	g_object_notify (G_OBJECT (config), "verbosity");
+}
+
+void
+tracker_config_set_initial_sleep (TrackerConfig *config,
+				  gint		 value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!tracker_keyfile_object_validate_int (config, "initial-sleep", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->initial_sleep = value;
+	g_object_notify (G_OBJECT (config), "initial-sleep");
+}
+
+void
+tracker_config_set_enable_watches (TrackerConfig *config,
+				   gboolean	  value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->enable_watches = value;
+	g_object_notify (G_OBJECT (config), "enable-watches");
+}
+
+void
+tracker_config_set_throttle (TrackerConfig *config,
+			     gint	    value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!tracker_keyfile_object_validate_int (config, "throttle", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->throttle = value;
+	g_object_notify (G_OBJECT (config), "throttle");
+}
+
+void
+tracker_config_set_enable_thumbnails (TrackerConfig *config,
+				      gboolean	     value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->enable_thumbnails = value;
+	g_object_notify (G_OBJECT (config), "enable-thumbnails");
+}
+
+void
+tracker_config_set_disable_indexing_on_battery (TrackerConfig *config,
+						gboolean       value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->disable_indexing_on_battery = value;
+	g_object_notify (G_OBJECT (config), "disable-indexing-on-battery");
+}
+
+void
+tracker_config_set_disable_indexing_on_battery_init (TrackerConfig *config,
+						     gboolean	    value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->disable_indexing_on_battery_init = value;
+	g_object_notify (G_OBJECT (config), "disable-indexing-on-battery-init");
+}
+
+void
+tracker_config_set_low_disk_space_limit (TrackerConfig *config,
+					 gint		value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	if (!tracker_keyfile_object_validate_int (config, "low-disk-space-limit", value)) {
+		return;
+	}
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->low_disk_space_limit = value;
+	g_object_notify (G_OBJECT (config), "low-disk-space-limit");
+}
+
+void
+tracker_config_set_index_mounted_directories (TrackerConfig *config,
+					      gboolean	     value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->index_mounted_directories = value;
+	g_object_notify (G_OBJECT (config), "index-mounted-directories");
+}
+
+void
+tracker_config_set_index_removable_devices (TrackerConfig *config,
+					    gboolean	 value)
+{
+	TrackerConfigPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	priv->index_removable_devices = value;
+	g_object_notify (G_OBJECT (config), "index-removable-devices");
+}
+
+void	       
+tracker_config_set_watch_directory_roots (TrackerConfig *config,
+					  GSList        *roots)
+{
+	TrackerConfigPrivate *priv;
+	GSList               *l;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	l = priv->watch_directory_roots;
+
+	if (!roots) {
+		priv->watch_directory_roots = NULL;
+	} else {
+		priv->watch_directory_roots = tracker_gslist_copy_with_string_data (roots);
+	}
+
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	g_object_notify (G_OBJECT (config), "watch-directory-roots");
+}
+
+void	       
+tracker_config_set_crawl_directory_roots (TrackerConfig *config,
+					  GSList        *roots)
+{
+	TrackerConfigPrivate *priv;
+	GSList               *l;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	l = priv->crawl_directory_roots;
+
+	if (!roots) {
+		priv->crawl_directory_roots = NULL;
+	} else {
+		priv->crawl_directory_roots = tracker_gslist_copy_with_string_data (roots);
+	}
+
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	g_object_notify (G_OBJECT (config), "crawl-directory-roots");
+}
+
+void	       
+tracker_config_set_no_watch_directory_roots (TrackerConfig *config,
+					     GSList        *roots)
+{
+	TrackerConfigPrivate *priv;
+	GSList               *l;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+	
+	l = priv->no_watch_directory_roots;
+
+	if (!roots) {
+		priv->no_watch_directory_roots = NULL;
+	} else {
+		priv->no_watch_directory_roots = tracker_gslist_copy_with_string_data (roots);
+	}
+
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	g_object_notify (G_OBJECT (config), "no-watch-directory-roots");
+}
+
+void	       
+tracker_config_set_disabled_modules (TrackerConfig *config,
+				     GSList        *modules)
+{
+	TrackerConfigPrivate *priv;
+	GSList               *l;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+	l = priv->disabled_modules;
+
+	if (!modules) {
+		priv->disabled_modules = NULL;
+	} else {
+		priv->disabled_modules = tracker_gslist_copy_with_string_data (modules);
+	}
+
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	g_object_notify (G_OBJECT (config), "disabled-modules");
+}
diff --git a/src/libtracker-miner/tracker-config.h b/src/libtracker-miner/tracker-config.h
new file mode 100644
index 0000000..446e2b0
--- /dev/null
+++ b/src/libtracker-miner/tracker-config.h
@@ -0,0 +1,119 @@
+/* -*- 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 General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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_FS_CONFIG_H__
+#define __TRACKER_MINER_FS_CONFIG_H__
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-config-file.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_CONFIG	    (tracker_config_get_type ())
+#define TRACKER_CONFIG(o)	    (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_CONFIG, TrackerConfig))
+#define TRACKER_CONFIG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), TRACKER_TYPE_CONFIG, TrackerConfigClass))
+#define TRACKER_IS_CONFIG(o)	    (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_CONFIG))
+#define TRACKER_IS_CONFIG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), TRACKER_TYPE_CONFIG))
+#define TRACKER_CONFIG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_CONFIG, TrackerConfigClass))
+
+typedef struct TrackerConfig	  TrackerConfig;
+typedef struct TrackerConfigClass TrackerConfigClass;
+
+struct TrackerConfig {
+	TrackerConfigFile parent;
+};
+
+struct TrackerConfigClass {
+	TrackerConfigFileClass parent_class;
+};
+
+GType	       tracker_config_get_type				   (void) G_GNUC_CONST;
+
+TrackerConfig *tracker_config_new				   (void);
+gboolean       tracker_config_save                                 (TrackerConfig *config);
+
+gint	       tracker_config_get_verbosity			   (TrackerConfig *config);
+gint	       tracker_config_get_initial_sleep			   (TrackerConfig *config);
+GSList *       tracker_config_get_watch_directory_roots		   (TrackerConfig *config);
+GSList *       tracker_config_get_crawl_directory_roots		   (TrackerConfig *config);
+GSList *       tracker_config_get_no_watch_directory_roots	   (TrackerConfig *config);
+gboolean       tracker_config_get_enable_watches		   (TrackerConfig *config);
+gint	       tracker_config_get_throttle			   (TrackerConfig *config);
+gboolean       tracker_config_get_enable_thumbnails		   (TrackerConfig *config);
+GSList *       tracker_config_get_disabled_modules		   (TrackerConfig *config);
+gboolean       tracker_config_get_disable_indexing_on_battery	   (TrackerConfig *config);
+gboolean       tracker_config_get_disable_indexing_on_battery_init (TrackerConfig *config);
+gint	       tracker_config_get_low_disk_space_limit		   (TrackerConfig *config);
+gboolean       tracker_config_get_index_removable_devices	   (TrackerConfig *config);
+gboolean       tracker_config_get_index_mounted_directories	   (TrackerConfig *config);
+
+void	       tracker_config_set_verbosity			   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_initial_sleep			   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_enable_watches		   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_throttle			   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_enable_thumbnails		   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_disable_indexing_on_battery	   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_disable_indexing_on_battery_init (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_low_disk_space_limit		   (TrackerConfig *config,
+								    gint	   value);
+void	       tracker_config_set_index_removable_devices	   (TrackerConfig *config,
+								    gboolean	   value);
+void	       tracker_config_set_index_mounted_directories	   (TrackerConfig *config,
+								    gboolean	   value);
+
+/* List APIs*/
+void	       tracker_config_add_watch_directory_roots		   (TrackerConfig *config,
+								    gchar * const *roots);
+void	       tracker_config_add_crawl_directory_roots		   (TrackerConfig *config,
+								    gchar * const *roots);
+void	       tracker_config_add_no_watch_directory_roots	   (TrackerConfig *config,
+								    gchar * const *roots);
+void	       tracker_config_add_disabled_modules		   (TrackerConfig *config,
+								    const gchar * const *modules);
+void	       tracker_config_remove_watch_directory_roots         (TrackerConfig *config,
+								    const gchar   *root);
+void	       tracker_config_remove_crawl_directory_roots         (TrackerConfig *config,
+								    const gchar   *root);
+void	       tracker_config_remove_no_watch_directory_roots      (TrackerConfig *config,
+								    const gchar   *root);
+void	       tracker_config_remove_disabled_modules		   (TrackerConfig *config,
+								    const gchar   *module);
+
+void	       tracker_config_set_watch_directory_roots		   (TrackerConfig *config,
+								    GSList        *roots);
+void	       tracker_config_set_crawl_directory_roots		   (TrackerConfig *config,
+								    GSList        *roots);
+void	       tracker_config_set_no_watch_directory_roots	   (TrackerConfig *config,
+								    GSList        *roots);
+void	       tracker_config_set_disabled_modules		   (TrackerConfig *config,
+								    GSList        *modules);
+
+G_END_DECLS
+
+#endif /* __TRACKER_MINER_FS_CONFIG_H__ */
+
diff --git a/src/libtracker-miner/tracker-crawler.c b/src/libtracker-miner/tracker-crawler.c
new file mode 100644
index 0000000..1cc1be3
--- /dev/null
+++ b/src/libtracker-miner/tracker-crawler.c
@@ -0,0 +1,1236 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-utils.h>
+#include <libtracker-common/tracker-module-config.h>
+
+#include "tracker-config.h"
+#include "tracker-crawler.h"
+#include "tracker-dbus.h"
+#include "tracker-monitor.h"
+#include "tracker-marshal.h"
+#include "tracker-status.h"
+#include "tracker-utils.h"
+
+#define TRACKER_CRAWLER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_CRAWLER, TrackerCrawlerPrivate))
+
+#define FILE_ATTRIBUTES				\
+	G_FILE_ATTRIBUTE_STANDARD_NAME ","	\
+	G_FILE_ATTRIBUTE_STANDARD_TYPE
+
+#define FILES_QUEUE_PROCESS_INTERVAL 2000
+#define FILES_QUEUE_PROCESS_MAX      5000
+
+/* This is the number of files to be called back with from GIO at a
+ * time so we don't get called back for every file.
+ */
+#define FILES_GROUP_SIZE	     100
+
+struct _TrackerCrawlerPrivate {
+	TrackerConfig  *config;
+
+	gchar	       *module_name;
+
+	/* Found data */
+	GQueue	       *directories;
+	GQueue	       *files;
+
+	/* Idle handler for processing found data */
+	guint		idle_id;
+
+	/* Options */
+	gboolean	use_module_paths;
+
+	/* Actual paths that exist which we are crawling:
+	 *
+	 *  - 'Paths' are non-recursive.
+	 *  - 'Recurse Paths' are recursive.
+	 *  - 'Special Paths' are recursive but not in module config.
+	 */
+	GSList	       *paths;
+	GSList	       *paths_current;
+	gboolean	paths_are_done;
+
+	GSList	       *recurse_paths;
+	GSList	       *recurse_paths_current;
+	gboolean	recurse_paths_are_done;
+
+	GSList	       *special_paths;
+	GSList	       *special_paths_current;
+	gboolean	special_paths_are_done;
+
+	/* Ignore/Index patterns */
+	GList	       *ignored_directory_patterns;
+	GList	       *ignored_file_patterns;
+	GList	       *index_file_patterns;
+	GList          *ignored_directories_with_content;
+
+	/* Legacy NoWatchDirectoryRoots */
+	GSList	       *no_watch_directory_roots;
+	GSList	       *watch_directory_roots;
+	GSList	       *crawl_directory_roots;
+
+	/* Statistics */
+	GTimer	       *timer;
+	guint		enumerations;
+	guint		directories_found;
+	guint		directories_ignored;
+	guint		files_found;
+	guint		files_ignored;
+
+	/* Status */
+	gboolean	is_running;
+	gboolean	is_finished;
+	gboolean        was_started;
+};
+
+enum {
+	PROCESSING_DIRECTORY,
+	PROCESSING_FILE,
+	FINISHED,
+	LAST_SIGNAL
+};
+
+typedef struct {
+	GFile *child;
+	gboolean is_dir;
+} EnumeratorChildData;
+
+typedef struct {
+	TrackerCrawler *crawler;
+	GFile	       *parent;
+	GHashTable     *children;
+} EnumeratorData;
+
+static void tracker_crawler_finalize (GObject	      *object);
+static void file_enumerate_next      (GFileEnumerator *enumerator,
+				      EnumeratorData  *ed);
+static void file_enumerate_children  (TrackerCrawler  *crawler,
+				      GFile	      *file);
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE(TrackerCrawler, tracker_crawler, G_TYPE_OBJECT)
+
+static void
+tracker_crawler_class_init (TrackerCrawlerClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_crawler_finalize;
+
+	signals[PROCESSING_DIRECTORY] =
+		g_signal_new ("processing-directory",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT,
+			      G_TYPE_NONE,
+			      2,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT);
+	signals[PROCESSING_FILE] =
+		g_signal_new ("processing-file",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT,
+			      G_TYPE_NONE,
+			      2,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT);
+	signals[FINISHED] =
+		g_signal_new ("finished",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_UINT_UINT_UINT_UINT,
+			      G_TYPE_NONE,
+			      5,
+			      G_TYPE_STRING,
+			      G_TYPE_UINT,
+			      G_TYPE_UINT,
+			      G_TYPE_UINT,
+			      G_TYPE_UINT);
+
+	g_type_class_add_private (object_class, sizeof (TrackerCrawlerPrivate));
+}
+
+static void
+tracker_crawler_init (TrackerCrawler *object)
+{
+	TrackerCrawlerPrivate *priv;
+
+	object->private = TRACKER_CRAWLER_GET_PRIVATE (object);
+
+	priv = object->private;
+
+	priv->directories = g_queue_new ();
+	priv->files = g_queue_new ();
+}
+
+static void
+tracker_crawler_finalize (GObject *object)
+{
+	TrackerCrawlerPrivate *priv;
+
+	priv = TRACKER_CRAWLER_GET_PRIVATE (object);
+
+	if (priv->timer) {
+		g_timer_destroy (priv->timer);
+	}
+
+	g_slist_foreach (priv->no_watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->no_watch_directory_roots);
+
+	g_slist_foreach (priv->watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->watch_directory_roots);
+
+	g_slist_foreach (priv->crawl_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->crawl_directory_roots);
+
+	if (priv->index_file_patterns) {
+		g_list_free (priv->index_file_patterns);
+	}
+
+	if (priv->ignored_directory_patterns) {
+		g_list_free (priv->ignored_directory_patterns);
+	}
+
+	if (priv->ignored_file_patterns) {
+		g_list_free (priv->ignored_file_patterns);
+	}
+
+	if (priv->ignored_directories_with_content) {
+		g_list_free (priv->ignored_directories_with_content);
+	}
+
+	/* Don't free the 'current_' variant of these, they are just
+	 * place holders so we know our status.
+	 */
+	g_slist_foreach (priv->paths, (GFunc) g_free, NULL);
+	g_slist_free (priv->paths);
+
+	g_slist_foreach (priv->recurse_paths, (GFunc) g_free, NULL);
+	g_slist_free (priv->recurse_paths);
+
+	g_slist_foreach (priv->special_paths, (GFunc) g_free, NULL);
+	g_slist_free (priv->special_paths);
+
+	if (priv->idle_id) {
+		g_source_remove (priv->idle_id);
+	}
+
+	g_queue_foreach (priv->files, (GFunc) g_object_unref, NULL);
+	g_queue_free (priv->files);
+
+	g_queue_foreach (priv->directories, (GFunc) g_object_unref, NULL);
+	g_queue_free (priv->directories);
+
+	g_free (priv->module_name);
+
+	g_object_unref (priv->config);
+
+	G_OBJECT_CLASS (tracker_crawler_parent_class)->finalize (object);
+}
+
+TrackerCrawler *
+tracker_crawler_new (TrackerConfig *config,
+		     const gchar   *module_name)
+{
+	TrackerCrawler *crawler;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+	g_return_val_if_fail (module_name != NULL, NULL);
+
+	crawler = g_object_new (TRACKER_TYPE_CRAWLER, NULL);
+
+	crawler->private->config = g_object_ref (config);
+
+	crawler->private->module_name = g_strdup (module_name);
+
+	/* Set up crawl data */
+	crawler->private->ignored_directory_patterns =
+		tracker_module_config_get_ignored_directory_patterns (module_name);
+	crawler->private->ignored_file_patterns =
+		tracker_module_config_get_ignored_file_patterns (module_name);
+	crawler->private->index_file_patterns =
+		tracker_module_config_get_index_file_patterns (module_name);
+	crawler->private->ignored_directories_with_content =
+		tracker_module_config_get_ignored_directories_with_content (module_name);
+
+	/* Should we use module config paths? If true, when we
+	 * _start() the module config paths are used to import paths
+	 * to crawl. By default this is TRUE.
+	 */
+	crawler->private->use_module_paths = TRUE;
+
+	return crawler;
+}
+
+/*
+ * Functions
+ */
+
+static gboolean
+is_path_ignored (TrackerCrawler *crawler,
+		 const gchar	*path,
+		 gboolean	 is_directory)
+{
+	GList	 *l;
+	gchar	 *basename;
+	gboolean  ignore;
+
+	if (tracker_is_empty_string (path)) {
+		return TRUE;
+	}
+
+	if (!g_utf8_validate (path, -1, NULL)) {
+		g_message ("Ignoring path:'%s', not valid UTF-8", path);
+		return TRUE;
+	}
+
+	if (is_directory) {
+		GSList *sl;
+
+		/* Most common things to ignore */
+		if (strcmp (path, "/dev") == 0 ||
+		    strcmp (path, "/lib") == 0 ||
+		    strcmp (path, "/proc") == 0 ||
+		    strcmp (path, "/sys") == 0) {
+			return TRUE;
+		}
+
+		if (g_str_has_prefix (path, g_get_tmp_dir ())) {
+			return TRUE;
+		}
+
+		/* Check ignored directories in config */
+		for (sl = crawler->private->no_watch_directory_roots; sl; sl = sl->next) {
+			if (strcmp (sl->data, path) == 0) {
+				return TRUE;
+			}
+		}
+	}
+
+	/* Check basename against pattern matches */
+	basename = g_path_get_basename (path);
+	ignore = TRUE;
+
+	if (!basename) {
+		goto done;
+	}
+
+	/* Test ignore types */
+	if (is_directory) {
+		GSList *sl;
+
+		/* If directory begins with ".", check it isn't one of
+		 * the top level directories to watch/crawl if it
+		 * isn't we ignore it. If it is, we don't.
+		 */
+		if (basename[0] == '.') {
+			for (sl = crawler->private->watch_directory_roots; sl; sl = sl->next) {
+				if (strcmp (sl->data, path) == 0) {
+					ignore = FALSE;
+					goto done;
+				}
+			}
+
+			for (sl = crawler->private->crawl_directory_roots; sl; sl = sl->next) {
+				if (strcmp (sl->data, path) == 0) {
+					ignore = FALSE;
+					goto done;
+				}
+			}
+
+			goto done;
+		}
+
+		/* Check module directory ignore patterns */
+		for (l = crawler->private->ignored_directory_patterns; l; l = l->next) {
+			if (g_pattern_match_string (l->data, basename)) {
+				goto done;
+			}
+		}
+	} else {
+		if (basename[0] == '.') {
+			goto done;
+		}
+
+		/* Check module file ignore patterns */
+		for (l = crawler->private->ignored_file_patterns; l; l = l->next) {
+			if (g_pattern_match_string (l->data, basename)) {
+				goto done;
+			}
+		}
+
+		/* Check module file match patterns */
+		for (l = crawler->private->index_file_patterns; l; l = l->next) {
+			if (!g_pattern_match_string (l->data, basename)) {
+				goto done;
+			}
+		}
+	}
+
+	ignore = FALSE;
+
+done:
+	g_free (basename);
+
+	return ignore;
+}
+
+static void
+add_file (TrackerCrawler *crawler,
+	  GFile		 *file)
+{
+	gchar *path;
+
+	g_return_if_fail (G_IS_FILE (file));
+
+	path = g_file_get_path (file);
+
+	if (is_path_ignored (crawler, path, FALSE)) {
+		crawler->private->files_ignored++;
+
+		g_debug ("Ignored:'%s' (%d)",
+			 path,
+			 crawler->private->enumerations);
+	} else {
+		crawler->private->files_found++;
+
+		g_debug ("Found  :'%s' (%d)",
+			 path,
+			 crawler->private->enumerations);
+
+		g_queue_push_tail (crawler->private->files, g_object_ref (file));
+	}
+
+	g_free (path);
+}
+
+static void
+add_directory (TrackerCrawler *crawler,
+	       GFile	      *file)
+{
+	gchar *path;
+
+	g_return_if_fail (G_IS_FILE (file));
+
+	path = g_file_get_path (file);
+
+	if (is_path_ignored (crawler, path, TRUE)) {
+		crawler->private->directories_ignored++;
+
+		g_debug ("Ignored:'%s' (%d)",
+			 path,
+			 crawler->private->enumerations);
+	} else {
+		g_debug ("Found  :'%s' (%d)",
+			 path,
+			 crawler->private->enumerations);
+
+		g_queue_push_tail (crawler->private->directories, g_object_ref (file));
+	}
+
+	g_free (path);
+}
+
+static void
+process_file (TrackerCrawler *crawler,
+	      GFile	     *file)
+{
+	g_signal_emit (crawler, signals[PROCESSING_FILE], 0,
+		       crawler->private->module_name, file);
+}
+
+static void
+process_directory (TrackerCrawler *crawler,
+		   GFile	  *file)
+{
+	file_enumerate_children (crawler, file);
+}
+
+static gboolean
+process_func (gpointer data)
+{
+	TrackerCrawler	      *crawler;
+	TrackerCrawlerPrivate *priv;
+	GFile		      *file;
+
+	crawler = TRACKER_CRAWLER (data);
+	priv = crawler->private;
+
+	/* If manually paused, we hold off until unpaused */
+	if (tracker_status_get_is_paused_manually () ||
+	    tracker_status_get_is_paused_for_io ()) {
+		return TRUE;
+	}
+
+	/* Throttle the crawler, with testing, throttling every item
+	 * took the time to crawl 130k files from 7 seconds up to 68
+	 * seconds. So it is important to get this figure right.
+	 */
+	tracker_throttle (priv->config, 25);
+
+	/* Crawler files */
+	file = g_queue_pop_head (priv->files);
+
+	if (file) {
+		process_file (crawler, file);
+		g_object_unref (file);
+
+		return TRUE;
+	}
+
+	/* Crawler directories */
+	file = g_queue_pop_head (priv->directories);
+
+	if (file) {
+		process_directory (crawler, file);
+		g_object_unref (file);
+
+		return TRUE;
+	}
+
+	/* If we still have some async operations in progress, wait
+	 * for them to finish, if not, we are truly done.
+	 */
+	if (priv->enumerations > 0) {
+		return TRUE;
+	}
+
+	/* Process next path in list */
+	if (!priv->paths_are_done) {
+		/* This is done so we don't go over the list again
+		 * when we get to the end and the current item = NULL.
+		 */
+		priv->paths_are_done = TRUE;
+
+		if (!priv->paths_current) {
+			priv->paths_current = priv->paths;
+		}
+	} else {
+		if (priv->paths_current) {
+			priv->paths_current = priv->paths_current->next;
+		}
+	}
+
+	if (priv->paths_current) {
+		g_message ("  Searching directory:'%s'",
+			   (gchar*) priv->paths_current->data);
+
+		file = g_file_new_for_path (priv->paths_current->data);
+		add_directory (crawler, file);
+		g_object_unref (file);
+
+		return TRUE;
+	}
+
+	/* Process next recursive path in list */
+	if (!priv->recurse_paths_are_done) {
+		/* This is done so we don't go over the list again
+		 * when we get to the end and the current item = NULL.
+		 */
+		priv->recurse_paths_are_done = TRUE;
+
+		if (!priv->recurse_paths_current) {
+			priv->recurse_paths_current = priv->recurse_paths;
+		}
+	} else {
+		if (priv->recurse_paths_current) {
+			priv->recurse_paths_current = priv->recurse_paths_current->next;
+		}
+	}
+
+	if (priv->recurse_paths_current) {
+		g_message ("  Searching directory:'%s' (recursively)",
+			   (gchar *) priv->recurse_paths_current->data);
+
+		file = g_file_new_for_path (priv->recurse_paths_current->data);
+		add_directory (crawler, file);
+		g_object_unref (file);
+
+		return TRUE;
+	}
+
+	/* Process next special path in list */
+	if (!priv->special_paths_are_done) {
+		/* This is done so we don't go over the list again
+		 * when we get to the end and the current item = NULL.
+		 */
+		priv->special_paths_are_done = TRUE;
+
+		if (!priv->special_paths_current) {
+			priv->special_paths_current = priv->special_paths;
+		}
+	} else {
+		if (priv->special_paths_current) {
+			priv->special_paths_current = priv->special_paths_current->next;
+		}
+	}
+
+	if (priv->special_paths_current) {
+		g_message ("  Searching directory:'%s' (special)",
+			   (gchar *) priv->special_paths_current->data);
+
+		file = g_file_new_for_path (priv->special_paths_current->data);
+		add_directory (crawler, file);
+		g_object_unref (file);
+
+		return TRUE;
+	}
+
+	priv->idle_id = 0;
+	priv->is_finished = TRUE;
+
+	tracker_crawler_stop (crawler);
+
+	return FALSE;
+}
+
+static EnumeratorChildData *
+enumerator_child_data_new (GFile    *child,
+			   gboolean  is_dir)
+{
+	EnumeratorChildData *cd;
+
+	cd = g_slice_new (EnumeratorChildData);
+
+	cd->child = g_object_ref (child);
+	cd->is_dir = is_dir;
+
+	return cd;
+}
+
+static void
+enumerator_child_data_free (EnumeratorChildData *cd)
+{
+	g_object_unref (cd->child);
+	g_slice_free (EnumeratorChildData, cd);
+}
+
+static EnumeratorData *
+enumerator_data_new (TrackerCrawler *crawler,
+		     GFile	    *parent)
+{
+	EnumeratorData *ed;
+
+	ed = g_slice_new0 (EnumeratorData);
+
+	ed->crawler = g_object_ref (crawler);
+	ed->parent = g_object_ref (parent);
+	ed->children = g_hash_table_new_full (g_str_hash,
+					      g_str_equal,
+					      (GDestroyNotify) g_free,
+					      (GDestroyNotify) enumerator_child_data_free);
+	return ed;
+}
+
+static void
+enumerator_data_add_child (EnumeratorData *ed,
+			   const gchar    *name,
+			   GFile          *file,
+			   gboolean        is_dir)
+{
+	g_hash_table_insert (ed->children,
+			     g_strdup (name),
+			     enumerator_child_data_new (file, is_dir));
+}
+
+static void
+enumerator_data_process (EnumeratorData *ed)
+{
+	TrackerCrawler *crawler;
+	GHashTableIter iter;
+	EnumeratorChildData *cd;
+	GList *l;
+
+	crawler = ed->crawler;
+
+	/* Ignore directory if its contents match something we should ignore */
+	for (l = crawler->private->ignored_directories_with_content; l; l = l->next) {
+		if (g_hash_table_lookup (ed->children, l->data)) {
+			gchar *path;
+
+			path = g_file_get_path (ed->parent);
+
+			crawler->private->directories_ignored++;
+			g_debug ("Ignoring directory '%s' since it contains a file named '%s'", path, (gchar *) l->data);
+			g_free (path);
+
+			return;
+		}
+	}
+
+	crawler->private->directories_found++;
+	g_signal_emit (crawler, signals[PROCESSING_DIRECTORY], 0,
+		       crawler->private->module_name, ed->parent);
+
+	g_hash_table_iter_init (&iter, ed->children);
+
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cd)) {
+		if (cd->is_dir) {
+			/* This is a bit of a hack, but we assume this is a
+			 * recursive lookup because the current non-recursive
+			 * path is NULL, meaning they have all been traversed
+			 * already.
+			 */
+			if (crawler->private->paths_are_done) {
+				add_directory (crawler, cd->child);
+			}
+		} else {
+			add_file (crawler, cd->child);
+		}
+	}
+}
+
+static void
+enumerator_data_free (EnumeratorData *ed)
+{
+	g_object_unref (ed->parent);
+	g_object_unref (ed->crawler);
+	g_hash_table_destroy (ed->children);
+	g_slice_free (EnumeratorData, ed);
+}
+
+static void
+file_enumerator_close_cb (GObject      *enumerator,
+			  GAsyncResult *result,
+			  gpointer	user_data)
+{
+	TrackerCrawler *crawler;
+	GError         *error = NULL;
+
+	crawler = TRACKER_CRAWLER (user_data);
+	crawler->private->enumerations--;
+
+	if (!g_file_enumerator_close_finish (G_FILE_ENUMERATOR (enumerator),
+					     result,
+					     &error)) {
+		g_warning ("Couldn't close GFileEnumerator (%p): %s", enumerator,
+			   (error) ? error->message : "No reason");
+
+		g_clear_error (&error);
+	}
+}
+
+static void
+file_enumerate_next_cb (GObject      *object,
+			GAsyncResult *result,
+			gpointer      user_data)
+{
+	TrackerCrawler	*crawler;
+	EnumeratorData	*ed;
+	GFileEnumerator *enumerator;
+	GFile		*parent, *child;
+	GFileInfo	*info;
+	GList		*files, *l;
+	GError          *error = NULL;
+
+	enumerator = G_FILE_ENUMERATOR (object);
+
+	ed = (EnumeratorData*) user_data;
+	crawler = ed->crawler;
+	parent = ed->parent;
+
+	files = g_file_enumerator_next_files_finish (enumerator,
+						     result,
+						     &error);
+
+	if (error || !files || !crawler->private->is_running) {
+		if (error) {
+			g_critical ("Could not crawl through directory: %s", error->message);
+			g_error_free (error);
+		}
+
+		/* No more files or we are stopping anyway, so clean
+		 * up and close all file enumerators.
+		 */
+		if (files) {
+			g_list_foreach (files, (GFunc) g_object_unref, NULL);
+			g_list_free (files);
+		}
+
+		enumerator_data_process (ed);
+		enumerator_data_free (ed);
+		g_file_enumerator_close_async (enumerator,
+					       G_PRIORITY_DEFAULT,
+					       NULL,
+					       file_enumerator_close_cb,
+					       crawler);
+		g_object_unref (enumerator);
+
+		return;
+	}
+
+	for (l = files; l; l = l->next) {
+		const gchar *child_name;
+		gboolean is_dir;
+
+		info = l->data;
+
+		child_name = g_file_info_get_name (info);
+		child = g_file_get_child (parent, child_name);
+		is_dir = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
+
+		enumerator_data_add_child (ed, child_name, child, is_dir);
+
+		g_object_unref (child);
+		g_object_unref (info);
+	}
+
+	g_list_free (files);
+
+	/* Get next files */
+	file_enumerate_next (enumerator, ed);
+}
+
+static void
+file_enumerate_next (GFileEnumerator *enumerator,
+		     EnumeratorData  *ed)
+{
+	g_file_enumerator_next_files_async (enumerator,
+					    FILES_GROUP_SIZE,
+					    G_PRIORITY_LOW,
+					    NULL,
+					    file_enumerate_next_cb,
+					    ed);
+}
+
+static void
+file_enumerate_children_cb (GObject	 *file,
+			    GAsyncResult *result,
+			    gpointer	  user_data)
+{
+	TrackerCrawler	*crawler;
+	EnumeratorData	*ed;
+	GFileEnumerator *enumerator;
+	GFile		*parent;
+	GError          *error = NULL;
+
+	parent = G_FILE (file);
+	ed = (EnumeratorData *) user_data;
+	crawler = ed->crawler;
+	enumerator = g_file_enumerate_children_finish (parent, result, &error);
+
+	if (!enumerator) {
+		if (error) {
+			gchar *uri;
+
+			uri = g_file_get_uri (parent);
+
+			g_critical ("Could not open directory '%s': %s",
+				    uri, error->message);
+
+			g_error_free (error);
+			g_free (uri);
+		}
+
+		crawler->private->enumerations--;
+		return;
+	}
+
+	/* Start traversing the directory's files */
+	file_enumerate_next (enumerator, ed);
+}
+
+static void
+file_enumerate_children (TrackerCrawler *crawler,
+			 GFile		*file)
+{
+	EnumeratorData *ed;
+
+	crawler->private->enumerations++;
+
+	ed = enumerator_data_new (crawler, file);
+
+	g_file_enumerate_children_async (file,
+					 FILE_ATTRIBUTES,
+					 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+					 G_PRIORITY_DEFAULT,
+					 NULL,
+					 file_enumerate_children_cb,
+					 ed);
+}
+
+static GSList *
+prune_none_existing_gslist_paths (TrackerCrawler *crawler, 
+				  GSList         *paths,
+				  const gchar    *path_type)
+{
+	TrackerCrawlerPrivate *priv;
+	GSList                *new_paths = NULL;
+
+	priv = crawler->private;
+
+	if (paths) {
+		GSList	 *l;
+		GFile	 *file;
+		gchar	 *path;
+		gboolean  exists;
+
+		/* Check the currently set recurse paths are real */
+		for (l = paths; l; l = l->next) {
+			path = l->data;
+
+			/* Check location exists before we do anything */
+			file = g_file_new_for_path (path);
+			exists = g_file_query_exists (file, NULL);
+
+			if (exists) {
+				g_message ("  Directory:'%s' added to list to crawl exists %s%s%s",
+					   path,
+					   path_type ? "(" : "",
+					   path_type ? path_type : "",
+					   path_type ? ")" : "");
+				new_paths = g_slist_prepend (new_paths, g_strdup (path));
+			} else {
+				g_message ("  Directory:'%s' does not exist",
+					   path);
+			}
+
+			g_object_unref (file);
+		}
+
+		new_paths = g_slist_reverse (new_paths);
+	}
+
+	return new_paths;
+}
+
+static GSList *
+prune_none_existing_glist_paths (TrackerCrawler *crawler, 
+				 GList          *paths,
+				 const gchar    *path_type)
+{
+	GSList *temporary_paths;
+	GSList *new_paths;
+	GList  *l;
+
+	/* The only difference with this function is that it takes a
+	 * GList instead of a GSList.
+	 */
+
+	temporary_paths = NULL;
+
+	for (l = paths; l; l = l->next) {
+		temporary_paths = g_slist_prepend (temporary_paths, l->data);
+	}
+
+	temporary_paths = g_slist_reverse (temporary_paths);
+	new_paths = prune_none_existing_gslist_paths (crawler, temporary_paths, path_type);
+	g_slist_free (temporary_paths);
+
+	return new_paths;
+}
+
+gboolean
+tracker_crawler_start (TrackerCrawler *crawler)
+{
+	TrackerCrawlerPrivate *priv;
+	GSList                *l;
+
+	g_return_val_if_fail (TRACKER_IS_CRAWLER (crawler), FALSE);
+
+	priv = crawler->private;
+
+	priv->was_started = TRUE;
+
+	g_message ("Crawling directories for module:'%s'",
+		   crawler->private->module_name);
+
+	if (priv->use_module_paths) {
+		GSList *new_paths;
+		GList  *recurse_paths;
+		GList  *paths;
+
+		g_message ("  Using module paths");
+
+		recurse_paths =	tracker_module_config_get_monitor_recurse_directories (priv->module_name);
+		paths = tracker_module_config_get_monitor_directories (priv->module_name);
+
+		if (recurse_paths || paths) {
+			/* First we do non-recursive directories */
+			new_paths = prune_none_existing_glist_paths (crawler, paths, NULL);
+			g_slist_foreach (priv->paths, (GFunc) g_free, NULL);
+			g_slist_free (priv->paths);
+			priv->paths = new_paths;
+
+			/* Second we do recursive directories */
+			new_paths = prune_none_existing_glist_paths (crawler, recurse_paths, "recursively");
+			g_slist_foreach (priv->recurse_paths, (GFunc) g_free, NULL);
+			g_slist_free (priv->recurse_paths);
+			priv->recurse_paths = new_paths;
+		} else {
+			g_message ("  No directories from module config");
+		}
+
+		g_list_free (paths);
+		g_list_free (recurse_paths);
+	} else {
+		GSList *new_paths;
+
+		g_message ("  Not using module config paths, using special paths added");
+
+		new_paths = prune_none_existing_gslist_paths (crawler, priv->special_paths, "special");
+		g_slist_foreach (priv->special_paths, (GFunc) g_free, NULL);
+		g_slist_free (priv->special_paths);
+		priv->special_paths = new_paths;
+	}
+
+	if (!priv->paths && !priv->recurse_paths && !priv->special_paths) {
+		g_message ("  No directories that actually exist to iterate, doing nothing");
+		return FALSE;
+	}
+
+	/* Filter duplicates */
+	l = priv->paths;
+	priv->paths = tracker_path_list_filter_duplicates (priv->paths, ".");
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	l = priv->recurse_paths;
+	priv->recurse_paths = tracker_path_list_filter_duplicates (priv->recurse_paths, ".");
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	l = priv->special_paths;
+	priv->special_paths = tracker_path_list_filter_duplicates (priv->special_paths, ".");
+	g_slist_foreach (l, (GFunc) g_free, NULL);
+	g_slist_free (l);
+
+	/* Set up legacy NoWatchDirectoryRoots so we don't have to get
+	 * them from the config for EVERY file we traverse.
+	 */
+	g_slist_foreach (priv->no_watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->no_watch_directory_roots);
+	l = tracker_config_get_no_watch_directory_roots (priv->config);
+	priv->no_watch_directory_roots = tracker_gslist_copy_with_string_data (l);
+
+	g_slist_foreach (priv->watch_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->watch_directory_roots);
+	l = tracker_config_get_watch_directory_roots (priv->config);
+	priv->watch_directory_roots = tracker_gslist_copy_with_string_data (l);
+
+	g_slist_foreach (priv->crawl_directory_roots, (GFunc) g_free, NULL);
+	g_slist_free (priv->crawl_directory_roots);
+	l = tracker_config_get_crawl_directory_roots (priv->config);
+	priv->crawl_directory_roots = tracker_gslist_copy_with_string_data (l);
+
+	/* Time the event */
+	if (priv->timer) {
+		g_timer_destroy (priv->timer);
+	}
+
+	priv->timer = g_timer_new ();
+
+	/* Set as running now */
+	priv->is_running = TRUE;
+	priv->is_finished = FALSE;
+
+	/* Reset stats */
+	priv->directories_found = 0;
+	priv->directories_ignored = 0;
+	priv->files_found = 0;
+	priv->files_ignored = 0;
+
+	/* Reset paths which have been iterated */
+	priv->paths_are_done = FALSE;
+	priv->recurse_paths_are_done = FALSE;
+	priv->special_paths_are_done = FALSE;
+
+	/* Set idle handler to process directories and files found */
+	priv->idle_id = g_idle_add (process_func, crawler);
+
+	return TRUE;
+}
+
+void
+tracker_crawler_stop (TrackerCrawler *crawler)
+{
+	TrackerCrawlerPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+
+	priv = crawler->private;
+
+
+	g_message ("  %s crawling files in %4.4f seconds",
+		   priv->is_finished ? "Finished" : "Stopped",
+		   g_timer_elapsed (priv->timer, NULL));
+	g_message ("  Found %d directories, ignored %d directories",
+		   priv->directories_found,
+		   priv->directories_ignored);
+	g_message ("  Found %d files, ignored %d files",
+		   priv->files_found,
+		   priv->files_ignored);
+
+	priv->is_running = FALSE;
+
+	if (priv->idle_id) {
+		g_source_remove (priv->idle_id);
+		priv->idle_id = 0;
+	}
+
+	if (priv->timer) {
+		g_timer_destroy (priv->timer);
+		priv->timer = NULL;
+	}
+
+	g_signal_emit (crawler, signals[FINISHED], 0,
+		       priv->module_name,
+		       priv->directories_found,
+		       priv->directories_ignored,
+		       priv->files_found,
+		       priv->files_ignored);
+}
+
+/* This function is a convenience for the monitor module so we can
+ * just ask it to crawl another path which we didn't know about
+ * before.
+ */
+void
+tracker_crawler_add_unexpected_path (TrackerCrawler *crawler,
+				     const gchar    *path)
+{
+	TrackerCrawlerPrivate *priv;
+	GFile                 *file;
+
+	g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+	g_return_if_fail (path != NULL);
+
+	priv = crawler->private;
+
+	/* This check should be fine, the reason being, that if we
+	 * call this, it is because we have received a monitor event
+	 * in the first place. This means we must already have been
+	 * started at some point.
+	 */
+	g_return_if_fail (priv->was_started);
+
+	/* FIXME: Should we check paths_are_done to see if we
+	 * need to actually call add_directory()?
+	 */
+	file = g_file_new_for_path (path);
+	add_directory (crawler, file);
+	g_object_unref (file);
+	
+	/* FIXME: Should we reset the stats? */
+	if (!priv->idle_id) {
+		/* Time the event */
+		if (priv->timer) {
+			g_timer_destroy (priv->timer);
+		}
+		
+		priv->timer = g_timer_new ();
+		
+		/* Set as running now */
+		priv->is_running = TRUE;
+		priv->is_finished = FALSE;
+	
+		/* Set idle handler to process directories and files found */
+		priv->idle_id = g_idle_add (process_func, crawler);
+	}
+}
+
+
+/* This is a convenience function to add extra locations because
+ * sometimes we want to add locations like the MMC or others to the
+ * "Files" module, for example.
+ */
+void
+tracker_crawler_special_paths_add (TrackerCrawler *crawler,
+				   const gchar    *path)
+{
+	TrackerCrawlerPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+	g_return_if_fail (path != NULL);
+
+	priv = crawler->private;
+
+	g_return_if_fail (!priv->is_running);
+
+	priv->special_paths = g_slist_append (priv->special_paths, g_strdup (path));
+}
+
+void
+tracker_crawler_special_paths_clear (TrackerCrawler *crawler)
+{
+	TrackerCrawlerPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+
+	priv = crawler->private;
+
+	g_return_if_fail (!priv->is_running);
+
+	g_slist_foreach (priv->special_paths, (GFunc) g_free, NULL);
+	g_slist_free (priv->special_paths);
+	priv->special_paths = NULL;
+}
+
+void
+tracker_crawler_use_module_paths (TrackerCrawler *crawler,
+				  gboolean        use_module_paths)
+{
+	TrackerCrawlerPrivate *priv;
+
+	g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+
+	priv = crawler->private;
+
+	g_return_if_fail (priv->is_running == FALSE);
+
+	priv->use_module_paths = use_module_paths;
+}
+
+gboolean
+tracker_crawler_is_path_ignored (TrackerCrawler *crawler,
+				 const gchar	*path,
+				 gboolean	 is_directory)
+{
+	g_return_val_if_fail (TRACKER_IS_CRAWLER (crawler), TRUE);
+
+	/* We have an internal function here we call. The reason for
+	 * this is that it is expensive to type check the Crawler
+	 * object for EVERY file we process. Internally, we don't do
+	 * that. Externally we do. Externally this is used by the
+	 * processor when we get new monitor events to know if we
+	 * should be sending new files to the indexer.
+	 */
+	return is_path_ignored (crawler, path, is_directory);
+}
diff --git a/src/libtracker-miner/tracker-crawler.h b/src/libtracker-miner/tracker-crawler.h
new file mode 100644
index 0000000..8d6a8d8
--- /dev/null
+++ b/src/libtracker-miner/tracker-crawler.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 __TRACKERD_CRAWLER_H__
+#define __TRACKERD_CRAWLER_H__
+
+#include <glib-object.h>
+
+#include "tracker-config.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_CRAWLER		(tracker_crawler_get_type ())
+#define TRACKER_CRAWLER(object)		(G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_CRAWLER, TrackerCrawler))
+#define TRACKER_CRAWLER_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_CRAWLER, TrackerCrawlerClass))
+#define TRACKER_IS_CRAWLER(object)	(G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_CRAWLER))
+#define TRACKER_IS_CRAWLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_CRAWLER))
+#define TRACKER_CRAWLER_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_CRAWLER, TrackerCrawlerClass))
+
+typedef struct _TrackerCrawler	       TrackerCrawler;
+typedef struct _TrackerCrawlerClass    TrackerCrawlerClass;
+typedef struct _TrackerCrawlerPrivate  TrackerCrawlerPrivate;
+
+struct _TrackerCrawler {
+	GObject		       parent;
+	TrackerCrawlerPrivate *private;
+};
+
+struct _TrackerCrawlerClass {
+	GObjectClass	       parent;
+};
+
+GType           tracker_crawler_get_type            (void);
+TrackerCrawler *tracker_crawler_new                 (TrackerConfig  *config,
+						     const gchar    *module_name);
+gboolean        tracker_crawler_start               (TrackerCrawler *crawler);
+void            tracker_crawler_stop                (TrackerCrawler *crawler);
+gboolean        tracker_crawler_is_path_ignored     (TrackerCrawler *crawler,
+						     const gchar    *path,
+						     gboolean        is_directory);
+void            tracker_crawler_add_unexpected_path (TrackerCrawler *crawler,
+						     const gchar    *path);
+
+/* Convenience API for old .cfg file */
+void            tracker_crawler_special_paths_add   (TrackerCrawler *crawler,
+						     const gchar    *path);
+void            tracker_crawler_special_paths_clear (TrackerCrawler *crawler);
+void            tracker_crawler_use_module_paths    (TrackerCrawler *crawler,
+						     gboolean        use_module_paths);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_CRAWLER_H__ */
diff --git a/src/libtracker-miner/tracker-marshal.list b/src/libtracker-miner/tracker-marshal.list
new file mode 100644
index 0000000..5bba08b
--- /dev/null
+++ b/src/libtracker-miner/tracker-marshal.list
@@ -0,0 +1,8 @@
+VOID:DOUBLE,UINT,UINT,BOOL
+VOID:DOUBLE,STRING,UINT,UINT,UINT
+VOID:STRING,OBJECT,BOOLEAN
+VOID:STRING,OBJECT,OBJECT,BOOLEAN,BOOLEAN
+VOID:STRING,BOOL
+VOID:STRING,OBJECT
+VOID:STRING,UINT,UINT,UINT,UINT
+VOID:POINTER,STRING,STRING,STRING,STRING
diff --git a/src/libtracker-miner/tracker-miner-crawler.c b/src/libtracker-miner/tracker-miner-crawler.c
index 1c3bed8..8679f83 100644
--- a/src/libtracker-miner/tracker-miner-crawler.c
+++ b/src/libtracker-miner/tracker-miner-crawler.c
@@ -20,12 +20,18 @@
  */
 
 #include "tracker-miner-crawler.h"
+#include "tracker-config.h"
+#include "tracker-processor.h"
+#include <libtracker-common/tracker-storage.h>
 
-#define TRACKER_MINER_CRAWLER_GET_PRIVATE (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_MINER_CRAWLER, TrackerMinerCrawlerPrivate))
+#define TRACKER_MINER_CRAWLER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_MINER_CRAWLER, TrackerMinerCrawlerPrivate))
 
 typedef struct TrackerMinerCrawlerPrivate TrackerMinerCrawlerPrivate;
 
 struct TrackerMinerCrawlerPrivate {
+	TrackerConfig *config;
+	TrackerStorage *storage;
+	TrackerProcessor *processor;
 };
 
 static void tracker_miner_crawler_finalize (GObject *object);
@@ -57,6 +63,14 @@ tracker_miner_crawler_class_init (TrackerMinerCrawlerClass *klass)
 static void
 tracker_miner_crawler_init (TrackerMinerCrawler *miner)
 {
+	TrackerMinerCrawlerPrivate *priv;
+
+	priv = miner->_priv = TRACKER_MINER_CRAWLER_GET_PRIVATE (miner);
+
+	priv->config = tracker_config_new ();
+	priv->storage = tracker_storage_new ();
+
+	priv->processor = tracker_processor_new (priv->config, priv->storage);
 }
 
 static void
@@ -68,4 +82,11 @@ tracker_miner_crawler_finalize (GObject *object)
 static void
 tracker_miner_crawler_started (TrackerMiner *miner)
 {
+	TrackerMinerCrawler *miner_crawler;
+	TrackerMinerCrawlerPrivate *priv;
+
+	miner_crawler = TRACKER_MINER_CRAWLER (miner);
+	priv = miner_crawler->_priv;
+
+	tracker_processor_start (priv->processor);
 }
diff --git a/src/libtracker-miner/tracker-monitor.c b/src/libtracker-miner/tracker-monitor.c
new file mode 100644
index 0000000..c56dd64
--- /dev/null
+++ b/src/libtracker-miner/tracker-monitor.c
@@ -0,0 +1,1632 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-module-config.h>
+
+#include <sys/inotify.h>
+#include <libinotify/libinotify.h>
+
+#include "tracker-monitor.h"
+#include "tracker-dbus.h"
+#include "tracker-marshal.h"
+#include "tracker-status.h"
+
+#define TRACKER_MONITOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_MONITOR, TrackerMonitorPrivate))
+
+/* The life time of an item in the cache */
+#define CACHE_LIFETIME_SECONDS 1
+
+/* When we receive IO monitor events, we pause sending information to
+ * the indexer for a few seconds before continuing. We have to receive
+ * NO events for at least a few seconds before unpausing.
+ */
+#define PAUSE_ON_IO_SECONDS    5
+
+/* If this is defined, we pause the indexer when we get events. If it
+ * is not, we don't do any pausing.
+ */
+#undef  PAUSE_ON_IO
+
+struct _TrackerMonitorPrivate {
+	TrackerConfig *config;
+
+	GHashTable    *modules;
+
+	gboolean       enabled;
+
+	GType	       monitor_backend;
+
+	guint	       monitor_limit;
+	gboolean       monitor_limit_warned;
+	guint	       monitors_ignored;
+
+	/* For FAM, the _CHANGES_DONE event is not signalled, so we
+	 * have to just use the _CHANGED event instead.
+	 */
+	gboolean       use_changed_event;
+
+#ifdef PAUSE_ON_IO
+	/* Timeout id for pausing when we get IO */
+	guint	       unpause_timeout_id;
+#endif /* PAUSE_ON_IO */
+
+	GHashTable    *event_pairs;
+	guint	       event_pairs_timeout_id;
+
+	GHashTable    *cached_events;
+	guint	       cached_events_timeout_id;
+};
+
+typedef struct {
+	GFile    *file;
+	GTimeVal  start_time;
+	GTimeVal  last_time;
+	guint32   event_type;
+} EventData;
+
+enum {
+	ITEM_CREATED,
+	ITEM_UPDATED,
+	ITEM_DELETED,
+	ITEM_MOVED,
+	LAST_SIGNAL
+};
+
+enum {
+	PROP_0,
+	PROP_ENABLED
+};
+
+static void           tracker_monitor_finalize     (GObject        *object);
+static void           tracker_monitor_set_property (GObject        *object,
+						    guint           prop_id,
+						    const GValue   *value,
+						    GParamSpec     *pspec);
+static void           tracker_monitor_get_property (GObject        *object,
+						    guint           prop_id,
+						    GValue         *value,
+						    GParamSpec     *pspec);
+static EventData *    event_data_new               (GFile          *file,
+						    guint32         event_type);
+static void           event_data_free              (gpointer        data);
+static guint          get_inotify_limit            (void);
+
+static INotifyHandle *libinotify_monitor_directory (TrackerMonitor *monitor,
+						    GFile          *file);
+static void           libinotify_monitor_cancel    (gpointer        data);
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE(TrackerMonitor, tracker_monitor, G_TYPE_OBJECT)
+
+static void
+tracker_monitor_class_init (TrackerMonitorClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = tracker_monitor_finalize;
+	object_class->set_property = tracker_monitor_set_property;
+	object_class->get_property = tracker_monitor_get_property;
+
+	signals[ITEM_CREATED] =
+		g_signal_new ("item-created",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT_BOOLEAN,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT,
+			      G_TYPE_BOOLEAN);
+	signals[ITEM_UPDATED] =
+		g_signal_new ("item-updated",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT_BOOLEAN,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT,
+			      G_TYPE_BOOLEAN);
+	signals[ITEM_DELETED] =
+		g_signal_new ("item-deleted",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT_BOOLEAN,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT,
+			      G_TYPE_BOOLEAN);
+	signals[ITEM_MOVED] =
+		g_signal_new ("item-moved",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      tracker_marshal_VOID__STRING_OBJECT_OBJECT_BOOLEAN_BOOLEAN,
+			      G_TYPE_NONE,
+			      5,
+			      G_TYPE_STRING,
+			      G_TYPE_OBJECT,
+			      G_TYPE_OBJECT,
+			      G_TYPE_BOOLEAN,
+			      G_TYPE_BOOLEAN);
+
+	g_object_class_install_property (object_class,
+					 PROP_ENABLED,
+					 g_param_spec_boolean ("enabled",
+							       "Enabled",
+							       "Enabled",
+							       TRUE,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_type_class_add_private (object_class, sizeof (TrackerMonitorPrivate));
+}
+
+static void
+tracker_monitor_init (TrackerMonitor *object)
+{
+	TrackerMonitorPrivate *priv;
+	GFile		      *file;
+	GFileMonitor	      *monitor;
+	GList		      *all_modules, *l;
+	const gchar	      *name;
+
+	object->private = TRACKER_MONITOR_GET_PRIVATE (object);
+
+	priv = object->private;
+
+	/* By default we enable monitoring */
+	priv->enabled = TRUE;
+
+	/* For each module we create a hash table for monitors */
+	priv->modules =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       (GDestroyNotify) g_hash_table_unref);
+
+	/* We have a hash table with cookies so we can pair up move
+	 * events.
+	 */
+	priv->event_pairs =
+		g_hash_table_new_full (g_direct_hash,
+				       g_direct_equal,
+				       NULL,
+				       event_data_free);
+
+	/* We have a hash table for events so we don't flood the
+	 * indexer with the same events too frequently, we also
+	 * concatenate some events with this like CREATED+UPDATED.
+	 */
+	priv->cached_events =
+		g_hash_table_new_full (g_file_hash,
+				       (GEqualFunc) g_file_equal,
+				       g_object_unref,
+				       event_data_free);
+
+	all_modules = tracker_module_config_get_modules ();
+
+	for (l = all_modules; l; l = l->next) {
+		GHashTable *monitors;
+
+		/* Create monitors table for this module */
+		monitors =
+			g_hash_table_new_full (g_file_hash,
+					       (GEqualFunc) g_file_equal,
+					       (GDestroyNotify) g_object_unref,
+					       (GDestroyNotify) libinotify_monitor_cancel);
+
+		g_hash_table_insert (priv->modules, g_strdup (l->data), monitors);
+	}
+
+	g_list_free (all_modules);
+
+	/* For the first monitor we get the type and find out if we
+	 * are using inotify, FAM, polling, etc.
+	 */
+	file = g_file_new_for_path (g_get_home_dir ());
+	monitor = g_file_monitor_directory (file,
+					    G_FILE_MONITOR_WATCH_MOUNTS,
+					    NULL,
+					    NULL);
+
+	priv->monitor_backend = G_OBJECT_TYPE (monitor);
+
+	/* We use the name because the type itself is actually
+	 * private and not available publically. Note this is
+	 * subject to change, but unlikely of course.
+	 */
+	name = g_type_name (priv->monitor_backend);
+	if (name) {
+		/* Set limits based on backend... */
+		if (strcmp (name, "GInotifyDirectoryMonitor") == 0) {
+			/* Using inotify */
+			g_message ("Monitor backend is INotify");
+
+			/* Setting limit based on kernel
+			 * settings in /proc...
+			 */
+			priv->monitor_limit = get_inotify_limit ();
+
+			/* We don't use 100% of the monitors, we allow other
+			 * applications to have at least 500 or so to use
+			 * between them selves. This only
+			 * applies to inotify because it is a
+			 * user shared resource.
+			 */
+			priv->monitor_limit -= 500;
+
+			/* Make sure we don't end up with a
+			 * negative maximum.
+			 */
+			priv->monitor_limit = MAX (priv->monitor_limit, 0);
+		}
+		else if (strcmp (name, "GFamDirectoryMonitor") == 0) {
+			/* Using Fam */
+			g_message ("Monitor backend is Fam");
+
+			/* Setting limit to an arbitary limit
+			 * based on testing
+			 */
+			priv->monitor_limit = 400;
+			priv->use_changed_event = TRUE;
+		}
+		else if (strcmp (name, "GFenDirectoryMonitor") == 0) {
+			/* Using Fen, what is this? */
+			g_message ("Monitor backend is Fen");
+
+			/* Guessing limit... */
+			priv->monitor_limit = 8192;
+		}
+		else if (strcmp (name, "GWin32DirectoryMonitor") == 0) {
+			/* Using Windows */
+			g_message ("Monitor backend is Windows");
+
+			/* Guessing limit... */
+			priv->monitor_limit = 8192;
+		}
+		else {
+			/* Unknown */
+			g_warning ("Monitor backend:'%s' is unknown, we have no limits "
+				   "in place because we don't know what we are dealing with!",
+				   name);
+
+			/* Guessing limit... */
+			priv->monitor_limit = 100;
+		}
+	}
+
+	g_message ("Monitor limit is %d", priv->monitor_limit);
+
+	g_file_monitor_cancel (monitor);
+	g_object_unref (monitor);
+	g_object_unref (file);
+}
+
+static void
+tracker_monitor_finalize (GObject *object)
+{
+	TrackerMonitorPrivate *priv;
+
+	priv = TRACKER_MONITOR_GET_PRIVATE (object);
+
+#ifdef PAUSE_ON_IO
+	if (priv->unpause_timeout_id) {
+		g_source_remove (priv->unpause_timeout_id);
+	}
+#endif /* PAUSE_ON_IO */
+
+	if (priv->cached_events_timeout_id) {
+		g_source_remove (priv->cached_events_timeout_id);
+	}
+
+	if (priv->event_pairs_timeout_id) {
+		g_source_remove (priv->event_pairs_timeout_id);
+	}
+
+	g_hash_table_unref (priv->cached_events);
+	g_hash_table_unref (priv->event_pairs);
+
+	g_hash_table_unref (priv->modules);
+
+	g_object_unref (priv->config);
+
+	G_OBJECT_CLASS (tracker_monitor_parent_class)->finalize (object);
+}
+
+static void
+tracker_monitor_set_property (GObject	   *object,
+			      guint	    prop_id,
+			      const GValue *value,
+			      GParamSpec   *pspec)
+{
+	switch (prop_id) {
+	case PROP_ENABLED:
+		tracker_monitor_set_enabled (TRACKER_MONITOR (object),
+					     g_value_get_boolean (value));
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+tracker_monitor_get_property (GObject	   *object,
+			      guint	    prop_id,
+			      GValue	   *value,
+			      GParamSpec   *pspec)
+{
+	TrackerMonitorPrivate *priv;
+
+	priv = TRACKER_MONITOR_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_ENABLED:
+		g_value_set_boolean (value, priv->enabled);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static EventData *
+event_data_new (GFile   *file,
+		guint32  event_type)
+{
+	EventData *event;
+	GTimeVal now;
+
+	event = g_new0 (EventData, 1);
+	g_get_current_time (&now);
+
+	event->file = g_object_ref (file);
+	event->start_time = now;
+	event->last_time = now;
+	event->event_type = event_type;
+
+	return event;
+}
+
+static void
+event_data_update (EventData *event)
+{
+	GTimeVal now;
+
+	g_get_current_time (&now);
+
+	event->last_time = now;
+}
+
+static void
+event_data_free (gpointer data)
+{
+	EventData *event;
+
+	event = data;
+	
+	g_object_unref (event->file);
+}
+
+static guint
+get_inotify_limit (void)
+{
+	GError	    *error = NULL;
+	const gchar *filename;
+	gchar	    *contents = NULL;
+	guint	     limit;
+
+	filename = "/proc/sys/fs/inotify/max_user_watches";
+
+	if (!g_file_get_contents (filename,
+				  &contents,
+				  NULL,
+				  &error)) {
+		g_warning ("Couldn't get INotify monitor limit from:'%s', %s",
+			   filename,
+			   error ? error->message : "no error given");
+		g_clear_error (&error);
+
+		/* Setting limit to an arbitary limit */
+		limit = 8192;
+	} else {
+		limit = atoi (contents);
+		g_free (contents);
+	}
+
+	return limit;
+}
+
+static const gchar *
+get_queue_from_gfile (GHashTable *modules,
+		      GFile	 *file)
+{
+	GHashTableIter iter;
+	gpointer       key, value;
+
+	g_hash_table_iter_init (&iter, modules);
+
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		GHashTable *hash_table;
+
+		hash_table = value;
+
+		if (g_hash_table_lookup (hash_table, file)) {
+			return key;
+		}
+	}
+
+	return NULL;
+}
+
+static const gchar *
+get_module_name_from_gfile (TrackerMonitor *monitor,
+			    GFile	   *file,
+			    gboolean	   *is_directory)
+{
+	const gchar *module_name;
+
+	if (is_directory) {
+		*is_directory = TRUE;
+	}
+
+	/* First try to get the module name from the file, this will
+	 * only work if the event we received is for a directory.
+	 */
+	module_name = get_queue_from_gfile (monitor->private->modules, file);
+	if (!module_name) {
+		GFile *parent;
+
+		/* Second we try to get the module name from the base
+		 * name of the file.
+		 */
+		parent = g_file_get_parent (file);
+
+		if (!parent) {
+			gchar *path;
+
+			path = g_file_get_path (file);
+
+			g_debug ("Could not get module name from GFile (path:'%s')",
+				 path);
+
+			g_free (path);
+
+			return NULL;
+		}
+
+		module_name = get_queue_from_gfile (monitor->private->modules, parent);
+
+		if (!module_name) {
+			gchar *parent_path;
+			gchar *child_path;
+
+			parent_path = g_file_get_path (parent);
+			child_path = g_file_get_path (file);
+
+			if (is_directory) {
+				*is_directory = g_file_test (child_path, G_FILE_TEST_IS_DIR);
+			}
+
+			g_debug ("Could not get module name from GFile (path:'%s' or parent:'%s')",
+				 child_path,
+				 parent_path);
+
+			g_free (parent_path);
+			g_free (child_path);
+		} else {
+			if (is_directory) {
+				gchar *child_path;
+
+				child_path = g_file_get_path (file);
+				*is_directory = g_file_test (child_path, G_FILE_TEST_IS_DIR);
+				g_free (child_path);
+			}
+		}
+
+		g_object_unref (parent);
+	}
+
+	return module_name;
+}
+
+#ifdef PAUSE_ON_IO 
+
+static gboolean
+unpause_cb (gpointer data)
+{
+	TrackerMonitor *monitor;
+
+	monitor = data;
+
+	g_message ("Resuming indexing now we have stopped "
+		   "receiving monitor events for %d seconds",
+		   PAUSE_ON_IO_SECONDS);
+
+	monitor->private->unpause_timeout_id = 0;
+	tracker_status_set_is_paused_for_io (FALSE);
+
+	return FALSE;
+}
+
+#endif /* PAUSE_ON_IO */
+
+
+static gchar *
+libinotify_monitor_event_to_string (guint32 event_type)
+{
+	GString *s;
+
+	s = g_string_new ("");
+
+	if (event_type & IN_ACCESS) {
+		s = g_string_append (s, "IN_ACCESS | ");
+	}
+	if (event_type & IN_MODIFY) {
+		s = g_string_append (s, "IN_MODIFY | ");
+	}
+	if (event_type & IN_ATTRIB) {
+		s = g_string_append (s, "IN_ATTRIB | ");
+	}
+	if (event_type & IN_CLOSE_WRITE) {
+		s = g_string_append (s, "IN_CLOSE_WRITE | ");
+	}
+	if (event_type & IN_CLOSE_NOWRITE) {
+		s = g_string_append (s, "IN_CLOSE_NOWRITE | ");
+	}
+	if (event_type & IN_OPEN) {
+		s = g_string_append (s, "IN_OPEN | ");
+	}
+	if (event_type & IN_MOVED_FROM) {
+		s = g_string_append (s, "IN_MOVED_FROM | ");
+	}
+	if (event_type & IN_MOVED_TO) {
+		s = g_string_append (s, "IN_MOVED_TO | ");
+	}
+	if (event_type & IN_CREATE) {
+		s = g_string_append (s, "IN_CREATE | ");
+	}
+	if (event_type & IN_DELETE) {
+		s = g_string_append (s, "IN_DELETE | ");
+	}
+	if (event_type & IN_DELETE_SELF) {
+		s = g_string_append (s, "IN_DELETE_SELF | ");
+	}
+	if (event_type & IN_MOVE_SELF) {
+		s = g_string_append (s, "IN_MOVE_SELF | ");
+	}
+	if (event_type & IN_UNMOUNT) {
+		s = g_string_append (s, "IN_UNMOUNT | ");
+	}
+	if (event_type & IN_Q_OVERFLOW) {
+		s = g_string_append (s, "IN_Q_OVERFLOW | ");
+	}
+	if (event_type & IN_IGNORED) {
+		s = g_string_append (s, "IN_IGNORED | ");
+	}
+
+	/* helper events */
+	if (event_type & IN_CLOSE) {
+		s = g_string_append (s, "IN_CLOSE* | ");
+	}
+	if (event_type & IN_MOVE) {
+		s = g_string_append (s, "IN_MOVE* | ");
+	}
+
+	/* special flags */
+	if (event_type & IN_MASK_ADD) {
+		s = g_string_append (s, "IN_MASK_ADD^ | ");
+	}
+	if (event_type & IN_ISDIR) {
+		s = g_string_append (s, "IN_ISDIR^ | ");
+	}
+	if (event_type & IN_ONESHOT) {
+		s = g_string_append (s, "IN_ONESHOT^ | ");
+	}
+
+	s->str[s->len - 3] = '\0';
+
+	return g_string_free (s, FALSE);
+}
+
+static gboolean
+libinotify_event_pairs_timeout_cb (gpointer data)
+{
+	TrackerMonitor *monitor;
+	GTimeVal	now;
+	GHashTableIter	iter;
+	gpointer	key, value;
+
+	monitor = data;
+
+	g_debug ("Checking for event pairs which have timed out...");
+
+	g_get_current_time (&now);
+
+	g_hash_table_iter_init (&iter, monitor->private->event_pairs);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		EventData   *event;
+		glong	     seconds;
+		glong	     seconds_then;
+		const gchar *module_name;
+		gboolean     is_directory;
+
+		event = value;
+
+		seconds_then = event->start_time.tv_sec;
+
+		seconds  = now.tv_sec;
+		seconds -= seconds_then;
+
+		g_debug ("Comparing now:%ld to then:%ld, diff:%ld",
+			 now.tv_sec,
+			 seconds_then,
+			 seconds);
+
+		if (seconds < 2) {
+			continue;
+		}
+
+		/* We didn't receive an event pair for this
+		 * cookie, so we just generate the CREATE or
+		 * DELETE event for the file we know about.
+		 */
+		g_debug ("Event:%d with cookie:%d has timed out (%ld seconds have elapsed)",
+			 event->event_type,
+			 GPOINTER_TO_UINT (key),
+			 seconds);
+
+		module_name = get_module_name_from_gfile (monitor,
+							  event->file,
+							  &is_directory);
+
+		switch (event->event_type) {
+		case IN_MOVED_FROM:
+		case IN_DELETE:
+		case IN_DELETE_SELF:
+			/* So we knew the source, but not the
+			 * target location for the event.
+			 */
+			if (is_directory) {
+ 				tracker_monitor_remove (monitor, 
+							module_name, 
+							event->file);
+			}
+
+			g_signal_emit (monitor,
+				       signals[ITEM_DELETED], 0,
+				       module_name,
+				       event->file,
+				       is_directory);
+			break;
+
+		case IN_CREATE:
+		case IN_MOVED_TO:
+			/* So we knew the target, but not the
+			 * source location for the event.
+			 */
+			g_signal_emit (monitor,
+				       signals[ITEM_CREATED], 0,
+				       module_name,
+				       event->file,
+				       is_directory);
+			break;
+		default:
+			break;
+		}
+		/* Clean up */
+		g_hash_table_iter_remove (&iter);
+	}
+
+	if (g_hash_table_size (monitor->private->event_pairs) < 1) {
+		g_debug ("No more events to pair, removing timeout");
+		monitor->private->event_pairs_timeout_id = 0;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+libinotify_cached_event_handle (TrackerMonitor *monitor,
+				EventData      *data,
+				const gchar    *module_name,
+				gboolean        is_directory)
+{
+	switch (data->event_type) {
+	case IN_MODIFY:
+	case IN_CLOSE_WRITE:
+	case IN_ATTRIB:
+		g_signal_emit (monitor,
+			       signals[ITEM_UPDATED], 0,
+			       module_name,
+			       data->file,
+			       is_directory);
+		break;
+
+	case IN_MOVED_FROM:
+		/* So we knew the source, but not the
+		 * target location for the event.
+		 */
+
+	case IN_DELETE:
+	case IN_DELETE_SELF:
+		if (is_directory) {
+			tracker_monitor_remove (monitor, 
+						module_name, 
+						data->file);
+		}
+
+		g_signal_emit (monitor,
+			       signals[ITEM_DELETED], 0,
+			       module_name,
+			       data->file,
+			       is_directory);
+
+		break;
+
+	case IN_MOVED_TO:
+		/* So we new the target, but not the
+		 * source location for the event.
+		 */
+
+	case IN_CREATE:
+		g_signal_emit (monitor,
+			       signals[ITEM_CREATED], 0,
+			       module_name,
+			       data->file,
+			       is_directory);
+
+		break;
+	default:
+		break;
+	}
+}
+
+static gboolean
+libinotify_cached_events_timeout_cb (gpointer data)
+{
+	TrackerMonitor *monitor;
+	GTimeVal	now;
+	GHashTableIter	iter;
+	gpointer	key, value;
+
+	monitor = data;
+
+	g_debug ("Checking for cached events that have timed out...");
+
+	g_get_current_time (&now);
+
+	g_hash_table_iter_init (&iter, monitor->private->cached_events);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		EventData   *event;
+		glong	     last_event_seconds;
+		glong        start_event_seconds;
+		gint         cache_timeout;
+		gint         scan_timeout;
+		const gchar *module_name;
+		gboolean     is_directory;
+		gboolean     force_emit = FALSE;
+		gboolean     timed_out = FALSE;
+
+		event = value;
+
+		last_event_seconds = now.tv_sec - event->last_time.tv_sec;
+		start_event_seconds = now.tv_sec - event->start_time.tv_sec;
+
+		g_debug ("Comparing now:%ld to then:%ld (start:%ld), diff:%ld (with start:%ld)",
+			 now.tv_sec,
+			 event->last_time.tv_sec,
+			 event->start_time.tv_sec,
+			 last_event_seconds,
+			 start_event_seconds);
+
+		module_name = get_module_name_from_gfile (monitor,
+							  event->file,
+							  &is_directory);
+
+		if (!module_name) {
+			/* File was deleted before we could check its
+			 * cached events, just discard it  
+			 */
+			g_hash_table_iter_remove (&iter);
+			continue;
+		}
+
+		/* If the item has been in the cache for too long
+		 * according to the module config options, then we
+		 * force the cache to expire in order to not starve
+		 * the indexer of events for files which are ALWAYS
+		 * changing.
+		 */
+		cache_timeout = tracker_module_config_get_cache_timeout (module_name);
+		scan_timeout = tracker_module_config_get_scan_timeout (module_name);
+
+		if (cache_timeout > 0) {
+			force_emit = start_event_seconds > cache_timeout;
+		}
+
+		timed_out = last_event_seconds >= MAX (CACHE_LIFETIME_SECONDS, scan_timeout);
+
+		/* Make sure the item is in the cache for at least 2
+		 * seconds OR the time as stated by the module config
+		 * options. This way, always changing files can not
+		 * be emitted too and flood the indexer with events.
+		 */
+		if (!force_emit) {
+			if (!timed_out) {
+				continue;
+			}
+
+			/* We didn't receive an event pair for this
+			 * cookie, so we just generate the CREATE or
+			 * DELETE event for the file we know about.
+			 */
+			g_debug ("Cached event:%d has timed out (%ld seconds have elapsed)",
+				 event->event_type,
+				 last_event_seconds);
+		} else {
+			event->start_time = now;
+			g_debug ("Cached event:%d has been forced to signal (%ld seconds have elapsed since the beginning)",
+				 event->event_type,
+				 start_event_seconds);
+		}
+
+		/* Signal event */
+		libinotify_cached_event_handle (monitor,
+						event,
+						module_name,
+						is_directory);
+
+
+		if (timed_out) {
+			/* Clean up */
+			g_hash_table_iter_remove (&iter);
+		} else {
+			if (event->event_type == IN_CREATE) {
+				/* The file has been already created,
+				 * We want any further events to be
+				 * IN_MODIFY.
+				 */
+				event->event_type = IN_MODIFY;
+			}
+		}
+	}
+
+	if (g_hash_table_size (monitor->private->cached_events) < 1) {
+		g_debug ("No more cached events, removing timeout");
+		monitor->private->cached_events_timeout_id = 0;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+libinotify_cached_event_delete_children_func (gpointer key,
+					      gpointer value,
+					      gpointer user_data)
+{
+	EventData *data;
+
+	data = user_data;
+
+	return (data->event_type == IN_DELETE ||
+		data->event_type == IN_DELETE_SELF) && 
+		g_file_has_prefix (key, data->file);
+}
+
+static void
+libinotify_monitor_force_emission (TrackerMonitor *monitor,
+				   GFile          *file,
+				   guint32         event_type,
+				   const gchar    *module_name,
+				   gboolean        is_directory)
+{
+	EventData *data;
+
+	data = g_hash_table_lookup (monitor->private->cached_events, file);
+
+	if (data) {
+		gchar *event_type_str;
+
+		event_type_str = libinotify_monitor_event_to_string (event_type);
+
+		g_debug ("Cached event:%d being handled before %s",
+			 data->event_type,
+			 event_type_str);
+
+		/* Signal event */
+		libinotify_cached_event_handle (monitor,
+						data,
+						module_name,
+						is_directory);
+
+		/* Clean up */
+		g_hash_table_remove (monitor->private->cached_events, data->file);
+	}
+}
+
+static void
+libinotify_monitor_event_cb (INotifyHandle *handle,
+			     const char    *monitor_name,
+			     const char    *filename,
+			     guint32	    event_type,
+			     guint32	    cookie,
+			     gpointer	    user_data)
+{
+	TrackerMonitor *monitor;
+	GFile	       *file;
+	GFile	       *other_file;
+	const gchar    *module_name;
+	gchar	       *str1;
+	gchar	       *str2;
+	gboolean	is_directory;
+	gchar	       *event_type_str;
+	EventData      *data = NULL;
+	gboolean        set_up_cache_timeout = FALSE;
+
+	monitor = user_data;
+
+	switch (event_type) {
+	case IN_Q_OVERFLOW:
+	case IN_OPEN:
+	case IN_CLOSE_NOWRITE:
+	case IN_ACCESS:
+	case IN_IGNORED:
+		/* Return, otherwise we spam with messages for every
+		 * file we open and look at.
+		 */
+		return;
+	default:
+		break;
+	}
+
+	if (G_UNLIKELY (!monitor->private->enabled)) {
+		g_debug ("Silently dropping monitor event, monitor disabled for now");
+		return;
+	}
+
+	if (monitor_name) {
+		str1 = g_build_filename (monitor_name, filename, NULL);
+		file = g_file_new_for_path (str1);
+	} else {
+		str1 = NULL;
+		file = g_file_new_for_path (filename);
+	}
+
+	other_file = NULL;
+	module_name = get_module_name_from_gfile (monitor, file, &is_directory);
+
+	if (!module_name) {
+		g_free (str1);
+		g_object_unref (file);
+		return;
+	}
+
+	if (!str1) {
+		str1 = g_file_get_path (file);
+	}
+
+	if (other_file) {
+		str2 = g_file_get_path (other_file);
+	} else {
+		str2 = NULL;
+	}
+
+	event_type_str = libinotify_monitor_event_to_string (event_type);
+	g_message ("Received monitor event:%d->'%s' for file:'%s' (cookie:%d)",
+		   event_type,
+		   event_type_str,
+		   str1 ? str1 : "",
+		   cookie);
+	g_free (event_type_str);
+
+#ifdef PAUSE_ON_IO
+	if (monitor->private->unpause_timeout_id != 0) {
+		g_source_remove (monitor->private->unpause_timeout_id);
+	} else {
+		g_message ("Pausing indexing because we are "
+			   "receiving monitor events");
+
+		tracker_status_set_is_paused_for_io (TRUE);
+	}
+
+	monitor->private->unpause_timeout_id =
+		g_timeout_add_seconds (PAUSE_ON_IO_SECONDS,
+				       unpause_cb,
+				       monitor);
+#endif /* PAUSE_ON_IO */
+
+	if (cookie > 0) {
+		/* First check if we already have a file in
+		 * the event pairs hash table.
+		 */
+		data = g_hash_table_lookup (monitor->private->event_pairs,
+					    GUINT_TO_POINTER (cookie));
+
+		if (!data) {
+			data = event_data_new (file, event_type);
+
+			g_hash_table_insert (monitor->private->event_pairs,
+					     GUINT_TO_POINTER (cookie),
+					     data);
+		} else {
+			other_file = data->file;
+		}
+
+		/* Add a check for old cookies we didn't
+		 * receive the follow up pair event for.
+		 */
+		if (!monitor->private->event_pairs_timeout_id) {
+			g_debug ("Setting up event pair timeout check");
+
+			monitor->private->event_pairs_timeout_id =
+				g_timeout_add_seconds (CACHE_LIFETIME_SECONDS,
+						       libinotify_event_pairs_timeout_cb,
+						       monitor);
+		}
+	}
+
+	switch (event_type) {
+	case IN_MODIFY:
+	case IN_CLOSE_WRITE:
+	case IN_ATTRIB:
+		set_up_cache_timeout = TRUE;
+
+		data = g_hash_table_lookup (monitor->private->cached_events, file);
+		if (data) {
+			/* We already have an event we will
+			 * signal when we timeout. So update
+			 * and don't propagate yet this event.
+			 *
+			 * See IN_CREATE.
+			 */
+			event_data_update (data);
+			break;
+		}
+
+		/* Here we just wait to make sure we don't get
+		 * any more MODIFY events and after 2 seconds
+		 * of no MODIFY events we emit it. This saves
+		 * spamming.
+		 */
+		data = event_data_new (file, event_type);
+
+		g_hash_table_insert (monitor->private->cached_events,
+				     g_object_ref (data->file),
+				     data);
+		break;
+
+	case IN_MOVED_FROM:
+		/* If the file is *ALREADY* in the cache, we need to
+		 * handle that cache first. Otherwise we have
+		 * disparity when the cache expires.
+		 */
+		libinotify_monitor_force_emission (monitor, 
+						   file, 
+						   event_type,
+						   module_name, 
+						   is_directory);
+
+		/* Fall through */
+	case IN_DELETE:
+	case IN_DELETE_SELF:
+	case IN_UNMOUNT:
+		if (cookie == 0 || event_type == IN_UNMOUNT) {
+			/* This is how things generally work with
+			 * deleted items: 
+			 *
+			 * 1. Check if we have a child file already in
+			 *    the deleted hash table AND this is a
+			 *    directory. If so, we delete all child
+			 *    items.
+			 * 2. Add the file to the deleted hash table
+			 *    but only if we don't have a higher level
+			 *    directory in the hash table already.
+			 * 3. Make sure we have the timeout set up to
+			 *    handle deleted items after n seconds.
+			 * 4. When we handle deleted items, only emit
+			 *    those which were deleted in the last 2
+			 *    seconds. 
+			 */
+
+			data = event_data_new (file, event_type);
+
+			/* Stage 1: */
+			if (is_directory) {
+				gint count;
+
+				tracker_monitor_remove (monitor,
+							module_name,
+							file);
+
+				count =	g_hash_table_foreach_remove (monitor->private->cached_events,
+								     libinotify_cached_event_delete_children_func,
+								     data);
+
+				g_debug ("Removed %d child items in recently deleted cache", count);
+			}
+
+			/* Stage 2: */
+			g_hash_table_insert (monitor->private->cached_events,
+					     g_object_ref (data->file),
+					     data);
+
+			/* Stage 3: */
+			set_up_cache_timeout = TRUE;
+
+			/* FIXME: How do we handle the deleted items
+			 * cache for other events, i.e. we should
+			 * have something like
+			 * libinotify_monitor_force_emission() for
+			 * deleted items if we get CREATED before we
+			 * emit the DELETE event.
+			 */
+		} else if (other_file) {
+			g_signal_emit (monitor,
+				       signals[ITEM_MOVED], 0,
+				       module_name,
+				       file,
+				       other_file,
+				       is_directory, 
+				       TRUE);
+			g_hash_table_remove (monitor->private->event_pairs,
+					     GUINT_TO_POINTER (cookie));
+		}
+
+		break;
+
+	case IN_CREATE:
+		/* Here we just wait with CREATE events and
+		 * if we get MODIFY after, we drop the MODIFY
+		 * and just emit CREATE because otherwise we
+		 * send twice as much traffic to the indexer.
+		 */
+		set_up_cache_timeout = TRUE;
+
+		data = event_data_new (file, event_type);
+
+		g_hash_table_insert (monitor->private->cached_events,
+				     g_object_ref (data->file),
+				     data);
+		break;
+
+	case IN_MOVED_TO:
+		/* If the file is *ALREADY* in the cache, we need to
+		 * handle that cache first. Otherwise we have
+		 * disparity when the cache expires.
+		 */
+		libinotify_monitor_force_emission (monitor, file, event_type,
+						   module_name, is_directory);
+
+		/* Handle event */
+		if (cookie == 0) {
+			g_signal_emit (monitor,
+				       signals[ITEM_CREATED], 0,
+				       module_name,
+				       file,
+				       is_directory);
+		} else if (other_file) {
+			gboolean is_source_indexed;
+
+			/* We check for the event pair in the
+			 * hash table here. If it doesn't
+			 * exist even though we have a cookie
+			 * it means we didn't have a monitor
+			 * set up on the source location.
+			 * This means we need to get the
+			 * processor to crawl the new
+			 * location.
+			 */
+
+			if (g_hash_table_lookup (monitor->private->event_pairs,
+						 GUINT_TO_POINTER (cookie))) {
+				is_source_indexed = TRUE;
+			} else {
+				is_source_indexed = FALSE;
+			}
+
+			g_signal_emit (monitor,
+				       signals[ITEM_MOVED], 0,
+				       module_name,
+				       other_file,
+				       file,
+				       is_directory,
+				       is_source_indexed);
+			g_hash_table_remove (monitor->private->event_pairs,
+					     GUINT_TO_POINTER (cookie));
+		}
+
+		break;
+
+	case IN_MOVE_SELF:
+		/* We ignore this one because it is a
+		 * convenience state and we handle the
+		 * MOVE_TO and MOVE_FROM already.
+		 */
+
+	default:
+		break;
+	}
+
+	if (set_up_cache_timeout &&
+	    monitor->private->cached_events_timeout_id == 0) {
+		g_debug ("Setting up cached events timeout check");
+
+		monitor->private->cached_events_timeout_id =
+			g_timeout_add_seconds (CACHE_LIFETIME_SECONDS,
+					       libinotify_cached_events_timeout_cb,
+					       monitor);
+	}
+
+	g_free (str1);
+	g_free (str2);
+	g_object_unref (file);
+}
+
+static INotifyHandle *
+libinotify_monitor_directory (TrackerMonitor *monitor,
+			      GFile	     *file)
+{
+	INotifyHandle *handle;
+	gchar	      *path;
+	guint32	       mask;
+	unsigned long  flags;
+
+	flags  = 0;
+	flags &= ~IN_FLAG_FILE_BASED;
+	mask   =  IN_ALL_EVENTS;
+
+	/* For files */
+	/* flags |= IN_FLAG_FILE_BASED; */
+
+	path = g_file_get_path (file);
+	handle = inotify_monitor_add (path,
+				      mask,
+				      flags,
+				      libinotify_monitor_event_cb,
+				      monitor);
+	g_free (path);
+
+	if (!handle) {
+		return NULL;
+	}
+
+	return handle;
+}
+
+static void
+libinotify_monitor_cancel (gpointer data)
+{
+	inotify_monitor_remove (data);
+}
+
+TrackerMonitor *
+tracker_monitor_new (TrackerConfig *config)
+{
+	TrackerMonitor	      *monitor;
+	TrackerMonitorPrivate *priv;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+	monitor = g_object_new (TRACKER_TYPE_MONITOR, NULL);
+
+	priv = monitor->private;
+
+	priv->config = g_object_ref (config);
+
+	return monitor;
+}
+
+gboolean
+tracker_monitor_get_enabled (TrackerMonitor *monitor)
+{
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+
+	return monitor->private->enabled;
+}
+
+void
+tracker_monitor_set_enabled (TrackerMonitor *monitor,
+			     gboolean	     enabled)
+{
+	g_return_if_fail (TRACKER_IS_MONITOR (monitor));
+
+	monitor->private->enabled = enabled;
+
+	g_object_notify (G_OBJECT (monitor), "enabled");
+}
+
+gboolean
+tracker_monitor_add (TrackerMonitor *monitor,
+		     const gchar    *module_name,
+		     GFile	    *file)
+{
+	INotifyHandle *file_monitor;
+	GHashTable   *monitors;
+	GSList	     *ignored_roots;
+	GSList	     *l;
+	gchar	     *path;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+	g_return_val_if_fail (module_name != NULL, FALSE);
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+	if (!tracker_config_get_enable_watches (monitor->private->config)) {
+		return TRUE;
+	}
+
+	monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+	if (!monitors) {
+		g_warning ("No monitor hash table for module:'%s'",
+			   module_name);
+		return FALSE;
+	}
+
+	if (g_hash_table_lookup (monitors, file)) {
+		return TRUE;
+	}
+
+	/* Cap the number of monitors */
+	if (g_hash_table_size (monitors) >= monitor->private->monitor_limit) {
+		monitor->private->monitors_ignored++;
+
+		if (!monitor->private->monitor_limit_warned) {
+			g_warning ("The maximum number of monitors to set (%d) "
+				   "has been reached, not adding any new ones",
+				   monitor->private->monitor_limit);
+			monitor->private->monitor_limit_warned = TRUE;
+		}
+
+		return FALSE;
+	}
+
+	path = g_file_get_path (file);
+
+	ignored_roots = tracker_config_get_no_watch_directory_roots (monitor->private->config);
+
+	/* Check this location isn't excluded in the config */
+	for (l = ignored_roots; l; l = l->next) {
+		if (strcmp (path, l->data) == 0) {
+			g_message ("Not adding monitor for:'%s', path is in config ignore list",
+				   path);
+			g_free (path);
+			return FALSE;
+		}
+	}
+
+	/* We don't check if a file exists or not since we might want
+	 * to monitor locations which don't exist yet.
+	 *
+	 * Also, we assume ALL paths passed are directories.
+	 */
+	file_monitor = libinotify_monitor_directory (monitor, file);
+
+	if (!file_monitor) {
+		g_warning ("Could not add monitor for path:'%s'",
+			   path);
+		g_free (path);
+		return FALSE;
+	}
+
+	g_hash_table_insert (monitors,
+			     g_object_ref (file),
+			     file_monitor);
+
+	g_debug ("Added monitor for module:'%s', path:'%s', total monitors:%d",
+		 module_name,
+		 path,
+		 g_hash_table_size (monitors));
+
+	g_free (path);
+
+	return TRUE;
+}
+
+gboolean
+tracker_monitor_remove (TrackerMonitor *monitor,
+			const gchar    *module_name,
+			GFile          *file)
+{
+	GHashTable *monitors;
+	gchar      *path;
+	gboolean    removed;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+	g_return_val_if_fail (module_name != NULL, FALSE);
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
+	
+	monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+	if (!monitors) {
+		g_warning ("No monitor hash table for module:'%s'",
+			   module_name);
+		return FALSE;
+	}
+
+	removed = g_hash_table_remove (monitors, file);
+	path = g_file_get_path (file);
+	
+	g_debug ("Removed monitor for module:'%s', path:'%s', total monitors:%d",
+		 module_name,
+		 path,
+		 g_hash_table_size (monitors));
+	
+	g_free (path);
+
+	/* tracker_monitor_remove_recursively (monitor, event->file); */
+	
+	return removed;
+}
+
+	
+gboolean
+tracker_monitor_remove_recursively (TrackerMonitor *monitor,
+				    GFile          *file)
+{
+	GHashTableIter iter1;
+	gpointer       iter_module_name, iter_hash_table;
+	guint          items_removed = 0;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+	g_hash_table_iter_init (&iter1, monitor->private->modules);
+	while (g_hash_table_iter_next (&iter1, &iter_module_name, &iter_hash_table)) {
+		GHashTableIter iter2;
+		gpointer       iter_file, iter_file_monitor;
+
+		g_hash_table_iter_init (&iter2, iter_hash_table);
+		while (g_hash_table_iter_next (&iter2, &iter_file, &iter_file_monitor)) {
+			gchar *path;
+		
+			if (!g_file_has_prefix (iter_file, file) &&
+			    !g_file_equal (iter_file, file)) {
+				continue;
+			}
+
+			path = g_file_get_path (iter_file);
+			
+			g_debug ("Removed monitor for module:'%s', path:'%s', total monitors:%d",
+				 (gchar*) iter_module_name,
+				 path,
+				 g_hash_table_size (iter_hash_table));
+
+			g_free (path);
+
+			g_hash_table_iter_remove (&iter2);
+		
+			/* We reset this because now it is possible we have limit - 1 */
+			monitor->private->monitor_limit_warned = FALSE;
+			
+			items_removed++;
+		}
+	}
+
+	return items_removed > 0;
+}
+
+gboolean
+tracker_monitor_is_watched (TrackerMonitor *monitor,
+			    const gchar    *module_name,
+			    GFile	   *file)
+{
+	GHashTable *monitors;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+	g_return_val_if_fail (module_name != NULL, FALSE);
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+	monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+	if (!monitors) {
+		g_warning ("No monitor hash table for module:'%s'",
+			   module_name);
+		return FALSE;
+	}
+
+	return g_hash_table_lookup (monitors, file) != NULL;
+}
+
+gboolean
+tracker_monitor_is_watched_by_string (TrackerMonitor *monitor,
+				      const gchar    *module_name,
+				      const gchar    *path)
+{
+	GFile	   *file;
+	GHashTable *monitors;
+	gboolean    watched;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+	g_return_val_if_fail (module_name != NULL, FALSE);
+	g_return_val_if_fail (path != NULL, FALSE);
+
+	monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+	if (!monitors) {
+		g_warning ("No monitor hash table for module:'%s'",
+			   module_name);
+		return FALSE;
+	}
+
+	file = g_file_new_for_path (path);
+	watched = g_hash_table_lookup (monitors, file) != NULL;
+	g_object_unref (file);
+
+	return watched;
+}
+
+guint
+tracker_monitor_get_count (TrackerMonitor *monitor,
+			   const gchar	  *module_name)
+{
+	guint count;
+
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
+
+	if (module_name) {
+		GHashTable *monitors;
+
+		monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+		if (!monitors) {
+			g_warning ("No monitor hash table for module:'%s'",
+				   module_name);
+			return 0;
+		}
+
+		count = g_hash_table_size (monitors);
+	} else {
+		GList *all_modules, *l;
+
+		all_modules = g_hash_table_get_values (monitor->private->modules);
+
+		for (l = all_modules, count = 0; l; l = l->next) {
+			count += g_hash_table_size (l->data);
+		}
+
+		g_list_free (all_modules);
+	}
+
+	return count;
+}
+
+guint
+tracker_monitor_get_ignored (TrackerMonitor *monitor)
+{
+	g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
+
+	return monitor->private->monitors_ignored;
+}
diff --git a/src/libtracker-miner/tracker-monitor.h b/src/libtracker-miner/tracker-monitor.h
new file mode 100644
index 0000000..c456988
--- /dev/null
+++ b/src/libtracker-miner/tracker-monitor.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 __TRACKERD_MONITOR_H__
+#define __TRACKERD_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "tracker-config.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_MONITOR		(tracker_monitor_get_type ())
+#define TRACKER_MONITOR(object)		(G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_MONITOR, TrackerMonitor))
+#define TRACKER_MONITOR_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_MONITOR, TrackerMonitorClass))
+#define TRACKER_IS_MONITOR(object)	(G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_MONITOR))
+#define TRACKER_IS_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_MONITOR))
+#define TRACKER_MONITOR_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_MONITOR, TrackerMonitorClass))
+
+typedef struct _TrackerMonitor	       TrackerMonitor;
+typedef struct _TrackerMonitorClass    TrackerMonitorClass;
+typedef struct _TrackerMonitorPrivate  TrackerMonitorPrivate;
+
+struct _TrackerMonitor {
+	GObject		       parent;
+	TrackerMonitorPrivate *private;
+};
+
+struct _TrackerMonitorClass {
+	GObjectClass	       parent;
+};
+
+GType		tracker_monitor_get_type	     (void);
+TrackerMonitor *tracker_monitor_new		     (TrackerConfig  *config);
+gboolean	tracker_monitor_get_enabled	     (TrackerMonitor *monitor);
+void		tracker_monitor_set_enabled	     (TrackerMonitor *monitor,
+						      gboolean	      enabled);
+gboolean	tracker_monitor_add		     (TrackerMonitor *monitor,
+						      const gchar    *module_name,
+						      GFile	     *file);
+gboolean        tracker_monitor_remove               (TrackerMonitor *monitor,
+						      const gchar    *module_name,
+						      GFile          *file);
+gboolean        tracker_monitor_remove_recursively   (TrackerMonitor *monitor,
+						      GFile	     *file);
+gboolean	tracker_monitor_is_watched	     (TrackerMonitor *monitor,
+						      const gchar    *module_name,
+						      GFile	     *file);
+gboolean	tracker_monitor_is_watched_by_string (TrackerMonitor *monitor,
+						      const gchar    *module_name,
+						      const gchar    *path);
+guint		tracker_monitor_get_count	     (TrackerMonitor *monitor,
+						      const gchar    *module_name);
+guint		tracker_monitor_get_ignored	     (TrackerMonitor *monitor);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_MONITOR_H__ */
diff --git a/src/libtracker-miner/tracker-processor.c b/src/libtracker-miner/tracker-processor.c
new file mode 100644
index 0000000..663d14b
--- /dev/null
+++ b/src/libtracker-miner/tracker-processor.c
@@ -0,0 +1,1774 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "config.h"
+
+#include <string.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-storage.h>
+#include <libtracker-common/tracker-module-config.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-crawler.h"
+#include "tracker-dbus.h"
+#include "tracker-monitor.h"
+#include "tracker-processor.h"
+#include "tracker-status.h"
+
+#define TRACKER_PROCESSOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_PROCESSOR, TrackerProcessorPrivate))
+
+typedef enum {
+	SENT_TYPE_NONE,
+	SENT_TYPE_CREATED,
+	SENT_TYPE_UPDATED,
+	SENT_TYPE_DELETED,
+	SENT_TYPE_MOVED
+} SentType;
+
+struct TrackerProcessorPrivate {
+	TrackerConfig  *config;
+	TrackerStorage *hal;
+	TrackerMonitor *monitor;
+
+	/* Crawlers */
+	GHashTable     *crawlers;
+
+	/* File queues for indexer */
+	guint		item_queues_handler_id;
+
+	GHashTable     *items_created_queues;
+	GHashTable     *items_updated_queues;
+	GHashTable     *items_deleted_queues;
+	GHashTable     *items_moved_queues;
+
+	SentType	sent_type;
+	GStrv		sent_items;
+	const gchar    *sent_module_name;
+
+	/* Status */
+	GList	       *modules;
+	GList	       *current_module;
+
+	GList          *devices;
+	GList          *current_device;
+
+	GTimer	       *timer;
+
+	gboolean        been_started;
+	gboolean	interrupted;
+
+	gboolean	finished_modules;
+	gboolean	finished_devices;
+	gboolean	finished_sending;
+	gboolean	finished_indexer;
+
+	/* Statistics */
+	guint		total_directories_found;
+	guint		total_directories_ignored;
+	guint		total_files_found;
+	guint		total_files_ignored;
+
+	guint		directories_found;
+	guint		directories_ignored;
+	guint		files_found;
+	guint		files_ignored;
+};
+
+enum {
+	FINISHED,
+	LAST_SIGNAL
+};
+
+static void tracker_processor_finalize        (GObject          *object);
+static void crawler_destroy_notify            (gpointer          data);
+static void item_queue_destroy_notify         (gpointer          data);
+static void process_module_next               (TrackerProcessor *processor);
+static void process_modules_stop              (TrackerProcessor *processor);
+static void process_device_next               (TrackerProcessor *processor);
+static void process_devices_stop              (TrackerProcessor *processor);
+static void process_check_completely_finished (TrackerProcessor *processor);
+static void process_next                      (TrackerProcessor *processor);
+#if 0
+static void indexer_started_cb                (TrackerIndexer   *indexer,
+					       gpointer          user_data);
+static void indexer_finished_cb               (TrackerIndexer   *indexer,
+					       gdouble           seconds_elapsed,
+					       guint             items_processed,
+					       guint             items_indexed,
+					       gboolean          interrupted,
+					       gpointer          user_data);
+#endif
+static void monitor_item_created_cb           (TrackerMonitor   *monitor,
+					       const gchar      *module_name,
+					       GFile            *file,
+					       gboolean          is_directory,
+					       gpointer          user_data);
+static void monitor_item_updated_cb           (TrackerMonitor   *monitor,
+					       const gchar      *module_name,
+					       GFile            *file,
+					       gboolean          is_directory,
+					       gpointer          user_data);
+static void monitor_item_deleted_cb           (TrackerMonitor   *monitor,
+					       const gchar      *module_name,
+					       GFile            *file,
+					       gboolean          is_directory,
+					       gpointer          user_data);
+static void monitor_item_moved_cb             (TrackerMonitor   *monitor,
+					       const gchar      *module_name,
+					       GFile            *file,
+					       GFile            *other_file,
+					       gboolean          is_directory,
+					       gboolean          is_source_monitored,
+					       gpointer          user_data);
+static void crawler_processing_file_cb        (TrackerCrawler   *crawler,
+					       const gchar      *module_name,
+					       GFile            *file,
+					       gpointer          user_data);
+static void crawler_processing_directory_cb   (TrackerCrawler   *crawler,
+					       const gchar      *module_name,
+					       GFile            *file,
+					       gpointer          user_data);
+static void crawler_finished_cb               (TrackerCrawler   *crawler,
+					       const gchar      *module_name,
+					       guint             directories_found,
+					       guint             directories_ignored,
+					       guint             files_found,
+					       guint             files_ignored,
+					       gpointer          user_data);
+#ifdef HAVE_HAL
+static void mount_point_added_cb              (TrackerStorage   *hal,
+                                               const gchar      *volume_uuid,
+                                               const gchar      *mount_point,
+                                               gpointer          user_data);
+static void mount_point_removed_cb            (TrackerStorage   *hal,
+                                               const gchar      *volume_uuid,
+                                               const gchar      *mount_point,
+                                               gpointer          user_data);
+#endif /* HAVE_HAL */
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (TrackerProcessor, tracker_processor, G_TYPE_OBJECT)
+
+static void
+tracker_processor_class_init (TrackerProcessorClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+	object_class->finalize = tracker_processor_finalize;
+
+	signals[FINISHED] =
+		g_signal_new ("finished",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (TrackerProcessorClass, finished),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);
+
+	g_type_class_add_private (object_class, sizeof (TrackerProcessorPrivate));
+}
+
+static void
+tracker_processor_init (TrackerProcessor *object)
+{
+	TrackerProcessorPrivate *priv;
+	GList			*l;
+
+	object->private = TRACKER_PROCESSOR_GET_PRIVATE (object);
+
+	priv = object->private;
+
+	priv->modules = tracker_module_config_get_modules ();
+
+	/* For each module we create a TrackerCrawler and keep them in
+	 * a hash table to look up.
+	 */
+	priv->crawlers =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       crawler_destroy_notify);
+
+
+	/* For each module we create a hash table for queues for items
+	 * to update/create/delete in the indexer. This is sent on
+	 * when the queue is processed.
+	 */
+	priv->items_created_queues =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       item_queue_destroy_notify);
+	priv->items_updated_queues =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       item_queue_destroy_notify);
+	priv->items_deleted_queues =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       item_queue_destroy_notify);
+	priv->items_moved_queues =
+		g_hash_table_new_full (g_str_hash,
+				       g_str_equal,
+				       g_free,
+				       item_queue_destroy_notify);
+
+	for (l = priv->modules; l; l = l->next) {
+		/* Create queues for this module */
+		g_hash_table_insert (priv->items_created_queues,
+				     g_strdup (l->data),
+				     g_queue_new ());
+		g_hash_table_insert (priv->items_updated_queues,
+				     g_strdup (l->data),
+				     g_queue_new ());
+		g_hash_table_insert (priv->items_deleted_queues,
+				     g_strdup (l->data),
+				     g_queue_new ());
+		g_hash_table_insert (priv->items_moved_queues,
+				     g_strdup (l->data),
+				     g_queue_new ());
+	}
+}
+
+static void
+tracker_processor_finalize (GObject *object)
+{
+	TrackerProcessorPrivate *priv;
+
+	priv = TRACKER_PROCESSOR_GET_PRIVATE (object);
+
+	if (priv->timer) {
+		g_timer_destroy (priv->timer);
+	}
+
+	if (priv->item_queues_handler_id) {
+		g_source_remove (priv->item_queues_handler_id);
+		priv->item_queues_handler_id = 0;
+	}
+
+	if (priv->items_moved_queues) {
+		g_hash_table_unref (priv->items_moved_queues);
+	}
+
+	if (priv->items_deleted_queues) {
+		g_hash_table_unref (priv->items_deleted_queues);
+	}
+
+	if (priv->items_updated_queues) {
+		g_hash_table_unref (priv->items_updated_queues);
+	}
+
+	if (priv->items_created_queues) {
+		g_hash_table_unref (priv->items_created_queues);
+	}
+
+	if (priv->crawlers) {
+		g_hash_table_unref (priv->crawlers);
+	}
+
+	g_list_free (priv->modules);
+
+#if 0
+	g_signal_handlers_disconnect_by_func (priv->indexer,
+					      G_CALLBACK (indexer_started_cb),
+					      NULL);
+	g_signal_handlers_disconnect_by_func (priv->indexer,
+					      G_CALLBACK (indexer_finished_cb),
+					      NULL);
+#endif
+
+	g_signal_handlers_disconnect_by_func (priv->monitor,
+					      G_CALLBACK (monitor_item_deleted_cb),
+					      object);
+	g_signal_handlers_disconnect_by_func (priv->monitor,
+					      G_CALLBACK (monitor_item_updated_cb),
+					      object);
+	g_signal_handlers_disconnect_by_func (priv->monitor,
+					      G_CALLBACK (monitor_item_created_cb),
+					      object);
+	g_signal_handlers_disconnect_by_func (priv->monitor,
+					      G_CALLBACK (monitor_item_moved_cb),
+					      object);
+	g_object_unref (priv->monitor);
+
+#ifdef HAVE_HAL
+	if (priv->devices) {
+		g_list_foreach (priv->devices, (GFunc) g_free, NULL);
+		g_list_free (priv->devices);
+	}
+
+	if (priv->hal) {
+		g_signal_handlers_disconnect_by_func (priv->hal,
+		                                      mount_point_added_cb,
+		                                      object);
+		g_signal_handlers_disconnect_by_func (priv->hal,
+		                                      mount_point_removed_cb,
+		                                      object);
+
+		g_object_unref (priv->hal);
+	}
+#endif /* HAVE_HAL */
+
+	g_object_unref (priv->config);
+
+	G_OBJECT_CLASS (tracker_processor_parent_class)->finalize (object);
+}
+
+
+static void
+get_remote_roots (TrackerProcessor  *processor,
+		  GList	           **mounted_directory_roots,
+		  GList	           **removable_device_roots)
+{
+	GList *l1;
+	GList *l2;
+
+#ifdef HAVE_HAL
+	l1 = tracker_storage_get_mounted_directory_roots (processor->private->hal);
+	l2 = tracker_storage_get_removable_device_roots (processor->private->hal);
+#else  /* HAVE_HAL */
+	l1 = NULL;
+	l2 = NULL;
+#endif /* HAVE_HAL */
+
+	/* The options to index removable media and the index mounted
+	 * directories are both mutually exclusive even though
+	 * removable media is mounted on a directory.
+	 *
+	 * Since we get ALL mounted directories from HAL, we need to
+	 * remove those which are removable device roots.
+	 */
+	if (l2) {
+		GList *l;
+		GList *list = NULL;
+
+		for (l = l1; l; l = l->next) {
+			if (g_list_find_custom (l2, l->data, (GCompareFunc) g_strcmp0)) {
+				continue;
+			}
+
+			list = g_list_prepend (list, l->data);
+		}
+
+		*mounted_directory_roots = g_list_reverse (list);
+	} else {
+		*mounted_directory_roots = NULL;
+	}
+
+	*removable_device_roots = g_list_copy (l2);
+}
+
+static gboolean
+path_should_be_ignored_for_media (TrackerProcessor *processor,
+				  const gchar	   *path)
+{
+	GList	 *roots = NULL;
+	GList	 *mounted_directory_roots = NULL;
+	GList	 *removable_device_roots = NULL;
+	GList	 *l;
+	gboolean  ignore_mounted_directories;
+	gboolean  ignore_removable_devices;
+	gboolean  ignore = FALSE;
+
+	ignore_mounted_directories =
+		!tracker_config_get_index_mounted_directories (processor->private->config);
+	ignore_removable_devices =
+		!tracker_config_get_index_removable_devices (processor->private->config);
+
+	if (ignore_mounted_directories || ignore_removable_devices) {
+		get_remote_roots (processor,
+				  &mounted_directory_roots,
+				  &removable_device_roots);
+	}
+
+	if (ignore_mounted_directories) {
+		roots = g_list_concat (roots, mounted_directory_roots);
+	}
+
+	if (ignore_removable_devices) {
+		roots = g_list_concat (roots, removable_device_roots);
+	}
+
+	for (l = roots; l && !ignore; l = l->next) {
+		/* If path matches a mounted or removable device by
+		 * prefix then we should ignore it since we don't
+		 * crawl those by choice in the config.
+		 */
+		if (strcmp (path, l->data) == 0) {
+			ignore = TRUE;
+		}
+
+		/* FIXME: Should we add a DIR_SEPARATOR on the end of
+		 * these before comparing them?
+		 */
+		if (g_str_has_prefix (path, l->data)) {
+			ignore = TRUE;
+		}
+	}
+
+	g_list_free (roots);
+
+	return ignore;
+}
+
+static GQueue *
+get_next_queue_with_data (GList       *modules,
+			  GHashTable  *hash_table,
+			  gchar      **module_name)
+{
+	GQueue *found_queue;
+	GQueue *queue;
+	GList  *l;
+
+	if (module_name) {
+		*module_name = NULL;
+	}
+
+	for (l = modules, found_queue = NULL; l && !found_queue; l = l->next) {
+		queue = g_hash_table_lookup (hash_table, l->data);
+
+		if (g_queue_get_length (queue) > 0) {
+			if (module_name) {
+				*module_name = l->data;
+				found_queue = queue;
+			}
+		}
+	}
+
+	return found_queue;
+}
+
+static void
+crawler_destroy_notify (gpointer data)
+{
+	TrackerCrawler *crawler;
+
+	crawler = TRACKER_CRAWLER (data);
+
+	if (crawler) {
+		guint lsignals;
+
+		lsignals = g_signal_handlers_disconnect_matched (crawler,
+								 G_SIGNAL_MATCH_FUNC,
+								 0,
+								 0,
+								 NULL,
+								 G_CALLBACK (crawler_processing_file_cb),
+								 NULL);
+		lsignals = g_signal_handlers_disconnect_matched (crawler,
+								 G_SIGNAL_MATCH_FUNC,
+								 0,
+								 0,
+								 NULL,
+								 G_CALLBACK (crawler_processing_directory_cb),
+								 NULL);
+		lsignals = g_signal_handlers_disconnect_matched (crawler,
+								 G_SIGNAL_MATCH_FUNC,
+								 0,
+								 0,
+								 NULL,
+								 G_CALLBACK (crawler_finished_cb),
+								 NULL);
+
+		g_object_unref (crawler);
+	}
+}
+
+static void
+item_queue_destroy_notify (gpointer data)
+{
+	GQueue *queue;
+
+	queue = (GQueue *) data;
+
+	g_queue_foreach (queue, (GFunc) g_object_unref, NULL);
+	g_queue_free (queue);
+}
+
+static void
+item_queue_processed_cb (TrackerProcessor *processor)
+{
+	g_strfreev (processor->private->sent_items);
+
+	/* Reset for next batch to be sent */
+	processor->private->sent_items = NULL;
+	processor->private->sent_module_name = NULL;
+	processor->private->sent_type = SENT_TYPE_NONE;
+}
+
+static gboolean
+item_queue_handlers_cb (gpointer user_data)
+{
+	TrackerProcessor *processor;
+	TrackerStatus     status;
+	GQueue		 *queue;
+	GStrv		  files;
+	gchar		 *module_name;
+	gboolean          should_repeat = FALSE;
+	GTimeVal          time_now;
+	static GTimeVal   time_last = { 0, 0 };
+
+	processor = user_data;
+
+	status = tracker_status_get ();
+
+	/* Don't spam */
+	g_get_current_time (&time_now);
+
+	should_repeat = (time_now.tv_sec - time_last.tv_sec) >= 10;
+	if (should_repeat) {
+		time_last = time_now;
+	}
+
+	switch (status) {
+	case TRACKER_STATUS_PAUSED:
+		/* This way we don't send anything to the indexer from
+		 * monitor events but we still queue them ready to
+		 * send when we are unpaused.  
+		 */
+		if (should_repeat) {
+			g_message ("We are paused, sending nothing to the "
+				   "indexer until we are unpaused");
+		}
+
+	case TRACKER_STATUS_PENDING:
+	case TRACKER_STATUS_WATCHING:
+		/* Wait until we have finished crawling before
+		 * sending anything.
+		 */
+		return TRUE;
+
+	default:
+		break;
+	}
+
+	/* This is here so we don't try to send something if we are
+	 * still waiting for a response from the last send.
+	 */
+	if (processor->private->sent_type != SENT_TYPE_NONE) {
+		if (should_repeat) {
+			g_message ("Still waiting for response from indexer, "
+				   "not sending more files yet");
+		}
+
+		return TRUE;
+	}
+
+	/* Process the deleted items first */
+	queue = get_next_queue_with_data (processor->private->modules,
+					  processor->private->items_deleted_queues,
+					  &module_name);
+
+	if (queue) {
+		files = tracker_dbus_queue_gfile_to_strv (queue, -1);
+
+		g_message ("Queue for module:'%s' deleted items processed, sending %d to the indexer",
+			   module_name,
+			   g_strv_length (files));
+
+		processor->private->finished_indexer = FALSE;
+
+		processor->private->sent_type = SENT_TYPE_DELETED;
+		processor->private->sent_module_name = module_name;
+		processor->private->sent_items = files;
+
+#if 0
+		tracker_indexer_files_check (processor->private->indexer,
+					     module_name,
+					     files);
+#endif
+		item_queue_processed_cb (processor);
+
+		return TRUE;
+	}
+
+	/* Process the created items next */
+	queue = get_next_queue_with_data (processor->private->modules,
+					  processor->private->items_created_queues,
+					  &module_name);
+
+	if (queue) {
+		/* Now we try to send items to the indexer */
+		tracker_status_set_and_signal (TRACKER_STATUS_INDEXING);
+
+		files = tracker_dbus_queue_gfile_to_strv (queue, -1);
+
+		g_message ("Queue for module:'%s' created items processed, sending %d to the indexer",
+			   module_name,
+			   g_strv_length (files));
+
+		processor->private->finished_indexer = FALSE;
+
+		processor->private->sent_type = SENT_TYPE_CREATED;
+		processor->private->sent_module_name = module_name;
+		processor->private->sent_items = files;
+
+#if 0
+		tracker_indexer_files_check (processor->private->indexer,
+					     module_name,
+					     files);
+#endif
+		item_queue_processed_cb (processor);
+
+		return TRUE;
+	}
+
+	/* Process the updated items next */
+	queue = get_next_queue_with_data (processor->private->modules,
+					  processor->private->items_updated_queues,
+					  &module_name);
+
+	if (queue) {
+		/* Now we try to send items to the indexer */
+		tracker_status_set_and_signal (TRACKER_STATUS_INDEXING);
+
+		files = tracker_dbus_queue_gfile_to_strv (queue, -1);
+
+		g_message ("Queue for module:'%s' updated items processed, sending %d to the indexer",
+			   module_name,
+			   g_strv_length (files));
+
+		processor->private->finished_indexer = FALSE;
+
+		processor->private->sent_type = SENT_TYPE_UPDATED;
+		processor->private->sent_module_name = module_name;
+		processor->private->sent_items = files;
+
+#if 0
+		tracker_indexer_files_check (processor->private->indexer,
+					     module_name,
+					     files);
+#endif
+		item_queue_processed_cb (processor);
+
+		return TRUE;
+	}
+
+	/* Process the moved items last */
+	queue = get_next_queue_with_data (processor->private->modules,
+					  processor->private->items_moved_queues,
+					  &module_name);
+
+	if (queue) {
+		const gchar *source;
+		const gchar *target;
+
+		/* Now we try to send items to the indexer */
+		tracker_status_set_and_signal (TRACKER_STATUS_INDEXING);
+
+		files = tracker_dbus_queue_gfile_to_strv (queue, 2);
+
+		if (files) {
+			source = files[0];
+			target = files[1];
+		} else {
+			source = NULL;
+			target = NULL;
+		}
+
+		g_message ("Queue for module:'%s' moved items processed, sending %d to the indexer",
+			   module_name,
+			   g_strv_length (files));
+
+		processor->private->finished_indexer = FALSE;
+
+		processor->private->sent_type = SENT_TYPE_MOVED;
+		processor->private->sent_module_name = module_name;
+		processor->private->sent_items = files;
+
+#if 0
+		tracker_indexer_file_move (processor->private->indexer,
+					   module_name,
+					   source,
+					   target);
+#endif
+		item_queue_processed_cb (processor);
+
+		return TRUE;
+	}
+
+	processor->private->item_queues_handler_id = 0;
+
+	processor->private->finished_sending = TRUE;
+	process_check_completely_finished (processor);
+
+	return FALSE;
+}
+
+static void
+item_queue_handlers_set_up (TrackerProcessor *processor)
+{
+	processor->private->finished_sending = FALSE;
+
+	if (processor->private->item_queues_handler_id != 0) {
+		return;
+	}
+
+	processor->private->item_queues_handler_id =
+		g_idle_add (item_queue_handlers_cb,
+			    processor);
+}
+
+static gboolean
+is_path_on_ignore_list (GSList	    *ignore_list,
+			const gchar *path)
+{
+	GSList *l;
+
+	if (!ignore_list || !path) {
+		return FALSE;
+	}
+
+	for (l = ignore_list; l; l = l->next) {
+		if (strcmp (path, l->data) == 0) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void
+process_module_files_add_legacy_options (TrackerProcessor *processor)
+{
+	TrackerCrawler *crawler;
+	GSList	       *no_watch_roots;
+	GSList	       *watch_roots;
+	GSList	       *crawl_roots;
+	GSList	       *l;
+	guint		watch_root_count;
+	guint		crawl_root_count;
+	const gchar    *module_name = "files";
+
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+	tracker_crawler_use_module_paths (crawler, TRUE);
+	tracker_crawler_special_paths_clear (crawler);
+
+	no_watch_roots = tracker_config_get_no_watch_directory_roots (processor->private->config);
+	watch_roots = tracker_config_get_watch_directory_roots (processor->private->config);
+	crawl_roots = tracker_config_get_crawl_directory_roots (processor->private->config);
+
+	watch_root_count = 0;
+	crawl_root_count = 0;
+
+	/* This module get special treatment to make sure legacy
+	 * options are supported.
+	 */
+
+	/* Print ignored locations */
+	g_message ("  User ignored crawls & monitors:");
+	for (l = no_watch_roots; l; l = l->next) {
+		g_message ("    %s", (gchar*) l->data);
+	}
+
+	if (g_slist_length (no_watch_roots) == 0) {
+		g_message ("    NONE");
+	}
+
+	/* Add monitors, from WatchDirectoryRoots config key */
+	g_message ("  User monitors being added:");
+	for (l = watch_roots; l; l = l->next) {
+		GFile *file;
+
+		if (is_path_on_ignore_list (no_watch_roots, l->data)) {
+			continue;
+		}
+
+		g_message ("    %s", (gchar*) l->data);
+
+		file = g_file_new_for_path (l->data);
+		tracker_monitor_add (processor->private->monitor, module_name, file);
+		g_object_unref (file);
+
+		watch_root_count++;
+	}
+
+	if (g_slist_length (watch_roots) == 0) {
+		g_message ("    NONE");
+	}
+
+	/* Add crawls, from WatchDirectoryRoots and
+	 * CrawlDirectoryRoots config keys.
+	 */
+	g_message ("  User crawls being added:");
+
+	for (l = watch_roots; l; l = l->next) {
+		if (is_path_on_ignore_list (no_watch_roots, l->data)) {
+			continue;
+		}
+
+		g_message ("    %s", (gchar*) l->data);
+		tracker_crawler_special_paths_add (crawler, l->data);
+	}
+
+	for (l = crawl_roots; l; l = l->next) {
+		if (is_path_on_ignore_list (no_watch_roots, l->data)) {
+			continue;
+		}
+
+		g_message ("    %s", (gchar*) l->data);
+		tracker_crawler_special_paths_add (crawler, l->data);
+
+		crawl_root_count++;
+	}
+
+	if (g_slist_length (watch_roots) == 0 &&
+	    g_slist_length (crawl_roots) == 0) {
+		g_message ("    NONE");
+	}
+}
+
+static gboolean
+process_module_is_disabled (TrackerProcessor *processor,
+			    const gchar      *module_name)
+{
+	GSList *disabled_modules;
+	
+	if (!tracker_module_config_get_enabled (module_name)) {
+		g_message ("  Module disabled by module config");
+		return TRUE;
+	} 
+
+	disabled_modules = tracker_config_get_disabled_modules (processor->private->config);
+	
+	if (g_slist_find_custom (disabled_modules, module_name, (GCompareFunc) g_strcmp0)) {
+		g_message ("  Module disabled by user");
+		return TRUE;
+	} 
+
+	return FALSE;
+}
+
+static void
+process_module (TrackerProcessor *processor,
+		const gchar	 *module_name)
+{
+	TrackerCrawler *crawler;
+
+	g_message ("Processing module:'%s'", module_name);
+
+	if (process_module_is_disabled (processor, module_name)) {
+		process_module_next (processor);
+		return;
+	}
+
+	/* Here we set up legacy .cfg options like watch roots */
+	tracker_status_set_and_signal (TRACKER_STATUS_WATCHING);
+
+	if (strcmp (module_name, "files") == 0) {
+		process_module_files_add_legacy_options (processor);
+	}
+
+	/* Gets all files and directories */
+	tracker_status_set_and_signal (TRACKER_STATUS_PENDING);
+
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+	if (!tracker_crawler_start (crawler)) {
+		process_module_next (processor);
+	}
+}
+
+static void
+process_module_next (TrackerProcessor *processor)
+{
+	if (tracker_status_get_is_readonly ()) {
+		/* Block any request to process
+		 * modules if indexing is not enabled
+		 */
+		return;
+	}
+
+	/* Don't recursively iterate the modules */
+	if (!processor->private->current_module) {
+		if (!processor->private->finished_modules) {
+			processor->private->current_module = processor->private->modules;
+		}
+	} else {
+		processor->private->current_module = processor->private->current_module->next;
+	}
+
+	/* If we have no further modules to iterate */
+	if (!processor->private->current_module) {
+		process_modules_stop (processor);
+		process_next (processor);
+		return;
+	}
+
+	process_module (processor, processor->private->current_module->data);
+}
+
+static void
+process_device (TrackerProcessor *processor,
+		const gchar	 *device_root)
+{
+	TrackerCrawler *crawler;
+	GFile          *file;
+	const gchar    *module_name = "files";
+
+	g_message ("Processing device with root:'%s'", device_root);
+
+	if (process_module_is_disabled (processor, module_name)) {
+		process_device_next (processor);
+		return;
+	}
+
+	/* Here we set up legacy .cfg options like watch roots */
+	tracker_status_set_and_signal (TRACKER_STATUS_WATCHING);
+
+	/* Gets all files and directories */
+	tracker_status_set_and_signal (TRACKER_STATUS_PENDING);
+
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+	tracker_crawler_use_module_paths (crawler, FALSE);
+	tracker_crawler_special_paths_clear (crawler);
+
+	if (path_should_be_ignored_for_media (processor, device_root)) {
+		g_message ("  Ignored due to config");
+		process_device_next (processor);
+		return;
+	}
+
+	file = g_file_new_for_path (device_root);
+	tracker_monitor_add (processor->private->monitor, module_name, file);
+	g_object_unref (file);
+	
+	tracker_crawler_special_paths_add (crawler, device_root);
+
+	if (!tracker_crawler_start (crawler)) {
+		process_device_next (processor);
+	}
+}
+
+static void
+process_device_next (TrackerProcessor *processor)
+{
+	if (tracker_status_get_is_readonly ()) {
+		/* Block any request to process
+		 * modules if indexing is not enabled
+		 */
+		return;
+	}
+
+	/* Don't recursively iterate the devices */
+	if (!processor->private->current_device) {
+		if (!processor->private->finished_devices) {
+			processor->private->current_device = processor->private->devices;
+		}
+	} else {
+		GList *l;
+
+		l = processor->private->current_device;
+		
+		/* Now free that device so we don't recrawl it */
+		if (l) {
+			g_free (l->data);
+			
+			processor->private->current_device = 
+			processor->private->devices = 
+				g_list_delete_link (processor->private->devices, l);
+		}
+	}
+
+	/* If we have no further devices to iterate */
+	if (!processor->private->current_device) {
+		process_devices_stop (processor);
+		process_next (processor);
+		return;
+	}
+
+	process_device (processor, processor->private->current_device->data);
+}
+
+static void
+process_modules_start (TrackerProcessor *processor)
+{
+	g_message ("Processor has started iterating %d modules", 
+		   g_list_length (processor->private->modules));
+
+	if (processor->private->timer) {
+		g_timer_destroy (processor->private->timer);
+	}
+
+	processor->private->timer = g_timer_new ();
+
+	processor->private->finished_modules = FALSE;
+
+	processor->private->directories_found = 0;
+	processor->private->directories_ignored = 0;
+	processor->private->files_found = 0;
+	processor->private->files_ignored = 0;
+
+	process_module_next (processor);
+}
+
+static void
+process_modules_stop (TrackerProcessor *processor)
+{
+	if (processor->private->finished_modules) {
+		return;
+	}
+
+	g_message ("--------------------------------------------------");
+	g_message ("Processor has %s iterating modules",
+		   processor->private->interrupted ? "been stopped while" : "finished");
+
+	processor->private->finished_modules = TRUE;
+
+	if (processor->private->interrupted) {
+		TrackerCrawler *crawler;
+
+		crawler = g_hash_table_lookup (processor->private->crawlers,
+					       processor->private->current_module->data);
+		if (crawler) {
+			tracker_crawler_stop (crawler);
+		}
+
+		if (processor->private->timer) {
+			g_timer_destroy (processor->private->timer);
+			processor->private->timer = NULL;
+		}
+	} else {
+		gdouble elapsed;
+	
+		if (processor->private->timer) {
+			g_timer_stop (processor->private->timer);
+			elapsed = g_timer_elapsed (processor->private->timer, NULL);
+		} else {
+			elapsed = 0;
+		}
+		
+		g_message ("Module time taken : %4.4f seconds",
+			   elapsed);
+		g_message ("Module directories: %d (%d ignored)",
+			   processor->private->directories_found,
+			   processor->private->directories_ignored);
+		g_message ("Module files      : %d (%d ignored)",
+			   processor->private->files_found,
+			   processor->private->files_ignored);
+	}
+
+	g_message ("--------------------------------------------------\n");
+}
+
+static void
+process_devices_start (TrackerProcessor *processor)
+{
+	g_message ("Processor has started iterating %d devices", 
+		   g_list_length (processor->private->devices));
+
+	if (processor->private->timer) {
+		g_timer_destroy (processor->private->timer);
+	}
+
+	processor->private->timer = g_timer_new ();
+
+	processor->private->finished_devices = FALSE;
+
+	processor->private->directories_found = 0;
+	processor->private->directories_ignored = 0;
+	processor->private->files_found = 0;
+	processor->private->files_ignored = 0;
+
+	process_device_next (processor);
+}
+
+static void
+process_devices_stop (TrackerProcessor *processor)
+{
+	if (processor->private->finished_devices) {
+		return;
+	}
+
+	g_message ("--------------------------------------------------");
+	g_message ("Processor has %s iterating devices",
+		   processor->private->interrupted ? "been stopped while" : "finished");
+
+	processor->private->finished_devices = TRUE;
+
+	if (processor->private->interrupted) {
+		TrackerCrawler *crawler;
+
+		crawler = g_hash_table_lookup (processor->private->crawlers, "files");
+		if (crawler) {
+			tracker_crawler_stop (crawler);
+		}
+
+		if (processor->private->timer) {
+			g_timer_destroy (processor->private->timer);
+			processor->private->timer = NULL;
+		}
+	} else {
+		gdouble elapsed;
+	
+		if (processor->private->timer) {
+			g_timer_stop (processor->private->timer);
+			elapsed = g_timer_elapsed (processor->private->timer, NULL);
+		} else {
+			elapsed = 0;
+		}
+		
+		g_message ("Device time taken : %4.4f seconds",
+			   elapsed);
+		g_message ("Device directories: %d (%d ignored)",
+			   processor->private->directories_found,
+			   processor->private->directories_ignored);
+		g_message ("Device files      : %d (%d ignored)",
+			   processor->private->files_found,
+			   processor->private->files_ignored);
+	}
+	
+	g_message ("--------------------------------------------------\n");
+}
+
+static void
+process_continue (TrackerProcessor *processor)
+{
+	if (!processor->private->finished_modules) {
+		process_module_next (processor);
+		return;
+	}
+
+	if (!processor->private->finished_devices) {
+		process_device_next (processor);
+		return;
+	}
+
+	/* Nothing to do */
+}
+
+static void
+process_next (TrackerProcessor *processor)
+{
+	if (!processor->private->finished_modules) {
+		process_modules_start (processor);
+		return;
+	}
+
+	if (!processor->private->finished_devices) {
+		process_devices_start (processor);
+		return;
+	}
+
+	/* Only do this the first time, otherwise the results are
+	 * likely to be inaccurate. Devices can be added or removed so
+	 * we can't assume stats are correct.
+	 */
+	if (tracker_status_get_is_initial_check ()) {
+		g_message ("--------------------------------------------------");
+		g_message ("Total directories : %d (%d ignored)",
+			   processor->private->total_directories_found,
+			   processor->private->total_directories_ignored);
+		g_message ("Total files       : %d (%d ignored)",
+			   processor->private->total_files_found,
+			   processor->private->total_files_ignored);
+		g_message ("Total monitors    : %d",
+			   tracker_monitor_get_count (processor->private->monitor, NULL));
+		g_message ("--------------------------------------------------\n");
+	}
+
+	/* Now we have finished crawling, we enable monitor events */
+	g_message ("Enabling monitor events");
+	tracker_monitor_set_enabled (processor->private->monitor, TRUE);
+
+	/* Now we set the state to IDLE, the reason we do this, is it
+	 * allows us to either return to an idle state if there was
+	 * nothing to do, OR it allows us to start handling the
+	 * queues of files we just crawled. The queue handler won't
+	 * send files to the indexer while we are PENDING or WATCHING.
+	 */
+	tracker_status_set_and_signal (TRACKER_STATUS_IDLE);	
+}
+
+static void
+process_finish (TrackerProcessor *processor)
+{
+	/* TODO: Optimize DBs
+	tracker_status_set_and_signal (TRACKER_STATUS_OPTIMIZING);
+	tracker_db_manager_optimize ();
+	 */
+	
+	/* All done */
+	tracker_status_set_and_signal (TRACKER_STATUS_IDLE);
+
+	/* Set our internal state */
+	tracker_status_set_is_initial_check (FALSE);
+	
+	g_signal_emit (processor, signals[FINISHED], 0);
+}
+
+static void
+process_check_completely_finished (TrackerProcessor *processor)
+{
+	if (!processor->private->finished_sending ||
+	    !processor->private->finished_indexer) {
+		return;
+	}
+	
+	process_finish (processor);
+}
+
+#if 0
+static void
+indexer_started_cb (TrackerIndexer *indexer,
+		    gpointer	 user_data)
+{
+	TrackerProcessor *processor;
+
+	processor = user_data;
+
+	processor->private->finished_indexer = FALSE;
+}
+
+static void
+indexer_finished_cb (TrackerIndexer *indexer,
+		     gdouble	  seconds_elapsed,
+		     guint        items_processed,
+		     guint	  items_indexed,
+		     gboolean	  interrupted,
+		     gpointer	  user_data)
+{
+	TrackerProcessor *processor;
+
+	processor = user_data;
+
+	/* Save indexer's state */
+	processor->private->finished_indexer = TRUE;
+	process_check_completely_finished (processor);
+}
+#endif
+
+static void
+processor_files_check (TrackerProcessor *processor,
+		       const gchar	*module_name,
+		       GFile		*file,
+		       gboolean		 is_directory)
+{
+	TrackerCrawler *crawler;
+	GQueue	       *queue;
+	gboolean	ignored;
+	gchar	       *path;
+
+	path = g_file_get_path (file);
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+	ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+
+	g_debug ("%s:'%s' (%s) (create monitor event or user request)",
+		 ignored ? "Ignored" : "Found ",
+		 path,
+		 is_directory ? "DIR" : "FILE");
+
+	if (!ignored) {
+		if (is_directory) {
+			tracker_crawler_add_unexpected_path (crawler, path);
+		}
+
+		queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+		g_queue_push_tail (queue, g_object_ref (file));
+			
+		item_queue_handlers_set_up (processor);
+	}
+
+	g_free (path);
+}
+
+static void
+processor_files_update (TrackerProcessor *processor,
+			const gchar	 *module_name,
+			GFile		 *file,
+			gboolean	  is_directory)
+{
+	TrackerCrawler *crawler;
+	GQueue	       *queue;
+	gchar	       *path;
+	gboolean	ignored;
+
+	path = g_file_get_path (file);
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+	ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+
+	g_debug ("%s:'%s' (%s) (update monitor event or user request)",
+		 ignored ? "Ignored" : "Found ",
+		 path,
+		 is_directory ? "DIR" : "FILE");
+
+	if (!ignored) {
+		queue = g_hash_table_lookup (processor->private->items_updated_queues, module_name);
+		g_queue_push_tail (queue, g_object_ref (file));
+		
+		item_queue_handlers_set_up (processor);
+	}
+
+	g_free (path);
+}
+
+static void
+processor_files_delete (TrackerProcessor *processor,
+			const gchar	 *module_name,
+			GFile		 *file,
+			gboolean	  is_directory)
+{
+	TrackerCrawler *crawler;
+	GQueue	       *queue;
+	gchar	       *path;
+	gboolean	ignored;
+
+	path = g_file_get_path (file);
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+	ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+
+	g_debug ("%s:'%s' (%s) (delete monitor event or user request)",
+		 ignored ? "Ignored" : "Found ",
+		 path,
+		 is_directory ? "DIR" : "FILE");
+
+	if (!ignored) {
+		queue = g_hash_table_lookup (processor->private->items_deleted_queues, module_name);
+		g_queue_push_tail (queue, g_object_ref (file));
+		
+		item_queue_handlers_set_up (processor);
+	}
+
+	g_free (path);
+}
+
+static void
+processor_files_move (TrackerProcessor *processor,
+		      const gchar      *module_name,
+		      GFile	       *file,
+		      GFile	       *other_file,
+		      gboolean		is_directory)
+{
+	TrackerCrawler *crawler;
+	GQueue	       *queue;
+	gchar	       *path;
+	gchar	       *other_path;
+	gboolean	path_ignored;
+	gboolean	other_path_ignored;
+
+	path = g_file_get_path (file);
+	other_path = g_file_get_path (other_file);
+	crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+	path_ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+	other_path_ignored = tracker_crawler_is_path_ignored (crawler, other_path, is_directory);
+
+	g_debug ("%s:'%s'->'%s':%s (%s) (move monitor event or user request)",
+		 path_ignored ? "Ignored" : "Found ",
+		 path,
+		 other_path,
+		 other_path_ignored ? "Ignored" : " Found",
+		 is_directory ? "DIR" : "FILE");
+
+	if (path_ignored && other_path_ignored) {
+		/* Do nothing */
+	} else if (path_ignored) {
+		/* Check new file */
+		if (!is_directory) {
+			queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+			g_queue_push_tail (queue, g_object_ref (other_file));
+
+			item_queue_handlers_set_up (processor);
+		}
+
+		/* If this is a directory we need to crawl it */
+		tracker_crawler_add_unexpected_path (crawler, other_path);
+	} else if (other_path_ignored) {
+		/* Delete old file */
+		queue = g_hash_table_lookup (processor->private->items_deleted_queues, module_name);
+		g_queue_push_tail (queue, g_object_ref (file));
+
+		item_queue_handlers_set_up (processor);
+	} else {
+		/* Move old file to new file */
+		queue = g_hash_table_lookup (processor->private->items_moved_queues, module_name);
+		g_queue_push_tail (queue, g_object_ref (file));
+		g_queue_push_tail (queue, g_object_ref (other_file));
+
+		item_queue_handlers_set_up (processor);
+	}
+
+	g_free (other_path);
+	g_free (path);
+}
+
+static void
+monitor_item_created_cb (TrackerMonitor *monitor,
+			 const gchar	*module_name,
+			 GFile		*file,
+			 gboolean	 is_directory,
+			 gpointer	 user_data)
+{
+	processor_files_check (user_data, module_name, file, is_directory);
+}
+
+static void
+monitor_item_updated_cb (TrackerMonitor *monitor,
+			 const gchar	*module_name,
+			 GFile		*file,
+			 gboolean	 is_directory,
+			 gpointer	 user_data)
+{
+	processor_files_update (user_data, module_name, file, is_directory);
+}
+
+static void
+monitor_item_deleted_cb (TrackerMonitor *monitor,
+			 const gchar	*module_name,
+			 GFile		*file,
+			 gboolean	 is_directory,
+			 gpointer	 user_data)
+{
+	processor_files_delete (user_data, module_name, file, is_directory);
+}
+
+static void
+monitor_item_moved_cb (TrackerMonitor *monitor,
+		       const gchar    *module_name,
+		       GFile	      *file,
+		       GFile	      *other_file,
+		       gboolean        is_directory,
+		       gboolean        is_source_monitored,
+		       gpointer        user_data)
+{
+	if (!is_source_monitored) {
+		TrackerProcessor *processor;
+		TrackerCrawler   *crawler;
+		gchar            *path;
+
+		processor = user_data;
+
+		/* If the source is not monitored, we need to crawl it. */
+		path = g_file_get_path (other_file);
+		crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+		tracker_crawler_add_unexpected_path (crawler, path);
+		g_free (path);
+	} else {
+		processor_files_move (user_data, module_name, file, other_file, is_directory);
+	}
+}
+
+static void
+crawler_processing_file_cb (TrackerCrawler *crawler,
+			    const gchar    *module_name,
+			    GFile	   *file,
+			    gpointer	    user_data)
+{
+	TrackerProcessor *processor;
+	GQueue		 *queue;
+
+	processor = user_data;
+
+	/* Add files in queue to our queues to send to the indexer */
+	queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+	g_queue_push_tail (queue, g_object_ref (file));
+
+	item_queue_handlers_set_up (processor);
+}
+
+static void
+crawler_processing_directory_cb (TrackerCrawler *crawler,
+				 const gchar	*module_name,
+				 GFile		*file,
+				 gpointer	 user_data)
+{
+	TrackerProcessor *processor;
+	GQueue		 *queue;
+	gboolean	  add_monitor;
+
+	processor = user_data;
+
+	/* FIXME: Get ignored directories from .cfg? We know that
+	 * normally these would have monitors because these
+	 * directories are those crawled based on the module config.
+	 */
+	add_monitor = TRUE;
+
+	/* Should we add? */
+	if (add_monitor) {
+		tracker_monitor_add (processor->private->monitor, module_name, file);
+	}
+
+	/* Add files in queue to our queues to send to the indexer */
+	queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+	g_queue_push_tail (queue, g_object_ref (file));
+
+	item_queue_handlers_set_up (processor);
+}
+
+static void
+crawler_finished_cb (TrackerCrawler *crawler,
+		     const gchar    *module_name,
+		     guint	     directories_found,
+		     guint	     directories_ignored,
+		     guint	     files_found,
+		     guint	     files_ignored,
+		     gpointer	     user_data)
+{
+	TrackerProcessor *processor;
+
+	processor = user_data;
+
+	/* Update stats */
+	processor->private->directories_found += directories_found;
+	processor->private->directories_ignored += directories_ignored;
+	processor->private->files_found += files_found;
+	processor->private->files_ignored += files_ignored;
+
+	processor->private->total_directories_found += directories_found;
+	processor->private->total_directories_ignored += directories_ignored;
+	processor->private->total_files_found += files_found;
+	processor->private->total_files_ignored += files_ignored;
+
+	/* Proceed to next thing to process */
+	process_continue (processor);
+}
+
+#ifdef HAVE_HAL
+
+static gchar *
+normalize_mount_point (const gchar *mount_point)
+{
+	if (g_str_has_suffix (mount_point, G_DIR_SEPARATOR_S)) {
+		return g_strdup (mount_point);
+	} else {
+		return g_strconcat (mount_point, G_DIR_SEPARATOR_S, NULL);
+	}
+}
+
+static void
+mount_point_added_cb (TrackerStorage *hal,
+		      const gchar    *udi,
+		      const gchar     *mount_point,
+		      gpointer         user_data)
+{
+	TrackerProcessor        *processor;
+	TrackerProcessorPrivate *priv;
+	TrackerStatus	         status;
+	gchar                   *mp;
+
+	processor = user_data;
+
+	priv = processor->private;
+
+	status = tracker_status_get ();
+	mp = normalize_mount_point (mount_point);
+
+	/* Add removable device to list of known devices to iterate */
+	if (!g_list_find_custom (priv->devices, mp, (GCompareFunc) g_strcmp0)) {
+		priv->devices = g_list_append (priv->devices, mp);
+	}
+
+	/* Reset finished devices flag */
+	processor->private->finished_devices = FALSE;
+
+	/* If we are idle/not doing anything, start up the processor
+	 * again so we handle the new location.
+	 */
+	if (status == TRACKER_STATUS_INDEXING ||
+	    status == TRACKER_STATUS_OPTIMIZING ||
+	    status == TRACKER_STATUS_IDLE) {
+		/* If we are indexing then we must have already
+		 * crawled all locations so we need to start up the
+		 * processor again for the removable media once more.
+		 */
+		process_next (processor);
+	}
+}
+
+static void
+mount_point_removed_cb (TrackerStorage *hal,
+			const gchar    *udi,
+			const gchar    *mount_point,
+			gpointer        user_data)
+{
+	TrackerProcessor        *processor;
+	TrackerProcessorPrivate *priv;
+	GFile		        *file;
+	GList                   *l;
+	gchar                   *mp;
+
+	processor = user_data;
+
+	priv = processor->private;
+	mp = normalize_mount_point (mount_point);
+
+	/* Remove directory from list of iterated_removable_media, so
+	 * we don't traverse it.
+	 */
+	l = g_list_find_custom (priv->devices, mp, (GCompareFunc) g_strcmp0);
+
+	/* Make sure we don't remove the current device we are
+	 * processing, this is because we do this same clean up later
+	 * in process_device_next() 
+	 */
+	if (l && l != priv->current_device) {
+		g_free (l->data);
+		priv->devices = g_list_delete_link (priv->devices, l);
+	}
+
+	/* Remove the monitor, the volumes are updated somewhere else
+	 * in main. 
+	 */
+	file = g_file_new_for_path (mount_point);
+	tracker_monitor_remove_recursively (priv->monitor, file);
+	g_object_unref (file);
+
+	g_free (mp);
+}
+
+#endif /* HAVE_HAL */
+
+TrackerProcessor *
+tracker_processor_new (TrackerConfig  *config,
+		       TrackerStorage *storage)
+{
+	TrackerProcessor	*processor;
+	TrackerProcessorPrivate *priv;
+	TrackerCrawler		*crawler;
+	GList			*l;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+#ifdef HAVE_HAL
+	g_return_val_if_fail (TRACKER_IS_STORAGE (storage), NULL);
+#endif /* HAVE_HAL */
+
+	tracker_status_set_and_signal (TRACKER_STATUS_INITIALIZING);
+
+	processor = g_object_new (TRACKER_TYPE_PROCESSOR, NULL);
+	priv = processor->private;
+
+	/* Set up config */
+	priv->config = g_object_ref (config);
+
+#ifdef HAVE_HAL
+	/* Set up hal */
+	priv->hal = g_object_ref (storage);
+
+	priv->devices = tracker_storage_get_removable_device_roots (priv->hal);
+
+	g_signal_connect (priv->hal, "mount-point-added",
+		          G_CALLBACK (mount_point_added_cb),
+		          processor);
+	g_signal_connect (priv->hal, "mount-point-removed",
+		          G_CALLBACK (mount_point_removed_cb),
+		          processor);
+#endif /* HAVE_HAL */
+
+	/* Set up the crawlers now we have config and hal */
+	for (l = priv->modules; l; l = l->next) {
+		crawler = tracker_crawler_new (priv->config, l->data);
+
+		g_signal_connect (crawler, "processing-file",
+				  G_CALLBACK (crawler_processing_file_cb),
+				  processor);
+		g_signal_connect (crawler, "processing-directory",
+				  G_CALLBACK (crawler_processing_directory_cb),
+				  processor);
+		g_signal_connect (crawler, "finished",
+				  G_CALLBACK (crawler_finished_cb),
+				  processor);
+
+		g_hash_table_insert (priv->crawlers,
+				     g_strdup (l->data),
+				     crawler);
+	}
+
+	/* Set up the monitor */
+	priv->monitor = tracker_monitor_new (config);
+
+	g_message ("Disabling monitor events until we have crawled the file system");
+	tracker_monitor_set_enabled (priv->monitor, FALSE);
+
+	g_signal_connect (priv->monitor, "item-created",
+			  G_CALLBACK (monitor_item_created_cb),
+			  processor);
+	g_signal_connect (priv->monitor, "item-updated",
+			  G_CALLBACK (monitor_item_updated_cb),
+			  processor);
+	g_signal_connect (priv->monitor, "item-deleted",
+			  G_CALLBACK (monitor_item_deleted_cb),
+			  processor);
+	g_signal_connect (priv->monitor, "item-moved",
+			  G_CALLBACK (monitor_item_moved_cb),
+			  processor);
+
+#if 0
+	/* Set up the indexer and signalling to know when we are
+	 * finished.
+	 */
+
+	g_signal_connect (priv->indexer, "started",
+			  G_CALLBACK (indexer_started_cb),
+			  processor);
+	g_signal_connect (priv->indexer, "finished",
+			  G_CALLBACK (indexer_finished_cb),
+			  processor);
+#endif
+
+	return processor;
+}
+
+void
+tracker_processor_start (TrackerProcessor *processor)
+{
+	g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+
+	processor->private->been_started = TRUE;
+
+	processor->private->interrupted = FALSE;
+
+	processor->private->finished_modules = FALSE;
+	processor->private->finished_devices = FALSE;
+	processor->private->finished_sending = FALSE;
+	processor->private->finished_indexer = FALSE;
+
+	process_next (processor);
+}
+
+void 
+tracker_processor_stop (TrackerProcessor *processor)
+{
+	g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+
+	if (!processor->private->been_started) {
+		return;
+	}
+
+	processor->private->interrupted = TRUE;
+
+	process_modules_stop (processor);
+	process_devices_stop (processor);
+	
+	/* Queues? */
+
+	process_finish (processor);
+}
+
diff --git a/src/libtracker-miner/tracker-processor.h b/src/libtracker-miner/tracker-processor.h
new file mode 100644
index 0000000..e367b0c
--- /dev/null
+++ b/src/libtracker-miner/tracker-processor.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 __TRACKERD_PROCESSOR_H__
+#define __TRACKERD_PROCESSOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-storage.h>
+
+#include "tracker-config.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_PROCESSOR	       (tracker_processor_get_type())
+#define TRACKER_PROCESSOR(o)	       (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_PROCESSOR, TrackerProcessor))
+#define TRACKER_PROCESSOR_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),	 TRACKER_TYPE_PROCESSOR, TrackerProcessorClass))
+#define TRACKER_IS_PROCESSOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_PROCESSOR))
+#define TRACKER_IS_PROCESSOR_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),	 TRACKER_TYPE_PROCESSOR))
+#define TRACKER_PROCESSOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_PROCESSOR, TrackerProcessorClass))
+
+typedef struct TrackerProcessor        TrackerProcessor;
+typedef struct TrackerProcessorClass   TrackerProcessorClass;
+typedef struct TrackerProcessorPrivate TrackerProcessorPrivate;
+
+struct TrackerProcessor {
+	GObject			 parent;
+	TrackerProcessorPrivate *private;
+};
+
+struct TrackerProcessorClass {
+	GObjectClass		 parent;
+
+	void (*finished) (TrackerProcessor *processor);
+};
+
+GType		  tracker_processor_get_type		    (void) G_GNUC_CONST;
+
+TrackerProcessor *tracker_processor_new			    (TrackerConfig    *config,
+							     TrackerStorage   *storage);
+void		  tracker_processor_start		    (TrackerProcessor *processor);
+void		  tracker_processor_stop		    (TrackerProcessor *processor);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_PROCESSOR_H__ */
diff --git a/src/libtracker-miner/tracker-status.c b/src/libtracker-miner/tracker-status.c
new file mode 100644
index 0000000..191b39d
--- /dev/null
+++ b/src/libtracker-miner/tracker-status.c
@@ -0,0 +1,1097 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "config.h"
+
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "tracker-status.h"
+
+#define DISK_SPACE_CHECK_FREQUENCY 10
+
+#define THROTTLE_DEFAULT	    0
+#define THROTTLE_DEFAULT_ON_BATTERY 5
+
+#define PROCESS_PRIORITY_FOR_BUSY   19
+
+typedef struct {
+	TrackerStatus  status;
+	TrackerStatus  status_before_paused;
+	gpointer       type_class;
+
+	guint          disk_space_check_id;
+
+	gint           cpu_priority;
+
+	TrackerConfig *config;
+	TrackerPower  *hal;
+
+
+	gboolean       is_readonly;
+	gboolean       is_ready;
+	gboolean       is_running;
+	gboolean       is_first_time_index;
+	gboolean       is_initial_check;
+	gboolean       is_paused_manually;
+	gboolean       is_paused_for_batt;
+	gboolean       is_paused_for_io;
+	gboolean       is_paused_for_space;
+	gboolean       is_paused_for_dbus;
+	gboolean       is_paused_for_unknown;
+	gboolean       in_merge;
+} TrackerStatusPrivate;
+
+static void low_disk_space_limit_cb (GObject     *gobject,
+				     GParamSpec  *arg1,
+				     gpointer     user_data);
+static void on_battery_cb     (GObject     *gobject,
+				   GParamSpec  *arg1,
+				   gpointer     user_data);
+static void on_low_battery_cb (GObject     *object,
+				   GParamSpec  *pspec,
+				   gpointer     user_data);
+static void disk_space_check_stop   (void);
+
+static GStaticPrivate private_key = G_STATIC_PRIVATE_INIT;
+
+static void
+private_free (gpointer data)
+{
+	TrackerStatusPrivate *private;
+
+	private = data;
+
+	if (private->disk_space_check_id) {
+		g_source_remove (private->disk_space_check_id);
+	}
+
+	g_signal_handlers_disconnect_by_func (private->config,
+					      low_disk_space_limit_cb,
+					      NULL);
+
+	g_object_unref (private->config);
+
+#ifdef HAVE_HAL
+	g_signal_handlers_disconnect_by_func (private->hal,
+					      on_battery_cb,
+					      NULL);
+	g_signal_handlers_disconnect_by_func (private->hal,
+					      on_low_battery_cb,
+					      NULL);
+
+	g_object_unref (private->hal);
+#endif
+
+	if (private->type_class) {
+		g_type_class_unref (private->type_class);
+	}
+
+	g_free (private);
+}
+
+/*
+ * Handle Indexer pausing/continuation
+ */
+
+static void
+indexer_recheck (gboolean should_block,
+		 gboolean should_signal_small_changes)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	/* Are we paused in any way? */
+	if (private->is_paused_manually ||
+	    private->is_paused_for_batt || 
+	    private->is_paused_for_io ||
+	    private->is_paused_for_space ||
+	    private->is_paused_for_dbus ||
+	    private->is_paused_for_unknown) {
+		/* We are paused, but our status is NOT paused? */
+		if (private->status != TRACKER_STATUS_PAUSED) {
+			tracker_status_set_and_signal (TRACKER_STATUS_PAUSED);
+			
+			return;
+		}
+	} else {
+		/* We are not paused, but our status is paused */
+		if (private->status == TRACKER_STATUS_PAUSED) {
+			tracker_status_set_and_signal (private->status_before_paused);
+
+			return;
+		}
+	}
+
+	/* Simply signal because in this case, state hasn't changed
+	 * but one of the reasons for being paused has changed. 
+	 */
+	if (should_signal_small_changes) {
+		tracker_status_signal ();
+	}
+}
+
+static gboolean
+disk_space_check (void)
+{
+	TrackerStatusPrivate *private;
+	struct statvfs        st;
+	gint                  limit;
+	gchar                *data_dir;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	limit = tracker_config_get_low_disk_space_limit (private->config);
+
+	if (limit < 1) {
+		return FALSE;
+	}
+
+	data_dir = g_build_filename (g_get_user_cache_dir (),
+				     "tracker",
+				     NULL);
+
+	if (statvfs (data_dir, &st) == -1) {
+		g_warning ("Could not statvfs() '%s'", data_dir);
+		g_free (data_dir);
+		return FALSE;
+	}
+
+	g_free (data_dir);
+
+	if (((long long) st.f_bavail * 100 / st.f_blocks) <= limit) {
+		g_message ("WARNING: Available disk space is below configured "
+			   "threshold for acceptable working (%d%%)",
+			   limit);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+disk_space_check_cb (gpointer user_data)
+{
+	if (disk_space_check ()) {
+		tracker_status_set_is_paused_for_space (TRUE);
+	} else {
+		tracker_status_set_is_paused_for_space (FALSE);
+	}
+
+	return TRUE;
+}
+
+static void
+disk_space_check_start (void)
+{
+	TrackerStatusPrivate *private;
+	gint limit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	if (private->disk_space_check_id != 0) {
+		return;
+	}
+
+	limit = tracker_config_get_low_disk_space_limit (private->config);
+
+	if (limit != -1) {
+		g_message ("Starting disk space check for every %d seconds",
+			   DISK_SPACE_CHECK_FREQUENCY);
+		private->disk_space_check_id = 
+			g_timeout_add_seconds (DISK_SPACE_CHECK_FREQUENCY,
+					       disk_space_check_cb,
+					       NULL);
+
+		/* Call the function now too to make sure we have an
+		 * initial value too!
+		 */
+		disk_space_check_cb (NULL);
+	} else {
+		g_message ("Not setting disk space, configuration is set to -1 (disabled)");
+	}
+}
+
+static void
+disk_space_check_stop (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	if (private->disk_space_check_id) {
+		g_message ("Stopping disk space check");
+		g_source_remove (private->disk_space_check_id);
+		private->disk_space_check_id = 0;
+	}
+}
+
+static void
+low_disk_space_limit_cb (GObject    *gobject,
+			 GParamSpec *arg1,
+			 gpointer    user_data)
+{
+	disk_space_check_cb (NULL);
+}
+
+#ifdef HAVE_HAL
+
+static void
+set_up_throttle (gboolean debugging)
+{
+	TrackerStatusPrivate *private;
+	gint                  throttle;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	/* If on a laptop battery and the throttling is default (i.e.
+	 * 0), then set the throttle to be higher so we don't kill
+	 * the laptop battery.
+	 */
+	throttle = tracker_config_get_throttle (private->config);
+
+	if (tracker_power_get_on_battery (private->hal)) {
+		if (debugging) {
+			g_message ("We are running on battery");
+		}
+
+		if (throttle == THROTTLE_DEFAULT) {
+			tracker_config_set_throttle (private->config,
+						     THROTTLE_DEFAULT_ON_BATTERY);
+
+			if (debugging) {
+				g_message ("Setting throttle from %d to %d",
+					   throttle,
+					   THROTTLE_DEFAULT_ON_BATTERY);
+			}
+		} else {
+			if (debugging) {
+				g_message ("Not setting throttle, it is currently set to %d",
+					   throttle);
+			}
+		}
+	} else {
+		if (debugging) {
+			g_message ("We are not running on battery");
+		}
+
+		if (throttle == THROTTLE_DEFAULT_ON_BATTERY) {
+			tracker_config_set_throttle (private->config,
+						     THROTTLE_DEFAULT);
+
+			if (debugging) {
+				g_message ("Setting throttle from %d to %d",
+					   throttle,
+					   THROTTLE_DEFAULT);
+			}
+		} else {
+			if (debugging) {
+				g_message ("Not setting throttle, it is currently set to %d",
+					   throttle);
+			}
+		}
+	}
+}
+
+static void
+on_battery_cb (GObject *gobject,
+		   GParamSpec *arg1,
+		   gpointer user_data)
+{
+	set_up_throttle (TRUE);
+}
+
+static void
+on_low_battery_cb (GObject    *object,
+		       GParamSpec *pspec,
+		       gpointer    user_data)
+{
+	TrackerStatusPrivate *private;
+	gboolean              on_low_battery;
+	gboolean              on_battery;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	on_low_battery = tracker_power_get_on_low_battery (private->hal);
+	on_battery = tracker_power_get_on_battery (private->hal);
+
+	/* FIXME: This could be a configuration option */
+	if (on_battery) {
+		if (on_low_battery) {
+			/* Running on low batteries, stop indexing for now */
+			tracker_status_set_is_paused_for_batt (TRUE);
+		} else {
+			tracker_status_set_is_paused_for_batt (FALSE);
+		}
+	} else {
+		tracker_status_set_is_paused_for_batt (FALSE);
+	}
+
+	set_up_throttle (FALSE);
+}
+
+#endif /* HAVE_HAL */
+
+gboolean
+tracker_status_init (TrackerConfig *config,
+		     TrackerPower  *hal)
+{
+	GType		      type;
+	TrackerStatusPrivate *private;
+
+	g_return_val_if_fail (TRACKER_IS_CONFIG (config), FALSE);
+
+	private = g_static_private_get (&private_key);
+	if (private) {
+		g_warning ("Already initialized (%s)",
+			   __FUNCTION__);
+		return FALSE;
+	}
+
+	private = g_new0 (TrackerStatusPrivate, 1);
+
+	private->status = TRACKER_STATUS_INITIALIZING;
+	private->status_before_paused = private->status;
+
+	/* Since we don't reference this enum anywhere, we do
+	 * it here to make sure it exists when we call
+	 * g_type_class_peek(). This wouldn't be necessary if
+	 * it was a param in a GObject for example.
+	 *
+	 * This does mean that we are leaking by 1 reference
+	 * here and should clean it up, but it doesn't grow so
+	 * this is acceptable.
+	 */
+	type = tracker_status_get_type ();
+	private->type_class = g_type_class_ref (type);
+
+	private->config = g_object_ref (config);
+
+	g_signal_connect (private->config, "notify::low-disk-space-limit",
+			  G_CALLBACK (low_disk_space_limit_cb),
+			  NULL);
+
+
+#ifdef HAVE_HAL 
+	private->hal = g_object_ref (hal);
+
+	g_message ("Setting battery percentage checking");
+	g_signal_connect (private->hal, "notify::on-low-battery",
+			  G_CALLBACK (on_low_battery_cb),
+			  NULL);
+	g_signal_connect (private->hal, "notify::on-battery",
+			  G_CALLBACK (on_battery_cb),
+			  NULL);
+#endif
+
+	private->is_readonly = FALSE;
+	private->is_ready = FALSE;
+	private->is_running = FALSE;
+	private->is_first_time_index = FALSE;
+	private->is_initial_check = FALSE;
+	private->is_paused_manually = FALSE;
+	private->is_paused_for_batt = FALSE;
+	private->is_paused_for_io = FALSE;
+	private->is_paused_for_space = FALSE;
+	private->is_paused_for_dbus = FALSE;
+	private->is_paused_for_unknown = FALSE;
+	private->in_merge = FALSE;
+
+	/* Get process priority on start up:
+	 * We use nice() when crawling so we don't steal all
+	 * the processor time, for all other states, we return the
+	 * nice() value to what it was.
+	 *
+	 * NOTE: We set errno first because -1 is a valid priority and
+	 * we need to check it isn't an error.
+	 */
+	errno = 0;
+	private->cpu_priority = getpriority (PRIO_PROCESS, 0);
+
+	if ((private->cpu_priority < 0) && errno) {
+		const gchar *str = g_strerror (errno);
+
+		g_message ("Couldn't get nice value, %s", 
+			   str ? str : "no error given");
+
+		/* Default to 0 */
+		private->cpu_priority = 0;
+	}
+
+	g_message ("Current process priority is set to %d, this will be used for all non-crawling states",
+		   private->cpu_priority);
+
+	g_static_private_set (&private_key,
+			      private,
+			      private_free);
+
+	/* Do initial disk space check, we don't start the timeout
+	 * which checks the disk space every 10 seconds here because
+	 * we might not have enough to begin with. So we do one
+	 * initial check to set the state correctly. 
+	 */
+	disk_space_check_cb (NULL);
+
+	return TRUE;
+}
+
+void
+tracker_status_shutdown (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	if (!private) {
+		g_warning ("Not initialized (%s)",
+			   __FUNCTION__);
+		return;
+	}
+
+	g_static_private_free (&private_key);
+}
+
+gboolean
+tracker_status_is_initialized (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+
+	return (private != NULL);
+}
+
+GType
+tracker_status_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GEnumValue values[] = {
+			{ TRACKER_STATUS_INITIALIZING,
+			  "TRACKER_STATUS_INITIALIZING",
+			  "Initializing" },
+			{ TRACKER_STATUS_WATCHING,
+			  "TRACKER_STATUS_WATCHING",
+			  "Watching" },
+			{ TRACKER_STATUS_INDEXING,
+			  "TRACKER_STATUS_INDEXING",
+			  "Indexing" },
+			{ TRACKER_STATUS_PAUSED,
+			  "TRACKER_STATUS_PAUSED",
+			  "Paused" },
+			{ TRACKER_STATUS_PENDING,
+			  "TRACKER_STATUS_PENDING",
+			  "Pending" },
+			{ TRACKER_STATUS_OPTIMIZING,
+			  "TRACKER_STATUS_OPTIMIZING",
+			  "Optimizing" },
+			{ TRACKER_STATUS_IDLE,
+			  "TRACKER_STATUS_IDLE",
+			  "Idle" },
+			{ TRACKER_STATUS_SHUTDOWN,
+			  "TRACKER_STATUS_SHUTDOWN",
+			  "Shutdown" },
+			{ 0, NULL, NULL }
+		};
+
+		type = g_enum_register_static ("TrackerStatus", values);
+	}
+
+	return type;
+}
+
+const gchar *
+tracker_status_to_string (TrackerStatus status)
+{
+	GType	    type;
+	GEnumClass *enum_class;
+	GEnumValue *enum_value;
+
+	type = tracker_status_get_type ();
+	enum_class = G_ENUM_CLASS (g_type_class_peek (type));
+	enum_value = g_enum_get_value (enum_class, status);
+
+	if (!enum_value) {
+		enum_value = g_enum_get_value (enum_class, TRACKER_STATUS_IDLE);
+	}
+
+	return enum_value->value_nick;
+}
+
+TrackerStatus
+tracker_status_get (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, TRACKER_STATUS_INITIALIZING);
+
+	return private->status;
+}
+
+const gchar *
+tracker_status_get_as_string (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, tracker_status_to_string (TRACKER_STATUS_INITIALIZING));
+
+	return tracker_status_to_string (private->status);
+}
+
+static void
+status_set_priority (TrackerStatus old_status,
+		     TrackerStatus new_status,
+		     gint          special_priority,
+		     gint          default_priority)
+{
+	/* Note, we can't use -1 here, so we use a value
+	 * outside the nice values from -20->19 
+	 */
+	gint new_priority = 100;
+	
+	/* Handle priority changes */
+	if (new_status == TRACKER_STATUS_PENDING) {
+		new_priority = special_priority;
+	} else if (old_status == TRACKER_STATUS_PENDING) {
+		new_priority = default_priority;
+	}
+	
+	if (new_priority != 100) {
+		g_message ("Setting process priority to %d (%s)", 
+			   new_priority,
+			   new_priority == default_priority ? "default" : "special");
+		
+		if (nice (new_priority) == -1) {
+			const gchar *str = g_strerror (errno);
+			
+			g_message ("Couldn't set nice value to %d, %s",
+				   new_priority,
+				   str ? str : "no error given");
+		}
+	}
+}
+
+void
+tracker_status_set (TrackerStatus new_status)
+{
+	TrackerStatusPrivate *private;
+	gboolean should_be_paused;
+	gboolean invalid_new_state;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+
+	should_be_paused =
+		private->is_paused_manually ||
+		private->is_paused_for_batt ||
+		private->is_paused_for_io ||
+		private->is_paused_for_space ||
+		private->is_paused_for_dbus ||
+		private->is_paused_for_unknown;
+
+	invalid_new_state =
+		should_be_paused &&
+		new_status != TRACKER_STATUS_PAUSED;
+
+	g_message ("State change from '%s' --> '%s' %s",
+		   tracker_status_to_string (private->status),
+		   tracker_status_to_string (new_status),
+		   invalid_new_state ? "attempted with pause conditions, doing nothing" : "");
+
+	/* Don't set previous status to the same as we are now,
+	 * otherwise we could end up setting PAUSED and our old state
+	 * to return to is also PAUSED.
+	 *  
+	 * We really should start using a proper state machine here,
+	 * but I am avoiding it for now, -mr.
+	 */
+	if (private->status_before_paused != new_status && 
+	    private->status != TRACKER_STATUS_PAUSED) {
+		/* ALWAYS only set this after checking against the
+		 * previous value 
+		 */
+		private->status_before_paused = private->status;
+	}
+
+	/* State machine */
+	if (private->status != new_status) {
+		/* The reason we have this check, is that it is
+		 * possible that we are trying to set our state to
+		 * IDLE after finishing crawling but actually, we
+		 * should be in a PAUSED state due to another flag,
+		 * such as low disk space. So we force PAUSED state.
+		 * However, the interesting thing here is, we must
+		 * remember to set the OLD state so we go back to the
+		 * state as set by the caller. If we don't we end up
+		 * going back to PENDING/WATCHING instead of IDLE when
+		 * we come out of being PAUSED.
+		 *
+		 * FIXME: Should we ONLY cater for IDLE here? -mr
+		 */
+
+		if (invalid_new_state) {
+			g_message ("Attempt to set state to '%s' with pause conditions, doing nothing",
+				   tracker_status_to_string (new_status));
+
+			if (private->status != TRACKER_STATUS_PAUSED) {
+				tracker_status_set (TRACKER_STATUS_PAUSED);
+			}
+
+			/* Set last state so we know what to return
+			 * to when we come out of PAUSED
+			 */
+
+			private->status_before_paused = new_status;
+
+			return;
+		}
+
+		/* Make sure we are using the correct priority for
+		 * the new state.
+		 */
+		status_set_priority (new_status, 
+				     private->status,
+				     TRACKER_STATUS_PENDING,
+				     private->cpu_priority);
+		
+		/* Handle disk space moitor changes
+		 * 
+		 * So these are the conditions when we need checks
+		 * running:
+		 *
+		 * 1. Are we low on space?
+		 * 3. State is INDEXING or OPTIMIZING (use of disk)
+		 */
+		if (private->is_paused_for_space ||
+		    new_status == TRACKER_STATUS_INDEXING || 
+		    new_status == TRACKER_STATUS_OPTIMIZING) {
+			disk_space_check_start ();
+		} else {
+			disk_space_check_stop ();
+		}
+	}
+
+	private->status = new_status;
+}
+
+void
+tracker_status_signal (void)
+{
+#if 0
+	TrackerStatusPrivate *private;
+	GObject		     *object;
+	gboolean              pause_for_io;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	object = tracker_dbus_get_object (TRACKER_TYPE_DAEMON);
+
+	/* There are times on startup whe we haven't initialized the
+	 * DBus objects yet so signalling status is not practical.
+	 */
+	if (!object) {
+		return;
+	}
+
+	/* NOTE: We have to combine pausing for IO, pausing for
+	 * space and pausing for unknown reasons here because we can't
+	 * change API yet.
+	 */
+	pause_for_io = 
+		private->is_paused_for_io || 
+		private->is_paused_for_space || 
+		private->is_paused_for_dbus ||
+		private->is_paused_for_unknown;
+	
+	g_signal_emit_by_name (object,
+			       "index-state-change",
+			       tracker_status_to_string (private->status),
+			       private->is_first_time_index,
+			       private->in_merge,
+			       private->is_paused_manually,
+			       private->is_paused_for_batt,
+			       pause_for_io,
+			       !private->is_readonly);
+#endif
+}
+
+void
+tracker_status_set_and_signal (TrackerStatus new_status)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->status != new_status;
+
+	if (!emit) {
+		return;
+	}
+
+	tracker_status_set (new_status);
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_readonly (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_readonly;
+}
+
+void
+tracker_status_set_is_readonly (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->is_readonly != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->is_readonly = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_ready (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_ready;
+}
+
+void
+tracker_status_set_is_ready (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->is_ready != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->is_ready = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_running (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_running;
+}
+
+void
+tracker_status_set_is_running (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->is_running != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->is_running = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_first_time_index (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_first_time_index;
+}
+
+void
+tracker_status_set_is_first_time_index (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->is_first_time_index != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->is_first_time_index = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_initial_check (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_initial_check;
+}
+
+void
+tracker_status_set_is_initial_check (gboolean value)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	/* Set value */
+	private->is_initial_check = value;
+
+	/* We don't need to signal this */
+}
+
+gboolean
+tracker_status_get_in_merge (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->in_merge;
+}
+
+void
+tracker_status_set_in_merge (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	emit = private->in_merge != value;
+
+	if (!emit) {
+		return;
+	}
+
+	/* Set value */
+	private->in_merge = value;
+
+	/* Signal the status change */
+	tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_paused_manually (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_paused_manually;
+}
+
+void
+tracker_status_set_is_paused_manually (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean              emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	/* Set value */
+	emit = private->is_paused_manually != value;
+	private->is_paused_manually = value;
+
+	/* Set indexer state and our state to paused or not */ 
+	indexer_recheck (FALSE, emit);
+}
+
+gboolean
+tracker_status_get_is_paused_for_batt (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_paused_for_batt;
+}
+
+void
+tracker_status_set_is_paused_for_batt (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean              emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	/* Set value */
+	emit = private->is_paused_for_batt != value;
+	private->is_paused_for_batt = value;
+
+	/* Set indexer state and our state to paused or not */ 
+	indexer_recheck (FALSE, emit);
+}
+
+gboolean
+tracker_status_get_is_paused_for_io (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_paused_for_io;
+}
+
+void
+tracker_status_set_is_paused_for_io (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean	      emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	/* Set value */
+	emit = private->is_paused_for_io != value;
+	private->is_paused_for_io = value;
+
+	/* Set indexer state and our state to paused or not */ 
+	indexer_recheck (FALSE, emit);
+}
+
+gboolean
+tracker_status_get_is_paused_for_space (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_paused_for_space;
+}
+
+void
+tracker_status_set_is_paused_for_space (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean              emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	/* Set value */
+	emit = private->is_paused_for_space != value;
+	private->is_paused_for_space = value;
+
+	/* Set indexer state and our state to paused or not */ 
+	indexer_recheck (FALSE, emit);
+}
+
+gboolean
+tracker_status_get_is_paused_for_dbus (void)
+{
+	TrackerStatusPrivate *private;
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, FALSE);
+
+	return private->is_paused_for_space;
+}
+
+void
+tracker_status_set_is_paused_for_dbus (gboolean value)
+{
+	TrackerStatusPrivate *private;
+	gboolean              emit;
+
+	private = g_static_private_get (&private_key);
+	g_return_if_fail (private != NULL);
+
+	/* Set value */
+	emit = private->is_paused_for_dbus != value;
+	private->is_paused_for_dbus = value;
+
+	/* Set indexer state and our state to paused or not */ 
+	indexer_recheck (TRUE, emit);
+}
diff --git a/src/libtracker-miner/tracker-status.h b/src/libtracker-miner/tracker-status.h
new file mode 100644
index 0000000..d73e13b
--- /dev/null
+++ b/src/libtracker-miner/tracker-status.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 __TRACKERD_STATUS_H__
+#define __TRACKERD_STATUS_H__
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-power.h>
+
+#include "tracker-config.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_STATUS (tracker_status_get_type ())
+
+typedef enum {
+	TRACKER_STATUS_INITIALIZING,
+	TRACKER_STATUS_WATCHING,
+	TRACKER_STATUS_INDEXING,
+	TRACKER_STATUS_PAUSED,
+	TRACKER_STATUS_PENDING,
+	TRACKER_STATUS_OPTIMIZING,
+	TRACKER_STATUS_IDLE,
+	TRACKER_STATUS_SHUTDOWN
+} TrackerStatus;
+
+
+gboolean      tracker_status_init		     (TrackerConfig *config,
+						      TrackerPower  *hal);
+void	      tracker_status_shutdown		     (void);
+
+gboolean      tracker_status_is_initialized          (void);
+
+GType	      tracker_status_get_type		     (void) G_GNUC_CONST;
+const gchar * tracker_status_to_string		     (TrackerStatus  status);
+TrackerStatus tracker_status_get		     (void);
+const gchar * tracker_status_get_as_string	     (void);
+void	      tracker_status_set		     (TrackerStatus  new_status);
+void	      tracker_status_set_and_signal	     (TrackerStatus  new_status);
+void	      tracker_status_signal		     (void);
+
+gboolean      tracker_status_get_is_readonly	     (void);
+void	      tracker_status_set_is_readonly	     (gboolean	     value);
+
+gboolean      tracker_status_get_is_ready	     (void);
+void	      tracker_status_set_is_ready	     (gboolean	     value);
+
+gboolean      tracker_status_get_is_running	     (void);
+void	      tracker_status_set_is_running	     (gboolean	     value);
+
+void	      tracker_status_set_is_first_time_index (gboolean	     value);
+gboolean      tracker_status_get_is_first_time_index (void);
+
+void	      tracker_status_set_is_initial_check    (gboolean	     value);
+gboolean      tracker_status_get_is_initial_check    (void);
+
+gboolean      tracker_status_get_in_merge	     (void);
+void	      tracker_status_set_in_merge	     (gboolean	     value);
+
+gboolean      tracker_status_get_is_paused_manually  (void);
+void	      tracker_status_set_is_paused_manually  (gboolean	     value);
+
+gboolean      tracker_status_get_is_paused_for_batt  (void);
+void	      tracker_status_set_is_paused_for_batt  (gboolean	     value);
+
+gboolean      tracker_status_get_is_paused_for_io    (void);
+void	      tracker_status_set_is_paused_for_io    (gboolean	     value);
+
+gboolean      tracker_status_get_is_paused_for_space (void);
+void	      tracker_status_set_is_paused_for_space (gboolean	     value);
+
+gboolean      tracker_status_get_is_paused_for_dbus  (void);
+void	      tracker_status_set_is_paused_for_dbus  (gboolean	     value);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_STATUS_H__ */
diff --git a/src/libtracker-miner/tracker-utils.c b/src/libtracker-miner/tracker-utils.c
new file mode 100644
index 0000000..69b3fd5
--- /dev/null
+++ b/src/libtracker-miner/tracker-utils.c
@@ -0,0 +1,43 @@
+/* -*- 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 General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "config.h"
+
+#include "tracker-utils.h"
+
+void
+tracker_throttle (TrackerConfig *config,
+		  gint		 multiplier)
+{
+	gint throttle;
+
+	g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+	/* Get the throttle, add 5 (minimum value) so we don't do
+	 * nothing and then multiply it by the factor given
+	 */
+	throttle  = tracker_config_get_throttle (config);
+	/* throttle += 5; */
+	throttle *= multiplier;
+
+	if (throttle > 0) {
+		g_usleep (throttle);
+	}
+}
diff --git a/src/libtracker-miner/tracker-utils.h b/src/libtracker-miner/tracker-utils.h
new file mode 100644
index 0000000..f5b643b
--- /dev/null
+++ b/src/libtracker-miner/tracker-utils.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 __TRACKERD_UTILS_H__
+#define __TRACKERD_UTILS_H__
+
+#include <glib.h>
+
+#include "tracker-config.h"
+
+void tracker_throttle (TrackerConfig *config,
+		       gint           multiplier);
+
+#endif /* __TRACKERD_UTILS_H__ */



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