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