1 /* grabbag - Convenience lib for various routines common to several tools
2  * Copyright (C) 2006-2009  Josh Coalson
3  * Copyright (C) 2011-2016  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 #ifdef 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 #include "share/safe_str.h"
32 
33 /* slightly different that strndup(): this always copies 'size' bytes starting from s into a NUL-terminated string. */
local__strndup_(const char * s,size_t size)34 static char *local__strndup_(const char *s, size_t size)
35 {
36 	char *x = safe_malloc_add_2op_(size, /*+*/1);
37 	if(x) {
38 		memcpy(x, s, size);
39 		x[size] = '\0';
40 	}
41 	return x;
42 }
43 
local__parse_type_(const char * s,size_t len,FLAC__StreamMetadata_Picture * picture)44 static FLAC__bool local__parse_type_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture)
45 {
46 	size_t i;
47 	FLAC__uint32 val = 0;
48 
49 	picture->type = FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
50 
51 	if(len == 0)
52 		return true; /* empty string implies default to 'front cover' */
53 
54 	for(i = 0; i < len; i++) {
55 		if(s[i] >= '0' && s[i] <= '9')
56 			val = 10*val + (FLAC__uint32)(s[i] - '0');
57 		else
58 			return false;
59 	}
60 
61 	if(i == len)
62 		picture->type = val;
63 	else
64 		return false;
65 
66 	return true;
67 }
68 
local__parse_resolution_(const char * s,size_t len,FLAC__StreamMetadata_Picture * picture)69 static FLAC__bool local__parse_resolution_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture)
70 {
71 	int state = 0;
72 	size_t i;
73 	FLAC__uint32 val = 0;
74 
75 	picture->width = picture->height = picture->depth = picture->colors = 0;
76 
77 	if(len == 0)
78 		return true; /* empty string implies client wants to get info from the file itself */
79 
80 	for(i = 0; i < len; i++) {
81 		if(s[i] == 'x') {
82 			if(state == 0)
83 				picture->width = val;
84 			else if(state == 1)
85 				picture->height = val;
86 			else
87 				return false;
88 			state++;
89 			val = 0;
90 		}
91 		else if(s[i] == '/') {
92 			if(state == 2)
93 				picture->depth = val;
94 			else
95 				return false;
96 			state++;
97 			val = 0;
98 		}
99 		else if(s[i] >= '0' && s[i] <= '9')
100 			val = 10*val + (FLAC__uint32)(s[i] - '0');
101 		else
102 			return false;
103 	}
104 
105 	if(state < 2)
106 		return false;
107 	else if(state == 2)
108 		picture->depth = val;
109 	else if(state == 3)
110 		picture->colors = val;
111 	else
112 		return false;
113 	if(picture->depth < 32 && 1u<<picture->depth < picture->colors)
114 		return false;
115 
116 	return true;
117 }
118 
local__extract_mime_type_(FLAC__StreamMetadata * obj)119 static FLAC__bool local__extract_mime_type_(FLAC__StreamMetadata *obj)
120 {
121 	if(obj->data.picture.data_length >= 8 && 0 == memcmp(obj->data.picture.data, "\x89PNG\x0d\x0a\x1a\x0a", 8))
122 		return FLAC__metadata_object_picture_set_mime_type(obj, "image/png", /*copy=*/true);
123 	else if(obj->data.picture.data_length >= 6 && (0 == memcmp(obj->data.picture.data, "GIF87a", 6) || 0 == memcmp(obj->data.picture.data, "GIF89a", 6)))
124 		return FLAC__metadata_object_picture_set_mime_type(obj, "image/gif", /*copy=*/true);
125 	else if(obj->data.picture.data_length >= 2 && 0 == memcmp(obj->data.picture.data, "\xff\xd8", 2))
126 		return FLAC__metadata_object_picture_set_mime_type(obj, "image/jpeg", /*copy=*/true);
127 	return false;
128 }
129 
local__extract_resolution_color_info_(FLAC__StreamMetadata_Picture * picture)130 static FLAC__bool local__extract_resolution_color_info_(FLAC__StreamMetadata_Picture *picture)
131 {
132 	const FLAC__byte *data = picture->data;
133 	FLAC__uint32 len = picture->data_length;
134 
135 	if(0 == strcmp(picture->mime_type, "image/png")) {
136 		/* c.f. http://www.w3.org/TR/PNG/ */
137 		FLAC__bool need_palette = false; /* if IHDR has color_type=3, we need to also read the PLTE chunk to get the #colors */
138 		if(len < 8 || memcmp(data, "\x89PNG\x0d\x0a\x1a\x0a", 8))
139 			return false;
140 		/* try to find IHDR chunk */
141 		data += 8;
142 		len -= 8;
143 		while(len > 12) { /* every PNG chunk must be at least 12 bytes long */
144 			const FLAC__uint32 clen = (FLAC__uint32)data[0] << 24 | (FLAC__uint32)data[1] << 16 | (FLAC__uint32)data[2] << 8 | (FLAC__uint32)data[3];
145 			if(0 == memcmp(data+4, "IHDR", 4) && clen == 13) {
146 				uint32_t color_type = data[17];
147 				picture->width = (FLAC__uint32)data[8] << 24 | (FLAC__uint32)data[9] << 16 | (FLAC__uint32)data[10] << 8 | (FLAC__uint32)data[11];
148 				picture->height = (FLAC__uint32)data[12] << 24 | (FLAC__uint32)data[13] << 16 | (FLAC__uint32)data[14] << 8 | (FLAC__uint32)data[15];
149 				if(color_type == 3) {
150 					/* even though the bit depth for color_type==3 can be 1,2,4,or 8,
151 					 * the spec in 11.2.2 of http://www.w3.org/TR/PNG/ says that the
152 					 * sample depth is always 8
153 					 */
154 					picture->depth = 8 * 3u;
155 					need_palette = true;
156 					data += 12 + clen;
157 					len -= 12 + clen;
158 				}
159 				else {
160 					if(color_type == 0) /* greyscale, 1 sample per pixel */
161 						picture->depth = (FLAC__uint32)data[16];
162 					if(color_type == 2) /* truecolor, 3 samples per pixel */
163 						picture->depth = (FLAC__uint32)data[16] * 3u;
164 					if(color_type == 4) /* greyscale+alpha, 2 samples per pixel */
165 						picture->depth = (FLAC__uint32)data[16] * 2u;
166 					if(color_type == 6) /* truecolor+alpha, 4 samples per pixel */
167 						picture->depth = (FLAC__uint32)data[16] * 4u;
168 					picture->colors = 0;
169 					return true;
170 				}
171 			}
172 			else if(need_palette && 0 == memcmp(data+4, "PLTE", 4)) {
173 				picture->colors = clen / 3u;
174 				return true;
175 			}
176 			else if(clen + 12 > len)
177 				return false;
178 			else {
179 				data += 12 + clen;
180 				len -= 12 + clen;
181 			}
182 		}
183 	}
184 	else if(0 == strcmp(picture->mime_type, "image/jpeg")) {
185 		/* c.f. http://www.w3.org/Graphics/JPEG/itu-t81.pdf and Q22 of http://www.faqs.org/faqs/jpeg-faq/part1/ */
186 		if(len < 2 || memcmp(data, "\xff\xd8", 2))
187 			return false;
188 		data += 2;
189 		len -= 2;
190 		while(1) {
191 			/* look for sync FF byte */
192 			for( ; len > 0; data++, len--) {
193 				if(*data == 0xff)
194 					break;
195 			}
196 			if(len == 0)
197 				return false;
198 			/* eat any extra pad FF bytes before marker */
199 			for( ; len > 0; data++, len--) {
200 				if(*data != 0xff)
201 					break;
202 			}
203 			if(len == 0)
204 				return false;
205 			/* if we hit SOS or EOI, bail */
206 			if(*data == 0xda || *data == 0xd9)
207 				return false;
208 			/* looking for some SOFn */
209 			else if(memchr("\xc0\xc1\xc2\xc3\xc5\xc6\xc7\xc9\xca\xcb\xcd\xce\xcf", *data, 13)) {
210 				data++; len--; /* skip marker byte */
211 				if(len < 2)
212 					return false;
213 				else {
214 					const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1];
215 					if(clen < 8 || len < clen)
216 						return false;
217 					picture->width = (FLAC__uint32)data[5] << 8 | (FLAC__uint32)data[6];
218 					picture->height = (FLAC__uint32)data[3] << 8 | (FLAC__uint32)data[4];
219 					picture->depth = (FLAC__uint32)data[2] * (FLAC__uint32)data[7];
220 					picture->colors = 0;
221 					return true;
222 				}
223 			}
224 			/* else skip it */
225 			else {
226 				data++; len--; /* skip marker byte */
227 				if(len < 2)
228 					return false;
229 				else {
230 					const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1];
231 					if(clen < 2 || len < clen)
232 						return false;
233 					data += clen;
234 					len -= clen;
235 				}
236 			}
237 		}
238 	}
239 	else if(0 == strcmp(picture->mime_type, "image/gif")) {
240 		/* c.f. http://www.w3.org/Graphics/GIF/spec-gif89a.txt */
241 		if(len < 14)
242 			return false;
243 		if(memcmp(data, "GIF87a", 6) && memcmp(data, "GIF89a", 6))
244 			return false;
245 #if 0
246 		/* according to the GIF spec, even if the GCTF is 0, the low 3 bits should still tell the total # colors used */
247 		if(data[10] & 0x80 == 0)
248 			return false;
249 #endif
250 		picture->width = (FLAC__uint32)data[6] | ((FLAC__uint32)data[7] << 8);
251 		picture->height = (FLAC__uint32)data[8] | ((FLAC__uint32)data[9] << 8);
252 #if 0
253 		/* this value doesn't seem to be reliable... */
254 		picture->depth = (((FLAC__uint32)(data[10] & 0x70) >> 4) + 1) * 3u;
255 #else
256 		/* ...just pessimistically assume it's 24-bit color without scanning all the color tables */
257 		picture->depth = 8u * 3u;
258 #endif
259 		picture->colors = 1u << ((FLAC__uint32)(data[10] & 0x07) + 1u);
260 		return true;
261 	}
262 	return false;
263 }
264 
265 static const char *error_messages[] = {
266 	"memory allocation error",
267 	"invalid picture specification",
268 	"invalid picture specification: can't parse resolution/color part",
269 	"unable to extract resolution and color info from URL, user must set explicitly",
270 	"unable to extract resolution and color info from file, user must set explicitly",
271 	"error opening picture file",
272 	"error reading picture file",
273 	"invalid picture type",
274 	"unable to guess MIME type from file, user must set explicitly",
275 	"type 1 icon must be a 32x32 pixel PNG",
276 	"file not found", /* currently unused */
277 	"file is too large"
278 };
279 
read_file(const char * filepath,FLAC__StreamMetadata * obj)280 static const char * read_file (const char * filepath, FLAC__StreamMetadata * obj)
281 {
282 	const FLAC__off_t size = grabbag__file_get_filesize(filepath);
283 	FLAC__byte *buffer;
284 	FILE *file;
285 	const char *error_message=NULL;
286 
287 	if (size < 0)
288 		return error_messages[5];
289 
290 	if (size >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) /* actual limit is less because of other fields in the PICTURE metadata block */
291 		return error_messages[11];
292 
293 	if ((buffer = safe_malloc_(size)) == NULL)
294 		return error_messages[0];
295 
296 	if ((file = flac_fopen(filepath, "rb")) == NULL) {
297 		free(buffer);
298 		return error_messages[5];
299 	}
300 
301 	if (fread(buffer, 1, size, file) != (size_t) size) {
302 		fclose(file);
303 		free(buffer);
304 		return error_messages[6];
305 	}
306 	fclose(file);
307 
308 	if (!FLAC__metadata_object_picture_set_data(obj, buffer, size, /*copy=*/false))
309 		error_message = error_messages[6];
310 	/* try to extract MIME type if user left it blank */
311 	else if (*obj->data.picture.mime_type == '\0' && !local__extract_mime_type_(obj))
312 		error_message = error_messages[8];
313 	/* try to extract resolution/color info if user left it blank */
314 	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))
315 		error_message = error_messages[4];
316 	/* check metadata block size */
317 	else if (obj->length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN))
318 		error_message = error_messages[11];
319 
320 	return error_message;
321 }
322 
grabbag__picture_parse_specification(const char * spec,const char ** error_message)323 FLAC__StreamMetadata *grabbag__picture_parse_specification(const char *spec, const char **error_message)
324 {
325 	FLAC__StreamMetadata *obj;
326 	int state = 0;
327 
328 	FLAC__ASSERT(0 != spec);
329 	FLAC__ASSERT(0 != error_message);
330 
331 	/* double protection */
332 	if(0 == spec)
333 		return 0;
334 	if(0 == error_message)
335 		return 0;
336 
337 	*error_message = 0;
338 
339 	if(0 == (obj = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE))) {
340 		*error_message = error_messages[0];
341 		return obj;
342 	}
343 
344 	if(strchr(spec, '|')) { /* full format */
345 		const char *p;
346 		char *q;
347 		for(p = spec; *error_message==0 && *p; ) {
348 			if(*p == '|') {
349 				switch(state) {
350 					case 0: /* type */
351 						if(!local__parse_type_(spec, p-spec, &obj->data.picture))
352 							*error_message = error_messages[7];
353 						break;
354 					case 1: /* mime type */
355 						if(p-spec) { /* if blank, we'll try to guess later from the picture data */
356 							if(0 == (q = local__strndup_(spec, p-spec)))
357 								*error_message = error_messages[0];
358 							else if(!FLAC__metadata_object_picture_set_mime_type(obj, q, /*copy=*/false))
359 								*error_message = error_messages[0];
360 						}
361 						break;
362 					case 2: /* description */
363 						if(0 == (q = local__strndup_(spec, p-spec)))
364 							*error_message = error_messages[0];
365 						else if(!FLAC__metadata_object_picture_set_description(obj, (FLAC__byte*)q, /*copy=*/false))
366 							*error_message = error_messages[0];
367 						break;
368 					case 3: /* resolution/color (e.g. [300x300x16[/1234]] */
369 						if(!local__parse_resolution_(spec, p-spec, &obj->data.picture))
370 							*error_message = error_messages[2];
371 						break;
372 					default:
373 						*error_message = error_messages[1];
374 						break;
375 				}
376 				p++;
377 				spec = p;
378 				state++;
379 			}
380 			else
381 				p++;
382 		}
383 	}
384 	else { /* simple format, filename only, everything else guessed */
385 		if(!local__parse_type_("", 0, &obj->data.picture)) /* use default picture type */
386 			*error_message = error_messages[7];
387 		/* leave MIME type to be filled in later */
388 		/* leave description empty */
389 		/* leave the rest to be filled in later: */
390 		else if(!local__parse_resolution_("", 0, &obj->data.picture))
391 			*error_message = error_messages[2];
392 		else
393 			state = 4;
394 	}
395 
396 	/* parse filename, read file, try to extract resolution/color info if needed */
397 	if(*error_message == 0) {
398 		if(state != 4)
399 			*error_message = error_messages[1];
400 		else { /* 'spec' points to filename/URL */
401 			if(0 == strcmp(obj->data.picture.mime_type, "-->")) { /* magic MIME type means URL */
402 				if(!FLAC__metadata_object_picture_set_data(obj, (FLAC__byte*)spec, strlen(spec), /*copy=*/true))
403 					*error_message = error_messages[0];
404 				else if(obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0)
405 					*error_message = error_messages[3];
406 			}
407 			else { /* regular picture file */
408 				*error_message = read_file (spec, obj);
409 			}
410 		}
411 	}
412 
413 	if(*error_message == 0) {
414 		if(
415 			obj->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD &&
416 			(
417 				(strcmp(obj->data.picture.mime_type, "image/png") && strcmp(obj->data.picture.mime_type, "-->")) ||
418 				obj->data.picture.width != 32 ||
419 				obj->data.picture.height != 32
420 			)
421 		)
422 			*error_message = error_messages[9];
423 	}
424 
425 	if(*error_message && obj) {
426 		FLAC__metadata_object_delete(obj);
427 		obj = 0;
428 	}
429 
430 	return obj;
431 }
432 
grabbag__picture_from_specification(int type,const char * mime_type_in,const char * description,const PictureResolution * res,const char * filepath,const char ** error_message)433 FLAC__StreamMetadata *grabbag__picture_from_specification(int type, const char *mime_type_in, const char * description,
434 		const PictureResolution * res, const char * filepath, const char **error_message)
435 {
436 
437 	FLAC__StreamMetadata *obj;
438 	char mime_type [64] ;
439 
440 	if (error_message == 0)
441 		return 0;
442 
443 	safe_strncpy(mime_type, mime_type_in, sizeof (mime_type));
444 
445 	*error_message = 0;
446 
447 	if ((obj = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE)) == 0) {
448 		*error_message = error_messages[0];
449 		return obj;
450 	}
451 
452 	/* Picture type if known. */
453 	obj->data.picture.type = type >= 0 ? type : FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
454 
455 	/* Mime type if known. */
456 	if (mime_type_in && ! FLAC__metadata_object_picture_set_mime_type(obj, mime_type, /*copy=*/true)) {
457 		*error_message = error_messages[0];
458 		return obj;
459 	}
460 
461 	/* Description if present. */
462 	if (description && ! FLAC__metadata_object_picture_set_description(obj, (FLAC__byte*) description, /*copy=*/true)) {
463 		*error_message = error_messages[0];
464 		return obj;
465 	}
466 
467 	if (res == NULL) {
468 		obj->data.picture.width = 0;
469 		obj->data.picture.height = 0;
470 		obj->data.picture.depth = 0;
471 		obj->data.picture.colors = 0;
472 	}
473 	else {
474 		obj->data.picture.width = res->width;
475 		obj->data.picture.height = res->height;
476 		obj->data.picture.depth = res->depth;
477 		obj->data.picture.colors = res->colors;
478 	}
479 
480 	if (strcmp(obj->data.picture.mime_type, "-->") == 0) { /* magic MIME type means URL */
481 		if (!FLAC__metadata_object_picture_set_data(obj, (FLAC__byte*)filepath, strlen(filepath), /*copy=*/true))
482 			*error_message = error_messages[0];
483 		else if (obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0)
484 			*error_message = error_messages[3];
485 	}
486 	else {
487 		*error_message = read_file (filepath, obj);
488 	}
489 
490 	if (*error_message == NULL) {
491 		if (
492 			obj->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD &&
493 			(
494 				(strcmp(obj->data.picture.mime_type, "image/png") && strcmp(obj->data.picture.mime_type, "-->")) ||
495 				obj->data.picture.width != 32 ||
496 				obj->data.picture.height != 32
497 			)
498 		)
499 			*error_message = error_messages[9];
500 	}
501 
502 	if (*error_message && obj) {
503 		FLAC__metadata_object_delete(obj);
504 		obj = 0;
505 	}
506 
507 	return obj;
508 }
509