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