No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | tracker-miner-files.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * Copyright (C) 2008, 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 General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 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 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU 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
20 #include "config.h"
21
22 #include <sys/statvfs.h>
23 #include <fcntl.h>
24 #ifdef __linux__
25 #include <sys/ioctl.h>
26 #include <linux/msdos_fs.h>
27 #endif /* __linux__ */
28 #include <unistd.h>
29
30 #include <glib/gi18n.h>
31 #include <glib/gstdio.h>
32
33 #include <gio/gio.h>
34 #include <gio/gunixfdlist.h>
35 #include <gio/gunixinputstream.h>
36
37 #include <libtracker-common/tracker-date-time.h>
38 #include <libtracker-common/tracker-ontologies.h>
39 #include <libtracker-common/tracker-type-utils.h>
40 #include <libtracker-common/tracker-utils.h>
41 #include <libtracker-common/tracker-file-utils.h>
42
43 #include <libtracker-data/tracker-db-manager.h>
44
45 #include <libtracker-extract/tracker-module-manager.h>
46 #include <libtracker-extract/tracker-extract-client.h>
47
48 #include "tracker-power.h"
49 #include "tracker-miner-files.h"
50 #include "tracker-config.h"
51 #include "tracker-marshal.h"
52
53 #define DISK_SPACE_CHECK_FREQUENCY 10
54 #define SECONDS_PER_DAY 86400
55
56 #define TRACKER_MINER_FILES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_MINER_FILES, TrackerMinerFilesPrivate))
57
58 static GQuark miner_files_error_quark = 0;
59
60 typedef struct ProcessFileData ProcessFileData;
61
62 struct ProcessFileData {
63 TrackerMinerFiles *miner;
64 TrackerSparqlBuilder *sparql;
65 GCancellable *cancellable;
66 GFile *file;
67 gchar *mime_type;
68 };
69
70 struct TrackerMinerFilesPrivate {
71 TrackerConfig *config;
72 TrackerStorage *storage;
73
74 GVolumeMonitor *volume_monitor;
75
76 GSList *index_recursive_directories;
77 GSList *index_single_directories;
78
79 guint disk_space_check_id;
80 guint disk_space_pause_cookie;
81
82 guint low_battery_pause_cookie;
83
84 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
85 TrackerPower *power;
86 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
87 gulong finished_handler;
88
89 GDBusConnection *connection;
90
91 GQuark quark_mount_point_uuid;
92
93 guint force_recheck_id;
94
95 gboolean index_removable_devices;
96 gboolean index_optical_discs;
97 guint volumes_changed_id;
98
99 gboolean mount_points_initialized;
100
101 guint stale_volumes_check_id;
102
103 guint failed_extraction_pause_cookie;
104 GList *extraction_queue;
105 GList *failed_extraction_queue;
106
107 gboolean failsafe_extraction;
108 };
109
110 enum {
111 VOLUME_MOUNTED_IN_STORE = 1 << 0,
112 VOLUME_MOUNTED = 1 << 1
113 };
114
115 enum {
116 PROP_0,
117 PROP_CONFIG
118 };
119
120 static void miner_files_set_property (GObject *object,
121 guint param_id,
122 const GValue *value,
123 GParamSpec *pspec);
124 static void miner_files_get_property (GObject *object,
125 guint param_id,
126 GValue *value,
127 GParamSpec *pspec);
128 static void miner_files_finalize (GObject *object);
129 static void miner_files_initable_iface_init (GInitableIface *iface);
130 static gboolean miner_files_initable_init (GInitable *initable,
131 GCancellable *cancellable,
132 GError **error);
133 static void mount_pre_unmount_cb (GVolumeMonitor *volume_monitor,
134 GMount *mount,
135 TrackerMinerFiles *mf);
136
137 static void mount_point_added_cb (TrackerStorage *storage,
138 const gchar *uuid,
139 const gchar *mount_point,
140 const gchar *mount_name,
141 gboolean removable,
142 gboolean optical,
143 gpointer user_data);
144 static void mount_point_removed_cb (TrackerStorage *storage,
145 const gchar *uuid,
146 const gchar *mount_point,
147 gpointer user_data);
148 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
149 static void check_battery_status (TrackerMinerFiles *fs);
150 static void battery_status_cb (GObject *object,
151 GParamSpec *pspec,
152 gpointer user_data);
153 static void index_on_battery_cb (GObject *object,
154 GParamSpec *pspec,
155 gpointer user_data);
156 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
157 static void init_mount_points (TrackerMinerFiles *miner);
158 static void init_stale_volume_removal (TrackerMinerFiles *miner);
159 static void disk_space_check_start (TrackerMinerFiles *mf);
160 static void disk_space_check_stop (TrackerMinerFiles *mf);
161 static void low_disk_space_limit_cb (GObject *gobject,
162 GParamSpec *arg1,
163 gpointer user_data);
164 static void index_recursive_directories_cb (GObject *gobject,
165 GParamSpec *arg1,
166 gpointer user_data);
167 static void index_single_directories_cb (GObject *gobject,
168 GParamSpec *arg1,
169 gpointer user_data);
170 static gboolean miner_files_force_recheck_idle (gpointer user_data);
171 static void trigger_recheck_cb (GObject *gobject,
172 GParamSpec *arg1,
173 gpointer user_data);
174 static void index_volumes_changed_cb (GObject *gobject,
175 GParamSpec *arg1,
176 gpointer user_data);
177 static gboolean miner_files_process_file (TrackerMinerFS *fs,
178 GFile *file,
179 TrackerSparqlBuilder *sparql,
180 GCancellable *cancellable);
181 static gboolean miner_files_process_file_attributes (TrackerMinerFS *fs,
182 GFile *file,
183 TrackerSparqlBuilder *sparql,
184 GCancellable *cancellable);
185 static gboolean miner_files_ignore_next_update_file (TrackerMinerFS *fs,
186 GFile *file,
187 TrackerSparqlBuilder *sparql,
188 GCancellable *cancellable);
189 static void miner_files_finished (TrackerMinerFS *fs);
190
191 static void miner_finished_cb (TrackerMinerFS *fs,
192 gdouble seconds_elapsed,
193 guint total_directories_found,
194 guint total_directories_ignored,
195 guint total_files_found,
196 guint total_files_ignored,
197 gpointer user_data);
198
199 static gboolean miner_files_in_removable_media_remove_by_type (TrackerMinerFiles *miner,
200 TrackerStorageType type);
201 static void miner_files_in_removable_media_remove_by_date (TrackerMinerFiles *miner,
202 const gchar *date);
203
204 static void miner_files_add_removable_or_optical_directory (TrackerMinerFiles *mf,
205 const gchar *mount_path,
206 const gchar *uuid);
207
208 static void extractor_process_failsafe (TrackerMinerFiles *miner);
209
210 static void miner_files_update_filters (TrackerMinerFiles *files);
211
212
213 static GInitableIface* miner_files_initable_parent_iface;
214
215 G_DEFINE_TYPE_WITH_CODE (TrackerMinerFiles, tracker_miner_files, TRACKER_TYPE_MINER_FS,
216 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
217 miner_files_initable_iface_init));
218
219 static void
220 tracker_miner_files_class_init (TrackerMinerFilesClass *klass)
221 {
222 GObjectClass *object_class = G_OBJECT_CLASS (klass);
223 TrackerMinerFSClass *miner_fs_class = TRACKER_MINER_FS_CLASS (klass);
224
225 object_class->finalize = miner_files_finalize;
226 object_class->get_property = miner_files_get_property;
227 object_class->set_property = miner_files_set_property;
228
229 miner_fs_class->process_file = miner_files_process_file;
230 miner_fs_class->process_file_attributes = miner_files_process_file_attributes;
231 miner_fs_class->ignore_next_update_file = miner_files_ignore_next_update_file;
232 miner_fs_class->finished = miner_files_finished;
233
234 g_object_class_install_property (object_class,
235 PROP_CONFIG,
236 g_param_spec_object ("config",
237 "Config",
238 "Config",
239 TRACKER_TYPE_CONFIG,
240 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
241
242 g_type_class_add_private (klass, sizeof (TrackerMinerFilesPrivate));
243
244 miner_files_error_quark = g_quark_from_static_string ("TrackerMinerFiles");
245 }
246
247 static void
248 tracker_miner_files_init (TrackerMinerFiles *mf)
249 {
250 TrackerMinerFilesPrivate *priv;
251
252 priv = mf->private = TRACKER_MINER_FILES_GET_PRIVATE (mf);
253
254 priv->storage = tracker_storage_new ();
255
256 g_signal_connect (priv->storage, "mount-point-added",
257 G_CALLBACK (mount_point_added_cb),
258 mf);
259
260 g_signal_connect (priv->storage, "mount-point-removed",
261 G_CALLBACK (mount_point_removed_cb),
262 mf);
263
264 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
265 priv->power = tracker_power_new ();
266
267 g_signal_connect (priv->power, "notify::on-low-battery",
268 G_CALLBACK (battery_status_cb),
269 mf);
270 g_signal_connect (priv->power, "notify::on-battery",
271 G_CALLBACK (battery_status_cb),
272 mf);
273 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
274
275 priv->finished_handler = g_signal_connect_after (mf, "finished",
276 G_CALLBACK (miner_finished_cb),
277 NULL);
278
279 priv->volume_monitor = g_volume_monitor_get ();
280 g_signal_connect (priv->volume_monitor, "mount-pre-unmount",
281 G_CALLBACK (mount_pre_unmount_cb),
282 mf);
283
284 priv->quark_mount_point_uuid = g_quark_from_static_string ("tracker-mount-point-uuid");
285 }
286
287 static void
288 miner_files_initable_iface_init (GInitableIface *iface)
289 {
290 miner_files_initable_parent_iface = g_type_interface_peek_parent (iface);
291 iface->init = miner_files_initable_init;
292 }
293
294 static gboolean
295 miner_files_initable_init (GInitable *initable,
296 GCancellable *cancellable,
297 GError **error)
298 {
299 TrackerMinerFiles *mf;
300 TrackerMinerFS *fs;
301 TrackerIndexingTree *indexing_tree;
302 TrackerDirectoryFlags flags;
303 GError *inner_error = NULL;
304 GSList *mounts = NULL;
305 GSList *dirs;
306 GSList *m;
307
308 mf = TRACKER_MINER_FILES (initable);
309 fs = TRACKER_MINER_FS (initable);
310 indexing_tree = tracker_miner_fs_get_indexing_tree (fs);
311 tracker_indexing_tree_set_filter_hidden (indexing_tree, TRUE);
312
313 miner_files_update_filters (mf);
314
315 /* Chain up parent's initable callback before calling child's one */
316 if (!miner_files_initable_parent_iface->init (initable, cancellable, &inner_error)) {
317 g_propagate_error (error, inner_error);
318 return FALSE;
319 }
320
321 /* Set up extractor and signals */
322 mf->private->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &inner_error);
323 if (!mf->private->connection) {
324 g_propagate_error (error, inner_error);
325 g_prefix_error (error,
326 "Could not connect to the D-Bus session bus. ");
327 return FALSE;
328 }
329
330 /* Setup mount points */
331 init_mount_points (mf);
332
333 /* We must have a configuration setup here */
334 if (G_UNLIKELY (!mf->private->config)) {
335 g_set_error (error,
336 TRACKER_MINER_ERROR,
337 0,
338 "No config set for miner %s",
339 G_OBJECT_TYPE_NAME (mf));
340 return FALSE;
341 }
342
343 /* If this happened AFTER we have initialized mount points, initialize
344 * stale volume removal now. */
345 if (mf->private->mount_points_initialized) {
346 init_stale_volume_removal (mf);
347 }
348
349 /* Setup initial flag for removable devices */
350 mf->private->index_removable_devices = tracker_config_get_index_removable_devices (mf->private->config);
351 if (mf->private->index_removable_devices) {
352 /* Get list of roots for removable devices (excluding optical) */
353 mounts = tracker_storage_get_device_roots (mf->private->storage,
354 TRACKER_STORAGE_REMOVABLE,
355 TRUE);
356 }
357
358 /* Setup initial flag for optical discs. Note that if removable devices not indexed,
359 * optical discs will also never be indexed */
360 mf->private->index_optical_discs = (mf->private->index_removable_devices ?
361 tracker_config_get_index_optical_discs (mf->private->config) :
362 FALSE);
363 if (mf->private->index_optical_discs) {
364 /* Get list of roots for removable+optical devices */
365 m = tracker_storage_get_device_roots (mf->private->storage,
366 TRACKER_STORAGE_OPTICAL | TRACKER_STORAGE_REMOVABLE,
367 TRUE);
368 mounts = g_slist_concat (mounts, m);
369 }
370
371 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
372 check_battery_status (mf);
373 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
374
375 g_message ("Setting up directories to iterate from config (IndexSingleDirectory)");
376
377 /* Fill in directories to inspect */
378 dirs = tracker_config_get_index_single_directories (mf->private->config);
379
380 /* Copy in case of config changes */
381 mf->private->index_single_directories = tracker_gslist_copy_with_string_data (dirs);
382
383 for (; dirs; dirs = dirs->next) {
384 GFile *file;
385
386 /* Do some simple checks for silly locations */
387 if (strcmp (dirs->data, "/dev") == 0 ||
388 strcmp (dirs->data, "/lib") == 0 ||
389 strcmp (dirs->data, "/proc") == 0 ||
390 strcmp (dirs->data, "/sys") == 0) {
391 continue;
392 }
393
394 if (g_str_has_prefix (dirs->data, g_get_tmp_dir ())) {
395 continue;
396 }
397
398 /* Make sure we don't crawl volumes. */
399 if (mounts) {
400 gboolean found = FALSE;
401
402 for (m = mounts; m && !found; m = m->next) {
403 found = strcmp (m->data, dirs->data) == 0;
404 }
405
406 if (found) {
407 g_message (" Duplicate found:'%s' - same as removable device path",
408 (gchar*) dirs->data);
409 continue;
410 }
411 }
412
413 g_message (" Adding:'%s'", (gchar*) dirs->data);
414
415 file = g_file_new_for_path (dirs->data);
416
417 flags = TRACKER_DIRECTORY_FLAG_NONE;
418
419 if (tracker_config_get_enable_monitors (mf->private->config)) {
420 flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
421 }
422
423 if (tracker_miner_fs_get_mtime_checking (TRACKER_MINER_FS (mf))) {
424 flags |= TRACKER_DIRECTORY_FLAG_CHECK_MTIME;
425 }
426
427 tracker_indexing_tree_add (indexing_tree, file, flags);
428 g_object_unref (file);
429 }
430
431 g_message ("Setting up directories to iterate from config (IndexRecursiveDirectory)");
432
433 dirs = tracker_config_get_index_recursive_directories (mf->private->config);
434
435 /* Copy in case of config changes */
436 mf->private->index_recursive_directories = tracker_gslist_copy_with_string_data (dirs);
437
438 for (; dirs; dirs = dirs->next) {
439 GFile *file;
440
441 /* Do some simple checks for silly locations */
442 if (strcmp (dirs->data, "/dev") == 0 ||
443 strcmp (dirs->data, "/lib") == 0 ||
444 strcmp (dirs->data, "/proc") == 0 ||
445 strcmp (dirs->data, "/sys") == 0) {
446 continue;
447 }
448
449 if (g_str_has_prefix (dirs->data, g_get_tmp_dir ())) {
450 continue;
451 }
452
453 /* Make sure we don't crawl volumes. */
454 if (mounts) {
455 gboolean found = FALSE;
456
457 for (m = mounts; m && !found; m = m->next) {
458 found = strcmp (m->data, dirs->data) == 0;
459 }
460
461 if (found) {
462 g_message (" Duplicate found:'%s' - same as removable device path",
463 (gchar*) dirs->data);
464 continue;
465 }
466 }
467
468 g_message (" Adding:'%s'", (gchar*) dirs->data);
469
470 file = g_file_new_for_path (dirs->data);
471
472 flags = TRACKER_DIRECTORY_FLAG_RECURSE;
473
474 if (tracker_config_get_enable_monitors (mf->private->config)) {
475 flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
476 }
477
478 if (tracker_miner_fs_get_mtime_checking (TRACKER_MINER_FS (mf))) {
479 flags |= TRACKER_DIRECTORY_FLAG_CHECK_MTIME;
480 }
481
482 tracker_indexing_tree_add (indexing_tree, file, flags);
483 g_object_unref (file);
484 }
485
486 /* Add mounts */
487 g_message ("Setting up directories to iterate from devices/discs");
488
489 if (!mf->private->index_removable_devices) {
490 g_message (" Removable devices are disabled in the config");
491
492 /* Make sure we don't have any resource in a volume of the given type */
493 miner_files_in_removable_media_remove_by_type (mf, TRACKER_STORAGE_REMOVABLE);
494 }
495
496 if (!mf->private->index_optical_discs) {
497 g_message (" Optical discs are disabled in the config");
498
499 /* Make sure we don't have any resource in a volume of the given type */
500 miner_files_in_removable_media_remove_by_type (mf, TRACKER_STORAGE_REMOVABLE | TRACKER_STORAGE_OPTICAL);
501 }
502
503 for (m = mounts; m; m = m->next) {
504 miner_files_add_removable_or_optical_directory (mf,
505 (gchar *) m->data,
506 NULL);
507 }
508
509 /* We want to get notified when config changes */
510
511 g_signal_connect (mf->private->config, "notify::low-disk-space-limit",
512 G_CALLBACK (low_disk_space_limit_cb),
513 mf);
514 g_signal_connect (mf->private->config, "notify::index-recursive-directories",
515 G_CALLBACK (index_recursive_directories_cb),
516 mf);
517 g_signal_connect (mf->private->config, "notify::index-single-directories",
518 G_CALLBACK (index_single_directories_cb),
519 mf);
520 g_signal_connect (mf->private->config, "notify::ignored-directories",
521 G_CALLBACK (trigger_recheck_cb),
522 mf);
523 g_signal_connect (mf->private->config, "notify::ignored-directories-with-content",
524 G_CALLBACK (trigger_recheck_cb),
525 mf);
526 g_signal_connect (mf->private->config, "notify::ignored-files",
527 G_CALLBACK (trigger_recheck_cb),
528 mf);
529 g_signal_connect (mf->private->config, "notify::index-removable-devices",
530 G_CALLBACK (index_volumes_changed_cb),
531 mf);
532 g_signal_connect (mf->private->config, "notify::index-optical-discs",
533 G_CALLBACK (index_volumes_changed_cb),
534 mf);
535 g_signal_connect (mf->private->config, "notify::removable-days-threshold",
536 G_CALLBACK (index_volumes_changed_cb),
537 mf);
538
539 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
540
541 g_signal_connect (mf->private->config, "notify::index-on-battery",
542 G_CALLBACK (index_on_battery_cb),
543 mf);
544 g_signal_connect (mf->private->config, "notify::index-on-battery-first-time",
545 G_CALLBACK (index_on_battery_cb),
546 mf);
547
548 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
549
550 g_slist_foreach (mounts, (GFunc) g_free, NULL);
551 g_slist_free (mounts);
552
553 disk_space_check_start (mf);
554
555 return TRUE;
556 }
557
558 static void
559 miner_files_set_property (GObject *object,
560 guint prop_id,
561 const GValue *value,
562 GParamSpec *pspec)
563 {
564 TrackerMinerFilesPrivate *priv;
565
566 priv = TRACKER_MINER_FILES_GET_PRIVATE (object);
567
568 switch (prop_id) {
569 case PROP_CONFIG:
570 priv->config = g_value_dup_object (value);
571 break;
572 default:
573 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
574 break;
575 }
576 }
577
578 static void
579 miner_files_get_property (GObject *object,
580 guint prop_id,
581 GValue *value,
582 GParamSpec *pspec)
583 {
584 TrackerMinerFilesPrivate *priv;
585
586 priv = TRACKER_MINER_FILES_GET_PRIVATE (object);
587
588 switch (prop_id) {
589 case PROP_CONFIG:
590 g_value_set_object (value, priv->config);
591 break;
592 default:
593 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
594 break;
595 }
596 }
597
598 static void
599 miner_files_finalize (GObject *object)
600 {
601 TrackerMinerFiles *mf;
602 TrackerMinerFilesPrivate *priv;
603
604 mf = TRACKER_MINER_FILES (object);
605 priv = mf->private;
606
607 if (priv->config) {
608 g_signal_handlers_disconnect_by_func (priv->config,
609 low_disk_space_limit_cb,
610 NULL);
611 g_object_unref (priv->config);
612 }
613
614 disk_space_check_stop (TRACKER_MINER_FILES (object));
615
616 if (priv->index_recursive_directories) {
617 g_slist_foreach (priv->index_recursive_directories, (GFunc) g_free, NULL);
618 g_slist_free (priv->index_recursive_directories);
619 }
620
621 if (priv->index_single_directories) {
622 g_slist_foreach (priv->index_single_directories, (GFunc) g_free, NULL);
623 g_slist_free (priv->index_single_directories);
624 }
625
626 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
627 if (priv->power) {
628 g_object_unref (priv->power);
629 }
630 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
631
632 if (priv->storage) {
633 g_object_unref (priv->storage);
634 }
635
636 if (priv->volume_monitor) {
637 g_signal_handlers_disconnect_by_func (priv->volume_monitor,
638 mount_pre_unmount_cb,
639 object);
640 g_object_unref (priv->volume_monitor);
641 }
642
643 if (priv->force_recheck_id) {
644 g_source_remove (priv->force_recheck_id);
645 priv->force_recheck_id = 0;
646 }
647
648 if (priv->stale_volumes_check_id) {
649 g_source_remove (priv->stale_volumes_check_id);
650 priv->stale_volumes_check_id = 0;
651 }
652
653 g_list_free (priv->extraction_queue);
654 g_list_free (priv->failed_extraction_queue);
655
656 G_OBJECT_CLASS (tracker_miner_files_parent_class)->finalize (object);
657 }
658
659 static void
660 ensure_mount_point_exists (TrackerMinerFiles *miner,
661 GFile *mount_point,
662 GString *accumulator)
663 {
664 gchar *iri;
665 gchar *uri;
666
667 uri = g_file_get_uri (mount_point);
668
669 /* Query the store for the URN of the mount point */
670 iri = tracker_miner_fs_query_urn (TRACKER_MINER_FS (miner),
671 mount_point);
672
673 if (iri) {
674 /* If exists, just return, nothing else to do */
675 g_message ("Mount point '%s' already exists in store: '%s'",
676 uri, iri);
677 g_free (iri);
678 } else {
679 /* If it doesn't exist, we need to create it */
680 g_message ("Mount point '%s' does not exist in store, need to create it",
681 uri);
682
683 /* Create a nfo:Folder for the mount point */
684 g_string_append_printf (accumulator,
685 "INSERT SILENT INTO <" TRACKER_MINER_FS_GRAPH_URN "> {"
686 " _:file a nfo:FileDataObject, nie:InformationElement, nfo:Folder ; "
687 " nie:isStoredAs _:file ; "
688 " nie:url \"%s\" ; "
689 " nie:mimeType \"inode/directory\" ; "
690 " nfo:fileLastModified \"1981-06-05T02:20:00Z\" . "
691 "}",
692 uri);
693 }
694
695 g_free (uri);
696 }
697
698 static void
699 set_up_mount_point_cb (GObject *source,
700 GAsyncResult *result,
701 gpointer user_data)
702 {
703 TrackerSparqlConnection *connection = TRACKER_SPARQL_CONNECTION (source);
704 gchar *removable_device_urn = user_data;
705 GError *error = NULL;
706
707 tracker_sparql_connection_update_finish (connection, result, &error);
708
709 if (error) {
710 g_critical ("Could not set mount point in database '%s', %s",
711 removable_device_urn,
712 error->message);
713 g_error_free (error);
714 }
715
716 g_free (removable_device_urn);
717 }
718
719 static void
720 set_up_mount_point_type (TrackerMinerFiles *miner,
721 const gchar *removable_device_urn,
722 gboolean removable,
723 gboolean optical,
724 GString *accumulator)
725 {
726 if (!accumulator) {
727 return;
728 }
729
730 g_debug ("Mount point type being set in DB for URN '%s'",
731 removable_device_urn);
732
733 g_string_append_printf (accumulator,
734 "DELETE { <%s> tracker:isRemovable ?unknown } WHERE { <%s> a tracker:Volume; tracker:isRemovable ?unknown } ",
735 removable_device_urn, removable_device_urn);
736
737 g_string_append_printf (accumulator,
738 "INSERT INTO <%s> { <%s> a tracker:Volume; tracker:isRemovable %s } ",
739 removable_device_urn, removable_device_urn, removable ? "true" : "false");
740
741 g_string_append_printf (accumulator,
742 "DELETE { <%s> tracker:isOptical ?unknown } WHERE { <%s> a tracker:Volume; tracker:isOptical ?unknown } ",
743 removable_device_urn, removable_device_urn);
744
745 g_string_append_printf (accumulator,
746 "INSERT INTO <%s> { <%s> a tracker:Volume; tracker:isOptical %s } ",
747 removable_device_urn, removable_device_urn, optical ? "true" : "false");
748 }
749
750 static void
751 set_up_mount_point (TrackerMinerFiles *miner,
752 const gchar *removable_device_urn,
753 const gchar *mount_point,
754 const gchar *mount_name,
755 gboolean mounted,
756 GString *accumulator)
757 {
758 GString *queries;
759
760 queries = g_string_new (NULL);
761
762 if (mounted) {
763 g_debug ("Mount point state (MOUNTED) being set in DB for URN '%s' (mount_point: %s)",
764 removable_device_urn,
765 mount_point ? mount_point : "unknown");
766
767 if (mount_point) {
768 GFile *file;
769 gchar *uri;
770
771 file = g_file_new_for_path (mount_point);
772 uri = g_file_get_uri (file);
773
774 /* Before assigning a nfo:FileDataObject as tracker:mountPoint for
775 * the volume, make sure the nfo:FileDataObject exists in the store */
776 ensure_mount_point_exists (miner, file, queries);
777
778 g_string_append_printf (queries,
779 "DELETE { "
780 " <%s> tracker:mountPoint ?u "
781 "} WHERE { "
782 " ?u a nfo:FileDataObject; "
783 " nie:url \"%s\" "
784 "} ",
785 removable_device_urn, uri);
786
787 g_string_append_printf (queries,
788 "DELETE { <%s> a rdfs:Resource } "
789 "INSERT { "
790 " <%s> a tracker:Volume; "
791 " tracker:mountPoint ?u "
792 "} WHERE { "
793 " ?u a nfo:FileDataObject; "
794 " nie:url \"%s\" "
795 "} ",
796 removable_device_urn, removable_device_urn, uri);
797
798 g_object_unref (file);
799 g_free (uri);
800 }
801
802 g_string_append_printf (queries,
803 "DELETE { <%s> tracker:isMounted ?unknown } WHERE { <%s> a tracker:Volume; tracker:isMounted ?unknown } ",
804 removable_device_urn, removable_device_urn);
805
806 if (mount_name) {
807 g_string_append_printf (queries,
808 "INSERT INTO <%s> { <%s> a tracker:Volume; tracker:isMounted true; nie:title \"%s\" } ",
809 removable_device_urn, removable_device_urn, mount_name);
810 } else {
811 g_string_append_printf (queries,
812 "INSERT INTO <%s> { <%s> a tracker:Volume; tracker:isMounted true } ",
813 removable_device_urn, removable_device_urn);
814 }
815
816 g_string_append_printf (queries,
817 "INSERT { GRAPH <%s> { ?do tracker:available true } } WHERE { ?do nie:dataSource <%s> } ",
818 removable_device_urn, removable_device_urn);
819 } else {
820 gchar *now;
821
822 g_debug ("Mount point state (UNMOUNTED) being set in DB for URN '%s'",
823 removable_device_urn);
824
825 now = tracker_date_to_string (time (NULL));
826
827 g_string_append_printf (queries,
828 "DELETE { <%s> tracker:unmountDate ?unknown } WHERE { <%s> a tracker:Volume; tracker:unmountDate ?unknown } ",
829 removable_device_urn, removable_device_urn);
830
831 g_string_append_printf (queries,
832 "INSERT INTO <%s> { <%s> a tracker:Volume; tracker:unmountDate \"%s\" } ",
833 removable_device_urn, removable_device_urn, now);
834
835 g_string_append_printf (queries,
836 "DELETE { <%s> tracker:isMounted ?unknown } WHERE { <%s> a tracker:Volume; tracker:isMounted ?unknown } ",
837 removable_device_urn, removable_device_urn);
838
839 g_string_append_printf (queries,
840 "INSERT INTO <%s> { <%s> a tracker:Volume; tracker:isMounted false } ",
841 removable_device_urn, removable_device_urn);
842
843 g_string_append_printf (queries,
844 "DELETE { ?do tracker:available true } WHERE { ?do nie:dataSource <%s> } ",
845 removable_device_urn);
846
847 g_free (now);
848 }
849
850 if (accumulator) {
851 g_string_append_printf (accumulator, "%s ", queries->str);
852 } else {
853 tracker_sparql_connection_update_async (tracker_miner_get_connection (TRACKER_MINER (miner)),
854 queries->str,
855 G_PRIORITY_LOW,
856 NULL,
857 set_up_mount_point_cb,
858 g_strdup (removable_device_urn));
859 }
860
861 g_string_free (queries, TRUE);
862 }
863
864 static void
865 init_mount_points_cb (GObject *source,
866 GAsyncResult *result,
867 gpointer user_data)
868 {
869 GError *error = NULL;
870
871 tracker_sparql_connection_update_finish (TRACKER_SPARQL_CONNECTION (source),
872 result,
873 &error);
874
875 if (error) {
876 g_critical ("Could not initialize currently active mount points: %s",
877 error->message);
878 g_error_free (error);
879 } else {
880 /* Mount points correctly initialized */
881 (TRACKER_MINER_FILES (user_data))->private->mount_points_initialized = TRUE;
882 /* If this happened AFTER we have a proper config, initialize
883 * stale volume removal now. */
884 if ((TRACKER_MINER_FILES (user_data))->private->config) {
885 init_stale_volume_removal (TRACKER_MINER_FILES (user_data));
886 }
887 }
888 }
889
890 static void
891 init_mount_points (TrackerMinerFiles *miner_files)
892 {
893 TrackerMiner *miner = TRACKER_MINER (miner_files);
894 TrackerMinerFilesPrivate *priv;
895 GHashTable *volumes;
896 GHashTableIter iter;
897 gpointer key, value;
898 GString *accumulator;
899 GError *error = NULL;
900 TrackerSparqlCursor *cursor;
901 GSList *uuids, *u;
902
903 g_debug ("Initializing mount points...");
904
905 /* First, get all mounted volumes, according to tracker-store (SYNC!) */
906 cursor = tracker_sparql_connection_query (tracker_miner_get_connection (miner),
907 "SELECT ?v WHERE { ?v a tracker:Volume ; tracker:isMounted true }",
908 NULL, &error);
909 if (error) {
910 g_critical ("Could not obtain the mounted volumes: %s", error->message);
911 g_error_free (error);
912 return;
913 }
914
915 priv = TRACKER_MINER_FILES_GET_PRIVATE (miner);
916
917 volumes = g_hash_table_new_full (g_str_hash, g_str_equal,
918 (GDestroyNotify) g_free,
919 NULL);
920
921
922 /* Make sure the root partition is always set to mounted, as GIO won't
923 * report it as a proper mount */
924 g_hash_table_insert (volumes,
925 g_strdup (TRACKER_NON_REMOVABLE_MEDIA_DATASOURCE_URN),
926 GINT_TO_POINTER (VOLUME_MOUNTED));
927
928 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
929 gint state;
930 const gchar *urn;
931
932 state = VOLUME_MOUNTED_IN_STORE;
933
934 urn = tracker_sparql_cursor_get_string (cursor, 0, NULL);
935
936 if (strcmp (urn, TRACKER_NON_REMOVABLE_MEDIA_DATASOURCE_URN) == 0) {
937 /* Report non-removable media to be mounted by HAL as well */
938 state |= VOLUME_MOUNTED;
939 }
940
941 g_hash_table_replace (volumes, g_strdup (urn), GINT_TO_POINTER (state));
942 }
943
944 g_object_unref (cursor);
945
946 /* Then, get all currently mounted non-REMOVABLE volumes, according to GIO */
947 uuids = tracker_storage_get_device_uuids (priv->storage, 0, TRUE);
948 for (u = uuids; u; u = u->next) {
949 const gchar *uuid;
950 gchar *non_removable_device_urn;
951 gint state;
952
953 uuid = u->data;
954 non_removable_device_urn = g_strdup_printf (TRACKER_DATASOURCE_URN_PREFIX "%s", uuid);
955
956 state = GPOINTER_TO_INT (g_hash_table_lookup (volumes, non_removable_device_urn));
957 state |= VOLUME_MOUNTED;
958
959 g_hash_table_replace (volumes, non_removable_device_urn, GINT_TO_POINTER (state));
960 }
961
962 g_slist_foreach (uuids, (GFunc) g_free, NULL);
963 g_slist_free (uuids);
964
965 /* Then, get all currently mounted REMOVABLE volumes, according to GIO */
966 if (priv->index_removable_devices) {
967 uuids = tracker_storage_get_device_uuids (priv->storage, TRACKER_STORAGE_REMOVABLE, FALSE);
968 for (u = uuids; u; u = u->next) {
969 const gchar *uuid;
970 gchar *removable_device_urn;
971 gint state;
972
973 uuid = u->data;
974 removable_device_urn = g_strdup_printf (TRACKER_DATASOURCE_URN_PREFIX "%s", uuid);
975
976 state = GPOINTER_TO_INT (g_hash_table_lookup (volumes, removable_device_urn));
977 state |= VOLUME_MOUNTED;
978
979 g_hash_table_replace (volumes, removable_device_urn, GINT_TO_POINTER (state));
980 }
981
982 g_slist_foreach (uuids, (GFunc) g_free, NULL);
983 g_slist_free (uuids);
984 }
985
986 accumulator = g_string_new (NULL);
987 g_hash_table_iter_init (&iter, volumes);
988
989 /* Finally, set up volumes based on the composed info */
990 while (g_hash_table_iter_next (&iter, &key, &value)) {
991 const gchar *urn = key;
992 gint state = GPOINTER_TO_INT (value);
993
994 if ((state & VOLUME_MOUNTED) &&
995 !(state & VOLUME_MOUNTED_IN_STORE)) {
996 const gchar *mount_point = NULL;
997 TrackerStorageType type = 0;
998
999 /* Note: is there any case where the urn doesn't have our
1000 * datasource prefix? */
1001 if (g_str_has_prefix (urn, TRACKER_DATASOURCE_URN_PREFIX)) {
1002 const gchar *uuid;
1003
1004 uuid = urn + strlen (TRACKER_DATASOURCE_URN_PREFIX);
1005 mount_point = tracker_storage_get_mount_point_for_uuid (priv->storage, uuid);
1006 type = tracker_storage_get_type_for_uuid (priv->storage, uuid);
1007 }
1008
1009 if (urn) {
1010 if (mount_point) {
1011 g_debug ("Mount point state incorrect in DB for URN '%s', "
1012 "currently it is mounted on '%s'",
1013 urn,
1014 mount_point);
1015 } else {
1016 g_debug ("Mount point state incorrect in DB for URN '%s', "
1017 "currently it is mounted",
1018 urn);
1019 }
1020
1021 /* Set mount point state */
1022 set_up_mount_point (TRACKER_MINER_FILES (miner),
1023 urn,
1024 mount_point,
1025 NULL,
1026 TRUE,
1027 accumulator);
1028
1029 /* Set mount point type */
1030 set_up_mount_point_type (TRACKER_MINER_FILES (miner),
1031 urn,
1032 TRACKER_STORAGE_TYPE_IS_REMOVABLE (type),
1033 TRACKER_STORAGE_TYPE_IS_OPTICAL (type),
1034 accumulator);
1035
1036 if (mount_point) {
1037 TrackerIndexingTree *indexing_tree;
1038 TrackerDirectoryFlags flags;
1039 GFile *file;
1040
1041 indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (miner));
1042 flags = TRACKER_DIRECTORY_FLAG_RECURSE |
1043 TRACKER_DIRECTORY_FLAG_CHECK_MTIME |
1044 TRACKER_DIRECTORY_FLAG_PRESERVE;
1045
1046 if (tracker_config_get_enable_monitors (miner_files->private->config)) {
1047 flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
1048 }
1049
1050 /* Add the current mount point as reported to have incorrect
1051 * state. We will force mtime checks on this mount points,
1052 * even if no-mtime-check-needed was set. */
1053 file = g_file_new_for_path (mount_point);
1054 if (tracker_miner_files_is_file_eligible (miner_files, file)) {
1055 tracker_indexing_tree_add (indexing_tree,
1056 file,
1057 flags);
1058 }
1059 g_object_unref (file);
1060 }
1061 }
1062 } else if (!(state & VOLUME_MOUNTED) &&
1063 (state & VOLUME_MOUNTED_IN_STORE)) {
1064 if (urn) {
1065 g_debug ("Mount point state incorrect in DB for URN '%s', "
1066 "currently it is NOT mounted",
1067 urn);
1068 set_up_mount_point (TRACKER_MINER_FILES (miner),
1069 urn,
1070 NULL,
1071 NULL,
1072 FALSE,
1073 accumulator);
1074 /* There's no need to force mtime check in these inconsistent
1075 * mount points, as they are not mounted right now. */
1076 }
1077 }
1078 }
1079
1080 if (accumulator->str[0] != '\0') {
1081 tracker_sparql_connection_update_async (tracker_miner_get_connection (miner),
1082 accumulator->str,
1083 G_PRIORITY_LOW,
1084 NULL,
1085 init_mount_points_cb,
1086 miner);
1087 } else {
1088 /* Note. Not initializing stale volume removal timeout because
1089 * we do not have the configuration setup yet */
1090 (TRACKER_MINER_FILES (miner))->private->mount_points_initialized = TRUE;
1091 }
1092
1093 g_string_free (accumulator, TRUE);
1094 g_hash_table_unref (volumes);
1095 }
1096
1097 static gboolean
1098 cleanup_stale_removable_volumes_cb (gpointer user_data)
1099 {
1100 TrackerMinerFiles *miner = TRACKER_MINER_FILES (user_data);
1101 gint n_days_threshold;
1102 time_t n_days_ago;
1103 gchar *n_days_ago_as_string;
1104
1105 n_days_threshold = tracker_config_get_removable_days_threshold (miner->private->config);
1106
1107 if (n_days_threshold == 0)
1108 return TRUE;
1109
1110 n_days_ago = (time (NULL) - (SECONDS_PER_DAY * n_days_threshold));
1111 n_days_ago_as_string = tracker_date_to_string (n_days_ago);
1112
1113 g_message ("Running stale volumes check...");
1114
1115 miner_files_in_removable_media_remove_by_date (miner, n_days_ago_as_string);
1116
1117 g_free (n_days_ago_as_string);
1118
1119 return TRUE;
1120 }
1121
1122 static void
1123 init_stale_volume_removal (TrackerMinerFiles *miner)
1124 {
1125 /* If disabled, make sure we don't do anything */
1126 if (tracker_config_get_removable_days_threshold (miner->private->config) == 0) {
1127 g_message ("Stale volume check is disabled");
1128 return;
1129 }
1130
1131 /* Run right away the first check */
1132 cleanup_stale_removable_volumes_cb (miner);
1133
1134 g_message ("Initializing stale volume check timeout...");
1135
1136 /* Then, setup new timeout event every day */
1137 miner->private->stale_volumes_check_id =
1138 g_timeout_add_seconds (SECONDS_PER_DAY + 1,
1139 cleanup_stale_removable_volumes_cb,
1140 miner);
1141 }
1142
1143
1144 static void
1145 mount_point_removed_cb (TrackerStorage *storage,
1146 const gchar *uuid,
1147 const gchar *mount_point,
1148 gpointer user_data)
1149 {
1150 TrackerMinerFiles *miner = user_data;
1151 TrackerIndexingTree *indexing_tree;
1152 gchar *urn;
1153 GFile *mount_point_file;
1154
1155 urn = g_strdup_printf (TRACKER_DATASOURCE_URN_PREFIX "%s", uuid);
1156 g_debug ("Mount point removed for URN '%s'", urn);
1157
1158 mount_point_file = g_file_new_for_path (mount_point);
1159
1160 /* Notify extractor about cancellation of all tasks under the mount point */
1161 tracker_extract_client_cancel_for_prefix (mount_point_file);
1162
1163 /* Tell TrackerMinerFS to skip monitoring everything under the mount
1164 * point (in case there was no pre-unmount notification) */
1165 indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (miner));
1166 tracker_indexing_tree_remove (indexing_tree, mount_point_file);
1167
1168 /* Set mount point status in tracker-store */
1169 set_up_mount_point (miner, urn, mount_point, NULL, FALSE, NULL);
1170
1171 g_free (urn);
1172 g_object_unref (mount_point_file);
1173 }
1174
1175 static void
1176 mount_point_added_cb (TrackerStorage *storage,
1177 const gchar *uuid,
1178 const gchar *mount_point,
1179 const gchar *mount_name,
1180 gboolean removable,
1181 gboolean optical,
1182 gpointer user_data)
1183 {
1184 TrackerMinerFiles *miner = user_data;
1185 TrackerMinerFilesPrivate *priv;
1186 gchar *urn;
1187 GString *queries;
1188
1189 priv = TRACKER_MINER_FILES_GET_PRIVATE (miner);
1190
1191 urn = g_strdup_printf (TRACKER_DATASOURCE_URN_PREFIX "%s", uuid);
1192 g_message ("Mount point added for URN '%s'", urn);
1193
1194 if (removable && !priv->index_removable_devices) {
1195 g_message (" Not crawling, removable devices disabled in config");
1196 } else if (optical && !priv->index_optical_discs) {
1197 g_message (" Not crawling, optical devices discs disabled in config");
1198 } else if (!removable && !optical) {
1199 TrackerIndexingTree *indexing_tree;
1200 TrackerDirectoryFlags flags;
1201 GFile *mount_point_file;
1202 GSList *l;
1203
1204 mount_point_file = g_file_new_for_path (mount_point);
1205 indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (miner));
1206
1207 /* Check if one of the recursively indexed locations is in
1208 * the mounted path, or if the mounted path is inside
1209 * a recursively indexed directory... */
1210 for (l = tracker_config_get_index_recursive_directories (miner->private->config);
1211 l;
1212 l = g_slist_next (l)) {
1213 GFile *config_file;
1214
1215 config_file = g_file_new_for_path (l->data);
1216 flags = TRACKER_DIRECTORY_FLAG_RECURSE |
1217 TRACKER_DIRECTORY_FLAG_CHECK_MTIME |
1218 TRACKER_DIRECTORY_FLAG_PRESERVE;
1219
1220 if (tracker_config_get_enable_monitors (miner->private->config)) {
1221 flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
1222 }
1223
1224 if (g_file_equal (config_file, mount_point_file) ||
1225 g_file_has_prefix (config_file, mount_point_file)) {
1226 /* If the config path is contained inside the mount path,
1227 * then add the config path to re-check */
1228 g_message (" Re-check of configured path '%s' needed (recursively)",
1229 (gchar *) l->data);
1230 tracker_indexing_tree_add (indexing_tree,
1231 config_file,
1232 flags);
1233 } else if (g_file_has_prefix (mount_point_file, config_file)) {
1234 /* If the mount path is contained inside the config path,
1235 * then add the mount path to re-check */
1236 g_message (" Re-check of path '%s' needed (inside configured path '%s')",
1237 mount_point,
1238 (gchar *) l->data);
1239 tracker_indexing_tree_add (indexing_tree,
1240 config_file,
1241 flags);
1242 }
1243 g_object_unref (config_file);
1244 }
1245
1246 /* Check if one of the non-recursively indexed locations is in
1247 * the mount path... */
1248 for (l = tracker_config_get_index_single_directories (miner->private->config);
1249 l;
1250 l = g_slist_next (l)) {
1251 GFile *config_file;
1252
1253 flags = TRACKER_DIRECTORY_FLAG_CHECK_MTIME;
1254
1255 if (tracker_config_get_enable_monitors (miner->private->config)) {
1256 flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
1257 }
1258
1259 config_file = g_file_new_for_path (l->data);
1260 if (g_file_equal (config_file, mount_point_file) ||
1261 g_file_has_prefix (config_file, mount_point_file)) {
1262 g_message (" Re-check of configured path '%s' needed (non-recursively)",
1263 (gchar *) l->data);
1264 tracker_indexing_tree_add (indexing_tree,
1265 config_file,
1266 flags);
1267 }
1268 g_object_unref (config_file);
1269 }
1270
1271 g_object_unref (mount_point_file);
1272 } else {
1273 g_message (" Adding directories in removable/optical media to crawler's queue");
1274 miner_files_add_removable_or_optical_directory (miner,
1275 mount_point,
1276 uuid);
1277 }
1278
1279 queries = g_string_new ("");
1280 set_up_mount_point (miner, urn, mount_point, mount_name, TRUE, queries);
1281 set_up_mount_point_type (miner, urn, removable, optical, queries);
1282 tracker_sparql_connection_update_async (tracker_miner_get_connection (TRACKER_MINER (miner)),
1283 queries->str,
1284 G_PRIORITY_LOW,
1285 NULL,
1286 set_up_mount_point_cb,
1287 g_strdup (urn));
1288 g_string_free (queries, TRUE);
1289 g_free (urn);
1290 }
1291
1292 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
1293
1294 static void
1295 set_up_throttle (TrackerMinerFiles *mf,
1296 gboolean enable)
1297 {
1298 gdouble throttle;
1299 gint config_throttle;
1300
1301 config_throttle = tracker_config_get_throttle (mf->private->config);
1302 throttle = (1.0 / 20) * config_throttle;
1303
1304 if (enable) {
1305 throttle += 0.25;
1306 }
1307
1308 throttle = CLAMP (throttle, 0, 1);
1309
1310 g_debug ("Setting new throttle to %0.3f", throttle);
1311 tracker_miner_fs_set_throttle (TRACKER_MINER_FS (mf), throttle);
1312 }
1313
1314 static void
1315 check_battery_status (TrackerMinerFiles *mf)
1316 {
1317 gboolean on_battery, on_low_battery;
1318 gboolean should_pause = FALSE;
1319 gboolean should_throttle = FALSE;
1320
1321 on_low_battery = tracker_power_get_on_low_battery (mf->private->power);
1322 on_battery = tracker_power_get_on_battery (mf->private->power);
1323
1324 if (!on_battery) {
1325 g_message ("Running on AC power");
1326 should_pause = FALSE;
1327 should_throttle = FALSE;
1328 } else if (on_low_battery) {
1329 g_message ("Running on LOW Battery, pausing");
1330 should_pause = TRUE;
1331 should_throttle = TRUE;
1332 } else {
1333 should_throttle = TRUE;
1334
1335 /* Check if miner should be paused based on configuration */
1336 if (!tracker_config_get_index_on_battery (mf->private->config)) {
1337 if (!tracker_config_get_index_on_battery_first_time (mf->private->config)) {
1338 g_message ("Running on battery, but not enabled, pausing");
1339 should_pause = TRUE;
1340 } else if (tracker_db_manager_get_first_index_done ()) {
1341 g_message ("Running on battery and first-time index "
1342 "already done, pausing");
1343 should_pause = TRUE;
1344 } else {
1345 g_message ("Running on battery, but first-time index not "
1346 "already finished, keeping on");
1347 }
1348 } else {
1349 g_message ("Running on battery");
1350 }
1351 }
1352
1353 if (should_pause) {
1354 /* Don't try to pause again */
1355 if (mf->private->low_battery_pause_cookie == 0) {
1356 mf->private->low_battery_pause_cookie =
1357 tracker_miner_pause (TRACKER_MINER (mf),
1358 _("Low battery"),
1359 NULL);
1360 }
1361 } else {
1362 /* Don't try to resume again */
1363 if (mf->private->low_battery_pause_cookie != 0) {
1364 tracker_miner_resume (TRACKER_MINER (mf),
1365 mf->private->low_battery_pause_cookie,
1366 NULL);
1367 mf->private->low_battery_pause_cookie = 0;
1368 }
1369 }
1370
1371 set_up_throttle (mf, should_throttle);
1372 }
1373
1374 /* Called when battery status change is detected */
1375 static void
1376 battery_status_cb (GObject *object,
1377 GParamSpec *pspec,
1378 gpointer user_data)
1379 {
1380 TrackerMinerFiles *mf = user_data;
1381
1382 check_battery_status (mf);
1383 }
1384
1385 /* Called when battery-related configuration change is detected */
1386 static void
1387 index_on_battery_cb (GObject *object,
1388 GParamSpec *pspec,
1389 gpointer user_data)
1390 {
1391 TrackerMinerFiles *mf = user_data;
1392
1393 check_battery_status (mf);
1394 }
1395
1396 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
1397
1398 /* Called when mining has finished the first time */
1399 static void
1400 miner_finished_cb (TrackerMinerFS *fs,
1401 gdouble seconds_elapsed,
1402 guint total_directories_found,
1403 guint total_directories_ignored,
1404 guint total_files_found,
1405 guint total_files_ignored,
1406 gpointer user_data)
1407 {
1408 TrackerMinerFiles *mf = TRACKER_MINER_FILES (fs);
1409
1410 /* Create stamp file if not already there */
1411 if (!tracker_db_manager_get_first_index_done ()) {
1412 tracker_db_manager_set_first_index_done (TRUE);
1413 }
1414
1415 /* And remove the signal handler so that it's not
1416 * called again */
1417 if (mf->private->finished_handler) {
1418 g_signal_handler_disconnect (fs, mf->private->finished_handler);
1419 mf->private->finished_handler = 0;
1420 }
1421
1422 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
1423 check_battery_status (mf);
1424 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
1425 }
1426
1427 static void
1428 mount_pre_unmount_cb (GVolumeMonitor *volume_monitor,
1429 GMount *mount,
1430 TrackerMinerFiles *mf)
1431 {
1432 TrackerIndexingTree *indexing_tree;
1433 GFile *mount_root;
1434 gchar *uri;
1435
1436 mount_root = g_mount_get_root (mount);
1437 uri = g_file_get_uri (mount_root);
1438 g_message ("Pre-unmount requested for '%s'", uri);
1439
1440 indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (mf));
1441 tracker_indexing_tree_remove (indexing_tree, mount_root);
1442 g_object_unref (mount_root);
1443
1444 g_free (uri);
1445 }
1446
1447 static gboolean
1448 disk_space_check (TrackerMinerFiles *mf)
1449 {
1450 gint limit;
1451 gchar *data_dir;
1452 gdouble remaining;
1453
1454 limit = tracker_config_get_low_disk_space_limit (mf->private->config);
1455
1456 if (limit < 1) {
1457 return FALSE;
1458 }
1459
1460 /* Get % of remaining space in the partition where the cache is */
1461 data_dir = g_build_filename (g_get_user_cache_dir (), "tracker", NULL);
1462 remaining = tracker_file_system_get_remaining_space_percentage (data_dir);
1463 g_free (data_dir);
1464
1465 if (remaining <= limit) {
1466 g_message ("WARNING: Available disk space (%lf%%) is below "
1467 "configured threshold for acceptable working (%d%%)",
1468 remaining, limit);
1469 return TRUE;
1470 }
1471
1472 return FALSE;
1473 }
1474
1475 static gboolean
1476 disk_space_check_cb (gpointer user_data)
1477 {
1478 TrackerMinerFiles *mf = user_data;
1479
1480 if (disk_space_check (mf)) {
1481 /* Don't try to pause again */
1482 if (mf->private->disk_space_pause_cookie == 0) {
1483 mf->private->disk_space_pause_cookie =
1484 tracker_miner_pause (TRACKER_MINER (mf),
1485 _("Low disk space"),
1486 NULL);
1487 }
1488 } else {
1489 /* Don't try to resume again */
1490 if (mf->private->disk_space_pause_cookie != 0) {
1491 tracker_miner_resume (TRACKER_MINER (mf),
1492 mf->private->disk_space_pause_cookie,
1493 NULL);
1494 mf->private->disk_space_pause_cookie = 0;
1495 }
1496 }
1497
1498 return TRUE;
1499 }
1500
1501 static void
1502 disk_space_check_start (TrackerMinerFiles *mf)
1503 {
1504 gint limit;
1505
1506 if (mf->private->disk_space_check_id != 0) {
1507 return;
1508 }
1509
1510 limit = tracker_config_get_low_disk_space_limit (mf->private->config);
1511
1512 if (limit != -1) {
1513 g_message ("Starting disk space check for every %d seconds",
1514 DISK_SPACE_CHECK_FREQUENCY);
1515 mf->private->disk_space_check_id =
1516 g_timeout_add_seconds (DISK_SPACE_CHECK_FREQUENCY,
1517 disk_space_check_cb,
1518 mf);
1519
1520 /* Call the function now too to make sure we have an
1521 * initial value too!
1522 */
1523 disk_space_check_cb (mf);
1524 } else {
1525 g_message ("Not setting disk space, configuration is set to -1 (disabled)");
1526 }
1527 }
1528
1529 static void
1530 disk_space_check_stop (TrackerMinerFiles *mf)
1531 {
1532 if (mf->private->disk_space_check_id) {
1533 g_message ("Stopping disk space check");
1534 g_source_remove (mf->private->disk_space_check_id);
1535 mf->private->disk_space_check_id = 0;
1536 }
1537 }
1538
1539 static void
1540 low_disk_space_limit_cb (GObject *gobject,
1541 GParamSpec *arg1,
1542 gpointer user_data)
1543 {
1544 TrackerMinerFiles *mf = user_data;
1545
1546 disk_space_check_cb (mf);
1547 }
1548
1549 static void
1550 indexing_tree_update_filter (TrackerIndexingTree *indexing_tree,
1551 TrackerFilterType filter,
1552 GSList *new_elems)
1553 {
1554 tracker_indexing_tree_clear_filters (indexing_tree, filter);
1555
1556 while (new_elems) {
1557 tracker_indexing_tree_add_filter (indexing_tree, filter,
1558 new_elems->data);
1559 new_elems = new_elems->next;
1560 }
1561 }
1562
1563 static void
1564 miner_files_update_filters (TrackerMinerFiles *files)
1565 {
1566 TrackerIndexingTree *indexing_tree;
1567 GSList *list;
1568
1569 indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (files));
1570
1571 /* Ignored files */
1572 list = tracker_config_get_ignored_files (files->private->config);
1573 indexing_tree_update_filter (indexing_tree, TRACKER_FILTER_FILE, list);
1574
1575 /* Ignored directories */
1576 list = tracker_config_get_ignored_directories (files->private->config);
1577 indexing_tree_update_filter (indexing_tree,
1578 TRACKER_FILTER_DIRECTORY,
1579 list);
1580
1581 /* Directories with content */
1582 list = tracker_config_get_ignored_directories_with_content (files->private->config);
1583 indexing_tree_update_filter (indexing_tree,
1584 TRACKER_FILTER_PARENT_DIRECTORY,
1585 list);
1586 }
1587
1588 static void
1589 update_directories_from_new_config (TrackerMinerFS *mf,
1590 GSList *new_dirs,
1591 GSList *old_dirs,
1592 gboolean recurse)
1593 {
1594 TrackerMinerFilesPrivate *priv;
1595 TrackerDirectoryFlags flags = 0;
1596 TrackerIndexingTree *indexing_tree;
1597 GSList *sl;
1598
1599 priv = TRACKER_MINER_FILES_GET_PRIVATE (mf);
1600 indexing_tree = tracker_miner_fs_get_indexing_tree (mf);
1601
1602 g_message ("Updating %s directories changed from configuration",
1603 recurse ? "recursive" : "single");
1604
1605 /* First remove all directories removed from the config */
1606 for (sl = old_dirs; sl; sl = sl->next) {
1607 const gchar *path;
1608
1609 path = sl->data;
1610
1611 /* If we are not still in the list, remove the dir */
1612 if (!tracker_string_in_gslist (path, new_dirs)) {
1613 GFile *file;
1614
1615 g_message (" Removing directory: '%s'", path);
1616
1617 file = g_file_new_for_path (path);
1618
1619 /* First, remove the preserve flag, it might be
1620 * set on configuration directories within mount
1621 * points, as data should be persistent across
1622 * unmounts.
1623 */
1624 tracker_indexing_tree_get_root (indexing_tree,
1625 file, &flags);
1626
1627 if ((flags & TRACKER_DIRECTORY_FLAG_PRESERVE) != 0) {
1628 flags &= ~(TRACKER_DIRECTORY_FLAG_PRESERVE);
1629 tracker_indexing_tree_add (indexing_tree,
1630 file, flags);
1631 }
1632
1633 /* Fully remove item (monitors and from store),
1634 * now that there's no preserve flag.
1635 */
1636 tracker_indexing_tree_remove (indexing_tree, file);
1637 g_object_unref (file);
1638 }
1639 }
1640
1641 flags = TRACKER_DIRECTORY_FLAG_NONE;
1642
1643 if (recurse) {
1644 flags |= TRACKER_DIRECTORY_FLAG_RECURSE;
1645 }
1646
1647 if (tracker_config_get_enable_monitors (priv->config)) {
1648 flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
1649 }
1650
1651 if (tracker_miner_fs_get_mtime_checking (TRACKER_MINER_FS (mf))) {
1652 flags |= TRACKER_DIRECTORY_FLAG_CHECK_MTIME;
1653 }
1654
1655 /* Second add directories which are new */
1656 for (sl = new_dirs; sl; sl = sl->next) {
1657 const gchar *path;
1658
1659 path = sl->data;
1660
1661 /* If we are now in the list, add the dir */
1662 if (!tracker_string_in_gslist (path, old_dirs)) {
1663 GFile *file;
1664
1665 g_message (" Adding directory:'%s'", path);
1666
1667 file = g_file_new_for_path (path);
1668 tracker_indexing_tree_add (indexing_tree, file, flags);
1669 g_object_unref (file);
1670 }
1671 }
1672 }
1673
1674 static void
1675 index_recursive_directories_cb (GObject *gobject,
1676 GParamSpec *arg1,
1677 gpointer user_data)
1678 {
1679 TrackerMinerFilesPrivate *private;
1680 GSList *new_dirs, *old_dirs;
1681
1682 private = TRACKER_MINER_FILES_GET_PRIVATE (user_data);
1683
1684 new_dirs = tracker_config_get_index_recursive_directories (private->config);
1685 old_dirs = private->index_recursive_directories;
1686
1687 update_directories_from_new_config (TRACKER_MINER_FS (user_data),
1688 new_dirs,
1689 old_dirs,
1690 TRUE);
1691
1692 /* Re-set the stored config in case it changes again */
1693 if (private->index_recursive_directories) {
1694 g_slist_foreach (private->index_recursive_directories, (GFunc) g_free, NULL);
1695 g_slist_free (private->index_recursive_directories);
1696 }
1697
1698 private->index_recursive_directories = tracker_gslist_copy_with_string_data (new_dirs);
1699 }
1700
1701 static void
1702 index_single_directories_cb (GObject *gobject,
1703 GParamSpec *arg1,
1704 gpointer user_data)
1705 {
1706 TrackerMinerFilesPrivate *private;
1707 GSList *new_dirs, *old_dirs;
1708
1709 private = TRACKER_MINER_FILES_GET_PRIVATE (user_data);
1710
1711 new_dirs = tracker_config_get_index_single_directories (private->config);
1712 old_dirs = private->index_single_directories;
1713
1714 update_directories_from_new_config (TRACKER_MINER_FS (user_data),
1715 new_dirs,
1716 old_dirs,
1717 FALSE);
1718
1719 /* Re-set the stored config in case it changes again */
1720 if (private->index_single_directories) {
1721 g_slist_foreach (private->index_single_directories, (GFunc) g_free, NULL);
1722 g_slist_free (private->index_single_directories);
1723 }
1724
1725 private->index_single_directories = tracker_gslist_copy_with_string_data (new_dirs);
1726 }
1727
1728 static gboolean
1729 miner_files_force_recheck_idle (gpointer user_data)
1730 {
1731 TrackerMinerFiles *miner_files = user_data;
1732 TrackerIndexingTree *indexing_tree;
1733 GList *roots, *l;
1734
1735 miner_files_update_filters (miner_files);
1736
1737 indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (miner_files));
1738 roots = tracker_indexing_tree_list_roots (indexing_tree);
1739
1740 for (l = roots; l; l = l->next) {
1741 GFile *root = l->data;
1742
1743 g_signal_emit_by_name (indexing_tree, "directory-updated", root);
1744 }
1745
1746 miner_files->private->force_recheck_id = 0;
1747 g_list_free (roots);
1748
1749 return FALSE;
1750 }
1751
1752 static void
1753 trigger_recheck_cb (GObject *gobject,
1754 GParamSpec *arg1,
1755 gpointer user_data)
1756 {
1757 TrackerMinerFiles *mf = user_data;
1758
1759 g_message ("Ignored content related configuration changed, checking index...");
1760
1761 if (mf->private->force_recheck_id == 0) {
1762 /* Set idle so multiple changes in the config lead to one recheck */
1763 mf->private->force_recheck_id =
1764 g_idle_add (miner_files_force_recheck_idle, mf);
1765 }
1766 }
1767
1768 static gboolean
1769 index_volumes_changed_idle (gpointer user_data)
1770 {
1771 TrackerMinerFiles *mf = user_data;
1772 GSList *mounts_removed = NULL;
1773 GSList *mounts_added = NULL;
1774 gboolean new_index_removable_devices;
1775 gboolean new_index_optical_discs;
1776
1777 g_message ("Volume related configuration changed, updating...");
1778
1779 /* Read new config values. Note that if removable devices is FALSE,
1780 * optical discs will also always be FALSE. */
1781 new_index_removable_devices = tracker_config_get_index_removable_devices (mf->private->config);
1782 new_index_optical_discs = (new_index_removable_devices ?
1783 tracker_config_get_index_optical_discs (mf->private->config) :
1784 FALSE);
1785
1786 /* Removable devices config changed? */
1787 if (mf->private->index_removable_devices != new_index_removable_devices) {
1788 GSList *m;
1789
1790 /* Get list of roots for currently mounted removable devices
1791 * (excluding optical) */
1792 m = tracker_storage_get_device_roots (mf->private->storage,
1793 TRACKER_STORAGE_REMOVABLE,
1794 TRUE);
1795 /* Set new config value */
1796 mf->private->index_removable_devices = new_index_removable_devices;
1797
1798 if (mf->private->index_removable_devices) {
1799 /* If previously not indexing and now indexing, need to re-check
1800 * current mounted volumes, add new monitors and index new files
1801 */
1802 mounts_added = m;
1803 } else {
1804 /* If previously indexing and now not indexing, need to re-check
1805 * current mounted volumes, remove monitors and remove all resources
1806 * from the store belonging to a removable device
1807 */
1808 mounts_removed = m;
1809
1810 /* And now, single sparql update to remove all resources
1811 * corresponding to removable devices (includes those
1812 * not currently mounted) */
1813 miner_files_in_removable_media_remove_by_type (mf, TRACKER_STORAGE_REMOVABLE);
1814 }
1815 }
1816
1817 /* Optical discs config changed? */
1818 if (mf->private->index_optical_discs != new_index_optical_discs) {
1819 GSList *m;
1820
1821 /* Get list of roots for removable devices (excluding optical) */
1822 m = tracker_storage_get_device_roots (mf->private->storage,
1823 TRACKER_STORAGE_REMOVABLE | TRACKER_STORAGE_OPTICAL,
1824 TRUE);
1825
1826 /* Set new config value */
1827 mf->private->index_optical_discs = new_index_optical_discs;
1828
1829 if (mf->private->index_optical_discs) {
1830 /* If previously not indexing and now indexing, need to re-check
1831 * current mounted volumes, add new monitors and index new files
1832 */
1833 mounts_added = g_slist_concat (mounts_added, m);
1834 } else {
1835 /* If previously indexing and now not indexing, need to re-check
1836 * current mounted volumes, remove monitors and remove all resources
1837 * from the store belonging to a optical disc
1838 */
1839 mounts_removed = g_slist_concat (mounts_removed, m);
1840
1841 /* And now, single sparql update to remove all resources
1842 * corresponding to removable+optical devices (includes those
1843 * not currently mounted) */
1844 miner_files_in_removable_media_remove_by_type (mf, TRACKER_STORAGE_REMOVABLE | TRACKER_STORAGE_OPTICAL);
1845 }
1846 }
1847
1848 /* Tell TrackerMinerFS to stop monitoring the given removed mount paths, if any */
1849 if (mounts_removed) {
1850 TrackerIndexingTree *indexing_tree;
1851 GSList *sl;
1852
1853 indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (mf));
1854
1855 for (sl = mounts_removed; sl; sl = g_slist_next (sl)) {
1856 GFile *mount_point_file;
1857
1858 mount_point_file = g_file_new_for_path (sl->data);
1859 tracker_indexing_tree_remove (indexing_tree,
1860 mount_point_file);
1861 g_object_unref (mount_point_file);
1862 }
1863
1864 g_slist_foreach (mounts_removed, (GFunc) g_free, NULL);
1865 g_slist_free (mounts_removed);
1866 }
1867
1868 /* Tell TrackerMinerFS to start monitoring the given added mount paths, if any */
1869 if (mounts_added) {
1870 GSList *sl;
1871
1872 for (sl = mounts_added; sl; sl = g_slist_next (sl)) {
1873 miner_files_add_removable_or_optical_directory (mf,
1874 (gchar *) sl->data,
1875 NULL);
1876 }
1877
1878 g_slist_foreach (mounts_added, (GFunc) g_free, NULL);
1879 g_slist_free (mounts_added);
1880 }
1881
1882 mf->private->volumes_changed_id = 0;
1883
1884 /* Check if the stale volume removal configuration changed from enabled to disabled
1885 * or from disabled to enabled */
1886 if (tracker_config_get_removable_days_threshold (mf->private->config) == 0 &&
1887 mf->private->stale_volumes_check_id != 0) {
1888 /* From having the check enabled to having it disabled, remove the timeout */
1889 g_debug (" Stale volume removal now disabled, removing timeout");
1890 g_source_remove (mf->private->stale_volumes_check_id);
1891 mf->private->stale_volumes_check_id = 0;
1892 } else if (tracker_config_get_removable_days_threshold (mf->private->config) > 0 &&
1893 mf->private->stale_volumes_check_id == 0) {
1894 g_debug (" Stale volume removal now enabled, initializing timeout");
1895 /* From having the check disabled to having it enabled, so fire up the
1896 * timeout. */
1897 init_stale_volume_removal (TRACKER_MINER_FILES (mf));
1898 }
1899
1900 return FALSE;
1901 }
1902
1903 static void
1904 index_volumes_changed_cb (GObject *gobject,
1905 GParamSpec *arg1,
1906 gpointer user_data)
1907 {
1908 TrackerMinerFiles *miner_files = user_data;
1909
1910 if (miner_files->private->volumes_changed_id == 0) {
1911 /* Set idle so multiple changes in the config lead to one check */
1912 miner_files->private->volumes_changed_id =
1913 g_idle_add (index_volumes_changed_idle, miner_files);
1914 }
1915 }
1916
1917 static const gchar *
1918 miner_files_get_file_urn (TrackerMinerFiles *miner,
1919 GFile *file,
1920 gboolean *is_iri)
1921 {
1922 const gchar *urn;
1923
1924 urn = tracker_miner_fs_get_urn (TRACKER_MINER_FS (miner), file);
1925 *is_iri = TRUE;
1926
1927 if (!urn) {
1928 /* This is a new insertion, use anonymous URNs to store files */
1929 urn = "_:file";
1930 *is_iri = FALSE;
1931 }
1932
1933 return urn;
1934 }
1935
1936 static void
1937 miner_files_add_to_datasource (TrackerMinerFiles *mf,
1938 GFile *file,
1939 TrackerSparqlBuilder *sparql)
1940 {
1941 TrackerMinerFilesPrivate *priv;
1942 const gchar *removable_device_uuid;
1943 gchar *removable_device_urn, *uri;
1944 const gchar *urn;
1945 gboolean is_iri;
1946
1947 priv = TRACKER_MINER_FILES_GET_PRIVATE (mf);
1948 uri = g_file_get_uri (file);
1949
1950 removable_device_uuid = tracker_storage_get_uuid_for_file (priv->storage, file);
1951
1952 if (removable_device_uuid) {
1953 removable_device_urn = g_strdup_printf (TRACKER_DATASOURCE_URN_PREFIX "%s",
1954 removable_device_uuid);
1955 } else {
1956 removable_device_urn = g_strdup (TRACKER_NON_REMOVABLE_MEDIA_DATASOURCE_URN);
1957 }
1958
1959 urn = miner_files_get_file_urn (mf, file, &is_iri);
1960
1961 if (is_iri) {
1962 tracker_sparql_builder_subject_iri (sparql, urn);
1963 } else {
1964 tracker_sparql_builder_subject (sparql, urn);
1965 }
1966
1967 tracker_sparql_builder_predicate (sparql, "a");
1968 tracker_sparql_builder_object (sparql, "nfo:FileDataObject");
1969
1970 tracker_sparql_builder_predicate (sparql, "nie:dataSource");
1971 tracker_sparql_builder_object_iri (sparql, removable_device_urn);
1972
1973 tracker_sparql_builder_predicate (sparql, "tracker:available");
1974 tracker_sparql_builder_object_boolean (sparql, TRUE);
1975
1976 g_free (removable_device_urn);
1977 g_free (uri);
1978 }
1979
1980 static void
1981 process_file_data_free (ProcessFileData *data)
1982 {
1983 g_object_unref (data->miner);
1984 g_object_unref (data->sparql);
1985 g_object_unref (data->cancellable);
1986 g_object_unref (data->file);
1987 g_free (data->mime_type);
1988 g_slice_free (ProcessFileData, data);
1989 }
1990
1991 static void
1992 sparql_builder_finish (ProcessFileData *data,
1993 const gchar *preupdate,
1994 const gchar *postupdate,
1995 const gchar *sparql,
1996 const gchar *where)
1997 {
1998 const gchar *uuid;
1999
2000 if (sparql && *sparql) {
2001 gboolean is_iri;
2002 const gchar *urn;
2003
2004 urn = miner_files_get_file_urn (data->miner, data->file, &is_iri);
2005
2006 if (is_iri) {
2007 gchar *str;
2008
2009 str = g_strdup_printf ("<%s>", urn);
2010 tracker_sparql_builder_append (data->sparql, str);
2011 g_free (str);
2012 } else {
2013 tracker_sparql_builder_append (data->sparql, urn);
2014 }
2015
2016 tracker_sparql_builder_append (data->sparql, sparql);
2017 }
2018
2019 tracker_sparql_builder_graph_close (data->sparql);
2020 tracker_sparql_builder_insert_close (data->sparql);
2021
2022 if (where && *where) {
2023 tracker_sparql_builder_where_open (data->sparql);
2024 tracker_sparql_builder_append (data->sparql, where);
2025 tracker_sparql_builder_where_close (data->sparql);
2026 }
2027
2028 /* Prepend preupdate queries */
2029 if (preupdate && *preupdate) {
2030 tracker_sparql_builder_prepend (data->sparql, preupdate);
2031 }
2032
2033 /* Append postupdate */
2034 if (postupdate && *postupdate) {
2035 tracker_sparql_builder_append (data->sparql, postupdate);
2036 }
2037
2038 uuid = g_object_get_qdata (G_OBJECT (data->file),
2039 data->miner->private->quark_mount_point_uuid);
2040
2041 /* File represents a mount point */
2042 if (G_UNLIKELY (uuid)) {
2043 GString *queries;
2044 gchar *removable_device_urn, *uri;
2045
2046 removable_device_urn = g_strdup_printf (TRACKER_DATASOURCE_URN_PREFIX "%s", uuid);
2047 uri = g_file_get_uri (G_FILE (data->file));
2048 queries = g_string_new ("");
2049
2050 g_string_append_printf (queries,
2051 "DELETE { "
2052 " <%s> tracker:mountPoint ?unknown "
2053 "} WHERE { "
2054 " <%s> a tracker:Volume; "
2055 " tracker:mountPoint ?unknown "
2056 "} ",
2057 removable_device_urn, removable_device_urn);
2058
2059 g_string_append_printf (queries,
2060 "INSERT { GRAPH <%s> {"
2061 " <%s> a tracker:Volume; "
2062 " tracker:mountPoint ?u "
2063 "} } WHERE { "
2064 " ?u a nfo:FileDataObject; "
2065 " nie:url \"%s\" "
2066 "} ",
2067 removable_device_urn, removable_device_urn, uri);
2068
2069 tracker_sparql_builder_append (data->sparql, queries->str);
2070 g_string_free (queries, TRUE);
2071 g_free (removable_device_urn);
2072 g_free (uri);
2073 }
2074 }
2075
2076 static void
2077 extractor_get_failsafe_metadata_cb (GObject *object,
2078 GAsyncResult *res,
2079 gpointer user_data)
2080 {
2081 ProcessFileData *data = user_data;
2082 TrackerMinerFiles *miner = data->miner;
2083 TrackerMinerFilesPrivate *priv = miner->private;
2084 const gchar *preupdate, *postupdate, *sparql, *where;
2085 TrackerExtractInfo *info;
2086 GError *error = NULL;
2087 gchar *uri;
2088
2089 info = tracker_extract_client_get_metadata_finish (G_FILE (object), res, &error);
2090 preupdate = postupdate = sparql = where = NULL;
2091
2092 if (error) {
2093 GStrv types;
2094
2095 uri = g_file_get_uri (data->file);
2096 g_warning (" Got second extraction DBus error on '%s'. "
2097 "Adding only non-embedded metadata to the SparQL, "
2098 "the error was: %s",
2099 uri, error->message);
2100 g_error_free (error);
2101 g_free (uri);
2102
2103 types = tracker_extract_module_manager_get_fallback_rdf_types (data->mime_type);
2104
2105 if (types && types[0] != NULL) {
2106 guint i;
2107 GString *str = g_string_new (" a ");
2108 for (i = 0; types[i] != NULL; i++) {
2109 if (i != 0) {
2110 g_string_append_c (str, ',');
2111 }
2112 g_string_append (str, types[i]);
2113 }
2114 g_string_append (str, " .");
2115 sparql = g_string_free (str, FALSE);
2116 }
2117
2118 g_strfreev (types);
2119
2120 } else {
2121 TrackerSparqlBuilder *builder;
2122
2123 g_debug (" Extraction succeeded the second time");
2124
2125 builder = tracker_extract_info_get_preupdate_builder (info);
2126 preupdate = tracker_sparql_builder_get_result (builder);
2127
2128 builder = tracker_extract_info_get_postupdate_builder (info);
2129 postupdate = tracker_sparql_builder_get_result (builder);
2130
2131 builder = tracker_extract_info_get_metadata_builder (info);
2132 sparql = tracker_sparql_builder_get_result (builder);
2133
2134 where = tracker_extract_info_get_where_clause (info);
2135 }
2136
2137 sparql_builder_finish (data, preupdate, postupdate, sparql, where);
2138
2139 /* Notify success even if the extraction failed
2140 * again, so we get the essential data in the store.
2141 */
2142 tracker_miner_fs_file_notify (TRACKER_MINER_FS (miner), data->file, NULL);
2143
2144 priv->failed_extraction_queue = g_list_remove (priv->failed_extraction_queue, data);
2145 process_file_data_free (data);
2146
2147 /* Get on to the next failed extraction, or resume miner */
2148 extractor_process_failsafe (miner);
2149 }
2150
2151 /* This function processes failed files one by one,
2152 * the function will be called after each operation
2153 * is finished, so elements are processed linearly.
2154 */
2155 static void
2156 extractor_process_failsafe (TrackerMinerFiles *miner)
2157 {
2158 TrackerMinerFilesPrivate *priv;
2159 ProcessFileData *data;
2160
2161 priv = miner->private;
2162
2163 if (priv->failed_extraction_queue) {
2164 gchar *uri;
2165
2166 data = priv->failed_extraction_queue->data;
2167
2168 uri = g_file_get_uri (data->file);
2169 g_message ("Performing failsafe extraction on '%s'", uri);
2170 g_free (uri);
2171
2172 tracker_extract_client_get_metadata (data->file,
2173 data->mime_type,
2174 TRACKER_MINER_FS_GRAPH_URN,
2175 data->cancellable,
2176 extractor_get_failsafe_metadata_cb,
2177 data);
2178 } else {
2179 g_debug ("Failsafe extraction finished. Resuming miner...");
2180
2181 if (priv->failed_extraction_pause_cookie != 0) {
2182 tracker_miner_resume (TRACKER_MINER (miner),
2183 priv->failed_extraction_pause_cookie,
2184 NULL);
2185
2186 priv->failed_extraction_pause_cookie = 0;
2187 }
2188
2189 priv->failsafe_extraction = FALSE;
2190 }
2191 }
2192
2193 static void
2194 extractor_check_process_failsafe (TrackerMinerFiles *miner)
2195 {
2196 TrackerMinerFilesPrivate *priv;
2197
2198 priv = miner->private;
2199
2200 if (priv->failsafe_extraction) {
2201 /* already on failsafe extraction */
2202 return;
2203 }
2204
2205 if (priv->extraction_queue ||
2206 !priv->failed_extraction_queue) {
2207 /* No reasons (yet) to start failsafe extraction */
2208 return;
2209 }
2210
2211 priv->failsafe_extraction = TRUE;
2212 extractor_process_failsafe (miner);
2213 }
2214
2215 static void
2216 extractor_get_embedded_metadata_cb (GObject *object,
2217 GAsyncResult *res,
2218 gpointer user_data)
2219 {
2220 TrackerMinerFilesPrivate *priv;
2221 TrackerMinerFiles *miner;
2222 ProcessFileData *data = user_data;
2223 TrackerSparqlBuilder *preupdate, *postupdate, *sparql;
2224 const gchar *where;
2225 TrackerExtractInfo *info;
2226 GError *error = NULL;
2227
2228 miner = data->miner;
2229 priv = miner->private;
2230 priv->extraction_queue = g_list_remove (priv->extraction_queue, data);
2231 info = tracker_extract_client_get_metadata_finish (G_FILE (object), res, &error);
2232
2233 if (error) {
2234 if (error->code == G_DBUS_ERROR_NO_REPLY ||
2235 error->code == G_DBUS_ERROR_TIMEOUT ||
2236 error->code == G_DBUS_ERROR_TIMED_OUT) {
2237 gchar *uri;
2238
2239 uri = g_file_get_uri (data->file);
2240 g_warning (" Got extraction DBus error on '%s': %s", uri, error->message);
2241
2242 if (priv->failed_extraction_pause_cookie != 0) {
2243 priv->failed_extraction_pause_cookie =
2244 tracker_miner_pause (TRACKER_MINER (data->miner),
2245 _("Extractor error, performing "
2246 "failsafe embedded metadata extraction"),
2247 NULL);
2248 }
2249
2250 priv->failed_extraction_queue = g_list_prepend (priv->failed_extraction_queue, data);
2251 g_free (uri);
2252 } else {
2253 sparql_builder_finish (data, NULL, NULL, NULL, NULL);
2254
2255 /* Something bad happened, notify about the error */
2256 tracker_miner_fs_file_notify (TRACKER_MINER_FS (data->miner), data->file, error);
2257 process_file_data_free (data);
2258 }
2259
2260 g_error_free (error);
2261 } else {
2262 preupdate = tracker_extract_info_get_preupdate_builder (info);
2263 postupdate = tracker_extract_info_get_postupdate_builder (info);
2264 sparql = tracker_extract_info_get_metadata_builder (info);
2265 where = tracker_extract_info_get_where_clause (info);
2266
2267 sparql_builder_finish (data,
2268 tracker_sparql_builder_get_result (preupdate),
2269 tracker_sparql_builder_get_result (postupdate),
2270 tracker_sparql_builder_get_result (sparql),
2271 where);
2272
2273 /* Notify about the success */
2274 tracker_miner_fs_file_notify (TRACKER_MINER_FS (data->miner), data->file, NULL);
2275
2276 process_file_data_free (data);
2277 }
2278
2279 /* Wait until there are no pending extraction requests
2280 * before starting failsafe extraction process.
2281 */
2282 extractor_check_process_failsafe (miner);
2283 }
2284
2285 static void
2286 process_file_cb (GObject *object,
2287 GAsyncResult *result,
2288 gpointer user_data)
2289 {
2290 TrackerMinerFilesPrivate *priv;
2291 TrackerSparqlBuilder *sparql;
2292 ProcessFileData *data;
2293 const gchar *mime_type, *urn, *parent_urn;
2294 GFileInfo *file_info;
2295 guint64 time_;
2296 GFile *file;
2297 gchar *uri;
2298 GError *error = NULL;
2299 gboolean is_iri;
2300 gboolean is_directory;
2301
2302 data = user_data;
2303 file = G_FILE (object);
2304 sparql = data->sparql;
2305 file_info = g_file_query_info_finish (file, result, &error);
2306 priv = TRACKER_MINER_FILES (data->miner)->private;
2307
2308 if (error) {
2309 /* Something bad happened, notify about the error */
2310 tracker_miner_fs_file_notify (TRACKER_MINER_FS (data->miner), file, error);
2311 priv->extraction_queue = g_list_remove (priv->extraction_queue, data);
2312 process_file_data_free (data);
2313 g_error_free (error);
2314
2315 return;
2316 }
2317
2318 uri = g_file_get_uri (file);
2319 mime_type = g_file_info_get_content_type (file_info);
2320 urn = miner_files_get_file_urn (TRACKER_MINER_FILES (data->miner), file, &is_iri);
2321
2322 data->mime_type = g_strdup (mime_type);
2323
2324 tracker_sparql_builder_insert_silent_open (sparql, NULL);
2325 tracker_sparql_builder_graph_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
2326
2327 if (is_iri) {
2328 tracker_sparql_builder_subject_iri (sparql, urn);
2329 } else {
2330 tracker_sparql_builder_subject (sparql, urn);
2331 }
2332
2333 tracker_sparql_builder_predicate (sparql, "a");
2334 tracker_sparql_builder_object (sparql, "nfo:FileDataObject");
2335 tracker_sparql_builder_object (sparql, "nie:InformationElement");
2336
2337 is_directory = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY ?
2338 TRUE : FALSE);
2339 if (is_directory) {
2340 tracker_sparql_builder_object (sparql, "nfo:Folder");
2341 }
2342
2343 parent_urn = tracker_miner_fs_get_parent_urn (TRACKER_MINER_FS (data->miner), file);
2344
2345 if (parent_urn) {
2346 tracker_sparql_builder_predicate (sparql, "nfo:belongsToContainer");
2347 tracker_sparql_builder_object_iri (sparql, parent_urn);
2348 }
2349
2350 tracker_sparql_builder_predicate (sparql, "nfo:fileName");
2351 tracker_sparql_builder_object_string (sparql, g_file_info_get_display_name (file_info));
2352
2353 tracker_sparql_builder_predicate (sparql, "nfo:fileSize");
2354 tracker_sparql_builder_object_int64 (sparql, g_file_info_get_size (file_info));
2355
2356 time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2357 tracker_sparql_builder_predicate (sparql, "nfo:fileLastModified");
2358 tracker_sparql_builder_object_date (sparql, (time_t *) &time_);
2359
2360 time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2361 tracker_sparql_builder_predicate (sparql, "nfo:fileLastAccessed");
2362 tracker_sparql_builder_object_date (sparql, (time_t *) &time_);
2363
2364 /* Laying the link between the IE and the DO. We use IE = DO */
2365 tracker_sparql_builder_predicate (sparql, "nie:isStoredAs");
2366 if (is_iri) {
2367 tracker_sparql_builder_object_iri (sparql, urn);
2368 } else {
2369 tracker_sparql_builder_object (sparql, urn);
2370 }
2371
2372 /* The URL of the DataObject (because IE = DO, this is correct) */
2373 tracker_sparql_builder_predicate (sparql, "nie:url");
2374 tracker_sparql_builder_object_string (sparql, uri);
2375
2376 tracker_sparql_builder_predicate (sparql, "nie:mimeType");
2377 tracker_sparql_builder_object_string (sparql, mime_type);
2378
2379 miner_files_add_to_datasource (data->miner, file, sparql);
2380
2381 if (tracker_extract_module_manager_mimetype_is_handled (mime_type)) {
2382 /* Next step, if handled by the extractor, get embedded metadata */
2383 tracker_extract_client_get_metadata (data->file,
2384 mime_type,
2385 TRACKER_MINER_FS_GRAPH_URN,
2386 data->cancellable,
2387 extractor_get_embedded_metadata_cb,
2388 data);
2389 } else {
2390 /* Otherwise, don't request embedded metadata extraction. */
2391 g_debug ("Avoiding embedded metadata request for uri '%s'", uri);
2392 sparql_builder_finish (data, NULL, NULL, NULL, NULL);
2393 tracker_miner_fs_file_notify (TRACKER_MINER_FS (data->miner), data->file, NULL);
2394
2395 priv->extraction_queue = g_list_remove (priv->extraction_queue, data);
2396 extractor_check_process_failsafe (data->miner);
2397 process_file_data_free (data);
2398 }
2399
2400 g_object_unref (file_info);
2401 g_free (uri);
2402 }
2403
2404 static gboolean
2405 miner_files_process_file (TrackerMinerFS *fs,
2406 GFile *file,
2407 TrackerSparqlBuilder *sparql,
2408 GCancellable *cancellable)
2409 {
2410 TrackerMinerFilesPrivate *priv;
2411 ProcessFileData *data;
2412 const gchar *attrs;
2413
2414 data = g_slice_new0 (ProcessFileData);
2415 data->miner = g_object_ref (fs);
2416 data->cancellable = g_object_ref (cancellable);
2417 data->sparql = g_object_ref (sparql);
2418 data->file = g_object_ref (file);
2419
2420 priv = TRACKER_MINER_FILES (fs)->private;
2421 priv->extraction_queue = g_list_prepend (priv->extraction_queue, data);
2422
2423 attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE ","
2424 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
2425 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
2426 G_FILE_ATTRIBUTE_STANDARD_SIZE ","
2427 G_FILE_ATTRIBUTE_TIME_MODIFIED ","
2428 G_FILE_ATTRIBUTE_TIME_ACCESS;
2429
2430 g_file_query_info_async (file,
2431 attrs,
2432 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2433 G_PRIORITY_DEFAULT,
2434 cancellable,
2435 process_file_cb,
2436 data);
2437
2438 return TRUE;
2439 }
2440
2441 static void
2442 process_file_attributes_cb (GObject *object,
2443 GAsyncResult *result,
2444 gpointer user_data)
2445 {
2446 TrackerSparqlBuilder *sparql;
2447 ProcessFileData *data;
2448 const gchar *urn;
2449 GFileInfo *file_info;
2450 guint64 time_;
2451 GFile *file;
2452 gchar *uri;
2453 GError *error = NULL;
2454 gboolean is_iri;
2455
2456 data = user_data;
2457 file = G_FILE (object);
2458 sparql = data->sparql;
2459 file_info = g_file_query_info_finish (file, result, &error);
2460
2461 if (error) {
2462 /* Something bad happened, notify about the error */
2463 tracker_miner_fs_file_notify (TRACKER_MINER_FS (data->miner), file, error);
2464 process_file_data_free (data);
2465 g_error_free (error);
2466 return;
2467 }
2468
2469 uri = g_file_get_uri (file);
2470 urn = miner_files_get_file_urn (TRACKER_MINER_FILES (data->miner), file, &is_iri);
2471
2472 /* We MUST have an IRI in attributes updating */
2473 if (!is_iri) {
2474 error = g_error_new_literal (miner_files_error_quark,
2475 0,
2476 "Received request to update attributes but no IRI available!");
2477 /* Notify about the error */
2478 tracker_miner_fs_file_notify (TRACKER_MINER_FS (data->miner), file, error);
2479 process_file_data_free (data);
2480 g_error_free (error);
2481 return;
2482 }
2483
2484 /* Update nfo:fileLastModified */
2485 tracker_sparql_builder_delete_open (sparql, NULL);
2486 tracker_sparql_builder_subject_iri (sparql, urn);
2487 tracker_sparql_builder_predicate (sparql, "nfo:fileLastModified");
2488 tracker_sparql_builder_object_variable (sparql, "lastmodified");
2489 tracker_sparql_builder_delete_close (sparql);
2490 tracker_sparql_builder_where_open (sparql);
2491 tracker_sparql_builder_subject_iri (sparql, urn);
2492 tracker_sparql_builder_predicate (sparql, "nfo:fileLastModified");
2493 tracker_sparql_builder_object_variable (sparql, "lastmodified");
2494 tracker_sparql_builder_where_close (sparql);
2495 tracker_sparql_builder_insert_open (sparql, NULL);
2496 tracker_sparql_builder_graph_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
2497 tracker_sparql_builder_subject_iri (sparql, urn);
2498 time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2499 tracker_sparql_builder_predicate (sparql, "nfo:fileLastModified");
2500 tracker_sparql_builder_object_date (sparql, (time_t *) &time_);
2501 tracker_sparql_builder_graph_close (sparql);
2502 tracker_sparql_builder_insert_close (sparql);
2503
2504 /* Update nfo:fileLastAccessed */
2505 tracker_sparql_builder_delete_open (sparql, NULL);
2506 tracker_sparql_builder_subject_iri (sparql, urn);
2507 tracker_sparql_builder_predicate (sparql, "nfo:fileLastAccessed");
2508 tracker_sparql_builder_object_variable (sparql, "lastaccessed");
2509 tracker_sparql_builder_delete_close (sparql);
2510 tracker_sparql_builder_where_open (sparql);
2511 tracker_sparql_builder_subject_iri (sparql, urn);
2512 tracker_sparql_builder_predicate (sparql, "nfo:fileLastAccessed");
2513 tracker_sparql_builder_object_variable (sparql, "lastaccessed");
2514 tracker_sparql_builder_where_close (sparql);
2515 tracker_sparql_builder_insert_open (sparql, NULL);
2516 tracker_sparql_builder_graph_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
2517 tracker_sparql_builder_subject_iri (sparql, urn);
2518 time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2519 tracker_sparql_builder_predicate (sparql, "nfo:fileLastAccessed");
2520 tracker_sparql_builder_object_date (sparql, (time_t *) &time_);
2521 tracker_sparql_builder_graph_close (sparql);
2522 tracker_sparql_builder_insert_close (sparql);
2523
2524 g_object_unref (file_info);
2525 g_free (uri);
2526
2527 /* Notify about the success */
2528 tracker_miner_fs_file_notify (TRACKER_MINER_FS (data->miner), data->file, NULL);
2529
2530 process_file_data_free (data);
2531 }
2532
2533 static gboolean
2534 miner_files_process_file_attributes (TrackerMinerFS *fs,
2535 GFile *file,
2536 TrackerSparqlBuilder *sparql,
2537 GCancellable *cancellable)
2538 {
2539 ProcessFileData *data;
2540 const gchar *attrs;
2541
2542 data = g_slice_new0 (ProcessFileData);
2543 data->miner = g_object_ref (fs);
2544 data->cancellable = g_object_ref (cancellable);
2545 data->sparql = g_object_ref (sparql);
2546 data->file = g_object_ref (file);
2547
2548 /* Query only attributes that may change in an ATTRIBUTES_UPDATED event */
2549 attrs = G_FILE_ATTRIBUTE_TIME_MODIFIED ","
2550 G_FILE_ATTRIBUTE_TIME_ACCESS;
2551
2552 g_file_query_info_async (file,
2553 attrs,
2554 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2555 G_PRIORITY_DEFAULT,
2556 cancellable,
2557 process_file_attributes_cb,
2558 data);
2559
2560 return TRUE;
2561 }
2562
2563 static gboolean
2564 miner_files_ignore_next_update_file (TrackerMinerFS *fs,
2565 GFile *file,
2566 TrackerSparqlBuilder *sparql,
2567 GCancellable *cancellable)
2568 {
2569 const gchar *attrs;
2570 const gchar *mime_type;
2571 GFileInfo *file_info;
2572 guint64 time_;
2573 gchar *uri;
2574 GError *error = NULL;
2575
2576 attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE ","
2577 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
2578 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
2579 G_FILE_ATTRIBUTE_STANDARD_SIZE ","
2580 G_FILE_ATTRIBUTE_TIME_MODIFIED ","
2581 G_FILE_ATTRIBUTE_TIME_ACCESS;
2582
2583 file_info = g_file_query_info (file, attrs,
2584 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2585 cancellable, &error);
2586
2587 if (error) {
2588 g_warning ("Can't ignore-next-update: '%s'", error->message);
2589 g_clear_error (&error);
2590 return FALSE;
2591 }
2592
2593 uri = g_file_get_uri (file);
2594 mime_type = g_file_info_get_content_type (file_info);
2595
2596 /* For ignore-next-update we only write a few properties back. These properties
2597 * should NEVER be marked as tracker:writeback in the ontology! (else you break
2598 * the tracker-writeback feature) */
2599
2600 tracker_sparql_builder_insert_silent_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
2601
2602 tracker_sparql_builder_subject_variable (sparql, "urn");
2603 tracker_sparql_builder_predicate (sparql, "a");
2604 tracker_sparql_builder_object (sparql, "nfo:FileDataObject");
2605
2606 tracker_sparql_builder_predicate (sparql, "nfo:fileSize");
2607 tracker_sparql_builder_object_int64 (sparql, g_file_info_get_size (file_info));
2608
2609 time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2610 tracker_sparql_builder_predicate (sparql, "nfo:fileLastModified");
2611 tracker_sparql_builder_object_date (sparql, (time_t *) &time_);
2612
2613 time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2614 tracker_sparql_builder_predicate (sparql, "nfo:fileLastAccessed");
2615 tracker_sparql_builder_object_date (sparql, (time_t *) &time_);
2616
2617 tracker_sparql_builder_predicate (sparql, "nie:mimeType");
2618 tracker_sparql_builder_object_string (sparql, mime_type);
2619
2620 tracker_sparql_builder_insert_close (sparql);
2621
2622 tracker_sparql_builder_where_open (sparql);
2623
2624 tracker_sparql_builder_subject_variable (sparql, "urn");
2625 tracker_sparql_builder_predicate (sparql, "nie:url");
2626 tracker_sparql_builder_object_string (sparql, uri);
2627
2628 tracker_sparql_builder_where_close (sparql);
2629
2630 g_object_unref (file_info);
2631 g_free (uri);
2632
2633 return TRUE;
2634 }
2635
2636 static void
2637 miner_files_finished (TrackerMinerFS *fs)
2638 {
2639 tracker_db_manager_set_last_crawl_done (TRUE);
2640 }
2641
2642 TrackerMiner *
2643 tracker_miner_files_new (TrackerConfig *config,
2644 GError **error)
2645 {
2646 return g_initable_new (TRACKER_TYPE_MINER_FILES,
2647 NULL,
2648 error,
2649 "name", "Files",
2650 "config", config,
2651 "processing-pool-wait-limit", 10,
2652 "processing-pool-ready-limit", 100,
2653 NULL);
2654 }
2655
2656 gboolean
2657 tracker_miner_files_check_file (GFile *file,
2658 GSList *ignored_file_paths,
2659 GSList *ignored_file_patterns)
2660 {
2661 GSList *l;
2662 gchar *basename;
2663 gchar *path;
2664 gboolean should_process;
2665
2666 should_process = FALSE;
2667 basename = NULL;
2668 path = NULL;
2669
2670 if (tracker_file_is_hidden (file)) {
2671 /* Ignore hidden files */
2672 goto done;
2673 }
2674
2675 path = g_file_get_path (file);
2676
2677 for (l = ignored_file_paths; l; l = l->next) {
2678 if (strcmp (l->data, path) == 0) {
2679 goto done;
2680 }
2681 }
2682
2683 basename = g_file_get_basename (file);
2684
2685 for (l = ignored_file_patterns; l; l = l->next) {
2686 if (g_pattern_match_string (l->data, basename)) {
2687 goto done;
2688 }
2689 }
2690
2691 should_process = TRUE;
2692
2693 done:
2694 g_free (basename);
2695 g_free (path);
2696
2697 return should_process;
2698 }
2699
2700 gboolean
2701 tracker_miner_files_check_directory (GFile *file,
2702 GSList *index_recursive_directories,
2703 GSList *index_single_directories,
2704 GSList *ignored_directory_paths,
2705 GSList *ignored_directory_patterns)
2706 {
2707 GSList *l;
2708 gchar *basename;
2709 gchar *path;
2710 gboolean should_process;
2711 gboolean is_hidden;
2712
2713 should_process = FALSE;
2714 basename = NULL;
2715
2716 path = g_file_get_path (file);
2717
2718 /* First we check the GIO hidden check. This does a number of
2719 * things for us which is good (like checking ".foo" dirs).
2720 */
2721 is_hidden = tracker_file_is_hidden (file);
2722
2723 #ifdef __linux__
2724 /* Second we check if the file is on FAT and if the hidden
2725 * attribute is set. GIO does this but ONLY on a Windows OS,
2726 * not for Windows files under a Linux OS, so we have to check
2727 * anyway.
2728 */
2729 if (!is_hidden) {
2730 int fd;
2731
2732 fd = g_open (path, O_RDONLY, 0);
2733 if (fd != -1) {
2734 __u32 attrs;
2735
2736 if (ioctl (fd, FAT_IOCTL_GET_ATTRIBUTES, &attrs) == 0) {
2737 is_hidden = attrs & ATTR_HIDDEN ? TRUE : FALSE;
2738 }
2739
2740 close (fd);
2741 }
2742 }
2743 #endif /* __linux__ */
2744
2745 if (is_hidden) {
2746 /* FIXME: We need to check if the file is actually a
2747 * config specified location before blanket ignoring
2748 * all hidden files.
2749 */
2750 if (tracker_string_in_gslist (path, index_recursive_directories)) {
2751 should_process = TRUE;
2752 }
2753
2754 if (tracker_string_in_gslist (path, index_single_directories)) {
2755 should_process = TRUE;
2756 }
2757
2758 /* Ignore hidden dirs */
2759 goto done;
2760 }
2761
2762 for (l = ignored_directory_paths; l; l = l->next) {
2763 if (strcmp (l->data, path) == 0) {
2764 goto done;
2765 }
2766 }
2767
2768 basename = g_file_get_basename (file);
2769
2770 for (l = ignored_directory_patterns; l; l = l->next) {
2771 if (g_pattern_match_string (l->data, basename)) {
2772 goto done;
2773 }
2774 }
2775
2776 /* Check module directory ignore patterns */
2777 should_process = TRUE;
2778
2779 done:
2780 g_free (basename);
2781 g_free (path);
2782
2783 return should_process;
2784 }
2785
2786 gboolean
2787 tracker_miner_files_check_directory_contents (GFile *parent,
2788 GList *children,
2789 GSList *ignored_content)
2790 {
2791 GSList *l;
2792
2793 if (!ignored_content) {
2794 return TRUE;
2795 }
2796
2797 while (children) {
2798 gchar *basename;
2799
2800 basename = g_file_get_basename (children->data);
2801
2802 for (l = ignored_content; l; l = l->next) {
2803 if (g_strcmp0 (basename, l->data) == 0) {
2804 gchar *parent_uri;
2805
2806 parent_uri = g_file_get_uri (parent);
2807 /* g_debug ("Directory '%s' ignored since it contains a file named '%s'", */
2808 /* parent_uri, basename); */
2809
2810 g_free (parent_uri);
2811 g_free (basename);
2812
2813 return FALSE;
2814 }
2815 }
2816
2817 children = children->next;
2818 g_free (basename);
2819 }
2820
2821 return TRUE;
2822 }
2823
2824 gboolean
2825 tracker_miner_files_monitor_directory (GFile *file,
2826 gboolean enable_monitors,
2827 GSList *directories_to_check)
2828 {
2829 if (!enable_monitors) {
2830 return FALSE;
2831 }
2832
2833 /* We'll only get this signal for the directories where check_directory()
2834 * and check_directory_contents() returned TRUE, so by default we want
2835 * these directories to be indexed. */
2836
2837 return TRUE;
2838 }
2839
2840 static void
2841 remove_files_in_removable_media_cb (GObject *object,
2842 GAsyncResult *result,
2843 gpointer user_data)
2844 {
2845 GError *error = NULL;
2846
2847 tracker_sparql_connection_update_finish (TRACKER_SPARQL_CONNECTION (object), result, &error);
2848
2849 if (error) {
2850 g_critical ("Could not remove files in volumes: %s", error->message);
2851 g_error_free (error);
2852 }
2853 }
2854
2855 static gboolean
2856 miner_files_in_removable_media_remove_by_type (TrackerMinerFiles *miner,
2857 TrackerStorageType type)
2858 {
2859 gboolean removable;
2860 gboolean optical;
2861
2862 removable = TRACKER_STORAGE_TYPE_IS_REMOVABLE (type);
2863 optical = TRACKER_STORAGE_TYPE_IS_OPTICAL (type);
2864
2865 /* Only remove if any of the flags was TRUE */
2866 if (removable || optical) {
2867 GString *queries;
2868
2869 g_debug (" Removing all resources in store from %s ",
2870 optical ? "optical discs" : "removable devices");
2871
2872 queries = g_string_new ("");
2873
2874 /* Delete all resources where nie:dataSource is a volume
2875 * of the given type */
2876 g_string_append_printf (queries,
2877 "DELETE { "
2878 " ?f a rdfs:Resource . "
2879 " ?ie a rdfs:Resource "
2880 "} WHERE { "
2881 " ?v a tracker:Volume ; "
2882 " tracker:isRemovable %s ; "
2883 " tracker:isOptical %s . "
2884 " ?f nie:dataSource ?v . "
2885 " ?ie nie:isStoredAs ?f "
2886 "}",
2887 removable ? "true" : "false",
2888 optical ? "true" : "false");
2889
2890 tracker_sparql_connection_update_async (tracker_miner_get_connection (TRACKER_MINER (miner)),
2891 queries->str,
2892 G_PRIORITY_LOW,
2893 NULL,
2894 remove_files_in_removable_media_cb,
2895 NULL);
2896
2897 g_string_free (queries, TRUE);
2898
2899 return TRUE;
2900 }
2901
2902 return FALSE;
2903 }
2904
2905 static void
2906 miner_files_in_removable_media_remove_by_date (TrackerMinerFiles *miner,
2907 const gchar *date)
2908 {
2909 GString *queries;
2910
2911 g_debug (" Removing all resources in store from removable or "
2912 "optical devices not mounted after '%s'",
2913 date);
2914
2915 queries = g_string_new ("");
2916
2917 /* Delete all resources where nie:dataSource is a volume
2918 * which was last unmounted before the given date */
2919 g_string_append_printf (queries,
2920 "DELETE { "
2921 " ?f a rdfs:Resource . "
2922 " ?ie a rdfs:Resource "
2923 "} WHERE { "
2924 " ?v a tracker:Volume ; "
2925 " tracker:isRemovable true ; "
2926 " tracker:isMounted false ; "
2927 " tracker:unmountDate ?d . "
2928 " ?f nie:dataSource ?v . "
2929 " ?ie nie:isStoredAs ?f "
2930 " FILTER ( ?d < \"%s\") "
2931 "}",
2932 date);
2933
2934 tracker_sparql_connection_update_async (tracker_miner_get_connection (TRACKER_MINER (miner)),
2935 queries->str,
2936 G_PRIORITY_LOW,
2937 NULL,
2938 remove_files_in_removable_media_cb,
2939 NULL);
2940
2941 g_string_free (queries, TRUE);
2942 }
2943
2944 static void
2945 miner_files_add_removable_or_optical_directory (TrackerMinerFiles *mf,
2946 const gchar *mount_path,
2947 const gchar *uuid)
2948 {
2949 TrackerIndexingTree *indexing_tree;
2950 TrackerDirectoryFlags flags;
2951 GFile *mount_point_file;
2952
2953 mount_point_file = g_file_new_for_path (mount_path);
2954
2955 /* UUID may be NULL, and if so, get it */
2956 if (!uuid) {
2957 uuid = tracker_storage_get_uuid_for_file (mf->private->storage,
2958 mount_point_file);
2959 if (!uuid) {
2960 g_critical ("Couldn't get UUID for mount point '%s'",
2961 mount_path);
2962 g_object_unref (mount_point_file);
2963 return;
2964 }
2965 }
2966
2967 indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (mf));
2968 flags = TRACKER_DIRECTORY_FLAG_RECURSE |
2969 TRACKER_DIRECTORY_FLAG_CHECK_MTIME |
2970 TRACKER_DIRECTORY_FLAG_PRESERVE;
2971
2972 if (tracker_config_get_enable_monitors (mf->private->config)) {
2973 flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
2974 }
2975
2976 g_object_set_qdata_full (G_OBJECT (mount_point_file),
2977 mf->private->quark_mount_point_uuid,
2978 g_strdup (uuid),
2979 (GDestroyNotify) g_free);
2980
2981 g_message (" Adding removable/optical: '%s'", mount_path);
2982 tracker_indexing_tree_add (indexing_tree,
2983 mount_point_file,
2984 flags);
2985 g_object_unref (mount_point_file);
2986 }
2987
2988 gboolean
2989 tracker_miner_files_is_file_eligible (TrackerMinerFiles *miner,
2990 GFile *file)
2991 {
2992 TrackerConfig *config;
2993 GFile *dir;
2994 GFileInfo *file_info;
2995 gboolean is_dir;
2996
2997 file_info = g_file_query_info (file,
2998 G_FILE_ATTRIBUTE_STANDARD_TYPE,
2999 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3000 NULL, NULL);
3001
3002 if (!file_info) {
3003 /* file does not exist */
3004 return FALSE;
3005 }
3006
3007 is_dir = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY);
3008 g_object_unref (file_info);
3009
3010 g_object_get (miner,
3011 "config", &config,
3012 NULL);
3013
3014 if (is_dir) {
3015 dir = g_object_ref (file);
3016 } else {
3017 if (!tracker_miner_files_check_file (file,
3018 tracker_config_get_ignored_file_paths (config),
3019 tracker_config_get_ignored_file_patterns (config))) {
3020 /* file is not eligible to be indexed */
3021 g_object_unref (config);
3022 return FALSE;
3023 }
3024
3025 dir = g_file_get_parent (file);
3026 }
3027
3028 if (dir) {
3029 gboolean found = FALSE;
3030 GSList *l;
3031
3032 if (!tracker_miner_files_check_directory (dir,
3033 tracker_config_get_index_recursive_directories (config),
3034 tracker_config_get_index_single_directories (config),
3035 tracker_config_get_ignored_directory_paths (config),
3036 tracker_config_get_ignored_directory_patterns (config))) {
3037 /* file is not eligible to be indexed */
3038 g_object_unref (dir);
3039 g_object_unref (config);
3040 return FALSE;
3041 }
3042
3043 l = tracker_config_get_index_recursive_directories (config);
3044
3045 while (l && !found) {
3046 GFile *config_dir;
3047
3048 config_dir = g_file_new_for_path ((gchar *) l->data);
3049
3050 if (g_file_equal (dir, config_dir) ||
3051 g_file_has_prefix (dir, config_dir)) {
3052 found = TRUE;
3053 }
3054
3055 g_object_unref (config_dir);
3056 l = l->next;
3057 }
3058
3059 l = tracker_config_get_index_single_directories (config);
3060
3061 while (l && !found) {
3062 GFile *config_dir;
3063
3064 config_dir = g_file_new_for_path ((gchar *) l->data);
3065
3066 if (g_file_equal (dir, config_dir)) {
3067 found = TRUE;
3068 }
3069
3070 g_object_unref (config_dir);
3071 l = l->next;
3072 }
3073
3074 g_object_unref (dir);
3075
3076 if (!found) {
3077 /* file is not eligible to be indexed */
3078 g_object_unref (config);
3079 return FALSE;
3080 }
3081 }
3082
3083 g_object_unref (config);
3084
3085 /* file is eligible to be indexed */
3086 return TRUE;
3087 }