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