1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis
3  * Copyright (C) 1997 Josh MacDonald
4  *
5  * gimppluginmanager-file-procedure.c
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <errno.h>
24 #include <stdlib.h>
25 
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27 #include <gegl.h>
28 
29 #include "libgimpbase/gimpbase.h"
30 
31 #include "plug-in-types.h"
32 
33 #include "core/gimp-utils.h"
34 
35 #include "gimppluginmanager-file-procedure.h"
36 #include "gimppluginprocedure.h"
37 
38 #include "gimp-log.h"
39 
40 #include "gimp-intl.h"
41 
42 
43 typedef enum
44 {
45   /*  positive values indicate the length of a matching magic  */
46 
47   FILE_MATCH_NONE = 0,
48   FILE_MATCH_SIZE = -1
49 } FileMatchType;
50 
51 
52 /*  local function prototypes  */
53 
54 static GimpPlugInProcedure * file_proc_find_by_prefix    (GSList       *procs,
55                                                           GFile        *file,
56                                                           gboolean      skip_magic);
57 static GimpPlugInProcedure * file_proc_find_by_extension (GSList       *procs,
58                                                           GFile        *file,
59                                                           gboolean      skip_magic);
60 static GimpPlugInProcedure * file_proc_find_by_name      (GSList       *procs,
61                                                           GFile        *file,
62                                                           gboolean      skip_magic);
63 
64 static void                  file_convert_string         (const gchar  *instr,
65                                                           gchar        *outmem,
66                                                           gint          maxmem,
67                                                           gint         *nmem);
68 static FileMatchType         file_check_single_magic     (const gchar  *offset,
69                                                           const gchar  *type,
70                                                           const gchar  *value,
71                                                           const guchar *file_head,
72                                                           gint          headsize,
73                                                           GFile        *file,
74                                                           GInputStream *input);
75 static FileMatchType         file_check_magic_list       (GSList       *magics_list,
76                                                           const guchar *head,
77                                                           gint          headsize,
78                                                           GFile        *file,
79                                                           GInputStream *input);
80 
81 
82 /*  public functions  */
83 
84 GimpPlugInProcedure *
file_procedure_find(GSList * procs,GFile * file,GError ** error)85 file_procedure_find (GSList  *procs,
86                      GFile   *file,
87                      GError **error)
88 {
89   GimpPlugInProcedure *file_proc;
90   GimpPlugInProcedure *size_matched_proc = NULL;
91   gint                 size_match_count  = 0;
92 
93   g_return_val_if_fail (procs != NULL, NULL);
94   g_return_val_if_fail (G_IS_FILE (file), NULL);
95   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
96 
97   /* First, check magicless prefixes/suffixes */
98   file_proc = file_proc_find_by_name (procs, file, TRUE);
99 
100   if (file_proc)
101     return file_proc;
102 
103   /* Then look for magics, but not on remote files */
104   if (g_file_is_native (file))
105     {
106       GSList              *list;
107       GInputStream        *input     = NULL;
108       gboolean             opened    = FALSE;
109       gsize                head_size = 0;
110       guchar               head[256];
111       FileMatchType        best_match_val = FILE_MATCH_NONE;
112       GimpPlugInProcedure *best_file_proc = NULL;
113 
114       for (list = procs; list; list = g_slist_next (list))
115         {
116           file_proc = list->data;
117 
118           if (file_proc->magics_list)
119             {
120               if (G_UNLIKELY (! opened))
121                 {
122                   input = G_INPUT_STREAM (g_file_read (file, NULL, error));
123 
124                   if (input)
125                     {
126                       g_input_stream_read_all (input,
127                                                head, sizeof (head),
128                                                &head_size, NULL, error);
129 
130                       if (head_size < 4)
131                         {
132                           g_object_unref (input);
133                           input = NULL;
134                         }
135                       else
136                         {
137                           GDataInputStream *data_input;
138 
139                           data_input = g_data_input_stream_new (input);
140                           g_object_unref (input);
141                           input = G_INPUT_STREAM (data_input);
142                         }
143                     }
144 
145                   opened = TRUE;
146                 }
147 
148               if (head_size >= 4)
149                 {
150                   FileMatchType match_val;
151 
152                   match_val = file_check_magic_list (file_proc->magics_list,
153                                                      head, head_size,
154                                                      file, input);
155 
156                   if (match_val == FILE_MATCH_SIZE)
157                     {
158                       /* Use it only if no other magic matches */
159                       size_match_count++;
160                       size_matched_proc = file_proc;
161                     }
162                   else if (match_val != FILE_MATCH_NONE)
163                     {
164                       GIMP_LOG (MAGIC_MATCH,
165                                 "magic match %d on %s\n",
166                                 match_val,
167                                 gimp_object_get_name (file_proc));
168 
169                       if (match_val > best_match_val)
170                         {
171                           best_match_val = match_val;
172                           best_file_proc = file_proc;
173                         }
174                     }
175                 }
176             }
177         }
178 
179       if (input)
180         g_object_unref (input);
181 
182       if (best_file_proc)
183         {
184           GIMP_LOG (MAGIC_MATCH,
185                     "best magic match on %s\n",
186                     gimp_object_get_name (best_file_proc));
187 
188           return best_file_proc;
189         }
190     }
191 
192   if (size_match_count == 1)
193     return size_matched_proc;
194 
195   /* As a last resort, try matching by name, not skipping magic procs */
196   file_proc = file_proc_find_by_name (procs, file, FALSE);
197 
198   if (file_proc)
199     {
200       /* we found a procedure, clear error that might have been set */
201       g_clear_error (error);
202     }
203   else
204     {
205       /* set an error message unless one was already set */
206       if (error && *error == NULL)
207         g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
208                              _("Unknown file type"));
209     }
210 
211   return file_proc;
212 }
213 
214 GimpPlugInProcedure *
file_procedure_find_by_prefix(GSList * procs,GFile * file)215 file_procedure_find_by_prefix (GSList *procs,
216                                GFile  *file)
217 {
218   g_return_val_if_fail (G_IS_FILE (file), NULL);
219 
220   return file_proc_find_by_prefix (procs, file, FALSE);
221 }
222 
223 GimpPlugInProcedure *
file_procedure_find_by_extension(GSList * procs,GFile * file)224 file_procedure_find_by_extension (GSList *procs,
225                                   GFile  *file)
226 {
227   g_return_val_if_fail (G_IS_FILE (file), NULL);
228 
229   return file_proc_find_by_extension (procs, file, FALSE);
230 }
231 
232 GimpPlugInProcedure *
file_procedure_find_by_mime_type(GSList * procs,const gchar * mime_type)233 file_procedure_find_by_mime_type (GSList      *procs,
234                                   const gchar *mime_type)
235 {
236   GSList *list;
237 
238   g_return_val_if_fail (mime_type != NULL, NULL);
239 
240   for (list = procs; list; list = g_slist_next (list))
241     {
242       GimpPlugInProcedure *proc = list->data;
243       GSList              *mime;
244 
245       for (mime = proc->mime_types_list; mime; mime = g_slist_next (mime))
246         {
247           if (! strcmp (mime_type, mime->data))
248             return proc;
249         }
250     }
251 
252   return NULL;
253 }
254 
255 
256 /*  private functions  */
257 
258 static GimpPlugInProcedure *
file_proc_find_by_prefix(GSList * procs,GFile * file,gboolean skip_magic)259 file_proc_find_by_prefix (GSList   *procs,
260                           GFile    *file,
261                           gboolean  skip_magic)
262 {
263   gchar  *uri = g_file_get_uri (file);
264   GSList *p;
265 
266   for (p = procs; p; p = g_slist_next (p))
267     {
268       GimpPlugInProcedure *proc = p->data;
269       GSList              *prefixes;
270 
271       if (skip_magic && proc->magics_list)
272         continue;
273 
274       for (prefixes = proc->prefixes_list;
275            prefixes;
276            prefixes = g_slist_next (prefixes))
277         {
278           if (g_str_has_prefix (uri, prefixes->data))
279             {
280               g_free (uri);
281               return proc;
282             }
283         }
284      }
285 
286   g_free (uri);
287 
288   return NULL;
289 }
290 
291 static GimpPlugInProcedure *
file_proc_find_by_extension(GSList * procs,GFile * file,gboolean skip_magic)292 file_proc_find_by_extension (GSList   *procs,
293                              GFile    *file,
294                              gboolean  skip_magic)
295 {
296   gchar *ext = gimp_file_get_extension (file);
297 
298   if (ext)
299     {
300       GSList *p;
301 
302       for (p = procs; p; p = g_slist_next (p))
303         {
304           GimpPlugInProcedure *proc = p->data;
305 
306           if (skip_magic && proc->magics_list)
307             continue;
308 
309           if (g_slist_find_custom (proc->extensions_list,
310                                    ext + 1,
311                                    (GCompareFunc) g_ascii_strcasecmp))
312             {
313               g_free (ext);
314 
315               return proc;
316             }
317         }
318 
319       g_free (ext);
320     }
321 
322   return NULL;
323 }
324 
325 static GimpPlugInProcedure *
file_proc_find_by_name(GSList * procs,GFile * file,gboolean skip_magic)326 file_proc_find_by_name (GSList   *procs,
327                         GFile    *file,
328                         gboolean  skip_magic)
329 {
330   GimpPlugInProcedure *proc;
331 
332   proc = file_proc_find_by_prefix (procs, file, skip_magic);
333 
334   if (! proc)
335     proc = file_proc_find_by_extension (procs, file, skip_magic);
336 
337   return proc;
338 }
339 
340 static void
file_convert_string(const gchar * instr,gchar * outmem,gint maxmem,gint * nmem)341 file_convert_string (const gchar *instr,
342                      gchar       *outmem,
343                      gint         maxmem,
344                      gint        *nmem)
345 {
346   /* Convert a string in C-notation to array of char */
347   const guchar *uin  = (const guchar *) instr;
348   guchar       *uout = (guchar *) outmem;
349   guchar        tmp[5], *tmpptr;
350   guint         k;
351 
352   while ((*uin != '\0') && ((((gchar *) uout) - outmem) < maxmem))
353     {
354       if (*uin != '\\')   /* Not an escaped character ? */
355         {
356           *(uout++) = *(uin++);
357           continue;
358         }
359 
360       if (*(++uin) == '\0')
361         {
362           *(uout++) = '\\';
363           break;
364         }
365 
366       switch (*uin)
367         {
368           case '0':  case '1':  case '2':  case '3': /* octal */
369             for (tmpptr = tmp; (tmpptr - tmp) <= 3;)
370               {
371                 *(tmpptr++) = *(uin++);
372                 if (   (*uin == '\0') || (!g_ascii_isdigit (*uin))
373                     || (*uin == '8') || (*uin == '9'))
374                   break;
375               }
376 
377             *tmpptr = '\0';
378             sscanf ((gchar *) tmp, "%o", &k);
379             *(uout++) = k;
380             break;
381 
382           case 'a': *(uout++) = '\a'; uin++; break;
383           case 'b': *(uout++) = '\b'; uin++; break;
384           case 't': *(uout++) = '\t'; uin++; break;
385           case 'n': *(uout++) = '\n'; uin++; break;
386           case 'v': *(uout++) = '\v'; uin++; break;
387           case 'f': *(uout++) = '\f'; uin++; break;
388           case 'r': *(uout++) = '\r'; uin++; break;
389 
390           default : *(uout++) = *(uin++); break;
391         }
392     }
393 
394   *nmem = ((gchar *) uout) - outmem;
395 }
396 
397 static FileMatchType
file_check_single_magic(const gchar * offset,const gchar * type,const gchar * value,const guchar * file_head,gint headsize,GFile * file,GInputStream * input)398 file_check_single_magic (const gchar  *offset,
399                          const gchar  *type,
400                          const gchar  *value,
401                          const guchar *file_head,
402                          gint          headsize,
403                          GFile        *file,
404                          GInputStream *input)
405 
406 {
407   FileMatchType found = FILE_MATCH_NONE;
408   glong         offs;
409   gulong        num_testval;
410   gulong        num_operator_val;
411   gint          numbytes, k;
412   const gchar  *num_operator_ptr;
413   gchar         num_operator;
414 
415   /* Check offset */
416   if (sscanf (offset, "%ld", &offs) != 1)
417     return FILE_MATCH_NONE;
418 
419   /* Check type of test */
420   num_operator_ptr = NULL;
421   num_operator     = '\0';
422 
423   if (g_str_has_prefix (type, "byte"))
424     {
425       numbytes = 1;
426       num_operator_ptr = type + strlen ("byte");
427     }
428   else if (g_str_has_prefix (type, "short"))
429     {
430       numbytes = 2;
431       num_operator_ptr = type + strlen ("short");
432     }
433   else if (g_str_has_prefix (type, "long"))
434     {
435       numbytes = 4;
436       num_operator_ptr = type + strlen ("long");
437     }
438   else if (g_str_has_prefix (type, "size"))
439     {
440       numbytes = 5;
441     }
442   else if (strcmp (type, "string") == 0)
443     {
444       numbytes = 0;
445     }
446   else
447     {
448       return FILE_MATCH_NONE;
449     }
450 
451   /* Check numerical operator value if present */
452   if (num_operator_ptr && (*num_operator_ptr == '&'))
453     {
454       if (g_ascii_isdigit (num_operator_ptr[1]))
455         {
456           if (num_operator_ptr[1] != '0')      /* decimal */
457             sscanf (num_operator_ptr+1, "%lu", &num_operator_val);
458           else if (num_operator_ptr[2] == 'x') /* hexadecimal */
459             sscanf (num_operator_ptr+3, "%lx", &num_operator_val);
460           else                                 /* octal */
461             sscanf (num_operator_ptr+2, "%lo", &num_operator_val);
462 
463           num_operator = *num_operator_ptr;
464         }
465     }
466 
467   if (numbytes > 0)
468     {
469       /* Numerical test */
470 
471       gchar   num_test = '=';
472       gulong  fileval  = 0;
473 
474       /* Check test value */
475       if ((value[0] == '>') || (value[0] == '<'))
476         {
477           num_test = value[0];
478           value++;
479         }
480 
481       errno = 0;
482       num_testval = strtol (value, NULL, 0);
483 
484       if (errno != 0)
485         return FILE_MATCH_NONE;
486 
487       if (numbytes == 5)
488         {
489           /* Check for file size */
490 
491           GFileInfo *info = g_file_query_info (file,
492                                                G_FILE_ATTRIBUTE_STANDARD_SIZE,
493                                                G_FILE_QUERY_INFO_NONE,
494                                                NULL, NULL);
495           if (! info)
496             return FILE_MATCH_NONE;
497 
498           fileval = g_file_info_get_size (info);
499           g_object_unref (info);
500         }
501       else if (offs >= 0 &&
502                (offs + numbytes <= headsize))
503         {
504            /* We have it in memory */
505 
506           for (k = 0; k < numbytes; k++)
507             fileval = (fileval << 8) | (glong) file_head[offs + k];
508         }
509       else
510         {
511           /* Read it from file */
512 
513           if (! g_seekable_seek (G_SEEKABLE (input), offs,
514                                  (offs >= 0) ? G_SEEK_SET : G_SEEK_END,
515                                  NULL, NULL))
516             return FILE_MATCH_NONE;
517 
518           for (k = 0; k < numbytes; k++)
519             {
520               guchar  byte;
521               GError *error = NULL;
522 
523               byte = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (input),
524                                                     NULL, &error);
525               if (error)
526                 {
527                   g_clear_error (&error);
528                   return FILE_MATCH_NONE;
529                 }
530 
531               fileval = (fileval << 8) | byte;
532             }
533         }
534 
535       if (num_operator == '&')
536         fileval &= num_operator_val;
537 
538       if (num_test == '<')
539         {
540           if (fileval < num_testval)
541             found = numbytes;
542         }
543       else if (num_test == '>')
544         {
545           if (fileval > num_testval)
546             found = numbytes;
547         }
548       else
549         {
550           if (fileval == num_testval)
551             found = numbytes;
552         }
553 
554       if (found && (numbytes == 5))
555         found = FILE_MATCH_SIZE;
556     }
557   else if (numbytes == 0)
558     {
559        /* String test */
560 
561       gchar mem_testval[256];
562 
563       file_convert_string (value,
564                            mem_testval, sizeof (mem_testval),
565                            &numbytes);
566 
567       if (numbytes <= 0)
568         return FILE_MATCH_NONE;
569 
570       if (offs >= 0 &&
571           (offs + numbytes <= headsize))
572         {
573           /* We have it in memory */
574 
575           if (memcmp (mem_testval, file_head + offs, numbytes) == 0)
576             found = numbytes;
577         }
578       else
579         {
580           /* Read it from file */
581 
582           if (! g_seekable_seek (G_SEEKABLE (input), offs,
583                                  (offs >= 0) ? G_SEEK_SET : G_SEEK_END,
584                                  NULL, NULL))
585             return FILE_MATCH_NONE;
586 
587           for (k = 0; k < numbytes; k++)
588             {
589               guchar  byte;
590               GError *error = NULL;
591 
592               byte = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (input),
593                                                     NULL, &error);
594               if (error)
595                 {
596                   g_clear_error (&error);
597 
598                   return FILE_MATCH_NONE;
599                 }
600 
601               if (byte != mem_testval[k])
602                 return FILE_MATCH_NONE;
603             }
604 
605           found = numbytes;
606         }
607     }
608 
609   return found;
610 }
611 
612 static FileMatchType
file_check_magic_list(GSList * magics_list,const guchar * head,gint headsize,GFile * file,GInputStream * input)613 file_check_magic_list (GSList       *magics_list,
614                        const guchar *head,
615                        gint          headsize,
616                        GFile        *file,
617                        GInputStream *input)
618 
619 {
620   gboolean      and            = FALSE;
621   gboolean      found          = FALSE;
622   FileMatchType best_match_val = FILE_MATCH_NONE;
623   FileMatchType match_val      = FILE_MATCH_NONE;
624 
625   for (; magics_list; magics_list = magics_list->next)
626     {
627       const gchar   *offset;
628       const gchar   *type;
629       const gchar   *value;
630       FileMatchType  single_match_val = FILE_MATCH_NONE;
631 
632       if ((offset      = magics_list->data) == NULL) return FILE_MATCH_NONE;
633       if ((magics_list = magics_list->next) == NULL) return FILE_MATCH_NONE;
634       if ((type        = magics_list->data) == NULL) return FILE_MATCH_NONE;
635       if ((magics_list = magics_list->next) == NULL) return FILE_MATCH_NONE;
636       if ((value       = magics_list->data) == NULL) return FILE_MATCH_NONE;
637 
638       single_match_val = file_check_single_magic (offset, type, value,
639                                                   head, headsize,
640                                                   file, input);
641 
642       if (and)
643         found = found && (single_match_val != FILE_MATCH_NONE);
644       else
645         found = (single_match_val != FILE_MATCH_NONE);
646 
647       if (found)
648         {
649           if (match_val == FILE_MATCH_NONE)
650             {
651               /* if we have no match yet, this is it in any case */
652 
653               match_val = single_match_val;
654             }
655           else if (single_match_val != FILE_MATCH_NONE)
656             {
657               /* else if we have a match on this one, combine it with the
658                * existing return value
659                */
660 
661               if (single_match_val == FILE_MATCH_SIZE)
662                 {
663                   /* if we already have a magic match, simply increase
664                    * that by one to indicate "better match", not perfect
665                    * but better than losing the additional size match
666                    * entirely
667                    */
668                   if (match_val != FILE_MATCH_SIZE)
669                     match_val += 1;
670                 }
671               else
672                 {
673                   /* if we already have a magic match, simply add to its
674                    * length; otherwise if we already have a size match,
675                    * combine it with this match, see comment above
676                    */
677                   if (match_val != FILE_MATCH_SIZE)
678                     match_val += single_match_val;
679                   else
680                     match_val = single_match_val + 1;
681                 }
682             }
683         }
684       else
685         {
686           match_val = FILE_MATCH_NONE;
687         }
688 
689       and = (strchr (offset, '&') != NULL);
690 
691       if (! and)
692         {
693           /* when done with this 'and' list, update best_match_val */
694 
695           if (best_match_val == FILE_MATCH_NONE)
696             {
697               /* if we have no best match yet, this is it */
698 
699               best_match_val = match_val;
700             }
701           else if (match_val != FILE_MATCH_NONE)
702             {
703               /* otherwise if this was a match, update the best match, note
704                * that by using MAX we will not overwrite a magic match
705                * with a size match
706                */
707 
708               best_match_val = MAX (best_match_val, match_val);
709             }
710 
711           match_val = FILE_MATCH_NONE;
712         }
713     }
714 
715   return best_match_val;
716 }
717