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