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