tracker-0.16.2/src/libtracker-miner/tracker-file-notifier.c

No issues found

   1 /*
   2  * Copyright (C) 2011, Nokia <ivan.frade@nokia.com>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the
  16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17  * Boston, MA  02110-1301, USA.
  18  *
  19  * Author: Carlos Garnacho  <carlos@lanedo.com>
  20  */
  21 
  22 #include <libtracker-common/tracker-log.h>
  23 #include <libtracker-common/tracker-date-time.h>
  24 #include <libtracker-sparql/tracker-sparql.h>
  25 
  26 #include "tracker-miner-common.h"
  27 #include "tracker-file-notifier.h"
  28 #include "tracker-file-system.h"
  29 #include "tracker-crawler.h"
  30 #include "tracker-monitor.h"
  31 #include "tracker-marshal.h"
  32 
  33 static GQuark quark_property_crawled = 0;
  34 static GQuark quark_property_queried = 0;
  35 static GQuark quark_property_iri = 0;
  36 static GQuark quark_property_store_mtime = 0;
  37 static GQuark quark_property_filesystem_mtime = 0;
  38 
  39 enum {
  40 	PROP_0,
  41 	PROP_INDEXING_TREE
  42 };
  43 
  44 enum {
  45 	FILE_CREATED,
  46 	FILE_UPDATED,
  47 	FILE_DELETED,
  48 	FILE_MOVED,
  49 	DIRECTORY_STARTED,
  50 	DIRECTORY_FINISHED,
  51 	FINISHED,
  52 	LAST_SIGNAL
  53 };
  54 
  55 static guint signals[LAST_SIGNAL] = { 0 };
  56 
  57 typedef struct {
  58 	TrackerIndexingTree *indexing_tree;
  59 	TrackerFileSystem *file_system;
  60 
  61 	TrackerSparqlConnection *connection;
  62 	GCancellable *cancellable;
  63 
  64 	TrackerCrawler *crawler;
  65 	TrackerMonitor *monitor;
  66 
  67 	GTimer *timer;
  68 
  69 	/* List of pending directory
  70 	 * trees to get data from
  71 	 */
  72 	GList *pending_index_roots;
  73 
  74 	guint stopped : 1;
  75 } TrackerFileNotifierPrivate;
  76 
  77 typedef struct {
  78 	TrackerFileNotifier *notifier;
  79 	GNode *cur_parent_node;
  80 
  81 	/* Canonical copy from priv->file_system */
  82 	GFile *cur_parent;
  83 } DirectoryCrawledData;
  84 
  85 static gboolean crawl_directories_start (TrackerFileNotifier *notifier);
  86 
  87 
  88 G_DEFINE_TYPE (TrackerFileNotifier, tracker_file_notifier, G_TYPE_OBJECT)
  89 
  90 static void
  91 tracker_file_notifier_set_property (GObject      *object,
  92                                     guint         prop_id,
  93                                     const GValue *value,
  94                                     GParamSpec   *pspec)
  95 {
  96 	TrackerFileNotifierPrivate *priv;
  97 
  98 	priv = TRACKER_FILE_NOTIFIER (object)->priv;
  99 
 100 	switch (prop_id) {
 101 	case PROP_INDEXING_TREE:
 102 		priv->indexing_tree = g_value_dup_object (value);
 103 		tracker_monitor_set_indexing_tree (priv->monitor,
 104 		                                   priv->indexing_tree);
 105 		break;
 106 	default:
 107 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 108 		break;
 109 	}
 110 }
 111 
 112 static void
 113 tracker_file_notifier_get_property (GObject    *object,
 114                                     guint       prop_id,
 115                                     GValue     *value,
 116                                     GParamSpec *pspec)
 117 {
 118 	TrackerFileNotifierPrivate *priv;
 119 
 120 	priv = TRACKER_FILE_NOTIFIER (object)->priv;
 121 
 122 	switch (prop_id) {
 123 	case PROP_INDEXING_TREE:
 124 		g_value_set_object (value, priv->indexing_tree);
 125 		break;
 126 	default:
 127 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 128 		break;
 129 	}
 130 }
 131 
 132 /* Crawler signal handlers */
 133 static gboolean
 134 crawler_check_file_cb (TrackerCrawler *crawler,
 135                        GFile          *file,
 136                        gpointer        user_data)
 137 {
 138 	TrackerFileNotifierPrivate *priv;
 139 
 140 	priv = TRACKER_FILE_NOTIFIER (user_data)->priv;
 141 
 142 	return tracker_indexing_tree_file_is_indexable (priv->indexing_tree,
 143 	                                                file,
 144 	                                                G_FILE_TYPE_REGULAR);
 145 }
 146 
 147 static gboolean
 148 crawler_check_directory_cb (TrackerCrawler *crawler,
 149                             GFile          *directory,
 150                             gpointer        user_data)
 151 {
 152 	TrackerFileNotifierPrivate *priv;
 153 	GFile *root, *canonical;
 154 
 155 	priv = TRACKER_FILE_NOTIFIER (user_data)->priv;
 156 
 157 	canonical = tracker_file_system_peek_file (priv->file_system, directory);
 158 	root = tracker_indexing_tree_get_root (priv->indexing_tree, directory, NULL);
 159 
 160 	/* If it's a config root itself, other than the one
 161 	 * currently processed, bypass it, it will be processed
 162 	 * when the time arrives.
 163 	 */
 164 	if (canonical &&
 165 	    root == canonical &&
 166 	    root != priv->pending_index_roots->data) {
 167 		return FALSE;
 168 	}
 169 
 170 	return tracker_indexing_tree_file_is_indexable (priv->indexing_tree,
 171 	                                                directory,
 172 	                                                G_FILE_TYPE_DIRECTORY);
 173 }
 174 
 175 static gboolean
 176 crawler_check_directory_contents_cb (TrackerCrawler *crawler,
 177                                      GFile          *parent,
 178                                      GList          *children,
 179                                      gpointer        user_data)
 180 {
 181 	TrackerFileNotifierPrivate *priv;
 182 	gboolean process;
 183 
 184 	priv = TRACKER_FILE_NOTIFIER (user_data)->priv;
 185 	process = tracker_indexing_tree_parent_is_indexable (priv->indexing_tree,
 186 	                                                     parent, children);
 187 	if (process) {
 188 		TrackerDirectoryFlags parent_flags;
 189 		GFile *canonical;
 190 		gboolean add_monitor;
 191 
 192 		canonical = tracker_file_system_get_file (priv->file_system,
 193 		                                          parent,
 194 		                                          G_FILE_TYPE_DIRECTORY,
 195 		                                          NULL);
 196 		tracker_indexing_tree_get_root (priv->indexing_tree,
 197 		                                canonical, &parent_flags);
 198 
 199 		add_monitor = (parent_flags & TRACKER_DIRECTORY_FLAG_MONITOR) != 0;
 200 
 201 		if (add_monitor) {
 202 			tracker_monitor_add (priv->monitor, canonical);
 203 		} else {
 204 			tracker_monitor_remove (priv->monitor, canonical);
 205 		}
 206 	}
 207 
 208 	return process;
 209 }
 210 
 211 static gboolean
 212 file_notifier_traverse_tree_foreach (GFile    *file,
 213                                      gpointer  user_data)
 214 {
 215 	TrackerFileNotifier *notifier;
 216 	TrackerFileNotifierPrivate *priv;
 217 	guint64 *store_mtime, *disk_mtime;
 218 
 219 	notifier = user_data;
 220 	priv = notifier->priv;
 221 
 222 	store_mtime = tracker_file_system_get_property (priv->file_system, file,
 223 	                                                quark_property_store_mtime);
 224 	disk_mtime = tracker_file_system_get_property (priv->file_system, file,
 225 	                                               quark_property_filesystem_mtime);
 226 
 227 	if (store_mtime && !disk_mtime) {
 228 		/* In store but not in disk, delete */
 229 		g_signal_emit (notifier, signals[FILE_DELETED], 0, file);
 230 
 231 		return TRUE;
 232 	} else if (disk_mtime && !store_mtime) {
 233 		/* In disk but not in store, create */
 234 		g_signal_emit (notifier, signals[FILE_CREATED], 0, file);
 235 	} else if (store_mtime && disk_mtime &&
 236 	           abs (*disk_mtime - *store_mtime) > 2) {
 237 		/* Mtime changed, update */
 238 		g_signal_emit (notifier, signals[FILE_UPDATED], 0, file, FALSE);
 239 	} else if (!store_mtime && !disk_mtime) {
 240 		/* what are we doing with such file? should happen rarely,
 241 		 * only with files that we've queried, but we decided not
 242 		 * to crawl (i.e. embedded root directories, that would
 243 		 * be processed when that root is being crawled).
 244 		 */
 245 		if (!tracker_indexing_tree_file_is_root (priv->indexing_tree, file)) {
 246 			gchar *uri;
 247 
 248 			uri = g_file_get_uri (file);
 249 			g_critical ("File '%s' has no disk nor store mtime",
 250 			            uri);
 251 			g_free (uri);
 252 		}
 253 	}
 254 
 255 	return FALSE;
 256 }
 257 
 258 static void
 259 file_notifier_traverse_tree (TrackerFileNotifier *notifier)
 260 {
 261 	TrackerFileNotifierPrivate *priv;
 262 	GFile *current_root, *config_root;
 263 	TrackerDirectoryFlags flags;
 264 
 265 	priv = notifier->priv;
 266 	current_root = priv->pending_index_roots->data;
 267 	config_root = tracker_indexing_tree_get_root (priv->indexing_tree,
 268 						      current_root, &flags);
 269 
 270 	/* Check mtime for 1) directories with the check_mtime flag
 271 	 * and 2) directories gotten from monitor events.
 272 	 */
 273 	if (config_root != current_root ||
 274 	    flags & TRACKER_DIRECTORY_FLAG_CHECK_MTIME) {
 275 		tracker_file_system_traverse (priv->file_system,
 276 		                              current_root,
 277 		                              G_LEVEL_ORDER,
 278 		                              file_notifier_traverse_tree_foreach,
 279 		                              notifier);
 280 	}
 281 
 282 	/* We dispose regular files here, only directories are cached once crawling
 283 	 * has completed.
 284 	 */
 285 	tracker_file_system_forget_files (priv->file_system,
 286 	                                  current_root,
 287 	                                  G_FILE_TYPE_REGULAR);
 288 
 289 	tracker_info ("  Notified files after %2.2f seconds",
 290 	              g_timer_elapsed (priv->timer, NULL));
 291 
 292 	/* We've finished crawling/querying on the first element
 293 	 * of the pending list, continue onto the next */
 294 	priv->pending_index_roots = g_list_delete_link (priv->pending_index_roots,
 295 	                                                priv->pending_index_roots);
 296 
 297 	if (priv->pending_index_roots) {
 298 		crawl_directories_start (notifier);
 299 	} else {
 300 		g_signal_emit (notifier, signals[FINISHED], 0);
 301 	}
 302 }
 303 
 304 static gboolean
 305 file_notifier_add_node_foreach (GNode    *node,
 306                                 gpointer  user_data)
 307 {
 308 	DirectoryCrawledData *data = user_data;
 309 	TrackerFileNotifierPrivate *priv;
 310 	GFileInfo *file_info;
 311 	GFile *canonical, *file;
 312 
 313 	priv = data->notifier->priv;
 314 	file = node->data;
 315 
 316 	if (node->parent &&
 317 	    node->parent != data->cur_parent_node) {
 318 		data->cur_parent_node = node->parent;
 319 		data->cur_parent = tracker_file_system_peek_file (priv->file_system,
 320 		                                                  node->parent->data);
 321 	} else {
 322 		data->cur_parent_node = NULL;
 323 		data->cur_parent = NULL;
 324 	}
 325 
 326 	file_info = tracker_crawler_get_file_info (priv->crawler, file);
 327 
 328 	if (file_info) {
 329 		GFileType file_type;
 330 		guint64 time, *time_ptr;
 331 
 332 		file_type = g_file_info_get_file_type (file_info);
 333 
 334 		/* Intern file in filesystem */
 335 		canonical = tracker_file_system_get_file (priv->file_system,
 336 							  file, file_type,
 337 							  data->cur_parent);
 338 
 339 		time = g_file_info_get_attribute_uint64 (file_info,
 340 		                                         G_FILE_ATTRIBUTE_TIME_MODIFIED);
 341 
 342 		time_ptr = g_new (guint64, 1);
 343 		*time_ptr = time;
 344 
 345 		tracker_file_system_set_property (priv->file_system, canonical,
 346 		                                  quark_property_filesystem_mtime,
 347 		                                  time_ptr);
 348 	}
 349 
 350 	return FALSE;
 351 }
 352 
 353 static void
 354 crawler_directory_crawled_cb (TrackerCrawler *crawler,
 355                               GFile          *directory,
 356                               GNode          *tree,
 357                               guint           directories_found,
 358                               guint           directories_ignored,
 359                               guint           files_found,
 360                               guint           files_ignored,
 361                               gpointer        user_data)
 362 {
 363 	TrackerFileNotifier *notifier;
 364 	DirectoryCrawledData data = { 0 };
 365 
 366 	notifier = data.notifier = user_data;
 367 	g_node_traverse (tree,
 368 	                 G_PRE_ORDER,
 369 	                 G_TRAVERSE_ALL,
 370 	                 -1,
 371 	                 file_notifier_add_node_foreach,
 372 	                 &data);
 373 
 374 	g_signal_emit (notifier, signals[DIRECTORY_FINISHED], 0,
 375 	               directory,
 376 	               directories_found, directories_ignored,
 377 	               files_found, files_ignored);
 378 
 379 	tracker_info ("  Found %d directories, ignored %d directories",
 380 	              directories_found,
 381 	              directories_ignored);
 382 	tracker_info ("  Found %d files, ignored %d files",
 383 	              files_found,
 384 	              files_ignored);
 385 }
 386 
 387 static void
 388 sparql_file_query_populate (TrackerFileNotifier *notifier,
 389                             TrackerSparqlCursor *cursor,
 390                             gboolean             check_root)
 391 {
 392 	TrackerFileNotifierPrivate *priv;
 393 
 394 	priv = notifier->priv;
 395 
 396 	while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
 397 		GFile *file, *canonical, *root;
 398 		const gchar *mtime, *iri;
 399 		guint64 *time_ptr;
 400 		GError *error = NULL;
 401 
 402 		file = g_file_new_for_uri (tracker_sparql_cursor_get_string (cursor, 0, NULL));
 403 
 404 		if (check_root) {
 405 			/* If it's a config root itself, other than the one
 406 			 * currently processed, bypass it, it will be processed
 407 			 * when the time arrives.
 408 			 */
 409 			canonical = tracker_file_system_peek_file (priv->file_system, file);
 410 			root = tracker_indexing_tree_get_root (priv->indexing_tree, file, NULL);
 411 
 412 			if (canonical &&
 413 			    root == file &&
 414 			    root != priv->pending_index_roots->data) {
 415 				g_object_unref (file);
 416 				continue;
 417 			}
 418 		}
 419 
 420 		canonical = tracker_file_system_get_file (priv->file_system,
 421 		                                          file,
 422 		                                          G_FILE_TYPE_UNKNOWN,
 423 		                                          NULL);
 424 
 425 		iri = tracker_sparql_cursor_get_string (cursor, 1, NULL);
 426 		tracker_file_system_set_property (priv->file_system, canonical,
 427 		                                  quark_property_iri,
 428 		                                  g_strdup (iri));
 429 
 430 		mtime = tracker_sparql_cursor_get_string (cursor, 2, NULL);
 431 		time_ptr = g_new (guint64, 1);
 432 		*time_ptr = (guint64) tracker_string_to_date (mtime, NULL, &error);
 433 
 434 		if (error) {
 435 			/* This should never happen. Assume that file was modified. */
 436 			g_critical ("Getting store mtime: %s", error->message);
 437 			g_clear_error (&error);
 438 			*time_ptr = 0;
 439 		}
 440 
 441 		tracker_file_system_set_property (priv->file_system, canonical,
 442 		                                  quark_property_store_mtime,
 443 		                                  time_ptr);
 444 		g_object_unref (file);
 445 	}
 446 }
 447 
 448 static void
 449 sparql_query_cb (GObject      *object,
 450                  GAsyncResult *result,
 451                  gpointer      user_data)
 452 {
 453 	TrackerFileNotifierPrivate *priv;
 454 	TrackerFileNotifier *notifier;
 455 	TrackerSparqlCursor *cursor;
 456 	GError *error = NULL;
 457 
 458 	notifier = user_data;
 459 	priv = notifier->priv;
 460 	cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object),
 461 	                                                 result, &error);
 462 
 463 	if (!cursor || error) {
 464 		g_warning ("Could not query directory elements: %s\n", error->message);
 465 		g_error_free (error);
 466 		return;
 467 	}
 468 
 469 	sparql_file_query_populate (notifier, cursor, TRUE);
 470 
 471 	/* Mark the directory root as queried */
 472 	tracker_file_system_set_property (priv->file_system,
 473 	                                  priv->pending_index_roots->data,
 474 	                                  quark_property_queried,
 475 	                                  GUINT_TO_POINTER (TRUE));
 476 
 477 	tracker_info ("  Queried files after %2.2f seconds",
 478 	              g_timer_elapsed (priv->timer, NULL));
 479 
 480 	/* If it's also been crawled, finish operation */
 481 	if (tracker_file_system_get_property (priv->file_system,
 482 	                                      priv->pending_index_roots->data,
 483 	                                      quark_property_crawled)) {
 484 		file_notifier_traverse_tree (notifier);
 485 	}
 486 
 487 	g_object_unref (cursor);
 488 }
 489 
 490 static void
 491 sparql_file_query_start (TrackerFileNotifier *notifier,
 492                          GFile               *file,
 493                          GFileType            file_type,
 494                          gboolean             recursive,
 495                          gboolean             sync)
 496 {
 497 	TrackerFileNotifierPrivate *priv;
 498 	gchar *uri, *sparql;
 499 
 500 	priv = notifier->priv;
 501 	uri = g_file_get_uri (file);
 502 
 503 	if (file_type == G_FILE_TYPE_DIRECTORY) {
 504 		if (recursive) {
 505 			sparql = g_strdup_printf ("select ?url ?u nfo:fileLastModified(?u) "
 506 			                          "where {"
 507 			                          "  ?u a nie:DataObject ; "
 508 			                          "     nie:url ?url . "
 509 			                          "  FILTER (?url = \"%s\" || "
 510 			                          "          fn:starts-with (?url, \"%s/\")) "
 511 			                          "}", uri, uri);
 512 		} else {
 513 			sparql = g_strdup_printf ("select ?url ?u nfo:fileLastModified(?u) "
 514 			                          "where { "
 515 			                          "  ?u a nie:DataObject ; "
 516 			                          "     nie:url ?url . "
 517 			                          "  OPTIONAL { ?u nfo:belongsToContainer ?p } . "
 518 			                          "  FILTER (?url = \"%s\" || "
 519 			                          "          nie:url(?p) = \"%s\") "
 520 			                          "}", uri, uri);
 521 		}
 522 	} else {
 523 		/* If it's a regular file, only query this item */
 524 		sparql = g_strdup_printf ("select ?url ?u nfo:fileLastModified(?u) "
 525 		                          "where { "
 526 		                          "  ?u a nie:DataObject ; "
 527 		                          "     nie:url ?url ; "
 528 		                          "     nie:url \"%s\" . "
 529 		                          "}", uri);
 530 	}
 531 
 532 	if (sync) {
 533 		TrackerSparqlCursor *cursor;
 534 
 535 		cursor = tracker_sparql_connection_query (priv->connection,
 536 		                                          sparql, NULL, NULL);
 537 		if (cursor) {
 538 			sparql_file_query_populate (notifier, cursor, FALSE);
 539 			g_object_unref (cursor);
 540 		}
 541 	} else {
 542 		tracker_sparql_connection_query_async (priv->connection,
 543 		                                       sparql,
 544 		                                       priv->cancellable,
 545 		                                       sparql_query_cb,
 546 		                                       notifier);
 547 	}
 548 
 549 	g_free (sparql);
 550 	g_free (uri);
 551 }
 552 
 553 static gboolean
 554 crawl_directories_start (TrackerFileNotifier *notifier)
 555 {
 556 	TrackerFileNotifierPrivate *priv = notifier->priv;
 557 	TrackerDirectoryFlags flags;
 558 	GFile *directory;
 559 
 560 	if (!priv->pending_index_roots) {
 561 		return FALSE;
 562 	}
 563 
 564 	if (priv->stopped) {
 565 		return FALSE;
 566 	}
 567 
 568 	while (priv->pending_index_roots) {
 569 		directory = priv->pending_index_roots->data;
 570 
 571 		tracker_indexing_tree_get_root (priv->indexing_tree,
 572 		                                directory,
 573 		                                &flags);
 574 
 575 		/* Unset crawled/queried checks on the
 576 		 * directory, we might have requested a
 577 		 * reindex.
 578 		 */
 579 		tracker_file_system_unset_property (priv->file_system,
 580 						    directory,
 581 						    quark_property_crawled);
 582 		tracker_file_system_unset_property (priv->file_system,
 583 						    directory,
 584 						    quark_property_queried);
 585 
 586 		g_cancellable_reset (priv->cancellable);
 587 
 588 		if ((flags & TRACKER_DIRECTORY_FLAG_IGNORE) == 0 &&
 589 		    tracker_crawler_start (priv->crawler,
 590 		                           directory,
 591 		                           (flags & TRACKER_DIRECTORY_FLAG_RECURSE) != 0)) {
 592 			gchar *uri;
 593 
 594 			sparql_file_query_start (notifier, directory,
 595 			                         G_FILE_TYPE_DIRECTORY,
 596 			                         (flags & TRACKER_DIRECTORY_FLAG_RECURSE) != 0,
 597 			                         FALSE);
 598 
 599 			g_timer_reset (priv->timer);
 600 			g_signal_emit (notifier, signals[DIRECTORY_STARTED], 0, directory);
 601 
 602 			uri = g_file_get_uri (directory);
 603 			tracker_info ("Started inspecting '%s'", uri);
 604 			g_free (uri);
 605 
 606 			return TRUE;
 607 		} else {
 608 			/* Emit both signals for consistency */
 609 			g_signal_emit (notifier, signals[DIRECTORY_STARTED], 0, directory);
 610 
 611 			if ((flags & TRACKER_DIRECTORY_FLAG_PRESERVE) == 0) {
 612 				g_signal_emit (notifier, signals[FILE_DELETED], 0, directory);
 613 			}
 614 
 615 			g_signal_emit (notifier, signals[DIRECTORY_FINISHED], 0,
 616 			               directory, 0, 0, 0, 0);
 617 		}
 618 
 619 		/* Remove index root and try the next one */
 620 		priv->pending_index_roots = g_list_delete_link (priv->pending_index_roots,
 621 		                                                priv->pending_index_roots);
 622 	}
 623 
 624 	g_signal_emit (notifier, signals[FINISHED], 0);
 625 
 626 	return FALSE;
 627 }
 628 
 629 static void
 630 crawler_finished_cb (TrackerCrawler *crawler,
 631                      gboolean        was_interrupted,
 632                      gpointer        user_data)
 633 {
 634 	TrackerFileNotifier *notifier = user_data;
 635 	TrackerFileNotifierPrivate *priv = notifier->priv;
 636 
 637 	tracker_info ("  %s crawling files after %2.2f seconds",
 638 	              was_interrupted ? "Stopped" : "Finished",
 639 	              g_timer_elapsed (priv->timer, NULL));
 640 
 641 	if (!was_interrupted) {
 642 		GFile *directory;
 643 
 644 		directory = priv->pending_index_roots->data;
 645 
 646 		/* Mark the directory root as crawled */
 647 		tracker_file_system_set_property (priv->file_system, directory,
 648 		                                  quark_property_crawled,
 649 		                                  GUINT_TO_POINTER (TRUE));
 650 
 651 		/* If it's also been queried, finish operation */
 652 		if (tracker_file_system_get_property (priv->file_system,
 653 		                                      directory,
 654 		                                      quark_property_queried)) {
 655 			file_notifier_traverse_tree (notifier);
 656 		}
 657 	}
 658 }
 659 
 660 /* Monitor signal handlers */
 661 static void
 662 monitor_item_created_cb (TrackerMonitor *monitor,
 663                          GFile          *file,
 664                          gboolean        is_directory,
 665                          gpointer        user_data)
 666 {
 667 	TrackerFileNotifier *notifier = user_data;
 668 	TrackerFileNotifierPrivate *priv = notifier->priv;
 669 	GFileType file_type;
 670 	GFile *canonical;
 671 
 672 	file_type = (is_directory) ? G_FILE_TYPE_DIRECTORY : G_FILE_TYPE_REGULAR;
 673 
 674 	if (!tracker_indexing_tree_file_is_indexable (priv->indexing_tree,
 675 	                                              file, file_type)) {
 676 		/* File should not be indexed */
 677 		return ;
 678 	}
 679 
 680 	if (!is_directory) {
 681 		gboolean indexable;
 682 		GList *children;
 683 		GFile *parent;
 684 
 685 		parent = g_file_get_parent (file);
 686 
 687 		if (parent) {
 688 			children = g_list_prepend (NULL, file);
 689 			indexable = tracker_indexing_tree_parent_is_indexable (priv->indexing_tree,
 690 			                                                       parent,
 691 			                                                       children);
 692 			g_list_free (children);
 693 
 694 			if (!indexable) {
 695 				/* New file triggered a directory content
 696 				 * filter, remove parent directory altogether
 697 				 */
 698 				g_signal_emit (notifier, signals[FILE_DELETED], 0, parent);
 699 				g_object_unref (parent);
 700 				return;
 701 			}
 702 
 703 			g_object_unref (parent);
 704 		}
 705 	} else {
 706 		TrackerDirectoryFlags flags;
 707 
 708 		/* If config for the directory is recursive,
 709 		 * Crawl new entire directory and add monitors
 710 		 */
 711 		tracker_indexing_tree_get_root (priv->indexing_tree,
 712 		                                file, &flags);
 713 
 714 		if (flags & TRACKER_DIRECTORY_FLAG_RECURSE) {
 715 			canonical = tracker_file_system_get_file (priv->file_system,
 716 			                                          file,
 717 			                                          file_type,
 718 			                                          NULL);
 719 			priv->pending_index_roots =
 720 				g_list_append (priv->pending_index_roots,
 721 				               canonical);
 722 
 723 			crawl_directories_start (notifier);
 724 			return;
 725 		}
 726 	}
 727 
 728 	/* Fetch the interned copy */
 729 	canonical = tracker_file_system_get_file (priv->file_system,
 730 	                                          file, file_type, NULL);
 731 
 732 	g_signal_emit (notifier, signals[FILE_CREATED], 0, canonical);
 733 
 734 	if (!is_directory) {
 735 		tracker_file_system_forget_files (priv->file_system, canonical,
 736 		                                  G_FILE_TYPE_REGULAR);
 737 	}
 738 }
 739 
 740 static void
 741 monitor_item_updated_cb (TrackerMonitor *monitor,
 742                          GFile          *file,
 743                          gboolean        is_directory,
 744                          gpointer        user_data)
 745 {
 746 	TrackerFileNotifier *notifier = user_data;
 747 	TrackerFileNotifierPrivate *priv = notifier->priv;
 748 	GFileType file_type;
 749 	GFile *canonical;
 750 
 751 	file_type = (is_directory) ? G_FILE_TYPE_DIRECTORY : G_FILE_TYPE_REGULAR;
 752 
 753 	if (!tracker_indexing_tree_file_is_indexable (priv->indexing_tree,
 754 	                                              file, file_type)) {
 755 		/* File should not be indexed */
 756 		return;
 757 	}
 758 
 759 	/* Fetch the interned copy */
 760 	canonical = tracker_file_system_get_file (priv->file_system,
 761 	                                          file, file_type, NULL);
 762 	g_signal_emit (notifier, signals[FILE_UPDATED], 0, canonical, FALSE);
 763 
 764 	if (!is_directory) {
 765 		tracker_file_system_forget_files (priv->file_system, canonical,
 766 		                                  G_FILE_TYPE_REGULAR);
 767 	}
 768 }
 769 
 770 static void
 771 monitor_item_attribute_updated_cb (TrackerMonitor *monitor,
 772                                    GFile          *file,
 773                                    gboolean        is_directory,
 774                                    gpointer        user_data)
 775 {
 776 	TrackerFileNotifier *notifier = user_data;
 777 	TrackerFileNotifierPrivate *priv = notifier->priv;
 778 	GFile *canonical;
 779 	GFileType file_type;
 780 
 781 	file_type = (is_directory) ? G_FILE_TYPE_DIRECTORY : G_FILE_TYPE_REGULAR;
 782 
 783 	if (!tracker_indexing_tree_file_is_indexable (priv->indexing_tree,
 784 	                                              file, file_type)) {
 785 		/* File should not be indexed */
 786 		return;
 787 	}
 788 
 789 	/* Fetch the interned copy */
 790 	canonical = tracker_file_system_get_file (priv->file_system,
 791 	                                          file, file_type, NULL);
 792 	g_signal_emit (notifier, signals[FILE_UPDATED], 0, canonical, TRUE);
 793 
 794 	if (!is_directory) {
 795 		tracker_file_system_forget_files (priv->file_system, canonical,
 796 		                                  G_FILE_TYPE_REGULAR);
 797 	}
 798 }
 799 
 800 static void
 801 monitor_item_deleted_cb (TrackerMonitor *monitor,
 802                          GFile          *file,
 803                          gboolean        is_directory,
 804                          gpointer        user_data)
 805 {
 806 	TrackerFileNotifier *notifier = user_data;
 807 	TrackerFileNotifierPrivate *priv = notifier->priv;
 808 	GFile *canonical;
 809 	GFileType file_type;
 810 
 811 	file_type = (is_directory) ? G_FILE_TYPE_DIRECTORY : G_FILE_TYPE_REGULAR;
 812 
 813 	if (!tracker_indexing_tree_file_is_indexable (priv->indexing_tree,
 814 	                                              file, file_type)) {
 815 		/* File was not indexed */
 816 		return ;
 817 	}
 818 
 819 	if (!is_directory) {
 820 		gboolean indexable;
 821 		GList *children;
 822 		GFile *parent;
 823 
 824 		children = g_list_prepend (NULL, file);
 825 		parent = g_file_get_parent (file);
 826 
 827 		indexable = tracker_indexing_tree_parent_is_indexable (priv->indexing_tree,
 828 		                                                       parent, children);
 829 		g_object_unref (parent);
 830 		g_list_free (children);
 831 
 832 		/* note: This supposedly works, but in practice
 833 		 * won't ever happen as we don't get monitor events
 834 		 * from directories triggering a filter of type
 835 		 * TRACKER_FILTER_PARENT_DIRECTORY.
 836 		 */
 837 		if (!indexable) {
 838 			/* New file was triggering a directory content
 839 			 * filter, reindex parent directory altogether
 840 			 */
 841 			file = tracker_file_system_get_file (priv->file_system,
 842 			                                     file,
 843 			                                     G_FILE_TYPE_DIRECTORY,
 844 			                                     NULL);
 845 			priv->pending_index_roots =
 846 				g_list_append (priv->pending_index_roots, file);
 847 
 848 			crawl_directories_start (notifier);
 849 			return;
 850 		}
 851 	}
 852 
 853 	/* Fetch the interned copy */
 854 	canonical = tracker_file_system_get_file (priv->file_system,
 855 	                                          file, file_type, NULL);
 856 	g_signal_emit (notifier, signals[FILE_DELETED], 0, canonical);
 857 
 858 	/* Remove the file from the cache (works recursively for directories) */
 859 	tracker_file_system_forget_files (priv->file_system,
 860 	                                  file,
 861 	                                  G_FILE_TYPE_UNKNOWN);
 862 }
 863 
 864 static void
 865 monitor_item_moved_cb (TrackerMonitor *monitor,
 866                        GFile          *file,
 867                        GFile          *other_file,
 868                        gboolean        is_directory,
 869                        gboolean        is_source_monitored,
 870                        gpointer        user_data)
 871 {
 872 	TrackerFileNotifier *notifier;
 873 	TrackerFileNotifierPrivate *priv;
 874 
 875 	notifier = user_data;
 876 	priv = notifier->priv;
 877 
 878 	if (!is_source_monitored) {
 879 		if (is_directory) {
 880 			/* Remove monitors if any */
 881 			tracker_monitor_remove_recursively (priv->monitor, file);
 882 
 883 			/* If should recurse, crawl other_file, as content is "new" */
 884 			file = tracker_file_system_get_file (priv->file_system,
 885 			                                     other_file,
 886 			                                     G_FILE_TYPE_DIRECTORY,
 887 			                                     NULL);
 888 			priv->pending_index_roots =
 889 				g_list_append (priv->pending_index_roots, file);
 890 
 891 			crawl_directories_start (notifier);
 892 		}
 893 		/* else, file, do nothing */
 894 	} else {
 895 		gboolean source_stored, should_process_other;
 896 		GFileType file_type;
 897 		GFile *check_file;
 898 
 899 		if (is_directory) {
 900 			check_file = g_object_ref (file);
 901 		} else {
 902 			check_file = g_file_get_parent (file);
 903 		}
 904 
 905 		file_type = (is_directory) ? G_FILE_TYPE_DIRECTORY : G_FILE_TYPE_REGULAR;
 906 
 907 		/* If the (parent) directory is in
 908 		 * the filesystem, file is stored
 909 		 */
 910 		source_stored = (tracker_file_system_peek_file (priv->file_system,
 911 		                                                check_file) != NULL);
 912 		should_process_other = tracker_indexing_tree_file_is_indexable (priv->indexing_tree,
 913 		                                                                other_file,
 914 		                                                                file_type);
 915 		g_object_unref (check_file);
 916 
 917 		if (!source_stored) {
 918 			/* Destination location should be indexed as if new */
 919 			/* Remove monitors if any */
 920 			if (is_directory) {
 921 				tracker_monitor_remove_recursively (priv->monitor,
 922 				                                    file);
 923 			}
 924 
 925 			if (should_process_other) {
 926 				gboolean dest_is_recursive;
 927 				TrackerDirectoryFlags flags;
 928 
 929 				tracker_indexing_tree_get_root (priv->indexing_tree, other_file, &flags);
 930 				dest_is_recursive = (flags & TRACKER_DIRECTORY_FLAG_RECURSE) != 0;
 931 
 932 				/* Source file was not stored, check dest file as new */
 933 				if (!is_directory || !dest_is_recursive) {
 934 					g_signal_emit (notifier, signals[FILE_CREATED], 0, other_file);
 935 				} else if (is_directory) {
 936 					/* Crawl dest directory */
 937 					other_file = tracker_file_system_get_file (priv->file_system,
 938 					                                           other_file,
 939 					                                           G_FILE_TYPE_DIRECTORY,
 940 					                                           NULL);
 941 					priv->pending_index_roots =
 942 						g_list_append (priv->pending_index_roots,
 943 						               other_file);
 944 
 945 					crawl_directories_start (notifier);
 946 				}
 947 			}
 948 			/* Else, do nothing else */
 949 		} else if (!should_process_other) {
 950 			/* Delete original location as it moves to be non indexable */
 951 			if (is_directory) {
 952 				tracker_monitor_remove_recursively (priv->monitor,
 953 				                                    file);
 954 			}
 955 
 956 			g_signal_emit (notifier, signals[FILE_DELETED], 0, file);
 957 		} else {
 958 			/* Handle move */
 959 			if (is_directory) {
 960 				gboolean dest_is_recursive, source_is_recursive;
 961 				TrackerDirectoryFlags flags;
 962 
 963 				tracker_monitor_move (priv->monitor,
 964 				                      file, other_file);
 965 
 966 				tracker_indexing_tree_get_root (priv->indexing_tree,
 967 				                                other_file,
 968 				                                &flags);
 969 				dest_is_recursive = (flags & TRACKER_DIRECTORY_FLAG_RECURSE) != 0;
 970 
 971 				tracker_indexing_tree_get_root (priv->indexing_tree,
 972 				                                file, &flags);
 973 				source_is_recursive = (flags & TRACKER_DIRECTORY_FLAG_RECURSE) != 0;
 974 
 975 				if (source_is_recursive && !dest_is_recursive) {
 976 					/* A directory is being moved from a
 977 					 * recursive location to a non-recursive
 978 					 * one, don't do anything here, and let
 979 					 * TrackerMinerFS handle it, see item_move().
 980 					 */
 981 				} else if (!source_is_recursive && dest_is_recursive) {
 982 					/* crawl the folder */
 983 					file = tracker_file_system_get_file (priv->file_system,
 984 					                                     other_file,
 985 					                                     G_FILE_TYPE_DIRECTORY,
 986 					                                     NULL);
 987 					priv->pending_index_roots =
 988 						g_list_append (priv->pending_index_roots,
 989 						               file);
 990 
 991 					crawl_directories_start (notifier);
 992 				}
 993 			}
 994 
 995 			g_signal_emit (notifier, signals[FILE_MOVED], 0, file, other_file);
 996 		}
 997 	}
 998 }
 999 
1000 /* Indexing tree signal handlers */
1001 static void
1002 indexing_tree_directory_added (TrackerIndexingTree *indexing_tree,
1003                                GFile               *directory,
1004                                gpointer             user_data)
1005 {
1006 	TrackerFileNotifier *notifier = user_data;
1007 	TrackerFileNotifierPrivate *priv = notifier->priv;
1008 	gboolean start_crawler = FALSE;
1009 	TrackerDirectoryFlags flags;
1010 
1011 	tracker_indexing_tree_get_root (indexing_tree, directory, &flags);
1012 
1013 	directory = tracker_file_system_get_file (priv->file_system, directory,
1014 	                                          G_FILE_TYPE_DIRECTORY, NULL);
1015 	if (!priv->stopped &&
1016 	    !priv->pending_index_roots) {
1017 		start_crawler = TRUE;
1018 	}
1019 
1020 	if (!g_list_find (priv->pending_index_roots, directory)) {
1021 		priv->pending_index_roots = g_list_append (priv->pending_index_roots,
1022 		                                           directory);
1023 		if (start_crawler) {
1024 			crawl_directories_start (notifier);
1025 		}
1026 	}
1027 }
1028 
1029 static void
1030 indexing_tree_directory_removed (TrackerIndexingTree *indexing_tree,
1031                                  GFile               *directory,
1032                                  gpointer             user_data)
1033 {
1034 	TrackerFileNotifier *notifier = user_data;
1035 	TrackerFileNotifierPrivate *priv = notifier->priv;
1036 	TrackerDirectoryFlags flags;
1037 
1038 	/* Flags are still valid at the moment of deletion */
1039 	tracker_indexing_tree_get_root (indexing_tree, directory, &flags);
1040 	directory = tracker_file_system_peek_file (priv->file_system, directory);
1041 
1042 	if (!directory) {
1043 		/* If the dir has no canonical copy,
1044 		 * it wasn't even told to be indexed.
1045 		 */
1046 		return;
1047 	}
1048 
1049 	/* If the folder was being ignored, index/crawl it from scratch */
1050 	if (flags & TRACKER_DIRECTORY_FLAG_IGNORE) {
1051 		GFile *parent;
1052 
1053 		parent = g_file_get_parent (directory);
1054 
1055 		if (parent) {
1056 			TrackerDirectoryFlags parent_flags;
1057 
1058 			tracker_indexing_tree_get_root (indexing_tree,
1059 			                                parent,
1060 			                                &parent_flags);
1061 
1062 			if (parent_flags & TRACKER_DIRECTORY_FLAG_RECURSE) {
1063 				priv->pending_index_roots =
1064 					g_list_append (priv->pending_index_roots,
1065 						       g_object_ref (directory));
1066 
1067 				crawl_directories_start (notifier);
1068 			} else if (tracker_indexing_tree_file_is_root (indexing_tree,
1069 			                                               parent)) {
1070 				g_signal_emit (notifier, signals[FILE_CREATED],
1071 					       0, directory);
1072 			}
1073 
1074 			g_object_unref (parent);
1075 		}
1076 		return;
1077 	}
1078 
1079 	if ((flags & TRACKER_DIRECTORY_FLAG_PRESERVE) == 0) {
1080 		/* Directory needs to be deleted from the store too */
1081 		g_signal_emit (notifier, signals[FILE_DELETED], 0, directory);
1082 	}
1083 
1084 	if (priv->pending_index_roots) {
1085 		gboolean start_crawler = FALSE;
1086 
1087 		if (directory == priv->pending_index_roots->data) {
1088 			/* Directory being currently processed */
1089 			tracker_crawler_stop (priv->crawler);
1090 			g_cancellable_cancel (priv->cancellable);
1091 			start_crawler = TRUE;
1092 		}
1093 
1094 		priv->pending_index_roots = g_list_remove_all (priv->pending_index_roots,
1095 		                                               directory);
1096 
1097 		if (start_crawler && priv->pending_index_roots != NULL) {
1098 			crawl_directories_start (notifier);
1099 		}
1100 	}
1101 
1102 	/* Remove monitors if any */
1103 	tracker_monitor_remove_recursively (priv->monitor, directory);
1104 
1105 	/* Remove all files from cache */
1106 	tracker_file_system_forget_files (priv->file_system, directory,
1107 	                                  G_FILE_TYPE_UNKNOWN);
1108 }
1109 
1110 static void
1111 tracker_file_notifier_finalize (GObject *object)
1112 {
1113 	TrackerFileNotifierPrivate *priv;
1114 
1115 	priv = TRACKER_FILE_NOTIFIER (object)->priv;
1116 
1117 	if (priv->indexing_tree) {
1118 		g_object_unref (priv->indexing_tree);
1119 	}
1120 
1121 	g_object_unref (priv->crawler);
1122 	g_object_unref (priv->monitor);
1123 	g_object_unref (priv->file_system);
1124 	g_object_unref (priv->cancellable);
1125 
1126 	g_list_free (priv->pending_index_roots);
1127 	g_timer_destroy (priv->timer);
1128 
1129 	G_OBJECT_CLASS (tracker_file_notifier_parent_class)->finalize (object);
1130 }
1131 
1132 static void
1133 tracker_file_notifier_constructed (GObject *object)
1134 {
1135 	TrackerFileNotifierPrivate *priv;
1136 
1137 	priv = TRACKER_FILE_NOTIFIER (object)->priv;
1138 	g_assert (priv->indexing_tree);
1139 
1140 	g_signal_connect (priv->indexing_tree, "directory-added",
1141 	                  G_CALLBACK (indexing_tree_directory_added), object);
1142 	g_signal_connect (priv->indexing_tree, "directory-updated",
1143 	                  G_CALLBACK (indexing_tree_directory_added), object);
1144 	g_signal_connect (priv->indexing_tree, "directory-removed",
1145 	                  G_CALLBACK (indexing_tree_directory_removed), object);
1146 }
1147 
1148 static void
1149 tracker_file_notifier_class_init (TrackerFileNotifierClass *klass)
1150 {
1151 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
1152 
1153 	object_class->finalize = tracker_file_notifier_finalize;
1154 	object_class->set_property = tracker_file_notifier_set_property;
1155 	object_class->get_property = tracker_file_notifier_get_property;
1156 	object_class->constructed = tracker_file_notifier_constructed;
1157 
1158 	signals[FILE_CREATED] =
1159 		g_signal_new ("file-created",
1160 		              G_TYPE_FROM_CLASS (klass),
1161 		              G_SIGNAL_RUN_LAST,
1162 		              G_STRUCT_OFFSET (TrackerFileNotifierClass,
1163 		                               file_created),
1164 		              NULL, NULL,
1165 		              g_cclosure_marshal_VOID__OBJECT,
1166 		              G_TYPE_NONE,
1167 		              1, G_TYPE_FILE);
1168 	signals[FILE_UPDATED] =
1169 		g_signal_new ("file-updated",
1170 		              G_TYPE_FROM_CLASS (klass),
1171 		              G_SIGNAL_RUN_LAST,
1172 		              G_STRUCT_OFFSET (TrackerFileNotifierClass,
1173 		                               file_updated),
1174 		              NULL, NULL,
1175 		              tracker_marshal_VOID__OBJECT_BOOLEAN,
1176 		              G_TYPE_NONE,
1177 		              2, G_TYPE_FILE, G_TYPE_BOOLEAN);
1178 	signals[FILE_DELETED] =
1179 		g_signal_new ("file-deleted",
1180 		              G_TYPE_FROM_CLASS (klass),
1181 		              G_SIGNAL_RUN_LAST,
1182 		              G_STRUCT_OFFSET (TrackerFileNotifierClass,
1183 		                               file_deleted),
1184 		              NULL, NULL,
1185 		              g_cclosure_marshal_VOID__OBJECT,
1186 		              G_TYPE_NONE,
1187 		              1, G_TYPE_FILE);
1188 	signals[FILE_MOVED] =
1189 		g_signal_new ("file-moved",
1190 		              G_TYPE_FROM_CLASS (klass),
1191 		              G_SIGNAL_RUN_LAST,
1192 		              G_STRUCT_OFFSET (TrackerFileNotifierClass,
1193 		                               file_moved),
1194 		              NULL, NULL,
1195 		              tracker_marshal_VOID__OBJECT_OBJECT,
1196 		              G_TYPE_NONE,
1197 		              2, G_TYPE_FILE, G_TYPE_FILE);
1198 	signals[DIRECTORY_STARTED] =
1199 		g_signal_new ("directory-started",
1200 		              G_TYPE_FROM_CLASS (klass),
1201 		              G_SIGNAL_RUN_LAST,
1202 		              G_STRUCT_OFFSET (TrackerFileNotifierClass,
1203 		                               directory_started),
1204 		              NULL, NULL,
1205 		              g_cclosure_marshal_VOID__OBJECT,
1206 		              G_TYPE_NONE,
1207 		              1, G_TYPE_FILE);
1208 	signals[DIRECTORY_FINISHED] =
1209 		g_signal_new ("directory-finished",
1210 		              G_TYPE_FROM_CLASS (klass),
1211 		              G_SIGNAL_RUN_LAST,
1212 		              G_STRUCT_OFFSET (TrackerFileNotifierClass,
1213 		                               directory_finished),
1214 		              NULL, NULL,
1215 		              tracker_marshal_VOID__OBJECT_UINT_UINT_UINT_UINT,
1216 		              G_TYPE_NONE,
1217 		              5, G_TYPE_FILE, G_TYPE_UINT,
1218 		              G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
1219 	signals[FINISHED] =
1220 		g_signal_new ("finished",
1221 		              G_TYPE_FROM_CLASS (klass),
1222 		              G_SIGNAL_RUN_LAST,
1223 		              G_STRUCT_OFFSET (TrackerFileNotifierClass,
1224 		                               finished),
1225 		              NULL, NULL,
1226 		              g_cclosure_marshal_VOID__VOID,
1227 		              G_TYPE_NONE, 0, G_TYPE_NONE);
1228 
1229 	g_object_class_install_property (object_class,
1230 	                                 PROP_INDEXING_TREE,
1231 	                                 g_param_spec_object ("indexing-tree",
1232 	                                                      "Indexing tree",
1233 	                                                      "Indexing tree",
1234 	                                                      TRACKER_TYPE_INDEXING_TREE,
1235 	                                                      G_PARAM_READWRITE |
1236 	                                                      G_PARAM_CONSTRUCT_ONLY));
1237 	g_type_class_add_private (object_class,
1238 	                          sizeof (TrackerFileNotifierClass));
1239 
1240 	/* Initialize property quarks */
1241 	quark_property_crawled = g_quark_from_static_string ("tracker-property-crawled");
1242 	tracker_file_system_register_property (quark_property_crawled, NULL);
1243 
1244 	quark_property_queried = g_quark_from_static_string ("tracker-property-queried");
1245 	tracker_file_system_register_property (quark_property_queried, NULL);
1246 
1247 	quark_property_iri = g_quark_from_static_string ("tracker-property-iri");
1248 	tracker_file_system_register_property (quark_property_iri, g_free);
1249 
1250 	quark_property_store_mtime = g_quark_from_static_string ("tracker-property-store-mtime");
1251 	tracker_file_system_register_property (quark_property_store_mtime,
1252 	                                       g_free);
1253 
1254 	quark_property_filesystem_mtime = g_quark_from_static_string ("tracker-property-filesystem-mtime");
1255 	tracker_file_system_register_property (quark_property_filesystem_mtime,
1256 	                                       g_free);
1257 }
1258 
1259 static void
1260 tracker_file_notifier_init (TrackerFileNotifier *notifier)
1261 {
1262 	TrackerFileNotifierPrivate *priv;
1263 	GError *error = NULL;
1264 
1265 	priv = notifier->priv =
1266 		G_TYPE_INSTANCE_GET_PRIVATE (notifier,
1267 		                             TRACKER_TYPE_FILE_NOTIFIER,
1268 		                             TrackerFileNotifierPrivate);
1269 
1270 	priv->connection = tracker_sparql_connection_get (NULL, &error);
1271 	priv->cancellable = g_cancellable_new ();
1272 
1273 	if (error) {
1274 		g_critical ("Could not get SPARQL connection: %s\n",
1275 		            error->message);
1276 		g_error_free (error);
1277 
1278 		g_assert_not_reached ();
1279 	}
1280 
1281 	/* Initialize filesystem and register properties */
1282 	priv->file_system = tracker_file_system_new ();
1283 
1284 	priv->timer = g_timer_new ();
1285 	priv->stopped = TRUE;
1286 
1287 	/* Set up crawler */
1288 	priv->crawler = tracker_crawler_new ();
1289 	tracker_crawler_set_file_attributes (priv->crawler,
1290 	                                     G_FILE_ATTRIBUTE_TIME_MODIFIED ","
1291 	                                     G_FILE_ATTRIBUTE_STANDARD_TYPE);
1292 
1293 	g_signal_connect (priv->crawler, "check-file",
1294 	                  G_CALLBACK (crawler_check_file_cb),
1295 	                  notifier);
1296 	g_signal_connect (priv->crawler, "check-directory",
1297 	                  G_CALLBACK (crawler_check_directory_cb),
1298 	                  notifier);
1299 	g_signal_connect (priv->crawler, "check-directory-contents",
1300 	                  G_CALLBACK (crawler_check_directory_contents_cb),
1301 	                  notifier);
1302 	g_signal_connect (priv->crawler, "directory-crawled",
1303 	                  G_CALLBACK (crawler_directory_crawled_cb),
1304 	                  notifier);
1305 	g_signal_connect (priv->crawler, "finished",
1306 	                  G_CALLBACK (crawler_finished_cb),
1307 	                  notifier);
1308 
1309 	/* Set up monitor */
1310 	priv->monitor = tracker_monitor_new ();
1311 
1312 	g_signal_connect (priv->monitor, "item-created",
1313 	                  G_CALLBACK (monitor_item_created_cb),
1314 	                  notifier);
1315 	g_signal_connect (priv->monitor, "item-updated",
1316 	                  G_CALLBACK (monitor_item_updated_cb),
1317 	                  notifier);
1318 	g_signal_connect (priv->monitor, "item-attribute-updated",
1319 	                  G_CALLBACK (monitor_item_attribute_updated_cb),
1320 	                  notifier);
1321 	g_signal_connect (priv->monitor, "item-deleted",
1322 	                  G_CALLBACK (monitor_item_deleted_cb),
1323 	                  notifier);
1324 	g_signal_connect (priv->monitor, "item-moved",
1325 	                  G_CALLBACK (monitor_item_moved_cb),
1326 	                  notifier);
1327 }
1328 
1329 TrackerFileNotifier *
1330 tracker_file_notifier_new (TrackerIndexingTree *indexing_tree)
1331 {
1332 	g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (indexing_tree), NULL);
1333 
1334 	return g_object_new (TRACKER_TYPE_FILE_NOTIFIER,
1335 	                     "indexing-tree", indexing_tree,
1336 	                     NULL);
1337 }
1338 
1339 gboolean
1340 tracker_file_notifier_start (TrackerFileNotifier *notifier)
1341 {
1342 	TrackerFileNotifierPrivate *priv;
1343 
1344 	g_return_val_if_fail (TRACKER_IS_FILE_NOTIFIER (notifier), FALSE);
1345 
1346 	priv = notifier->priv;
1347 
1348 	if (priv->stopped) {
1349 		priv->stopped = FALSE;
1350 
1351 		if (priv->pending_index_roots) {
1352 			crawl_directories_start (notifier);
1353 		}
1354 	}
1355 
1356 	return TRUE;
1357 }
1358 
1359 void
1360 tracker_file_notifier_stop (TrackerFileNotifier *notifier)
1361 {
1362 	TrackerFileNotifierPrivate *priv;
1363 
1364 	g_return_if_fail (TRACKER_IS_FILE_NOTIFIER (notifier));
1365 
1366 	priv = notifier->priv;
1367 
1368 	if (!priv->stopped) {
1369 		tracker_crawler_stop (priv->crawler);
1370 		g_cancellable_cancel (priv->cancellable);
1371 		priv->stopped = TRUE;
1372 	}
1373 }
1374 
1375 gboolean
1376 tracker_file_notifier_is_active (TrackerFileNotifier *notifier)
1377 {
1378 	TrackerFileNotifierPrivate *priv;
1379 
1380 	g_return_val_if_fail (TRACKER_IS_FILE_NOTIFIER (notifier), FALSE);
1381 
1382 	priv = notifier->priv;
1383 	return priv->pending_index_roots != NULL;
1384 }
1385 
1386 const gchar *
1387 tracker_file_notifier_get_file_iri (TrackerFileNotifier *notifier,
1388                                     GFile               *file)
1389 {
1390 	TrackerFileNotifierPrivate *priv;
1391 	GFile *canonical;
1392 	gchar *iri = NULL;
1393 
1394 	g_return_val_if_fail (TRACKER_IS_FILE_NOTIFIER (notifier), NULL);
1395 	g_return_val_if_fail (G_IS_FILE (file), NULL);
1396 
1397 	priv = notifier->priv;
1398 	canonical = tracker_file_system_get_file (priv->file_system,
1399 	                                          file,
1400 	                                          G_FILE_TYPE_REGULAR,
1401 	                                          NULL);
1402 	if (!canonical) {
1403 		return NULL;
1404 	}
1405 
1406 	iri = tracker_file_system_get_property (priv->file_system,
1407 	                                        canonical,
1408 	                                        quark_property_iri);
1409 
1410 	if (!iri) {
1411 		/* Fetch data for this file synchronously */
1412 		sparql_file_query_start (notifier, canonical,
1413 		                         G_FILE_TYPE_REGULAR,
1414 		                         FALSE, TRUE);
1415 
1416 		iri = tracker_file_system_get_property (priv->file_system,
1417 		                                        canonical,
1418 		                                        quark_property_iri);
1419 	}
1420 
1421 	return iri;
1422 }