No issues found
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 Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA
18 */
19
20 #include "config.h"
21
22 #include <string.h>
23
24 #include <libtracker-common/tracker-dbus.h>
25
26 #include "tracker-thumbnailer.h"
27
28 /**
29 * SECTION:tracker-thumbnailer
30 * @title: Thumbnailer management
31 * @short_description: Request the thumbnailer service creates or
32 * updates thumbnails.
33 * @include: libtracker-miner/tracker-miner.h
34 *
35 * This is a convenience API using D-Bus for creating and updating
36 * thumbnails for files being mined. It is also used to create
37 * thumbnails for album art found embedded in some medias.
38 *
39 * This follows the thumbnailer specification:
40 * http://live.gnome.org/ThumbnailerSpec
41 **/
42
43 #define THUMBCACHE_SERVICE "org.freedesktop.thumbnails.Cache1"
44 #define THUMBCACHE_PATH "/org/freedesktop/thumbnails/Cache1"
45 #define THUMBCACHE_INTERFACE "org.freedesktop.thumbnails.Cache1"
46
47 #define THUMBMAN_SERVICE "org.freedesktop.thumbnails.Thumbnailer1"
48 #define THUMBMAN_PATH "/org/freedesktop/thumbnails/Thumbnailer1"
49 #define THUMBMAN_INTERFACE "org.freedesktop.thumbnails.Thumbnailer1"
50
51 typedef struct {
52 GDBusProxy *cache_proxy;
53 GDBusProxy *manager_proxy;
54 GDBusConnection *connection;
55
56 GStrv supported_mime_types;
57
58 GSList *removes;
59 GSList *moves_to;
60 GSList *moves_from;
61
62 guint request_id;
63 gboolean service_is_available;
64 } TrackerThumbnailerPrivate;
65
66 #if GLIB_CHECK_VERSION (2,31,0)
67 static void private_free (gpointer data);
68 static GPrivate private_key = G_PRIVATE_INIT (private_free);
69 #else
70 static GStaticPrivate private_key = G_STATIC_PRIVATE_INIT;
71 #endif
72
73 static void
74 private_free (gpointer data)
75 {
76 TrackerThumbnailerPrivate *private;
77
78 private = data;
79
80 if (private->cache_proxy) {
81 g_object_unref (private->cache_proxy);
82 }
83
84 if (private->manager_proxy) {
85 g_object_unref (private->manager_proxy);
86 }
87
88 if (private->connection) {
89 g_object_unref (private->connection);
90 }
91
92 g_strfreev (private->supported_mime_types);
93
94 g_slist_foreach (private->removes, (GFunc) g_free, NULL);
95 g_slist_free (private->removes);
96
97 g_slist_foreach (private->moves_to, (GFunc) g_free, NULL);
98 g_slist_free (private->moves_to);
99
100 g_slist_foreach (private->moves_from, (GFunc) g_free, NULL);
101 g_slist_free (private->moves_from);
102
103 g_free (private);
104 }
105
106 inline static gboolean
107 should_be_thumbnailed (GStrv list,
108 const gchar *mime)
109 {
110 gboolean should_thumbnail;
111 guint i;
112
113 if (!list) {
114 return TRUE;
115 }
116
117 for (should_thumbnail = FALSE, i = 0;
118 should_thumbnail == FALSE && list[i] != NULL;
119 i++) {
120 if (g_ascii_strcasecmp (list[i], mime) == 0) {
121 should_thumbnail = TRUE;
122 }
123 }
124
125 return should_thumbnail;
126 }
127
128 /**
129 * tracker_thumbnailer_init:
130 *
131 * Initializes the thumbnailer connection.
132 *
133 * Returns: #TRUE if connection was successfully initialized, #FALSE otherwise.
134 *
135 * Since: 0.8
136 */
137 gboolean
138 tracker_thumbnailer_init (void)
139 {
140 TrackerThumbnailerPrivate *private;
141 GError *error = NULL;
142 GVariant *v;
143
144 private = g_new0 (TrackerThumbnailerPrivate, 1);
145
146 /* Don't start at 0, start at 1. */
147 private->request_id = 1;
148
149 #if GLIB_CHECK_VERSION (2,31,0)
150 g_private_replace (&private_key, private);
151 #else
152 g_static_private_set (&private_key, private, private_free);
153 #endif
154
155 g_message ("Thumbnailer connections being set up...");
156
157 private->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
158
159 if (!private->connection) {
160 g_critical ("Could not connect to the D-Bus session bus, %s",
161 error ? error->message : "no error given.");
162 g_clear_error (&error);
163
164 private->service_is_available = FALSE;
165
166 return FALSE;
167 }
168
169 private->cache_proxy = g_dbus_proxy_new_sync (private->connection,
170 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
171 NULL,
172 THUMBCACHE_SERVICE,
173 THUMBCACHE_PATH,
174 THUMBCACHE_INTERFACE,
175 NULL,
176 &error);
177
178 if (error) {
179 goto error_handler;
180 }
181
182 private->manager_proxy = g_dbus_proxy_new_sync (private->connection,
183 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
184 NULL,
185 THUMBMAN_SERVICE,
186 THUMBMAN_PATH,
187 THUMBMAN_INTERFACE,
188 NULL,
189 &error);
190
191 if (error) {
192 goto error_handler;
193 }
194
195 v = g_dbus_proxy_call_sync (private->manager_proxy,
196 "GetSupported",
197 NULL,
198 G_DBUS_CALL_FLAGS_NONE,
199 -1,
200 NULL,
201 &error);
202
203 error_handler:
204
205 if (error) {
206 g_message ("Thumbnailer service did not return supported mime types, %s",
207 error->message);
208
209 g_error_free (error);
210
211 if (private->cache_proxy) {
212 g_object_unref (private->cache_proxy);
213 private->cache_proxy = NULL;
214 }
215
216 if (private->manager_proxy) {
217 g_object_unref (private->manager_proxy);
218 private->manager_proxy = NULL;
219 }
220
221 return FALSE;
222 } else if (v) {
223 GStrv mime_types = NULL;
224 GStrv uri_schemes = NULL;
225
226 g_variant_get (v, "(^a&s^a&s)", &uri_schemes, &mime_types);
227
228 if (mime_types) {
229 GHashTable *hash;
230 GHashTableIter iter;
231 gpointer key, value;
232 guint i;
233
234 /* The table that you receive may contain duplicate mime-types, because
235 * they are grouped against the uri_schemes table */
236
237 hash = g_hash_table_new (g_str_hash, g_str_equal);
238
239 for (i = 0; mime_types[i] != NULL; i++) {
240 g_hash_table_insert (hash, mime_types[i], NULL);
241 }
242
243 i = g_hash_table_size (hash);
244 g_message ("Thumbnailer supports %d mime types", i);
245
246 g_hash_table_iter_init (&iter, hash);
247 private->supported_mime_types = (GStrv) g_new0 (gchar *, i + 1);
248
249 i = 0;
250 while (g_hash_table_iter_next (&iter, &key, &value)) {
251 private->supported_mime_types[i] = g_strdup (key);
252 i++;
253 }
254
255 g_hash_table_unref (hash);
256
257 private->service_is_available = TRUE;
258 }
259
260 g_free (mime_types);
261 g_free (uri_schemes);
262
263 g_variant_unref (v);
264 }
265
266 return TRUE;
267 }
268
269 /**
270 * tracker_thumbnailer_shutdown:
271 *
272 * Shuts down the thumbnailer connection.
273 *
274 * Since: 0.8
275 */
276 void
277 tracker_thumbnailer_shutdown (void)
278 {
279 #if GLIB_CHECK_VERSION (2,31,0)
280 g_private_replace (&private_key, NULL);
281 #else
282 g_static_private_set (&private_key, NULL, NULL);
283 #endif
284 }
285
286 /**
287 * tracker_thumbnailer_move_add:
288 * @from_uri: URI of the file before the move
289 * @mime_type: mime-type of the file
290 * @to_uri: URI of the file after the move
291 *
292 * Adds a new request to tell the thumbnailer that @from_uri was moved to
293 * @to_uri. Stored requests can be sent with tracker_thumbnailer_send().
294 *
295 * Returns: #TRUE if successfully stored to be reported, #FALSE otherwise.
296 *
297 * Since: 0.8
298 */
299 gboolean
300 tracker_thumbnailer_move_add (const gchar *from_uri,
301 const gchar *mime_type,
302 const gchar *to_uri)
303 {
304
305 TrackerThumbnailerPrivate *private;
306
307 /* mime_type can be NULL */
308
309 g_return_val_if_fail (from_uri != NULL, FALSE);
310 g_return_val_if_fail (to_uri != NULL, FALSE);
311
312 #if GLIB_CHECK_VERSION (2,31,0)
313 private = g_private_get (&private_key);
314 #else
315 private = g_static_private_get (&private_key);
316 #endif
317 g_return_val_if_fail (private != NULL, FALSE);
318
319 if (!private->service_is_available) {
320 return FALSE;
321 }
322
323 if (mime_type && !should_be_thumbnailed (private->supported_mime_types, mime_type)) {
324 return FALSE;
325 }
326
327 private->moves_from = g_slist_prepend (private->moves_from, g_strdup (from_uri));
328 private->moves_to = g_slist_prepend (private->moves_to, g_strdup (to_uri));
329
330 g_debug ("Thumbnailer request to move uri from:'%s' to:'%s' queued",
331 from_uri,
332 to_uri);
333
334 return TRUE;
335 }
336
337 /**
338 * tracker_thumbnailer_remove_add:
339 * @uri: URI of the file
340 * @mime_type: mime-type of the file
341 *
342 * Adds a new request to tell the thumbnailer that @uri was removed.
343 * Stored requests can be sent with tracker_thumbnailer_send().
344 *
345 * Returns: #TRUE if successfully stored to be reported, #FALSE otherwise.
346 *
347 * Since: 0.8
348 */
349 gboolean
350 tracker_thumbnailer_remove_add (const gchar *uri,
351 const gchar *mime_type)
352 {
353 TrackerThumbnailerPrivate *private;
354
355 /* mime_type can be NULL */
356
357 g_return_val_if_fail (uri != NULL, FALSE);
358
359 #if GLIB_CHECK_VERSION (2,31,0)
360 private = g_private_get (&private_key);
361 #else
362 private = g_static_private_get (&private_key);
363 #endif
364 g_return_val_if_fail (private != NULL, FALSE);
365
366 if (!private->service_is_available) {
367 return FALSE;
368 }
369
370 if (mime_type && !should_be_thumbnailed (private->supported_mime_types, mime_type)) {
371 return FALSE;
372 }
373
374 private->removes = g_slist_prepend (private->removes, g_strdup (uri));
375
376 g_debug ("Thumbnailer request to remove uri:'%s', appended to queue", uri);
377
378 return TRUE;
379 }
380
381 /**
382 * tracker_thumbnailer_cleanup:
383 * @uri_prefix: URI prefix
384 *
385 * Tells thumbnailer to cleanup all thumbnails under @uri_prefix.
386 *
387 * Returns: #TRUE if successfully reported, #FALSE otherwise.
388 *
389 * Since: 0.8
390 */
391 gboolean
392 tracker_thumbnailer_cleanup (const gchar *uri_prefix)
393 {
394 TrackerThumbnailerPrivate *private;
395
396 g_return_val_if_fail (uri_prefix != NULL, FALSE);
397
398 #if GLIB_CHECK_VERSION (2,31,0)
399 private = g_private_get (&private_key);
400 #else
401 private = g_static_private_get (&private_key);
402 #endif
403 g_return_val_if_fail (private != NULL, FALSE);
404
405 if (!private->service_is_available) {
406 return FALSE;
407 }
408
409 private->request_id++;
410
411 g_debug ("Thumbnailer cleaning up uri:'%s', request_id:%d...",
412 uri_prefix,
413 private->request_id);
414
415 g_dbus_proxy_call (private->cache_proxy,
416 "Cleanup",
417 g_variant_new ("(s)", uri_prefix),
418 G_DBUS_CALL_FLAGS_NONE,
419 -1,
420 NULL,
421 NULL,
422 NULL);
423
424 return TRUE;
425 }
426
427 /**
428 * tracker_thumbnailer_send:
429 *
430 * Sends to the thumbnailer all stored requests.
431 *
432 * Since: 0.8
433 */
434 void
435 tracker_thumbnailer_send (void)
436 {
437 TrackerThumbnailerPrivate *private;
438 guint list_len;
439
440 #if GLIB_CHECK_VERSION (2,31,0)
441 private = g_private_get (&private_key);
442 #else
443 private = g_static_private_get (&private_key);
444 #endif
445 g_return_if_fail (private != NULL);
446
447 if (!private->service_is_available) {
448 return;
449 }
450
451 list_len = g_slist_length (private->removes);
452
453 if (list_len > 0) {
454 GStrv uri_strv;
455
456 uri_strv = tracker_dbus_slist_to_strv (private->removes);
457
458 g_dbus_proxy_call (private->cache_proxy,
459 "Delete",
460 g_variant_new ("(^as)", uri_strv),
461 G_DBUS_CALL_FLAGS_NONE,
462 -1,
463 NULL,
464 NULL,
465 NULL);
466
467 g_message ("Thumbnailer removes queue sent with %d items to thumbnailer daemon, request ID:%d...",
468 list_len,
469 private->request_id++);
470
471 /* Clean up newly created GStrv */
472 g_strfreev (uri_strv);
473
474 /* Clean up privately held data */
475 g_slist_foreach (private->removes, (GFunc) g_free, NULL);
476 g_slist_free (private->removes);
477 private->removes = NULL;
478 }
479
480 list_len = g_slist_length (private->moves_from);
481
482 if (list_len > 0) {
483 GStrv from_strv, to_strv;
484
485 g_assert (list_len == g_slist_length (private->moves_to));
486
487 from_strv = tracker_dbus_slist_to_strv (private->moves_from);
488 to_strv = tracker_dbus_slist_to_strv (private->moves_to);
489
490 g_dbus_proxy_call (private->cache_proxy,
491 "Move",
492 g_variant_new ("(^as^as)", from_strv, to_strv),
493 G_DBUS_CALL_FLAGS_NONE,
494 -1,
495 NULL,
496 NULL,
497 NULL);
498
499 g_message ("Thumbnailer moves queue sent with %d items to thumbnailer daemon, request ID:%d...",
500 list_len,
501 private->request_id++);
502
503 /* Clean up newly created GStrv */
504 g_strfreev (from_strv);
505 g_strfreev (to_strv);
506
507 /* Clean up privately held data */
508 g_slist_foreach (private->moves_from, (GFunc) g_free, NULL);
509 g_slist_free (private->moves_from);
510 private->moves_from = NULL;
511
512 g_slist_foreach (private->moves_to, (GFunc) g_free, NULL);
513 g_slist_free (private->moves_to);
514 private->moves_to = NULL;
515 }
516 }