Coverage Report

Created: 2025-07-23 08:13

/src/pango/subprojects/glib/gio/xdgmime/xdgmimecache.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: C; c-file-style: "gnu" -*- */
2
/* xdgmimealias.c: Private file.  mmappable caches for mime data
3
 *
4
 * More info can be found at http://www.freedesktop.org/standards/
5
 *
6
 * Copyright (C) 2005  Matthias Clasen <mclasen@redhat.com>
7
 *
8
 * SPDX-License-Identifier: LGPL-2.1-or-later or AFL-2.0
9
 */
10
11
#ifdef HAVE_CONFIG_H
12
#include <config.h>
13
#endif
14
15
#include <stdio.h>
16
#include <stdlib.h>
17
#include <string.h>
18
19
#include <fcntl.h>
20
#include <unistd.h>
21
#include <errno.h>
22
#include <fnmatch.h>
23
#include <assert.h>
24
25
#include <netinet/in.h> /* for ntohl/ntohs */
26
27
#ifdef HAVE_MMAP
28
#include <sys/mman.h>
29
#else
30
#warning Building xdgmime without MMAP support. Binary "mime.cache" files will not be used.
31
#endif
32
33
#include <sys/stat.h>
34
#include <sys/types.h>
35
36
#include "xdgmimecache.h"
37
#include "xdgmimeint.h"
38
39
#ifndef MAX
40
0
#define MAX(a,b) ((a) > (b) ? (a) : (b))
41
#endif
42
43
#ifndef FALSE
44
#define FALSE (0)
45
#endif
46
47
#ifndef TRUE
48
#define TRUE  (!FALSE)
49
#endif
50
51
#ifndef _O_BINARY
52
0
#define _O_BINARY 0
53
#endif
54
55
#ifndef MAP_FAILED
56
#define MAP_FAILED ((void *) -1)
57
#endif
58
59
#ifndef S_ISREG
60
#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
61
#endif
62
63
0
#define MAJOR_VERSION 1
64
0
#define MINOR_VERSION_MIN 1
65
0
#define MINOR_VERSION_MAX 2
66
67
struct _XdgMimeCache
68
{
69
  int ref_count;
70
  int minor;
71
72
  size_t  size;
73
  char   *buffer;
74
};
75
76
0
#define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
77
0
#define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
78
79
// Validates that it is safe to call GET_UINT32() at
80
// cache->buffer[offset + (n * record_size)]. Ensures that offset is aligned to
81
// a 4-byte boundary, and that offset+(n*record_size) does not overflow.
82
// `record_size` values are known constants and never 0.
83
#define OUT_OF_BOUNDS(offset,n,record_size,max) \
84
0
  (((offset) & 0x3) || (offset) > (max) || (n) > ((max) - (offset)) / (record_size))
85
86
XdgMimeCache *
87
_xdg_mime_cache_ref (XdgMimeCache *cache)
88
0
{
89
0
  cache->ref_count++;
90
0
  return cache;
91
0
}
92
93
void
94
_xdg_mime_cache_unref (XdgMimeCache *cache)
95
0
{
96
0
  cache->ref_count--;
97
98
0
  if (cache->ref_count == 0)
99
0
    {
100
0
#ifdef HAVE_MMAP
101
0
      munmap (cache->buffer, cache->size);
102
0
#endif
103
0
      free (cache);
104
0
    }
105
0
}
106
107
XdgMimeCache *
108
_xdg_mime_cache_new_from_file (const char *file_name)
109
0
{
110
0
  XdgMimeCache *cache = NULL;
111
112
0
#ifdef HAVE_MMAP
113
0
  int fd = -1;
114
0
  struct stat st;
115
0
  char *buffer = NULL;
116
0
  int minor;
117
118
  /* Open the file and map it into memory */
119
0
  do {
120
0
    fd = open (file_name, O_RDONLY|_O_BINARY, 0);
121
0
  } while (fd == -1 && errno == EINTR);
122
123
0
  if (fd < 0)
124
0
    return NULL;
125
  
126
  // A valid cache must be at least 40 bytes for the header.
127
0
  if (fstat (fd, &st) < 0 || st.st_size < 40)
128
0
    goto done;
129
130
0
  buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
131
132
0
  if (buffer == MAP_FAILED)
133
0
    goto done;
134
135
0
  minor = GET_UINT16 (buffer, 2);
136
  /* Verify version */
137
0
  if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
138
0
      (minor < MINOR_VERSION_MIN ||
139
0
       minor > MINOR_VERSION_MAX))
140
0
    {
141
0
      munmap (buffer, st.st_size);
142
143
0
      goto done;
144
0
    }
145
  
146
0
  cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
147
0
  cache->minor = minor;
148
0
  cache->ref_count = 1;
149
0
  cache->buffer = buffer;
150
0
  cache->size = st.st_size;
151
152
0
 done:
153
0
  if (fd != -1)
154
0
    close (fd);
155
156
#else /* HAVE_MMAP */
157
  cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
158
  cache->minor = 0;
159
  cache->ref_count = 1;
160
  cache->buffer = NULL;
161
  cache->size = 0;
162
#endif  /* HAVE_MMAP */
163
164
0
  return cache;
165
0
}
166
167
static int
168
cache_magic_matchlet_compare_to_data (XdgMimeCache *cache, 
169
              xdg_uint32_t  offset,
170
              const void   *data,
171
              size_t        len)
172
0
{
173
0
  xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
174
0
  xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
175
0
  xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
176
0
  xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
177
0
  xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
178
  
179
0
  xdg_uint32_t i, j;
180
181
0
  for (i = range_start; i < range_start + range_length; i++)
182
0
    {
183
0
      int valid_matchlet = TRUE;
184
      
185
0
      if (i + data_length > len)
186
0
  return FALSE;
187
188
0
      if (mask_offset)
189
0
  {
190
0
    for (j = 0; j < data_length; j++)
191
0
      {
192
0
        if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) !=
193
0
      ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j]))
194
0
    {
195
0
      valid_matchlet = FALSE;
196
0
      break;
197
0
    }
198
0
      }
199
0
  }
200
0
      else
201
0
  {
202
0
    valid_matchlet = memcmp(cache->buffer + data_offset, (unsigned char *)data + i, data_length) == 0;
203
0
  }
204
205
0
      if (valid_matchlet)
206
0
  return TRUE;
207
0
    }
208
  
209
0
  return FALSE;  
210
0
}
211
212
static int
213
cache_magic_matchlet_compare (XdgMimeCache *cache, 
214
            xdg_uint32_t  offset,
215
            const void   *data,
216
            size_t        len)
217
0
{
218
0
  xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
219
0
  xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
220
0
  if (OUT_OF_BOUNDS (child_offset, n_children, 32, cache->size))
221
0
    return FALSE;
222
223
0
  xdg_uint32_t i;
224
  
225
0
  if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
226
0
    {
227
0
      if (n_children == 0)
228
0
  return TRUE;
229
      
230
0
      for (i = 0; i < n_children; i++)
231
0
  {
232
0
    if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
233
0
              data, len))
234
0
      return TRUE;
235
0
  }
236
0
    }
237
  
238
0
  return FALSE;  
239
0
}
240
241
static const char *
242
cache_magic_compare_to_data (XdgMimeCache *cache, 
243
           xdg_uint32_t  offset,
244
           const void   *data, 
245
           size_t        len, 
246
           int          *prio)
247
0
{
248
0
  xdg_uint32_t priority, mimetype_offset, n_matchlets, matchlet_offset;
249
250
0
  assert (cache->buffer != NULL);
251
252
0
  priority = GET_UINT32 (cache->buffer, offset);
253
0
  mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
254
0
  n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
255
0
  matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
256
257
0
  if (OUT_OF_BOUNDS (matchlet_offset, n_matchlets, 32, cache->size))
258
0
    return NULL;
259
260
0
  xdg_uint32_t i;
261
262
0
  for (i = 0; i < n_matchlets; i++)
263
0
    {
264
0
      if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32, 
265
0
          data, len))
266
0
  {
267
0
    *prio = priority;
268
    
269
0
    return cache->buffer + mimetype_offset;
270
0
  }
271
0
    }
272
273
0
  return NULL;
274
0
}
275
276
static const char *
277
cache_magic_lookup_data (XdgMimeCache *cache, 
278
       const void   *data, 
279
       size_t        len, 
280
       int          *prio)
281
0
{
282
0
  xdg_uint32_t list_offset;
283
0
  xdg_uint32_t n_entries;
284
0
  xdg_uint32_t offset;
285
286
0
  xdg_uint32_t j;
287
288
0
  assert (cache->buffer != NULL);
289
290
0
  *prio = 0;
291
292
0
  list_offset = GET_UINT32 (cache->buffer, 24);
293
0
  if (OUT_OF_BOUNDS (list_offset, 1, 12, cache->size))
294
0
    return NULL;
295
296
0
  n_entries = GET_UINT32 (cache->buffer, list_offset);
297
0
  offset = GET_UINT32 (cache->buffer, list_offset + 8);
298
0
  if (OUT_OF_BOUNDS (offset, n_entries, 16, cache->size))
299
0
    return NULL;
300
  
301
0
  for (j = 0; j < n_entries; j++)
302
0
    {
303
0
      const char *match;
304
305
0
      match = cache_magic_compare_to_data (cache, offset + 16 * j, 
306
0
             data, len, prio);
307
0
      if (match)
308
0
  return match;
309
0
    }
310
311
0
  return NULL;
312
0
}
313
314
static const char *
315
cache_alias_lookup (const char *alias)
316
0
{
317
0
  const char *ptr;
318
0
  int i, min, max, mid, cmp;
319
320
0
  for (i = 0; _caches[i]; i++)
321
0
    {
322
0
      XdgMimeCache *cache = _caches[i];
323
0
      xdg_uint32_t list_offset;
324
0
      xdg_uint32_t n_entries;
325
0
      xdg_uint32_t offset;
326
327
0
      if (cache->buffer == NULL)
328
0
        continue;
329
330
0
      list_offset = GET_UINT32 (cache->buffer, 4);
331
0
      if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size))
332
0
        continue;
333
334
0
      n_entries = GET_UINT32 (cache->buffer, list_offset);
335
0
      if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 8, cache->size))
336
0
        continue;
337
338
0
      min = 0; 
339
0
      max = n_entries - 1;
340
0
      while (max >= min) 
341
0
  {
342
0
    mid = (min + max) / 2;
343
344
0
    offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
345
0
    ptr = cache->buffer + offset;
346
0
    cmp = strcmp (ptr, alias);
347
    
348
0
    if (cmp < 0)
349
0
      min = mid + 1;
350
0
    else if (cmp > 0)
351
0
      max = mid - 1;
352
0
    else
353
0
      {
354
0
        offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
355
0
        return cache->buffer + offset;
356
0
      }
357
0
  }
358
0
    }
359
360
0
  return NULL;
361
0
}
362
363
typedef struct {
364
  const char *mime;
365
  int weight;
366
} MimeWeight;
367
368
static int
369
cache_glob_lookup_literal (const char *file_name,
370
         const char *mime_types[],
371
         int         n_mime_types,
372
         int         case_sensitive_check)
373
0
{
374
0
  const char *ptr;
375
0
  int i, min, max, mid, cmp;
376
377
0
  assert (n_mime_types > 0);
378
379
0
  for (i = 0; _caches[i]; i++)
380
0
    {
381
0
      XdgMimeCache *cache = _caches[i];
382
0
      xdg_uint32_t list_offset;
383
0
      xdg_uint32_t n_entries;
384
0
      xdg_uint32_t offset;
385
386
0
      if (cache->buffer == NULL)
387
0
        continue;
388
389
0
      list_offset = GET_UINT32 (cache->buffer, 12);
390
0
      if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size))
391
0
        continue;
392
393
0
      n_entries = GET_UINT32 (cache->buffer, list_offset);
394
0
      if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 12, cache->size))
395
0
        continue;
396
397
0
      min = 0; 
398
0
      max = n_entries - 1;
399
0
      while (max >= min) 
400
0
  {
401
0
    mid = (min + max) / 2;
402
403
0
    offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid);
404
0
    ptr = cache->buffer + offset;
405
0
    cmp = strcmp (ptr, file_name);
406
407
0
    if (cmp < 0)
408
0
      min = mid + 1;
409
0
    else if (cmp > 0)
410
0
      max = mid - 1;
411
0
    else
412
0
      {
413
0
        int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 8);
414
0
        int case_sensitive = weight & 0x100;
415
0
        weight = weight & 0xff;
416
417
0
        if (case_sensitive_check || !case_sensitive)
418
0
    {
419
0
      offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4);
420
0
      mime_types[0] = (const char *)(cache->buffer + offset);
421
422
0
      return 1;
423
0
    }
424
0
        return 0;
425
0
      }
426
0
  }
427
0
    }
428
429
0
  return 0;
430
0
}
431
432
static int
433
cache_glob_lookup_fnmatch (const char *file_name,
434
         MimeWeight  mime_types[],
435
         int         n_mime_types,
436
         int         case_sensitive_check)
437
0
{
438
0
  const char *mime_type;
439
0
  const char *ptr;
440
441
0
  int i, n;
442
0
  xdg_uint32_t j;
443
444
0
  n = 0;
445
0
  for (i = 0; _caches[i]; i++)
446
0
    {
447
0
      XdgMimeCache *cache = _caches[i];
448
449
0
      xdg_uint32_t list_offset;
450
0
      xdg_uint32_t n_entries;
451
452
0
      if (cache->buffer == NULL)
453
0
        continue;
454
455
0
      list_offset = GET_UINT32 (cache->buffer, 20);
456
0
      if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size))
457
0
        continue;
458
459
0
      n_entries = GET_UINT32 (cache->buffer, list_offset);
460
0
      if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 12, cache->size))
461
0
        continue;
462
463
0
      for (j = 0; j < n_entries && n < n_mime_types; j++)
464
0
  {
465
0
    xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j);
466
0
    xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4);
467
0
    int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8);
468
0
    int case_sensitive = weight & 0x100;
469
0
    weight = weight & 0xff;
470
0
    ptr = cache->buffer + offset;
471
0
    mime_type = cache->buffer + mimetype_offset;
472
0
    if (case_sensitive_check || !case_sensitive)
473
0
      {
474
        /* FIXME: Not UTF-8 safe */
475
0
        if (fnmatch (ptr, file_name, 0) == 0)
476
0
          {
477
0
            mime_types[n].mime = mime_type;
478
0
            mime_types[n].weight = weight;
479
0
            n++;
480
0
          }
481
0
      }
482
0
  }
483
484
0
      if (n == n_mime_types)
485
0
  break;
486
0
    }
487
488
0
  return n;
489
0
}
490
491
static int
492
cache_glob_node_lookup_suffix (XdgMimeCache  *cache,
493
             xdg_uint32_t   n_entries,
494
             xdg_uint32_t   offset,
495
             const char    *file_name,
496
             int            len,
497
             int            case_sensitive_check,
498
             MimeWeight     mime_types[],
499
             int            n_mime_types)
500
0
{
501
0
  xdg_unichar_t character;
502
0
  xdg_unichar_t match_char;
503
0
  xdg_uint32_t mimetype_offset;
504
0
  xdg_uint32_t n_children;
505
0
  xdg_uint32_t child_offset; 
506
0
  int weight;
507
0
  int case_sensitive;
508
509
0
  xdg_uint32_t i;
510
0
  int min, max, mid, n;
511
512
0
  character = file_name[len - 1];
513
514
0
  assert (character != 0);
515
516
0
  min = 0;
517
0
  max = n_entries - 1;
518
0
  while (max >= min)
519
0
    {
520
0
      mid = (min + max) /  2;
521
0
      match_char = GET_UINT32 (cache->buffer, offset + 12 * mid);
522
0
      if (match_char < character)
523
0
  min = mid + 1;
524
0
      else if (match_char > character)
525
0
  max = mid - 1;
526
0
      else 
527
0
  {
528
0
          len--;
529
0
          n = 0;
530
0
          n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4);
531
0
          child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8);
532
0
          if (OUT_OF_BOUNDS (child_offset, n_children, 12, cache->size))
533
0
            continue;
534
      
535
0
          if (len > 0)
536
0
            {
537
0
              n = cache_glob_node_lookup_suffix (cache, 
538
0
                                                 n_children, child_offset,
539
0
                                                 file_name, len, 
540
0
                                                 case_sensitive_check,
541
0
                                                 mime_types,
542
0
                                                 n_mime_types);
543
0
            }
544
0
          if (n == 0)
545
0
            {
546
0
        i = 0;
547
0
        while (n < n_mime_types && i < n_children)
548
0
    {
549
0
      match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i);
550
0
      if (match_char != 0)
551
0
        break;
552
553
0
      mimetype_offset = GET_UINT32 (cache->buffer, child_offset + 12 * i + 4);
554
0
      weight = GET_UINT32 (cache->buffer, child_offset + 12 * i + 8);
555
0
      case_sensitive = weight & 0x100;
556
0
      weight = weight & 0xff;
557
558
0
      if (case_sensitive_check || !case_sensitive)
559
0
        {
560
0
          mime_types[n].mime = cache->buffer + mimetype_offset;
561
0
          mime_types[n].weight = weight;
562
0
          n++;
563
0
        }
564
0
      i++;
565
0
    }
566
0
      }
567
0
    return n;
568
0
  }
569
0
    }
570
0
  return 0;
571
0
}
572
573
static int
574
cache_glob_lookup_suffix (const char *file_name,
575
        int         len,
576
        int         ignore_case,
577
        MimeWeight  mime_types[],
578
        int         n_mime_types)
579
0
{
580
0
  int i, n;
581
582
0
  n = 0;
583
0
  for (i = 0; _caches[i]; i++)
584
0
    {
585
0
      XdgMimeCache *cache = _caches[i];
586
587
0
      xdg_uint32_t list_offset;
588
0
      xdg_uint32_t n_entries;
589
0
      xdg_uint32_t offset;
590
591
0
      if (cache->buffer == NULL)
592
0
        continue;
593
594
0
      list_offset = GET_UINT32 (cache->buffer, 16);
595
0
      if (OUT_OF_BOUNDS (list_offset, 1, 8, cache->size))
596
0
        continue;
597
598
0
      n_entries = GET_UINT32 (cache->buffer, list_offset);
599
0
      offset = GET_UINT32 (cache->buffer, list_offset + 4);
600
0
      if (OUT_OF_BOUNDS (offset, n_entries, 12, cache->size))
601
0
        continue;
602
603
0
      n += cache_glob_node_lookup_suffix (cache,
604
0
            n_entries, offset,
605
0
            file_name, len,
606
0
            ignore_case,
607
0
            mime_types + n,
608
0
            n_mime_types - n);
609
0
      if (n == n_mime_types)
610
0
  break;
611
0
    }
612
613
0
  return n;
614
0
}
615
616
static int compare_mime_weight (const void *a, const void *b)
617
0
{
618
0
  const MimeWeight *aa = (const MimeWeight *)a;
619
0
  const MimeWeight *bb = (const MimeWeight *)b;
620
621
0
  return bb->weight - aa->weight;
622
0
}
623
624
0
#define ISUPPER(c)    ((c) >= 'A' && (c) <= 'Z')
625
static char *
626
ascii_tolower (const char *str)
627
0
{
628
0
  char *p, *lower;
629
630
0
  lower = strdup (str);
631
0
  p = lower;
632
0
  while (*p != 0)
633
0
    {
634
0
      char c = *p;
635
0
      *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
636
0
    }
637
0
  return lower;
638
0
}
639
640
static int
641
filter_out_dupes (MimeWeight mimes[], int n_mimes)
642
0
{
643
0
  int last;
644
0
  int i, j;
645
646
0
  last = n_mimes;
647
648
0
  for (i = 0; i < last; i++)
649
0
    {
650
0
      j = i + 1;
651
0
      while (j < last)
652
0
        {
653
0
          if (strcmp (mimes[i].mime, mimes[j].mime) == 0)
654
0
            {
655
0
              mimes[i].weight = MAX (mimes[i].weight, mimes[j].weight);
656
0
              last--;
657
0
              mimes[j].mime = mimes[last].mime;
658
0
              mimes[j].weight = mimes[last].weight;
659
0
            }
660
0
          else
661
0
            j++;
662
0
        }
663
0
    }
664
665
0
  return last;
666
0
}
667
668
static int
669
cache_glob_lookup_file_name (const char *file_name,
670
           const char *mime_types[],
671
           int         n_mime_types)
672
0
{
673
0
  int n;
674
0
  MimeWeight mimes[10];
675
0
  int n_mimes = 10;
676
0
  int i;
677
0
  int len;
678
0
  char *lower_case;
679
680
0
  assert (file_name != NULL && n_mime_types > 0);
681
682
  /* First, check the literals */
683
684
0
  lower_case = ascii_tolower (file_name);
685
686
0
  n = cache_glob_lookup_literal (lower_case, mime_types, n_mime_types, FALSE);
687
0
  if (n > 0)
688
0
    {
689
0
      free (lower_case);
690
0
      return n;
691
0
    }
692
693
0
  n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types, TRUE);
694
0
  if (n > 0)
695
0
    {
696
0
      free (lower_case);
697
0
      return n;
698
0
    }
699
700
0
  len = strlen (file_name);
701
0
  n = cache_glob_lookup_suffix (lower_case, len, FALSE, mimes, n_mimes);
702
0
  if (n < 2)
703
0
    n += cache_glob_lookup_suffix (file_name, len, TRUE, mimes + n, n_mimes - n);
704
705
  /* Last, try fnmatch */
706
0
  if (n == 0)
707
0
    n = cache_glob_lookup_fnmatch (lower_case, mimes, n_mimes, FALSE);
708
0
  if (n < 2)
709
0
    n += cache_glob_lookup_fnmatch (file_name, mimes + n, n_mimes - n, TRUE);
710
711
0
  n = filter_out_dupes (mimes, n);
712
713
0
  free (lower_case);
714
715
0
  qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
716
717
0
  if (n_mime_types < n)
718
0
    n = n_mime_types;
719
720
0
  for (i = 0; i < n; i++)
721
0
    mime_types[i] = mimes[i].mime;
722
723
0
  return n;
724
0
}
725
726
int
727
_xdg_mime_cache_get_max_buffer_extents (void)
728
0
{
729
0
  xdg_uint32_t offset;
730
0
  xdg_uint32_t max_extent;
731
0
  int i;
732
733
0
  max_extent = 0;
734
0
  for (i = 0; _caches[i]; i++)
735
0
    {
736
0
      XdgMimeCache *cache = _caches[i];
737
738
0
      if (cache->buffer == NULL)
739
0
        continue;
740
741
0
      offset = GET_UINT32 (cache->buffer, 24);
742
0
      if (OUT_OF_BOUNDS (offset, 1, 8, cache->size))
743
0
        continue;
744
745
0
      max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
746
0
    }
747
748
0
  return max_extent;
749
0
}
750
751
static const char *
752
cache_get_mime_type_for_data (const void *data,
753
            size_t      len,
754
            int        *result_prio,
755
            const char *mime_types[],
756
            int         n_mime_types)
757
0
{
758
0
  const char *mime_type;
759
0
  int i, n, priority;
760
761
0
  priority = 0;
762
0
  mime_type = NULL;
763
0
  for (i = 0; _caches[i]; i++)
764
0
    {
765
0
      XdgMimeCache *cache = _caches[i];
766
767
0
      int prio;
768
0
      const char *match;
769
770
0
      if (cache->buffer == NULL)
771
0
        continue;
772
773
0
      match = cache_magic_lookup_data (cache, data, len, &prio);
774
0
      if (prio > priority)
775
0
  {
776
0
    priority = prio;
777
0
    mime_type = match;
778
0
  }
779
0
    }
780
781
0
  if (result_prio)
782
0
    *result_prio = priority;
783
784
0
  if (priority > 0)
785
0
    {
786
      /* Pick glob-result R where mime_type inherits from R */
787
0
      for (n = 0; n < n_mime_types; n++)
788
0
        {
789
0
          if (mime_types[n] && _xdg_mime_cache_mime_type_subclass (mime_types[n], mime_type, NULL))
790
0
            return mime_types[n];
791
0
        }
792
0
      if (n == 0)
793
0
        {
794
          /* No globs: return magic match */
795
0
          return mime_type;
796
0
        }
797
0
    }
798
799
  /* Pick first glob result, as fallback */
800
0
  for (n = 0; n < n_mime_types; n++)
801
0
    {
802
0
      if (mime_types[n])
803
0
        return mime_types[n];
804
0
    }
805
806
0
  return NULL;
807
0
}
808
809
const char *
810
_xdg_mime_cache_get_mime_type_for_data (const void *data,
811
          size_t      len,
812
          int        *result_prio)
813
0
{
814
0
  return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
815
0
}
816
817
const char *
818
_xdg_mime_cache_get_mime_type_for_file (const char  *file_name,
819
          struct stat *statbuf)
820
0
{
821
0
  const char *mime_type;
822
0
  const char *mime_types[10];
823
0
  FILE *file;
824
0
  unsigned char *data;
825
0
  int max_extent;
826
0
  int bytes_read;
827
0
  struct stat buf;
828
0
  const char *base_name;
829
0
  int n;
830
831
0
  if (file_name == NULL)
832
0
    return NULL;
833
834
0
  if (! _xdg_utf8_validate (file_name))
835
0
    return NULL;
836
837
0
  base_name = _xdg_get_base_name (file_name);
838
0
  n = cache_glob_lookup_file_name (base_name, mime_types, 10);
839
840
0
  if (n == 1)
841
0
    return mime_types[0];
842
843
0
  if (!statbuf)
844
0
    {
845
0
      if (stat (file_name, &buf) != 0)
846
0
  return XDG_MIME_TYPE_UNKNOWN;
847
848
0
      statbuf = &buf;
849
0
    }
850
851
0
  if (statbuf->st_size == 0)
852
0
    return XDG_MIME_TYPE_EMPTY;
853
854
0
  if (!S_ISREG (statbuf->st_mode))
855
0
    return XDG_MIME_TYPE_UNKNOWN;
856
857
  /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
858
   * be large and need getting from a stream instead of just reading it all
859
   * in. */
860
0
  max_extent = _xdg_mime_cache_get_max_buffer_extents ();
861
0
  data = malloc (max_extent);
862
0
  if (data == NULL)
863
0
    return XDG_MIME_TYPE_UNKNOWN;
864
        
865
0
  file = fopen (file_name, "r");
866
0
  if (file == NULL)
867
0
    {
868
0
      free (data);
869
0
      return XDG_MIME_TYPE_UNKNOWN;
870
0
    }
871
872
0
  bytes_read = fread (data, 1, max_extent, file);
873
0
  if (ferror (file))
874
0
    {
875
0
      free (data);
876
0
      fclose (file);
877
0
      return XDG_MIME_TYPE_UNKNOWN;
878
0
    }
879
880
0
  mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
881
0
              mime_types, n);
882
883
0
  if (!mime_type)
884
0
    mime_type = _xdg_binary_or_text_fallback (data, bytes_read);
885
886
0
  free (data);
887
0
  fclose (file);
888
889
0
  return mime_type;
890
0
}
891
892
const char *
893
_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
894
0
{
895
0
  const char *mime_type;
896
897
0
  if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
898
0
    return mime_type;
899
0
  else
900
0
    return XDG_MIME_TYPE_UNKNOWN;
901
0
}
902
903
int
904
_xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
905
                 const char  *mime_types[],
906
                 int          n_mime_types)
907
0
{
908
0
  return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
909
0
}
910
911
#if 1
912
static int
913
ends_with (const char *str,
914
           const char *suffix)
915
0
{
916
0
  int length;
917
0
  int suffix_length;
918
919
0
  length = strlen (str);
920
0
  suffix_length = strlen (suffix);
921
0
  if (length < suffix_length)
922
0
    return 0;
923
924
0
  if (strcmp (str + length - suffix_length, suffix) == 0)
925
0
    return 1;
926
927
0
  return 0;
928
0
}
929
930
static int
931
is_super_type (const char *mime)
932
0
{
933
0
  return ends_with (mime, "/*");
934
0
}
935
#endif
936
937
int
938
_xdg_mime_cache_mime_type_subclass (const char *mime,
939
            const char *base,
940
            const char ***seen)
941
0
{
942
0
  const char *umime, *ubase, *parent;
943
0
  const char **first_seen = NULL, **new_seen;
944
945
0
  xdg_uint32_t j;
946
0
  int i, k, min, max, med, cmp, ret = 0;
947
948
0
  umime = _xdg_mime_cache_unalias_mime_type (mime);
949
0
  ubase = _xdg_mime_cache_unalias_mime_type (base);
950
951
0
  if (strcmp (umime, ubase) == 0)
952
0
    return 1;
953
954
  /* We really want to handle text/ * in GtkFileFilter, so we just
955
   * turn on the supertype matching
956
   */
957
0
#if 1
958
  /* Handle supertypes */
959
0
  if (is_super_type (ubase) &&
960
0
      xdg_mime_media_type_equal (umime, ubase))
961
0
    return 1;
962
0
#endif
963
964
  /*  Handle special cases text/plain and application/octet-stream */
965
0
  if (strcmp (ubase, "text/plain") == 0 && 
966
0
      strncmp (umime, "text/", 5) == 0)
967
0
    return 1;
968
969
0
  if (strcmp (ubase, "application/octet-stream") == 0 &&
970
0
      strncmp (umime, "inode/", 6) != 0)
971
0
    return 1;
972
973
0
  if (!seen)
974
0
    {
975
0
      first_seen = calloc (1, sizeof (char *));
976
0
      seen = &first_seen;
977
0
    }
978
979
0
  for (i = 0; _caches[i]; i++)
980
0
    {
981
0
      XdgMimeCache *cache = _caches[i];
982
0
      xdg_uint32_t list_offset;
983
0
      xdg_uint32_t n_entries;
984
0
      xdg_uint32_t offset, n_parents, parent_offset;
985
986
0
      if (cache->buffer == NULL)
987
0
        continue;
988
989
0
      list_offset = GET_UINT32 (cache->buffer, 8);
990
0
      if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size))
991
0
        continue;
992
993
0
      n_entries = GET_UINT32 (cache->buffer, list_offset);
994
0
      if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 8, cache->size))
995
0
        continue;
996
997
0
      min = 0; 
998
0
      max = n_entries - 1;
999
0
      while (max >= min)
1000
0
  {
1001
0
    med = (min + max)/2;
1002
    
1003
0
    offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
1004
0
    cmp = strcmp (cache->buffer + offset, umime);
1005
0
    if (cmp < 0)
1006
0
      min = med + 1;
1007
0
    else if (cmp > 0)
1008
0
      max = med - 1;
1009
0
    else
1010
0
      {
1011
0
        offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
1012
0
        n_parents = GET_UINT32 (cache->buffer, offset);
1013
        
1014
0
        for (j = 0; j < n_parents; j++)
1015
0
    {
1016
0
      parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
1017
0
      parent = cache->buffer + parent_offset;
1018
1019
      /* Detect and avoid buggy circular relationships */
1020
0
      for (k = 0; (*seen)[k] != NULL; k++)
1021
0
        if (parent == (*seen)[k])
1022
0
          goto next_parent;
1023
0
      new_seen = realloc (*seen, (k + 2) * sizeof (char *));
1024
0
      if (!new_seen)
1025
0
        goto done;
1026
0
      new_seen[k] = parent;
1027
0
      new_seen[k + 1] = NULL;
1028
0
      *seen = new_seen;
1029
1030
0
      if (_xdg_mime_cache_mime_type_subclass (parent, ubase, seen))
1031
0
        {
1032
0
          ret = 1;
1033
0
          goto done;
1034
0
        }
1035
1036
0
    next_parent:
1037
0
      continue;
1038
0
    }
1039
1040
0
        break;
1041
0
      }
1042
0
  }
1043
0
    }
1044
1045
0
done:
1046
0
  free (first_seen);
1047
0
  return ret;
1048
0
}
1049
1050
const char *
1051
_xdg_mime_cache_unalias_mime_type (const char *mime)
1052
0
{
1053
0
  const char *lookup;
1054
  
1055
0
  lookup = cache_alias_lookup (mime);
1056
  
1057
0
  if (lookup)
1058
0
    return lookup;
1059
  
1060
0
  return mime;  
1061
0
}
1062
1063
char **
1064
_xdg_mime_cache_list_mime_parents (const char *mime)
1065
0
{
1066
0
  int i, l, p;
1067
0
  xdg_uint32_t j, k;
1068
0
  char *all_parents[128]; /* we'll stop at 128 */ 
1069
0
  char **result;
1070
1071
0
  mime = xdg_mime_unalias_mime_type (mime);
1072
1073
0
  p = 0;
1074
0
  for (i = 0; _caches[i]; i++)
1075
0
    {
1076
0
      XdgMimeCache *cache = _caches[i];
1077
0
      xdg_uint32_t list_offset;
1078
0
      xdg_uint32_t n_entries;
1079
1080
0
      if (cache->buffer == NULL)
1081
0
        continue;
1082
1083
0
      list_offset = GET_UINT32 (cache->buffer, 8);
1084
0
      if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size))
1085
0
        continue;
1086
1087
0
      n_entries = GET_UINT32 (cache->buffer, list_offset);
1088
0
      if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 8, cache->size))
1089
0
        continue;
1090
1091
0
      for (j = 0; j < n_entries; j++)
1092
0
  {
1093
0
    xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
1094
0
    xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
1095
1096
0
    if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
1097
0
      {
1098
0
        xdg_uint32_t parent_mime_offset;
1099
0
        xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
1100
1101
0
        for (k = 0; k < n_parents && p < 127; k++)
1102
0
    {
1103
0
      parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
1104
1105
      /* Don't add same parent multiple times.
1106
       * This can happen for instance if the same type is listed in multiple directories
1107
       */
1108
0
      for (l = 0; l < p; l++)
1109
0
        {
1110
0
          if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0)
1111
0
      break;
1112
0
        }
1113
1114
0
      if (l == p)
1115
0
        all_parents[p++] = cache->buffer + parent_mime_offset;
1116
0
    }
1117
1118
0
        break;
1119
0
      }
1120
0
  }
1121
0
    }
1122
0
  all_parents[p++] = NULL;
1123
  
1124
0
  result = (char **) malloc (p * sizeof (char *));
1125
0
  memcpy (result, all_parents, p * sizeof (char *));
1126
1127
0
  return result;
1128
0
}
1129
1130
static const char *
1131
cache_lookup_icon (const char *mime, size_t header)
1132
0
{
1133
0
  const char *ptr;
1134
0
  int i, min, max, mid, cmp;
1135
1136
0
  for (i = 0; _caches[i]; i++)
1137
0
    {
1138
0
      XdgMimeCache *cache = _caches[i];
1139
0
      xdg_uint32_t list_offset;
1140
0
      xdg_uint32_t n_entries;
1141
0
      xdg_uint32_t offset;
1142
1143
0
      if (cache->buffer == NULL)
1144
0
        continue;
1145
1146
0
      if (OUT_OF_BOUNDS (header, 1, 4, cache->size))
1147
0
        continue;
1148
1149
0
      list_offset = GET_UINT32 (cache->buffer, header);
1150
0
      if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size))
1151
0
        continue;
1152
1153
0
      n_entries = GET_UINT32 (cache->buffer, list_offset);
1154
0
      if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 8, cache->size))
1155
0
        continue;
1156
1157
0
      min = 0; 
1158
0
      max = n_entries - 1;
1159
0
      while (max >= min) 
1160
0
        {
1161
0
          mid = (min + max) / 2;
1162
1163
0
          offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
1164
0
          ptr = cache->buffer + offset;
1165
0
          cmp = strcmp (ptr, mime);
1166
         
1167
0
          if (cmp < 0)
1168
0
            min = mid + 1;
1169
0
          else if (cmp > 0)
1170
0
            max = mid - 1;
1171
0
          else
1172
0
            {
1173
0
              offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
1174
0
              return cache->buffer + offset;
1175
0
            }
1176
0
        }
1177
0
    }
1178
1179
0
  return NULL;
1180
0
}
1181
1182
const char *
1183
_xdg_mime_cache_get_generic_icon (const char *mime)
1184
0
{
1185
0
  return cache_lookup_icon (mime, 36);
1186
0
}
1187
1188
const char *
1189
_xdg_mime_cache_get_icon (const char *mime)
1190
0
{
1191
0
  return cache_lookup_icon (mime, 32);
1192
0
}
1193
1194
static void
1195
dump_glob_node (XdgMimeCache *cache,
1196
    xdg_uint32_t  offset,
1197
    int           depth)
1198
0
{
1199
0
  xdg_unichar_t character;
1200
0
  xdg_uint32_t mime_offset;
1201
0
  xdg_uint32_t n_children;
1202
0
  xdg_uint32_t child_offset;
1203
0
  xdg_uint32_t k;
1204
0
  int i;
1205
1206
0
  character = GET_UINT32 (cache->buffer, offset);
1207
0
  mime_offset = GET_UINT32 (cache->buffer, offset + 4);
1208
0
  n_children = GET_UINT32 (cache->buffer, offset + 8);
1209
0
  child_offset = GET_UINT32 (cache->buffer, offset + 12);
1210
0
  if (OUT_OF_BOUNDS (child_offset, n_children, 20, cache->size))
1211
0
    return;
1212
1213
0
  for (i = 0; i < depth; i++)
1214
0
    printf (" ");
1215
0
  printf ("%c", character);
1216
0
  if (mime_offset)
1217
0
    printf (" - %s", cache->buffer + mime_offset);
1218
0
  printf ("\n");
1219
0
  if (child_offset)
1220
0
  {
1221
0
    for (k = 0; k < n_children; k++)
1222
0
      dump_glob_node (cache, child_offset + 20 * k, depth + 1);
1223
0
  }
1224
0
}
1225
1226
void
1227
_xdg_mime_cache_glob_dump (void)
1228
0
{
1229
0
  xdg_uint32_t i, j;
1230
0
  for (i = 0; _caches[i]; i++)
1231
0
  {
1232
0
    XdgMimeCache *cache = _caches[i];
1233
0
    xdg_uint32_t list_offset;
1234
0
    xdg_uint32_t n_entries;
1235
0
    xdg_uint32_t offset;
1236
1237
0
    if (cache->buffer == NULL)
1238
0
      continue;
1239
1240
0
    list_offset = GET_UINT32 (cache->buffer, 16);
1241
0
    if (OUT_OF_BOUNDS (list_offset, 1, 8, cache->size))
1242
0
      return;
1243
1244
0
    n_entries = GET_UINT32 (cache->buffer, list_offset);
1245
0
    offset = GET_UINT32 (cache->buffer, list_offset + 4);
1246
0
    if (OUT_OF_BOUNDS (offset, n_entries, 20, cache->size))
1247
0
      return;
1248
1249
0
    for (j = 0; j < n_entries; j++)
1250
0
      dump_glob_node (cache, offset + 20 * j, 0);
1251
0
  }
1252
0
}
1253
1254