1 /*
2  * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
3  * Copyright (C) 2015 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA  02110-1301, USA.
19  */
20 
21 #include "config.h"
22 
23 #include <gio/gio.h>
24 #include <string.h>
25 
26 #include <libtracker-common/tracker-dbus.h>
27 #include <libtracker-common/tracker-type-utils.h>
28 #include <libtracker-common/tracker-domain-ontology.h>
29 #include <libtracker-miner/tracker-miner.h>
30 
31 #include "tracker-miner-manager.h"
32 
33 /**
34  * SECTION:tracker-miner-manager
35  * @short_description: External control and monitoring of miners
36  * @include: libtracker-control/tracker-control.h
37  *
38  * #TrackerMinerManager keeps track of available miners, their current
39  * progress/status, and also allows basic external control on them, such
40  * as pausing or resuming data processing.
41  **/
42 
43 #define DESKTOP_ENTRY_GROUP "D-BUS Service"
44 #define DBUS_NAME_SUFFIX_KEY "NameSuffix"
45 #define DBUS_PATH_KEY "Path"
46 #define DISPLAY_NAME_KEY "DisplayName"
47 #define DESCRIPTION_KEY "Comment"
48 
49 #define METHOD_INDEX_FILE "IndexFile"
50 #define METHOD_INDEX_FILE_FOR_PROCESS "IndexFileForProcess"
51 
52 typedef struct TrackerMinerManagerPrivate TrackerMinerManagerPrivate;
53 typedef struct MinerData MinerData;
54 
55 struct MinerData {
56 	gchar *dbus_name;
57 	gchar *dbus_path;
58 	gchar *display_name;
59 	gchar *description;
60 	gchar *name_suffix;
61 
62 	GDBusConnection *connection;
63 	guint progress_signal;
64 	guint paused_signal;
65 	guint resumed_signal;
66 	guint watch_name_id;
67 	GObject *manager; /* weak */
68 };
69 
70 struct TrackerMinerManagerPrivate {
71 	GDBusConnection *connection;
72 	GList *miners;
73 	GHashTable *miner_proxies;
74 
75 	/* Property values */
76 	gboolean auto_start;
77 	gchar *domain_ontology_name;
78 	TrackerDomainOntology *domain_ontology;
79 };
80 
81 static void miner_manager_initable_iface_init (GInitableIface         *iface);
82 static void miner_manager_set_property        (GObject             *object,
83                                                guint                param_id,
84                                                const GValue        *value,
85                                                GParamSpec          *pspec);
86 static void miner_manager_get_property        (GObject             *object,
87                                                guint                param_id,
88                                                GValue              *value,
89                                                GParamSpec          *pspec);
90 static void miner_manager_finalize            (GObject             *object);
91 static void initialize_miners_data            (TrackerMinerManager *manager);
92 
93 G_DEFINE_TYPE_WITH_CODE (TrackerMinerManager, tracker_miner_manager, G_TYPE_OBJECT,
94                          G_ADD_PRIVATE (TrackerMinerManager)
95                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
96                                                 miner_manager_initable_iface_init));
97 
98 enum {
99 	PROP_0,
100 	PROP_AUTO_START,
101 	PROP_DOMAIN_ONTOLOGY
102 };
103 
104 enum {
105 	MINER_PROGRESS,
106 	MINER_PAUSED,
107 	MINER_RESUMED,
108 	MINER_ACTIVATED,
109 	MINER_DEACTIVATED,
110 	LAST_SIGNAL
111 };
112 
113 static guint signals [LAST_SIGNAL] = { 0 };
114 
115 static void
tracker_miner_manager_class_init(TrackerMinerManagerClass * klass)116 tracker_miner_manager_class_init (TrackerMinerManagerClass *klass)
117 {
118 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
119 
120 	object_class->set_property = miner_manager_set_property;
121 	object_class->get_property = miner_manager_get_property;
122 	object_class->finalize = miner_manager_finalize;
123 
124 	g_object_class_install_property (object_class,
125 	                                 PROP_AUTO_START,
126 	                                 g_param_spec_boolean ("auto-start",
127 	                                                      "Auto Start",
128 	                                                      "If set, auto starts miners when querying their status",
129 	                                                       TRUE,
130 	                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
131 	g_object_class_install_property (object_class,
132 	                                 PROP_DOMAIN_ONTOLOGY,
133 	                                 g_param_spec_string ("domain-ontology",
134 	                                                      "Domain ontology",
135 	                                                      "The domain ontology this object controls",
136 	                                                      NULL,
137 	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
138 
139 	/**
140 	 * TrackerMinerManager::miner-progress:
141 	 * @manager: the #TrackerMinerManager
142 	 * @miner: miner reference
143 	 * @status: miner status
144 	 * @progress: miner progress, from 0 to 1
145 	 * @remaining_time: remaining processing time
146 	 *
147 	 * The ::miner-progress signal is meant to report status/progress changes
148 	 * in any tracked miner.
149 	 *
150 	 * Since: 0.12
151 	 **/
152 	signals [MINER_PROGRESS] =
153 		g_signal_new ("miner-progress",
154 		              G_OBJECT_CLASS_TYPE (object_class),
155 		              G_SIGNAL_RUN_LAST,
156 		              G_STRUCT_OFFSET (TrackerMinerManagerClass, miner_progress),
157 		              NULL, NULL,
158 		              NULL,
159 		              G_TYPE_NONE, 4,
160 		              G_TYPE_STRING,
161 		              G_TYPE_STRING,
162 		              G_TYPE_DOUBLE,
163 		              G_TYPE_INT);
164 	/**
165 	 * TrackerMinerManager::miner-paused:
166 	 * @manager: the #TrackerMinerManager
167 	 * @miner: miner reference
168 	 *
169 	 * The ::miner-paused signal will be emitted whenever a miner
170 	 * (referenced by @miner) is paused.
171 	 *
172 	 * Since: 0.8
173 	 **/
174 	signals [MINER_PAUSED] =
175 		g_signal_new ("miner-paused",
176 		              G_OBJECT_CLASS_TYPE (object_class),
177 		              G_SIGNAL_RUN_LAST,
178 		              G_STRUCT_OFFSET (TrackerMinerManagerClass, miner_paused),
179 		              NULL, NULL,
180 		              NULL,
181 		              G_TYPE_NONE, 1,
182 		              G_TYPE_STRING);
183 	/**
184 	 * TrackerMinerManager::miner-resumed:
185 	 * @manager: the #TrackerMinerManager
186 	 * @miner: miner reference
187 	 *
188 	 * The ::miner-resumed signal will be emitted whenever a miner
189 	 * (referenced by @miner) is resumed.
190 	 *
191 	 * Since: 0.8
192 	 **/
193 	signals [MINER_RESUMED] =
194 		g_signal_new ("miner-resumed",
195 		              G_OBJECT_CLASS_TYPE (object_class),
196 		              G_SIGNAL_RUN_LAST,
197 		              G_STRUCT_OFFSET (TrackerMinerManagerClass, miner_resumed),
198 		              NULL, NULL,
199 		              NULL,
200 		              G_TYPE_NONE, 1,
201 		              G_TYPE_STRING);
202 	/**
203 	 * TrackerMinerManager::miner-activated:
204 	 * @manager: the #TrackerMinerManager
205 	 * @miner: miner reference
206 	 *
207 	 * The ::miner-activated signal will be emitted whenever a miner
208 	 * (referenced by @miner) is activated (technically, this means
209 	 * the miner has appeared in the session bus).
210 	 *
211 	 * Since: 0.8
212 	 **/
213 	signals [MINER_ACTIVATED] =
214 		g_signal_new ("miner-activated",
215 		              G_OBJECT_CLASS_TYPE (object_class),
216 		              G_SIGNAL_RUN_LAST,
217 		              G_STRUCT_OFFSET (TrackerMinerManagerClass, miner_activated),
218 		              NULL, NULL,
219 		              NULL,
220 		              G_TYPE_NONE, 1,
221 		              G_TYPE_STRING);
222 	/**
223 	 * TrackerMinerManager::miner-deactivated:
224 	 * @manager: the #TrackerMinerManager
225 	 * @miner: miner reference
226 	 *
227 	 * The ::miner-deactivated signal will be emitted whenever a miner
228 	 * (referenced by @miner) is deactivated (technically, this means
229 	 * the miner has disappeared from the session bus).
230 	 *
231 	 * Since: 0.8
232 	 **/
233 	signals [MINER_DEACTIVATED] =
234 		g_signal_new ("miner-deactivated",
235 		              G_OBJECT_CLASS_TYPE (object_class),
236 		              G_SIGNAL_RUN_LAST,
237 		              G_STRUCT_OFFSET (TrackerMinerManagerClass, miner_deactivated),
238 		              NULL, NULL,
239 		              NULL,
240 		              G_TYPE_NONE, 1,
241 		              G_TYPE_STRING);
242 }
243 
244 static void
miner_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)245 miner_manager_set_property (GObject      *object,
246                             guint         prop_id,
247                             const GValue *value,
248                             GParamSpec   *pspec)
249 {
250 	TrackerMinerManager *manager;
251 	TrackerMinerManagerPrivate *priv;
252 
253 	manager = TRACKER_MINER_MANAGER (object);
254 	priv = tracker_miner_manager_get_instance_private (manager);
255 
256 	switch (prop_id) {
257 	case PROP_AUTO_START:
258 		priv->auto_start = g_value_get_boolean (value);
259 		break;
260 	case PROP_DOMAIN_ONTOLOGY:
261 		priv->domain_ontology_name = g_value_dup_string (value);
262 		break;
263 	default:
264 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
265 		break;
266 	}
267 }
268 
269 static void
miner_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)270 miner_manager_get_property (GObject    *object,
271                             guint       prop_id,
272                             GValue     *value,
273                             GParamSpec *pspec)
274 {
275 	TrackerMinerManager *manager;
276 	TrackerMinerManagerPrivate *priv;
277 
278 	manager = TRACKER_MINER_MANAGER (object);
279 	priv = tracker_miner_manager_get_instance_private (manager);
280 
281 	switch (prop_id) {
282 	case PROP_AUTO_START:
283 		g_value_set_boolean (value, priv->auto_start);
284 		break;
285 	case PROP_DOMAIN_ONTOLOGY:
286 		g_value_set_string (value, priv->domain_ontology_name);
287 		break;
288 	default:
289 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
290 		break;
291 	}
292 }
293 
294 static GDBusProxy *
find_miner_proxy(TrackerMinerManager * manager,const gchar * name,gboolean try_suffix)295 find_miner_proxy (TrackerMinerManager *manager,
296                   const gchar         *name,
297                   gboolean             try_suffix)
298 {
299 	TrackerMinerManagerPrivate *priv;
300 	GHashTableIter iter;
301 	gpointer key, value;
302 
303 	priv = tracker_miner_manager_get_instance_private (manager);
304 	g_hash_table_iter_init (&iter, priv->miner_proxies);
305 
306 	while (g_hash_table_iter_next (&iter, &key, &value)) {
307 		if (g_strcmp0 (name, (gchar *) value) == 0) {
308 			return key;
309 		}
310 
311 		if (try_suffix) {
312 			if (g_str_has_suffix (value, name)) {
313 				return key;
314 			}
315 		}
316 	}
317 
318 	return NULL;
319 }
320 
321 static void
miner_appears(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)322 miner_appears (GDBusConnection *connection,
323                const gchar     *name,
324                const gchar     *name_owner,
325                gpointer         user_data)
326 {
327 	MinerData *data = user_data;
328 	if (data->manager) {
329 		g_signal_emit (data->manager, signals[MINER_ACTIVATED], 0, data->dbus_name);
330 	}
331 }
332 
333 static void
miner_disappears(GDBusConnection * connection,const gchar * name,gpointer user_data)334 miner_disappears (GDBusConnection *connection,
335                   const gchar     *name,
336                   gpointer         user_data)
337 {
338 	MinerData *data = user_data;
339 	if (data->manager) {
340 		g_signal_emit (data->manager, signals[MINER_DEACTIVATED], 0, data->dbus_name);
341 	}
342 }
343 
344 static void
miner_progress_changed(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)345 miner_progress_changed (GDBusConnection *connection,
346                         const gchar     *sender_name,
347                         const gchar     *object_path,
348                         const gchar     *interface_name,
349                         const gchar     *signal_name,
350                         GVariant        *parameters,
351                         gpointer         user_data)
352 {
353 	MinerData *data = user_data;
354 	const gchar *status = NULL;
355 	gdouble progress = 0;
356 	gint remaining_time = -1;
357 
358 	g_variant_get (parameters, "(&sdi)", &status, &progress, &remaining_time);
359 	if (data->manager) {
360 		g_signal_emit (data->manager, signals[MINER_PROGRESS], 0, data->dbus_name, status, progress, remaining_time);
361 	}
362 }
363 
364 static void
miner_paused(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)365 miner_paused (GDBusConnection *connection,
366               const gchar     *sender_name,
367               const gchar     *object_path,
368               const gchar     *interface_name,
369               const gchar     *signal_name,
370               GVariant        *parameters,
371               gpointer         user_data)
372 {
373 	MinerData *data = user_data;
374 	if (data->manager) {
375 		g_signal_emit (data->manager, signals[MINER_PAUSED], 0, data->dbus_name);
376 	}
377 }
378 
379 static void
miner_resumed(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)380 miner_resumed (GDBusConnection *connection,
381                const gchar     *sender_name,
382                const gchar     *object_path,
383                const gchar     *interface_name,
384                const gchar     *signal_name,
385                GVariant        *parameters,
386                gpointer         user_data)
387 {
388 	MinerData *data = user_data;
389 	if (data->manager) {
390 		g_signal_emit (data->manager, signals[MINER_RESUMED], 0, data->dbus_name);
391 	}
392 }
393 
394 static void
data_manager_weak_notify(gpointer user_data,GObject * old_object)395 data_manager_weak_notify (gpointer user_data, GObject *old_object)
396 {
397 	MinerData *data = user_data;
398 	data->manager = NULL;
399 }
400 
401 static void
tracker_miner_manager_init(TrackerMinerManager * manager)402 tracker_miner_manager_init (TrackerMinerManager *manager)
403 {
404 	TrackerMinerManagerPrivate *priv;
405 
406 	priv = tracker_miner_manager_get_instance_private (manager);
407 
408 	priv->miner_proxies = g_hash_table_new_full (NULL, NULL,
409 	                                             (GDestroyNotify) g_object_unref,
410 	                                             (GDestroyNotify) g_free);
411 }
412 
413 static gboolean
miner_manager_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)414 miner_manager_initable_init (GInitable     *initable,
415                              GCancellable  *cancellable,
416                              GError       **error)
417 {
418 	TrackerMinerManager *manager;
419 	GError *inner_error = NULL;
420 	TrackerMinerManagerPrivate *priv;
421 	GList *m;
422 
423 	manager = TRACKER_MINER_MANAGER (initable);
424 	priv = tracker_miner_manager_get_instance_private (manager);
425 
426 	priv->connection = g_bus_get_sync (TRACKER_IPC_BUS, NULL, &inner_error);
427 	if (!priv->connection) {
428 		g_propagate_error (error, inner_error);
429 		return FALSE;
430 	}
431 
432 	priv->domain_ontology = tracker_domain_ontology_new (priv->domain_ontology_name,
433 	                                                     cancellable, &inner_error);
434 	if (!priv->domain_ontology) {
435 		g_propagate_error (error, inner_error);
436 		return FALSE;
437 	}
438 
439 	initialize_miners_data (manager);
440 
441 	for (m = priv->miners; m; m = m->next) {
442 		GDBusProxy *proxy;
443 		MinerData *data;
444 
445 		data = m->data;
446 		data->connection = g_object_ref (priv->connection);
447 		data->manager = G_OBJECT (manager);
448 		g_object_weak_ref (data->manager, data_manager_weak_notify, data);
449 
450 		proxy = g_dbus_proxy_new_sync (priv->connection,
451 		                               (priv->auto_start ?
452 		                                G_DBUS_PROXY_FLAGS_NONE :
453 		                                G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
454 		                               NULL,
455 		                               data->dbus_name,
456 		                               data->dbus_path,
457 		                               TRACKER_MINER_DBUS_INTERFACE,
458 		                               NULL,
459 		                               &inner_error);
460 		/* This error shouldn't be considered fatal */
461 		if (inner_error) {
462 			g_critical ("Could not create proxy on the D-Bus session bus, %s",
463 			            inner_error ? inner_error->message : "no error given.");
464 			g_clear_error (&inner_error);
465 			continue;
466 		}
467 
468 		data->progress_signal = g_dbus_connection_signal_subscribe (priv->connection,
469 		                                                            data->dbus_name,
470 		                                                            TRACKER_MINER_DBUS_INTERFACE,
471 		                                                            "Progress",
472 		                                                            data->dbus_path,
473 		                                                            NULL,
474 		                                                            G_DBUS_SIGNAL_FLAGS_NONE,
475 		                                                            miner_progress_changed,
476 		                                                            data,
477 		                                                            NULL);
478 
479 		data->paused_signal = g_dbus_connection_signal_subscribe (priv->connection,
480 		                                                          data->dbus_name,
481 		                                                          TRACKER_MINER_DBUS_INTERFACE,
482 		                                                          "Paused",
483 		                                                          data->dbus_path,
484 		                                                          NULL,
485 		                                                          G_DBUS_SIGNAL_FLAGS_NONE,
486 		                                                          miner_paused,
487 		                                                          data,
488 		                                                          NULL);
489 
490 
491 		data->resumed_signal = g_dbus_connection_signal_subscribe (priv->connection,
492 		                                                           data->dbus_name,
493 		                                                           TRACKER_MINER_DBUS_INTERFACE,
494 		                                                           "Resumed",
495 		                                                           data->dbus_path,
496 		                                                           NULL,
497 		                                                           G_DBUS_SIGNAL_FLAGS_NONE,
498 		                                                           miner_resumed,
499 		                                                           data,
500 		                                                           NULL);
501 
502 		g_hash_table_insert (priv->miner_proxies, proxy, g_strdup (data->dbus_name));
503 
504 		data->watch_name_id = g_bus_watch_name (TRACKER_IPC_BUS,
505 		                                        data->dbus_name,
506 		                                        G_BUS_NAME_WATCHER_FLAGS_NONE,
507 		                                        miner_appears,
508 		                                        miner_disappears,
509 		                                        data,
510 		                                        NULL);
511 
512 	}
513 
514 	return TRUE;
515 }
516 
517 static void
miner_manager_initable_iface_init(GInitableIface * iface)518 miner_manager_initable_iface_init (GInitableIface *iface)
519 {
520 	iface->init = miner_manager_initable_init;
521 }
522 
523 
524 static void
miner_data_free(MinerData * data)525 miner_data_free (MinerData *data)
526 {
527 	if (data->watch_name_id != 0) {
528 		g_bus_unwatch_name (data->watch_name_id);
529 	}
530 
531 	if (data->progress_signal) {
532 		g_dbus_connection_signal_unsubscribe (data->connection,
533 		                                      data->progress_signal);
534 	}
535 
536 	if (data->paused_signal) {
537 		g_dbus_connection_signal_unsubscribe (data->connection,
538 		                                      data->paused_signal);
539 	}
540 
541 	if (data->resumed_signal) {
542 		g_dbus_connection_signal_unsubscribe (data->connection,
543 		                                      data->resumed_signal);
544 	}
545 
546 	if (data->connection) {
547 		g_object_unref (data->connection);
548 	}
549 
550 	if (data->manager) {
551 		g_object_weak_unref (data->manager, data_manager_weak_notify, data);
552 	}
553 
554 	g_free (data->dbus_path);
555 	g_free (data->dbus_name);
556 	g_free (data->display_name);
557 	g_free (data->description);
558 	g_free (data->name_suffix);
559 	g_slice_free (MinerData, data);
560 }
561 
562 static void
miner_manager_finalize(GObject * object)563 miner_manager_finalize (GObject *object)
564 {
565 	TrackerMinerManagerPrivate *priv;
566 
567 	priv = tracker_miner_manager_get_instance_private (TRACKER_MINER_MANAGER (object));
568 
569 	if (priv->connection) {
570 		g_object_unref (priv->connection);
571 	}
572 
573 	g_list_foreach (priv->miners, (GFunc) miner_data_free, NULL);
574 	g_list_free (priv->miners);
575 	g_hash_table_unref (priv->miner_proxies);
576 	g_free (priv->domain_ontology_name);
577 	g_clear_pointer (&priv->domain_ontology, tracker_domain_ontology_unref);
578 
579 	G_OBJECT_CLASS (tracker_miner_manager_parent_class)->finalize (object);
580 }
581 
582 /**
583  * tracker_miner_manager_new:
584  *
585  * Creates a new #TrackerMinerManager instance.
586  *
587  * Note: Auto-starting miners when querying status will be enabled.
588  *
589  * Returns: a #TrackerMinerManager or #NULL if an error happened.
590  *
591  * Since: 0.8
592  **/
593 TrackerMinerManager *
tracker_miner_manager_new(void)594 tracker_miner_manager_new (void)
595 {
596 	GError *inner_error = NULL;
597 	TrackerMinerManager *manager;
598 
599 	manager = g_initable_new (TRACKER_TYPE_MINER_MANAGER,
600 	                          NULL,
601 	                          &inner_error,
602 	                          NULL);
603 	if (!manager) {
604 		g_critical ("Couldn't create new TrackerMinerManager: '%s'",
605 		            inner_error ? inner_error->message : "unknown error");
606 		g_clear_error (&inner_error);
607 	}
608 
609 	return manager;
610 }
611 
612 /**
613  * tracker_miner_manager_new_full:
614  * @auto_start: Flag to disable auto-starting the miners when querying status
615  * @error: a #GError to report errors.
616  *
617  * Creates a new #TrackerMinerManager.
618  *
619  * Returns: a #TrackerMinerManager. On error, #NULL is returned and @error is set
620  * accordingly.
621  *
622  * Since: 0.10.5
623  **/
624 TrackerMinerManager *
tracker_miner_manager_new_full(gboolean auto_start,GError ** error)625 tracker_miner_manager_new_full (gboolean   auto_start,
626                                 GError   **error)
627 {
628 	GError *inner_error = NULL;
629 	TrackerMinerManager *manager;
630 	const gchar *domain_ontology;
631 
632 	domain_ontology = tracker_sparql_connection_get_domain ();
633 	manager = g_initable_new (TRACKER_TYPE_MINER_MANAGER,
634 	                          NULL,
635 	                          &inner_error,
636 	                          "domain-ontology", domain_ontology,
637 	                          "auto-start", auto_start,
638 	                          NULL);
639 	if (inner_error)
640 		g_propagate_error (error, inner_error);
641 
642 	return manager;
643 }
644 
645 /**
646  * tracker_miner_manager_get_running:
647  * @manager: a #trackerMinerManager
648  *
649  * Returns a list of references for all active miners. Active miners
650  * are miners which are running within a process.
651  *
652  * Returns: (transfer full) (element-type utf8) (nullable): a #GSList which
653  * must be freed with g_slist_free() and all contained data with g_free().
654  * Otherwise %NULL is returned if there are no miners.
655  *
656  * Since: 0.8
657  **/
658 GSList *
tracker_miner_manager_get_running(TrackerMinerManager * manager)659 tracker_miner_manager_get_running (TrackerMinerManager *manager)
660 {
661 	TrackerMinerManagerPrivate *priv;
662 	GSList *list = NULL;
663 	GError *error = NULL;
664 	GVariant *v;
665 	GVariantIter *iter;
666 	const gchar *str = NULL;
667 	gchar *prefix;
668 
669 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), NULL);
670 
671 	priv = tracker_miner_manager_get_instance_private (manager);
672 
673 	if (!priv->connection) {
674 		return NULL;
675 	}
676 
677 	v = g_dbus_connection_call_sync (priv->connection,
678 	                                 "org.freedesktop.DBus",
679 	                                 "/org/freedesktop/DBus",
680 	                                 "org.freedesktop.DBus",
681 	                                 "ListNames",
682 	                                 NULL,
683 	                                 G_VARIANT_TYPE ("(as)"),
684 	                                 G_DBUS_CALL_FLAGS_NONE,
685 	                                 -1,
686 	                                 NULL,
687 	                                 &error);
688 
689 	if (error) {
690 		g_critical ("Could not get a list of names registered on the session bus, %s",
691 		            error ? error->message : "no error given");
692 		g_clear_error (&error);
693 		return NULL;
694 	}
695 
696 	prefix = tracker_domain_ontology_get_domain (priv->domain_ontology, "Tracker1.Miner");
697 
698 	g_variant_get (v, "(as)", &iter);
699 	while (g_variant_iter_loop (iter, "&s", &str)) {
700 		if (!g_str_has_prefix (str, prefix)) {
701 			continue;
702 		}
703 
704 		list = g_slist_prepend (list, g_strdup (str));
705 	}
706 
707 	g_variant_iter_free (iter);
708 	g_variant_unref (v);
709 	g_free (prefix);
710 
711 	list = g_slist_reverse (list);
712 
713 	return list;
714 }
715 
716 static void
check_file(GFile * file,gpointer user_data)717 check_file (GFile    *file,
718             gpointer  user_data)
719 {
720 	TrackerMinerManager *manager;
721 	TrackerMinerManagerPrivate *priv;
722 	GKeyFile *key_file;
723 	gchar *path, *dbus_path, *display_name, *name_suffix, *full_name_suffix, *description;
724 	GError *error = NULL;
725 	MinerData *data;
726 
727 	manager = user_data;
728 	path = g_file_get_path (file);
729 	priv = tracker_miner_manager_get_instance_private (manager);
730 
731 	key_file = g_key_file_new ();
732 	g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &error);
733 
734 	if (error) {
735 		g_warning ("Error parsing miner .desktop file: %s", error->message);
736 		g_error_free (error);
737 		g_key_file_free (key_file);
738 		return;
739 	}
740 
741 	dbus_path = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, DBUS_PATH_KEY, NULL);
742 	display_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, DISPLAY_NAME_KEY, NULL, NULL);
743 	name_suffix = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, DBUS_NAME_SUFFIX_KEY, NULL);
744 
745 	if (!dbus_path || !display_name || !name_suffix) {
746 		g_warning ("Essential data (DBusPath, NameSuffix or Name) are missing in miner .desktop file");
747 		g_key_file_free (key_file);
748 		g_free (dbus_path);
749 		g_free (display_name);
750 		g_free (name_suffix);
751 		return;
752 	}
753 
754 	if (!tracker_domain_ontology_uses_miner (priv->domain_ontology, name_suffix)) {
755 		/* Silently ignore, this domain ontology is not meant to use this miner */
756 		g_key_file_free (key_file);
757 		g_free (dbus_path);
758 		g_free (display_name);
759 		g_free (name_suffix);
760 		return;
761 	}
762 
763 	description = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, DESCRIPTION_KEY, NULL, NULL);
764 
765 	data = g_slice_new0 (MinerData);
766 	data->dbus_path = dbus_path;
767 	data->name_suffix = name_suffix;
768 
769 	full_name_suffix = g_strconcat ("Tracker1.", name_suffix, NULL);
770 	data->dbus_name = tracker_domain_ontology_get_domain (priv->domain_ontology,
771 	                                                      full_name_suffix);
772 	g_free (full_name_suffix);
773 
774 	data->display_name = display_name;
775 	data->description = description;    /* In .desktop file as _comment */
776 
777 	priv->miners = g_list_prepend (priv->miners, data);
778 
779 	g_key_file_free (key_file);
780 	g_free (path);
781 
782 	return;
783 }
784 
785 static void
directory_foreach(GFile * file,gchar * suffix,GFunc func,gpointer user_data)786 directory_foreach (GFile    *file,
787                    gchar    *suffix,
788                    GFunc     func,
789                    gpointer  user_data)
790 {
791 	GFileEnumerator *enumerator;
792 	GFileInfo *info;
793 	GFile *child;
794 
795 	enumerator = g_file_enumerate_children (file,
796 	                                        G_FILE_ATTRIBUTE_STANDARD_NAME,
797 	                                        G_FILE_QUERY_INFO_NONE,
798 	                                        NULL,
799 	                                        NULL);
800 
801 	if (!enumerator) {
802 		return;
803 	}
804 
805 	while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL) {
806 
807 		if (!suffix || g_str_has_suffix (g_file_info_get_name (info), suffix)) {
808 			child = g_file_enumerator_get_child (enumerator, info);
809 			(func) (child, user_data);
810 			g_object_unref (child);
811 		}
812 
813 		g_object_unref (info);
814 	}
815 
816 	g_object_unref (enumerator);
817 }
818 
819 static void
initialize_miners_data(TrackerMinerManager * manager)820 initialize_miners_data (TrackerMinerManager *manager)
821 {
822 	GFile *file;
823 
824 	/* Go through service files */
825 	file = g_file_new_for_path (TRACKER_MINERS_DIR);
826 	directory_foreach (file, ".service", (GFunc) check_file, manager);
827 	g_object_unref (file);
828 }
829 
830 /**
831  * tracker_miner_manager_get_available:
832  * @manager: a #TrackerMinerManager
833  *
834  * Returns a list of references for all available miners. Available
835  * miners are miners which may or may not be running in a process at
836  * the current time.
837  *
838  * Returns: (transfer full) (element-type utf8) (nullable): a #GSList which
839  * must be freed with g_slist_free() and all contained data with g_free().
840  * Otherwise %NULL is returned if there are no miners.
841  *
842  * Since: 0.8
843  **/
844 GSList *
tracker_miner_manager_get_available(TrackerMinerManager * manager)845 tracker_miner_manager_get_available (TrackerMinerManager *manager)
846 {
847 	TrackerMinerManagerPrivate *priv;
848 	GSList *list = NULL;
849 	GList *m;
850 
851 	priv = tracker_miner_manager_get_instance_private (manager);
852 
853 	for (m = priv->miners; m; m = m->next) {
854 		MinerData *data = m->data;
855 
856 		list = g_slist_prepend (list, g_strdup (data->dbus_name));
857 	}
858 
859 	return g_slist_reverse (list);
860 }
861 
862 /**
863  * tracker_miner_manager_pause:
864  * @manager: a #TrackerMinerManager.
865  * @miner: miner reference
866  * @reason: reason to pause
867  * @cookie: (out) (allow-none): return location for the pause cookie ID
868  *
869  * Asks @miner to pause. a miner could be paused by
870  * several reasons, and its activity won't be resumed
871  * until all pause requests have been resumed.
872  *
873  * Returns: %TRUE if the miner was paused successfully, otherwise
874  * %FALSE.
875  *
876  * Since: 0.8
877  **/
878 gboolean
tracker_miner_manager_pause(TrackerMinerManager * manager,const gchar * miner,const gchar * reason,guint32 * cookie)879 tracker_miner_manager_pause (TrackerMinerManager *manager,
880                              const gchar         *miner,
881                              const gchar         *reason,
882                              guint32             *cookie)
883 {
884 	GDBusProxy *proxy;
885 	const gchar *app_name;
886 	GError *error = NULL;
887 	GVariant *v;
888 
889 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), FALSE);
890 	g_return_val_if_fail (miner != NULL, FALSE);
891 	g_return_val_if_fail (reason != NULL, FALSE);
892 
893 	proxy = find_miner_proxy (manager, miner, TRUE);
894 
895 	if (!proxy) {
896 		g_critical ("No D-Bus proxy found for miner '%s'", miner);
897 		return FALSE;
898 	}
899 
900 	/* Find a reasonable app name */
901 	app_name = g_get_application_name ();
902 
903 	if (!app_name) {
904 		app_name = g_get_prgname ();
905 	}
906 
907 	if (!app_name) {
908 		app_name = "TrackerMinerManager client";
909 	}
910 
911 	v = g_dbus_proxy_call_sync (proxy,
912 	                            "Pause",
913 	                            g_variant_new ("(ss)", app_name, reason),
914 	                            G_DBUS_CALL_FLAGS_NONE,
915 	                            -1,
916 	                            NULL,
917 	                            &error);
918 
919 	if (error) {
920 		g_critical ("Could not pause miner '%s': %s", miner, error->message);
921 		g_error_free (error);
922 		return FALSE;
923 	}
924 
925 	if (cookie) {
926 		g_variant_get (v, "(i)", cookie);
927 	}
928 
929 	g_variant_unref (v);
930 
931 	return TRUE;
932 }
933 
934 /**
935  * tracker_miner_manager_pause_for_process:
936  * @manager: a #TrackerMinerManager.
937  * @miner: miner reference
938  * @reason: reason to pause
939  * @cookie: (out) (allow-none): return location for the pause cookie ID
940  *
941  * This function operates exactly the same way as
942  * tracker_miner_manager_pause() with the exception that if the calling
943  * process dies, the pause is resumed. This API is useful for cases
944  * where the calling process has a risk of crashing without resuming
945  * the pause.
946  *
947  * NOTE: If you call g_object_unref() on the @manager before you
948  * intend to resume the pause and it finalizes, it will automatically
949  * resume.
950  *
951  * Returns: %TRUE if the miner was paused successfully, otherwise
952  * %FALSE.
953  *
954  * Since: 0.10.15
955  **/
956 gboolean
tracker_miner_manager_pause_for_process(TrackerMinerManager * manager,const gchar * miner,const gchar * reason,guint32 * cookie)957 tracker_miner_manager_pause_for_process (TrackerMinerManager *manager,
958                                          const gchar         *miner,
959                                          const gchar         *reason,
960                                          guint32             *cookie)
961 {
962 	GDBusProxy *proxy;
963 	const gchar *app_name;
964 	GError *error = NULL;
965 	GVariant *v;
966 
967 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), FALSE);
968 	g_return_val_if_fail (miner != NULL, FALSE);
969 	g_return_val_if_fail (reason != NULL, FALSE);
970 
971 	proxy = find_miner_proxy (manager, miner, TRUE);
972 
973 	if (!proxy) {
974 		g_critical ("No D-Bus proxy found for miner '%s'", miner);
975 		return FALSE;
976 	}
977 
978 	/* Find a reasonable app name */
979 	app_name = g_get_application_name ();
980 
981 	if (!app_name) {
982 		app_name = g_get_prgname ();
983 	}
984 
985 	if (!app_name) {
986 		app_name = "TrackerMinerManager client";
987 	}
988 
989 	v = g_dbus_proxy_call_sync (proxy,
990 	                            "PauseForProcess",
991 	                            g_variant_new ("(ss)", app_name, reason),
992 	                            G_DBUS_CALL_FLAGS_NONE,
993 	                            -1,
994 	                            NULL,
995 	                            &error);
996 
997 	if (error) {
998 		g_critical ("Could not pause miner '%s': %s", miner, error->message);
999 		g_error_free (error);
1000 		return FALSE;
1001 	}
1002 
1003 	if (cookie) {
1004 		g_variant_get (v, "(i)", cookie);
1005 	}
1006 
1007 	g_variant_unref (v);
1008 
1009 	return TRUE;
1010 }
1011 
1012 /**
1013  * tracker_miner_manager_resume:
1014  * @manager: a #TrackerMinerManager
1015  * @miner: miner reference
1016  * @cookie: pause cookie
1017  *
1018  * Tells @miner to resume activity. The miner won't actually resume
1019  * operations until all pause requests have been resumed.
1020  *
1021  * Returns: %TRUE if the miner was successfully resumed, otherwise
1022  * %FALSE.
1023  *
1024  * Since: 0.8
1025  **/
1026 gboolean
tracker_miner_manager_resume(TrackerMinerManager * manager,const gchar * miner,guint32 cookie)1027 tracker_miner_manager_resume (TrackerMinerManager *manager,
1028                               const gchar         *miner,
1029                               guint32              cookie)
1030 {
1031 	GDBusProxy *proxy;
1032 	GError *error = NULL;
1033 	GVariant *v;
1034 
1035 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), FALSE);
1036 	g_return_val_if_fail (miner != NULL, FALSE);
1037 	proxy = find_miner_proxy (manager, miner, TRUE);
1038 
1039 	if (!proxy) {
1040 		g_critical ("No D-Bus proxy found for miner '%s'", miner);
1041 		return FALSE;
1042 	}
1043 
1044 	v = g_dbus_proxy_call_sync (proxy,
1045 	                            "Resume",
1046 	                            g_variant_new ("(i)", (gint) cookie),
1047 	                            G_DBUS_CALL_FLAGS_NONE,
1048 	                            -1,
1049 	                            NULL,
1050 	                            &error);
1051 
1052 	if (error) {
1053 		g_critical ("Could not resume miner '%s': %s", miner, error->message);
1054 		g_error_free (error);
1055 		return FALSE;
1056 	}
1057 
1058 	g_variant_unref (v);
1059 
1060 	return TRUE;
1061 }
1062 
1063 /**
1064  * tracker_miner_manager_is_active:
1065  * @manager: a #TrackerMinerManager
1066  * @miner: miner reference
1067  *
1068  * Returns the miner's current activity.
1069  *
1070  * Returns: %TRUE if the @miner is active, otherwise %FALSE.
1071  *
1072  * Since: 0.8
1073  **/
1074 gboolean
tracker_miner_manager_is_active(TrackerMinerManager * manager,const gchar * miner)1075 tracker_miner_manager_is_active (TrackerMinerManager *manager,
1076                                  const gchar         *miner)
1077 {
1078 	TrackerMinerManagerPrivate *priv;
1079 	GError *error = NULL;
1080 	gboolean active = FALSE;
1081 	GVariant *v;
1082 
1083 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), FALSE);
1084 	g_return_val_if_fail (miner != NULL, FALSE);
1085 
1086 	priv = tracker_miner_manager_get_instance_private (manager);
1087 
1088 	v = g_dbus_connection_call_sync (priv->connection,
1089 	                                 "org.freedesktop.DBus",
1090 	                                 "/org/freedesktop/DBus",
1091 	                                 "org.freedesktop.DBus",
1092 	                                 "NameHasOwner",
1093 	                                 g_variant_new ("(s)", miner),
1094 	                                 (GVariantType *) "(b)",
1095 	                                 G_DBUS_CALL_FLAGS_NONE,
1096 	                                 -1,
1097 	                                 NULL,
1098 	                                 &error);
1099 
1100 	if (error) {
1101 		g_critical ("Could not check whether miner '%s' is currently active: %s",
1102 		            miner, error ? error->message : "no error given");
1103 		g_error_free (error);
1104 		return FALSE;
1105 	}
1106 
1107 	g_variant_get (v, "(b)", &active);
1108 	g_variant_unref (v);
1109 
1110 	return active;
1111 }
1112 
1113 /**
1114  * tracker_miner_manager_get_status:
1115  * @manager: a #TrackerMinerManager
1116  * @miner: miner reference
1117  * @status: (out) (allow-none): return location for status
1118  * @progress: (out) (allow-none): return location for progress
1119  * @remaining_time: (out) (allow-none): return location for remaining time
1120  *
1121  * Returns the current status, progress and remaining time for @miner.
1122  * @remaining_time will be 0 if not possible to compute it yet,
1123  * and less than zero if it is not applicable.
1124  *
1125  * Returns: %TRUE if the status could be retrieved successfully,
1126  * otherwise %FALSE
1127  *
1128  * Since: 0.12
1129  **/
1130 gboolean
tracker_miner_manager_get_status(TrackerMinerManager * manager,const gchar * miner,gchar ** status,gdouble * progress,gint * remaining_time)1131 tracker_miner_manager_get_status (TrackerMinerManager  *manager,
1132                                   const gchar          *miner,
1133                                   gchar               **status,
1134                                   gdouble              *progress,
1135                                   gint                 *remaining_time)
1136 {
1137 	GDBusProxy *proxy;
1138 
1139 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), FALSE);
1140 	g_return_val_if_fail (miner != NULL, FALSE);
1141 	/* At least one of them should be asked */
1142 	g_return_val_if_fail (status != NULL ||
1143 	                      progress != NULL ||
1144 	                      remaining_time != NULL, FALSE);
1145 
1146 	proxy = find_miner_proxy (manager, miner, TRUE);
1147 
1148 	if (!proxy) {
1149 		g_critical ("No D-Bus proxy found for miner '%s'", miner);
1150 		return FALSE;
1151 	}
1152 
1153 	if (progress) {
1154 		GError *error = NULL;
1155 		GVariant *v;
1156 
1157 		v = g_dbus_proxy_call_sync (proxy,
1158 		                            "GetProgress",
1159 		                            NULL,
1160 		                            G_DBUS_CALL_FLAGS_NONE,
1161 		                            -1,
1162 		                            NULL,
1163 		                            &error);
1164 		if (error) {
1165 			/* We handle this error as a special case, some
1166 			 * plugins don't have .service files.
1167 			 */
1168 			if (error->code != G_DBUS_ERROR_SERVICE_UNKNOWN) {
1169 				g_critical ("Could not get miner progress for '%s': %s", miner,
1170 				            error->message);
1171 			}
1172 
1173 			g_error_free (error);
1174 
1175 			return FALSE;
1176 		}
1177 
1178 		g_variant_get (v, "(d)", progress);
1179 		g_variant_unref (v);
1180 	}
1181 
1182 	if (status) {
1183 		GError *error = NULL;
1184 		GVariant *v;
1185 
1186 		v = g_dbus_proxy_call_sync (proxy,
1187 		                            "GetStatus",
1188 		                            NULL,
1189 		                            G_DBUS_CALL_FLAGS_NONE,
1190 		                            -1,
1191 		                            NULL,
1192 		                            &error);
1193 		if (error) {
1194 			g_critical ("Could not get miner status for '%s': %s", miner,
1195 			            error->message);
1196 			g_error_free (error);
1197 			return FALSE;
1198 		}
1199 
1200 		g_variant_get (v, "(s)", status);
1201 		g_variant_unref (v);
1202 	}
1203 
1204 	if (remaining_time) {
1205 		GError *error = NULL;
1206 		GVariant *v;
1207 
1208 		v = g_dbus_proxy_call_sync (proxy,
1209 		                            "GetRemainingTime",
1210 		                            NULL,
1211 		                            G_DBUS_CALL_FLAGS_NONE,
1212 		                            -1,
1213 		                            NULL,
1214 		                            &error);
1215 		if (error) {
1216 			g_critical ("Could not get miner remaining processing "
1217 			            "time for '%s': %s", miner,
1218 			            error->message);
1219 			g_error_free (error);
1220 			return FALSE;
1221 		}
1222 
1223 		g_variant_get (v, "(i)", remaining_time);
1224 		g_variant_unref (v);
1225 	}
1226 
1227 	return TRUE;
1228 }
1229 
1230 /**
1231  * tracker_miner_manager_is_paused:
1232  * @manager: a #TrackerMinerManager
1233  * @miner: miner reference
1234  * @applications: (out callee-allocates) (allow-none) (transfer full):
1235  * return location for application names.
1236  * @reasons: (out callee-allocates) (allow-none) (transfer full):
1237  * return location for pause reasons.
1238  *
1239  * This function either returns %FALSE if the miner is not paused,
1240  * or returns %TRUE and fills in @applications and @reasons with
1241  * the pause reasons and the applications that asked for it. Both
1242  * arrays will have the same lengh, and will be sorted so the
1243  * application/pause reason pairs have the same index.
1244  *
1245  * Returns: %TRUE if @miner is paused, otherwise %FALSE.
1246  *
1247  * Since: 0.8
1248  **/
1249 gboolean
tracker_miner_manager_is_paused(TrackerMinerManager * manager,const gchar * miner,GStrv * applications,GStrv * reasons)1250 tracker_miner_manager_is_paused (TrackerMinerManager *manager,
1251                                  const gchar         *miner,
1252                                  GStrv               *applications,
1253                                  GStrv               *reasons)
1254 {
1255 	GDBusProxy *proxy;
1256 	GStrv apps, r;
1257 	GError *error = NULL;
1258 	gboolean paused;
1259 	GVariant *v;
1260 
1261 	if (applications) {
1262 		*applications = NULL;
1263 	}
1264 
1265 	if (reasons) {
1266 		*reasons = NULL;
1267 	}
1268 
1269 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), TRUE);
1270 	g_return_val_if_fail (miner != NULL, TRUE);
1271 
1272 	proxy = find_miner_proxy (manager, miner, TRUE);
1273 
1274 	if (!proxy) {
1275 		g_critical ("No D-Bus proxy found for miner '%s'", miner);
1276 		return FALSE;
1277 	}
1278 
1279 	v = g_dbus_proxy_call_sync (proxy,
1280 	                            "GetPauseDetails",
1281 	                            NULL,
1282 	                            G_DBUS_CALL_FLAGS_NONE,
1283 	                            -1,
1284 	                            NULL,
1285 	                            &error);
1286 
1287 	if (error) {
1288 		g_critical ("Could not get pause details for miner '%s': %s", miner,
1289 		            error->message);
1290 		g_error_free (error);
1291 		return FALSE;
1292 	}
1293 
1294 	g_variant_get (v, "(^as^as)", &apps, &r);
1295 	g_variant_unref (v);
1296 
1297 	paused = (g_strv_length (apps) > 0);
1298 
1299 	if (applications) {
1300 		*applications = apps;
1301 	} else {
1302 		g_strfreev (apps);
1303 	}
1304 
1305 	if (reasons) {
1306 		*reasons = r;
1307 	} else  {
1308 		g_strfreev (r);
1309 	}
1310 
1311 	return paused;
1312 }
1313 
1314 /**
1315  * tracker_miner_manager_get_display_name:
1316  * @manager: a #TrackerMinerManager
1317  * @miner: miner reference
1318  *
1319  * Returns a translated display name for @miner.
1320  *
1321  * Returns: (transfer none): A string which should not be freed or %NULL.
1322  *
1323  * Since: 0.8
1324  **/
1325 const gchar *
tracker_miner_manager_get_display_name(TrackerMinerManager * manager,const gchar * miner)1326 tracker_miner_manager_get_display_name (TrackerMinerManager *manager,
1327                                         const gchar         *miner)
1328 {
1329 	TrackerMinerManagerPrivate *priv;
1330 	GList *m;
1331 
1332 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), NULL);
1333 	g_return_val_if_fail (miner != NULL, NULL);
1334 
1335 	priv = tracker_miner_manager_get_instance_private (manager);
1336 
1337 	for (m = priv->miners; m; m = m->next) {
1338 		MinerData *data = m->data;
1339 
1340 		if (strcmp (miner, data->dbus_name) == 0) {
1341 			return data->display_name;
1342 		}
1343 	}
1344 
1345 	return NULL;
1346 }
1347 
1348 /**
1349  * tracker_miner_manager_get_description:
1350  * @manager: a #TrackerMinerManager
1351  * @miner: miner reference
1352  *
1353  * Returns the description for the given @miner.
1354  *
1355  * Returns: (transfer none): A string which should not be freed or %NULL if none is specified.
1356  *
1357  * Since: 0.8
1358  **/
1359 const gchar *
tracker_miner_manager_get_description(TrackerMinerManager * manager,const gchar * miner)1360 tracker_miner_manager_get_description (TrackerMinerManager *manager,
1361                                        const gchar         *miner)
1362 {
1363 	TrackerMinerManagerPrivate *priv;
1364 	GList *m;
1365 
1366 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), NULL);
1367 	g_return_val_if_fail (miner != NULL, NULL);
1368 
1369 	priv = tracker_miner_manager_get_instance_private (manager);
1370 
1371 	for (m = priv->miners; m; m = m->next) {
1372 		MinerData *data = m->data;
1373 
1374 		if (strcmp (miner, data->dbus_name) == 0) {
1375 			return data->description;
1376 		}
1377 	}
1378 
1379 	return NULL;
1380 }
1381 
1382 /**
1383  * tracker_miner_manager_error_quark:
1384  *
1385  * Returns: the #GQuark used to identify miner manager errors in
1386  * GError structures.
1387  *
1388  * Since: 0.8
1389  **/
1390 GQuark
tracker_miner_manager_error_quark(void)1391 tracker_miner_manager_error_quark (void)
1392 {
1393 	static GQuark error_quark = 0;
1394 
1395 	if (G_UNLIKELY (error_quark == 0)) {
1396 		error_quark = g_quark_from_static_string ("tracker-miner-manager-error-quark");
1397 	}
1398 
1399 	return error_quark;
1400 }
1401 
1402 /**
1403  * tracker_miner_manager_reindex_by_mimetype:
1404  * @manager: a #TrackerMinerManager
1405  * @mimetypes: (in): an array of mimetypes (E.G. "text/plain"). All items
1406  * with a mimetype in that list will be reindexed.
1407  * @error: (out callee-allocates) (transfer full) (allow-none): return location for errors
1408  *
1409  * Tells the filesystem miner to reindex any file with a mimetype in
1410  * the @mimetypes list.
1411  *
1412  * On failure @error will be set.
1413  *
1414  * Returns: %TRUE on success, otherwise %FALSE.
1415  *
1416  * Since: 0.10
1417  **/
1418 gboolean
tracker_miner_manager_reindex_by_mimetype(TrackerMinerManager * manager,const GStrv mimetypes,GError ** error)1419 tracker_miner_manager_reindex_by_mimetype (TrackerMinerManager  *manager,
1420                                            const GStrv           mimetypes,
1421                                            GError              **error)
1422 {
1423 	TrackerMinerManagerPrivate *priv;
1424 	GVariant *v;
1425 	GError *new_error = NULL;
1426 
1427 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), FALSE);
1428 	g_return_val_if_fail (mimetypes != NULL, FALSE);
1429 
1430 	if (!tracker_miner_manager_is_active (manager,
1431 	                                      "org.freedesktop.Tracker1.Miner.Files")) {
1432 		g_set_error_literal (error,
1433 		                     TRACKER_MINER_MANAGER_ERROR,
1434 		                     TRACKER_MINER_MANAGER_ERROR_NOT_AVAILABLE,
1435 		                     "Filesystem miner is not active");
1436 		return FALSE;
1437 	}
1438 
1439 	priv = tracker_miner_manager_get_instance_private (manager);
1440 
1441 	v = g_dbus_connection_call_sync (priv->connection,
1442 	                                 "org.freedesktop.Tracker1.Miner.Files",
1443 	                                 "/org/freedesktop/Tracker1/Miner/Files/Index",
1444 	                                 "org.freedesktop.Tracker1.Miner.Files.Index",
1445 	                                 "ReindexMimeTypes",
1446 	                                 g_variant_new ("(^as)", mimetypes),
1447 	                                 NULL,
1448 	                                 G_DBUS_CALL_FLAGS_NONE,
1449 	                                 -1,
1450 	                                 NULL,
1451 	                                 &new_error);
1452 
1453 	if (new_error) {
1454 		g_propagate_error (error, new_error);
1455 		return FALSE;
1456 	}
1457 
1458 	g_variant_unref (v);
1459 
1460 	return FALSE;
1461 }
1462 
1463 static gboolean
miner_manager_index_file_sync(TrackerMinerManager * manager,const gchar * method_name,GFile * file,GCancellable * cancellable,GError ** error)1464 miner_manager_index_file_sync (TrackerMinerManager *manager,
1465                                const gchar         *method_name,
1466                                GFile               *file,
1467                                GCancellable        *cancellable,
1468                                GError             **error)
1469 {
1470 	TrackerMinerManagerPrivate *priv;
1471 	gchar *uri;
1472 	GVariant *v;
1473 	GError *new_error = NULL;
1474 
1475 	if (!g_file_query_exists (file, cancellable)) {
1476 		g_set_error_literal (error,
1477 		                     TRACKER_MINER_MANAGER_ERROR,
1478 		                     TRACKER_MINER_MANAGER_ERROR_NOENT,
1479 		                     "File or directory does not exist");
1480 		return FALSE;
1481 	}
1482 
1483 	if (!tracker_miner_manager_is_active (manager,
1484 	                                      "org.freedesktop.Tracker1.Miner.Files")) {
1485 		g_set_error_literal (error,
1486 		                     TRACKER_MINER_MANAGER_ERROR,
1487 		                     TRACKER_MINER_MANAGER_ERROR_NOT_AVAILABLE,
1488 		                     "Filesystem miner is not active");
1489 		return FALSE;
1490 	}
1491 
1492 	priv = tracker_miner_manager_get_instance_private (manager);
1493 
1494 	uri = g_file_get_uri (file);
1495 
1496 	v = g_dbus_connection_call_sync (priv->connection,
1497 	                                 "org.freedesktop.Tracker1.Miner.Files",
1498 	                                 "/org/freedesktop/Tracker1/Miner/Files/Index",
1499 	                                 "org.freedesktop.Tracker1.Miner.Files.Index",
1500 	                                 method_name,
1501 	                                 g_variant_new ("(s)", uri),
1502 	                                 NULL,
1503 	                                 G_DBUS_CALL_FLAGS_NONE,
1504 	                                 -1,
1505 	                                 cancellable,
1506 	                                 &new_error);
1507 
1508 	g_free (uri);
1509 
1510 	if (new_error) {
1511 		g_propagate_error (error, new_error);
1512 		return FALSE;
1513 	}
1514 
1515 	g_variant_unref (v);
1516 
1517 	return TRUE;
1518 }
1519 
1520 static void
miner_manager_index_file_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1521 miner_manager_index_file_thread (GTask *task,
1522                                  gpointer source_object,
1523                                  gpointer task_data,
1524                                  GCancellable *cancellable)
1525 {
1526 	TrackerMinerManager *manager = source_object;
1527 	GFile *file = task_data;
1528 	GError *error = NULL;
1529 
1530 	miner_manager_index_file_sync (manager, METHOD_INDEX_FILE,
1531 	                               file, cancellable, &error);
1532 	if (error != NULL) {
1533 		g_task_return_error (task, error);
1534 	} else {
1535 		g_task_return_boolean (task, TRUE);
1536 	}
1537 }
1538 
1539 /**
1540  * tracker_miner_manager_index_file:
1541  * @manager: a #TrackerMinerManager
1542  * @file: a URL valid in GIO of a file to give to the miner for processing
1543  * @cancellable: (allow-none): a #GCancellable, or %NULL
1544  * @error: (out callee-allocates) (transfer full) (allow-none): return location for errors
1545  *
1546  * Tells the filesystem miner to start indexing the @file.
1547  *
1548  * On failure @error will be set.
1549  *
1550  * Returns: %TRUE on success, otherwise %FALSE.
1551  *
1552  * Since: 2.0
1553  **/
1554 gboolean
tracker_miner_manager_index_file(TrackerMinerManager * manager,GFile * file,GCancellable * cancellable,GError ** error)1555 tracker_miner_manager_index_file (TrackerMinerManager  *manager,
1556                                   GFile                *file,
1557                                   GCancellable         *cancellable,
1558                                   GError              **error)
1559 {
1560 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), FALSE);
1561 	g_return_val_if_fail (G_IS_FILE (file), FALSE);
1562 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1563 
1564 	return miner_manager_index_file_sync (manager, METHOD_INDEX_FILE,
1565 	                                      file, cancellable, error);
1566 }
1567 
1568 /**
1569  * tracker_miner_manager_index_file_async:
1570  * @manager: a #TrackerMinerManager
1571  * @file: a URL valid in GIO of a file to give to the miner for processing
1572  * @cancellable: (allow-none): a #GCancellable, or %NULL
1573  * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
1574  * @user_data: the data to pass to the callback function
1575  *
1576  * Tells the filesystem miner to start indexing the @file. Once the message has been sent,
1577  * @callback will be called. You can then call tracker_miner_manager_index_file_finish()
1578  * to get the result.
1579  *
1580  * Since: 0.16
1581  **/
1582 void
tracker_miner_manager_index_file_async(TrackerMinerManager * manager,GFile * file,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1583 tracker_miner_manager_index_file_async (TrackerMinerManager *manager,
1584                                         GFile               *file,
1585                                         GCancellable        *cancellable,
1586                                         GAsyncReadyCallback  callback,
1587                                         gpointer             user_data)
1588 {
1589 	GTask *task = g_task_new (manager, cancellable, callback, user_data);
1590 	g_task_set_task_data (task, g_object_ref (file), (GDestroyNotify) g_object_unref);
1591 	g_task_run_in_thread (task, miner_manager_index_file_thread);
1592 	g_object_unref (task);
1593 }
1594 
1595 /**
1596  * tracker_miner_manager_index_file_finish:
1597  * @manager: a #TrackerMinerManager
1598  * @result: a #GAsyncResult
1599  * @error: (out callee-allocates) (transfer full) (allow-none): return location for errors
1600  *
1601  * Finishes a request to index a file. See tracker_miner_manager_index_file_async()
1602  *
1603  * On failure @error will be set.
1604  *
1605  * Returns: %TRUE on success, otherwise %FALSE.
1606  *
1607  * Since: 0.16
1608  **/
1609 gboolean
tracker_miner_manager_index_file_finish(TrackerMinerManager * manager,GAsyncResult * result,GError ** error)1610 tracker_miner_manager_index_file_finish (TrackerMinerManager *manager,
1611                                          GAsyncResult        *result,
1612                                          GError             **error)
1613 {
1614 	return g_task_propagate_boolean (G_TASK (result), error);
1615 }
1616 
1617 static void
miner_manager_index_file_for_process_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1618 miner_manager_index_file_for_process_thread (GTask        *task,
1619                                              gpointer      source_object,
1620                                              gpointer      task_data,
1621                                              GCancellable *cancellable)
1622 {
1623 	TrackerMinerManager *manager = source_object;
1624 	GFile *file = task_data;
1625 	GError *error = NULL;
1626 
1627 	miner_manager_index_file_sync (manager, METHOD_INDEX_FILE_FOR_PROCESS,
1628 	                               file, cancellable, &error);
1629 	if (error != NULL) {
1630 		g_task_return_error (task, error);
1631 	} else {
1632 		g_task_return_boolean (task, TRUE);
1633 	}
1634 }
1635 
1636 /**
1637  * tracker_miner_manager_index_file_for_process:
1638  * @manager: a #TrackerMinerManager
1639  * @file: a URL valid in GIO of a file to give to the miner for processing
1640  * @cancellable: (allow-none): a #GCancellable, or %NULL
1641  * @error: (out callee-allocates) (transfer full) (allow-none): return location for errors
1642  *
1643  * This function operates exactly the same way as
1644  * tracker_miner_manager_index_file() with the exception that if the
1645  * calling process dies, the indexing is cancelled. This API is useful
1646  * for cases where the calling process wants to tie the indexing
1647  * operation closely to its own lifetime.
1648  *
1649  * On failure @error will be set.
1650  *
1651  * Returns: %TRUE on success, otherwise %FALSE.
1652  *
1653  * Since: 1.10
1654  **/
1655 gboolean
tracker_miner_manager_index_file_for_process(TrackerMinerManager * manager,GFile * file,GCancellable * cancellable,GError ** error)1656 tracker_miner_manager_index_file_for_process (TrackerMinerManager  *manager,
1657                                               GFile                *file,
1658                                               GCancellable         *cancellable,
1659                                               GError              **error)
1660 {
1661 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), FALSE);
1662 	g_return_val_if_fail (G_IS_FILE (file), FALSE);
1663 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1664 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1665 
1666 	return miner_manager_index_file_sync (manager, METHOD_INDEX_FILE_FOR_PROCESS,
1667 	                                      file, cancellable, error);
1668 }
1669 
1670 /**
1671  * tracker_miner_manager_index_file_for_process_async:
1672  * @manager: a #TrackerMinerManager
1673  * @file: a URL valid in GIO of a file to give to the miner for processing
1674  * @cancellable: (allow-none): a #GCancellable, or %NULL
1675  * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
1676  * @user_data: the data to pass to the callback function
1677  *
1678  * This function operates exactly the same way as
1679  * tracker_miner_manager_index_file() with the exception that if the
1680  * calling process dies, the indexing is cancelled. This API is useful
1681  * for cases where the calling process wants to tie the indexing
1682  * operation closely to its own lifetime.
1683  *
1684  * When the operation is finished, @callback will be called. You can
1685  * then call tracker_miner_manager_index_file_for_process_finish() to
1686  * get the result of the operation.
1687  *
1688  * Since: 1.10
1689  **/
1690 void
tracker_miner_manager_index_file_for_process_async(TrackerMinerManager * manager,GFile * file,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1691 tracker_miner_manager_index_file_for_process_async (TrackerMinerManager *manager,
1692                                                     GFile               *file,
1693                                                     GCancellable        *cancellable,
1694                                                     GAsyncReadyCallback  callback,
1695                                                     gpointer             user_data)
1696 {
1697 	GTask *task;
1698 
1699 	g_return_if_fail (TRACKER_IS_MINER_MANAGER (manager));
1700 	g_return_if_fail (G_IS_FILE (file));
1701 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1702 
1703 	task = g_task_new (manager, cancellable, callback, user_data);
1704 	g_task_set_task_data (task, g_object_ref (file), (GDestroyNotify) g_object_unref);
1705 	g_task_run_in_thread (task, miner_manager_index_file_for_process_thread);
1706 	g_object_unref (task);
1707 }
1708 
1709 /**
1710  * tracker_miner_manager_index_file_for_process_finish:
1711  * @manager: a #TrackerMinerManager
1712  * @result: a #GAsyncResult
1713  * @error: (out callee-allocates) (transfer full) (allow-none): return location for errors
1714  *
1715  * Finishes a request to index a file. See tracker_miner_manager_index_file_for_process_async()
1716  *
1717  * On failure @error will be set.
1718  *
1719  * Returns: %TRUE on success, otherwise %FALSE.
1720  *
1721  * Since: 1.10
1722  **/
1723 gboolean
tracker_miner_manager_index_file_for_process_finish(TrackerMinerManager * manager,GAsyncResult * result,GError ** error)1724 tracker_miner_manager_index_file_for_process_finish (TrackerMinerManager  *manager,
1725                                                      GAsyncResult         *result,
1726                                                      GError              **error)
1727 {
1728 	g_return_val_if_fail (TRACKER_IS_MINER_MANAGER (manager), FALSE);
1729 	g_return_val_if_fail (g_task_is_valid (result, manager), FALSE);;
1730 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1731 
1732 	return g_task_propagate_boolean (G_TASK (result), error);
1733 }
1734