[tracker-miners/wip/carlosg/apps-in-miner-fs: 14/17] tracker-extract: Add desktop file extractor



commit 6b9829058ede96b39dfac87f270b7fdb963a2136
Author: Carlos Garnacho <carlosg gnome org>
Date:   Sat Nov 10 16:55:30 2018 +0100

    tracker-extract: Add desktop file extractor
    
    This is the first step towards folding tracker-miner-apps into
    tracker-miner-fs, thus dropping one executable from a usual session.
    
    Implement an extractor for .desktop files as per the desktop entry
    specification [1]. This is basically a rip off of src/miner/apps,
    just tuned down to ignore directory/link types, and made to use
    TrackerResource instead of the deprecated TrackerSparqlBuilder.
    
    [1] https://specifications.freedesktop.org/desktop-entry-spec/latest/

 src/tracker-extract/10-desktop.rule             |   4 +
 src/tracker-extract/meson.build                 |   1 +
 src/tracker-extract/tracker-extract-decorator.c |   1 +
 src/tracker-extract/tracker-extract-desktop.c   | 276 ++++++++++++++++++++++++
 4 files changed, 282 insertions(+)
---
diff --git a/src/tracker-extract/10-desktop.rule b/src/tracker-extract/10-desktop.rule
new file mode 100644
index 000000000..851bd097a
--- /dev/null
+++ b/src/tracker-extract/10-desktop.rule
@@ -0,0 +1,4 @@
+[ExtractorRule]
+ModulePath=libextract-desktop.so
+MimeTypes=application/x-desktop
+FallbackRdfTypes=nfo:SoftwareApplication;
diff --git a/src/tracker-extract/meson.build b/src/tracker-extract/meson.build
index bf19f8bac..325f68329 100644
--- a/src/tracker-extract/meson.build
+++ b/src/tracker-extract/meson.build
@@ -6,6 +6,7 @@
 
 modules = [
   ['extract-bmp', 'tracker-extract-bmp.c', ['10-bmp.rule'], [tracker_miners_common_dep]],
+  ['extract-desktop', 'tracker-extract-desktop.c', ['10-desktop.rule'], [tracker_miners_common_dep]],
   ['extract-dummy', 'tracker-extract-dummy.c', ['10-comics.rule', '10-ebooks.rule'], 
[tracker_miners_common_dep]]
 ]
 
diff --git a/src/tracker-extract/tracker-extract-decorator.c b/src/tracker-extract/tracker-extract-decorator.c
index 516e1ee48..7c504363c 100644
--- a/src/tracker-extract/tracker-extract-decorator.c
+++ b/src/tracker-extract/tracker-extract-decorator.c
@@ -74,6 +74,7 @@ static const gchar *supported_classes[] = {
        "nfo:Video",
        "nfo:FilesystemImage",
        "nmm:Playlist",
+       "nfo:SoftwareApplication",
        NULL
 };
 
diff --git a/src/tracker-extract/tracker-extract-desktop.c b/src/tracker-extract/tracker-extract-desktop.c
new file mode 100644
index 000000000..27e622127
--- /dev/null
+++ b/src/tracker-extract/tracker-extract-desktop.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018, Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "tracker-common.h"
+#include <libtracker-extract/tracker-extract.h>
+
+#include "tracker-main.h"
+
+#define GROUP_DESKTOP_ENTRY          "Desktop Entry"
+
+#define SOFTWARE_CATEGORY_URN_PREFIX "urn:software-category:"
+#define THEME_ICON_URN_PREFIX        "urn:theme-icon:"
+
+static GKeyFile *
+get_desktop_key_file (GFile   *file,
+                      gchar  **type,
+                      GError **error)
+{
+       GKeyFile *key_file;
+       gchar *path;
+       gchar *str;
+
+       path = g_file_get_path (file);
+       key_file = g_key_file_new ();
+       *type = NULL;
+
+       if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, error)) {
+               g_key_file_free (key_file);
+               g_free (path);
+               return NULL;
+       }
+
+       str = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "Type", NULL);
+
+       if (G_UNLIKELY (!str)) {
+               *type = NULL;
+
+               g_set_error_literal (error, G_KEY_FILE_ERROR,
+                                    G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+                                    "Desktop file doesn't contain type");
+               g_key_file_free (key_file);
+               g_free (path);
+               return NULL;
+       } else {
+               /* Sanitize type */
+               *type = g_strstrip (str);
+       }
+
+       g_free (path);
+
+       return key_file;
+}
+
+static void
+insert_data_from_desktop_file (TrackerResource *resource,
+                               const gchar     *metadata_key,
+                               GKeyFile        *desktop_file,
+                               const gchar     *key,
+                               const gchar     *locale)
+{
+       gchar *str;
+
+       if (locale) {
+               /* Try to get the key with our desired LANG locale... */
+               str = g_key_file_get_locale_string (desktop_file, GROUP_DESKTOP_ENTRY, key, locale, NULL);
+               /* If our desired locale failed, use the list of LANG locales prepared by GLib
+                * (will return untranslated string if none of the locales available) */
+               if (!str) {
+                       str = g_key_file_get_locale_string (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL, 
NULL);
+               }
+       } else {
+               str = g_key_file_get_string (desktop_file, GROUP_DESKTOP_ENTRY, key, NULL);
+       }
+
+       if (str) {
+               tracker_resource_set_string (resource, metadata_key, str);
+               g_free (str);
+       }
+}
+
+static gboolean
+process_desktop_file (TrackerResource  *resource,
+                      GFile            *file)
+{
+       GKeyFile *key_file;
+       GError *error = NULL;
+       gchar *name = NULL;
+       gchar *type;
+       GStrv cats;
+       gsize cats_len;
+       gboolean is_software = FALSE;
+       gchar *lang;
+
+       key_file = get_desktop_key_file (file, &type, &error);
+       if (!key_file) {
+               gchar *uri;
+
+               uri = g_file_get_uri (file);
+               g_warning ("Could not load desktop file '%s': %s",
+                          uri,
+                          error->message ? error->message : "no error given");
+
+               g_error_free (error);
+               g_free (uri);
+               return FALSE;
+       }
+
+       if (g_key_file_get_boolean (key_file, GROUP_DESKTOP_ENTRY, "Hidden", NULL)) {
+               g_debug ("Desktop file is hidden");
+               g_key_file_free (key_file);
+               return FALSE;
+       }
+
+       /* Retrieve LANG locale setup */
+       lang = tracker_locale_get (TRACKER_LOCALE_LANGUAGE);
+
+       /* Try to get the categories with our desired LANG locale... */
+       cats = g_key_file_get_locale_string_list (key_file, GROUP_DESKTOP_ENTRY, "Categories", lang, 
&cats_len, NULL);
+       if (!cats) {
+               /* If our desired locale failed, use the list of LANG locales prepared by GLib
+                * (will return untranslated string if none of the locales available) */
+               cats = g_key_file_get_locale_string_list (key_file, GROUP_DESKTOP_ENTRY, "Categories", NULL, 
&cats_len, NULL);
+       }
+
+       if (!name) {
+               /* Try to get the name with our desired LANG locale... */
+               name = g_key_file_get_locale_string (key_file, GROUP_DESKTOP_ENTRY, "Name", lang, NULL);
+               if (!name) {
+                       /* If our desired locale failed, use the list of LANG locales prepared by GLib
+                        * (will return untranslated string if none of the locales available) */
+                       name = g_key_file_get_locale_string (key_file, GROUP_DESKTOP_ENTRY, "Name", NULL, 
NULL);
+               }
+       }
+
+       /* Sanitize name */
+       if (name) {
+               g_strstrip (name);
+       }
+
+       if (name && g_ascii_strcasecmp (type, "Application") == 0) {
+               tracker_resource_add_uri (resource, "rdf:type", "nfo:SoftwareApplication");
+               is_software = TRUE;
+       } else {
+               /* Invalid type, all valid types are already listed above */
+               g_warning ("Unknown desktop entry type '%s'", type);
+               g_key_file_free (key_file);
+               g_strfreev (cats);
+               g_free (lang);
+               g_free (name);
+               return FALSE;
+       }
+
+       /* We should always always have a proper name if the desktop file is correct
+        * w.r.t to the Freedesktop specs, but sometimes this is not true, so
+        * instead of passing wrong stuff to the SPARQL builder, we avoid it.
+        * If we don't have a proper name, we already warned it before. */
+       if (name)
+               tracker_resource_set_string (resource, "nie:title", name);
+
+       if (is_software) {
+               gchar *icon;
+
+               tracker_resource_add_uri (resource, "rdf:type", "nfo:Executable");
+               insert_data_from_desktop_file (resource,
+                                              "nie:comment",
+                                              key_file,
+                                              "Comment",
+                                              lang);
+               insert_data_from_desktop_file (resource,
+                                              "nfo:softwareCmdLine",
+                                              key_file,
+                                              "Exec",
+                                              lang);
+
+               icon = g_key_file_get_string (key_file, GROUP_DESKTOP_ENTRY, "Icon", NULL);
+
+               if (icon) {
+                       TrackerResource *icon_resource;
+                       gchar *escaped_icon;
+                       gchar *icon_uri;
+
+                       /* Sanitize icon */
+                       g_strstrip (icon);
+
+                       escaped_icon = g_uri_escape_string (icon, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, 
FALSE);
+
+                       icon_uri = g_strdup_printf (THEME_ICON_URN_PREFIX "%s", escaped_icon);
+
+                       icon_resource = tracker_resource_new (icon_uri);
+                       tracker_resource_add_uri (icon_resource, "rdf:type", "nfo:Image");
+                       tracker_resource_set_take_relation (resource, "nfo:softwareIcon", icon_resource);
+
+                       g_free (icon_uri);
+                       g_free (escaped_icon);
+                       g_free (icon);
+               }
+       }
+
+       if (cats) {
+               gsize i;
+
+               for (i = 0 ; cats[i] && i < cats_len ; i++) {
+                       TrackerResource *category;
+                       gchar *cat_uri;
+                       gchar *cat;
+
+                       cat = cats[i];
+
+                       if (!cat) {
+                               continue;
+                       }
+
+                       /* Sanitize category */
+                       g_strstrip (cat);
+
+                       cat_uri = tracker_sparql_escape_uri_printf (SOFTWARE_CATEGORY_URN_PREFIX "%s", cat);
+
+                       /* There are also .desktop
+                        * files that describe these categories, but we can handle
+                        * preemptively creating them if we visit a app .desktop
+                        * file that mentions one that we don't yet know about */
+
+                       category = tracker_resource_new (cat_uri);
+                       tracker_resource_add_uri (category, "rdf:type", "nfo:SoftwareCategory");
+                       tracker_resource_set_string (category, "nie:title", cat);
+                       g_free (cat_uri);
+
+                       tracker_resource_add_take_relation (resource, "nie:isLogicalPartOf", category);
+               }
+       }
+
+       g_strfreev (cats);
+
+       g_free (name);
+       g_free (lang);
+
+       return TRUE;
+}
+
+G_MODULE_EXPORT gboolean
+tracker_extract_get_metadata (TrackerExtractInfo *info)
+{
+       TrackerResource *metadata;
+
+       metadata = tracker_resource_new (NULL);
+
+       if (!process_desktop_file (metadata, tracker_extract_info_get_file (info))) {
+               g_object_unref (metadata);
+               return FALSE;
+       }
+
+       tracker_extract_info_set_resource (info, metadata);
+       g_object_unref (metadata);
+
+       return TRUE;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]