1 /*
2      This file is part of libextractor.
3      Copyright (C) 2002, 2003, 2004, 2012 Vidyut Samanta and Christian Grothoff
4 
5      libextractor is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9 
10      libextractor is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14 
15      You should have received a copy of the GNU General Public License
16      along with libextractor; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19  */
20 /**
21  * @file plugins/jpeg_extractor.c
22  * @brief plugin to support JPEG files
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "extractor.h"
27 #if WINDOWS || DARWIN
28 #if DARWIN
29 typedef int boolean;
30 #endif
31 #define HAVE_BOOLEAN
32 #endif
33 #include <jpeglib.h>
34 #include <setjmp.h>
35 
36 
37 /**
38  * Context for custom functions.
39  */
40 struct Context
41 {
42   /**
43    * Environment for longjmp from within error_exit handler.
44    */
45   jmp_buf env;
46 };
47 
48 
49 /**
50  * Function used to avoid having libjpeg write error messages to the console.
51  */
52 static void
no_emit(j_common_ptr cinfo,int msg_level)53 no_emit (j_common_ptr cinfo, int msg_level)
54 {
55   /* do nothing */
56 }
57 
58 
59 /**
60  * Function used to avoid having libjpeg write error messages to the console.
61  */
62 static void
no_output(j_common_ptr cinfo)63 no_output (j_common_ptr cinfo)
64 {
65   /* do nothing */
66 }
67 
68 
69 /**
70  * Function used to avoid having libjpeg kill our process.
71  */
72 static void
no_exit(j_common_ptr cinfo)73 no_exit (j_common_ptr cinfo)
74 {
75   struct Context *ctx = cinfo->client_data;
76 
77   /* we're not allowed to return (by API definition),
78      and we don't want to abort/exit.  So we longjmp
79      to our cleanup code instead. */
80   longjmp (ctx->env, 1);
81 }
82 
83 
84 /**
85  * Main entry method for the 'image/jpeg' extraction plugin.
86  *
87  * @param ec extraction context provided to the plugin
88  */
89 void
EXTRACTOR_jpeg_extract_method(struct EXTRACTOR_ExtractContext * ec)90 EXTRACTOR_jpeg_extract_method (struct EXTRACTOR_ExtractContext *ec)
91 {
92   struct jpeg_decompress_struct jds;
93   struct jpeg_error_mgr em;
94   void *buf;
95   ssize_t size;
96   int is_jpeg;
97   unsigned int rounds;
98   char format[128];
99   struct jpeg_marker_struct *mptr;
100   struct Context ctx;
101 
102   is_jpeg = 0;
103   rounds = 0; /* used to avoid going on forever for non-jpeg files */
104   jpeg_std_error (&em);
105   em.emit_message = &no_emit;
106   em.output_message = &no_output;
107   em.error_exit = &no_exit;
108   jds.client_data = &ctx;
109   if (1 == setjmp (ctx.env))
110     goto EXIT; /* we get here if libjpeg calls 'no_exit' because it wants to die */
111   jds.err = &em;
112   jpeg_create_decompress (&jds);
113   jpeg_save_markers (&jds, JPEG_COM, 1024 * 8);
114   while ( (1 == is_jpeg) || (rounds++ < 8) )
115   {
116     if (-1 == (size = ec->read (ec->cls,
117                                 &buf,
118                                 16 * 1024)))
119       break;
120     if (0 == size)
121       break;
122     jpeg_mem_src (&jds, buf, size);
123     if (0 == is_jpeg)
124     {
125       if (JPEG_HEADER_OK == jpeg_read_header (&jds, 1))
126         is_jpeg = 1; /* ok, really a jpeg, keep going until the end */
127       continue;
128     }
129     jpeg_consume_input (&jds);
130   }
131 
132   if (1 != is_jpeg)
133     goto EXIT;
134   if (0 !=
135       ec->proc (ec->cls,
136                 "jpeg",
137                 EXTRACTOR_METATYPE_MIMETYPE,
138                 EXTRACTOR_METAFORMAT_UTF8,
139                 "text/plain",
140                 "image/jpeg",
141                 strlen ("image/jpeg") + 1))
142     goto EXIT;
143   snprintf (format,
144             sizeof (format),
145             "%ux%u",
146             (unsigned int) jds.image_width,
147             (unsigned int) jds.image_height);
148   if (0 !=
149       ec->proc (ec->cls,
150                 "jpeg",
151                 EXTRACTOR_METATYPE_IMAGE_DIMENSIONS,
152                 EXTRACTOR_METAFORMAT_UTF8,
153                 "text/plain",
154                 format,
155                 strlen (format) + 1))
156     goto EXIT;
157   for (mptr = jds.marker_list; NULL != mptr; mptr = mptr->next)
158   {
159     size_t off;
160 
161     if (JPEG_COM != mptr->marker)
162       continue;
163     off = 0;
164     while ( (off < mptr->data_length) &&
165             (isspace (((const unsigned char *) mptr->data)[mptr->data_length
166                                                            - 1 - off])) )
167       off++;
168     if (0 !=
169         ec->proc (ec->cls,
170                   "jpeg",
171                   EXTRACTOR_METATYPE_COMMENT,
172                   EXTRACTOR_METAFORMAT_C_STRING,
173                   "text/plain",
174                   (const char *) mptr->data,
175                   mptr->data_length - off))
176       goto EXIT;
177   }
178 
179 EXIT:
180   jpeg_destroy_decompress (&jds);
181 }
182 
183 
184 /* end of jpeg_extractor.c */
185