1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3/* GIO - GLib Input, Output and Streaming Library
4 *
5 * Copyright (C) 2006-2007 Red Hat, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23#include "config.h"
24#include <sys/types.h>
25#include <stdlib.h>
26#include <string.h>
27#include <stdio.h>
28#include "gcontenttypeprivate.h"
29#include "gthemedicon.h"
30#include "gicon.h"
31#include "gfile.h"
32#include "gfileenumerator.h"
33#include "gfileinfo.h"
34#include "glibintl.h"
35
36
37/**
38 * SECTION:gcontenttype
39 * @short_description: Platform-specific content typing
40 * @include: gio/gio.h
41 *
42 * A content type is a platform specific string that defines the type
43 * of a file. On UNIX it is a
44 * [MIME type](http://www.wikipedia.org/wiki/Internet_media_type)
45 * like `text/plain` or `image/png`.
46 * On Win32 it is an extension string like `.doc`, `.txt` or a perceived
47 * string like `audio`. Such strings can be looked up in the registry at
48 * `HKEY_CLASSES_ROOT`.
49 * On macOS it is a [Uniform Type Identifier](https://en.wikipedia.org/wiki/Uniform_Type_Identifier)
50 * such as `com.apple.application`.
51 **/
52
53#include <dirent.h>
54
55#define XDG_PREFIX _gio_xdg
56#include "xdgmime/xdgmime.h"
57
58static void tree_magic_schedule_reload (void);
59
60/* We lock this mutex whenever we modify global state in this module. */
61G_LOCK_DEFINE_STATIC (gio_xdgmime);
62
63gsize
64_g_unix_content_type_get_sniff_len (void)
65{
66 gsize size;
67
68 G_LOCK (gio_xdgmime);
69 size = xdg_mime_get_max_buffer_extents ();
70 G_UNLOCK (gio_xdgmime);
71
72 return size;
73}
74
75gchar *
76_g_unix_content_type_unalias (const gchar *type)
77{
78 gchar *res;
79
80 G_LOCK (gio_xdgmime);
81 res = g_strdup (xdg_mime_unalias_mime_type (mime: type));
82 G_UNLOCK (gio_xdgmime);
83
84 return res;
85}
86
87gchar **
88_g_unix_content_type_get_parents (const gchar *type)
89{
90 const gchar *umime;
91 gchar **parents;
92 GPtrArray *array;
93 int i;
94
95 array = g_ptr_array_new ();
96
97 G_LOCK (gio_xdgmime);
98
99 umime = xdg_mime_unalias_mime_type (mime: type);
100
101 g_ptr_array_add (array, data: g_strdup (str: umime));
102
103 parents = xdg_mime_list_mime_parents (mime: umime);
104 for (i = 0; parents && parents[i] != NULL; i++)
105 g_ptr_array_add (array, data: g_strdup (str: parents[i]));
106
107 free (ptr: parents);
108
109 G_UNLOCK (gio_xdgmime);
110
111 g_ptr_array_add (array, NULL);
112
113 return (gchar **)g_ptr_array_free (array, FALSE);
114}
115
116G_LOCK_DEFINE_STATIC (global_mime_dirs);
117static gchar **global_mime_dirs = NULL;
118
119static void
120_g_content_type_set_mime_dirs_locked (const char * const *dirs)
121{
122 g_clear_pointer (&global_mime_dirs, g_strfreev);
123
124 if (dirs != NULL)
125 {
126 global_mime_dirs = g_strdupv (str_array: (gchar **) dirs);
127 }
128 else
129 {
130 GPtrArray *mime_dirs = g_ptr_array_new_with_free_func (element_free_func: g_free);
131 const gchar * const *system_dirs = g_get_system_data_dirs ();
132
133 g_ptr_array_add (array: mime_dirs, data: g_build_filename (first_element: g_get_user_data_dir (), "mime", NULL));
134 for (; *system_dirs != NULL; system_dirs++)
135 g_ptr_array_add (array: mime_dirs, data: g_build_filename (first_element: *system_dirs, "mime", NULL));
136 g_ptr_array_add (array: mime_dirs, NULL); /* NULL terminator */
137
138 global_mime_dirs = (gchar **) g_ptr_array_free (array: mime_dirs, FALSE);
139 }
140
141 xdg_mime_set_dirs (dirs: (const gchar * const *) global_mime_dirs);
142 tree_magic_schedule_reload ();
143}
144
145/**
146 * g_content_type_set_mime_dirs:
147 * @dirs: (array zero-terminated=1) (nullable): %NULL-terminated list of
148 * directories to load MIME data from, including any `mime/` subdirectory,
149 * and with the first directory to try listed first
150 *
151 * Set the list of directories used by GIO to load the MIME database.
152 * If @dirs is %NULL, the directories used are the default:
153 *
154 * - the `mime` subdirectory of the directory in `$XDG_DATA_HOME`
155 * - the `mime` subdirectory of every directory in `$XDG_DATA_DIRS`
156 *
157 * This function is intended to be used when writing tests that depend on
158 * information stored in the MIME database, in order to control the data.
159 *
160 * Typically, in case your tests use %G_TEST_OPTION_ISOLATE_DIRS, but they
161 * depend on the system’s MIME database, you should call this function
162 * with @dirs set to %NULL before calling g_test_init(), for instance:
163 *
164 * |[<!-- language="C" -->
165 * // Load MIME data from the system
166 * g_content_type_set_mime_dirs (NULL);
167 * // Isolate the environment
168 * g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
169 *
170 * …
171 *
172 * return g_test_run ();
173 * ]|
174 *
175 * Since: 2.60
176 */
177/*< private >*/
178void
179g_content_type_set_mime_dirs (const gchar * const *dirs)
180{
181 G_LOCK (global_mime_dirs);
182 _g_content_type_set_mime_dirs_locked (dirs);
183 G_UNLOCK (global_mime_dirs);
184}
185
186/**
187 * g_content_type_get_mime_dirs:
188 *
189 * Get the list of directories which MIME data is loaded from. See
190 * g_content_type_set_mime_dirs() for details.
191 *
192 * Returns: (transfer none) (array zero-terminated=1): %NULL-terminated list of
193 * directories to load MIME data from, including any `mime/` subdirectory,
194 * and with the first directory to try listed first
195 * Since: 2.60
196 */
197/*< private >*/
198const gchar * const *
199g_content_type_get_mime_dirs (void)
200{
201 const gchar * const *mime_dirs;
202
203 G_LOCK (global_mime_dirs);
204
205 if (global_mime_dirs == NULL)
206 _g_content_type_set_mime_dirs_locked (NULL);
207
208 mime_dirs = (const gchar * const *) global_mime_dirs;
209
210 G_UNLOCK (global_mime_dirs);
211
212 g_assert (mime_dirs != NULL);
213 return mime_dirs;
214}
215
216/**
217 * g_content_type_equals:
218 * @type1: a content type string
219 * @type2: a content type string
220 *
221 * Compares two content types for equality.
222 *
223 * Returns: %TRUE if the two strings are identical or equivalent,
224 * %FALSE otherwise.
225 */
226gboolean
227g_content_type_equals (const gchar *type1,
228 const gchar *type2)
229{
230 gboolean res;
231
232 g_return_val_if_fail (type1 != NULL, FALSE);
233 g_return_val_if_fail (type2 != NULL, FALSE);
234
235 G_LOCK (gio_xdgmime);
236 res = xdg_mime_mime_type_equal (mime_a: type1, mime_b: type2);
237 G_UNLOCK (gio_xdgmime);
238
239 return res;
240}
241
242/**
243 * g_content_type_is_a:
244 * @type: a content type string
245 * @supertype: a content type string
246 *
247 * Determines if @type is a subset of @supertype.
248 *
249 * Returns: %TRUE if @type is a kind of @supertype,
250 * %FALSE otherwise.
251 */
252gboolean
253g_content_type_is_a (const gchar *type,
254 const gchar *supertype)
255{
256 gboolean res;
257
258 g_return_val_if_fail (type != NULL, FALSE);
259 g_return_val_if_fail (supertype != NULL, FALSE);
260
261 G_LOCK (gio_xdgmime);
262 res = xdg_mime_mime_type_subclass (mime_a: type, mime_b: supertype);
263 G_UNLOCK (gio_xdgmime);
264
265 return res;
266}
267
268/**
269 * g_content_type_is_mime_type:
270 * @type: a content type string
271 * @mime_type: a mime type string
272 *
273 * Determines if @type is a subset of @mime_type.
274 * Convenience wrapper around g_content_type_is_a().
275 *
276 * Returns: %TRUE if @type is a kind of @mime_type,
277 * %FALSE otherwise.
278 *
279 * Since: 2.52
280 */
281gboolean
282g_content_type_is_mime_type (const gchar *type,
283 const gchar *mime_type)
284{
285 return g_content_type_is_a (type, supertype: mime_type);
286}
287
288/**
289 * g_content_type_is_unknown:
290 * @type: a content type string
291 *
292 * Checks if the content type is the generic "unknown" type.
293 * On UNIX this is the "application/octet-stream" mimetype,
294 * while on win32 it is "*" and on OSX it is a dynamic type
295 * or octet-stream.
296 *
297 * Returns: %TRUE if the type is the unknown type.
298 */
299gboolean
300g_content_type_is_unknown (const gchar *type)
301{
302 g_return_val_if_fail (type != NULL, FALSE);
303
304 return strcmp (XDG_MIME_TYPE_UNKNOWN, s2: type) == 0;
305}
306
307
308typedef enum {
309 MIME_TAG_TYPE_OTHER,
310 MIME_TAG_TYPE_COMMENT
311} MimeTagType;
312
313typedef struct {
314 int current_type;
315 int current_lang_level;
316 int comment_lang_level;
317 char *comment;
318} MimeParser;
319
320
321static int
322language_level (const char *lang)
323{
324 const char * const *lang_list;
325 int i;
326
327 /* The returned list is sorted from most desirable to least
328 desirable and always contains the default locale "C". */
329 lang_list = g_get_language_names ();
330
331 for (i = 0; lang_list[i]; i++)
332 if (strcmp (s1: lang_list[i], s2: lang) == 0)
333 return 1000-i;
334
335 return 0;
336}
337
338static void
339mime_info_start_element (GMarkupParseContext *context,
340 const gchar *element_name,
341 const gchar **attribute_names,
342 const gchar **attribute_values,
343 gpointer user_data,
344 GError **error)
345{
346 int i;
347 const char *lang;
348 MimeParser *parser = user_data;
349
350 if (strcmp (s1: element_name, s2: "comment") == 0)
351 {
352 lang = "C";
353 for (i = 0; attribute_names[i]; i++)
354 if (strcmp (s1: attribute_names[i], s2: "xml:lang") == 0)
355 {
356 lang = attribute_values[i];
357 break;
358 }
359
360 parser->current_lang_level = language_level (lang);
361 parser->current_type = MIME_TAG_TYPE_COMMENT;
362 }
363 else
364 parser->current_type = MIME_TAG_TYPE_OTHER;
365}
366
367static void
368mime_info_end_element (GMarkupParseContext *context,
369 const gchar *element_name,
370 gpointer user_data,
371 GError **error)
372{
373 MimeParser *parser = user_data;
374
375 parser->current_type = MIME_TAG_TYPE_OTHER;
376}
377
378static void
379mime_info_text (GMarkupParseContext *context,
380 const gchar *text,
381 gsize text_len,
382 gpointer user_data,
383 GError **error)
384{
385 MimeParser *parser = user_data;
386
387 if (parser->current_type == MIME_TAG_TYPE_COMMENT &&
388 parser->current_lang_level > parser->comment_lang_level)
389 {
390 g_free (mem: parser->comment);
391 parser->comment = g_strndup (str: text, n: text_len);
392 parser->comment_lang_level = parser->current_lang_level;
393 }
394}
395
396static char *
397load_comment_for_mime_helper (const char *dir,
398 const char *basename)
399{
400 GMarkupParseContext *context;
401 char *filename, *data;
402 gsize len;
403 gboolean res;
404 MimeParser parse_data = {0};
405 GMarkupParser parser = {
406 mime_info_start_element,
407 mime_info_end_element,
408 mime_info_text,
409 NULL,
410 NULL
411 };
412
413 filename = g_build_filename (first_element: dir, basename, NULL);
414
415 res = g_file_get_contents (filename, contents: &data, length: &len, NULL);
416 g_free (mem: filename);
417 if (!res)
418 return NULL;
419
420 context = g_markup_parse_context_new (parser: &parser, flags: 0, user_data: &parse_data, NULL);
421 res = g_markup_parse_context_parse (context, text: data, text_len: len, NULL);
422 g_free (mem: data);
423 g_markup_parse_context_free (context);
424
425 if (!res)
426 return NULL;
427
428 return parse_data.comment;
429}
430
431
432static char *
433load_comment_for_mime (const char *mimetype)
434{
435 const char * const *dirs;
436 char *basename;
437 char *comment;
438 gsize i;
439
440 basename = g_strdup_printf (format: "%s.xml", mimetype);
441
442 dirs = g_content_type_get_mime_dirs ();
443 for (i = 0; dirs[i] != NULL; i++)
444 {
445 comment = load_comment_for_mime_helper (dir: dirs[i], basename);
446 if (comment)
447 {
448 g_free (mem: basename);
449 return comment;
450 }
451 }
452 g_free (mem: basename);
453
454 return g_strdup_printf (_("%s type"), mimetype);
455}
456
457/**
458 * g_content_type_get_description:
459 * @type: a content type string
460 *
461 * Gets the human readable description of the content type.
462 *
463 * Returns: a short description of the content type @type. Free the
464 * returned string with g_free()
465 */
466gchar *
467g_content_type_get_description (const gchar *type)
468{
469 static GHashTable *type_comment_cache = NULL;
470 gchar *comment;
471
472 g_return_val_if_fail (type != NULL, NULL);
473
474 G_LOCK (gio_xdgmime);
475 type = xdg_mime_unalias_mime_type (mime: type);
476
477 if (type_comment_cache == NULL)
478 type_comment_cache = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, value_destroy_func: g_free);
479
480 comment = g_hash_table_lookup (hash_table: type_comment_cache, key: type);
481 comment = g_strdup (str: comment);
482 G_UNLOCK (gio_xdgmime);
483
484 if (comment != NULL)
485 return comment;
486
487 comment = load_comment_for_mime (mimetype: type);
488
489 G_LOCK (gio_xdgmime);
490 g_hash_table_insert (hash_table: type_comment_cache,
491 key: g_strdup (str: type),
492 value: g_strdup (str: comment));
493 G_UNLOCK (gio_xdgmime);
494
495 return comment;
496}
497
498/**
499 * g_content_type_get_mime_type:
500 * @type: a content type string
501 *
502 * Gets the mime type for the content type, if one is registered.
503 *
504 * Returns: (nullable) (transfer full): the registered mime type for the
505 * given @type, or %NULL if unknown; free with g_free().
506 */
507char *
508g_content_type_get_mime_type (const char *type)
509{
510 g_return_val_if_fail (type != NULL, NULL);
511
512 return g_strdup (str: type);
513}
514
515static GIcon *
516g_content_type_get_icon_internal (const gchar *type,
517 gboolean symbolic)
518{
519 char *mimetype_icon;
520 char *generic_mimetype_icon = NULL;
521 char *q;
522 char *icon_names[6];
523 int n = 0;
524 GIcon *themed_icon;
525 const char *xdg_icon;
526 int i;
527
528 g_return_val_if_fail (type != NULL, NULL);
529
530 G_LOCK (gio_xdgmime);
531 xdg_icon = xdg_mime_get_icon (mime: type);
532 G_UNLOCK (gio_xdgmime);
533
534 if (xdg_icon)
535 icon_names[n++] = g_strdup (str: xdg_icon);
536
537 mimetype_icon = g_strdup (str: type);
538 while ((q = strchr (s: mimetype_icon, c: '/')) != NULL)
539 *q = '-';
540
541 icon_names[n++] = mimetype_icon;
542
543 generic_mimetype_icon = g_content_type_get_generic_icon_name (type);
544 if (generic_mimetype_icon)
545 icon_names[n++] = generic_mimetype_icon;
546
547 if (symbolic)
548 {
549 for (i = 0; i < n; i++)
550 {
551 icon_names[n + i] = icon_names[i];
552 icon_names[i] = g_strconcat (string1: icon_names[i], "-symbolic", NULL);
553 }
554
555 n += n;
556 }
557
558 themed_icon = g_themed_icon_new_from_names (iconnames: icon_names, len: n);
559
560 for (i = 0; i < n; i++)
561 g_free (mem: icon_names[i]);
562
563 return themed_icon;
564}
565
566/**
567 * g_content_type_get_icon:
568 * @type: a content type string
569 *
570 * Gets the icon for a content type.
571 *
572 * Returns: (transfer full): #GIcon corresponding to the content type. Free the returned
573 * object with g_object_unref()
574 */
575GIcon *
576g_content_type_get_icon (const gchar *type)
577{
578 return g_content_type_get_icon_internal (type, FALSE);
579}
580
581/**
582 * g_content_type_get_symbolic_icon:
583 * @type: a content type string
584 *
585 * Gets the symbolic icon for a content type.
586 *
587 * Returns: (transfer full): symbolic #GIcon corresponding to the content type.
588 * Free the returned object with g_object_unref()
589 *
590 * Since: 2.34
591 */
592GIcon *
593g_content_type_get_symbolic_icon (const gchar *type)
594{
595 return g_content_type_get_icon_internal (type, TRUE);
596}
597
598/**
599 * g_content_type_get_generic_icon_name:
600 * @type: a content type string
601 *
602 * Gets the generic icon name for a content type.
603 *
604 * See the
605 * [shared-mime-info](http://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec)
606 * specification for more on the generic icon name.
607 *
608 * Returns: (nullable): the registered generic icon name for the given @type,
609 * or %NULL if unknown. Free with g_free()
610 *
611 * Since: 2.34
612 */
613gchar *
614g_content_type_get_generic_icon_name (const gchar *type)
615{
616 const gchar *xdg_icon_name;
617 gchar *icon_name;
618
619 g_return_val_if_fail (type != NULL, NULL);
620
621 G_LOCK (gio_xdgmime);
622 xdg_icon_name = xdg_mime_get_generic_icon (mime: type);
623 G_UNLOCK (gio_xdgmime);
624
625 if (!xdg_icon_name)
626 {
627 const char *p;
628 const char *suffix = "-x-generic";
629
630 p = strchr (s: type, c: '/');
631 if (p == NULL)
632 p = type + strlen (s: type);
633
634 icon_name = g_malloc (n_bytes: p - type + strlen (s: suffix) + 1);
635 memcpy (dest: icon_name, src: type, n: p - type);
636 memcpy (dest: icon_name + (p - type), src: suffix, n: strlen (s: suffix));
637 icon_name[(p - type) + strlen (s: suffix)] = 0;
638 }
639 else
640 {
641 icon_name = g_strdup (str: xdg_icon_name);
642 }
643
644 return icon_name;
645}
646
647/**
648 * g_content_type_can_be_executable:
649 * @type: a content type string
650 *
651 * Checks if a content type can be executable. Note that for instance
652 * things like text files can be executables (i.e. scripts and batch files).
653 *
654 * Returns: %TRUE if the file type corresponds to a type that
655 * can be executable, %FALSE otherwise.
656 */
657gboolean
658g_content_type_can_be_executable (const gchar *type)
659{
660 g_return_val_if_fail (type != NULL, FALSE);
661
662 if (g_content_type_is_a (type, supertype: "application/x-executable") ||
663 g_content_type_is_a (type, supertype: "text/plain"))
664 return TRUE;
665
666 return FALSE;
667}
668
669static gboolean
670looks_like_text (const guchar *data, gsize data_size)
671{
672 gsize i;
673 char c;
674
675 for (i = 0; i < data_size; i++)
676 {
677 c = data[i];
678
679 if (g_ascii_iscntrl (c) &&
680 !g_ascii_isspace (c) &&
681 c != '\b')
682 return FALSE;
683 }
684 return TRUE;
685}
686
687/**
688 * g_content_type_from_mime_type:
689 * @mime_type: a mime type string
690 *
691 * Tries to find a content type based on the mime type name.
692 *
693 * Returns: (nullable): Newly allocated string with content type or
694 * %NULL. Free with g_free()
695 *
696 * Since: 2.18
697 **/
698gchar *
699g_content_type_from_mime_type (const gchar *mime_type)
700{
701 char *umime;
702
703 g_return_val_if_fail (mime_type != NULL, NULL);
704
705 G_LOCK (gio_xdgmime);
706 /* mime type and content type are same on unixes */
707 umime = g_strdup (xdg_mime_unalias_mime_type (mime: mime_type));
708 G_UNLOCK (gio_xdgmime);
709
710 return umime;
711}
712
713/**
714 * g_content_type_guess:
715 * @filename: (nullable): a string, or %NULL
716 * @data: (nullable) (array length=data_size): a stream of data, or %NULL
717 * @data_size: the size of @data
718 * @result_uncertain: (out) (optional): return location for the certainty
719 * of the result, or %NULL
720 *
721 * Guesses the content type based on example data. If the function is
722 * uncertain, @result_uncertain will be set to %TRUE. Either @filename
723 * or @data may be %NULL, in which case the guess will be based solely
724 * on the other argument.
725 *
726 * Returns: a string indicating a guessed content type for the
727 * given data. Free with g_free()
728 */
729gchar *
730g_content_type_guess (const gchar *filename,
731 const guchar *data,
732 gsize data_size,
733 gboolean *result_uncertain)
734{
735 char *basename;
736 const char *name_mimetypes[10], *sniffed_mimetype;
737 char *mimetype;
738 int i;
739 int n_name_mimetypes;
740 int sniffed_prio;
741
742 sniffed_prio = 0;
743 n_name_mimetypes = 0;
744 sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN;
745
746 if (result_uncertain)
747 *result_uncertain = FALSE;
748
749 /* our test suite and potentially other code used -1 in the past, which is
750 * not documented and not allowed; guard against that */
751 g_return_val_if_fail (data_size != (gsize) -1, g_strdup (XDG_MIME_TYPE_UNKNOWN));
752
753 G_LOCK (gio_xdgmime);
754
755 if (filename)
756 {
757 i = strlen (s: filename);
758 if (filename[i - 1] == '/')
759 {
760 name_mimetypes[0] = "inode/directory";
761 name_mimetypes[1] = NULL;
762 n_name_mimetypes = 1;
763 if (result_uncertain)
764 *result_uncertain = TRUE;
765 }
766 else
767 {
768 basename = g_path_get_basename (file_name: filename);
769 n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (file_name: basename, mime_types: name_mimetypes, n_mime_types: 10);
770 g_free (mem: basename);
771 }
772 }
773
774 /* Got an extension match, and no conflicts. This is it. */
775 if (n_name_mimetypes == 1)
776 {
777 gchar *s = g_strdup (str: name_mimetypes[0]);
778 G_UNLOCK (gio_xdgmime);
779 return s;
780 }
781
782 if (data)
783 {
784 sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, len: data_size, result_prio: &sniffed_prio);
785 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
786 data &&
787 looks_like_text (data, data_size))
788 sniffed_mimetype = "text/plain";
789
790 /* For security reasons we don't ever want to sniff desktop files
791 * where we know the filename and it doesn't have a .desktop extension.
792 * This is because desktop files allow executing any application and
793 * we don't want to make it possible to hide them looking like something
794 * else.
795 */
796 if (filename != NULL &&
797 strcmp (s1: sniffed_mimetype, s2: "application/x-desktop") == 0)
798 sniffed_mimetype = "text/plain";
799 }
800
801 if (n_name_mimetypes == 0)
802 {
803 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
804 result_uncertain)
805 *result_uncertain = TRUE;
806
807 mimetype = g_strdup (str: sniffed_mimetype);
808 }
809 else
810 {
811 mimetype = NULL;
812 if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN)
813 {
814 if (sniffed_prio >= 80) /* High priority sniffing match, use that */
815 mimetype = g_strdup (str: sniffed_mimetype);
816 else
817 {
818 /* There are conflicts between the name matches and we
819 * have a sniffed type, use that as a tie breaker.
820 */
821 for (i = 0; i < n_name_mimetypes; i++)
822 {
823 if ( xdg_mime_mime_type_subclass (mime_a: name_mimetypes[i], mime_b: sniffed_mimetype))
824 {
825 /* This nametype match is derived from (or the same as)
826 * the sniffed type). This is probably it.
827 */
828 mimetype = g_strdup (str: name_mimetypes[i]);
829 break;
830 }
831 }
832 }
833 }
834
835 if (mimetype == NULL)
836 {
837 /* Conflicts, and sniffed type was no help or not there.
838 * Guess on the first one
839 */
840 mimetype = g_strdup (str: name_mimetypes[0]);
841 if (result_uncertain)
842 *result_uncertain = TRUE;
843 }
844 }
845
846 G_UNLOCK (gio_xdgmime);
847
848 return mimetype;
849}
850
851static void
852enumerate_mimetypes_subdir (const char *dir,
853 const char *prefix,
854 GHashTable *mimetypes)
855{
856 DIR *d;
857 struct dirent *ent;
858 char *mimetype;
859
860 d = opendir (name: dir);
861 if (d)
862 {
863 while ((ent = readdir (dirp: d)) != NULL)
864 {
865 if (g_str_has_suffix (str: ent->d_name, suffix: ".xml"))
866 {
867 mimetype = g_strdup_printf (format: "%s/%.*s", prefix, (int) strlen (s: ent->d_name) - 4, ent->d_name);
868 g_hash_table_replace (hash_table: mimetypes, key: mimetype, NULL);
869 }
870 }
871 closedir (dirp: d);
872 }
873}
874
875static void
876enumerate_mimetypes_dir (const char *dir,
877 GHashTable *mimetypes)
878{
879 DIR *d;
880 struct dirent *ent;
881 const char *mimedir;
882 char *name;
883
884 mimedir = dir;
885
886 d = opendir (name: mimedir);
887 if (d)
888 {
889 while ((ent = readdir (dirp: d)) != NULL)
890 {
891 if (strcmp (s1: ent->d_name, s2: "packages") != 0)
892 {
893 name = g_build_filename (first_element: mimedir, ent->d_name, NULL);
894 if (g_file_test (filename: name, test: G_FILE_TEST_IS_DIR))
895 enumerate_mimetypes_subdir (dir: name, prefix: ent->d_name, mimetypes);
896 g_free (mem: name);
897 }
898 }
899 closedir (dirp: d);
900 }
901}
902
903/**
904 * g_content_types_get_registered:
905 *
906 * Gets a list of strings containing all the registered content types
907 * known to the system. The list and its data should be freed using
908 * `g_list_free_full (list, g_free)`.
909 *
910 * Returns: (element-type utf8) (transfer full): list of the registered
911 * content types
912 */
913GList *
914g_content_types_get_registered (void)
915{
916 const char * const *dirs;
917 GHashTable *mimetypes;
918 GHashTableIter iter;
919 gpointer key;
920 gsize i;
921 GList *l;
922
923 mimetypes = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, NULL);
924
925 dirs = g_content_type_get_mime_dirs ();
926 for (i = 0; dirs[i] != NULL; i++)
927 enumerate_mimetypes_dir (dir: dirs[i], mimetypes);
928
929 l = NULL;
930 g_hash_table_iter_init (iter: &iter, hash_table: mimetypes);
931 while (g_hash_table_iter_next (iter: &iter, key: &key, NULL))
932 {
933 l = g_list_prepend (list: l, data: key);
934 g_hash_table_iter_steal (iter: &iter);
935 }
936
937 g_hash_table_destroy (hash_table: mimetypes);
938
939 return l;
940}
941
942
943/* tree magic data */
944static GList *tree_matches = NULL;
945static gboolean need_reload = FALSE;
946
947G_LOCK_DEFINE_STATIC (gio_treemagic);
948
949typedef struct
950{
951 gchar *path;
952 GFileType type;
953 guint match_case : 1;
954 guint executable : 1;
955 guint non_empty : 1;
956 guint on_disc : 1;
957 gchar *mimetype;
958 GList *matches;
959} TreeMatchlet;
960
961typedef struct
962{
963 gchar *contenttype;
964 gint priority;
965 GList *matches;
966} TreeMatch;
967
968
969static void
970tree_matchlet_free (TreeMatchlet *matchlet)
971{
972 g_list_free_full (list: matchlet->matches, free_func: (GDestroyNotify) tree_matchlet_free);
973 g_free (mem: matchlet->path);
974 g_free (mem: matchlet->mimetype);
975 g_slice_free (TreeMatchlet, matchlet);
976}
977
978static void
979tree_match_free (TreeMatch *match)
980{
981 g_list_free_full (list: match->matches, free_func: (GDestroyNotify) tree_matchlet_free);
982 g_free (mem: match->contenttype);
983 g_slice_free (TreeMatch, match);
984}
985
986static TreeMatch *
987parse_header (gchar *line)
988{
989 gint len;
990 gchar *s;
991 TreeMatch *match;
992
993 len = strlen (s: line);
994
995 if (line[0] != '[' || line[len - 1] != ']')
996 return NULL;
997
998 line[len - 1] = 0;
999 s = strchr (s: line, c: ':');
1000
1001 match = g_slice_new0 (TreeMatch);
1002 match->priority = atoi (nptr: line + 1);
1003 match->contenttype = g_strdup (str: s + 1);
1004
1005 return match;
1006}
1007
1008static TreeMatchlet *
1009parse_match_line (gchar *line,
1010 gint *depth)
1011{
1012 gchar *s, *p;
1013 TreeMatchlet *matchlet;
1014 gchar **parts;
1015 gint i;
1016
1017 matchlet = g_slice_new0 (TreeMatchlet);
1018
1019 if (line[0] == '>')
1020 {
1021 *depth = 0;
1022 s = line;
1023 }
1024 else
1025 {
1026 *depth = atoi (nptr: line);
1027 s = strchr (s: line, c: '>');
1028 }
1029 s += 2;
1030 p = strchr (s: s, c: '"');
1031 *p = 0;
1032
1033 matchlet->path = g_strdup (str: s);
1034 s = p + 1;
1035 parts = g_strsplit (string: s, delimiter: ",", max_tokens: 0);
1036 if (strcmp (s1: parts[0], s2: "=file") == 0)
1037 matchlet->type = G_FILE_TYPE_REGULAR;
1038 else if (strcmp (s1: parts[0], s2: "=directory") == 0)
1039 matchlet->type = G_FILE_TYPE_DIRECTORY;
1040 else if (strcmp (s1: parts[0], s2: "=link") == 0)
1041 matchlet->type = G_FILE_TYPE_SYMBOLIC_LINK;
1042 else
1043 matchlet->type = G_FILE_TYPE_UNKNOWN;
1044 for (i = 1; parts[i]; i++)
1045 {
1046 if (strcmp (s1: parts[i], s2: "executable") == 0)
1047 matchlet->executable = 1;
1048 else if (strcmp (s1: parts[i], s2: "match-case") == 0)
1049 matchlet->match_case = 1;
1050 else if (strcmp (s1: parts[i], s2: "non-empty") == 0)
1051 matchlet->non_empty = 1;
1052 else if (strcmp (s1: parts[i], s2: "on-disc") == 0)
1053 matchlet->on_disc = 1;
1054 else
1055 matchlet->mimetype = g_strdup (str: parts[i]);
1056 }
1057
1058 g_strfreev (str_array: parts);
1059
1060 return matchlet;
1061}
1062
1063static gint
1064cmp_match (gconstpointer a, gconstpointer b)
1065{
1066 const TreeMatch *aa = (const TreeMatch *)a;
1067 const TreeMatch *bb = (const TreeMatch *)b;
1068
1069 return bb->priority - aa->priority;
1070}
1071
1072static void
1073insert_match (TreeMatch *match)
1074{
1075 tree_matches = g_list_insert_sorted (list: tree_matches, data: match, func: cmp_match);
1076}
1077
1078static void
1079insert_matchlet (TreeMatch *match,
1080 TreeMatchlet *matchlet,
1081 gint depth)
1082{
1083 if (depth == 0)
1084 match->matches = g_list_append (list: match->matches, data: matchlet);
1085 else
1086 {
1087 GList *last;
1088 TreeMatchlet *m;
1089
1090 last = g_list_last (list: match->matches);
1091 if (!last)
1092 {
1093 tree_matchlet_free (matchlet);
1094 g_warning ("can't insert tree matchlet at depth %d", depth);
1095 return;
1096 }
1097
1098 m = (TreeMatchlet *) last->data;
1099 while (--depth > 0)
1100 {
1101 last = g_list_last (list: m->matches);
1102 if (!last)
1103 {
1104 tree_matchlet_free (matchlet);
1105 g_warning ("can't insert tree matchlet at depth %d", depth);
1106 return;
1107 }
1108
1109 m = (TreeMatchlet *) last->data;
1110 }
1111 m->matches = g_list_append (list: m->matches, data: matchlet);
1112 }
1113}
1114
1115static void
1116read_tree_magic_from_directory (const gchar *prefix)
1117{
1118 gchar *filename;
1119 gchar *text;
1120 gsize len;
1121 gchar **lines;
1122 gint i;
1123 TreeMatch *match;
1124 TreeMatchlet *matchlet;
1125 gint depth;
1126
1127 filename = g_build_filename (first_element: prefix, "treemagic", NULL);
1128
1129 if (g_file_get_contents (filename, contents: &text, length: &len, NULL))
1130 {
1131 if (strcmp (s1: text, s2: "MIME-TreeMagic") == 0)
1132 {
1133 lines = g_strsplit (string: text + strlen (s: "MIME-TreeMagic") + 2, delimiter: "\n", max_tokens: 0);
1134 match = NULL;
1135 for (i = 0; lines[i] && lines[i][0]; i++)
1136 {
1137 if (lines[i][0] == '[')
1138 {
1139 match = parse_header (line: lines[i]);
1140 insert_match (match);
1141 }
1142 else if (match != NULL)
1143 {
1144 matchlet = parse_match_line (line: lines[i], depth: &depth);
1145 insert_matchlet (match, matchlet, depth);
1146 }
1147 else
1148 {
1149 g_warning ("%s: header corrupt; skipping", filename);
1150 break;
1151 }
1152 }
1153
1154 g_strfreev (str_array: lines);
1155 }
1156 else
1157 g_warning ("%s: header not found, skipping", filename);
1158
1159 g_free (mem: text);
1160 }
1161
1162 g_free (mem: filename);
1163}
1164
1165static void
1166tree_magic_schedule_reload (void)
1167{
1168 need_reload = TRUE;
1169}
1170
1171static void
1172xdg_mime_reload (void *user_data)
1173{
1174 tree_magic_schedule_reload ();
1175}
1176
1177static void
1178tree_magic_shutdown (void)
1179{
1180 g_list_free_full (list: tree_matches, free_func: (GDestroyNotify) tree_match_free);
1181 tree_matches = NULL;
1182}
1183
1184static void
1185tree_magic_init (void)
1186{
1187 static gboolean initialized = FALSE;
1188 gsize i;
1189
1190 if (!initialized)
1191 {
1192 initialized = TRUE;
1193
1194 xdg_mime_register_reload_callback (callback: xdg_mime_reload, NULL, NULL);
1195 need_reload = TRUE;
1196 }
1197
1198 if (need_reload)
1199 {
1200 const char * const *dirs;
1201
1202 need_reload = FALSE;
1203
1204 tree_magic_shutdown ();
1205
1206 dirs = g_content_type_get_mime_dirs ();
1207 for (i = 0; dirs[i] != NULL; i++)
1208 read_tree_magic_from_directory (prefix: dirs[i]);
1209 }
1210}
1211
1212/* a filtering enumerator */
1213
1214typedef struct
1215{
1216 gchar *path;
1217 gint depth;
1218 gboolean ignore_case;
1219 gchar **components;
1220 gchar **case_components;
1221 GFileEnumerator **enumerators;
1222 GFile **children;
1223} Enumerator;
1224
1225static gboolean
1226component_match (Enumerator *e,
1227 gint depth,
1228 const gchar *name)
1229{
1230 gchar *case_folded, *key;
1231 gboolean found;
1232
1233 if (strcmp (s1: name, s2: e->components[depth]) == 0)
1234 return TRUE;
1235
1236 if (!e->ignore_case)
1237 return FALSE;
1238
1239 case_folded = g_utf8_casefold (str: name, len: -1);
1240 key = g_utf8_collate_key (str: case_folded, len: -1);
1241
1242 found = strcmp (s1: key, s2: e->case_components[depth]) == 0;
1243
1244 g_free (mem: case_folded);
1245 g_free (mem: key);
1246
1247 return found;
1248}
1249
1250static GFile *
1251next_match_recurse (Enumerator *e,
1252 gint depth)
1253{
1254 GFile *file;
1255 GFileInfo *info;
1256 const gchar *name;
1257
1258 while (TRUE)
1259 {
1260 if (e->enumerators[depth] == NULL)
1261 {
1262 if (depth > 0)
1263 {
1264 file = next_match_recurse (e, depth: depth - 1);
1265 if (file)
1266 {
1267 e->children[depth] = file;
1268 e->enumerators[depth] = g_file_enumerate_children (file,
1269 G_FILE_ATTRIBUTE_STANDARD_NAME,
1270 flags: G_FILE_QUERY_INFO_NONE,
1271 NULL,
1272 NULL);
1273 }
1274 }
1275 if (e->enumerators[depth] == NULL)
1276 return NULL;
1277 }
1278
1279 while ((info = g_file_enumerator_next_file (enumerator: e->enumerators[depth], NULL, NULL)))
1280 {
1281 name = g_file_info_get_name (info);
1282 if (component_match (e, depth, name))
1283 {
1284 file = g_file_get_child (file: e->children[depth], name);
1285 g_object_unref (object: info);
1286 return file;
1287 }
1288 g_object_unref (object: info);
1289 }
1290
1291 g_object_unref (object: e->enumerators[depth]);
1292 e->enumerators[depth] = NULL;
1293 g_object_unref (object: e->children[depth]);
1294 e->children[depth] = NULL;
1295 }
1296}
1297
1298static GFile *
1299enumerator_next (Enumerator *e)
1300{
1301 return next_match_recurse (e, depth: e->depth - 1);
1302}
1303
1304static Enumerator *
1305enumerator_new (GFile *root,
1306 const char *path,
1307 gboolean ignore_case)
1308{
1309 Enumerator *e;
1310 gint i;
1311 gchar *case_folded;
1312
1313 e = g_new0 (Enumerator, 1);
1314 e->path = g_strdup (str: path);
1315 e->ignore_case = ignore_case;
1316
1317 e->components = g_strsplit (string: e->path, G_DIR_SEPARATOR_S, max_tokens: -1);
1318 e->depth = g_strv_length (str_array: e->components);
1319 if (e->ignore_case)
1320 {
1321 e->case_components = g_new0 (char *, e->depth + 1);
1322 for (i = 0; e->components[i]; i++)
1323 {
1324 case_folded = g_utf8_casefold (str: e->components[i], len: -1);
1325 e->case_components[i] = g_utf8_collate_key (str: case_folded, len: -1);
1326 g_free (mem: case_folded);
1327 }
1328 }
1329
1330 e->children = g_new0 (GFile *, e->depth);
1331 e->children[0] = g_object_ref (root);
1332 e->enumerators = g_new0 (GFileEnumerator *, e->depth);
1333 e->enumerators[0] = g_file_enumerate_children (file: root,
1334 G_FILE_ATTRIBUTE_STANDARD_NAME,
1335 flags: G_FILE_QUERY_INFO_NONE,
1336 NULL,
1337 NULL);
1338
1339 return e;
1340}
1341
1342static void
1343enumerator_free (Enumerator *e)
1344{
1345 gint i;
1346
1347 for (i = 0; i < e->depth; i++)
1348 {
1349 if (e->enumerators[i])
1350 g_object_unref (object: e->enumerators[i]);
1351 if (e->children[i])
1352 g_object_unref (object: e->children[i]);
1353 }
1354
1355 g_free (mem: e->enumerators);
1356 g_free (mem: e->children);
1357 g_strfreev (str_array: e->components);
1358 if (e->case_components)
1359 g_strfreev (str_array: e->case_components);
1360 g_free (mem: e->path);
1361 g_free (mem: e);
1362}
1363
1364static gboolean
1365matchlet_match (TreeMatchlet *matchlet,
1366 GFile *root)
1367{
1368 GFile *file;
1369 GFileInfo *info;
1370 gboolean result;
1371 const gchar *attrs;
1372 Enumerator *e;
1373 GList *l;
1374
1375 e = enumerator_new (root, path: matchlet->path, ignore_case: !matchlet->match_case);
1376
1377 do
1378 {
1379 file = enumerator_next (e);
1380 if (!file)
1381 {
1382 enumerator_free (e);
1383 return FALSE;
1384 }
1385
1386 if (matchlet->mimetype)
1387 attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE ","
1388 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ","
1389 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
1390 else
1391 attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE ","
1392 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE;
1393 info = g_file_query_info (file,
1394 attributes: attrs,
1395 flags: G_FILE_QUERY_INFO_NONE,
1396 NULL,
1397 NULL);
1398 if (info)
1399 {
1400 result = TRUE;
1401
1402 if (matchlet->type != G_FILE_TYPE_UNKNOWN &&
1403 g_file_info_get_file_type (info) != matchlet->type)
1404 result = FALSE;
1405
1406 if (matchlet->executable &&
1407 !g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
1408 result = FALSE;
1409 }
1410 else
1411 result = FALSE;
1412
1413 if (result && matchlet->non_empty)
1414 {
1415 GFileEnumerator *child_enum;
1416 GFileInfo *child_info;
1417
1418 child_enum = g_file_enumerate_children (file,
1419 G_FILE_ATTRIBUTE_STANDARD_NAME,
1420 flags: G_FILE_QUERY_INFO_NONE,
1421 NULL,
1422 NULL);
1423
1424 if (child_enum)
1425 {
1426 child_info = g_file_enumerator_next_file (enumerator: child_enum, NULL, NULL);
1427 if (child_info)
1428 g_object_unref (object: child_info);
1429 else
1430 result = FALSE;
1431 g_object_unref (object: child_enum);
1432 }
1433 else
1434 result = FALSE;
1435 }
1436
1437 if (result && matchlet->mimetype)
1438 {
1439 if (strcmp (s1: matchlet->mimetype, s2: g_file_info_get_content_type (info)) != 0)
1440 result = FALSE;
1441 }
1442
1443 if (info)
1444 g_object_unref (object: info);
1445 g_object_unref (object: file);
1446 }
1447 while (!result);
1448
1449 enumerator_free (e);
1450
1451 if (!matchlet->matches)
1452 return TRUE;
1453
1454 for (l = matchlet->matches; l; l = l->next)
1455 {
1456 TreeMatchlet *submatchlet;
1457
1458 submatchlet = l->data;
1459 if (matchlet_match (matchlet: submatchlet, root))
1460 return TRUE;
1461 }
1462
1463 return FALSE;
1464}
1465
1466static void
1467match_match (TreeMatch *match,
1468 GFile *root,
1469 GPtrArray *types)
1470{
1471 GList *l;
1472
1473 for (l = match->matches; l; l = l->next)
1474 {
1475 TreeMatchlet *matchlet = l->data;
1476 if (matchlet_match (matchlet, root))
1477 {
1478 g_ptr_array_add (array: types, data: g_strdup (str: match->contenttype));
1479 break;
1480 }
1481 }
1482}
1483
1484/**
1485 * g_content_type_guess_for_tree:
1486 * @root: the root of the tree to guess a type for
1487 *
1488 * Tries to guess the type of the tree with root @root, by
1489 * looking at the files it contains. The result is an array
1490 * of content types, with the best guess coming first.
1491 *
1492 * The types returned all have the form x-content/foo, e.g.
1493 * x-content/audio-cdda (for audio CDs) or x-content/image-dcf
1494 * (for a camera memory card). See the
1495 * [shared-mime-info](http://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec)
1496 * specification for more on x-content types.
1497 *
1498 * This function is useful in the implementation of
1499 * g_mount_guess_content_type().
1500 *
1501 * Returns: (transfer full) (array zero-terminated=1): an %NULL-terminated
1502 * array of zero or more content types. Free with g_strfreev()
1503 *
1504 * Since: 2.18
1505 */
1506gchar **
1507g_content_type_guess_for_tree (GFile *root)
1508{
1509 GPtrArray *types;
1510 GList *l;
1511
1512 types = g_ptr_array_new ();
1513
1514 G_LOCK (gio_treemagic);
1515
1516 tree_magic_init ();
1517 for (l = tree_matches; l; l = l->next)
1518 {
1519 TreeMatch *match = l->data;
1520 match_match (match, root, types);
1521 }
1522
1523 G_UNLOCK (gio_treemagic);
1524
1525 g_ptr_array_add (array: types, NULL);
1526
1527 return (gchar **)g_ptr_array_free (array: types, FALSE);
1528}
1529

source code of gtk/subprojects/glib/gio/gcontenttype.c