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
58 static void tree_magic_schedule_reload (void);
59
60 /* We lock this mutex whenever we modify global state in this module. */
61 G_LOCK_DEFINE_STATIC (gio_xdgmime);
62
63 gsize
_g_unix_content_type_get_sniff_len(void)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
75 gchar *
_g_unix_content_type_unalias(const gchar * type)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 (type));
82 G_UNLOCK (gio_xdgmime);
83
84 return res;
85 }
86
87 gchar **
_g_unix_content_type_get_parents(const gchar * type)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 (type);
100
101 g_ptr_array_add (array, g_strdup (umime));
102
103 parents = xdg_mime_list_mime_parents (umime);
104 for (i = 0; parents && parents[i] != NULL; i++)
105 g_ptr_array_add (array, g_strdup (parents[i]));
106
107 free (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
116 G_LOCK_DEFINE_STATIC (global_mime_dirs);
117 static gchar **global_mime_dirs = NULL;
118
119 static void
_g_content_type_set_mime_dirs_locked(const char * const * dirs)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 ((gchar **) dirs);
127 }
128 else
129 {
130 GPtrArray *mime_dirs = g_ptr_array_new_with_free_func (g_free);
131 const gchar * const *system_dirs = g_get_system_data_dirs ();
132
133 g_ptr_array_add (mime_dirs, g_build_filename (g_get_user_data_dir (), "mime", NULL));
134 for (; *system_dirs != NULL; system_dirs++)
135 g_ptr_array_add (mime_dirs, g_build_filename (*system_dirs, "mime", NULL));
136 g_ptr_array_add (mime_dirs, NULL); /* NULL terminator */
137
138 global_mime_dirs = (gchar **) g_ptr_array_free (mime_dirs, FALSE);
139 }
140
141 xdg_mime_set_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 >*/
178 void
g_content_type_set_mime_dirs(const gchar * const * dirs)179 g_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 >*/
198 const gchar * const *
g_content_type_get_mime_dirs(void)199 g_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 */
226 gboolean
g_content_type_equals(const gchar * type1,const gchar * type2)227 g_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 (type1, 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 */
252 gboolean
g_content_type_is_a(const gchar * type,const gchar * supertype)253 g_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 (type, 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 */
281 gboolean
g_content_type_is_mime_type(const gchar * type,const gchar * mime_type)282 g_content_type_is_mime_type (const gchar *type,
283 const gchar *mime_type)
284 {
285 return g_content_type_is_a (type, 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 */
299 gboolean
g_content_type_is_unknown(const gchar * type)300 g_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, type) == 0;
305 }
306
307
308 typedef enum {
309 MIME_TAG_TYPE_OTHER,
310 MIME_TAG_TYPE_COMMENT
311 } MimeTagType;
312
313 typedef struct {
314 int current_type;
315 int current_lang_level;
316 int comment_lang_level;
317 char *comment;
318 } MimeParser;
319
320
321 static int
language_level(const char * lang)322 language_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 (lang_list[i], lang) == 0)
333 return 1000-i;
334
335 return 0;
336 }
337
338 static void
mime_info_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)339 mime_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 (element_name, "comment") == 0)
351 {
352 lang = "C";
353 for (i = 0; attribute_names[i]; i++)
354 if (strcmp (attribute_names[i], "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
367 static void
mime_info_end_element(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)368 mime_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
378 static void
mime_info_text(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)379 mime_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 (parser->comment);
391 parser->comment = g_strndup (text, text_len);
392 parser->comment_lang_level = parser->current_lang_level;
393 }
394 }
395
396 static char *
load_comment_for_mime_helper(const char * dir,const char * basename)397 load_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 (dir, basename, NULL);
414
415 res = g_file_get_contents (filename, &data, &len, NULL);
416 g_free (filename);
417 if (!res)
418 return NULL;
419
420 context = g_markup_parse_context_new (&parser, 0, &parse_data, NULL);
421 res = g_markup_parse_context_parse (context, data, len, NULL);
422 g_free (data);
423 g_markup_parse_context_free (context);
424
425 if (!res)
426 return NULL;
427
428 return parse_data.comment;
429 }
430
431
432 static char *
load_comment_for_mime(const char * mimetype)433 load_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 ("%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 (dirs[i], basename);
446 if (comment)
447 {
448 g_free (basename);
449 return comment;
450 }
451 }
452 g_free (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 */
466 gchar *
g_content_type_get_description(const gchar * type)467 g_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 (type);
476
477 if (type_comment_cache == NULL)
478 type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
479
480 comment = g_hash_table_lookup (type_comment_cache, type);
481 comment = g_strdup (comment);
482 G_UNLOCK (gio_xdgmime);
483
484 if (comment != NULL)
485 return comment;
486
487 comment = load_comment_for_mime (type);
488
489 G_LOCK (gio_xdgmime);
490 g_hash_table_insert (type_comment_cache,
491 g_strdup (type),
492 g_strdup (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 */
507 char *
g_content_type_get_mime_type(const char * type)508 g_content_type_get_mime_type (const char *type)
509 {
510 g_return_val_if_fail (type != NULL, NULL);
511
512 return g_strdup (type);
513 }
514
515 static GIcon *
g_content_type_get_icon_internal(const gchar * type,gboolean symbolic)516 g_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 (type);
532 G_UNLOCK (gio_xdgmime);
533
534 if (xdg_icon)
535 icon_names[n++] = g_strdup (xdg_icon);
536
537 mimetype_icon = g_strdup (type);
538 while ((q = strchr (mimetype_icon, '/')) != 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 (icon_names[i], "-symbolic", NULL);
553 }
554
555 n += n;
556 }
557
558 themed_icon = g_themed_icon_new_from_names (icon_names, n);
559
560 for (i = 0; i < n; i++)
561 g_free (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 */
575 GIcon *
g_content_type_get_icon(const gchar * type)576 g_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 */
592 GIcon *
g_content_type_get_symbolic_icon(const gchar * type)593 g_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 */
613 gchar *
g_content_type_get_generic_icon_name(const gchar * type)614 g_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 (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 (type, '/');
631 if (p == NULL)
632 p = type + strlen (type);
633
634 icon_name = g_malloc (p - type + strlen (suffix) + 1);
635 memcpy (icon_name, type, p - type);
636 memcpy (icon_name + (p - type), suffix, strlen (suffix));
637 icon_name[(p - type) + strlen (suffix)] = 0;
638 }
639 else
640 {
641 icon_name = g_strdup (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 */
657 gboolean
g_content_type_can_be_executable(const gchar * type)658 g_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, "application/x-executable") ||
663 g_content_type_is_a (type, "text/plain"))
664 return TRUE;
665
666 return FALSE;
667 }
668
669 static gboolean
looks_like_text(const guchar * data,gsize data_size)670 looks_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 **/
698 gchar *
g_content_type_from_mime_type(const gchar * mime_type)699 g_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_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 */
729 gchar *
g_content_type_guess(const gchar * filename,const guchar * data,gsize data_size,gboolean * result_uncertain)730 g_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 (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 (filename);
769 n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10);
770 g_free (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 (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, data_size, &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 (sniffed_mimetype, "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 (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 (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 (name_mimetypes[i], 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 (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 (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
851 static void
enumerate_mimetypes_subdir(const char * dir,const char * prefix,GHashTable * mimetypes)852 enumerate_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 (dir);
861 if (d)
862 {
863 while ((ent = readdir (d)) != NULL)
864 {
865 if (g_str_has_suffix (ent->d_name, ".xml"))
866 {
867 mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name);
868 g_hash_table_replace (mimetypes, mimetype, NULL);
869 }
870 }
871 closedir (d);
872 }
873 }
874
875 static void
enumerate_mimetypes_dir(const char * dir,GHashTable * mimetypes)876 enumerate_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 (mimedir);
887 if (d)
888 {
889 while ((ent = readdir (d)) != NULL)
890 {
891 if (strcmp (ent->d_name, "packages") != 0)
892 {
893 name = g_build_filename (mimedir, ent->d_name, NULL);
894 if (g_file_test (name, G_FILE_TEST_IS_DIR))
895 enumerate_mimetypes_subdir (name, ent->d_name, mimetypes);
896 g_free (name);
897 }
898 }
899 closedir (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 */
913 GList *
g_content_types_get_registered(void)914 g_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 (g_str_hash, g_str_equal, g_free, NULL);
924
925 dirs = g_content_type_get_mime_dirs ();
926 for (i = 0; dirs[i] != NULL; i++)
927 enumerate_mimetypes_dir (dirs[i], mimetypes);
928
929 l = NULL;
930 g_hash_table_iter_init (&iter, mimetypes);
931 while (g_hash_table_iter_next (&iter, &key, NULL))
932 {
933 l = g_list_prepend (l, key);
934 g_hash_table_iter_steal (&iter);
935 }
936
937 g_hash_table_destroy (mimetypes);
938
939 return l;
940 }
941
942
943 /* tree magic data */
944 static GList *tree_matches = NULL;
945 static gboolean need_reload = FALSE;
946
947 G_LOCK_DEFINE_STATIC (gio_treemagic);
948
949 typedef 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
961 typedef struct
962 {
963 gchar *contenttype;
964 gint priority;
965 GList *matches;
966 } TreeMatch;
967
968
969 static void
tree_matchlet_free(TreeMatchlet * matchlet)970 tree_matchlet_free (TreeMatchlet *matchlet)
971 {
972 g_list_free_full (matchlet->matches, (GDestroyNotify) tree_matchlet_free);
973 g_free (matchlet->path);
974 g_free (matchlet->mimetype);
975 g_slice_free (TreeMatchlet, matchlet);
976 }
977
978 static void
tree_match_free(TreeMatch * match)979 tree_match_free (TreeMatch *match)
980 {
981 g_list_free_full (match->matches, (GDestroyNotify) tree_matchlet_free);
982 g_free (match->contenttype);
983 g_slice_free (TreeMatch, match);
984 }
985
986 static TreeMatch *
parse_header(gchar * line)987 parse_header (gchar *line)
988 {
989 gint len;
990 gchar *s;
991 TreeMatch *match;
992
993 len = strlen (line);
994
995 if (line[0] != '[' || line[len - 1] != ']')
996 return NULL;
997
998 line[len - 1] = 0;
999 s = strchr (line, ':');
1000
1001 match = g_slice_new0 (TreeMatch);
1002 match->priority = atoi (line + 1);
1003 match->contenttype = g_strdup (s + 1);
1004
1005 return match;
1006 }
1007
1008 static TreeMatchlet *
parse_match_line(gchar * line,gint * depth)1009 parse_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 (line);
1027 s = strchr (line, '>');
1028 }
1029 s += 2;
1030 p = strchr (s, '"');
1031 *p = 0;
1032
1033 matchlet->path = g_strdup (s);
1034 s = p + 1;
1035 parts = g_strsplit (s, ",", 0);
1036 if (strcmp (parts[0], "=file") == 0)
1037 matchlet->type = G_FILE_TYPE_REGULAR;
1038 else if (strcmp (parts[0], "=directory") == 0)
1039 matchlet->type = G_FILE_TYPE_DIRECTORY;
1040 else if (strcmp (parts[0], "=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 (parts[i], "executable") == 0)
1047 matchlet->executable = 1;
1048 else if (strcmp (parts[i], "match-case") == 0)
1049 matchlet->match_case = 1;
1050 else if (strcmp (parts[i], "non-empty") == 0)
1051 matchlet->non_empty = 1;
1052 else if (strcmp (parts[i], "on-disc") == 0)
1053 matchlet->on_disc = 1;
1054 else
1055 matchlet->mimetype = g_strdup (parts[i]);
1056 }
1057
1058 g_strfreev (parts);
1059
1060 return matchlet;
1061 }
1062
1063 static gint
cmp_match(gconstpointer a,gconstpointer b)1064 cmp_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
1072 static void
insert_match(TreeMatch * match)1073 insert_match (TreeMatch *match)
1074 {
1075 tree_matches = g_list_insert_sorted (tree_matches, match, cmp_match);
1076 }
1077
1078 static void
insert_matchlet(TreeMatch * match,TreeMatchlet * matchlet,gint depth)1079 insert_matchlet (TreeMatch *match,
1080 TreeMatchlet *matchlet,
1081 gint depth)
1082 {
1083 if (depth == 0)
1084 match->matches = g_list_append (match->matches, matchlet);
1085 else
1086 {
1087 GList *last;
1088 TreeMatchlet *m;
1089
1090 last = g_list_last (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 (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 (m->matches, matchlet);
1112 }
1113 }
1114
1115 static void
read_tree_magic_from_directory(const gchar * prefix)1116 read_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 (prefix, "treemagic", NULL);
1128
1129 if (g_file_get_contents (filename, &text, &len, NULL))
1130 {
1131 if (strcmp (text, "MIME-TreeMagic") == 0)
1132 {
1133 lines = g_strsplit (text + strlen ("MIME-TreeMagic") + 2, "\n", 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 (lines[i]);
1140 insert_match (match);
1141 }
1142 else if (match != NULL)
1143 {
1144 matchlet = parse_match_line (lines[i], &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 (lines);
1155 }
1156 else
1157 g_warning ("%s: header not found, skipping", filename);
1158
1159 g_free (text);
1160 }
1161
1162 g_free (filename);
1163 }
1164
1165 static void
tree_magic_schedule_reload(void)1166 tree_magic_schedule_reload (void)
1167 {
1168 need_reload = TRUE;
1169 }
1170
1171 static void
xdg_mime_reload(void * user_data)1172 xdg_mime_reload (void *user_data)
1173 {
1174 tree_magic_schedule_reload ();
1175 }
1176
1177 static void
tree_magic_shutdown(void)1178 tree_magic_shutdown (void)
1179 {
1180 g_list_free_full (tree_matches, (GDestroyNotify) tree_match_free);
1181 tree_matches = NULL;
1182 }
1183
1184 static void
tree_magic_init(void)1185 tree_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 (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 (dirs[i]);
1209 }
1210 }
1211
1212 /* a filtering enumerator */
1213
1214 typedef 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
1225 static gboolean
component_match(Enumerator * e,gint depth,const gchar * name)1226 component_match (Enumerator *e,
1227 gint depth,
1228 const gchar *name)
1229 {
1230 gchar *case_folded, *key;
1231 gboolean found;
1232
1233 if (strcmp (name, e->components[depth]) == 0)
1234 return TRUE;
1235
1236 if (!e->ignore_case)
1237 return FALSE;
1238
1239 case_folded = g_utf8_casefold (name, -1);
1240 key = g_utf8_collate_key (case_folded, -1);
1241
1242 found = strcmp (key, e->case_components[depth]) == 0;
1243
1244 g_free (case_folded);
1245 g_free (key);
1246
1247 return found;
1248 }
1249
1250 static GFile *
next_match_recurse(Enumerator * e,gint depth)1251 next_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 - 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 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 (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 (e->children[depth], name);
1285 g_object_unref (info);
1286 return file;
1287 }
1288 g_object_unref (info);
1289 }
1290
1291 g_object_unref (e->enumerators[depth]);
1292 e->enumerators[depth] = NULL;
1293 g_object_unref (e->children[depth]);
1294 e->children[depth] = NULL;
1295 }
1296 }
1297
1298 static GFile *
enumerator_next(Enumerator * e)1299 enumerator_next (Enumerator *e)
1300 {
1301 return next_match_recurse (e, e->depth - 1);
1302 }
1303
1304 static Enumerator *
enumerator_new(GFile * root,const char * path,gboolean ignore_case)1305 enumerator_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 (path);
1315 e->ignore_case = ignore_case;
1316
1317 e->components = g_strsplit (e->path, G_DIR_SEPARATOR_S, -1);
1318 e->depth = g_strv_length (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 (e->components[i], -1);
1325 e->case_components[i] = g_utf8_collate_key (case_folded, -1);
1326 g_free (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 (root,
1334 G_FILE_ATTRIBUTE_STANDARD_NAME,
1335 G_FILE_QUERY_INFO_NONE,
1336 NULL,
1337 NULL);
1338
1339 return e;
1340 }
1341
1342 static void
enumerator_free(Enumerator * e)1343 enumerator_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 (e->enumerators[i]);
1351 if (e->children[i])
1352 g_object_unref (e->children[i]);
1353 }
1354
1355 g_free (e->enumerators);
1356 g_free (e->children);
1357 g_strfreev (e->components);
1358 if (e->case_components)
1359 g_strfreev (e->case_components);
1360 g_free (e->path);
1361 g_free (e);
1362 }
1363
1364 static gboolean
matchlet_match(TreeMatchlet * matchlet,GFile * root)1365 matchlet_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, matchlet->path, !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 attrs,
1395 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 G_FILE_QUERY_INFO_NONE,
1421 NULL,
1422 NULL);
1423
1424 if (child_enum)
1425 {
1426 child_info = g_file_enumerator_next_file (child_enum, NULL, NULL);
1427 if (child_info)
1428 g_object_unref (child_info);
1429 else
1430 result = FALSE;
1431 g_object_unref (child_enum);
1432 }
1433 else
1434 result = FALSE;
1435 }
1436
1437 if (result && matchlet->mimetype)
1438 {
1439 if (strcmp (matchlet->mimetype, g_file_info_get_content_type (info)) != 0)
1440 result = FALSE;
1441 }
1442
1443 if (info)
1444 g_object_unref (info);
1445 g_object_unref (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 (submatchlet, root))
1460 return TRUE;
1461 }
1462
1463 return FALSE;
1464 }
1465
1466 static void
match_match(TreeMatch * match,GFile * root,GPtrArray * types)1467 match_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 (types, g_strdup (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 */
1506 gchar **
g_content_type_guess_for_tree(GFile * root)1507 g_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 (types, NULL);
1526
1527 return (gchar **)g_ptr_array_free (types, FALSE);
1528 }
1529