tracker-0.16.2/src/libtracker-miner/tracker-indexing-tree.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-file-utils.h>
 23 #include "tracker-indexing-tree.h"
 24 
 25 /**
 26  * SECTION:tracker-indexing-tree
 27  * @short_description: Indexing tree handling
 28  *
 29  * #TrackerIndexingTree handles the tree of directories configured to be indexed
 30  * by the #TrackerMinerFS.
 31  **/
 32 
 33 G_DEFINE_TYPE (TrackerIndexingTree, tracker_indexing_tree, G_TYPE_OBJECT)
 34 
 35 typedef struct _TrackerIndexingTreePrivate TrackerIndexingTreePrivate;
 36 typedef struct _NodeData NodeData;
 37 typedef struct _PatternData PatternData;
 38 typedef struct _FindNodeData FindNodeData;
 39 
 40 struct _NodeData
 41 {
 42 	GFile *file;
 43 	guint flags : 7;
 44 	guint shallow : 1;
 45 };
 46 
 47 struct _PatternData
 48 {
 49 	GPatternSpec *pattern;
 50 	TrackerFilterType type;
 51 	GFile *file; /* Only filled in in absolute paths */
 52 };
 53 
 54 struct _FindNodeData
 55 {
 56 	GEqualFunc func;
 57 	GNode *node;
 58 	GFile *file;
 59 };
 60 
 61 struct _TrackerIndexingTreePrivate
 62 {
 63 	GNode *config_tree;
 64 	GList *filter_patterns;
 65 	TrackerFilterPolicy policies[TRACKER_FILTER_PARENT_DIRECTORY + 1];
 66 
 67 	guint filter_hidden : 1;
 68 };
 69 
 70 enum {
 71 	PROP_0,
 72 	PROP_FILTER_HIDDEN
 73 };
 74 
 75 enum {
 76 	DIRECTORY_ADDED,
 77 	DIRECTORY_REMOVED,
 78 	DIRECTORY_UPDATED,
 79 	LAST_SIGNAL
 80 };
 81 
 82 static guint signals[LAST_SIGNAL] = { 0 };
 83 
 84 static NodeData *
 85 node_data_new (GFile *file,
 86                guint  flags)
 87 {
 88 	NodeData *data;
 89 
 90 	data = g_slice_new (NodeData);
 91 	data->file = g_object_ref (file);
 92 	data->flags = flags;
 93 	data->shallow = FALSE;
 94 
 95 	return data;
 96 }
 97 
 98 static void
 99 node_data_free (NodeData *data)
100 {
101 	g_object_unref (data->file);
102 	g_slice_free (NodeData, data);
103 }
104 
105 static gboolean
106 node_free (GNode    *node,
107            gpointer  user_data)
108 {
109 	node_data_free (node->data);
110 	return FALSE;
111 }
112 
113 static PatternData *
114 pattern_data_new (const gchar *glob_string,
115                   guint        type)
116 {
117 	PatternData *data;
118 
119 	data = g_slice_new0 (PatternData);
120 	data->pattern = g_pattern_spec_new (glob_string);
121 	data->type = type;
122 
123 	if (g_path_is_absolute (glob_string)) {
124 		data->file = g_file_new_for_path (glob_string);
125 	}
126 
127 	return data;
128 }
129 
130 static void
131 pattern_data_free (PatternData *data)
132 {
133 	if (data->file) {
134 		g_object_unref (data->file);
135 	}
136 
137 	g_pattern_spec_free (data->pattern);
138 	g_slice_free (PatternData, data);
139 }
140 
141 static void
142 tracker_indexing_tree_get_property (GObject    *object,
143                                     guint       prop_id,
144                                     GValue     *value,
145                                     GParamSpec *pspec)
146 {
147 	TrackerIndexingTreePrivate *priv;
148 
149 	priv = TRACKER_INDEXING_TREE (object)->priv;
150 
151 	switch (prop_id) {
152 	case PROP_FILTER_HIDDEN:
153 		g_value_set_boolean (value, priv->filter_hidden);
154 		break;
155 	default:
156 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
157 		break;
158 	}
159 }
160 
161 static void
162 tracker_indexing_tree_set_property (GObject      *object,
163                                     guint         prop_id,
164                                     const GValue *value,
165                                     GParamSpec   *pspec)
166 {
167 	TrackerIndexingTree *tree;
168 
169 	tree = TRACKER_INDEXING_TREE (object);
170 
171 	switch (prop_id) {
172 	case PROP_FILTER_HIDDEN:
173 		tracker_indexing_tree_set_filter_hidden (tree,
174 		                                         g_value_get_boolean (value));
175 		break;
176 	default:
177 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
178 		break;
179 	}
180 }
181 
182 static void
183 tracker_indexing_tree_finalize (GObject *object)
184 {
185 	TrackerIndexingTreePrivate *priv;
186 	TrackerIndexingTree *tree;
187 
188 	tree = TRACKER_INDEXING_TREE (object);
189 	priv = tree->priv;
190 
191 	g_list_foreach (priv->filter_patterns, (GFunc) pattern_data_free, NULL);
192 	g_list_free (priv->filter_patterns);
193 
194 	g_node_traverse (priv->config_tree,
195 	                 G_POST_ORDER,
196 	                 G_TRAVERSE_ALL,
197 	                 -1,
198 	                 (GNodeTraverseFunc) node_free,
199 	                 NULL);
200 	g_node_destroy (priv->config_tree);
201 
202 	G_OBJECT_CLASS (tracker_indexing_tree_parent_class)->finalize (object);
203 }
204 
205 static void
206 tracker_indexing_tree_class_init (TrackerIndexingTreeClass *klass)
207 {
208 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
209 
210 	object_class->finalize = tracker_indexing_tree_finalize;
211 	object_class->set_property = tracker_indexing_tree_set_property;
212 	object_class->get_property = tracker_indexing_tree_get_property;
213 
214 	g_object_class_install_property (object_class,
215 	                                 PROP_FILTER_HIDDEN,
216 	                                 g_param_spec_boolean ("filter-hidden",
217 	                                                       "Filter hidden",
218 	                                                       "Whether hidden resources are filtered",
219 	                                                       FALSE,
220 	                                                       G_PARAM_READWRITE));
221 	signals[DIRECTORY_ADDED] =
222 		g_signal_new ("directory-added",
223 		              G_OBJECT_CLASS_TYPE (object_class),
224 		              G_SIGNAL_RUN_LAST,
225 		              G_STRUCT_OFFSET (TrackerIndexingTreeClass,
226 		                               directory_added),
227 		              NULL, NULL,
228 		              g_cclosure_marshal_VOID__OBJECT,
229 		              G_TYPE_NONE, 1, G_TYPE_FILE);
230 	signals[DIRECTORY_REMOVED] =
231 		g_signal_new ("directory-removed",
232 		              G_OBJECT_CLASS_TYPE (object_class),
233 		              G_SIGNAL_RUN_LAST,
234 		              G_STRUCT_OFFSET (TrackerIndexingTreeClass,
235 		                               directory_removed),
236 		              NULL, NULL,
237 		              g_cclosure_marshal_VOID__OBJECT,
238 		              G_TYPE_NONE, 1, G_TYPE_FILE);
239 	signals[DIRECTORY_UPDATED] =
240 		g_signal_new ("directory-updated",
241 		              G_OBJECT_CLASS_TYPE (object_class),
242 		              G_SIGNAL_RUN_LAST,
243 		              G_STRUCT_OFFSET (TrackerIndexingTreeClass,
244 		                               directory_updated),
245 		              NULL, NULL,
246 		              g_cclosure_marshal_VOID__OBJECT,
247 		              G_TYPE_NONE, 1, G_TYPE_FILE);
248 
249 	g_type_class_add_private (object_class,
250 	                          sizeof (TrackerIndexingTreePrivate));
251 }
252 
253 static void
254 tracker_indexing_tree_init (TrackerIndexingTree *tree)
255 {
256 	TrackerIndexingTreePrivate *priv;
257 	NodeData *data;
258 	GFile *root;
259 	gint i;
260 
261 	priv = tree->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree,
262 	                                                 TRACKER_TYPE_INDEXING_TREE,
263 	                                                 TrackerIndexingTreePrivate);
264 	/* Add a shallow root node */
265 	root = g_file_new_for_uri ("file:///");
266 	data = node_data_new (root, 0);
267 	data->shallow = TRUE;
268 
269 	priv->config_tree = g_node_new (data);
270 	g_object_unref (root);
271 
272 	for (i = TRACKER_FILTER_FILE; i <= TRACKER_FILTER_PARENT_DIRECTORY; i++) {
273 		priv->policies[i] = TRACKER_FILTER_POLICY_ACCEPT;
274 	}
275 }
276 
277 /**
278  * tracker_indexing_tree_new:
279  *
280  * Returns a newly created #TrackerIndexingTree
281  *
282  * Returns: a newly allocated #TrackerIndexingTree
283  **/
284 TrackerIndexingTree *
285 tracker_indexing_tree_new (void)
286 {
287 	return g_object_new (TRACKER_TYPE_INDEXING_TREE, NULL);
288 }
289 
290 #ifdef PRINT_INDEXING_TREE
291 static gboolean
292 print_node_foreach (GNode    *node,
293                     gpointer  user_data)
294 {
295 	NodeData *node_data = node->data;
296 	gchar *uri;
297 
298 	uri = g_file_get_uri (node_data->file);
299 	g_debug ("%*s %s", g_node_depth (node), "-", uri);
300 	g_free (uri);
301 
302 	return FALSE;
303 }
304 
305 static void
306 print_tree (GNode *node)
307 {
308 	g_debug ("Printing modified tree...");
309 	g_node_traverse (node,
310 	                 G_PRE_ORDER,
311 	                 G_TRAVERSE_ALL,
312 	                 -1,
313 	                 print_node_foreach,
314 	                 NULL);
315 }
316 
317 #endif /* PRINT_INDEXING_TREE */
318 
319 static gboolean
320 find_node_foreach (GNode    *node,
321                    gpointer  user_data)
322 {
323 	FindNodeData *data = user_data;
324 	NodeData *node_data = node->data;
325 
326 	if ((data->func) (data->file, node_data->file)) {
327 		data->node = node;
328 		return TRUE;
329 	}
330 
331 	return FALSE;
332 }
333 
334 static GNode *
335 find_directory_node (GNode      *node,
336                      GFile      *file,
337                      GEqualFunc  func)
338 {
339 	FindNodeData data;
340 
341 	data.file = file;
342 	data.node = NULL;
343 	data.func = func;
344 
345 	g_node_traverse (node,
346 	                 G_POST_ORDER,
347 	                 G_TRAVERSE_ALL,
348 	                 -1,
349 	                 find_node_foreach,
350 	                 &data);
351 
352 	return data.node;
353 }
354 
355 static void
356 check_reparent_node (GNode    *node,
357                      gpointer  user_data)
358 {
359 	GNode *new_node = user_data;
360 	NodeData *new_node_data, *node_data;
361 
362 	new_node_data = new_node->data;
363 	node_data = node->data;
364 
365 	if (g_file_has_prefix (node_data->file,
366 	                       new_node_data->file)) {
367 		g_node_unlink (node);
368 		g_node_append (new_node, node);
369 	}
370 }
371 
372 /**
373  * tracker_indexing_tree_add:
374  * @tree: a #TrackerIndexingTree
375  * @directory: #GFile pointing to a directory
376  * @flags: Configuration flags for the directory
377  *
378  * Adds a directory to the indexing tree with the
379  * given configuration flags.
380  **/
381 void
382 tracker_indexing_tree_add (TrackerIndexingTree   *tree,
383                            GFile                 *directory,
384                            TrackerDirectoryFlags  flags)
385 {
386 	TrackerIndexingTreePrivate *priv;
387 	GNode *parent, *node;
388 	NodeData *data;
389 
390 	g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
391 	g_return_if_fail (G_IS_FILE (directory));
392 
393 	priv = tree->priv;
394 	node = find_directory_node (priv->config_tree, directory,
395 	                            (GEqualFunc) g_file_equal);
396 
397 	if (node) {
398 		/* Node already existed */
399 		data = node->data;
400 		data->shallow = FALSE;
401 
402 		/* Overwrite flags if they are different */
403 		if (data->flags != flags) {
404 			gchar *uri;
405 
406 			uri = g_file_get_uri (directory);
407 			g_message ("Overwriting flags for directory '%s'", uri);
408 			g_free (uri);
409 
410 			data->flags = flags;
411 			g_signal_emit (tree, signals[DIRECTORY_UPDATED], 0,
412 			               data->file);
413 		}
414 		return;
415 	}
416 
417 	/* Find out the parent */
418 	parent = find_directory_node (priv->config_tree, directory,
419 	                              (GEqualFunc) g_file_has_prefix);
420 
421 	/* Create node, move children of parent that
422 	 * could be children of this new node now.
423 	 */
424 	data = node_data_new (directory, flags);
425 	node = g_node_new (data);
426 
427 	g_node_children_foreach (parent, G_TRAVERSE_ALL,
428 	                         check_reparent_node, node);
429 
430 	/* Add the new node underneath the parent */
431 	g_node_append (parent, node);
432 
433 	g_signal_emit (tree, signals[DIRECTORY_ADDED], 0, directory);
434 
435 #ifdef PRINT_INDEXING_TREE
436 	/* Print tree */
437 	print_tree (priv->config_tree);
438 #endif /* PRINT_INDEXING_TREE */
439 }
440 
441 /**
442  * tracker_indexing_tree_remove:
443  * @tree: a #TrackerIndexingTree
444  * @directory: #GFile pointing to a directory
445  *
446  * Removes @directory from the indexing tree, note that
447  * only directories previously added with tracker_indexing_tree_add()
448  * can be effectively removed.
449  **/
450 void
451 tracker_indexing_tree_remove (TrackerIndexingTree *tree,
452                               GFile               *directory)
453 {
454 	TrackerIndexingTreePrivate *priv;
455 	GNode *node, *parent;
456 	NodeData *data;
457 
458 	g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
459 	g_return_if_fail (G_IS_FILE (directory));
460 
461 	priv = tree->priv;
462 	node = find_directory_node (priv->config_tree, directory,
463 	                            (GEqualFunc) g_file_equal);
464 	if (!node) {
465 		return;
466 	}
467 
468 	data = node->data;
469 
470 	if (!node->parent) {
471 		/* Node is the config tree
472 		 * root, mark as shallow again
473 		 */
474 		data->shallow = TRUE;
475 		return;
476 	}
477 
478 	g_signal_emit (tree, signals[DIRECTORY_REMOVED], 0, data->file);
479 
480 	parent = node->parent;
481 	g_node_unlink (node);
482 
483 	/* Move children to parent */
484 	g_node_children_foreach (node, G_TRAVERSE_ALL,
485 	                         check_reparent_node, parent);
486 
487 	node_data_free (node->data);
488 	g_node_destroy (node);
489 }
490 
491 /**
492  * tracker_indexing_tree_add_filter:
493  * @tree: a #TrackerIndexingTree
494  * @filter: filter type
495  * @glob_string: glob-style string for the filter
496  *
497  * Adds a new filter for basenames.
498  **/
499 void
500 tracker_indexing_tree_add_filter (TrackerIndexingTree *tree,
501                                   TrackerFilterType    filter,
502                                   const gchar         *glob_string)
503 {
504 	TrackerIndexingTreePrivate *priv;
505 	PatternData *data;
506 
507 	g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
508 	g_return_if_fail (glob_string != NULL);
509 
510 	priv = tree->priv;
511 
512 	data = pattern_data_new (glob_string, filter);
513 	priv->filter_patterns = g_list_prepend (priv->filter_patterns, data);
514 }
515 
516 /**
517  * tracker_indexing_tree_clear_filters:
518  * @tree: a #TrackerIndexingTree
519  * @type: filter type to clear
520  *
521  * Clears all filters of a given type.
522  **/
523 void
524 tracker_indexing_tree_clear_filters (TrackerIndexingTree *tree,
525                                      TrackerFilterType    type)
526 {
527 	TrackerIndexingTreePrivate *priv;
528 	GList *l;
529 
530 	g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
531 
532 	priv = tree->priv;
533 
534 	for (l = priv->filter_patterns; l; l = l->next) {
535 		PatternData *data = l->data;
536 
537 		if (data->type == type) {
538 			/* When we delete the link 'l', we point back
539 			 * to the beginning of the list to make sure
540 			 * we don't miss anything.
541 			 */
542 			l = priv->filter_patterns = g_list_delete_link (priv->filter_patterns, l);
543 			pattern_data_free (data);
544 		}
545 	}
546 }
547 
548 /**
549  * tracker_indexing_tree_file_matches_filter:
550  * @tree: a #TrackerIndexingTree
551  * @type: filter type
552  * @file: a #GFile
553  *
554  * Returns %TRUE if @file matches any filter of the given filter type.
555  *
556  * Returns: %TRUE if @file is filtered
557  **/
558 gboolean
559 tracker_indexing_tree_file_matches_filter (TrackerIndexingTree *tree,
560                                            TrackerFilterType    type,
561                                            GFile               *file)
562 {
563 	TrackerIndexingTreePrivate *priv;
564 	GList *filters;
565 	gchar *basename;
566 
567 	g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), FALSE);
568 	g_return_val_if_fail (G_IS_FILE (file), FALSE);
569 
570 	priv = tree->priv;
571 	filters = priv->filter_patterns;
572 	basename = g_file_get_basename (file);
573 
574 	while (filters) {
575 		PatternData *data = filters->data;
576 
577 		filters = filters->next;
578 
579 		if (data->type != type)
580 			continue;
581 
582 		if (data->file &&
583 		    (g_file_equal (file, data->file) ||
584 		     g_file_has_prefix (file, data->file))) {
585 			g_free (basename);
586 			return TRUE;
587 		}
588 
589 		if (g_pattern_match_string (data->pattern, basename)) {
590 			g_free (basename);
591 			return TRUE;
592 		}
593 	}
594 
595 	g_free (basename);
596 	return FALSE;
597 }
598 
599 static gboolean
600 parent_or_equals (GFile *file1,
601                   GFile *file2)
602 {
603 	return (file1 == file2 ||
604 	        g_file_equal (file1, file2) ||
605 	        g_file_has_prefix (file1, file2));
606 }
607 
608 static gboolean
609 indexing_tree_file_is_filtered (TrackerIndexingTree *tree,
610                                 TrackerFilterType    filter,
611                                 GFile               *file)
612 {
613 	TrackerIndexingTreePrivate *priv;
614 
615 	priv = tree->priv;
616 
617 	if (tracker_indexing_tree_file_matches_filter (tree, filter, file)) {
618 		if (priv->policies[filter] == TRACKER_FILTER_POLICY_ACCEPT) {
619 			/* Filter blocks otherwise accepted
620 			 * (by the default policy) file
621 			 */
622 			return TRUE;
623 		}
624 	} else {
625 		if (priv->policies[filter] == TRACKER_FILTER_POLICY_DENY) {
626 			/* No match, and the default policy denies it */
627 			return TRUE;
628 		}
629 	}
630 
631 	return FALSE;
632 }
633 
634 /**
635  * tracker_indexing_tree_file_is_indexable:
636  * @tree: a #TrackerIndexingTree
637  * @file: a #GFile
638  * @file_type: a #GFileType
639  *
640  * returns %TRUE if @file should be indexed according to the
641  * parameters given through tracker_indexing_tree_add() and
642  * tracker_indexing_tree_add_filter().
643  *
644  * If @file_type is #G_FILE_TYPE_UNKNOWN, file type will be queried to the
645  * file system.
646  *
647  * Returns: %TRUE if @file should be indexed.
648  **/
649 gboolean
650 tracker_indexing_tree_file_is_indexable (TrackerIndexingTree *tree,
651                                          GFile               *file,
652                                          GFileType            file_type)
653 {
654 	TrackerFilterType filter;
655 	TrackerDirectoryFlags config_flags;
656 	GFile *config_file;
657 
658 	g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), FALSE);
659 	g_return_val_if_fail (G_IS_FILE (file), FALSE);
660 
661 	if (file_type == G_FILE_TYPE_UNKNOWN)
662 		file_type = g_file_query_file_type (file,
663 		                                    G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
664 		                                    NULL);
665 
666 	filter = (file_type == G_FILE_TYPE_DIRECTORY) ?
667 		TRACKER_FILTER_DIRECTORY : TRACKER_FILTER_FILE;
668 
669 	if (indexing_tree_file_is_filtered (tree, filter, file)) {
670 		return FALSE;
671 	}
672 
673 	config_file = tracker_indexing_tree_get_root (tree,file, &config_flags);
674 	if (!config_file) {
675 		/* Not under an added dir */
676 		return FALSE;
677 	}
678 
679 	if (config_flags & TRACKER_DIRECTORY_FLAG_IGNORE) {
680 		return FALSE;
681 	}
682 
683 	if (g_file_equal (file, config_file)) {
684 		return TRUE;
685 	} else {
686 		if ((config_flags & TRACKER_DIRECTORY_FLAG_RECURSE) == 0 &&
687 		    !g_file_has_parent (file, config_file)) {
688 			/* Non direct child in a non-recursive dir, ignore */
689 			return FALSE;
690 		}
691 
692 		if (tracker_indexing_tree_get_filter_hidden (tree) &&
693 		    tracker_file_is_hidden (file)) {
694 			return FALSE;
695 		}
696 
697 		return TRUE;
698 	}
699 }
700 
701 /**
702  * tracker_indexing_tree_parent_is_indexable:
703  * @tree: a #TrackerIndexingTree
704  * @parent: parent directory
705  * @children: (element-type GFile): children within @parent
706  *
707  * returns %TRUE if @parent should be indexed based on its contents.
708  *
709  * Returns: %TRUE if @parent should be indexed.
710  **/
711 gboolean
712 tracker_indexing_tree_parent_is_indexable (TrackerIndexingTree *tree,
713                                            GFile               *parent,
714                                            GList               *children)
715 {
716 	if (!tracker_indexing_tree_file_is_indexable (tree,
717 	                                              parent,
718 	                                              G_FILE_TYPE_DIRECTORY)) {
719 		return FALSE;
720 	}
721 
722 	while (children) {
723 		if (indexing_tree_file_is_filtered (tree,
724 		                                    TRACKER_FILTER_PARENT_DIRECTORY,
725 		                                    children->data)) {
726 			return FALSE;
727 		}
728 
729 		children = children->next;
730 	}
731 
732 	return TRUE;
733 }
734 
735 gboolean
736 tracker_indexing_tree_get_filter_hidden (TrackerIndexingTree *tree)
737 {
738 	TrackerIndexingTreePrivate *priv;
739 
740 	g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), FALSE);
741 
742 	priv = tree->priv;
743 	return priv->filter_hidden;
744 }
745 
746 void
747 tracker_indexing_tree_set_filter_hidden (TrackerIndexingTree *tree,
748                                          gboolean             filter_hidden)
749 {
750 	TrackerIndexingTreePrivate *priv;
751 
752 	g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
753 
754 	priv = tree->priv;
755 	priv->filter_hidden = filter_hidden;
756 
757 	g_object_notify (G_OBJECT (tree), "filter-hidden");
758 }
759 
760 void
761 tracker_indexing_tree_set_default_policy (TrackerIndexingTree *tree,
762                                           TrackerFilterType    filter,
763                                           TrackerFilterPolicy  policy)
764 {
765 	TrackerIndexingTreePrivate *priv;
766 
767 	g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
768 	g_return_if_fail (filter >= TRACKER_FILTER_FILE && filter <= TRACKER_FILTER_PARENT_DIRECTORY);
769 
770 	priv = tree->priv;
771 	priv->policies[filter] = policy;
772 }
773 
774 TrackerFilterPolicy
775 tracker_indexing_tree_get_default_policy (TrackerIndexingTree *tree,
776                                           TrackerFilterType    filter)
777 {
778 	TrackerIndexingTreePrivate *priv;
779 
780 	g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree),
781 	                      TRACKER_FILTER_POLICY_DENY);
782 	g_return_val_if_fail (filter >= TRACKER_FILTER_FILE &&
783 	                      filter <= TRACKER_FILTER_PARENT_DIRECTORY,
784 	                      TRACKER_FILTER_POLICY_DENY);
785 
786 	priv = tree->priv;
787 	return priv->policies[filter];
788 }
789 
790 /**
791  * tracker_indexing_tree_get_root:
792  * @tree: a #TrackerIndexingTree
793  * @file: a #GFile
794  * @directory_flags: (out): return location for the applying #TrackerDirectoryFlags
795  *
796  * Returns the #GFile that was previously added through tracker_indexing_tree_add()
797  * and would equal or contain @file, or %NULL if none applies.
798  *
799  * If the return value is non-%NULL, @directory_flags would contain the
800  * #TrackerDirectoryFlags applying to @file.
801  *
802  * Returns: (transfer none): the effective parent in @tree, or %NULL
803  **/
804 GFile *
805 tracker_indexing_tree_get_root (TrackerIndexingTree   *tree,
806                                 GFile                 *file,
807                                 TrackerDirectoryFlags *directory_flags)
808 {
809 	TrackerIndexingTreePrivate *priv;
810 	NodeData *data;
811 	GNode *parent;
812 
813 	if (directory_flags) {
814 		*directory_flags = TRACKER_DIRECTORY_FLAG_NONE;
815 	}
816 
817 	g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), NULL);
818 	g_return_val_if_fail (G_IS_FILE (file), NULL);
819 
820 	priv = tree->priv;
821 	parent = find_directory_node (priv->config_tree, file,
822 	                              (GEqualFunc) parent_or_equals);
823 	if (!parent) {
824 		return NULL;
825 	}
826 
827 	data = parent->data;
828 
829 	if (!data->shallow &&
830 	    (file == data->file ||
831 	     g_file_equal (file, data->file) ||
832 	     g_file_has_prefix (file, data->file))) {
833 		if (directory_flags) {
834 			*directory_flags = data->flags;
835 		}
836 
837 		return data->file;
838 	}
839 
840 	return NULL;
841 }
842 
843 gboolean
844 tracker_indexing_tree_file_is_root (TrackerIndexingTree *tree,
845                                     GFile               *file)
846 {
847 	TrackerIndexingTreePrivate *priv;
848 	GNode *node;
849 
850 	g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), FALSE);
851 	g_return_val_if_fail (G_IS_FILE (file), FALSE);
852 
853 	priv = tree->priv;
854 	node = find_directory_node (priv->config_tree, file,
855 	                            (GEqualFunc) g_file_equal);
856 	return node != NULL;
857 }
858 
859 static gboolean
860 prepend_config_root (GNode    *node,
861                      gpointer  user_data)
862 {
863 	GList **list = user_data;
864 	NodeData *data = node->data;
865 
866 	*list = g_list_prepend (*list, data->file);
867 	return FALSE;
868 }
869 
870 /**
871  * tracker_indexing_tree_list_roots:
872  * @tree: a #TrackerIndexingTree
873  *
874  * Returns the list of indexing roots in @tree
875  *
876  * Returns: (transfer container) (element-type GFile): The list
877  *          of roots, the list itself must be freed with g_list_free(),
878  *          the list elements are owned by @tree and should not be
879  *          freed.
880  **/
881 GList *
882 tracker_indexing_tree_list_roots (TrackerIndexingTree *tree)
883 {
884 	TrackerIndexingTreePrivate *priv;
885 	GList *nodes = NULL;
886 
887 	g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), NULL);
888 
889 	priv = tree->priv;
890 	g_node_traverse (priv->config_tree,
891 	                 G_POST_ORDER,
892 	                 G_TRAVERSE_ALL,
893 	                 -1,
894 	                 prepend_config_root,
895 	                 &nodes);
896 	return nodes;
897 }