Coverage Report

Created: 2025-06-13 06:55

/src/glib/gio/xdgmime/xdgmime.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: C; c-file-style: "gnu" -*- */
2
/* xdgmime.c: XDG Mime Spec mime resolver.  Based on version 0.11 of the spec.
3
 *
4
 * More info can be found at http://www.freedesktop.org/standards/
5
 * 
6
 * Copyright (C) 2003,2004  Red Hat, Inc.
7
 * Copyright (C) 2003,2004  Jonathan Blandford <jrb@alum.mit.edu>
8
 *
9
 * SPDX-License-Identifier: LGPL-2.1-or-later or AFL-2.0
10
 */
11
12
#ifdef HAVE_CONFIG_H
13
#include <config.h>
14
#endif
15
16
#include "xdgmime.h"
17
#include "xdgmimeint.h"
18
#include "xdgmimeglob.h"
19
#include "xdgmimemagic.h"
20
#include "xdgmimealias.h"
21
#include "xdgmimeicon.h"
22
#include "xdgmimeparent.h"
23
#include "xdgmimecache.h"
24
#include <stdio.h>
25
#include <string.h>
26
#include <sys/stat.h>
27
#include <sys/types.h>
28
#include <sys/time.h>
29
#include <unistd.h>
30
#include <assert.h>
31
32
#ifndef S_ISREG
33
#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
34
#endif
35
36
typedef struct XdgDirTimeList XdgDirTimeList;
37
typedef struct XdgCallbackList XdgCallbackList;
38
39
static int need_reread = TRUE;
40
static time_t last_stat_time = 0;
41
42
static XdgGlobHash *global_hash = NULL;
43
static XdgMimeMagic *global_magic = NULL;
44
static XdgAliasList *alias_list = NULL;
45
static XdgParentList *parent_list = NULL;
46
static XdgDirTimeList *dir_time_list = NULL;
47
static XdgCallbackList *callback_list = NULL;
48
static XdgIconList *icon_list = NULL;
49
static XdgIconList *generic_icon_list = NULL;
50
51
static char **xdg_dirs = NULL;  /* NULL terminated */
52
53
XdgMimeCache **_caches = NULL;
54
static int n_caches = 0;
55
56
const char xdg_mime_type_unknown[] = "application/octet-stream";
57
const char xdg_mime_type_empty[] = "application/x-zerosize";
58
const char xdg_mime_type_textplain[] = "text/plain";
59
60
61
enum
62
{
63
  XDG_CHECKED_UNCHECKED,
64
  XDG_CHECKED_VALID,
65
  XDG_CHECKED_INVALID
66
};
67
68
struct XdgDirTimeList
69
{
70
  time_t mtime;
71
  char *directory_name;
72
  int checked;
73
  XdgDirTimeList *next;
74
};
75
76
struct XdgCallbackList
77
{
78
  XdgCallbackList *next;
79
  XdgCallbackList *prev;
80
  int              callback_id;
81
  XdgMimeCallback  callback;
82
  void            *data;
83
  XdgMimeDestroy   destroy;
84
};
85
86
/* Function called by xdg_run_command_on_dirs.  If it returns TRUE, further
87
 * directories aren't looked at */
88
typedef int (*XdgDirectoryFunc) (const char *directory,
89
         void       *user_data);
90
91
static void
92
xdg_dir_time_list_add (char   *file_name, 
93
           time_t  mtime)
94
0
{
95
0
  XdgDirTimeList *list;
96
97
0
  for (list = dir_time_list; list; list = list->next) 
98
0
    {
99
0
      if (strcmp (list->directory_name, file_name) == 0)
100
0
        {
101
0
          free (file_name);
102
0
          return;
103
0
        }
104
0
    }
105
  
106
0
  list = calloc (1, sizeof (XdgDirTimeList));
107
0
  list->checked = XDG_CHECKED_UNCHECKED;
108
0
  list->directory_name = file_name;
109
0
  list->mtime = mtime;
110
0
  list->next = dir_time_list;
111
0
  dir_time_list = list;
112
0
}
113
 
114
static void
115
xdg_dir_time_list_free (XdgDirTimeList *list)
116
0
{
117
0
  XdgDirTimeList *next;
118
119
0
  while (list)
120
0
    {
121
0
      next = list->next;
122
0
      free (list->directory_name);
123
0
      free (list);
124
0
      list = next;
125
0
    }
126
0
}
127
128
static int
129
xdg_mime_init_from_directory (const char *directory,
130
                              void       *user_data __attribute__((__unused__)))
131
0
{
132
0
  char *file_name;
133
0
  struct stat st;
134
135
0
  assert (directory != NULL);
136
137
0
  file_name = malloc (strlen (directory) + strlen ("/mime.cache") + 1);
138
0
  strcpy (file_name, directory); strcat (file_name, "/mime.cache");
139
0
  if (stat (file_name, &st) == 0)
140
0
    {
141
0
      XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name);
142
143
0
      if (cache != NULL)
144
0
  {
145
0
    xdg_dir_time_list_add (file_name, st.st_mtime);
146
147
0
    _caches = realloc (_caches, sizeof (XdgMimeCache *) * (n_caches + 2));
148
0
    _caches[n_caches] = cache;
149
0
          _caches[n_caches + 1] = NULL;
150
0
    n_caches++;
151
152
0
    return FALSE;
153
0
  }
154
0
    }
155
0
  free (file_name);
156
157
0
  file_name = malloc (strlen (directory) + strlen ("/globs2") + 1);
158
0
  strcpy (file_name, directory); strcat (file_name, "/globs2");
159
0
  if (stat (file_name, &st) == 0)
160
0
    {
161
0
      _xdg_mime_glob_read_from_file (global_hash, file_name, TRUE);
162
0
      xdg_dir_time_list_add (file_name, st.st_mtime);
163
0
    }
164
0
  else
165
0
    {
166
0
      free (file_name);
167
0
      file_name = malloc (strlen (directory) + strlen ("/globs") + 1);
168
0
      strcpy (file_name, directory); strcat (file_name, "/globs");
169
0
      if (stat (file_name, &st) == 0)
170
0
        {
171
0
          _xdg_mime_glob_read_from_file (global_hash, file_name, FALSE);
172
0
          xdg_dir_time_list_add (file_name, st.st_mtime);
173
0
        }
174
0
      else
175
0
        {
176
0
          free (file_name);
177
0
        }
178
0
    }
179
180
0
  file_name = malloc (strlen (directory) + strlen ("/magic") + 1);
181
0
  strcpy (file_name, directory); strcat (file_name, "/magic");
182
0
  if (stat (file_name, &st) == 0)
183
0
    {
184
0
      _xdg_mime_magic_read_from_file (global_magic, file_name);
185
0
      xdg_dir_time_list_add (file_name, st.st_mtime);
186
0
    }
187
0
  else
188
0
    {
189
0
      free (file_name);
190
0
    }
191
192
0
  file_name = malloc (strlen (directory) + strlen ("/aliases") + 1);
193
0
  strcpy (file_name, directory); strcat (file_name, "/aliases");
194
0
  _xdg_mime_alias_read_from_file (alias_list, file_name);
195
0
  free (file_name);
196
197
0
  file_name = malloc (strlen (directory) + strlen ("/subclasses") + 1);
198
0
  strcpy (file_name, directory); strcat (file_name, "/subclasses");
199
0
  _xdg_mime_parent_read_from_file (parent_list, file_name);
200
0
  free (file_name);
201
202
0
  file_name = malloc (strlen (directory) + strlen ("/icons") + 1);
203
0
  strcpy (file_name, directory); strcat (file_name, "/icons");
204
0
  _xdg_mime_icon_read_from_file (icon_list, file_name);
205
0
  free (file_name);
206
207
0
  file_name = malloc (strlen (directory) + strlen ("/generic-icons") + 1);
208
0
  strcpy (file_name, directory); strcat (file_name, "/generic-icons");
209
0
  _xdg_mime_icon_read_from_file (generic_icon_list, file_name);
210
0
  free (file_name);
211
212
0
  return FALSE; /* Keep processing */
213
0
}
214
215
/* Set @xdg_dirs from the environment. It must not have been set already. */
216
static void
217
xdg_init_dirs (void)
218
0
{
219
0
  const char *xdg_data_home, *home, *xdg_data_dirs;
220
0
  const char *ptr;
221
0
  size_t n_dirs = 0;
222
0
  size_t i, current_dir;
223
224
0
  assert (xdg_dirs == NULL);
225
226
0
  xdg_data_home = getenv ("XDG_DATA_HOME");
227
0
  home = getenv ("HOME");
228
0
  xdg_data_dirs = getenv ("XDG_DATA_DIRS");
229
230
0
  if (xdg_data_dirs == NULL)
231
0
    xdg_data_dirs = "/usr/local/share/:/usr/share/";
232
233
  /* Work out how many dirs we’re dealing with. */
234
0
  if (xdg_data_home != NULL || home != NULL)
235
0
    n_dirs++;
236
0
  n_dirs++;  /* initial entry in @xdg_data_dirs */
237
0
  for (i = 0; xdg_data_dirs[i] != '\0'; i++)
238
0
    if (xdg_data_dirs[i] == ':')
239
0
      n_dirs++;
240
241
0
  xdg_dirs = calloc (n_dirs + 1  /* NULL terminator */, sizeof (char *));
242
0
  current_dir = 0;
243
244
  /* $XDG_DATA_HOME */
245
0
  if (xdg_data_home != NULL)
246
0
    {
247
0
      char *mime_subdir;
248
249
0
      mime_subdir = malloc (strlen (xdg_data_home) + strlen ("/mime/") + 1);
250
0
      strcpy (mime_subdir, xdg_data_home);
251
0
      strcat (mime_subdir, "/mime/");
252
253
0
      xdg_dirs[current_dir++] = mime_subdir;
254
0
    }
255
0
  else if (home != NULL)
256
0
    {
257
0
      char *guessed_xdg_home;
258
259
0
      guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/mime/") + 1);
260
0
      strcpy (guessed_xdg_home, home);
261
0
      strcat (guessed_xdg_home, "/.local/share/mime/");
262
263
0
      xdg_dirs[current_dir++] = guessed_xdg_home;
264
0
    }
265
266
  /* $XDG_DATA_DIRS */
267
0
  ptr = xdg_data_dirs;
268
269
0
  while (*ptr != '\000')
270
0
    {
271
0
      const char *end_ptr;
272
0
      char *dir;
273
0
      int len;
274
275
0
      end_ptr = ptr;
276
0
      while (*end_ptr != ':' && *end_ptr != '\000')
277
0
        end_ptr ++;
278
279
0
      if (end_ptr == ptr)
280
0
        {
281
0
          ptr++;
282
0
          continue;
283
0
        }
284
285
0
      if (*end_ptr == ':')
286
0
        len = end_ptr - ptr;
287
0
      else
288
0
        len = end_ptr - ptr + 1;
289
0
      dir = malloc (len + strlen ("/mime/") + 1);
290
0
      strncpy (dir, ptr, len);
291
0
      dir[len] = '\0';
292
0
      strcat (dir, "/mime/");
293
294
0
      xdg_dirs[current_dir++] = dir;
295
296
0
      ptr = end_ptr;
297
0
    }
298
299
  /* NULL terminator */
300
0
  xdg_dirs[current_dir] = NULL;
301
302
0
  need_reread = TRUE;
303
0
}
304
305
/* Runs a command on all the directories in the search path (@xdg_dirs). */
306
static void
307
xdg_run_command_on_dirs (XdgDirectoryFunc  func,
308
                         void             *user_data)
309
0
{
310
0
  size_t i;
311
312
0
  if (xdg_dirs == NULL)
313
0
    xdg_init_dirs ();
314
315
0
  for (i = 0; xdg_dirs[i] != NULL; i++)
316
0
    {
317
0
      if ((func) (xdg_dirs[i], user_data))
318
0
        return;
319
0
    }
320
0
}
321
322
/* Allows the calling code to override the directories used by xdgmime, without
323
 * having to change environment variables in a running process (which is not
324
 * thread safe). This is intended to be used by tests. The changes will be
325
 * picked up by xdg_mime_init() next time public API is called.
326
 *
327
 * This will set @xdg_dirs. Directories in @dirs must be complete, including
328
 * the conventional `/mime` subdirectory. This is to allow tests to override
329
 * them without the need to create a subdirectory. */
330
void
331
xdg_mime_set_dirs (const char * const *dirs)
332
0
{
333
0
  size_t i;
334
335
0
  for (i = 0; xdg_dirs != NULL && xdg_dirs[i] != NULL; i++)
336
0
    free (xdg_dirs[i]);
337
0
  free (xdg_dirs);
338
0
  xdg_dirs = NULL;
339
340
0
  if (dirs != NULL)
341
0
    {
342
0
      for (i = 0; dirs[i] != NULL; i++);
343
0
      xdg_dirs = calloc (i + 1  /* NULL terminator */, sizeof (char*));
344
0
      for (i = 0; dirs[i] != NULL; i++)
345
0
        xdg_dirs[i] = strdup (dirs[i]);
346
0
      xdg_dirs[i] = NULL;
347
0
    }
348
349
0
  need_reread = TRUE;
350
0
}
351
352
/* Checks file_path to make sure it has the same mtime as last time it was
353
 * checked.  If it has a different mtime, or if the file doesn't exist, it
354
 * returns FALSE.
355
 *
356
 * FIXME: This doesn't protect against permission changes.
357
 */
358
static int
359
xdg_check_file (const char *file_path,
360
                int        *exists)
361
0
{
362
0
  struct stat st;
363
364
  /* If the file exists */
365
0
  if (stat (file_path, &st) == 0)
366
0
    {
367
0
      XdgDirTimeList *list;
368
369
0
      if (exists)
370
0
        *exists = TRUE;
371
372
0
      for (list = dir_time_list; list; list = list->next)
373
0
  {
374
0
    if (! strcmp (list->directory_name, file_path))
375
0
      {
376
0
        if (st.st_mtime == list->mtime)
377
0
    list->checked = XDG_CHECKED_VALID;
378
0
        else 
379
0
    list->checked = XDG_CHECKED_INVALID;
380
381
0
        return (list->checked != XDG_CHECKED_VALID);
382
0
      }
383
0
  }
384
0
      return TRUE;
385
0
    }
386
387
0
  if (exists)
388
0
    *exists = FALSE;
389
390
0
  return FALSE;
391
0
}
392
393
static int
394
xdg_check_dir (const char *directory,
395
         void       *user_data)
396
0
{
397
0
  int invalid, exists;
398
0
  char *file_name;
399
0
  int* invalid_dir_list = user_data;
400
401
0
  assert (directory != NULL);
402
403
  /* Check the mime.cache file */
404
0
  file_name = malloc (strlen (directory) + strlen ("/mime.cache") + 1);
405
0
  strcpy (file_name, directory); strcat (file_name, "/mime.cache");
406
0
  invalid = xdg_check_file (file_name, &exists);
407
0
  free (file_name);
408
0
  if (invalid)
409
0
    {
410
0
      *invalid_dir_list = TRUE;
411
0
      return TRUE;
412
0
    }
413
0
  else if (exists)
414
0
    {
415
0
      return FALSE;
416
0
    }
417
418
  /* Check the globs file */
419
0
  file_name = malloc (strlen (directory) + strlen ("/globs") + 1);
420
0
  strcpy (file_name, directory); strcat (file_name, "/globs");
421
0
  invalid = xdg_check_file (file_name, NULL);
422
0
  free (file_name);
423
0
  if (invalid)
424
0
    {
425
0
      *invalid_dir_list = TRUE;
426
0
      return TRUE;
427
0
    }
428
429
  /* Check the magic file */
430
0
  file_name = malloc (strlen (directory) + strlen ("/magic") + 1);
431
0
  strcpy (file_name, directory); strcat (file_name, "/magic");
432
0
  invalid = xdg_check_file (file_name, NULL);
433
0
  free (file_name);
434
0
  if (invalid)
435
0
    {
436
0
      *invalid_dir_list = TRUE;
437
0
      return TRUE;
438
0
    }
439
440
0
  return FALSE; /* Keep processing */
441
0
}
442
443
/* Walks through all the mime files stat()ing them to see if they've changed.
444
 * Returns TRUE if they have. */
445
static int
446
xdg_check_dirs (void)
447
0
{
448
0
  XdgDirTimeList *list;
449
0
  int invalid_dir_list = FALSE;
450
451
0
  for (list = dir_time_list; list; list = list->next)
452
0
    list->checked = XDG_CHECKED_UNCHECKED;
453
454
0
  xdg_run_command_on_dirs (xdg_check_dir, &invalid_dir_list);
455
456
0
  if (invalid_dir_list)
457
0
    return TRUE;
458
459
0
  for (list = dir_time_list; list; list = list->next)
460
0
    {
461
0
      if (list->checked != XDG_CHECKED_VALID)
462
0
  return TRUE;
463
0
    }
464
465
0
  return FALSE;
466
0
}
467
468
/* We want to avoid stat()ing on every single mime call, so we only look for
469
 * newer files every 5 seconds.  This will return TRUE if we need to reread the
470
 * mime data from disk.
471
 */
472
static int
473
xdg_check_time_and_dirs (void)
474
0
{
475
0
  struct timeval tv;
476
0
  time_t current_time;
477
0
  int retval = FALSE;
478
479
0
  gettimeofday (&tv, NULL);
480
0
  current_time = tv.tv_sec;
481
482
0
  if (current_time >= last_stat_time + 5)
483
0
    {
484
0
      retval = xdg_check_dirs ();
485
0
      last_stat_time = current_time;
486
0
    }
487
488
0
  return retval;
489
0
}
490
491
/* Called in every public function.  It reloads the hash function if need be.
492
 */
493
static void
494
xdg_mime_init (void)
495
0
{
496
0
  if (xdg_check_time_and_dirs ())
497
0
    {
498
0
      xdg_mime_shutdown ();
499
0
    }
500
501
0
  if (need_reread)
502
0
    {
503
0
      global_hash = _xdg_glob_hash_new ();
504
0
      global_magic = _xdg_mime_magic_new ();
505
0
      alias_list = _xdg_mime_alias_list_new ();
506
0
      parent_list = _xdg_mime_parent_list_new ();
507
0
      icon_list = _xdg_mime_icon_list_new ();
508
0
      generic_icon_list = _xdg_mime_icon_list_new ();
509
510
0
      xdg_run_command_on_dirs (xdg_mime_init_from_directory, NULL);
511
512
0
      need_reread = FALSE;
513
0
    }
514
0
}
515
516
const char *
517
xdg_mime_get_mime_type_for_data (const void *data,
518
         size_t      len,
519
         int        *result_prio)
520
0
{
521
0
  const char *mime_type;
522
523
0
  if (len == 0)
524
0
    {
525
0
      if (result_prio != NULL)
526
0
        *result_prio = 100;
527
0
      return XDG_MIME_TYPE_EMPTY;
528
0
    }
529
530
0
  xdg_mime_init ();
531
532
0
  if (_caches)
533
0
    mime_type = _xdg_mime_cache_get_mime_type_for_data (data, len, result_prio);
534
0
  else
535
0
    mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, result_prio, NULL, 0);
536
537
0
  if (mime_type)
538
0
    return mime_type;
539
540
0
  return _xdg_binary_or_text_fallback(data, len);
541
0
}
542
543
const char *
544
xdg_mime_get_mime_type_for_file (const char  *file_name,
545
                                 struct stat *statbuf)
546
0
{
547
0
  const char *mime_type;
548
  /* currently, only a few globs occur twice, and none
549
   * more often, so 5 seems plenty.
550
   */
551
0
  const char *mime_types[5];
552
0
  FILE *file;
553
0
  unsigned char *data;
554
0
  int max_extent;
555
0
  int bytes_read;
556
0
  struct stat buf;
557
0
  const char *base_name;
558
0
  int n;
559
560
0
  if (file_name == NULL)
561
0
    return NULL;
562
0
  if (! _xdg_utf8_validate (file_name))
563
0
    return NULL;
564
565
0
  xdg_mime_init ();
566
567
0
  if (_caches)
568
0
    return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf);
569
570
0
  base_name = _xdg_get_base_name (file_name);
571
0
  n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 5);
572
573
0
  if (n == 1)
574
0
    return mime_types[0];
575
576
0
  if (!statbuf)
577
0
    {
578
0
      if (stat (file_name, &buf) != 0)
579
0
  return XDG_MIME_TYPE_UNKNOWN;
580
581
0
      statbuf = &buf;
582
0
    }
583
584
0
  if (!S_ISREG (statbuf->st_mode))
585
0
    return XDG_MIME_TYPE_UNKNOWN;
586
587
  /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
588
   * be large and need getting from a stream instead of just reading it all
589
   * in. */
590
0
  max_extent = _xdg_mime_magic_get_buffer_extents (global_magic);
591
0
  data = malloc (max_extent);
592
0
  if (data == NULL)
593
0
    return XDG_MIME_TYPE_UNKNOWN;
594
        
595
0
  file = fopen (file_name, "r");
596
0
  if (file == NULL)
597
0
    {
598
0
      free (data);
599
0
      return XDG_MIME_TYPE_UNKNOWN;
600
0
    }
601
602
0
  bytes_read = fread (data, 1, max_extent, file);
603
0
  if (ferror (file))
604
0
    {
605
0
      free (data);
606
0
      fclose (file);
607
0
      return XDG_MIME_TYPE_UNKNOWN;
608
0
    }
609
610
0
  mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL,
611
0
             mime_types, n);
612
613
0
  if (!mime_type)
614
0
    mime_type = _xdg_binary_or_text_fallback (data, bytes_read);
615
616
0
  free (data);
617
0
  fclose (file);
618
619
0
  return mime_type;
620
0
}
621
622
const char *
623
xdg_mime_get_mime_type_from_file_name (const char *file_name)
624
0
{
625
0
  const char *mime_type;
626
627
0
  xdg_mime_init ();
628
629
0
  if (_caches)
630
0
    return _xdg_mime_cache_get_mime_type_from_file_name (file_name);
631
632
0
  if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, &mime_type, 1))
633
0
    return mime_type;
634
0
  else
635
0
    return XDG_MIME_TYPE_UNKNOWN;
636
0
}
637
638
int
639
xdg_mime_get_mime_types_from_file_name (const char *file_name,
640
          const char  *mime_types[],
641
          int          n_mime_types)
642
0
{
643
0
  xdg_mime_init ();
644
  
645
0
  if (_caches)
646
0
    return _xdg_mime_cache_get_mime_types_from_file_name (file_name, mime_types, n_mime_types);
647
  
648
0
  return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_mime_types);
649
0
}
650
651
int
652
xdg_mime_is_valid_mime_type (const char *mime_type)
653
0
{
654
  /* FIXME: We should make this a better test
655
   */
656
0
  return _xdg_utf8_validate (mime_type);
657
0
}
658
659
void
660
xdg_mime_shutdown (void)
661
0
{
662
0
  XdgCallbackList *list;
663
664
  /* FIXME: Need to make this (and the whole library) thread safe */
665
0
  if (dir_time_list)
666
0
    {
667
0
      xdg_dir_time_list_free (dir_time_list);
668
0
      dir_time_list = NULL;
669
0
    }
670
  
671
0
  if (global_hash)
672
0
    {
673
0
      _xdg_glob_hash_free (global_hash);
674
0
      global_hash = NULL;
675
0
    }
676
0
  if (global_magic)
677
0
    {
678
0
      _xdg_mime_magic_free (global_magic);
679
0
      global_magic = NULL;
680
0
    }
681
682
0
  if (alias_list)
683
0
    {
684
0
      _xdg_mime_alias_list_free (alias_list);
685
0
      alias_list = NULL;
686
0
    }
687
688
0
  if (parent_list)
689
0
    {
690
0
      _xdg_mime_parent_list_free (parent_list);
691
0
      parent_list = NULL;
692
0
    }
693
694
0
  if (icon_list)
695
0
    {
696
0
      _xdg_mime_icon_list_free (icon_list);
697
0
      icon_list = NULL;
698
0
    }
699
700
0
  if (generic_icon_list)
701
0
    {
702
0
      _xdg_mime_icon_list_free (generic_icon_list);
703
0
      generic_icon_list = NULL;
704
0
    }
705
  
706
0
  if (_caches)
707
0
    {
708
0
      int i;
709
710
0
      for (i = 0; i < n_caches; i++)
711
0
        _xdg_mime_cache_unref (_caches[i]);
712
0
      free (_caches);
713
0
      _caches = NULL;
714
0
      n_caches = 0;
715
0
    }
716
717
0
  for (list = callback_list; list; list = list->next)
718
0
    (list->callback) (list->data);
719
720
0
  need_reread = TRUE;
721
0
}
722
723
int
724
xdg_mime_get_max_buffer_extents (void)
725
0
{
726
0
  xdg_mime_init ();
727
  
728
0
  if (_caches)
729
0
    return _xdg_mime_cache_get_max_buffer_extents ();
730
731
0
  return _xdg_mime_magic_get_buffer_extents (global_magic);
732
0
}
733
734
const char *
735
_xdg_mime_unalias_mime_type (const char *mime_type)
736
0
{
737
0
  const char *lookup;
738
739
0
  if (_caches)
740
0
    return _xdg_mime_cache_unalias_mime_type (mime_type);
741
742
0
  if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
743
0
    return lookup;
744
745
0
  return mime_type;
746
0
}
747
748
const char *
749
xdg_mime_unalias_mime_type (const char *mime_type)
750
0
{
751
0
  xdg_mime_init ();
752
753
0
  return _xdg_mime_unalias_mime_type (mime_type);
754
0
}
755
756
int
757
_xdg_mime_mime_type_equal (const char *mime_a,
758
         const char *mime_b)
759
0
{
760
0
  const char *unalias_a, *unalias_b;
761
762
0
  unalias_a = _xdg_mime_unalias_mime_type (mime_a);
763
0
  unalias_b = _xdg_mime_unalias_mime_type (mime_b);
764
765
0
  if (strcmp (unalias_a, unalias_b) == 0)
766
0
    return 1;
767
768
0
  return 0;
769
0
}
770
771
int
772
xdg_mime_mime_type_equal (const char *mime_a,
773
        const char *mime_b)
774
0
{
775
0
  xdg_mime_init ();
776
777
0
  return _xdg_mime_mime_type_equal (mime_a, mime_b);
778
0
}
779
780
int
781
xdg_mime_media_type_equal (const char *mime_a,
782
         const char *mime_b)
783
0
{
784
0
  char *sep;
785
786
0
  sep = strchr (mime_a, '/');
787
  
788
0
  if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0)
789
0
    return 1;
790
791
0
  return 0;
792
0
}
793
794
#if 1
795
static int
796
ends_with (const char *str,
797
           const char *suffix)
798
0
{
799
0
  int length;
800
0
  int suffix_length;
801
802
0
  length = strlen (str);
803
0
  suffix_length = strlen (suffix);
804
0
  if (length < suffix_length)
805
0
    return 0;
806
807
0
  if (strcmp (str + length - suffix_length, suffix) == 0)
808
0
    return 1;
809
810
0
  return 0;
811
0
}
812
813
static int
814
xdg_mime_is_super_type (const char *mime)
815
0
{
816
0
  return ends_with (mime, "/*");
817
0
}
818
#endif
819
820
int
821
_xdg_mime_mime_type_subclass (const char *mime,
822
            const char *base,
823
            const char ***seen)
824
0
{
825
0
  const char *umime, *ubase, *parent;
826
0
  const char **parents, **first_seen = NULL, **new_seen;
827
828
0
  int i, ret = 0;
829
830
0
  if (_caches)
831
0
    return _xdg_mime_cache_mime_type_subclass (mime, base, NULL);
832
833
0
  umime = _xdg_mime_unalias_mime_type (mime);
834
0
  ubase = _xdg_mime_unalias_mime_type (base);
835
836
0
  if (strcmp (umime, ubase) == 0)
837
0
    return 1;
838
839
0
#if 1  
840
  /* Handle supertypes */
841
0
  if (xdg_mime_is_super_type (ubase) &&
842
0
      xdg_mime_media_type_equal (umime, ubase))
843
0
    return 1;
844
0
#endif
845
846
  /*  Handle special cases text/plain and application/octet-stream */
847
0
  if (strcmp (ubase, "text/plain") == 0 && 
848
0
      strncmp (umime, "text/", 5) == 0)
849
0
    return 1;
850
851
0
  if (strcmp (ubase, "application/octet-stream") == 0 &&
852
0
      strncmp (umime, "inode/", 6) != 0)
853
0
    return 1;
854
855
0
  if (!seen)
856
0
    {
857
0
      first_seen = calloc (1, sizeof (char *));
858
0
      seen = &first_seen;
859
0
    }
860
861
0
  parents = _xdg_mime_parent_list_lookup (parent_list, umime);
862
0
  for (; parents && *parents; parents++)
863
0
    {
864
0
      parent = *parents;
865
866
      /* Detect and avoid buggy circular relationships */
867
0
      for (i = 0; (*seen)[i] != NULL; i++)
868
0
        if (parent == (*seen)[i])
869
0
          goto next_parent;
870
0
      new_seen = realloc (*seen, (i + 2) * sizeof (char *));
871
0
      if (!new_seen)
872
0
        goto done;
873
0
      new_seen[i] = parent;
874
0
      new_seen[i + 1] = NULL;
875
0
      *seen = new_seen;
876
877
0
      if (_xdg_mime_mime_type_subclass (parent, ubase, seen))
878
0
        {
879
0
          ret = 1;
880
0
          goto done;
881
0
        }
882
883
0
    next_parent:
884
0
      continue;
885
0
    }
886
887
0
done:
888
0
  free (first_seen);
889
0
  return ret;
890
0
}
891
892
int
893
xdg_mime_mime_type_subclass (const char *mime,
894
           const char *base)
895
0
{
896
0
  xdg_mime_init ();
897
898
0
  return _xdg_mime_mime_type_subclass (mime, base, NULL);
899
0
}
900
901
char **
902
xdg_mime_list_mime_parents (const char *mime)
903
0
{
904
0
  const char **parents;
905
0
  char **result;
906
0
  int i, n;
907
908
0
  xdg_mime_init ();
909
910
0
  if (_caches)
911
0
    return _xdg_mime_cache_list_mime_parents (mime);
912
913
0
  parents = xdg_mime_get_mime_parents (mime);
914
915
0
  if (!parents)
916
0
    return NULL;
917
918
0
  for (i = 0; parents[i]; i++) ;
919
  
920
0
  n = (i + 1) * sizeof (char *);
921
0
  result = (char **) malloc (n);
922
0
  memcpy (result, parents, n);
923
924
0
  return result;
925
0
}
926
927
const char **
928
xdg_mime_get_mime_parents (const char *mime)
929
0
{
930
0
  const char *umime;
931
932
0
  xdg_mime_init ();
933
934
0
  umime = _xdg_mime_unalias_mime_type (mime);
935
936
0
  return _xdg_mime_parent_list_lookup (parent_list, umime);
937
0
}
938
939
void 
940
xdg_mime_dump (void)
941
0
{
942
0
  xdg_mime_init();
943
944
0
  printf ("*** ALIASES ***\n\n");
945
0
  _xdg_mime_alias_list_dump (alias_list);
946
0
  printf ("\n*** PARENTS ***\n\n");
947
0
  _xdg_mime_parent_list_dump (parent_list);
948
0
  printf ("\n*** CACHE ***\n\n");
949
0
  _xdg_glob_hash_dump (global_hash);
950
0
  printf ("\n*** GLOBS ***\n\n");
951
0
  _xdg_glob_hash_dump (global_hash);
952
0
  printf ("\n*** GLOBS REVERSE TREE ***\n\n");
953
0
  _xdg_mime_cache_glob_dump ();
954
0
}
955
956
957
/* Registers a function to be called every time the mime database reloads its files
958
 */
959
int
960
xdg_mime_register_reload_callback (XdgMimeCallback  callback,
961
           void            *data,
962
           XdgMimeDestroy   destroy)
963
0
{
964
0
  XdgCallbackList *list_el;
965
0
  static int callback_id = 1;
966
967
  /* Make a new list element */
968
0
  list_el = calloc (1, sizeof (XdgCallbackList));
969
0
  list_el->callback_id = callback_id;
970
0
  list_el->callback = callback;
971
0
  list_el->data = data;
972
0
  list_el->destroy = destroy;
973
0
  list_el->next = callback_list;
974
0
  if (list_el->next)
975
0
    list_el->next->prev = list_el;
976
977
0
  callback_list = list_el;
978
0
  callback_id ++;
979
980
0
  return callback_id - 1;
981
0
}
982
983
void
984
xdg_mime_remove_callback (int callback_id)
985
0
{
986
0
  XdgCallbackList *list;
987
988
0
  for (list = callback_list; list; list = list->next)
989
0
    {
990
0
      if (list->callback_id == callback_id)
991
0
  {
992
0
    if (list->next)
993
0
      list->next = list->prev;
994
995
0
    if (list->prev)
996
0
      list->prev->next = list->next;
997
0
    else
998
0
      callback_list = list->next;
999
1000
    /* invoke the destroy handler */
1001
0
    (list->destroy) (list->data);
1002
0
    free (list);
1003
0
    return;
1004
0
  }
1005
0
    }
1006
0
}
1007
1008
const char *
1009
xdg_mime_get_icon (const char *mime)
1010
0
{
1011
0
  xdg_mime_init ();
1012
  
1013
0
  if (_caches)
1014
0
    return _xdg_mime_cache_get_icon (mime);
1015
1016
0
  return _xdg_mime_icon_list_lookup (icon_list, mime);
1017
0
}
1018
1019
const char *
1020
xdg_mime_get_generic_icon (const char *mime)
1021
0
{
1022
0
  xdg_mime_init ();
1023
  
1024
0
  if (_caches)
1025
0
    return _xdg_mime_cache_get_generic_icon (mime);
1026
1027
0
  return _xdg_mime_icon_list_lookup (generic_icon_list, mime);
1028
0
}