tracker-0.16.2/src/miners/fs/tracker-miner-applications.c

No issues found

Incomplete coverage

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
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   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 }