configure.ac : Detect the size of off_t.
[platform/upstream/flac.git] / src / share / grabbag / picture.c
1 /* grabbag - Convenience lib for various routines common to several tools
2  * Copyright (C) 2006,2007,2008,2009  Josh Coalson
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #if HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include "share/alloc.h"
24 #include "share/grabbag.h"
25 #include "FLAC/assert.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 /* slightly different that strndup(): this always copies 'size' bytes starting from s into a NUL-terminated string. */
31 static char *local__strndup_(const char *s, size_t size)
32 {
33         char *x = safe_malloc_add_2op_(size, /*+*/1);
34         if(x) {
35                 memcpy(x, s, size);
36                 x[size] = '\0';
37         }
38         return x;
39 }
40
41 static FLAC__bool local__parse_type_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture)
42 {
43         size_t i;
44         FLAC__uint32 val = 0;
45
46         picture->type = FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
47
48         if(len == 0)
49                 return true; /* empty string implies default to 'front cover' */
50
51         for(i = 0; i < len; i++) {
52                 if(s[i] >= '0' && s[i] <= '9')
53                         val = 10*val + (FLAC__uint32)(s[i] - '0');
54                 else
55                         return false;
56         }
57
58         if(i == len)
59                 picture->type = val;
60         else
61                 return false;
62
63         return true;
64 }
65
66 static FLAC__bool local__parse_resolution_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture)
67 {
68         int state = 0;
69         size_t i;
70         FLAC__uint32 val = 0;
71
72         picture->width = picture->height = picture->depth = picture->colors = 0;
73
74         if(len == 0)
75                 return true; /* empty string implies client wants to get info from the file itself */
76
77         for(i = 0; i < len; i++) {
78                 if(s[i] == 'x') {
79                         if(state == 0)
80                                 picture->width = val;
81                         else if(state == 1)
82                                 picture->height = val;
83                         else
84                                 return false;
85                         state++;
86                         val = 0;
87                 }
88                 else if(s[i] == '/') {
89                         if(state == 2)
90                                 picture->depth = val;
91                         else
92                                 return false;
93                         state++;
94                         val = 0;
95                 }
96                 else if(s[i] >= '0' && s[i] <= '9')
97                         val = 10*val + (FLAC__uint32)(s[i] - '0');
98                 else
99                         return false;
100         }
101
102         if(state < 2)
103                 return false;
104         else if(state == 2)
105                 picture->depth = val;
106         else if(state == 3)
107                 picture->colors = val;
108         else
109                 return false;
110         if(picture->depth < 32 && 1u<<picture->depth < picture->colors)
111                 return false;
112
113         return true;
114 }
115
116 static FLAC__bool local__extract_mime_type_(FLAC__StreamMetadata *obj)
117 {
118         if(obj->data.picture.data_length >= 8 && 0 == memcmp(obj->data.picture.data, "\x89PNG\x0d\x0a\x1a\x0a", 8))
119                 return FLAC__metadata_object_picture_set_mime_type(obj, "image/png", /*copy=*/true);
120         else if(obj->data.picture.data_length >= 6 && (0 == memcmp(obj->data.picture.data, "GIF87a", 6) || 0 == memcmp(obj->data.picture.data, "GIF89a", 6)))
121                 return FLAC__metadata_object_picture_set_mime_type(obj, "image/gif", /*copy=*/true);
122         else if(obj->data.picture.data_length >= 2 && 0 == memcmp(obj->data.picture.data, "\xff\xd8", 2))
123                 return FLAC__metadata_object_picture_set_mime_type(obj, "image/jpeg", /*copy=*/true);
124         return false;
125 }
126
127 static FLAC__bool local__extract_resolution_color_info_(FLAC__StreamMetadata_Picture *picture)
128 {
129         const FLAC__byte *data = picture->data;
130         FLAC__uint32 len = picture->data_length;
131
132         if(0 == strcmp(picture->mime_type, "image/png")) {
133                 /* c.f. http://www.w3.org/TR/PNG/ */
134                 FLAC__bool need_palette = false; /* if IHDR has color_type=3, we need to also read the PLTE chunk to get the #colors */
135                 if(len < 8 || memcmp(data, "\x89PNG\x0d\x0a\x1a\x0a", 8))
136                         return false;
137                 /* try to find IHDR chunk */
138                 data += 8;
139                 len -= 8;
140                 while(len > 12) { /* every PNG chunk must be at least 12 bytes long */
141                         const FLAC__uint32 clen = (FLAC__uint32)data[0] << 24 | (FLAC__uint32)data[1] << 16 | (FLAC__uint32)data[2] << 8 | (FLAC__uint32)data[3];
142                         if(0 == memcmp(data+4, "IHDR", 4) && clen == 13) {
143                                 unsigned color_type = data[17];
144                                 picture->width = (FLAC__uint32)data[8] << 24 | (FLAC__uint32)data[9] << 16 | (FLAC__uint32)data[10] << 8 | (FLAC__uint32)data[11];
145                                 picture->height = (FLAC__uint32)data[12] << 24 | (FLAC__uint32)data[13] << 16 | (FLAC__uint32)data[14] << 8 | (FLAC__uint32)data[15];
146                                 if(color_type == 3) {
147                                         /* even though the bit depth for color_type==3 can be 1,2,4,or 8,
148                                          * the spec in 11.2.2 of http://www.w3.org/TR/PNG/ says that the
149                                          * sample depth is always 8
150                                          */
151                                         picture->depth = 8 * 3u;
152                                         need_palette = true;
153                                         data += 12 + clen;
154                                         len -= 12 + clen;
155                                 }
156                                 else {
157                                         if(color_type == 0) /* greyscale, 1 sample per pixel */
158                                                 picture->depth = (FLAC__uint32)data[16];
159                                         if(color_type == 2) /* truecolor, 3 samples per pixel */
160                                                 picture->depth = (FLAC__uint32)data[16] * 3u;
161                                         if(color_type == 4) /* greyscale+alpha, 2 samples per pixel */
162                                                 picture->depth = (FLAC__uint32)data[16] * 2u;
163                                         if(color_type == 6) /* truecolor+alpha, 4 samples per pixel */
164                                                 picture->depth = (FLAC__uint32)data[16] * 4u;
165                                         picture->colors = 0;
166                                         return true;
167                                 }
168                         }
169                         else if(need_palette && 0 == memcmp(data+4, "PLTE", 4)) {
170                                 picture->colors = clen / 3u;
171                                 return true;
172                         }
173                         else if(clen + 12 > len)
174                                 return false;
175                         else {
176                                 data += 12 + clen;
177                                 len -= 12 + clen;
178                         }
179                 }
180         }
181         else if(0 == strcmp(picture->mime_type, "image/jpeg")) {
182                 /* c.f. http://www.w3.org/Graphics/JPEG/itu-t81.pdf and Q22 of http://www.faqs.org/faqs/jpeg-faq/part1/ */
183                 if(len < 2 || memcmp(data, "\xff\xd8", 2))
184                         return false;
185                 data += 2;
186                 len -= 2;
187                 while(1) {
188                         /* look for sync FF byte */
189                         for( ; len > 0; data++, len--) {
190                                 if(*data == 0xff)
191                                         break;
192                         }
193                         if(len == 0)
194                                 return false;
195                         /* eat any extra pad FF bytes before marker */
196                         for( ; len > 0; data++, len--) {
197                                 if(*data != 0xff)
198                                         break;
199                         }
200                         if(len == 0)
201                                 return false;
202                         /* if we hit SOS or EOI, bail */
203                         if(*data == 0xda || *data == 0xd9)
204                                 return false;
205                         /* looking for some SOFn */
206                         else if(memchr("\xc0\xc1\xc2\xc3\xc5\xc6\xc7\xc9\xca\xcb\xcd\xce\xcf", *data, 13)) {
207                                 data++; len--; /* skip marker byte */
208                                 if(len < 2)
209                                         return false;
210                                 else {
211                                         const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1];
212                                         if(clen < 8 || len < clen)
213                                                 return false;
214                                         picture->width = (FLAC__uint32)data[5] << 8 | (FLAC__uint32)data[6];
215                                         picture->height = (FLAC__uint32)data[3] << 8 | (FLAC__uint32)data[4];
216                                         picture->depth = (FLAC__uint32)data[2] * (FLAC__uint32)data[7];
217                                         picture->colors = 0;
218                                         return true;
219                                 }
220                         }
221                         /* else skip it */
222                         else {
223                                 data++; len--; /* skip marker byte */
224                                 if(len < 2)
225                                         return false;
226                                 else {
227                                         const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1];
228                                         if(clen < 2 || len < clen)
229                                                 return false;
230                                         data += clen;
231                                         len -= clen;
232                                 }
233                         }
234                 }
235         }
236         else if(0 == strcmp(picture->mime_type, "image/gif")) {
237                 /* c.f. http://www.w3.org/Graphics/GIF/spec-gif89a.txt */
238                 if(len < 14)
239                         return false;
240                 if(memcmp(data, "GIF87a", 6) && memcmp(data, "GIF89a", 6))
241                         return false;
242 #if 0
243                 /* according to the GIF spec, even if the GCTF is 0, the low 3 bits should still tell the total # colors used */
244                 if(data[10] & 0x80 == 0)
245                         return false;
246 #endif
247                 picture->width = (FLAC__uint32)data[6] | ((FLAC__uint32)data[7] << 8);
248                 picture->height = (FLAC__uint32)data[8] | ((FLAC__uint32)data[9] << 8);
249 #if 0
250                 /* this value doesn't seem to be reliable... */
251                 picture->depth = (((FLAC__uint32)(data[10] & 0x70) >> 4) + 1) * 3u;
252 #else
253                 /* ...just pessimistically assume it's 24-bit color without scanning all the color tables */
254                 picture->depth = 8u * 3u;
255 #endif
256                 picture->colors = 1u << ((FLAC__uint32)(data[10] & 0x07) + 1u);
257                 return true;
258         }
259         return false;
260 }
261
262 FLAC__StreamMetadata *grabbag__picture_parse_specification(const char *spec, const char **error_message)
263 {
264         FLAC__StreamMetadata *obj;
265         int state = 0;
266         static const char *error_messages[] = {
267                 "memory allocation error",
268                 "invalid picture specification",
269                 "invalid picture specification: can't parse resolution/color part",
270                 "unable to extract resolution and color info from URL, user must set explicitly",
271                 "unable to extract resolution and color info from file, user must set explicitly",
272                 "error opening picture file",
273                 "error reading picture file",
274                 "invalid picture type",
275                 "unable to guess MIME type from file, user must set explicitly",
276                 "type 1 icon must be a 32x32 pixel PNG"
277         };
278
279         FLAC__ASSERT(0 != spec);
280         FLAC__ASSERT(0 != error_message);
281
282         /* double protection */
283         if(0 == spec)
284                 return 0;
285         if(0 == error_message)
286                 return 0;
287
288         *error_message = 0;
289
290         if(0 == (obj = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE))) {
291                 *error_message = error_messages[0];
292                 return obj;
293         }
294
295         if(strchr(spec, '|')) { /* full format */
296                 const char *p;
297                 char *q;
298                 for(p = spec; *error_message==0 && *p; ) {
299                         if(*p == '|') {
300                                 switch(state) {
301                                         case 0: /* type */
302                                                 if(!local__parse_type_(spec, p-spec, &obj->data.picture))
303                                                         *error_message = error_messages[7];
304                                                 break;
305                                         case 1: /* mime type */
306                                                 if(p-spec) { /* if blank, we'll try to guess later from the picture data */
307                                                         if(0 == (q = local__strndup_(spec, p-spec)))
308                                                                 *error_message = error_messages[0];
309                                                         else if(!FLAC__metadata_object_picture_set_mime_type(obj, q, /*copy=*/false))
310                                                                 *error_message = error_messages[0];
311                                                 }
312                                                 break;
313                                         case 2: /* description */
314                                                 if(0 == (q = local__strndup_(spec, p-spec)))
315                                                         *error_message = error_messages[0];
316                                                 else if(!FLAC__metadata_object_picture_set_description(obj, (FLAC__byte*)q, /*copy=*/false))
317                                                         *error_message = error_messages[0];
318                                                 break;
319                                         case 3: /* resolution/color (e.g. [300x300x16[/1234]] */
320                                                 if(!local__parse_resolution_(spec, p-spec, &obj->data.picture))
321                                                         *error_message = error_messages[2];
322                                                 break;
323                                         default:
324                                                 *error_message = error_messages[1];
325                                                 break;
326                                 }
327                                 p++;
328                                 spec = p;
329                                 state++;
330                         }
331                         else
332                                 p++;
333                 }
334         }
335         else { /* simple format, filename only, everything else guessed */
336                 if(!local__parse_type_("", 0, &obj->data.picture)) /* use default picture type */
337                         *error_message = error_messages[7];
338                 /* leave MIME type to be filled in later */
339                 /* leave description empty */
340                 /* leave the rest to be filled in later: */
341                 else if(!local__parse_resolution_("", 0, &obj->data.picture))
342                         *error_message = error_messages[2];
343                 else
344                         state = 4;
345         }
346
347         /* parse filename, read file, try to extract resolution/color info if needed */
348         if(*error_message == 0) {
349                 if(state != 4)
350                         *error_message = error_messages[1];
351                 else { /* 'spec' points to filename/URL */
352                         if(0 == strcmp(obj->data.picture.mime_type, "-->")) { /* magic MIME type means URL */
353                                 if(!FLAC__metadata_object_picture_set_data(obj, (FLAC__byte*)spec, strlen(spec), /*copy=*/true))
354                                         *error_message = error_messages[0];
355                                 else if(obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0)
356                                         *error_message = error_messages[3];
357                         }
358                         else { /* regular picture file */
359                                 const off_t size = grabbag__file_get_filesize(spec);
360                                 if(size < 0)
361                                         *error_message = error_messages[5];
362                                 else {
363                                         FLAC__byte *buffer = safe_malloc_(size);
364                                         if(0 == buffer)
365                                                 *error_message = error_messages[0];
366                                         else {
367                                                 FILE *f = fopen(spec, "rb");
368                                                 if(0 == f) {
369                                                         *error_message = error_messages[5];
370                                                         free(buffer);
371                                                 }
372                                                 else {
373                                                         if(fread(buffer, 1, size, f) != (size_t)size)
374                                                                 *error_message = error_messages[6];
375                                                         fclose(f);
376                                                         if(0 == *error_message) {
377                                                                 if(!FLAC__metadata_object_picture_set_data(obj, buffer, size, /*copy=*/false))
378                                                                         *error_message = error_messages[6];
379                                                                 /* try to extract MIME type if user left it blank */
380                                                                 else if(*obj->data.picture.mime_type == '\0' && !local__extract_mime_type_(obj))
381                                                                         *error_message = error_messages[8];
382                                                                 /* try to extract resolution/color info if user left it blank */
383                                                                 else if((obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0) && !local__extract_resolution_color_info_(&obj->data.picture))
384                                                                         *error_message = error_messages[4];
385                                                         }
386                                                         else {
387                                                                 free(buffer);
388                                                         }
389                                                 }
390                                         }
391                                 }
392                         }
393                 }
394         }
395
396         if(*error_message == 0) {
397                 if(
398                         obj->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD &&
399                         (
400                                 (strcmp(obj->data.picture.mime_type, "image/png") && strcmp(obj->data.picture.mime_type, "-->")) ||
401                                 obj->data.picture.width != 32 ||
402                                 obj->data.picture.height != 32
403                         )
404                 )
405                         *error_message = error_messages[9];
406         }
407
408         if(*error_message && obj) {
409                 FLAC__metadata_object_delete(obj);
410                 obj = 0;
411         }
412
413         return obj;
414 }