-/* Caching of mimeinfo.cache and defaults.list files {{{2 */
-
-typedef struct {
- char *path;
- GHashTable *mime_info_cache_map;
- GHashTable *defaults_list_map;
- GHashTable *mimeapps_list_added_map;
- GHashTable *mimeapps_list_removed_map;
- GHashTable *mimeapps_list_defaults_map;
- time_t mime_info_cache_timestamp;
- time_t defaults_list_timestamp;
- time_t mimeapps_list_timestamp;
-} MimeInfoCacheDir;
-
-typedef struct {
- GList *dirs; /* mimeinfo.cache and defaults.list */
- time_t last_stat_time;
-} MimeInfoCache;
-
-static MimeInfoCache *mime_info_cache = NULL;
-G_LOCK_DEFINE_STATIC (mime_info_cache);
-
-static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
- const char *mime_type,
- char **new_desktop_file_ids);
-
-static MimeInfoCache * mime_info_cache_new (void);
-
-static void
-destroy_info_cache_value (gpointer key,
- GList *value,
- gpointer data)
-{
- g_list_free_full (value, g_free);
-}
-
-static void
-destroy_info_cache_map (GHashTable *info_cache_map)
-{
- g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
- g_hash_table_destroy (info_cache_map);
-}
-
-static gboolean
-mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
- const char *cache_file,
- time_t *timestamp)
-{
- struct stat buf;
- char *filename;
-
- filename = g_build_filename (dir->path, cache_file, NULL);
-
- if (g_stat (filename, &buf) < 0)
- {
- g_free (filename);
- return TRUE;
- }
- g_free (filename);
-
- if (buf.st_mtime != *timestamp)
- return TRUE;
-
- return FALSE;
-}
-
-/* Call with lock held */
-static void
-mime_info_cache_dir_init (MimeInfoCacheDir *dir)
-{
- GError *load_error;
- GKeyFile *key_file;
- gchar *filename, **mime_types;
- int i;
- struct stat buf;
-
- load_error = NULL;
- mime_types = NULL;
-
- if (dir->mime_info_cache_map != NULL &&
- !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
- &dir->mime_info_cache_timestamp))
- return;
-
- if (dir->mime_info_cache_map != NULL)
- destroy_info_cache_map (dir->mime_info_cache_map);
-
- dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify) g_free,
- NULL);
-
- key_file = g_key_file_new ();
-
- filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
-
- if (g_stat (filename, &buf) < 0)
- goto error;
-
- dir->mime_info_cache_timestamp = buf.st_mtime;
-
- g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
-
- g_free (filename);
- filename = NULL;
-
- if (load_error != NULL)
- goto error;
-
- mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
- NULL, &load_error);
-
- if (load_error != NULL)
- goto error;
-
- for (i = 0; mime_types[i] != NULL; i++)
- {
- gchar **desktop_file_ids;
- char *unaliased_type;
- desktop_file_ids = g_key_file_get_string_list (key_file,
- MIME_CACHE_GROUP,
- mime_types[i],
- NULL,
- NULL);
-
- if (desktop_file_ids == NULL)
- continue;
-
- unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
- mime_info_cache_dir_add_desktop_entries (dir,
- unaliased_type,
- desktop_file_ids);
- g_free (unaliased_type);
-
- g_strfreev (desktop_file_ids);
- }
-
- g_strfreev (mime_types);
- g_key_file_free (key_file);
-
- return;
- error:
- g_free (filename);
- g_key_file_free (key_file);
-
- if (mime_types != NULL)
- g_strfreev (mime_types);
-
- if (load_error)
- g_error_free (load_error);
-}
-
-static void
-mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
-{
- GKeyFile *key_file;
- GError *load_error;
- gchar *filename, **mime_types;
- char *unaliased_type;
- char **desktop_file_ids;
- int i;
- struct stat buf;
-
- load_error = NULL;
- mime_types = NULL;
-
- if (dir->defaults_list_map != NULL &&
- !mime_info_cache_dir_out_of_date (dir, "defaults.list",
- &dir->defaults_list_timestamp))
- return;
-
- if (dir->defaults_list_map != NULL)
- g_hash_table_destroy (dir->defaults_list_map);
- dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)g_strfreev);
-
-
- key_file = g_key_file_new ();
-
- filename = g_build_filename (dir->path, "defaults.list", NULL);
- if (g_stat (filename, &buf) < 0)
- goto error;
-
- dir->defaults_list_timestamp = buf.st_mtime;
-
- g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
- g_free (filename);
- filename = NULL;
-
- if (load_error != NULL)
- goto error;
-
- mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
- NULL, NULL);
- if (mime_types != NULL)
- {
- for (i = 0; mime_types[i] != NULL; i++)
- {
- desktop_file_ids = g_key_file_get_string_list (key_file,
- DEFAULT_APPLICATIONS_GROUP,
- mime_types[i],
- NULL,
- NULL);
- if (desktop_file_ids == NULL)
- continue;
-
- unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
- g_hash_table_replace (dir->defaults_list_map,
- unaliased_type,
- desktop_file_ids);
- }
-
- g_strfreev (mime_types);
- }
-
- g_key_file_free (key_file);
- return;
-
- error:
- g_free (filename);
- g_key_file_free (key_file);
-
- if (mime_types != NULL)
- g_strfreev (mime_types);
-
- if (load_error)
- g_error_free (load_error);
-}
-
-static void
-mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
-{
- GKeyFile *key_file;
- GError *load_error;
- gchar *filename, **mime_types;
- char *unaliased_type;
- char **desktop_file_ids;
- char *desktop_id;
- int i;
- struct stat buf;
-
- load_error = NULL;
- mime_types = NULL;
-
- if (dir->mimeapps_list_added_map != NULL &&
- !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
- &dir->mimeapps_list_timestamp))
- return;
-
- if (dir->mimeapps_list_added_map != NULL)
- g_hash_table_destroy (dir->mimeapps_list_added_map);
- dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)g_strfreev);
-
- if (dir->mimeapps_list_removed_map != NULL)
- g_hash_table_destroy (dir->mimeapps_list_removed_map);
- dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)g_strfreev);
-
- if (dir->mimeapps_list_defaults_map != NULL)
- g_hash_table_destroy (dir->mimeapps_list_defaults_map);
- dir->mimeapps_list_defaults_map = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, g_free);
-
- key_file = g_key_file_new ();
-
- filename = g_build_filename (dir->path, "mimeapps.list", NULL);
- if (g_stat (filename, &buf) < 0)
- goto error;
-
- dir->mimeapps_list_timestamp = buf.st_mtime;
-
- g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
- g_free (filename);
- filename = NULL;
-
- if (load_error != NULL)
- goto error;
-
- mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
- NULL, NULL);
- if (mime_types != NULL)
- {
- for (i = 0; mime_types[i] != NULL; i++)
- {
- desktop_file_ids = g_key_file_get_string_list (key_file,
- ADDED_ASSOCIATIONS_GROUP,
- mime_types[i],
- NULL,
- NULL);
- if (desktop_file_ids == NULL)
- continue;
-
- unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
- g_hash_table_replace (dir->mimeapps_list_added_map,
- unaliased_type,
- desktop_file_ids);
- }
-
- g_strfreev (mime_types);
- }
-
- mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
- NULL, NULL);
- if (mime_types != NULL)
- {
- for (i = 0; mime_types[i] != NULL; i++)
- {
- desktop_file_ids = g_key_file_get_string_list (key_file,
- REMOVED_ASSOCIATIONS_GROUP,
- mime_types[i],
- NULL,
- NULL);
- if (desktop_file_ids == NULL)
- continue;
-
- unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
- g_hash_table_replace (dir->mimeapps_list_removed_map,
- unaliased_type,
- desktop_file_ids);
- }
-
- g_strfreev (mime_types);
- }
-
- mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
- NULL, NULL);
- if (mime_types != NULL)
- {
- for (i = 0; mime_types[i] != NULL; i++)
- {
- desktop_id = g_key_file_get_string (key_file,
- DEFAULT_APPLICATIONS_GROUP,
- mime_types[i],
- NULL);
- if (desktop_id == NULL)
- continue;
-
- unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
- g_hash_table_replace (dir->mimeapps_list_defaults_map,
- unaliased_type,
- desktop_id);
- }
-
- g_strfreev (mime_types);
- }
-
- g_key_file_free (key_file);
- return;
-
- error:
- g_free (filename);
- g_key_file_free (key_file);
-
- if (mime_types != NULL)
- g_strfreev (mime_types);
-
- if (load_error)
- g_error_free (load_error);
-}
-
-static MimeInfoCacheDir *
-mime_info_cache_dir_new (const char *path)
-{
- MimeInfoCacheDir *dir;
-
- dir = g_new0 (MimeInfoCacheDir, 1);
- dir->path = g_strdup (path);
-
- return dir;
-}
-
-static void
-mime_info_cache_dir_free (MimeInfoCacheDir *dir)
-{
- if (dir == NULL)
- return;
-
- if (dir->mime_info_cache_map != NULL)
- {
- destroy_info_cache_map (dir->mime_info_cache_map);
- dir->mime_info_cache_map = NULL;
-
- }
-
- if (dir->defaults_list_map != NULL)
- {
- g_hash_table_destroy (dir->defaults_list_map);
- dir->defaults_list_map = NULL;
- }
-
- if (dir->mimeapps_list_added_map != NULL)
- {
- g_hash_table_destroy (dir->mimeapps_list_added_map);
- dir->mimeapps_list_added_map = NULL;
- }
-
- if (dir->mimeapps_list_removed_map != NULL)
- {
- g_hash_table_destroy (dir->mimeapps_list_removed_map);
- dir->mimeapps_list_removed_map = NULL;
- }
-
- if (dir->mimeapps_list_defaults_map != NULL)
- {
- g_hash_table_destroy (dir->mimeapps_list_defaults_map);
- dir->mimeapps_list_defaults_map = NULL;
- }
-
- g_free (dir);
-}
-
-static void
-mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
- const char *mime_type,
- char **new_desktop_file_ids)
-{
- GList *desktop_file_ids;
- int i;
-
- desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
- mime_type);
-
- for (i = 0; new_desktop_file_ids[i] != NULL; i++)
- {
- if (!g_list_find_custom (desktop_file_ids, new_desktop_file_ids[i], (GCompareFunc) strcmp))
- desktop_file_ids = g_list_append (desktop_file_ids,
- g_strdup (new_desktop_file_ids[i]));
- }
-
- g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
-}
-
-static void
-mime_info_cache_init_dir_lists (void)
-{
- int i;
-
- mime_info_cache = mime_info_cache_new ();
-
- desktop_file_dirs_refresh ();
-
- for (i = 0; i < n_desktop_file_dirs; i++)
- {
- MimeInfoCacheDir *dir;
-
- dir = mime_info_cache_dir_new (desktop_file_dirs[i].path);
-
- if (dir != NULL)
- {
- mime_info_cache_dir_init (dir);
- mime_info_cache_dir_init_defaults_list (dir);
- mime_info_cache_dir_init_mimeapps_list (dir);
-
- mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
- }
- }
-}
-
-static void
-mime_info_cache_update_dir_lists (void)
-{
- GList *tmp;
-
- tmp = mime_info_cache->dirs;
-
- while (tmp != NULL)
- {
- MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
-
- /* No need to do this if we had file monitors... */
- mime_info_cache_dir_init (dir);
- mime_info_cache_dir_init_defaults_list (dir);
- mime_info_cache_dir_init_mimeapps_list (dir);
-
- tmp = tmp->next;
- }
-}
-
-static void
-mime_info_cache_init (void)
-{
- G_LOCK (mime_info_cache);
- if (mime_info_cache == NULL)
- mime_info_cache_init_dir_lists ();
- else
- {
- time_t now;
-
- time (&now);
- if (now >= mime_info_cache->last_stat_time + 10)
- {
- mime_info_cache_update_dir_lists ();
- mime_info_cache->last_stat_time = now;
- }
- }
-
- G_UNLOCK (mime_info_cache);
-}
-
-static MimeInfoCache *
-mime_info_cache_new (void)
-{
- MimeInfoCache *cache;
-
- cache = g_new0 (MimeInfoCache, 1);
-
- return cache;
-}
-
-static void
-mime_info_cache_free (MimeInfoCache *cache)
-{
- if (cache == NULL)
- return;
-
- g_list_free_full (cache->dirs, (GDestroyNotify) mime_info_cache_dir_free);
- g_free (cache);
-}
-
-/**
- * mime_info_cache_reload:
- * @dir: directory path which needs reloading.
- *
- * Reload the mime information for the @dir.
- */
-static void
-mime_info_cache_reload (const char *dir)
-{
- /* FIXME: just reload the dir that needs reloading,
- * don't blow the whole cache
- */
- if (mime_info_cache != NULL)
- {
- G_LOCK (mime_info_cache);
- mime_info_cache_free (mime_info_cache);
- mime_info_cache = NULL;
- G_UNLOCK (mime_info_cache);
- }
-}
-
-static GList *
-append_desktop_entry (GList *list,
- const char *desktop_entry,
- GList *removed_entries)
-{
- /* Add if not already in list, and valid */
- if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
- !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
- list = g_list_prepend (list, g_strdup (desktop_entry));
-
- return list;
-}
-
-/**
- * get_all_desktop_entries_for_mime_type:
- * @mime_type: a mime type.
- * @except: NULL or a strv list
- *
- * Returns all the desktop ids for @mime_type. The desktop files
- * are listed in an order so that default applications are listed before
- * non-default ones, and handlers for inherited mimetypes are listed
- * after the base ones.
- *
- * Optionally doesn't list the desktop ids given in the @except
- *
- * Returns: a #GList containing the desktop ids which claim
- * to handle @mime_type.
- */
-static GList *
-get_all_desktop_entries_for_mime_type (const char *base_mime_type,
- const char **except,
- gboolean include_fallback,
- char **explicit_default)
-{
- GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
- MimeInfoCacheDir *dir;
- char *mime_type, *default_entry = NULL;
- char *old_default_entry = NULL;
- const char *entry;
- char **mime_types;
- char **default_entries;
- char **removed_associations;
- gboolean already_found_handler;
- int i, j, k;
- GPtrArray *array;
- char **anc;
-
- mime_info_cache_init ();
-
- if (include_fallback)
- {
- /* collect all ancestors */
- mime_types = _g_unix_content_type_get_parents (base_mime_type);
- array = g_ptr_array_new ();
- for (i = 0; mime_types[i]; i++)
- g_ptr_array_add (array, mime_types[i]);
- g_free (mime_types);
- for (i = 0; i < array->len; i++)
- {
- anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
- for (j = 0; anc[j]; j++)
- {
- for (k = 0; k < array->len; k++)
- {
- if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
- break;
- }
- if (k == array->len) /* not found */
- g_ptr_array_add (array, g_strdup (anc[j]));
- }
- g_strfreev (anc);
- }
- g_ptr_array_add (array, NULL);
- mime_types = (char **)g_ptr_array_free (array, FALSE);
- }
- else
- {
- mime_types = g_malloc0 (2 * sizeof (gchar *));
- mime_types[0] = g_strdup (base_mime_type);
- mime_types[1] = NULL;
- }
-
- G_LOCK (mime_info_cache);
-
- removed_entries = NULL;
- desktop_entries = NULL;
-
- for (i = 0; except != NULL && except[i] != NULL; i++)
- removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
-
- for (i = 0; mime_types[i] != NULL; i++)
- {
- mime_type = mime_types[i];
-
- /* This is true if we already found a handler for a more specific
- mimetype. If its set we ignore any defaults for the less specific
- mimetypes. */
- already_found_handler = (desktop_entries != NULL);
-
- /* Go through all apps listed in user and system dirs */
- for (dir_list = mime_info_cache->dirs;
- dir_list != NULL;
- dir_list = dir_list->next)
- {
- dir = dir_list->data;
-
- /* Pick the explicit default application if we got no result earlier
- * (ie, for more specific mime types)
- */
- if (!already_found_handler)
- {
- entry = g_hash_table_lookup (dir->mimeapps_list_defaults_map, mime_type);
-
- if (entry != NULL)
- {
- /* Save the default entry if it's the first one we encounter */
- if (default_entry == NULL)
- default_entry = g_strdup (entry);
- }
- }
-
- /* Then added associations from mimeapps.list */
- default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
- for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
- desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
-
- /* Then removed associations from mimeapps.list */
- removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
- for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
- removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
-
- /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
- default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
- for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
- {
- if (default_entry == NULL && old_default_entry == NULL && !already_found_handler)
- old_default_entry = g_strdup (default_entries[j]);
-
- desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
- }
- }
-
- /* Go through all entries that support the mimetype */
- for (dir_list = mime_info_cache->dirs;
- dir_list != NULL;
- dir_list = dir_list->next)
- {
- dir = dir_list->data;
-
- list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
- for (tmp = list; tmp != NULL; tmp = tmp->next)
- desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
- }
- }
-
- G_UNLOCK (mime_info_cache);
-
- g_strfreev (mime_types);
-
- /* If we have no default from mimeapps.list, take it from
- * defaults.list intead.
- *
- * If we do have a default from mimeapps.list, free any one that came
- * from defaults.list.
- */
- if (default_entry == NULL)
- default_entry = old_default_entry;
- else
- g_free (old_default_entry);
-
- if (explicit_default != NULL)
- *explicit_default = default_entry;
- else
- g_free (default_entry);
-
- g_list_free_full (removed_entries, g_free);
-
- desktop_entries = g_list_reverse (desktop_entries);
-
- return desktop_entries;
-}
-