1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2014 The PHP Group |
6: +----------------------------------------------------------------------+
7: | This source file is subject to version 3.0 of the PHP license, |
8: | that is bundled with this package in the file LICENSE, and is |
9: | available through the world-wide-web at the following url: |
10: | http://www.php.net/license/3_0.txt. |
11: | If you did not receive a copy of the PHP license and are unable to |
12: | obtain it through the world-wide-web, please send a note to |
13: | license@php.net so we can mail you a copy immediately. |
14: +----------------------------------------------------------------------+
15: | Author: Ilia Alshanetsky <ilia@php.net> |
16: +----------------------------------------------------------------------+
17: */
18:
19: /* $Id: fileinfo.c,v 1.1.1.4 2014/06/15 20:03:48 misho Exp $ */
20:
21: #ifdef HAVE_CONFIG_H
22: #include "config.h"
23: #endif
24: #include "php.h"
25:
26: #include <magic.h>
27: /*
28: * HOWMANY specifies the maximum offset libmagic will look at
29: * this is currently hardcoded in the libmagic source but not exported
30: */
31: #ifndef HOWMANY
32: #define HOWMANY 65536
33: #endif
34:
35: #include "php_ini.h"
36: #include "ext/standard/info.h"
37: #include "ext/standard/file.h" /* needed for context stuff */
38: #include "php_fileinfo.h"
39: #include "fopen_wrappers.h" /* needed for is_url */
40:
41: #ifndef _S_IFDIR
42: # define _S_IFDIR S_IFDIR
43: #endif
44:
45: /* {{{ macros and type definitions */
46: struct php_fileinfo {
47: long options;
48: struct magic_set *magic;
49: };
50:
51: static zend_object_handlers finfo_object_handlers;
52: zend_class_entry *finfo_class_entry;
53:
54: struct finfo_object {
55: zend_object zo;
56: struct php_fileinfo *ptr;
57: };
58:
59: #define FILEINFO_DECLARE_INIT_OBJECT(object) \
60: zval *object = getThis();
61:
62: #define FILEINFO_REGISTER_OBJECT(_object, _ptr) \
63: { \
64: struct finfo_object *obj; \
65: obj = (struct finfo_object*)zend_object_store_get_object(_object TSRMLS_CC); \
66: obj->ptr = _ptr; \
67: }
68:
69: #define FILEINFO_FROM_OBJECT(finfo, object) \
70: { \
71: struct finfo_object *obj = zend_object_store_get_object(object TSRMLS_CC); \
72: finfo = obj->ptr; \
73: if (!finfo) { \
74: php_error_docref(NULL TSRMLS_CC, E_WARNING, "The invalid fileinfo object."); \
75: RETURN_FALSE; \
76: } \
77: }
78:
79: /* {{{ finfo_objects_free
80: */
81: static void finfo_objects_free(void *object TSRMLS_DC)
82: {
83: struct finfo_object *intern = (struct finfo_object *) object;
84:
85: if (intern->ptr) {
86: magic_close(intern->ptr->magic);
87: efree(intern->ptr);
88: }
89:
90: zend_object_std_dtor(&intern->zo TSRMLS_CC);
91: efree(intern);
92: }
93: /* }}} */
94:
95: /* {{{ finfo_objects_new
96: */
97: PHP_FILEINFO_API zend_object_value finfo_objects_new(zend_class_entry *class_type TSRMLS_DC)
98: {
99: zend_object_value retval;
100: struct finfo_object *intern;
101:
102: intern = emalloc(sizeof(struct finfo_object));
103: memset(intern, 0, sizeof(struct finfo_object));
104:
105: zend_object_std_init(&intern->zo, class_type TSRMLS_CC);
106: object_properties_init(&intern->zo, class_type);
107:
108: intern->ptr = NULL;
109:
110: retval.handle = zend_objects_store_put(intern, NULL,
111: finfo_objects_free, NULL TSRMLS_CC);
112: retval.handlers = (zend_object_handlers *) &finfo_object_handlers;
113:
114: return retval;
115: }
116: /* }}} */
117:
118: /* {{{ arginfo */
119: ZEND_BEGIN_ARG_INFO_EX(arginfo_finfo_open, 0, 0, 0)
120: ZEND_ARG_INFO(0, options)
121: ZEND_ARG_INFO(0, arg)
122: ZEND_END_ARG_INFO()
123:
124: ZEND_BEGIN_ARG_INFO_EX(arginfo_finfo_close, 0, 0, 1)
125: ZEND_ARG_INFO(0, finfo)
126: ZEND_END_ARG_INFO()
127:
128: ZEND_BEGIN_ARG_INFO_EX(arginfo_finfo_set_flags, 0, 0, 2)
129: ZEND_ARG_INFO(0, finfo)
130: ZEND_ARG_INFO(0, options)
131: ZEND_END_ARG_INFO()
132:
133: ZEND_BEGIN_ARG_INFO_EX(arginfo_finfo_method_set_flags, 0, 0, 1)
134: ZEND_ARG_INFO(0, options)
135: ZEND_END_ARG_INFO()
136:
137: ZEND_BEGIN_ARG_INFO_EX(arginfo_finfo_file, 0, 0, 2)
138: ZEND_ARG_INFO(0, finfo)
139: ZEND_ARG_INFO(0, filename)
140: ZEND_ARG_INFO(0, options)
141: ZEND_ARG_INFO(0, context)
142: ZEND_END_ARG_INFO()
143:
144: ZEND_BEGIN_ARG_INFO_EX(arginfo_finfo_method_file, 0, 0, 1)
145: ZEND_ARG_INFO(0, filename)
146: ZEND_ARG_INFO(0, options)
147: ZEND_ARG_INFO(0, context)
148: ZEND_END_ARG_INFO()
149:
150: ZEND_BEGIN_ARG_INFO_EX(arginfo_finfo_buffer, 0, 0, 2)
151: ZEND_ARG_INFO(0, finfo)
152: ZEND_ARG_INFO(0, string)
153: ZEND_ARG_INFO(0, options)
154: ZEND_ARG_INFO(0, context)
155: ZEND_END_ARG_INFO()
156:
157: ZEND_BEGIN_ARG_INFO_EX(arginfo_finfo_method_buffer, 0, 0, 1)
158: ZEND_ARG_INFO(0, string)
159: ZEND_ARG_INFO(0, options)
160: ZEND_ARG_INFO(0, context)
161: ZEND_END_ARG_INFO()
162:
163: ZEND_BEGIN_ARG_INFO_EX(arginfo_mime_content_type, 0, 0, 1)
164: ZEND_ARG_INFO(0, string)
165: ZEND_END_ARG_INFO()
166: /* }}} */
167:
168: /* {{{ finfo_class_functions
169: */
170: zend_function_entry finfo_class_functions[] = {
171: ZEND_ME_MAPPING(finfo, finfo_open, arginfo_finfo_open, ZEND_ACC_PUBLIC)
172: ZEND_ME_MAPPING(set_flags, finfo_set_flags,arginfo_finfo_method_set_flags, ZEND_ACC_PUBLIC)
173: ZEND_ME_MAPPING(file, finfo_file, arginfo_finfo_method_file, ZEND_ACC_PUBLIC)
174: ZEND_ME_MAPPING(buffer, finfo_buffer, arginfo_finfo_method_buffer, ZEND_ACC_PUBLIC)
175: PHP_FE_END
176: };
177: /* }}} */
178:
179: #define FINFO_SET_OPTION(magic, options) \
180: if (magic_setflags(magic, options) == -1) { \
181: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to set option '%ld' %d:%s", \
182: options, magic_errno(magic), magic_error(magic)); \
183: RETURN_FALSE; \
184: }
185:
186: /* True global resources - no need for thread safety here */
187: static int le_fileinfo;
188: /* }}} */
189:
190: void finfo_resource_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
191: {
192: if (rsrc->ptr) {
193: struct php_fileinfo *finfo = (struct php_fileinfo *) rsrc->ptr;
194: magic_close(finfo->magic);
195: efree(rsrc->ptr);
196: rsrc->ptr = NULL;
197: }
198: }
199: /* }}} */
200:
201:
202: /* {{{ fileinfo_functions[]
203: */
204: zend_function_entry fileinfo_functions[] = {
205: PHP_FE(finfo_open, arginfo_finfo_open)
206: PHP_FE(finfo_close, arginfo_finfo_close)
207: PHP_FE(finfo_set_flags, arginfo_finfo_set_flags)
208: PHP_FE(finfo_file, arginfo_finfo_file)
209: PHP_FE(finfo_buffer, arginfo_finfo_buffer)
210: PHP_FE(mime_content_type, arginfo_mime_content_type)
211: {NULL, NULL, NULL}
212: };
213: /* }}} */
214:
215: /* {{{ PHP_MINIT_FUNCTION
216: */
217: PHP_MINIT_FUNCTION(finfo)
218: {
219: zend_class_entry _finfo_class_entry;
220: INIT_CLASS_ENTRY(_finfo_class_entry, "finfo", finfo_class_functions);
221: _finfo_class_entry.create_object = finfo_objects_new;
222: finfo_class_entry = zend_register_internal_class(&_finfo_class_entry TSRMLS_CC);
223:
224: /* copy the standard object handlers to you handler table */
225: memcpy(&finfo_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
226:
227: le_fileinfo = zend_register_list_destructors_ex(finfo_resource_destructor, NULL, "file_info", module_number);
228:
229: REGISTER_LONG_CONSTANT("FILEINFO_NONE", MAGIC_NONE, CONST_CS|CONST_PERSISTENT);
230: REGISTER_LONG_CONSTANT("FILEINFO_SYMLINK", MAGIC_SYMLINK, CONST_CS|CONST_PERSISTENT);
231: REGISTER_LONG_CONSTANT("FILEINFO_MIME", MAGIC_MIME, CONST_CS|CONST_PERSISTENT);
232: REGISTER_LONG_CONSTANT("FILEINFO_MIME_TYPE", MAGIC_MIME_TYPE, CONST_CS|CONST_PERSISTENT);
233: REGISTER_LONG_CONSTANT("FILEINFO_MIME_ENCODING",MAGIC_MIME_ENCODING, CONST_CS|CONST_PERSISTENT);
234: /* REGISTER_LONG_CONSTANT("FILEINFO_COMPRESS", MAGIC_COMPRESS, CONST_CS|CONST_PERSISTENT); disabled, as it does fork now */
235: REGISTER_LONG_CONSTANT("FILEINFO_DEVICES", MAGIC_DEVICES, CONST_CS|CONST_PERSISTENT);
236: REGISTER_LONG_CONSTANT("FILEINFO_CONTINUE", MAGIC_CONTINUE, CONST_CS|CONST_PERSISTENT);
237: #ifdef MAGIC_PRESERVE_ATIME
238: REGISTER_LONG_CONSTANT("FILEINFO_PRESERVE_ATIME", MAGIC_PRESERVE_ATIME, CONST_CS|CONST_PERSISTENT);
239: #endif
240: #ifdef MAGIC_RAW
241: REGISTER_LONG_CONSTANT("FILEINFO_RAW", MAGIC_RAW, CONST_CS|CONST_PERSISTENT);
242: #endif
243:
244: return SUCCESS;
245: }
246: /* }}} */
247:
248: /* {{{ fileinfo_module_entry
249: */
250: zend_module_entry fileinfo_module_entry = {
251: STANDARD_MODULE_HEADER,
252: "fileinfo",
253: fileinfo_functions,
254: PHP_MINIT(finfo),
255: NULL,
256: NULL,
257: NULL,
258: PHP_MINFO(fileinfo),
259: PHP_FILEINFO_VERSION,
260: STANDARD_MODULE_PROPERTIES
261: };
262: /* }}} */
263:
264: #ifdef COMPILE_DL_FILEINFO
265: ZEND_GET_MODULE(fileinfo)
266: #endif
267:
268: /* {{{ PHP_MINFO_FUNCTION
269: */
270: PHP_MINFO_FUNCTION(fileinfo)
271: {
272: php_info_print_table_start();
273: php_info_print_table_row(2, "fileinfo support", "enabled");
274: php_info_print_table_row(2, "version", PHP_FILEINFO_VERSION);
275: php_info_print_table_end();
276: }
277: /* }}} */
278:
279: #define FILEINFO_DESTROY_OBJECT(object) \
280: do { \
281: if (object) { \
282: zend_object_store_ctor_failed(object TSRMLS_CC); \
283: zval_dtor(object); \
284: ZVAL_NULL(object); \
285: } \
286: } while (0)
287:
288: /* {{{ proto resource finfo_open([int options [, string arg]])
289: Create a new fileinfo resource. */
290: PHP_FUNCTION(finfo_open)
291: {
292: long options = MAGIC_NONE;
293: char *file = NULL;
294: int file_len = 0;
295: struct php_fileinfo *finfo;
296: FILEINFO_DECLARE_INIT_OBJECT(object)
297: char resolved_path[MAXPATHLEN];
298:
299: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lp", &options, &file, &file_len) == FAILURE) {
300: FILEINFO_DESTROY_OBJECT(object);
301: RETURN_FALSE;
302: }
303:
304: if (object) {
305: struct finfo_object *finfo_obj = (struct finfo_object*)zend_object_store_get_object(object TSRMLS_CC);
306:
307: if (finfo_obj->ptr) {
308: magic_close(finfo_obj->ptr->magic);
309: efree(finfo_obj->ptr);
310: finfo_obj->ptr = NULL;
311: }
312: }
313:
314: if (file_len == 0) {
315: file = NULL;
316: } else if (file && *file) { /* user specified file, perform open_basedir checks */
317:
318: #if PHP_API_VERSION < 20100412
319: if ((PG(safe_mode) && (!php_checkuid(file, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(file TSRMLS_CC)) {
320: #else
321: if (php_check_open_basedir(file TSRMLS_CC)) {
322: #endif
323: FILEINFO_DESTROY_OBJECT(object);
324: RETURN_FALSE;
325: }
326: if (!expand_filepath_with_mode(file, resolved_path, NULL, 0, CWD_EXPAND TSRMLS_CC)) {
327: FILEINFO_DESTROY_OBJECT(object);
328: RETURN_FALSE;
329: }
330: file = resolved_path;
331: }
332:
333: finfo = emalloc(sizeof(struct php_fileinfo));
334:
335: finfo->options = options;
336: finfo->magic = magic_open(options);
337:
338: if (finfo->magic == NULL) {
339: efree(finfo);
340: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid mode '%ld'.", options);
341: FILEINFO_DESTROY_OBJECT(object);
342: RETURN_FALSE;
343: }
344:
345: if (magic_load(finfo->magic, file) == -1) {
346: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to load magic database at '%s'.", file);
347: magic_close(finfo->magic);
348: efree(finfo);
349: FILEINFO_DESTROY_OBJECT(object);
350: RETURN_FALSE;
351: }
352:
353: if (object) {
354: FILEINFO_REGISTER_OBJECT(object, finfo);
355: } else {
356: ZEND_REGISTER_RESOURCE(return_value, finfo, le_fileinfo);
357: }
358: }
359: /* }}} */
360:
361: /* {{{ proto resource finfo_close(resource finfo)
362: Close fileinfo resource. */
363: PHP_FUNCTION(finfo_close)
364: {
365: struct php_fileinfo *finfo;
366: zval *zfinfo;
367:
368: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zfinfo) == FAILURE) {
369: RETURN_FALSE;
370: }
371: ZEND_FETCH_RESOURCE(finfo, struct php_fileinfo *, &zfinfo, -1, "file_info", le_fileinfo);
372:
373: zend_list_delete(Z_RESVAL_P(zfinfo));
374:
375: RETURN_TRUE;
376: }
377: /* }}} */
378:
379: /* {{{ proto bool finfo_set_flags(resource finfo, int options)
380: Set libmagic configuration options. */
381: PHP_FUNCTION(finfo_set_flags)
382: {
383: long options;
384: struct php_fileinfo *finfo;
385: zval *zfinfo;
386: FILEINFO_DECLARE_INIT_OBJECT(object)
387:
388: if (object) {
389: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &options) == FAILURE) {
390: RETURN_FALSE;
391: }
392: FILEINFO_FROM_OBJECT(finfo, object);
393: } else {
394: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zfinfo, &options) == FAILURE) {
395: RETURN_FALSE;
396: }
397: ZEND_FETCH_RESOURCE(finfo, struct php_fileinfo *, &zfinfo, -1, "file_info", le_fileinfo);
398: }
399:
400: FINFO_SET_OPTION(finfo->magic, options)
401: finfo->options = options;
402:
403: RETURN_TRUE;
404: }
405: /* }}} */
406:
407: #define FILEINFO_MODE_BUFFER 0
408: #define FILEINFO_MODE_STREAM 1
409: #define FILEINFO_MODE_FILE 2
410:
411: static void _php_finfo_get_type(INTERNAL_FUNCTION_PARAMETERS, int mode, int mimetype_emu) /* {{{ */
412: {
413: long options = 0;
414: char *ret_val = NULL, *buffer = NULL;
415: int buffer_len;
416: struct php_fileinfo *finfo = NULL;
417: zval *zfinfo, *zcontext = NULL;
418: zval *what;
419: char mime_directory[] = "directory";
420:
421: struct magic_set *magic = NULL;
422: FILEINFO_DECLARE_INIT_OBJECT(object)
423:
424: if (mimetype_emu) {
425:
426: /* mime_content_type(..) emulation */
427: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &what) == FAILURE) {
428: return;
429: }
430:
431: switch (Z_TYPE_P(what)) {
432: case IS_STRING:
433: buffer = Z_STRVAL_P(what);
434: buffer_len = Z_STRLEN_P(what);
435: mode = FILEINFO_MODE_FILE;
436: break;
437:
438: case IS_RESOURCE:
439: mode = FILEINFO_MODE_STREAM;
440: break;
441:
442: default:
443: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only process string or stream arguments");
444: RETURN_FALSE;
445: }
446:
447: magic = magic_open(MAGIC_MIME_TYPE);
448: if (magic_load(magic, NULL) == -1) {
449: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to load magic database.");
450: goto common;
451: }
452: } else if (object) {
453: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lr", &buffer, &buffer_len, &options, &zcontext) == FAILURE) {
454: RETURN_FALSE;
455: }
456: FILEINFO_FROM_OBJECT(finfo, object);
457: magic = finfo->magic;
458: } else {
459: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lr", &zfinfo, &buffer, &buffer_len, &options, &zcontext) == FAILURE) {
460: RETURN_FALSE;
461: }
462: ZEND_FETCH_RESOURCE(finfo, struct php_fileinfo *, &zfinfo, -1, "file_info", le_fileinfo);
463: magic = finfo->magic;
464: }
465:
466: /* Set options for the current file/buffer. */
467: if (options) {
468: FINFO_SET_OPTION(magic, options)
469: }
470:
471: switch (mode) {
472: case FILEINFO_MODE_BUFFER:
473: {
474: ret_val = (char *) magic_buffer(magic, buffer, buffer_len);
475: break;
476: }
477:
478: case FILEINFO_MODE_STREAM:
479: {
480: php_stream *stream;
481: off_t streampos;
482:
483: php_stream_from_zval_no_verify(stream, &what);
484: if (!stream) {
485: goto common;
486: }
487:
488: streampos = php_stream_tell(stream); /* remember stream position for restoration */
489: php_stream_seek(stream, 0, SEEK_SET);
490:
491: ret_val = (char *) magic_stream(magic, stream);
492:
493: php_stream_seek(stream, streampos, SEEK_SET);
494: break;
495: }
496:
497: case FILEINFO_MODE_FILE:
498: {
499: /* determine if the file is a local file or remote URL */
500: char *tmp2;
501: php_stream_wrapper *wrap;
502: php_stream_statbuf ssb;
503:
504: if (buffer == NULL || !*buffer) {
505: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty filename or path");
506: RETVAL_FALSE;
507: goto clean;
508: }
509:
510: wrap = php_stream_locate_url_wrapper(buffer, &tmp2, 0 TSRMLS_CC);
511:
512: if (wrap) {
513: php_stream *stream;
514: php_stream_context *context = php_stream_context_from_zval(zcontext, 0);
515:
516: #ifdef PHP_WIN32
517: if (php_stream_stat_path_ex(buffer, 0, &ssb, context) == SUCCESS) {
518: if (ssb.sb.st_mode & S_IFDIR) {
519: ret_val = mime_directory;
520: goto common;
521: }
522: }
523: #endif
524:
525: #if PHP_API_VERSION < 20100412
526: stream = php_stream_open_wrapper_ex(buffer, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context);
527: #else
528: stream = php_stream_open_wrapper_ex(buffer, "rb", REPORT_ERRORS, NULL, context);
529: #endif
530:
531: if (!stream) {
532: RETVAL_FALSE;
533: goto clean;
534: }
535:
536: if (php_stream_stat(stream, &ssb) == SUCCESS) {
537: if (ssb.sb.st_mode & S_IFDIR) {
538: ret_val = mime_directory;
539: } else {
540: ret_val = (char *)magic_stream(magic, stream);
541: }
542: }
543:
544: php_stream_close(stream);
545: }
546: break;
547: }
548:
549: default:
550: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only process string or stream arguments");
551: }
552:
553: common:
554: if (ret_val) {
555: RETVAL_STRING(ret_val, 1);
556: } else {
557: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed identify data %d:%s", magic_errno(magic), magic_error(magic));
558: RETVAL_FALSE;
559: }
560:
561: clean:
562: if (mimetype_emu) {
563: magic_close(magic);
564: }
565:
566: /* Restore options */
567: if (options) {
568: FINFO_SET_OPTION(magic, finfo->options)
569: }
570: return;
571: }
572: /* }}} */
573:
574: /* {{{ proto string finfo_file(resource finfo, char *file_name [, int options [, resource context]])
575: Return information about a file. */
576: PHP_FUNCTION(finfo_file)
577: {
578: _php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, FILEINFO_MODE_FILE, 0);
579: }
580: /* }}} */
581:
582: /* {{{ proto string finfo_buffer(resource finfo, char *string [, int options [, resource context]])
583: Return infromation about a string buffer. */
584: PHP_FUNCTION(finfo_buffer)
585: {
586: _php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, FILEINFO_MODE_BUFFER, 0);
587: }
588: /* }}} */
589:
590: /* {{{ proto string mime_content_type(string filename|resource stream)
591: Return content-type for file */
592: PHP_FUNCTION(mime_content_type)
593: {
594: _php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1, 1);
595: }
596: /* }}} */
597:
598:
599: /*
600: * Local variables:
601: * tab-width: 4
602: * c-basic-offset: 4
603: * End:
604: * vim600: noet sw=4 ts=4 fdm=marker
605: * vim<600: noet sw=4 ts=4
606: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>