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