xref: /PHP-8.0/ext/standard/image.c (revision 8f97f82e)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | http://www.php.net/license/3_01.txt                                  |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
14    |          Marcus Boerger <helly@php.net>                              |
15    +----------------------------------------------------------------------+
16  */
17 
18 #include "php.h"
19 #include <stdio.h>
20 #if HAVE_FCNTL_H
21 #include <fcntl.h>
22 #endif
23 #include "fopen_wrappers.h"
24 #include "ext/standard/fsock.h"
25 #if HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #include "php_image.h"
29 
30 #if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
31 #include "zlib.h"
32 #endif
33 
34 /* file type markers */
35 PHPAPI const char php_sig_gif[3] = {'G', 'I', 'F'};
36 PHPAPI const char php_sig_psd[4] = {'8', 'B', 'P', 'S'};
37 PHPAPI const char php_sig_bmp[2] = {'B', 'M'};
38 PHPAPI const char php_sig_swf[3] = {'F', 'W', 'S'};
39 PHPAPI const char php_sig_swc[3] = {'C', 'W', 'S'};
40 PHPAPI const char php_sig_jpg[3] = {(char) 0xff, (char) 0xd8, (char) 0xff};
41 PHPAPI const char php_sig_png[8] = {(char) 0x89, (char) 0x50, (char) 0x4e, (char) 0x47,
42                                     (char) 0x0d, (char) 0x0a, (char) 0x1a, (char) 0x0a};
43 PHPAPI const char php_sig_tif_ii[4] = {'I','I', (char)0x2A, (char)0x00};
44 PHPAPI const char php_sig_tif_mm[4] = {'M','M', (char)0x00, (char)0x2A};
45 PHPAPI const char php_sig_jpc[3]  = {(char)0xff, (char)0x4f, (char)0xff};
46 PHPAPI const char php_sig_jp2[12] = {(char)0x00, (char)0x00, (char)0x00, (char)0x0c,
47                                      (char)0x6a, (char)0x50, (char)0x20, (char)0x20,
48                                      (char)0x0d, (char)0x0a, (char)0x87, (char)0x0a};
49 PHPAPI const char php_sig_iff[4] = {'F','O','R','M'};
50 PHPAPI const char php_sig_ico[4] = {(char)0x00, (char)0x00, (char)0x01, (char)0x00};
51 PHPAPI const char php_sig_riff[4] = {'R', 'I', 'F', 'F'};
52 PHPAPI const char php_sig_webp[4] = {'W', 'E', 'B', 'P'};
53 
54 /* REMEMBER TO ADD MIME-TYPE TO FUNCTION php_image_type_to_mime_type */
55 /* PCX must check first 64bytes and byte 0=0x0a and byte2 < 0x06 */
56 
57 /* return info as a struct, to make expansion easier */
58 
59 struct gfxinfo {
60 	unsigned int width;
61 	unsigned int height;
62 	unsigned int bits;
63 	unsigned int channels;
64 };
65 
66 /* {{{ PHP_MINIT_FUNCTION(imagetypes)
67  * Register IMAGETYPE_<xxx> constants used by GetImageSize(), image_type_to_mime_type, ext/exif */
PHP_MINIT_FUNCTION(imagetypes)68 PHP_MINIT_FUNCTION(imagetypes)
69 {
70 	REGISTER_LONG_CONSTANT("IMAGETYPE_GIF",     IMAGE_FILETYPE_GIF,     CONST_CS | CONST_PERSISTENT);
71 	REGISTER_LONG_CONSTANT("IMAGETYPE_JPEG",    IMAGE_FILETYPE_JPEG,    CONST_CS | CONST_PERSISTENT);
72 	REGISTER_LONG_CONSTANT("IMAGETYPE_PNG",     IMAGE_FILETYPE_PNG,     CONST_CS | CONST_PERSISTENT);
73 	REGISTER_LONG_CONSTANT("IMAGETYPE_SWF",     IMAGE_FILETYPE_SWF,     CONST_CS | CONST_PERSISTENT);
74 	REGISTER_LONG_CONSTANT("IMAGETYPE_PSD",     IMAGE_FILETYPE_PSD,     CONST_CS | CONST_PERSISTENT);
75 	REGISTER_LONG_CONSTANT("IMAGETYPE_BMP",     IMAGE_FILETYPE_BMP,     CONST_CS | CONST_PERSISTENT);
76 	REGISTER_LONG_CONSTANT("IMAGETYPE_TIFF_II", IMAGE_FILETYPE_TIFF_II, CONST_CS | CONST_PERSISTENT);
77 	REGISTER_LONG_CONSTANT("IMAGETYPE_TIFF_MM", IMAGE_FILETYPE_TIFF_MM, CONST_CS | CONST_PERSISTENT);
78 	REGISTER_LONG_CONSTANT("IMAGETYPE_JPC",     IMAGE_FILETYPE_JPC,     CONST_CS | CONST_PERSISTENT);
79 	REGISTER_LONG_CONSTANT("IMAGETYPE_JP2",     IMAGE_FILETYPE_JP2,     CONST_CS | CONST_PERSISTENT);
80 	REGISTER_LONG_CONSTANT("IMAGETYPE_JPX",     IMAGE_FILETYPE_JPX,     CONST_CS | CONST_PERSISTENT);
81 	REGISTER_LONG_CONSTANT("IMAGETYPE_JB2",     IMAGE_FILETYPE_JB2,     CONST_CS | CONST_PERSISTENT);
82 #if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
83 	REGISTER_LONG_CONSTANT("IMAGETYPE_SWC",     IMAGE_FILETYPE_SWC,     CONST_CS | CONST_PERSISTENT);
84 #endif
85 	REGISTER_LONG_CONSTANT("IMAGETYPE_IFF",     IMAGE_FILETYPE_IFF,     CONST_CS | CONST_PERSISTENT);
86 	REGISTER_LONG_CONSTANT("IMAGETYPE_WBMP",    IMAGE_FILETYPE_WBMP,    CONST_CS | CONST_PERSISTENT);
87 	REGISTER_LONG_CONSTANT("IMAGETYPE_JPEG2000",IMAGE_FILETYPE_JPC,     CONST_CS | CONST_PERSISTENT); /* keep alias */
88 	REGISTER_LONG_CONSTANT("IMAGETYPE_XBM",     IMAGE_FILETYPE_XBM,     CONST_CS | CONST_PERSISTENT);
89 	REGISTER_LONG_CONSTANT("IMAGETYPE_ICO",     IMAGE_FILETYPE_ICO,     CONST_CS | CONST_PERSISTENT);
90 	REGISTER_LONG_CONSTANT("IMAGETYPE_WEBP",	IMAGE_FILETYPE_WEBP,	CONST_CS | CONST_PERSISTENT);
91 	REGISTER_LONG_CONSTANT("IMAGETYPE_UNKNOWN", IMAGE_FILETYPE_UNKNOWN, CONST_CS | CONST_PERSISTENT);
92 	REGISTER_LONG_CONSTANT("IMAGETYPE_COUNT",   IMAGE_FILETYPE_COUNT,   CONST_CS | CONST_PERSISTENT);
93 	return SUCCESS;
94 }
95 /* }}} */
96 
97 /* {{{ php_handle_gif
98  * routine to handle GIF files. If only everything were that easy... ;} */
php_handle_gif(php_stream * stream)99 static struct gfxinfo *php_handle_gif (php_stream * stream)
100 {
101 	struct gfxinfo *result = NULL;
102 	unsigned char dim[5];
103 
104 	if (php_stream_seek(stream, 3, SEEK_CUR))
105 		return NULL;
106 
107 	if (php_stream_read(stream, (char*)dim, sizeof(dim)) != sizeof(dim))
108 		return NULL;
109 
110 	result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
111 	result->width    = (unsigned int)dim[0] | (((unsigned int)dim[1])<<8);
112 	result->height   = (unsigned int)dim[2] | (((unsigned int)dim[3])<<8);
113 	result->bits     = dim[4]&0x80 ? ((((unsigned int)dim[4])&0x07) + 1) : 0;
114 	result->channels = 3; /* always */
115 
116 	return result;
117 }
118 /* }}} */
119 
120 /* {{{ php_handle_psd */
php_handle_psd(php_stream * stream)121 static struct gfxinfo *php_handle_psd (php_stream * stream)
122 {
123 	struct gfxinfo *result = NULL;
124 	unsigned char dim[8];
125 
126 	if (php_stream_seek(stream, 11, SEEK_CUR))
127 		return NULL;
128 
129 	if (php_stream_read(stream, (char*)dim, sizeof(dim)) != sizeof(dim))
130 		return NULL;
131 
132 	result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
133 	result->height   =  (((unsigned int)dim[0]) << 24) + (((unsigned int)dim[1]) << 16) + (((unsigned int)dim[2]) << 8) + ((unsigned int)dim[3]);
134 	result->width    =  (((unsigned int)dim[4]) << 24) + (((unsigned int)dim[5]) << 16) + (((unsigned int)dim[6]) << 8) + ((unsigned int)dim[7]);
135 
136 	return result;
137 }
138 /* }}} */
139 
140 /* {{{ php_handle_bmp */
php_handle_bmp(php_stream * stream)141 static struct gfxinfo *php_handle_bmp (php_stream * stream)
142 {
143 	struct gfxinfo *result = NULL;
144 	unsigned char dim[16];
145 	int size;
146 
147 	if (php_stream_seek(stream, 11, SEEK_CUR))
148 		return NULL;
149 
150 	if (php_stream_read(stream, (char*)dim, sizeof(dim)) != sizeof(dim))
151 		return NULL;
152 
153 	size   = (((unsigned int)dim[ 3]) << 24) + (((unsigned int)dim[ 2]) << 16) + (((unsigned int)dim[ 1]) << 8) + ((unsigned int) dim[ 0]);
154 	if (size == 12) {
155 		result = (struct gfxinfo *) ecalloc (1, sizeof(struct gfxinfo));
156 		result->width    =  (((unsigned int)dim[ 5]) << 8) + ((unsigned int) dim[ 4]);
157 		result->height   =  (((unsigned int)dim[ 7]) << 8) + ((unsigned int) dim[ 6]);
158 		result->bits     =  ((unsigned int)dim[11]);
159 	} else if (size > 12 && (size <= 64 || size == 108 || size == 124)) {
160 		result = (struct gfxinfo *) ecalloc (1, sizeof(struct gfxinfo));
161 		result->width    =  (((unsigned int)dim[ 7]) << 24) + (((unsigned int)dim[ 6]) << 16) + (((unsigned int)dim[ 5]) << 8) + ((unsigned int) dim[ 4]);
162 		result->height   =  (((unsigned int)dim[11]) << 24) + (((unsigned int)dim[10]) << 16) + (((unsigned int)dim[ 9]) << 8) + ((unsigned int) dim[ 8]);
163 		result->height   =  abs((int32_t)result->height);
164 		result->bits     =  (((unsigned int)dim[15]) <<  8) +  ((unsigned int)dim[14]);
165 	} else {
166 		return NULL;
167 	}
168 
169 	return result;
170 }
171 /* }}} */
172 
173 /* {{{ php_swf_get_bits
174  * routines to handle SWF files. */
php_swf_get_bits(unsigned char * buffer,unsigned int pos,unsigned int count)175 static unsigned long int php_swf_get_bits (unsigned char* buffer, unsigned int pos, unsigned int count)
176 {
177 	unsigned int loop;
178 	unsigned long int result = 0;
179 
180 	for (loop = pos; loop < pos + count; loop++)
181 	{
182 		result = result +
183 			((((buffer[loop / 8]) >> (7 - (loop % 8))) & 0x01) << (count - (loop - pos) - 1));
184 	}
185 	return result;
186 }
187 /* }}} */
188 
189 #if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
190 /* {{{ php_handle_swc */
php_handle_swc(php_stream * stream)191 static struct gfxinfo *php_handle_swc(php_stream * stream)
192 {
193 	struct gfxinfo *result = NULL;
194 
195 	long bits;
196 	unsigned char a[64];
197 	unsigned long len=64, szlength;
198 	int factor = 1,maxfactor = 16;
199 	int status = 0;
200 	unsigned char *b, *buf = NULL;
201 	zend_string *bufz;
202 
203 	if (php_stream_seek(stream, 5, SEEK_CUR)) {
204 		return NULL;
205 	}
206 
207 	if (php_stream_read(stream, (char *) a, sizeof(a)) != sizeof(a)) {
208 		return NULL;
209 	}
210 
211 	b = ecalloc(1, len + 1);
212 
213 	if (uncompress(b, &len, a, sizeof(a)) != Z_OK) {
214 		/* failed to decompress the file, will try reading the rest of the file */
215 		if (php_stream_seek(stream, 8, SEEK_SET)) {
216 			efree(b);
217 			return NULL;
218 		}
219 
220 		bufz = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0);
221 
222 		if (!bufz) {
223 			efree(b);
224 			return NULL;
225 		}
226 
227 		/*
228 		 * zlib::uncompress() wants to know the output data length
229 		 * if none was given as a parameter
230 		 * we try from input length * 2 up to input length * 2^8
231 		 * doubling it whenever it wasn't big enough
232 		 * that should be eneugh for all real life cases
233 		*/
234 
235 		do {
236 			szlength = ZSTR_LEN(bufz) * (1<<factor++);
237 			buf = erealloc(buf, szlength);
238 			status = uncompress(buf, &szlength, (unsigned char *) ZSTR_VAL(bufz), ZSTR_LEN(bufz));
239 		} while ((status==Z_BUF_ERROR)&&(factor<maxfactor));
240 
241 		if (bufz) {
242 			zend_string_release_ex(bufz, 0);
243 		}
244 
245 		if (status == Z_OK) {
246 			 memcpy(b, buf, len);
247 		}
248 
249 		if (buf) {
250 			efree(buf);
251 		}
252 	}
253 
254 	if (!status) {
255 		result = (struct gfxinfo *) ecalloc (1, sizeof (struct gfxinfo));
256 		bits = php_swf_get_bits (b, 0, 5);
257 		result->width = (php_swf_get_bits (b, 5 + bits, bits) -
258 			php_swf_get_bits (b, 5, bits)) / 20;
259 		result->height = (php_swf_get_bits (b, 5 + (3 * bits), bits) -
260 			php_swf_get_bits (b, 5 + (2 * bits), bits)) / 20;
261 	} else {
262 		result = NULL;
263 	}
264 
265 	efree (b);
266 	return result;
267 }
268 /* }}} */
269 #endif
270 
271 /* {{{ php_handle_swf */
php_handle_swf(php_stream * stream)272 static struct gfxinfo *php_handle_swf (php_stream * stream)
273 {
274 	struct gfxinfo *result = NULL;
275 	long bits;
276 	unsigned char a[32];
277 
278 	if (php_stream_seek(stream, 5, SEEK_CUR))
279 		return NULL;
280 
281 	if (php_stream_read(stream, (char*)a, sizeof(a)) != sizeof(a))
282 		return NULL;
283 
284 	result = (struct gfxinfo *) ecalloc (1, sizeof (struct gfxinfo));
285 	bits = php_swf_get_bits (a, 0, 5);
286 	result->width = (php_swf_get_bits (a, 5 + bits, bits) -
287 		php_swf_get_bits (a, 5, bits)) / 20;
288 	result->height = (php_swf_get_bits (a, 5 + (3 * bits), bits) -
289 		php_swf_get_bits (a, 5 + (2 * bits), bits)) / 20;
290 	result->bits     = 0;
291 	result->channels = 0;
292 	return result;
293 }
294 /* }}} */
295 
296 /* {{{ php_handle_png
297  * routine to handle PNG files */
php_handle_png(php_stream * stream)298 static struct gfxinfo *php_handle_png (php_stream * stream)
299 {
300 	struct gfxinfo *result = NULL;
301 	unsigned char dim[9];
302 /* Width:              4 bytes
303  * Height:             4 bytes
304  * Bit depth:          1 byte
305  * Color type:         1 byte
306  * Compression method: 1 byte
307  * Filter method:      1 byte
308  * Interlace method:   1 byte
309  */
310 
311 	if (php_stream_seek(stream, 8, SEEK_CUR))
312 		return NULL;
313 
314 	if((php_stream_read(stream, (char*)dim, sizeof(dim))) < sizeof(dim))
315 		return NULL;
316 
317 	result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
318 	result->width  = (((unsigned int)dim[0]) << 24) + (((unsigned int)dim[1]) << 16) + (((unsigned int)dim[2]) << 8) + ((unsigned int)dim[3]);
319 	result->height = (((unsigned int)dim[4]) << 24) + (((unsigned int)dim[5]) << 16) + (((unsigned int)dim[6]) << 8) + ((unsigned int)dim[7]);
320 	result->bits   = (unsigned int)dim[8];
321 	return result;
322 }
323 /* }}} */
324 
325 /* routines to handle JPEG data */
326 
327 /* some defines for the different JPEG block types */
328 #define M_SOF0  0xC0			/* Start Of Frame N */
329 #define M_SOF1  0xC1			/* N indicates which compression process */
330 #define M_SOF2  0xC2			/* Only SOF0-SOF2 are now in common use */
331 #define M_SOF3  0xC3
332 #define M_SOF5  0xC5			/* NB: codes C4 and CC are NOT SOF markers */
333 #define M_SOF6  0xC6
334 #define M_SOF7  0xC7
335 #define M_SOF9  0xC9
336 #define M_SOF10 0xCA
337 #define M_SOF11 0xCB
338 #define M_SOF13 0xCD
339 #define M_SOF14 0xCE
340 #define M_SOF15 0xCF
341 #define M_SOI   0xD8
342 #define M_EOI   0xD9			/* End Of Image (end of datastream) */
343 #define M_SOS   0xDA			/* Start Of Scan (begins compressed data) */
344 #define M_APP0  0xe0
345 #define M_APP1  0xe1
346 #define M_APP2  0xe2
347 #define M_APP3  0xe3
348 #define M_APP4  0xe4
349 #define M_APP5  0xe5
350 #define M_APP6  0xe6
351 #define M_APP7  0xe7
352 #define M_APP8  0xe8
353 #define M_APP9  0xe9
354 #define M_APP10 0xea
355 #define M_APP11 0xeb
356 #define M_APP12 0xec
357 #define M_APP13 0xed
358 #define M_APP14 0xee
359 #define M_APP15 0xef
360 #define M_COM   0xFE            /* COMment                                  */
361 
362 #define M_PSEUDO 0xFFD8			/* pseudo marker for start of image(byte 0) */
363 
364 /* {{{ php_read2 */
php_read2(php_stream * stream)365 static unsigned short php_read2(php_stream * stream)
366 {
367 	unsigned char a[2];
368 
369 	/* return 0 if we couldn't read enough data */
370 	if((php_stream_read(stream, (char *) a, sizeof(a))) < sizeof(a)) return 0;
371 
372 	return (((unsigned short)a[0]) << 8) + ((unsigned short)a[1]);
373 }
374 /* }}} */
375 
376 /* {{{ php_next_marker
377  * get next marker byte from file */
php_next_marker(php_stream * stream,int last_marker,int ff_read)378 static unsigned int php_next_marker(php_stream * stream, int last_marker, int ff_read)
379 {
380 	int a=0, marker;
381 
382 	/* get marker byte, swallowing possible padding                           */
383 	if (!ff_read) {
384 		size_t extraneous = 0;
385 
386 		while ((marker = php_stream_getc(stream)) != 0xff) {
387 			if (marker == EOF) {
388 				return M_EOI;/* we hit EOF */
389 	}
390 			extraneous++;
391 	}
392 		if (extraneous) {
393 			php_error_docref(NULL, E_WARNING, "Corrupt JPEG data: %zu extraneous bytes before marker", extraneous);
394 		}
395 	}
396 	a = 1;
397 	do {
398 		if ((marker = php_stream_getc(stream)) == EOF)
399 		{
400 			return M_EOI;/* we hit EOF */
401 		}
402 		a++;
403 	} while (marker == 0xff);
404 	if (a < 2)
405 	{
406 		return M_EOI; /* at least one 0xff is needed before marker code */
407 	}
408 	return (unsigned int)marker;
409 }
410 /* }}} */
411 
412 /* {{{ php_skip_variable
413  * skip over a variable-length block; assumes proper length marker */
php_skip_variable(php_stream * stream)414 static int php_skip_variable(php_stream * stream)
415 {
416 	zend_off_t length = ((unsigned int)php_read2(stream));
417 
418 	if (length < 2)	{
419 		return 0;
420 	}
421 	length = length - 2;
422 	php_stream_seek(stream, (zend_long)length, SEEK_CUR);
423 	return 1;
424 }
425 /* }}} */
426 
427 /* {{{ php_read_APP */
php_read_APP(php_stream * stream,unsigned int marker,zval * info)428 static int php_read_APP(php_stream * stream, unsigned int marker, zval *info)
429 {
430 	size_t length;
431 	char *buffer;
432 	char markername[16];
433 	zval *tmp;
434 
435 	length = php_read2(stream);
436 	if (length < 2)	{
437 		return 0;
438 	}
439 	length -= 2;				/* length includes itself */
440 
441 	buffer = emalloc(length);
442 
443 	if (php_stream_read(stream, buffer, (size_t) length) != length) {
444 		efree(buffer);
445 		return 0;
446 	}
447 
448 	snprintf(markername, sizeof(markername), "APP%d", marker - M_APP0);
449 
450 	if ((tmp = zend_hash_str_find(Z_ARRVAL_P(info), markername, strlen(markername))) == NULL) {
451 		/* XXX we only catch the 1st tag of it's kind! */
452 		add_assoc_stringl(info, markername, buffer, length);
453 	}
454 
455 	efree(buffer);
456 	return 1;
457 }
458 /* }}} */
459 
460 /* {{{ php_handle_jpeg
461    main loop to parse JPEG structure */
php_handle_jpeg(php_stream * stream,zval * info)462 static struct gfxinfo *php_handle_jpeg (php_stream * stream, zval *info)
463 {
464 	struct gfxinfo *result = NULL;
465 	unsigned int marker = M_PSEUDO;
466 	unsigned short length, ff_read=1;
467 
468 	for (;;) {
469 		marker = php_next_marker(stream, marker, ff_read);
470 		ff_read = 0;
471 		switch (marker) {
472 			case M_SOF0:
473 			case M_SOF1:
474 			case M_SOF2:
475 			case M_SOF3:
476 			case M_SOF5:
477 			case M_SOF6:
478 			case M_SOF7:
479 			case M_SOF9:
480 			case M_SOF10:
481 			case M_SOF11:
482 			case M_SOF13:
483 			case M_SOF14:
484 			case M_SOF15:
485 				if (result == NULL) {
486 					/* handle SOFn block */
487 					result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
488 					length = php_read2(stream);
489 					result->bits     = php_stream_getc(stream);
490 					result->height   = php_read2(stream);
491 					result->width    = php_read2(stream);
492 					result->channels = php_stream_getc(stream);
493 					if (!info || length < 8) { /* if we don't want an extanded info -> return */
494 						return result;
495 					}
496 					if (php_stream_seek(stream, length - 8, SEEK_CUR)) { /* file error after info */
497 						return result;
498 					}
499 				} else {
500 					if (!php_skip_variable(stream)) {
501 						return result;
502 					}
503 				}
504 				break;
505 
506 			case M_APP0:
507 			case M_APP1:
508 			case M_APP2:
509 			case M_APP3:
510 			case M_APP4:
511 			case M_APP5:
512 			case M_APP6:
513 			case M_APP7:
514 			case M_APP8:
515 			case M_APP9:
516 			case M_APP10:
517 			case M_APP11:
518 			case M_APP12:
519 			case M_APP13:
520 			case M_APP14:
521 			case M_APP15:
522 				if (info) {
523 					if (!php_read_APP(stream, marker, info)) { /* read all the app marks... */
524 						return result;
525 					}
526 				} else {
527 					if (!php_skip_variable(stream)) {
528 						return result;
529 					}
530 				}
531 				break;
532 
533 			case M_SOS:
534 			case M_EOI:
535 				return result;	/* we're about to hit image data, or are at EOF. stop processing. */
536 
537 			default:
538 				if (!php_skip_variable(stream)) { /* anything else isn't interesting */
539 					return result;
540 				}
541 				break;
542 		}
543 	}
544 
545 	return result; /* perhaps image broken -> no info but size */
546 }
547 /* }}} */
548 
549 /* {{{ php_read4 */
php_read4(php_stream * stream)550 static unsigned int php_read4(php_stream * stream)
551 {
552 	unsigned char a[4];
553 
554 	/* just return 0 if we hit the end-of-file */
555 	if ((php_stream_read(stream, (char*)a, sizeof(a))) != sizeof(a)) return 0;
556 
557 	return (((unsigned int)a[0]) << 24)
558 	     + (((unsigned int)a[1]) << 16)
559 	     + (((unsigned int)a[2]) <<  8)
560 	     + (((unsigned int)a[3]));
561 }
562 /* }}} */
563 
564 /* {{{ JPEG 2000 Marker Codes */
565 #define JPEG2000_MARKER_PREFIX 0xFF /* All marker codes start with this */
566 #define JPEG2000_MARKER_SOC 0x4F /* Start of Codestream */
567 #define JPEG2000_MARKER_SOT 0x90 /* Start of Tile part */
568 #define JPEG2000_MARKER_SOD 0x93 /* Start of Data */
569 #define JPEG2000_MARKER_EOC 0xD9 /* End of Codestream */
570 #define JPEG2000_MARKER_SIZ 0x51 /* Image and tile size */
571 #define JPEG2000_MARKER_COD 0x52 /* Coding style default */
572 #define JPEG2000_MARKER_COC 0x53 /* Coding style component */
573 #define JPEG2000_MARKER_RGN 0x5E /* Region of interest */
574 #define JPEG2000_MARKER_QCD 0x5C /* Quantization default */
575 #define JPEG2000_MARKER_QCC 0x5D /* Quantization component */
576 #define JPEG2000_MARKER_POC 0x5F /* Progression order change */
577 #define JPEG2000_MARKER_TLM 0x55 /* Tile-part lengths */
578 #define JPEG2000_MARKER_PLM 0x57 /* Packet length, main header */
579 #define JPEG2000_MARKER_PLT 0x58 /* Packet length, tile-part header */
580 #define JPEG2000_MARKER_PPM 0x60 /* Packed packet headers, main header */
581 #define JPEG2000_MARKER_PPT 0x61 /* Packed packet headers, tile part header */
582 #define JPEG2000_MARKER_SOP 0x91 /* Start of packet */
583 #define JPEG2000_MARKER_EPH 0x92 /* End of packet header */
584 #define JPEG2000_MARKER_CRG 0x63 /* Component registration */
585 #define JPEG2000_MARKER_COM 0x64 /* Comment */
586 /* }}} */
587 
588 /* {{{ php_handle_jpc
589    Main loop to parse JPEG2000 raw codestream structure */
php_handle_jpc(php_stream * stream)590 static struct gfxinfo *php_handle_jpc(php_stream * stream)
591 {
592 	struct gfxinfo *result = NULL;
593 	int highest_bit_depth, bit_depth;
594 	unsigned char first_marker_id;
595 	unsigned int i;
596 
597 	/* JPEG 2000 components can be vastly different from one another.
598 	   Each component can be sampled at a different resolution, use
599 	   a different colour space, have a separate colour depth, and
600 	   be compressed totally differently! This makes giving a single
601 	   "bit depth" answer somewhat problematic. For this implementation
602 	   we'll use the highest depth encountered. */
603 
604 	/* Get the single byte that remains after the file type identification */
605 	first_marker_id = php_stream_getc(stream);
606 
607 	/* Ensure that this marker is SIZ (as is mandated by the standard) */
608 	if (first_marker_id != JPEG2000_MARKER_SIZ) {
609 		php_error_docref(NULL, E_WARNING, "JPEG2000 codestream corrupt(Expected SIZ marker not found after SOC)");
610 		return NULL;
611 	}
612 
613 	result = (struct gfxinfo *)ecalloc(1, sizeof(struct gfxinfo));
614 
615 	php_read2(stream); /* Lsiz */
616 	php_read2(stream); /* Rsiz */
617 	result->width = php_read4(stream); /* Xsiz */
618 	result->height = php_read4(stream); /* Ysiz */
619 
620 #if MBO_0
621 	php_read4(stream); /* XOsiz */
622 	php_read4(stream); /* YOsiz */
623 	php_read4(stream); /* XTsiz */
624 	php_read4(stream); /* YTsiz */
625 	php_read4(stream); /* XTOsiz */
626 	php_read4(stream); /* YTOsiz */
627 #else
628 	if (php_stream_seek(stream, 24, SEEK_CUR)) {
629 		efree(result);
630 		return NULL;
631 	}
632 #endif
633 
634 	result->channels = php_read2(stream); /* Csiz */
635 	if ((result->channels == 0 && php_stream_eof(stream)) || result->channels > 256) {
636 		efree(result);
637 		return NULL;
638 	}
639 
640 	/* Collect bit depth info */
641 	highest_bit_depth = 0;
642 	for (i = 0; i < result->channels; i++) {
643 		bit_depth = php_stream_getc(stream); /* Ssiz[i] */
644 		bit_depth++;
645 		if (bit_depth > highest_bit_depth) {
646 			highest_bit_depth = bit_depth;
647 		}
648 
649 		php_stream_getc(stream); /* XRsiz[i] */
650 		php_stream_getc(stream); /* YRsiz[i] */
651 	}
652 
653 	result->bits = highest_bit_depth;
654 
655 	return result;
656 }
657 /* }}} */
658 
659 /* {{{ php_handle_jp2
660    main loop to parse JPEG 2000 JP2 wrapper format structure */
php_handle_jp2(php_stream * stream)661 static struct gfxinfo *php_handle_jp2(php_stream *stream)
662 {
663 	struct gfxinfo *result = NULL;
664 	unsigned int box_length;
665 	unsigned int box_type;
666 	char jp2c_box_id[] = {(char)0x6a, (char)0x70, (char)0x32, (char)0x63};
667 
668 	/* JP2 is a wrapper format for JPEG 2000. Data is contained within "boxes".
669 	   Boxes themselves can be contained within "super-boxes". Super-Boxes can
670 	   contain super-boxes which provides us with a hierarchical storage system.
671 
672 	   It is valid for a JP2 file to contain multiple individual codestreams.
673 	   We'll just look for the first codestream at the root of the box structure
674 	   and handle that.
675 	*/
676 
677 	for (;;)
678 	{
679 		box_length = php_read4(stream); /* LBox */
680 		/* TBox */
681 		if (php_stream_read(stream, (void *)&box_type, sizeof(box_type)) != sizeof(box_type)) {
682 			/* Use this as a general "out of stream" error */
683 			break;
684 		}
685 
686 		if (box_length == 1) {
687 			/* We won't handle XLBoxes */
688 			return NULL;
689 		}
690 
691 		if (!memcmp(&box_type, jp2c_box_id, 4))
692 		{
693 			/* Skip the first 3 bytes to emulate the file type examination */
694 			php_stream_seek(stream, 3, SEEK_CUR);
695 
696 			result = php_handle_jpc(stream);
697 			break;
698 		}
699 
700 		/* Stop if this was the last box */
701 		if ((int)box_length <= 0) {
702 			break;
703 		}
704 
705 		/* Skip over LBox (Which includes both TBox and LBox itself */
706 		if (php_stream_seek(stream, box_length - 8, SEEK_CUR)) {
707 			break;
708 		}
709 	}
710 
711 	if (result == NULL) {
712 		php_error_docref(NULL, E_WARNING, "JP2 file has no codestreams at root level");
713 	}
714 
715 	return result;
716 }
717 /* }}} */
718 
719 /* {{{ tiff constants */
720 PHPAPI const int php_tiff_bytes_per_format[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
721 
722 /* uncompressed only */
723 #define TAG_IMAGEWIDTH              0x0100
724 #define TAG_IMAGEHEIGHT             0x0101
725 /* compressed images only */
726 #define TAG_COMP_IMAGEWIDTH         0xA002
727 #define TAG_COMP_IMAGEHEIGHT        0xA003
728 
729 #define TAG_FMT_BYTE       1
730 #define TAG_FMT_STRING     2
731 #define TAG_FMT_USHORT     3
732 #define TAG_FMT_ULONG      4
733 #define TAG_FMT_URATIONAL  5
734 #define TAG_FMT_SBYTE      6
735 #define TAG_FMT_UNDEFINED  7
736 #define TAG_FMT_SSHORT     8
737 #define TAG_FMT_SLONG      9
738 #define TAG_FMT_SRATIONAL 10
739 #define TAG_FMT_SINGLE    11
740 #define TAG_FMT_DOUBLE    12
741 /* }}} */
742 
743 /* {{{ php_ifd_get16u
744  * Convert a 16 bit unsigned value from file's native byte order */
php_ifd_get16u(void * Short,int motorola_intel)745 static int php_ifd_get16u(void *Short, int motorola_intel)
746 {
747 	if (motorola_intel) {
748 		return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1];
749 	} else {
750 		return (((unsigned char *)Short)[1] << 8) | ((unsigned char *)Short)[0];
751 	}
752 }
753 /* }}} */
754 
755 /* {{{ php_ifd_get16s
756  * Convert a 16 bit signed value from file's native byte order */
php_ifd_get16s(void * Short,int motorola_intel)757 static signed short php_ifd_get16s(void *Short, int motorola_intel)
758 {
759 	return (signed short)php_ifd_get16u(Short, motorola_intel);
760 }
761 /* }}} */
762 
763 /* {{{ php_ifd_get32s
764  * Convert a 32 bit signed value from file's native byte order */
php_ifd_get32s(void * Long,int motorola_intel)765 static int php_ifd_get32s(void *Long, int motorola_intel)
766 {
767 	if (motorola_intel) {
768 		return  ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16)
769 		      | (((unsigned char *)Long)[2] << 8 ) | (((unsigned char *)Long)[3] << 0 );
770 	} else {
771 		return  ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16)
772 		      | (((unsigned char *)Long)[1] << 8 ) | (((unsigned char *)Long)[0] << 0 );
773 	}
774 }
775 /* }}} */
776 
777 /* {{{ php_ifd_get32u
778  * Convert a 32 bit unsigned value from file's native byte order */
php_ifd_get32u(void * Long,int motorola_intel)779 static unsigned php_ifd_get32u(void *Long, int motorola_intel)
780 {
781 	return (unsigned)php_ifd_get32s(Long, motorola_intel) & 0xffffffff;
782 }
783 /* }}} */
784 
785 /* {{{ php_handle_tiff
786    main loop to parse TIFF structure */
php_handle_tiff(php_stream * stream,zval * info,int motorola_intel)787 static struct gfxinfo *php_handle_tiff (php_stream * stream, zval *info, int motorola_intel)
788 {
789 	struct gfxinfo *result = NULL;
790 	int i, num_entries;
791 	unsigned char *dir_entry;
792 	size_t ifd_size, dir_size, entry_value, width=0, height=0, ifd_addr;
793 	int entry_tag , entry_type;
794 	char *ifd_data, ifd_ptr[4];
795 
796 	if (php_stream_read(stream, ifd_ptr, 4) != 4)
797 		return NULL;
798 	ifd_addr = php_ifd_get32u(ifd_ptr, motorola_intel);
799 	if (php_stream_seek(stream, ifd_addr-8, SEEK_CUR))
800 		return NULL;
801 	ifd_size = 2;
802 	ifd_data = emalloc(ifd_size);
803 	if (php_stream_read(stream, ifd_data, 2) != 2) {
804 		efree(ifd_data);
805 		return NULL;
806 	}
807 	num_entries = php_ifd_get16u(ifd_data, motorola_intel);
808 	dir_size = 2/*num dir entries*/ +12/*length of entry*/*num_entries +4/* offset to next ifd (points to thumbnail or NULL)*/;
809 	ifd_size = dir_size;
810 	ifd_data = erealloc(ifd_data,ifd_size);
811 	if (php_stream_read(stream, ifd_data+2, dir_size-2) != dir_size-2) {
812 		efree(ifd_data);
813 		return NULL;
814 	}
815 	/* now we have the directory we can look how long it should be */
816 	ifd_size = dir_size;
817 	for(i=0;i<num_entries;i++) {
818 		dir_entry 	 = (unsigned char *) ifd_data+2+i*12;
819 		entry_tag    = php_ifd_get16u(dir_entry+0, motorola_intel);
820 		entry_type   = php_ifd_get16u(dir_entry+2, motorola_intel);
821 		switch(entry_type) {
822 			case TAG_FMT_BYTE:
823 			case TAG_FMT_SBYTE:
824 				entry_value  = (size_t)(dir_entry[8]);
825 				break;
826 			case TAG_FMT_USHORT:
827 				entry_value  = php_ifd_get16u(dir_entry+8, motorola_intel);
828 				break;
829 			case TAG_FMT_SSHORT:
830 				entry_value  = php_ifd_get16s(dir_entry+8, motorola_intel);
831 				break;
832 			case TAG_FMT_ULONG:
833 				entry_value  = php_ifd_get32u(dir_entry+8, motorola_intel);
834 				break;
835 			case TAG_FMT_SLONG:
836 				entry_value  = php_ifd_get32s(dir_entry+8, motorola_intel);
837 				break;
838 			default:
839 				continue;
840 		}
841 		switch(entry_tag) {
842 			case TAG_IMAGEWIDTH:
843 			case TAG_COMP_IMAGEWIDTH:
844 				width  = entry_value;
845 				break;
846 			case TAG_IMAGEHEIGHT:
847 			case TAG_COMP_IMAGEHEIGHT:
848 				height = entry_value;
849 				break;
850 		}
851 	}
852 	efree(ifd_data);
853 	if ( width && height) {
854 		/* not the same when in for-loop */
855 		result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
856 		result->height   = height;
857 		result->width    = width;
858 		result->bits     = 0;
859 		result->channels = 0;
860 		return result;
861 	}
862 	return NULL;
863 }
864 /* }}} */
865 
866 /* {{{ php_handle_psd */
php_handle_iff(php_stream * stream)867 static struct gfxinfo *php_handle_iff(php_stream * stream)
868 {
869 	struct gfxinfo * result;
870 	unsigned char a[10];
871 	int chunkId;
872 	int size;
873 	short width, height, bits;
874 
875 	if (php_stream_read(stream, (char *) a, 8) != 8) {
876 		return NULL;
877 	}
878 	if (strncmp((char *) a+4, "ILBM", 4) && strncmp((char *) a+4, "PBM ", 4)) {
879 		return NULL;
880 	}
881 
882 	/* loop chunks to find BMHD chunk */
883 	do {
884 		if (php_stream_read(stream, (char*)a, 8) != 8) {
885 			return NULL;
886 		}
887 		chunkId = php_ifd_get32s(a+0, 1);
888 		size    = php_ifd_get32s(a+4, 1);
889 		if (size < 0) {
890 			return NULL;
891 		}
892 		if ((size & 1) == 1) {
893 			size++;
894 		}
895 		if (chunkId == 0x424d4844) { /* BMHD chunk */
896 			if (size < 9 || php_stream_read(stream, (char*)a, 9) != 9) {
897 				return NULL;
898 			}
899 			width  = php_ifd_get16s(a+0, 1);
900 			height = php_ifd_get16s(a+2, 1);
901 			bits   = a[8] & 0xff;
902 			if (width > 0 && height > 0 && bits > 0 && bits < 33) {
903 				result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
904 				result->width    = width;
905 				result->height   = height;
906 				result->bits     = bits;
907 				result->channels = 0;
908 				return result;
909 			}
910 		} else {
911 			if (php_stream_seek(stream, size, SEEK_CUR)) {
912 				return NULL;
913 			}
914 		}
915 	} while (1);
916 }
917 /* }}} */
918 
919 /* {{{ php_get_wbmp
920  * int WBMP file format type
921  * byte Header Type
922  *	byte Extended Header
923  *		byte Header Data (type 00 = multibyte)
924  *		byte Header Data (type 11 = name/pairs)
925  * int Number of columns
926  * int Number of rows
927  */
php_get_wbmp(php_stream * stream,struct gfxinfo ** result,int check)928 static int php_get_wbmp(php_stream *stream, struct gfxinfo **result, int check)
929 {
930 	int i, width = 0, height = 0;
931 
932 	if (php_stream_rewind(stream)) {
933 		return 0;
934 	}
935 
936 	/* get type */
937 	if (php_stream_getc(stream) != 0) {
938 		return 0;
939 	}
940 
941 	/* skip header */
942 	do {
943 		i = php_stream_getc(stream);
944 		if (i < 0) {
945 			return 0;
946 		}
947 	} while (i & 0x80);
948 
949 	/* get width */
950 	do {
951 		i = php_stream_getc(stream);
952 		if (i < 0) {
953 			return 0;
954 		}
955 		width = (width << 7) | (i & 0x7f);
956         /* maximum valid width for wbmp (although 127 may be a more accurate one) */
957         if (width > 2048) {
958             return 0;
959         }
960 	} while (i & 0x80);
961 
962 	/* get height */
963 	do {
964 		i = php_stream_getc(stream);
965 		if (i < 0) {
966 			return 0;
967 		}
968 		height = (height << 7) | (i & 0x7f);
969         /* maximum valid height for wbmp (although 127 may be a more accurate one) */
970         if (height > 2048) {
971             return 0;
972         }
973 	} while (i & 0x80);
974 
975 	if (!height || !width) {
976 		return 0;
977 	}
978 
979 	if (!check) {
980 		(*result)->width = width;
981 		(*result)->height = height;
982 	}
983 
984 	return IMAGE_FILETYPE_WBMP;
985 }
986 /* }}} */
987 
988 /* {{{ php_handle_wbmp */
php_handle_wbmp(php_stream * stream)989 static struct gfxinfo *php_handle_wbmp(php_stream * stream)
990 {
991 	struct gfxinfo *result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
992 
993 	if (!php_get_wbmp(stream, &result, 0)) {
994 		efree(result);
995 		return NULL;
996 	}
997 
998 	return result;
999 }
1000 /* }}} */
1001 
1002 /* {{{ php_get_xbm */
php_get_xbm(php_stream * stream,struct gfxinfo ** result)1003 static int php_get_xbm(php_stream *stream, struct gfxinfo **result)
1004 {
1005     char *fline;
1006     char *iname;
1007     char *type;
1008     int value;
1009     unsigned int width = 0, height = 0;
1010 
1011 	if (result) {
1012 		*result = NULL;
1013 	}
1014 	if (php_stream_rewind(stream)) {
1015 		return 0;
1016 	}
1017 	while ((fline=php_stream_gets(stream, NULL, 0)) != NULL) {
1018 		iname = estrdup(fline); /* simple way to get necessary buffer of required size */
1019 		if (sscanf(fline, "#define %s %d", iname, &value) == 2) {
1020 			if (!(type = strrchr(iname, '_'))) {
1021 				type = iname;
1022 			} else {
1023 				type++;
1024 			}
1025 
1026 			if (!strcmp("width", type)) {
1027 				width = (unsigned int) value;
1028 				if (height) {
1029 					efree(iname);
1030 					break;
1031 				}
1032 			}
1033 			if (!strcmp("height", type)) {
1034 				height = (unsigned int) value;
1035 				if (width) {
1036 					efree(iname);
1037 					break;
1038 				}
1039 			}
1040 		}
1041 		efree(fline);
1042 		efree(iname);
1043 	}
1044 	if (fline) {
1045 		efree(fline);
1046 	}
1047 
1048 	if (width && height) {
1049 		if (result) {
1050 			*result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
1051 			(*result)->width = width;
1052 			(*result)->height = height;
1053 		}
1054 		return IMAGE_FILETYPE_XBM;
1055 	}
1056 
1057 	return 0;
1058 }
1059 /* }}} */
1060 
1061 /* {{{ php_handle_xbm */
php_handle_xbm(php_stream * stream)1062 static struct gfxinfo *php_handle_xbm(php_stream * stream)
1063 {
1064 	struct gfxinfo *result;
1065 	php_get_xbm(stream, &result);
1066 	return result;
1067 }
1068 /* }}} */
1069 
1070 /* {{{ php_handle_ico */
php_handle_ico(php_stream * stream)1071 static struct gfxinfo *php_handle_ico(php_stream * stream)
1072 {
1073 	struct gfxinfo *result = NULL;
1074 	unsigned char dim[16];
1075 	int num_icons = 0;
1076 
1077 	if (php_stream_read(stream, (char *) dim, 2) != 2)
1078 		return NULL;
1079 
1080 	num_icons = (((unsigned int)dim[1]) << 8) + ((unsigned int) dim[0]);
1081 
1082 	if (num_icons < 1 || num_icons > 255)
1083 		return NULL;
1084 
1085 	result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
1086 
1087 	while (num_icons > 0)
1088 	{
1089 		if (php_stream_read(stream, (char *) dim, sizeof(dim)) != sizeof(dim))
1090 			break;
1091 
1092 		if ((((unsigned int)dim[7]) <<  8) +  ((unsigned int)dim[6]) >= result->bits)
1093 		{
1094 			result->width    =  (unsigned int)dim[0];
1095 			result->height   =  (unsigned int)dim[1];
1096 			result->bits     =  (((unsigned int)dim[7]) <<  8) +  ((unsigned int)dim[6]);
1097 		}
1098 		num_icons--;
1099 	}
1100 
1101 	if (0 == result->width)
1102 		result->width = 256;
1103 
1104 	if (0 == result->height)
1105 		result->height = 256;
1106 
1107 	return result;
1108 }
1109 /* }}} */
1110 
1111 /* {{{ php_handle_webp */
php_handle_webp(php_stream * stream)1112 static struct gfxinfo *php_handle_webp(php_stream * stream)
1113 {
1114 	struct gfxinfo *result = NULL;
1115 	const char sig[3] = {'V', 'P', '8'};
1116 	unsigned char buf[18];
1117 	char format;
1118 
1119 	if (php_stream_read(stream, (char *) buf, 18) != 18)
1120 		return NULL;
1121 
1122 	if (memcmp(buf, sig, 3)) {
1123 		return NULL;
1124 	}
1125 	switch (buf[3]) {
1126 		case ' ':
1127 		case 'L':
1128 		case 'X':
1129 			format = buf[3];
1130 			break;
1131 		default:
1132 			return NULL;
1133 	}
1134 
1135 	result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
1136 
1137 	switch (format) {
1138 		case ' ':
1139 			result->width = buf[14] + ((buf[15] & 0x3F) << 8);
1140 			result->height = buf[16] + ((buf[17] & 0x3F) << 8);
1141 			break;
1142 		case 'L':
1143 			result->width = buf[9] + ((buf[10] & 0x3F) << 8) + 1;
1144 			result->height = (buf[10] >> 6) + (buf[11] << 2) + ((buf[12] & 0xF) << 10) + 1;
1145 			break;
1146 		case 'X':
1147 			result->width = buf[12] + (buf[13] << 8) + (buf[14] << 16) + 1;
1148 			result->height = buf[15] + (buf[16] << 8) + (buf[17] << 16) + 1;
1149 			break;
1150 	}
1151 	result->bits = 8; /* always 1 byte */
1152 
1153 	return result;
1154 }
1155 /* }}} */
1156 
1157 /* {{{ php_image_type_to_mime_type
1158  * Convert internal image_type to mime type */
php_image_type_to_mime_type(int image_type)1159 PHPAPI char * php_image_type_to_mime_type(int image_type)
1160 {
1161 	switch( image_type) {
1162 		case IMAGE_FILETYPE_GIF:
1163 			return "image/gif";
1164 		case IMAGE_FILETYPE_JPEG:
1165 			return "image/jpeg";
1166 		case IMAGE_FILETYPE_PNG:
1167 			return "image/png";
1168 		case IMAGE_FILETYPE_SWF:
1169 		case IMAGE_FILETYPE_SWC:
1170 			return "application/x-shockwave-flash";
1171 		case IMAGE_FILETYPE_PSD:
1172 			return "image/psd";
1173 		case IMAGE_FILETYPE_BMP:
1174 			return "image/bmp";
1175 		case IMAGE_FILETYPE_TIFF_II:
1176 		case IMAGE_FILETYPE_TIFF_MM:
1177 			return "image/tiff";
1178 		case IMAGE_FILETYPE_IFF:
1179 			return "image/iff";
1180 		case IMAGE_FILETYPE_WBMP:
1181 			return "image/vnd.wap.wbmp";
1182 		case IMAGE_FILETYPE_JPC:
1183 			return "application/octet-stream";
1184 		case IMAGE_FILETYPE_JP2:
1185 			return "image/jp2";
1186 		case IMAGE_FILETYPE_XBM:
1187 			return "image/xbm";
1188 		case IMAGE_FILETYPE_ICO:
1189 			return "image/vnd.microsoft.icon";
1190 		case IMAGE_FILETYPE_WEBP:
1191 			return "image/webp";
1192 		default:
1193 		case IMAGE_FILETYPE_UNKNOWN:
1194 			return "application/octet-stream"; /* suppose binary format */
1195 	}
1196 }
1197 /* }}} */
1198 
1199 /* {{{ Get Mime-Type for image-type returned by getimagesize, exif_read_data, exif_thumbnail, exif_imagetype */
PHP_FUNCTION(image_type_to_mime_type)1200 PHP_FUNCTION(image_type_to_mime_type)
1201 {
1202 	zend_long p_image_type;
1203 
1204 	ZEND_PARSE_PARAMETERS_START(1, 1)
1205 		Z_PARAM_LONG(p_image_type)
1206 	ZEND_PARSE_PARAMETERS_END();
1207 
1208 	ZVAL_STRING(return_value, (char*)php_image_type_to_mime_type(p_image_type));
1209 }
1210 /* }}} */
1211 
1212 /* {{{ Get file extension for image-type returned by getimagesize, exif_read_data, exif_thumbnail, exif_imagetype */
PHP_FUNCTION(image_type_to_extension)1213 PHP_FUNCTION(image_type_to_extension)
1214 {
1215 	zend_long image_type;
1216 	zend_bool inc_dot=1;
1217 	const char *imgext = NULL;
1218 
1219 	ZEND_PARSE_PARAMETERS_START(1, 2)
1220 		Z_PARAM_LONG(image_type)
1221 		Z_PARAM_OPTIONAL
1222 		Z_PARAM_BOOL(inc_dot)
1223 	ZEND_PARSE_PARAMETERS_END();
1224 
1225 	switch (image_type) {
1226 		case IMAGE_FILETYPE_GIF:
1227 			imgext = ".gif";
1228 			break;
1229 		case IMAGE_FILETYPE_JPEG:
1230 			imgext = ".jpeg";
1231 			break;
1232 		case IMAGE_FILETYPE_PNG:
1233 			imgext = ".png";
1234 			break;
1235 		case IMAGE_FILETYPE_SWF:
1236 		case IMAGE_FILETYPE_SWC:
1237 			imgext = ".swf";
1238 			break;
1239 		case IMAGE_FILETYPE_PSD:
1240 			imgext = ".psd";
1241 			break;
1242 		case IMAGE_FILETYPE_BMP:
1243 		case IMAGE_FILETYPE_WBMP:
1244 			imgext = ".bmp";
1245 			break;
1246 		case IMAGE_FILETYPE_TIFF_II:
1247 		case IMAGE_FILETYPE_TIFF_MM:
1248 			imgext = ".tiff";
1249 			break;
1250 		case IMAGE_FILETYPE_IFF:
1251 			imgext = ".iff";
1252 			break;
1253 		case IMAGE_FILETYPE_JPC:
1254 			imgext = ".jpc";
1255 			break;
1256 		case IMAGE_FILETYPE_JP2:
1257 			imgext = ".jp2";
1258 			break;
1259 		case IMAGE_FILETYPE_JPX:
1260 			imgext = ".jpx";
1261 			break;
1262 		case IMAGE_FILETYPE_JB2:
1263 			imgext = ".jb2";
1264 			break;
1265 		case IMAGE_FILETYPE_XBM:
1266 			imgext = ".xbm";
1267 			break;
1268 		case IMAGE_FILETYPE_ICO:
1269 			imgext = ".ico";
1270 			break;
1271 		case IMAGE_FILETYPE_WEBP:
1272 			imgext = ".webp";
1273 			break;
1274 	}
1275 
1276 	if (imgext) {
1277 		RETURN_STRING(&imgext[!inc_dot]);
1278 	}
1279 
1280 	RETURN_FALSE;
1281 }
1282 /* }}} */
1283 
1284 /* {{{ php_imagetype
1285    detect filetype from first bytes */
php_getimagetype(php_stream * stream,const char * input,char * filetype)1286 PHPAPI int php_getimagetype(php_stream * stream, const char *input, char *filetype)
1287 {
1288 	char tmp[12];
1289     int twelve_bytes_read;
1290 
1291 	if ( !filetype) filetype = tmp;
1292 	if((php_stream_read(stream, filetype, 3)) != 3) {
1293 		php_error_docref(NULL, E_NOTICE, "Error reading from %s!", input);
1294 		return IMAGE_FILETYPE_UNKNOWN;
1295 	}
1296 
1297 /* BYTES READ: 3 */
1298 	if (!memcmp(filetype, php_sig_gif, 3)) {
1299 		return IMAGE_FILETYPE_GIF;
1300 	} else if (!memcmp(filetype, php_sig_jpg, 3)) {
1301 		return IMAGE_FILETYPE_JPEG;
1302 	} else if (!memcmp(filetype, php_sig_png, 3)) {
1303 		if (php_stream_read(stream, filetype+3, 5) != 5) {
1304 			php_error_docref(NULL, E_NOTICE, "Error reading from %s!", input);
1305 			return IMAGE_FILETYPE_UNKNOWN;
1306 		}
1307 		if (!memcmp(filetype, php_sig_png, 8)) {
1308 			return IMAGE_FILETYPE_PNG;
1309 		} else {
1310 			php_error_docref(NULL, E_WARNING, "PNG file corrupted by ASCII conversion");
1311 			return IMAGE_FILETYPE_UNKNOWN;
1312 		}
1313 	} else if (!memcmp(filetype, php_sig_swf, 3)) {
1314 		return IMAGE_FILETYPE_SWF;
1315 	} else if (!memcmp(filetype, php_sig_swc, 3)) {
1316 		return IMAGE_FILETYPE_SWC;
1317 	} else if (!memcmp(filetype, php_sig_psd, 3)) {
1318 		return IMAGE_FILETYPE_PSD;
1319 	} else if (!memcmp(filetype, php_sig_bmp, 2)) {
1320 		return IMAGE_FILETYPE_BMP;
1321 	} else if (!memcmp(filetype, php_sig_jpc, 3)) {
1322 		return IMAGE_FILETYPE_JPC;
1323 	} else if (!memcmp(filetype, php_sig_riff, 3)) {
1324 		if (php_stream_read(stream, filetype+3, 9) != 9) {
1325 			php_error_docref(NULL, E_NOTICE, "Error reading from %s!", input);
1326 			return IMAGE_FILETYPE_UNKNOWN;
1327 		}
1328 		if (!memcmp(filetype+8, php_sig_webp, 4)) {
1329 			return IMAGE_FILETYPE_WEBP;
1330 		} else {
1331 			return IMAGE_FILETYPE_UNKNOWN;
1332 		}
1333 	}
1334 
1335 	if (php_stream_read(stream, filetype+3, 1) != 1) {
1336 		php_error_docref(NULL, E_NOTICE, "Error reading from %s!", input);
1337 		return IMAGE_FILETYPE_UNKNOWN;
1338 	}
1339 /* BYTES READ: 4 */
1340 	if (!memcmp(filetype, php_sig_tif_ii, 4)) {
1341 		return IMAGE_FILETYPE_TIFF_II;
1342 	} else if (!memcmp(filetype, php_sig_tif_mm, 4)) {
1343 		return IMAGE_FILETYPE_TIFF_MM;
1344 	} else if (!memcmp(filetype, php_sig_iff, 4)) {
1345 		return IMAGE_FILETYPE_IFF;
1346 	} else if (!memcmp(filetype, php_sig_ico, 4)) {
1347 		return IMAGE_FILETYPE_ICO;
1348 	}
1349 
1350     /* WBMP may be smaller than 12 bytes, so delay error */
1351 	twelve_bytes_read = (php_stream_read(stream, filetype+4, 8) == 8);
1352 
1353 /* BYTES READ: 12 */
1354    	if (twelve_bytes_read && !memcmp(filetype, php_sig_jp2, 12)) {
1355 		return IMAGE_FILETYPE_JP2;
1356 	}
1357 
1358 /* AFTER ALL ABOVE FAILED */
1359 	if (php_get_wbmp(stream, NULL, 1)) {
1360 		return IMAGE_FILETYPE_WBMP;
1361 	}
1362     if (!twelve_bytes_read) {
1363 		php_error_docref(NULL, E_NOTICE, "Error reading from %s!", input);
1364 		return IMAGE_FILETYPE_UNKNOWN;
1365     }
1366 	if (php_get_xbm(stream, NULL)) {
1367 		return IMAGE_FILETYPE_XBM;
1368 	}
1369 	return IMAGE_FILETYPE_UNKNOWN;
1370 }
1371 /* }}} */
1372 
php_getimagesize_from_stream(php_stream * stream,char * input,zval * info,INTERNAL_FUNCTION_PARAMETERS)1373 static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *info, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
1374 {
1375 	int itype = 0;
1376 	struct gfxinfo *result = NULL;
1377 
1378 	if (!stream) {
1379 		RETURN_FALSE;
1380 	}
1381 
1382 	itype = php_getimagetype(stream, input, NULL);
1383 	switch( itype) {
1384 		case IMAGE_FILETYPE_GIF:
1385 			result = php_handle_gif(stream);
1386 			break;
1387 		case IMAGE_FILETYPE_JPEG:
1388 			if (info) {
1389 				result = php_handle_jpeg(stream, info);
1390 			} else {
1391 				result = php_handle_jpeg(stream, NULL);
1392 			}
1393 			break;
1394 		case IMAGE_FILETYPE_PNG:
1395 			result = php_handle_png(stream);
1396 			break;
1397 		case IMAGE_FILETYPE_SWF:
1398 			result = php_handle_swf(stream);
1399 			break;
1400 		case IMAGE_FILETYPE_SWC:
1401 #if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
1402 			result = php_handle_swc(stream);
1403 #else
1404 			php_error_docref(NULL, E_NOTICE, "The image is a compressed SWF file, but you do not have a static version of the zlib extension enabled");
1405 #endif
1406 			break;
1407 		case IMAGE_FILETYPE_PSD:
1408 			result = php_handle_psd(stream);
1409 			break;
1410 		case IMAGE_FILETYPE_BMP:
1411 			result = php_handle_bmp(stream);
1412 			break;
1413 		case IMAGE_FILETYPE_TIFF_II:
1414 			result = php_handle_tiff(stream, NULL, 0);
1415 			break;
1416 		case IMAGE_FILETYPE_TIFF_MM:
1417 			result = php_handle_tiff(stream, NULL, 1);
1418 			break;
1419 		case IMAGE_FILETYPE_JPC:
1420 			result = php_handle_jpc(stream);
1421 			break;
1422 		case IMAGE_FILETYPE_JP2:
1423 			result = php_handle_jp2(stream);
1424 			break;
1425 		case IMAGE_FILETYPE_IFF:
1426 			result = php_handle_iff(stream);
1427 			break;
1428 		case IMAGE_FILETYPE_WBMP:
1429 			result = php_handle_wbmp(stream);
1430 			break;
1431 		case IMAGE_FILETYPE_XBM:
1432 			result = php_handle_xbm(stream);
1433 			break;
1434 		case IMAGE_FILETYPE_ICO:
1435 			result = php_handle_ico(stream);
1436 			break;
1437 		case IMAGE_FILETYPE_WEBP:
1438 			result = php_handle_webp(stream);
1439 			break;
1440 		default:
1441 		case IMAGE_FILETYPE_UNKNOWN:
1442 			break;
1443 	}
1444 
1445 	if (result) {
1446 		char temp[MAX_LENGTH_OF_LONG * 2 + sizeof("width=\"\" height=\"\"")];
1447 		array_init(return_value);
1448 		add_index_long(return_value, 0, result->width);
1449 		add_index_long(return_value, 1, result->height);
1450 		add_index_long(return_value, 2, itype);
1451 		snprintf(temp, sizeof(temp), "width=\"%d\" height=\"%d\"", result->width, result->height);
1452 		add_index_string(return_value, 3, temp);
1453 
1454 		if (result->bits != 0) {
1455 			add_assoc_long(return_value, "bits", result->bits);
1456 		}
1457 		if (result->channels != 0) {
1458 			add_assoc_long(return_value, "channels", result->channels);
1459 		}
1460 		add_assoc_string(return_value, "mime", (char*)php_image_type_to_mime_type(itype));
1461 		efree(result);
1462 	} else {
1463 		RETURN_FALSE;
1464 	}
1465 }
1466 /* }}} */
1467 
1468 #define FROM_DATA 0
1469 #define FROM_PATH 1
1470 
php_getimagesize_from_any(INTERNAL_FUNCTION_PARAMETERS,int mode)1471 static void php_getimagesize_from_any(INTERNAL_FUNCTION_PARAMETERS, int mode) {  /* {{{ */
1472 	zval *info = NULL;
1473 	php_stream *stream = NULL;
1474 	char *input;
1475 	size_t input_len;
1476 	const int argc = ZEND_NUM_ARGS();
1477 
1478 	ZEND_PARSE_PARAMETERS_START(1, 2)
1479 		Z_PARAM_STRING(input, input_len)
1480 		Z_PARAM_OPTIONAL
1481 		Z_PARAM_ZVAL(info)
1482 	ZEND_PARSE_PARAMETERS_END();
1483 
1484 	if (mode == FROM_PATH && CHECK_NULL_PATH(input, input_len)) {
1485 		zend_argument_value_error(1, "must not contain any null bytes");
1486 		RETURN_THROWS();
1487 	}
1488 
1489 	if (argc == 2) {
1490 		info = zend_try_array_init(info);
1491 		if (!info) {
1492 			RETURN_THROWS();
1493 		}
1494 	}
1495 
1496 	if (mode == FROM_PATH) {
1497 		stream = php_stream_open_wrapper(input, "rb", STREAM_MUST_SEEK|REPORT_ERRORS|IGNORE_PATH, NULL);
1498 	} else {
1499 		stream = php_stream_memory_open(TEMP_STREAM_READONLY, input, input_len);
1500 	}
1501 
1502 	if (!stream) {
1503 		RETURN_FALSE;
1504 	}
1505 
1506 	php_getimagesize_from_stream(stream, input, info, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1507 	php_stream_close(stream);
1508 }
1509 /* }}} */
1510 
1511 /* {{{ Get the size of an image as 4-element array */
PHP_FUNCTION(getimagesize)1512 PHP_FUNCTION(getimagesize)
1513 {
1514 	php_getimagesize_from_any(INTERNAL_FUNCTION_PARAM_PASSTHRU, FROM_PATH);
1515 }
1516 /* }}} */
1517 
1518 /* {{{ Get the size of an image as 4-element array */
PHP_FUNCTION(getimagesizefromstring)1519 PHP_FUNCTION(getimagesizefromstring)
1520 {
1521 	php_getimagesize_from_any(INTERNAL_FUNCTION_PARAM_PASSTHRU, FROM_DATA);
1522 }
1523 /* }}} */
1524