xref: /imagick/imagick_file.c (revision 3e8bdc15)
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