1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 / Imagick |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2013 Mikko Koppanen, Scott MacVicar |
6 | ImageMagick (c) ImageMagick Studio LLC |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
16 | Author: Mikko Kopppanen <mkoppanen@php.net> |
17 | Scott MacVicar <scottmac@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 #include "php_imagick.h"
22 #include "php_imagick_file.h"
23 #include "php_imagick_macros.h"
24 #include "php_imagick_defs.h"
25
26 #if ZEND_MODULE_API_NO > 20060613
27 # define IMAGICK_INIT_ERROR_HANDLING zend_error_handling error_handling
28 # define IMAGICK_SET_ERROR_HANDLING_THROW zend_replace_error_handling(EH_THROW, php_imagick_exception_class_entry, &error_handling TSRMLS_CC)
29 # define IMAGICK_RESTORE_ERROR_HANDLING zend_restore_error_handling(&error_handling TSRMLS_CC)
30 #else
31 # define IMAGICK_INIT_ERROR_HANDLING
32 # define IMAGICK_SET_ERROR_HANDLING_THROW php_set_error_handling(EH_THROW, php_imagick_exception_class_entry TSRMLS_CC)
33 # define IMAGICK_RESTORE_ERROR_HANDLING php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC)
34 #endif
35
36 #ifndef S_ISDIR
37 # define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
38 #endif
39
40 static
php_imagick_is_virtual_format(const char * format)41 zend_bool php_imagick_is_virtual_format(const char *format)
42 {
43 int i, elements;
44
45 const char *virtual_fmt[] = {
46 "CAPTION",
47 "CLIPBOARD",
48 "FRACTAL",
49 "GRADIENT",
50 "LABEL",
51 "MATTE",
52 "NULL",
53 "PLASMA",
54 "PRINT",
55 "SCAN",
56 "RADIAL-GRADIENT",
57 "SCANX",
58 "WIN",
59 #ifndef PHP_WIN32
60 "X",
61 #endif
62 "XC",
63 "MAGICK",
64 "GRANITE",
65 "LOGO",
66 "NETSCAPE",
67 "ROSE"
68 };
69
70 elements = sizeof (virtual_fmt) / sizeof (virtual_fmt [0]);
71
72 for (i = 0; i < elements; i++) {
73 if (strcasecmp(format, virtual_fmt[i]) == 0) {
74 return 1;
75 }
76 }
77 return 0;
78 }
79
80 static
php_imagick_is_url(const char * filename TSRMLS_DC)81 zend_bool php_imagick_is_url(const char *filename TSRMLS_DC)
82 {
83 const char *path_for_open;
84
85 if (php_stream_locate_url_wrapper(filename, &path_for_open, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC)) {
86 return 1;
87 }
88 return 0;
89 }
90
php_imagick_file_init(struct php_imagick_file_t * file,const char * filename,size_t filename_len TSRMLS_DC)91 zend_bool php_imagick_file_init(struct php_imagick_file_t *file, const char *filename, size_t filename_len TSRMLS_DC)
92 {
93 char magick_path[MaxTextExtent], head_path[MaxTextExtent], tail_path[MaxTextExtent], buffer[MaxTextExtent];
94
95 if (!filename_len) {
96 return 0;
97 }
98
99 /* Undefined for now */
100 file->type = ImagickUndefinedType;
101
102 if (filename_len >= MaxTextExtent) {
103 return 0;
104 }
105
106 /* Take a copy of the original string */
107 strlcpy(file->filename, filename, MaxTextExtent);
108 file->filename_len = filename_len;
109
110 /* Break the path into pieces */
111 memset(magick_path, 0, MaxTextExtent);
112 GetPathComponent(file->filename, MagickPath, magick_path);
113
114 /* The path has a format identifier, check for url and virtual format */
115 if (strlen(magick_path) > 0) {
116 /* Virtual format? */
117 if (php_imagick_is_virtual_format(magick_path)) {
118 file->type = ImagickVirtualFormat;
119 file->absolute_path = estrdup("");
120 return 1;
121 }
122 /* Is it an url? */
123 else if (php_imagick_is_url(filename TSRMLS_CC)) {
124 file->type = ImagickUri;
125 file->absolute_path = estrdup("");
126 return 1;
127 }
128 }
129
130 /* This is a normal file path */
131 file->type = ImagickFile;
132
133 memset(head_path, 0, MaxTextExtent);
134 memset(tail_path, 0, MaxTextExtent);
135
136 GetPathComponent(file->filename, HeadPath, head_path);
137 GetPathComponent(file->filename, TailPath, tail_path);
138
139 (void) snprintf(buffer, MaxTextExtent, "%s/%s", head_path, tail_path);
140
141 /* The full path to the file */
142 file->absolute_path = expand_filepath(buffer, NULL TSRMLS_CC);
143
144 /* Failed to resolve absolute path */
145 if (!file->absolute_path) {
146 file->absolute_path = estrdup("");
147 }
148 return 1;
149 }
150
php_imagick_file_deinit(struct php_imagick_file_t * file)151 void php_imagick_file_deinit(struct php_imagick_file_t *file)
152 {
153 if (file->absolute_path) {
154 efree(file->absolute_path);
155 file->absolute_path = NULL;
156 }
157 }
158
159 static
php_imagick_read_image_using_imagemagick(php_imagick_object * intern,struct php_imagick_file_t * file,ImagickOperationType type TSRMLS_DC)160 int php_imagick_read_image_using_imagemagick(php_imagick_object *intern, struct php_imagick_file_t *file, ImagickOperationType type TSRMLS_DC)
161 {
162
163 #if PHP_VERSION_ID < 70000
164 #if PHP_VERSION_ID >= 50600
165 #ifdef ZTS
166 // This suppresses an 'unused parameter' warning.
167 (void)tsrm_ls;
168 #endif
169 #endif
170 #endif
171
172 if (type == ImagickReadImage) {
173 if (MagickReadImage(intern->magick_wand, file->filename) == MagickFalse) {
174
175 #if PHP_VERSION_ID >= 70000
176 zend_stat_t st;
177 #else
178 struct stat st;
179 #endif
180
181 /* Resolved to a filename. Check that it's not a dir */
182 if (php_sys_stat(file->absolute_path, &st) == 0 && S_ISDIR(st.st_mode)) {
183 return IMAGICK_RW_PATH_IS_DIR;
184 }
185 return IMAGICK_RW_UNDERLYING_LIBRARY;
186 }
187 } else if (type == ImagickPingImage){
188 if (MagickPingImage(intern->magick_wand, file->filename) == MagickFalse) {
189 return IMAGICK_RW_UNDERLYING_LIBRARY;
190 }
191 } else {
192 return IMAGICK_RW_UNDERLYING_LIBRARY;
193 }
194
195 MagickSetImageFilename(intern->magick_wand, file->absolute_path);
196 MagickSetLastIterator(intern->magick_wand);
197 return IMAGICK_RW_OK;
198 }
199
200 static
php_imagick_read_image_using_php_streams(php_imagick_object * intern,struct php_imagick_file_t * file,ImagickOperationType type TSRMLS_DC)201 int php_imagick_read_image_using_php_streams(php_imagick_object *intern, struct php_imagick_file_t *file, ImagickOperationType type TSRMLS_DC)
202 {
203 php_stream *stream;
204 MagickBooleanType status;
205 FILE *fp;
206 IMAGICK_INIT_ERROR_HANDLING;
207 IMAGICK_SET_ERROR_HANDLING_THROW;
208
209 #if PHP_VERSION_ID >= 70000
210 stream = php_stream_open_wrapper(file->filename, "rb", (IGNORE_PATH) & ~REPORT_ERRORS, NULL);
211 #else
212 stream = php_stream_open_wrapper(file->filename, "rb", (ENFORCE_SAFE_MODE|IGNORE_PATH) & ~REPORT_ERRORS, NULL);
213 #endif
214
215 if (!stream) {
216 IMAGICK_RESTORE_ERROR_HANDLING;
217 return IMAGICK_RW_UNDERLYING_LIBRARY;
218 }
219
220 if (php_stream_can_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_INTERNAL) == FAILURE ||
221 php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_INTERNAL, (void*)&fp, 0) == FAILURE) {
222
223 php_stream_close(stream);
224 IMAGICK_RESTORE_ERROR_HANDLING;
225 return IMAGICK_RW_UNDERLYING_LIBRARY;
226 }
227
228 IMAGICK_RESTORE_ERROR_HANDLING;
229
230 if (type == ImagickReadImage) {
231 status = MagickReadImageFile(intern->magick_wand, fp);
232 } else if (type == ImagickPingImage){
233 status = MagickPingImageFile(intern->magick_wand, fp);
234 } else {
235 php_stream_close(stream);
236 return IMAGICK_RW_UNDERLYING_LIBRARY;
237 }
238
239 if (status == MagickFalse) {
240 php_stream_close(stream);
241 return IMAGICK_RW_UNDERLYING_LIBRARY;
242 }
243
244 MagickSetImageFilename(intern->magick_wand, file->absolute_path);
245 php_stream_close(stream);
246
247 if (status == MagickFalse) {
248 return IMAGICK_RW_UNDERLYING_LIBRARY;
249 }
250
251 MagickSetLastIterator(intern->magick_wand);
252 return IMAGICK_RW_OK;
253 }
254
php_imagick_safe_mode_check(const char * filename TSRMLS_DC)255 int php_imagick_safe_mode_check(const char *filename TSRMLS_DC)
256 {
257 #if defined(CHECKUID_CHECK_FILE_AND_DIR)
258 if (PG(safe_mode) && (!php_checkuid_ex(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR, CHECKUID_NO_ERRORS))) {
259 return IMAGICK_RW_SAFE_MODE_ERROR;
260 }
261 #endif
262 if (PG(open_basedir) && php_check_open_basedir_ex(filename, 0 TSRMLS_CC)) {
263 return IMAGICK_RW_OPEN_BASEDIR_ERROR;
264 }
265
266 return IMAGICK_RW_OK;
267 }
268
php_imagick_read_file(php_imagick_object * intern,struct php_imagick_file_t * file,ImagickOperationType type TSRMLS_DC)269 php_imagick_rw_result_t php_imagick_read_file(php_imagick_object *intern, struct php_imagick_file_t *file, ImagickOperationType type TSRMLS_DC)
270 {
271 php_imagick_rw_result_t rc;
272
273 if (file->type == ImagickFile) {
274 rc = php_imagick_safe_mode_check(file->absolute_path TSRMLS_CC);
275
276 if (rc != IMAGICK_RW_OK) {
277 return rc;
278 }
279 }
280
281 if (file->type == ImagickUri) {
282 return php_imagick_read_image_using_php_streams(intern, file, type TSRMLS_CC);
283 } else {
284 return php_imagick_read_image_using_imagemagick(intern, file, type TSRMLS_CC);
285 }
286 }
287
php_imagick_write_file(php_imagick_object * intern,struct php_imagick_file_t * file,ImagickOperationType type,zend_bool adjoin TSRMLS_DC)288 php_imagick_rw_result_t php_imagick_write_file(php_imagick_object *intern, struct php_imagick_file_t *file, ImagickOperationType type, zend_bool adjoin TSRMLS_DC)
289 {
290 php_imagick_rw_result_t rc;
291 MagickBooleanType status = MagickFalse;
292
293 if (file->type == ImagickFile) {
294 rc = php_imagick_safe_mode_check(file->absolute_path TSRMLS_CC);
295 if (rc != IMAGICK_RW_OK) {
296 return rc;
297 }
298 }
299 if (type == ImagickWriteImage) {
300 status = MagickWriteImage(intern->magick_wand, file->filename);
301 } else if (type == ImagickWriteImages) {
302 status = MagickWriteImages(intern->magick_wand, file->filename, adjoin);
303 }
304
305 /* Write succeded ? */
306 if (status == MagickFalse) {
307 return IMAGICK_RW_UNDERLYING_LIBRARY;
308 }
309 /* All went well it seems */
310 return IMAGICK_RW_OK;
311 }
312
php_imagick_stream_handler(php_imagick_object * intern,php_stream * stream,ImagickOperationType type TSRMLS_DC)313 zend_bool php_imagick_stream_handler(php_imagick_object *intern, php_stream *stream, ImagickOperationType type TSRMLS_DC)
314 {
315 FILE *fp;
316 MagickBooleanType status = MagickFalse;
317
318 IMAGICK_INIT_ERROR_HANDLING;
319 IMAGICK_SET_ERROR_HANDLING_THROW;
320
321 if (php_stream_can_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_INTERNAL) == FAILURE ||
322 php_stream_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_INTERNAL, (void*)&fp, 0) == FAILURE) {
323 IMAGICK_RESTORE_ERROR_HANDLING;
324 return 0;
325 }
326
327 IMAGICK_RESTORE_ERROR_HANDLING;
328
329 /* php_stream_cast returns warning on some streams but still does not return FAILURE */
330 if (EG(exception)) {
331 return 0;
332 }
333
334 switch (type) {
335 case ImagickWriteImageFile:
336 status = MagickWriteImageFile(intern->magick_wand, fp);
337 break;
338
339 case ImagickWriteImagesFile:
340 status = MagickWriteImagesFile(intern->magick_wand, fp);
341 break;
342
343 case ImagickReadImageFile:
344 status = MagickReadImageFile(intern->magick_wand, fp);
345 break;
346
347 case ImagickPingImageFile:
348 status = MagickPingImageFile(intern->magick_wand, fp);
349 break;
350
351 default:
352 return 0;
353 break;
354 }
355 if (status == MagickFalse) {
356 return 0;
357 }
358 return 1;
359 }
360
361