No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | tracker-miner-applications.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 <libtracker-common/tracker-utils.h>
23 #include <libtracker-common/tracker-ontologies.h>
24 #include <libtracker-common/tracker-locale.h>
25
26 #include "tracker-miner-applications.h"
27 #include "tracker-miner-locale.h"
28
29 #ifdef HAVE_MEEGOTOUCH
30 #include "tracker-miner-meego.h"
31 #endif
32
33 #define GROUP_DESKTOP_ENTRY "Desktop Entry"
34
35 #define APPLICATION_DATASOURCE_URN "urn:nepomuk:datasource:84f20000-1241-11de-8c30-0800200c9a66"
36 #define APPLET_DATASOURCE_URN "urn:nepomuk:datasource:192bd060-1f9a-11de-8c30-0800200c9a66"
37 #define SOFTWARE_CATEGORY_URN_PREFIX "urn:software-category:"
38 #define THEME_ICON_URN_PREFIX "urn:theme-icon:"
39
40 static void miner_applications_initable_iface_init (GInitableIface *iface);
41 static gboolean miner_applications_initable_init (GInitable *initable,
42 GCancellable *cancellable,
43 GError **error);
44 static gboolean miner_applications_process_file (TrackerMinerFS *fs,
45 GFile *file,
46 TrackerSparqlBuilder *sparql,
47 GCancellable *cancellable);
48 static gboolean miner_applications_process_file_attributes (TrackerMinerFS *fs,
49 GFile *file,
50 TrackerSparqlBuilder *sparql,
51 GCancellable *cancellable);
52 static void miner_applications_finalize (GObject *object);
53
54
55 static GQuark miner_applications_error_quark = 0;
56
57 typedef struct ProcessApplicationData ProcessApplicationData;
58
59 struct ProcessApplicationData {
60 TrackerMinerFS *miner;
61 GFile *file;
62 TrackerSparqlBuilder *sparql;
63 GCancellable *cancellable;
64 GKeyFile *key_file;
65 gchar *type;
66 };
67
68 static GInitableIface* miner_applications_initable_parent_iface;
69
70 G_DEFINE_TYPE_WITH_CODE (TrackerMinerApplications, tracker_miner_applications, TRACKER_TYPE_MINER_FS,
71 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
72 miner_applications_initable_iface_init));
73
74 static void
75 tracker_miner_applications_class_init (TrackerMinerApplicationsClass *klass)
76 {
77 GObjectClass *object_class = G_OBJECT_CLASS (klass);
78 TrackerMinerFSClass *miner_fs_class = TRACKER_MINER_FS_CLASS (klass);
79
80 object_class->finalize = miner_applications_finalize;
81
82 miner_fs_class->process_file = miner_applications_process_file;
83 miner_fs_class->process_file_attributes = miner_applications_process_file_attributes;
84
85 miner_applications_error_quark = g_quark_from_static_string ("TrackerMinerApplications");
86 }
87
88 static void
89 tracker_miner_applications_init (TrackerMinerApplications *ma)
90 {
91 }
92
93 static void
94 miner_applications_initable_iface_init (GInitableIface *iface)
95 {
96 miner_applications_initable_parent_iface = g_type_interface_peek_parent (iface);
97 iface->init = miner_applications_initable_init;
98 }
99
100 static void
101 miner_applications_basedir_add (TrackerMinerFS *fs,
102 const gchar *basedir)
103 {
104 TrackerIndexingTree *indexing_tree;
105 GFile *file;
106 gchar *path;
107
108 indexing_tree = tracker_miner_fs_get_indexing_tree (fs);
109
110 /* Add $dir/applications */
111 path = g_build_filename (basedir, "applications", NULL);
112 file = g_file_new_for_path (path);
113 g_message (" Adding:'%s'", path);
114
115 tracker_indexing_tree_add (indexing_tree, file,
116 TRACKER_DIRECTORY_FLAG_RECURSE |
117 TRACKER_DIRECTORY_FLAG_MONITOR |
118 TRACKER_DIRECTORY_FLAG_CHECK_MTIME);
119 g_object_unref (file);
120 g_free (path);
121
122 /* Add $dir/desktop-directories */
123 path = g_build_filename (basedir, "desktop-directories", NULL);
124 file = g_file_new_for_path (path);
125 g_message (" Adding:'%s'", path);
126 tracker_indexing_tree_add (indexing_tree, file,
127 TRACKER_DIRECTORY_FLAG_RECURSE |
128 TRACKER_DIRECTORY_FLAG_MONITOR |
129 TRACKER_DIRECTORY_FLAG_CHECK_MTIME);
130 g_object_unref (file);
131 g_free (path);
132 }
133
134 static void
135 miner_applications_add_directories (TrackerMinerFS *fs)
136 {
137 #ifdef HAVE_MEEGOTOUCH
138 TrackerIndexingTree *indexing_tree;
139 GFile *file;
140 const gchar *path;
141 #endif /* HAVE_MEEGOTOUCH */
142 const gchar * const *xdg_dirs;
143 const gchar *user_data_dir;
144 gint i;
145
146 g_message ("Setting up applications to iterate from XDG system directories");
147
148 /* Add all XDG system and local dirs */
149 xdg_dirs = g_get_system_data_dirs ();
150
151 for (i = 0; xdg_dirs[i]; i++) {
152 miner_applications_basedir_add (fs, xdg_dirs[i]);
153 }
154
155 g_message ("Setting up applications to iterate from XDG user directories");
156
157 user_data_dir = g_get_user_data_dir ();
158 if (user_data_dir) {
159 miner_applications_basedir_add (fs, user_data_dir);
160 }
161
162 #ifdef HAVE_MEEGOTOUCH
163 /* NOTE: We don't use miner_applications_basedir_add() for
164 * this location because it is unique to MeeGoTouch.
165 */
166 path = "/usr/lib/duicontrolpanel/";
167 indexing_tree = tracker_miner_fs_get_indexing_tree (fs);
168
169 g_message ("Setting up applications to iterate from MeegoTouch directories");
170 g_message (" Adding:'%s'", path);
171
172 file = g_file_new_for_path (path);
173 tracker_indexing_tree_add (indexing_tree, file,
174 TRACKER_DIRECTORY_FLAG_RECURSE |
175 TRACKER_DIRECTORY_FLAG_MONITOR |
176 TRACKER_DIRECTORY_FLAG_CHECK_MTIME);
177 g_object_unref (file);
178 #endif /* HAVE_MEEGOTOUCH */
179 }
180
181 static void
182 tracker_locale_notify_cb (TrackerLocaleID id,
183 gpointer user_data)
184 {
185 TrackerMiner *miner = user_data;
186
187 if (tracker_miner_applications_detect_locale_changed (miner)) {
188 tracker_miner_fs_set_mtime_checking (TRACKER_MINER_FS (miner), TRUE);
189
190 miner_applications_add_directories (TRACKER_MINER_FS (miner));
191 }
192 }
193
194 static void
195 miner_finished_cb (TrackerMinerFS *fs,
196 gdouble seconds_elapsed,
197 guint total_directories_found,
198 guint total_directories_ignored,
199 guint total_files_found,
200 guint total_files_ignored,
201 gpointer user_data)
202 {
203 /* Update locale file if necessary */
204 if (tracker_miner_locale_changed ()) {
205 tracker_miner_locale_set_current ();
206 }
207 }
208
209 static gboolean
210 miner_applications_initable_init (GInitable *initable,
211 GCancellable *cancellable,
212 GError **error)
213 {
214 TrackerMinerFS *fs;
215 TrackerMinerApplications *app;
216 GError *inner_error = NULL;
217 TrackerIndexingTree *indexing_tree;
218
219 fs = TRACKER_MINER_FS (initable);
220 app = TRACKER_MINER_APPLICATIONS (initable);
221 indexing_tree = tracker_miner_fs_get_indexing_tree (fs);
222
223 /* Set up files filter, deny every file, but
224 * those with a .desktop/directory extension
225 */
226 tracker_indexing_tree_set_default_policy (indexing_tree,
227 TRACKER_FILTER_FILE,
228 TRACKER_FILTER_POLICY_DENY);
229 tracker_indexing_tree_add_filter (indexing_tree,
230 TRACKER_FILTER_FILE,
231 "*.desktop");
232 tracker_indexing_tree_add_filter (indexing_tree,
233 TRACKER_FILTER_FILE,
234 "*.directory");
235
236 /* Chain up parent's initable callback before calling child's one */
237 if (!miner_applications_initable_parent_iface->init (initable, cancellable, &inner_error)) {
238 g_propagate_error (error, inner_error);
239 return FALSE;
240 }
241
242 g_signal_connect (fs, "finished",
243 G_CALLBACK (miner_finished_cb),
244 NULL);
245
246 miner_applications_add_directories (fs);
247
248 #ifdef HAVE_MEEGOTOUCH
249 tracker_miner_applications_meego_init ();
250 #endif /* HAVE_MEEGOTOUCH */
251
252 app->locale_notification_id = tracker_locale_notify_add (TRACKER_LOCALE_LANGUAGE,
253 tracker_locale_notify_cb,
254 app,
255 NULL);
256
257 return TRUE;
258 }
259
260 static void
261 miner_applications_finalize (GObject *object)
262 {
263 TrackerMinerApplications *app;
264
265 app = TRACKER_MINER_APPLICATIONS (object);
266
267 tracker_locale_notify_remove (app->locale_notification_id);
268
269 #ifdef HAVE_MEEGOTOUCH
270 tracker_miner_applications_meego_shutdown ();
271 #endif /* HAVE_MEEGOTOUCH */
272
273 G_OBJECT_CLASS (tracker_miner_applications_parent_class)->finalize (object);
274 }
275
276 static void
277 insert_data_from_desktop_file (TrackerSparqlBuilder *sparql,
278 const gchar *subject,
279 const gchar *metadata_key,
280 GKeyFile *desktop_file,
281 const gchar *key,
282 const gchar *locale)
283 {
284 gchar *str;
285
286 if (locale) {
287 /* Try to get the key with our desired LANG locale... */
288 str = g_key_file_get_locale_string (desktop_file, GROUP_DESKTOP_ENTRY, key, locale, NULL);
289 /* If our desired locale failed, use the list of LANG locales prepared by GLib
290 * (will return untranslated string if none of the locales available) */
291 if (!str) {
292 str = g_key_file_get_locale_string (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL, NULL);
293 }
294 } else {
295 str = g_key_file_get_string (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL);
296 }
297
298 if (str) {
299 tracker_sparql_builder_subject_iri (sparql, subject);
300 tracker_sparql_builder_predicate_iri (sparql, metadata_key);
301 tracker_sparql_builder_object_string (sparql, str);
302 g_free (str);
303 }
304 }
305
306 static GKeyFile *
307 get_desktop_key_file (GFile *file,
308 gchar **type,
309 GError **error)
310 {
311 GKeyFile *key_file;
312 gchar *path;
313 gchar *str;
314
315 path = g_file_get_path (file);
316 key_file = g_key_file_new ();
317 *type = NULL;
318
319 if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL)) {
320 g_set_error (error, miner_applications_error_quark, 0, "Couldn't load desktop file:'%s'", path);
321 g_key_file_free (key_file);
322 g_free (path);
323 return NULL;
324 }
325
326 str = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "Type", NULL);
327
328 if (G_UNLIKELY (!str)) {
329 *type = NULL;
330
331 g_set_error_literal (error, miner_applications_error_quark, 0, "Desktop file doesn't contain type");
332 g_key_file_free (key_file);
333 g_free (path);
334 return NULL;
335 } else {
336 /* Sanitize type */
337 *type = g_strstrip (str);
338 }
339
340 g_free (path);
341
342 return key_file;
343 }
344
345 static void
346 process_directory (ProcessApplicationData *data,
347 GFileInfo *file_info,
348 GError **error)
349 {
350 TrackerSparqlBuilder *sparql;
351 gchar *urn, *path, *uri;
352
353 sparql = data->sparql;
354
355 path = g_file_get_path (data->file);
356 uri = g_file_get_uri (data->file);
357 urn = tracker_sparql_escape_uri_printf ("urn:applications-dir:%s", path);
358
359 tracker_sparql_builder_insert_silent_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
360
361 tracker_sparql_builder_subject_iri (sparql, urn);
362
363 tracker_sparql_builder_predicate (sparql, "a");
364 tracker_sparql_builder_object (sparql, "nfo:FileDataObject");
365 tracker_sparql_builder_object (sparql, "nie:DataObject");
366 tracker_sparql_builder_object (sparql, "nie:Folder");
367
368 tracker_sparql_builder_predicate (sparql, "tracker:available");
369 tracker_sparql_builder_object_boolean (sparql, TRUE);
370
371 tracker_sparql_builder_predicate (sparql, "nie:isStoredAs");
372 tracker_sparql_builder_object_iri (sparql, urn);
373
374 tracker_sparql_builder_predicate (sparql, "nie:url");
375 tracker_sparql_builder_object_string (sparql, uri);
376
377 if (file_info) {
378 guint64 time;
379
380 time = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
381 tracker_sparql_builder_predicate (sparql, "nfo:fileLastModified");
382 tracker_sparql_builder_object_date (sparql, (time_t *) &time);
383 }
384
385 tracker_sparql_builder_insert_close (data->sparql);
386
387 g_free (path);
388 g_free (urn);
389 g_free (uri);
390 }
391
392 static void
393 process_desktop_file (ProcessApplicationData *data,
394 GFileInfo *file_info,
395 GError **error)
396 {
397 TrackerSparqlBuilder *sparql;
398 GKeyFile *key_file;
399 gchar *name = NULL;
400 gchar *path;
401 gchar *type;
402 gchar *filename;
403 gchar *uri = NULL;
404 GStrv cats;
405 gsize cats_len;
406 gboolean is_software = TRUE;
407 const gchar *parent_urn;
408 gchar *lang;
409 #ifdef HAVE_MEEGOTOUCH
410 gchar *logical_id = NULL;
411 gchar *translation_catalog = NULL;
412 #endif /* HAVE_MEEGOTOUCH */
413
414 sparql = data->sparql;
415 key_file = data->key_file;
416 type = data->type;
417
418 path = g_file_get_path (data->file);
419
420 /* Retrieve LANG locale setup */
421 lang = tracker_locale_get (TRACKER_LOCALE_LANGUAGE);
422
423 /* Try to get the categories with our desired LANG locale... */
424 cats = g_key_file_get_locale_string_list (key_file, GROUP_DESKTOP_ENTRY, "Categories", lang, &cats_len, NULL);
425 if (!cats) {
426 /* If our desired locale failed, use the list of LANG locales prepared by GLib
427 * (will return untranslated string if none of the locales available) */
428 cats = g_key_file_get_locale_string_list (key_file, GROUP_DESKTOP_ENTRY, "Categories", NULL, &cats_len, NULL);
429 }
430
431 /* NOTE: We sanitize categories later on when iterating them */
432
433 #ifdef HAVE_MEEGOTOUCH
434 /* If defined, start with the logical strings */
435 logical_id = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "X-MeeGo-Logical-Id", NULL);
436 translation_catalog = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "X-MeeGo-Translation-Catalog", NULL);
437
438 if (logical_id && translation_catalog) {
439 name = tracker_miner_applications_meego_translate (translation_catalog, logical_id);
440 }
441
442 g_free (logical_id);
443 g_free (translation_catalog);
444 #endif /* HAVE_MEEGOTOUCH */
445
446 if (!name) {
447 /* Try to get the name with our desired LANG locale... */
448 name = g_key_file_get_locale_string (key_file, GROUP_DESKTOP_ENTRY, "Name", lang, NULL);
449 if (!name) {
450 /* If our desired locale failed, use the list of LANG locales prepared by GLib
451 * (will return untranslated string if none of the locales available) */
452 name = g_key_file_get_locale_string (key_file, GROUP_DESKTOP_ENTRY, "Name", NULL, NULL);
453 }
454 }
455
456 /* Sanitize name */
457 if (name) {
458 g_strstrip (name);
459 }
460
461 if (name && g_ascii_strcasecmp (type, "Directory") == 0) {
462 gchar *canonical_uri = tracker_sparql_escape_uri_printf (SOFTWARE_CATEGORY_URN_PREFIX "%s", path);
463 gchar *icon = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "Icon", NULL);
464
465 uri = canonical_uri;
466 tracker_sparql_builder_insert_silent_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
467 tracker_sparql_builder_subject_iri (sparql, uri);
468
469 tracker_sparql_builder_predicate (sparql, "a");
470 tracker_sparql_builder_object (sparql, "nfo:SoftwareCategory");
471
472 if (icon) {
473 gchar *escaped_icon;
474 gchar *icon_uri;
475
476 /* Sanitize icon */
477 g_strstrip (icon);
478
479 escaped_icon = g_uri_escape_string (icon, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
480
481 icon_uri = g_strdup_printf (THEME_ICON_URN_PREFIX "%s", escaped_icon);
482
483 tracker_sparql_builder_subject_iri (sparql, icon_uri);
484 tracker_sparql_builder_predicate (sparql, "a");
485 tracker_sparql_builder_object (sparql, "nfo:Image");
486
487 tracker_sparql_builder_subject_iri (sparql, uri);
488 tracker_sparql_builder_predicate (sparql, "nfo:softwareCategoryIcon");
489 tracker_sparql_builder_object_iri (sparql, icon_uri);
490
491 g_free (icon_uri);
492 g_free (escaped_icon);
493 g_free (icon);
494 }
495
496 is_software = FALSE;
497 } else if (name && g_ascii_strcasecmp (type, "Application") == 0) {
498 uri = g_file_get_uri (data->file);
499 tracker_sparql_builder_insert_silent_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
500
501 tracker_sparql_builder_subject_iri (sparql, APPLICATION_DATASOURCE_URN);
502 tracker_sparql_builder_predicate (sparql, "a");
503 tracker_sparql_builder_object (sparql, "nie:DataSource");
504
505 tracker_sparql_builder_subject_iri (sparql, uri);
506
507 tracker_sparql_builder_predicate (sparql, "a");
508 tracker_sparql_builder_object (sparql, "nfo:SoftwareApplication");
509 tracker_sparql_builder_object (sparql, "nie:DataObject");
510
511 tracker_sparql_builder_predicate (sparql, "nie:dataSource");
512 tracker_sparql_builder_object_iri (sparql, APPLICATION_DATASOURCE_URN);
513 } else if (name && g_ascii_strcasecmp (type, "Link") == 0) {
514 gchar *url = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "URL", NULL);
515
516 if (url) {
517 uri = g_file_get_uri (data->file);
518 tracker_sparql_builder_insert_silent_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
519
520 tracker_sparql_builder_subject_iri (sparql, uri);
521 tracker_sparql_builder_predicate (sparql, "a");
522 tracker_sparql_builder_object (sparql, "nfo:Bookmark");
523
524 tracker_sparql_builder_predicate (sparql, "nfo:bookmarks");
525 tracker_sparql_builder_object_iri (sparql, url);
526
527 tracker_sparql_builder_predicate (sparql, "nie:dataSource");
528 tracker_sparql_builder_object_iri (sparql, APPLICATION_DATASOURCE_URN);
529
530 is_software = FALSE;
531
532 g_free (url);
533 } else {
534 g_warning ("Invalid desktop file: '%s'", uri);
535 g_warning (" Type 'Link' requires a URL");
536 }
537 #ifdef HAVE_MEEGOTOUCH
538 } else if (name && g_ascii_strcasecmp (type, "ControlPanelApplet") == 0) {
539 /* Special case control panel applets */
540 /* The URI of the InformationElement should be a UUID URN */
541 uri = g_file_get_uri (data->file);
542 tracker_sparql_builder_insert_silent_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
543
544 tracker_sparql_builder_subject_iri (sparql, APPLET_DATASOURCE_URN);
545 tracker_sparql_builder_predicate (sparql, "a");
546 tracker_sparql_builder_object (sparql, "nie:DataSource");
547
548 /* TODO This is atm specific for Maemo */
549 tracker_sparql_builder_subject_iri (sparql, uri);
550
551 tracker_sparql_builder_predicate (sparql, "a");
552 tracker_sparql_builder_object (sparql, "maemo:ControlPanelApplet");
553
554 tracker_sparql_builder_predicate (sparql, "nie:dataSource");
555 tracker_sparql_builder_object_iri (sparql, APPLET_DATASOURCE_URN);
556
557 /* This matches SomeApplet as Type= */
558 } else if (name && g_str_has_suffix (type, "Applet")) {
559 /* The URI of the InformationElement should be a UUID URN */
560 uri = g_file_get_uri (data->file);
561 tracker_sparql_builder_insert_silent_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
562
563 tracker_sparql_builder_subject_iri (sparql, APPLET_DATASOURCE_URN);
564 tracker_sparql_builder_predicate (sparql, "a");
565 tracker_sparql_builder_object (sparql, "nie:DataSource");
566
567 /* TODO This is atm specific for Maemo */
568 tracker_sparql_builder_subject_iri (sparql, uri);
569
570 tracker_sparql_builder_predicate (sparql, "a");
571 tracker_sparql_builder_object (sparql, "maemo:SoftwareApplet");
572
573 tracker_sparql_builder_predicate (sparql, "nie:dataSource");
574 tracker_sparql_builder_object_iri (sparql, APPLET_DATASOURCE_URN);
575
576 } else if (name && g_ascii_strcasecmp (type, "DUIApplication") == 0) {
577
578 uri = g_file_get_uri (data->file);
579 tracker_sparql_builder_insert_silent_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
580
581 tracker_sparql_builder_subject_iri (sparql, APPLICATION_DATASOURCE_URN);
582 tracker_sparql_builder_predicate (sparql, "a");
583 tracker_sparql_builder_object (sparql, "nie:DataSource");
584
585 tracker_sparql_builder_subject_iri (sparql, uri);
586
587 tracker_sparql_builder_predicate (sparql, "a");
588 tracker_sparql_builder_object (sparql, "nfo:SoftwareApplication");
589 tracker_sparql_builder_object (sparql, "nie:DataObject");
590
591 tracker_sparql_builder_predicate (sparql, "nie:dataSource");
592 tracker_sparql_builder_object_iri (sparql, APPLICATION_DATASOURCE_URN);
593 #endif /* HAVE_MEEGOTOUCH */
594 } else {
595 /* Invalid type, all valid types are already listed above */
596 uri = g_file_get_uri (data->file);
597 tracker_sparql_builder_insert_silent_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
598
599 tracker_sparql_builder_subject_iri (sparql, APPLICATION_DATASOURCE_URN);
600 tracker_sparql_builder_predicate (sparql, "a");
601 tracker_sparql_builder_object (sparql, "nie:DataSource");
602
603 tracker_sparql_builder_subject_iri (sparql, uri);
604
605 tracker_sparql_builder_predicate (sparql, "a");
606 tracker_sparql_builder_object (sparql, "nfo:SoftwareApplication");
607 tracker_sparql_builder_object (sparql, "nie:DataObject");
608
609 tracker_sparql_builder_predicate (sparql, "nie:dataSource");
610 tracker_sparql_builder_object_iri (sparql, APPLICATION_DATASOURCE_URN);
611
612 if (name) {
613 /* If we got a name, then the issue comes from the type.
614 * As we're defaulting to Application here, we just g_debug() the problem. */
615 g_debug ("Invalid desktop file: '%s'", uri);
616 g_debug (" Type '%s' is not part of the desktop file specification (expected 'Application', 'Link' or 'Directory')", type);
617 g_debug (" Defaulting to 'Application'");
618 } else {
619 /* If we didn't get a name, the problem is more severe as we don't default it
620 * to anything, so we g_warning() it. */
621 g_warning ("Invalid desktop file: '%s'", uri);
622 #ifdef HAVE_MEEGOTOUCH
623 g_warning (" Couldn't get name, missing or wrong key (X-MeeGo-Logical-Id, X-MeeGo-Translation-Catalog or Name)");
624 #else
625 g_warning (" Couldn't get name, missing key (Name)");
626 #endif
627 }
628 }
629
630 if (sparql && uri) {
631 gchar *desktop_file_uri;
632
633 tracker_sparql_builder_predicate (sparql, "a");
634
635 if (is_software) {
636 tracker_sparql_builder_object (sparql, "nfo:Executable");
637 }
638
639 tracker_sparql_builder_object (sparql, "nfo:FileDataObject");
640 tracker_sparql_builder_object (sparql, "nie:DataObject");
641
642 /* Apparently this gets added by the file-module ATM
643 tracker_sparql_builder_predicate (sparql, "tracker:available");
644 tracker_sparql_builder_object_boolean (sparql, TRUE); */
645
646 /* We should always always have a proper name if the desktop file is correct
647 * w.r.t to the Meego or Freedesktop specs, but sometimes this is not true,
648 * so instead of passing wrong stuff to the SPARQL builder, we avoid it.
649 * If we don't have a proper name, we already warned it before. */
650 if (name) {
651 tracker_sparql_builder_predicate (sparql, "nie:title");
652 tracker_sparql_builder_object_string (sparql, name);
653 }
654
655 if (is_software) {
656 gchar *icon;
657
658 insert_data_from_desktop_file (sparql,
659 uri,
660 TRACKER_NIE_PREFIX "comment",
661 key_file,
662 "Comment",
663 lang);
664 insert_data_from_desktop_file (sparql,
665 uri,
666 TRACKER_NFO_PREFIX "softwareCmdLine",
667 key_file,
668 "Exec",
669 lang);
670
671 icon = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "Icon", NULL);
672
673 if (icon) {
674 gchar *escaped_icon;
675 gchar *icon_uri;
676
677 /* Sanitize icon */
678 g_strstrip (icon);
679
680 escaped_icon = g_uri_escape_string (icon, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
681
682 icon_uri = g_strdup_printf (THEME_ICON_URN_PREFIX "%s", escaped_icon);
683
684 tracker_sparql_builder_subject_iri (sparql, icon_uri);
685 tracker_sparql_builder_predicate (sparql, "a");
686 tracker_sparql_builder_object (sparql, "nfo:Image");
687
688 tracker_sparql_builder_subject_iri (sparql, uri);
689 tracker_sparql_builder_predicate (sparql, "nfo:softwareIcon");
690 tracker_sparql_builder_object_iri (sparql, icon_uri);
691
692 g_free (icon_uri);
693 g_free (escaped_icon);
694 g_free (icon);
695 }
696 }
697
698 if (cats) {
699 gsize i;
700
701 for (i = 0 ; cats[i] && i < cats_len ; i++) {
702 gchar *cat_uri;
703 gchar *cat;
704
705 cat = cats[i];
706
707 if (!cat) {
708 continue;
709 }
710
711 /* Sanitize category */
712 g_strstrip (cat);
713
714 cat_uri = tracker_sparql_escape_uri_printf (SOFTWARE_CATEGORY_URN_PREFIX "%s", cat);
715
716 /* There are also .desktop
717 * files that describe these categories, but we can handle
718 * preemptively creating them if we visit a app .desktop
719 * file that mentions one that we don't yet know about */
720
721 tracker_sparql_builder_subject_iri (sparql, cat_uri);
722 tracker_sparql_builder_predicate (sparql, "a");
723 tracker_sparql_builder_object (sparql, "nfo:SoftwareCategory");
724
725 tracker_sparql_builder_predicate (sparql, "nie:title");
726 tracker_sparql_builder_object_string (sparql, cat);
727
728 tracker_sparql_builder_subject_iri (sparql, uri);
729 tracker_sparql_builder_predicate (sparql, "nie:isLogicalPartOf");
730 tracker_sparql_builder_object_iri (sparql, cat_uri);
731
732 g_free (cat_uri);
733 }
734 }
735
736 filename = g_filename_display_basename (path);
737 tracker_sparql_builder_predicate (sparql, "nfo:fileName");
738 tracker_sparql_builder_object_string (sparql, filename);
739 g_free (filename);
740
741 /* The URL of the DataObject */
742 desktop_file_uri = g_file_get_uri (data->file);
743 tracker_sparql_builder_predicate (sparql, "nie:url");
744 tracker_sparql_builder_object_string (sparql, desktop_file_uri);
745
746 /* Laying the link between the IE and the DO */
747 tracker_sparql_builder_subject_iri (sparql, uri);
748 tracker_sparql_builder_predicate (sparql, "nie:isStoredAs");
749 tracker_sparql_builder_object_iri (sparql, desktop_file_uri);
750
751
752 g_free (desktop_file_uri);
753 }
754
755 if (file_info) {
756 guint64 time;
757
758 time = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
759 tracker_sparql_builder_predicate (sparql, "nfo:fileLastModified");
760 tracker_sparql_builder_object_date (sparql, (time_t *) &time);
761 }
762
763 parent_urn = tracker_miner_fs_get_parent_urn (TRACKER_MINER_FS (data->miner), data->file);
764
765 if (parent_urn) {
766 tracker_sparql_builder_predicate (sparql, "nfo:belongsToContainer");
767 tracker_sparql_builder_object_iri (sparql, parent_urn);
768 }
769
770 tracker_sparql_builder_insert_close (sparql);
771
772 g_strfreev (cats);
773
774 g_free (uri);
775 g_free (path);
776 g_free (name);
777 g_free (lang);
778 }
779
780 static void
781 process_application_data_free (ProcessApplicationData *data)
782 {
783 g_object_unref (data->miner);
784 g_object_unref (data->file);
785 g_object_unref (data->sparql);
786 g_object_unref (data->cancellable);
787 g_free (data->type);
788
789 if (data->key_file) {
790 g_key_file_free (data->key_file);
791 }
792
793 g_slice_free (ProcessApplicationData, data);
794 }
795
796 static void
797 process_file_cb (GObject *object,
798 GAsyncResult *result,
799 gpointer user_data)
800 {
801 ProcessApplicationData *data;
802 GFileInfo *file_info;
803 GError *error = NULL;
804 GFile *file;
805
806 data = user_data;
807 file = G_FILE (object);
808 file_info = g_file_query_info_finish (file, result, &error);
809
810 if (error) {
811 tracker_miner_fs_file_notify (TRACKER_MINER_FS (data->miner), file, error);
812 process_application_data_free (data);
813 g_error_free (error);
814 return;
815 }
816
817 if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) {
818 process_directory (data, file_info, &error);
819 } else {
820 data->key_file = get_desktop_key_file (file, &data->type, &error);
821 if (!data->key_file) {
822 gchar *uri;
823
824 uri = g_file_get_uri (file);
825 g_warning ("Couldn't properly parse desktop file '%s': '%s'",
826 uri,
827 error ? error->message : "unknown error");
828 g_free (uri);
829 g_clear_error (&error);
830
831 error = g_error_new_literal (miner_applications_error_quark, 0, "File is not a key file");
832 } else if (g_key_file_get_boolean (data->key_file, GROUP_DESKTOP_ENTRY, "Hidden", NULL)) {
833 error = g_error_new_literal (miner_applications_error_quark, 0, "Desktop file is 'hidden', not gathering metadata for it");
834 } else {
835 process_desktop_file (data, file_info, &error);
836 }
837 }
838
839 tracker_miner_fs_file_notify (TRACKER_MINER_FS (data->miner), data->file, error);
840 process_application_data_free (data);
841
842 if (error) {
843 g_error_free (error);
844 }
845
846 if (file_info) {
847 g_object_unref (file_info);
848 }
849 }
850
851 static gboolean
852 miner_applications_process_file (TrackerMinerFS *fs,
853 GFile *file,
854 TrackerSparqlBuilder *sparql,
855 GCancellable *cancellable)
856 {
857 ProcessApplicationData *data;
858 const gchar *attrs;
859
860 data = g_slice_new0 (ProcessApplicationData);
861 data->miner = g_object_ref (fs);
862 data->sparql = g_object_ref (sparql);
863 data->file = g_object_ref (file);
864 data->cancellable = g_object_ref (cancellable);
865
866 attrs = G_FILE_ATTRIBUTE_TIME_MODIFIED ","
867 G_FILE_ATTRIBUTE_STANDARD_TYPE;
868
869 g_file_query_info_async (file,
870 attrs,
871 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
872 G_PRIORITY_DEFAULT,
873 cancellable,
874 process_file_cb,
875 data);
876
877 return TRUE;
878 }
879
880 static gboolean
881 miner_applications_process_file_attributes (TrackerMinerFS *fs,
882 GFile *file,
883 TrackerSparqlBuilder *sparql,
884 GCancellable *cancellable)
885 {
886 gchar *uri;
887
888 /* We don't care about file attribute changes here */
889 uri = g_file_get_uri (file);
890 g_debug ("Ignoring file attribute changes in '%s'", uri);
891 g_free (uri);
892
893 return FALSE;
894 }
895
896 /* If a reset is requested, we will remove from the store all items previously
897 * inserted by the tracker-miner-applications, this is:
898 * (a) all elements which are nfo:softwareIcon of a given nfo:Software
899 * (b) all nfo:Software in our graph (includes both applications and maemo applets)
900 * (c) all elements which are nfo:softwareCategoryIcon of a given nfo:SoftwareCategory
901 * (d) all nfo:SoftwareCategory in our graph
902 */
903 static void
904 miner_applications_reset (TrackerMiner *miner)
905 {
906 GError *error = NULL;
907 TrackerSparqlBuilder *sparql;
908
909 sparql = tracker_sparql_builder_new_update ();
910
911 /* (a) all elements which are nfo:softwareIcon of a given nfo:Software */
912 tracker_sparql_builder_delete_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
913 tracker_sparql_builder_subject_variable (sparql, "icon");
914 tracker_sparql_builder_predicate (sparql, "a");
915 tracker_sparql_builder_object (sparql, "rdfs:Resource");
916 tracker_sparql_builder_delete_close (sparql);
917
918 tracker_sparql_builder_where_open (sparql);
919 tracker_sparql_builder_subject_variable (sparql, "software");
920 tracker_sparql_builder_predicate (sparql, "a");
921 tracker_sparql_builder_object (sparql, "nfo:Software");
922 tracker_sparql_builder_subject_variable (sparql, "icon");
923 tracker_sparql_builder_predicate (sparql, "nfo:softwareIcon");
924 tracker_sparql_builder_object_variable (sparql, "software");
925 tracker_sparql_builder_where_close (sparql);
926
927 /* (b) all nfo:Software in our graph (includes both applications and maemo applets) */
928 tracker_sparql_builder_delete_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
929 tracker_sparql_builder_subject_variable (sparql, "software");
930 tracker_sparql_builder_predicate (sparql, "a");
931 tracker_sparql_builder_object (sparql, "rdfs:Resource");
932 tracker_sparql_builder_delete_close (sparql);
933
934 tracker_sparql_builder_where_open (sparql);
935 tracker_sparql_builder_subject_variable (sparql, "software");
936 tracker_sparql_builder_predicate (sparql, "a");
937 tracker_sparql_builder_object (sparql, "nfo:Software");
938 tracker_sparql_builder_where_close (sparql);
939
940 /* (c) all elements which are nfo:softwareCategoryIcon of a given nfo:SoftwareCategory */
941 tracker_sparql_builder_delete_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
942 tracker_sparql_builder_subject_variable (sparql, "icon");
943 tracker_sparql_builder_predicate (sparql, "a");
944 tracker_sparql_builder_object (sparql, "rdfs:Resource");
945 tracker_sparql_builder_delete_close (sparql);
946
947 tracker_sparql_builder_where_open (sparql);
948 tracker_sparql_builder_subject_variable (sparql, "category");
949 tracker_sparql_builder_predicate (sparql, "a");
950 tracker_sparql_builder_object (sparql, "nfo:SoftwareCategory");
951 tracker_sparql_builder_subject_variable (sparql, "icon");
952 tracker_sparql_builder_predicate (sparql, "nfo:softwareCategoryIcon");
953 tracker_sparql_builder_object_variable (sparql, "category");
954 tracker_sparql_builder_where_close (sparql);
955
956 /* (d) all nfo:SoftwareCategory in our graph */
957 tracker_sparql_builder_delete_open (sparql, TRACKER_MINER_FS_GRAPH_URN);
958 tracker_sparql_builder_subject_variable (sparql, "category");
959 tracker_sparql_builder_predicate (sparql, "a");
960 tracker_sparql_builder_object (sparql, "rdfs:Resource");
961 tracker_sparql_builder_delete_close (sparql);
962
963 tracker_sparql_builder_where_open (sparql);
964 tracker_sparql_builder_subject_variable (sparql, "category");
965 tracker_sparql_builder_predicate (sparql, "a");
966 tracker_sparql_builder_object (sparql, "nfo:SoftwareCategory");
967 tracker_sparql_builder_where_close (sparql);
968
969 /* Execute a sync update, we don't want the apps miner to start before
970 * we finish this. */
971 tracker_sparql_connection_update (tracker_miner_get_connection (miner),
972 tracker_sparql_builder_get_result (sparql),
973 G_PRIORITY_HIGH,
974 NULL,
975 &error);
976
977 if (error) {
978 /* Some error happened performing the query, not good */
979 g_critical ("Couldn't reset mined applications: %s",
980 error ? error->message : "unknown error");
981 g_error_free (error);
982 }
983
984 g_object_unref (sparql);
985 }
986
987 gboolean
988 tracker_miner_applications_detect_locale_changed (TrackerMiner *miner)
989 {
990 gboolean changed;
991
992 changed = tracker_miner_locale_changed ();
993 if (changed) {
994 g_message ("Locale change detected, so resetting miner to "
995 "remove all previously created items...");
996 miner_applications_reset (miner);
997 }
998 return changed;
999 }
1000
1001 TrackerMiner *
1002 tracker_miner_applications_new (GError **error)
1003 {
1004 return g_initable_new (TRACKER_TYPE_MINER_APPLICATIONS,
1005 NULL,
1006 error,
1007 "name", "Applications",
1008 "processing-pool-wait-limit", 10,
1009 "processing-pool-ready-limit", 100,
1010 NULL);
1011 }