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