xref: /php-src/ext/zip/php_zip.c (revision 6eecb3e7)
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   | Author: Piere-Alain Joye <pierre@php.net>                            |
14   +----------------------------------------------------------------------+
15 */
16 
17 
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 
22 #include "php.h"
23 #include "php_ini.h"
24 #include "ext/standard/info.h"
25 #include "ext/standard/file.h"
26 #include "ext/standard/php_string.h"
27 #include "ext/pcre/php_pcre.h"
28 #include "ext/standard/php_filestat.h"
29 #include "zend_attributes.h"
30 #include "zend_interfaces.h"
31 #include "php_zip.h"
32 #include "php_zip_arginfo.h"
33 
34 #ifdef HAVE_GLOB
35 #ifndef PHP_WIN32
36 #include <glob.h>
37 #else
38 #include "win32/glob.h"
39 #endif
40 #endif
41 
42 /* {{{ Resource le */
43 static int le_zip_dir;
44 #define le_zip_dir_name "Zip Directory"
45 static int le_zip_entry;
46 #define le_zip_entry_name "Zip Entry"
47 /* }}} */
48 
49 /* {{{ PHP_ZIP_STAT_INDEX(za, index, flags, sb) */
50 #define PHP_ZIP_STAT_INDEX(za, index, flags, sb) \
51 	if (zip_stat_index(za, index, flags, &sb) != 0) { \
52 		RETURN_FALSE; \
53 	}
54 /* }}} */
55 
56 /* {{{  PHP_ZIP_STAT_PATH(za, path, path_len, flags, sb)
57 	This is always used for the first argument*/
58 #define PHP_ZIP_STAT_PATH(za, path, path_len, flags, sb) \
59 	if (path_len == 0) { \
60 		zend_argument_value_error(1, "cannot be empty"); \
61 		RETURN_THROWS(); \
62 	} \
63 	if (zip_stat(za, path, flags, &sb) != 0) { \
64 		RETURN_FALSE; \
65 	}
66 /* }}} */
67 
68 /* {{{ PHP_ZIP_SET_FILE_COMMENT(za, index, comment, comment_len) */
69 #define PHP_ZIP_SET_FILE_COMMENT(za, index, comment, comment_len) \
70 	if (comment_len == 0) { \
71 		/* Passing NULL remove the existing comment */ \
72 		if (zip_file_set_comment(za, index, NULL, 0, 0) < 0) { \
73 			RETURN_FALSE; \
74 		} \
75 	} else if (zip_file_set_comment(za, index, comment, comment_len, 0) < 0) { \
76 		RETURN_FALSE; \
77 	} \
78 	RETURN_TRUE;
79 /* }}} */
80 
81 # define add_ascii_assoc_string add_assoc_string
82 # define add_ascii_assoc_long add_assoc_long
83 
84 /* Flatten a path by making a relative path (to .)*/
php_zip_make_relative_path(char * path,size_t path_len)85 static char * php_zip_make_relative_path(char *path, size_t path_len) /* {{{ */
86 {
87 	char *path_begin = path;
88 	size_t i;
89 
90 	if (path_len < 1 || path == NULL) {
91 		return NULL;
92 	}
93 
94 	if (IS_ABSOLUTE_PATH(path, path_len)) {
95 		return path + COPY_WHEN_ABSOLUTE(path) + 1;
96 	}
97 
98 	i = path_len;
99 
100 	while (1) {
101 		while (i > 0 && !IS_SLASH(path[i])) {
102 			i--;
103 		}
104 
105 		if (!i) {
106 			return path;
107 		}
108 
109 		if (i >= 2 && path[i -1] == '.') {
110 			/* i is the position of ., add 1 for / */
111 			path_begin = path + i + 1;
112 			break;
113 		}
114 		i--;
115 	}
116 
117 	return path_begin;
118 }
119 /* }}} */
120 
121 # define CWD_STATE_ALLOC(l) emalloc(l)
122 # define CWD_STATE_FREE(s)  efree(s)
123 
124 /* {{{ php_zip_extract_file */
php_zip_extract_file(struct zip * za,char * dest,const char * file,size_t file_len,zip_int64_t idx)125 static int php_zip_extract_file(struct zip * za, char *dest, const char *file, size_t file_len, zip_int64_t idx)
126 {
127 	php_stream_statbuf ssb;
128 	struct zip_file *zf;
129 	struct zip_stat sb;
130 	char b[8192];
131 	int n, ret;
132 	php_stream *stream;
133 	char *fullpath;
134 	char *file_dirname_fullpath;
135 	char file_dirname[MAXPATHLEN];
136 	size_t dir_len, len;
137 	int is_dir_only = 0;
138 	char *path_cleaned;
139 	size_t path_cleaned_len;
140 	cwd_state new_state;
141 	zend_string *file_basename;
142 
143 	if (idx < 0) {
144 		idx = zip_name_locate(za, file, 0);
145 		if (idx < 0) {
146 			return 0;
147 		}
148 	}
149 	new_state.cwd = CWD_STATE_ALLOC(1);
150 	new_state.cwd[0] = '\0';
151 	new_state.cwd_length = 0;
152 
153 	/* Clean/normlize the path and then transform any path (absolute or relative)
154 		 to a path relative to cwd (../../mydir/foo.txt > mydir/foo.txt)
155 	 */
156 	virtual_file_ex(&new_state, file, NULL, CWD_EXPAND);
157 	path_cleaned =  php_zip_make_relative_path(new_state.cwd, new_state.cwd_length);
158 	if(!path_cleaned) {
159 		CWD_STATE_FREE(new_state.cwd);
160 		return 0;
161 	}
162 	path_cleaned_len = strlen(path_cleaned);
163 
164 	if (path_cleaned_len >= MAXPATHLEN || zip_stat_index(za, idx, 0, &sb) != 0) {
165 		CWD_STATE_FREE(new_state.cwd);
166 		return 0;
167 	}
168 
169 	/* it is a directory only, see #40228 */
170 	if (path_cleaned_len > 1 && IS_SLASH(path_cleaned[path_cleaned_len - 1])) {
171 		len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, path_cleaned);
172 		is_dir_only = 1;
173 	} else {
174 		memcpy(file_dirname, path_cleaned, path_cleaned_len);
175 		dir_len = php_dirname(file_dirname, path_cleaned_len);
176 
177 		if (!dir_len || (dir_len == 1 && file_dirname[0] == '.')) {
178 			len = spprintf(&file_dirname_fullpath, 0, "%s", dest);
179 		} else {
180 			len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file_dirname);
181 		}
182 
183 		file_basename =	php_basename(path_cleaned, path_cleaned_len, NULL, 0);
184 
185 		if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname_fullpath)) {
186 			efree(file_dirname_fullpath);
187 			zend_string_release_ex(file_basename, 0);
188 			CWD_STATE_FREE(new_state.cwd);
189 			return 0;
190 		}
191 	}
192 
193 	/* let see if the path already exists */
194 	if (php_stream_stat_path_ex(file_dirname_fullpath, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL) < 0) {
195 		ret = php_stream_mkdir(file_dirname_fullpath, 0777,  PHP_STREAM_MKDIR_RECURSIVE|REPORT_ERRORS, NULL);
196 		if (!ret) {
197 			efree(file_dirname_fullpath);
198 			if (!is_dir_only) {
199 				zend_string_release_ex(file_basename, 0);
200 			}
201 			CWD_STATE_FREE(new_state.cwd);
202 			return 0;
203 		}
204 	}
205 
206 	/* it is a standalone directory, job done */
207 	if (is_dir_only) {
208 		efree(file_dirname_fullpath);
209 		CWD_STATE_FREE(new_state.cwd);
210 		return 1;
211 	}
212 
213 	len = spprintf(&fullpath, 0, "%s/%s", file_dirname_fullpath, ZSTR_VAL(file_basename));
214 	if (!len) {
215 		efree(file_dirname_fullpath);
216 		zend_string_release_ex(file_basename, 0);
217 		CWD_STATE_FREE(new_state.cwd);
218 		return 0;
219 	} else if (len > MAXPATHLEN) {
220 		php_error_docref(NULL, E_WARNING, "Full extraction path exceed MAXPATHLEN (%i)", MAXPATHLEN);
221 		efree(file_dirname_fullpath);
222 		zend_string_release_ex(file_basename, 0);
223 		CWD_STATE_FREE(new_state.cwd);
224 		return 0;
225 	}
226 
227 	/* check again the full path, not sure if it
228 	 * is required, does a file can have a different
229 	 * safemode status as its parent folder?
230 	 */
231 	if (ZIP_OPENBASEDIR_CHECKPATH(fullpath)) {
232 		efree(fullpath);
233 		efree(file_dirname_fullpath);
234 		zend_string_release_ex(file_basename, 0);
235 		CWD_STATE_FREE(new_state.cwd);
236 		return 0;
237 	}
238 
239 	zf = zip_fopen_index(za, idx, 0);
240 	if (zf == NULL) {
241 		n = -1;
242 		goto done;
243 	}
244 
245 	stream = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL);
246 
247 	if (stream == NULL) {
248 		n = -1;
249 		zip_fclose(zf);
250 		goto done;
251 	}
252 
253 	n = 0;
254 
255 	while ((n=zip_fread(zf, b, sizeof(b))) > 0) {
256 		php_stream_write(stream, b, n);
257 	}
258 
259 	if (stream->wrapper->wops->stream_metadata) {
260 		struct utimbuf ut;
261 
262 		ut.modtime = ut.actime = sb.mtime;
263 		stream->wrapper->wops->stream_metadata(stream->wrapper, fullpath, PHP_STREAM_META_TOUCH, &ut, NULL);
264 	}
265 
266 	php_stream_close(stream);
267 	n = zip_fclose(zf);
268 
269 done:
270 	efree(fullpath);
271 	zend_string_release_ex(file_basename, 0);
272 	efree(file_dirname_fullpath);
273 	CWD_STATE_FREE(new_state.cwd);
274 
275 	if (n<0) {
276 		return 0;
277 	} else {
278 		return 1;
279 	}
280 }
281 /* }}} */
282 
php_zip_add_file(ze_zip_object * obj,const char * filename,size_t filename_len,char * entry_name,size_t entry_name_len,zip_uint64_t offset_start,zip_uint64_t offset_len,zend_long replace,zip_flags_t flags)283 static int php_zip_add_file(ze_zip_object *obj, const char *filename, size_t filename_len,
284 	char *entry_name, size_t entry_name_len, /* unused if replace >= 0 */
285 	zip_uint64_t offset_start, zip_uint64_t offset_len,
286 	zend_long replace, /* index to replace, add new file if < 0 */
287 	zip_flags_t flags
288 ) /* {{{ */
289 {
290 	struct zip_source *zs;
291 	char resolved_path[MAXPATHLEN];
292 	php_stream_statbuf ssb;
293 
294 	if (ZIP_OPENBASEDIR_CHECKPATH(filename)) {
295 		return -1;
296 	}
297 
298 	if (!expand_filepath(filename, resolved_path)) {
299 		php_error_docref(NULL, E_WARNING, "No such file or directory");
300 		return -1;
301 	}
302 
303 	if (php_stream_stat_path_ex(resolved_path, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL)) {
304 		php_error_docref(NULL, E_WARNING, "No such file or directory");
305 		return -1;
306 	}
307 
308 	if (flags & ZIP_FL_OPEN_FILE_NOW) {
309 		FILE *fd;
310 		fd = fopen(resolved_path, "rb");
311 		if (!fd) {
312 			return -1;
313 		}
314 		flags ^= ZIP_FL_OPEN_FILE_NOW;
315 		zs = zip_source_filep(obj->za, fd, offset_start, offset_len);
316 	} else {
317 		zs = zip_source_file(obj->za, resolved_path, offset_start, offset_len);
318 	}
319 	if (!zs) {
320 		return -1;
321 	}
322 	/* Replace */
323 	if (replace >= 0) {
324 		if (zip_file_replace(obj->za, replace, zs, flags) < 0) {
325 			zip_source_free(zs);
326 			return -1;
327 		}
328 		zip_error_clear(obj->za);
329 		return 1;
330 	}
331 	/* Add */
332 	obj->last_id = zip_file_add(obj->za, entry_name, zs, flags);
333 	if (obj->last_id < 0) {
334 		zip_source_free(zs);
335 		return -1;
336 	}
337 	zip_error_clear(obj->za);
338 	return 1;
339 }
340 /* }}} */
341 
342 typedef struct {
343 	zend_long    remove_all_path;
344 	char        *remove_path;
345 	size_t       remove_path_len;
346 	char        *add_path;
347 	size_t       add_path_len;
348 	zip_flags_t  flags;
349 	zip_int32_t  comp_method;
350 	zip_uint32_t comp_flags;
351 #ifdef HAVE_ENCRYPTION
352 	zip_int16_t  enc_method;
353 	char        *enc_password;
354 #endif
355 } zip_options;
356 
php_zip_parse_options(HashTable * options,zip_options * opts)357 static int php_zip_parse_options(HashTable *options, zip_options *opts)
358 /* {{{ */
359 {
360 	zval *option;
361 
362 	/* default values */
363 	memset(opts, 0, sizeof(zip_options));
364 	opts->flags = ZIP_FL_OVERWRITE;
365 	opts->comp_method = -1; /* -1 to not change default */
366 #ifdef HAVE_ENCRYPTION
367 	opts->enc_method = -1;  /* -1 to not change default */
368 #endif
369 
370 	if ((option = zend_hash_str_find(options, "remove_all_path", sizeof("remove_all_path") - 1)) != NULL) {
371 		if (Z_TYPE_P(option) != IS_FALSE && Z_TYPE_P(option) != IS_TRUE) {
372 			php_error_docref(NULL, E_WARNING, "Option \"remove_all_path\" must be of type bool, %s given",
373 				zend_zval_value_name(option));
374 		}
375 		opts->remove_all_path = zval_get_long(option);
376 	}
377 
378 	if ((option = zend_hash_str_find(options, "comp_method", sizeof("comp_method") - 1)) != NULL) {
379 		if (Z_TYPE_P(option) != IS_LONG) {
380 			php_error_docref(NULL, E_WARNING, "Option \"comp_method\" must be of type int, %s given",
381 				zend_zval_value_name(option));
382 		}
383 		opts->comp_method = zval_get_long(option);
384 
385 		if ((option = zend_hash_str_find(options, "comp_flags", sizeof("comp_flags") - 1)) != NULL) {
386 			if (Z_TYPE_P(option) != IS_LONG) {
387 				php_error_docref(NULL, E_WARNING, "Option \"comp_flags\" must be of type int, %s given",
388 					zend_zval_value_name(option));
389 			}
390 			opts->comp_flags = zval_get_long(option);
391 		}
392 	}
393 
394 #ifdef HAVE_ENCRYPTION
395 	if ((option = zend_hash_str_find(options, "enc_method", sizeof("enc_method") - 1)) != NULL) {
396 		if (Z_TYPE_P(option) != IS_LONG) {
397 			php_error_docref(NULL, E_WARNING, "Option \"enc_method\" must be of type int, %s given",
398 				zend_zval_value_name(option));
399 		}
400 		opts->enc_method = zval_get_long(option);
401 
402 		if ((option = zend_hash_str_find(options, "enc_password", sizeof("enc_password") - 1)) != NULL) {
403 			if (Z_TYPE_P(option) != IS_STRING) {
404 				zend_type_error("Option \"enc_password\" must be of type string, %s given",
405 					zend_zval_value_name(option));
406 				return -1;
407 			}
408 			opts->enc_password = Z_STRVAL_P(option);
409 		}
410 	}
411 #endif
412 
413 	if ((option = zend_hash_str_find(options, "remove_path", sizeof("remove_path") - 1)) != NULL) {
414 		if (Z_TYPE_P(option) != IS_STRING) {
415 			zend_type_error("Option \"remove_path\" must be of type string, %s given",
416 				zend_zval_value_name(option));
417 			return -1;
418 		}
419 
420 		if (Z_STRLEN_P(option) == 0) {
421 			zend_value_error("Option \"remove_path\" cannot be empty");
422 			return -1;
423 		}
424 
425 		if (Z_STRLEN_P(option) >= MAXPATHLEN) {
426 			zend_value_error("Option \"remove_path\" must be less than %d bytes", MAXPATHLEN - 1);
427 			return -1;
428 		}
429 		opts->remove_path_len = Z_STRLEN_P(option);
430 		opts->remove_path = Z_STRVAL_P(option);
431 	}
432 
433 	if ((option = zend_hash_str_find(options, "add_path", sizeof("add_path") - 1)) != NULL) {
434 		if (Z_TYPE_P(option) != IS_STRING) {
435 			zend_type_error("Option \"add_path\" must be of type string, %s given",
436 				zend_zval_value_name(option));
437 			return -1;
438 		}
439 
440 		if (Z_STRLEN_P(option) == 0) {
441 			zend_value_error("Option \"add_path\" cannot be empty");
442 			return -1;
443 		}
444 
445 		if (Z_STRLEN_P(option) >= MAXPATHLEN) {
446 			zend_value_error("Option \"add_path\" must be less than %d bytes", MAXPATHLEN - 1);
447 			return -1;
448 		}
449 		opts->add_path_len = Z_STRLEN_P(option);
450 		opts->add_path = Z_STRVAL_P(option);
451 	}
452 
453 	if ((option = zend_hash_str_find(options, "flags", sizeof("flags") - 1)) != NULL) {
454 		if (Z_TYPE_P(option) != IS_LONG) {
455 			zend_type_error("Option \"flags\" must be of type int, %s given",
456 				zend_zval_value_name(option));
457 			return -1;
458 		}
459 		opts->flags = Z_LVAL_P(option);
460 	}
461 
462 	return 1;
463 }
464 /* }}} */
465 
466 /* {{{ ZIP_FROM_OBJECT */
467 #define ZIP_FROM_OBJECT(intern, object) \
468 	{ \
469 		ze_zip_object *obj = Z_ZIP_P(object); \
470 		intern = obj->za; \
471 		if (!intern) { \
472 			zend_value_error("Invalid or uninitialized Zip object"); \
473 			RETURN_THROWS(); \
474 		} \
475 	}
476 /* }}} */
477 
478 /* {{{ RETURN_SB(sb) */
479 #ifdef HAVE_ENCRYPTION
480 #define RETURN_SB(sb) \
481 	{ \
482 		array_init(return_value); \
483 		add_ascii_assoc_string(return_value, "name", (char *)(sb)->name); \
484 		add_ascii_assoc_long(return_value, "index", (zend_long) (sb)->index); \
485 		add_ascii_assoc_long(return_value, "crc", (zend_long) (sb)->crc); \
486 		add_ascii_assoc_long(return_value, "size", (zend_long) (sb)->size); \
487 		add_ascii_assoc_long(return_value, "mtime", (zend_long) (sb)->mtime); \
488 		add_ascii_assoc_long(return_value, "comp_size", (zend_long) (sb)->comp_size); \
489 		add_ascii_assoc_long(return_value, "comp_method", (zend_long) (sb)->comp_method); \
490 		add_ascii_assoc_long(return_value, "encryption_method", (zend_long) (sb)->encryption_method); \
491 	}
492 #else
493 #define RETURN_SB(sb) \
494 	{ \
495 		array_init(return_value); \
496 		add_ascii_assoc_string(return_value, "name", (char *)(sb)->name); \
497 		add_ascii_assoc_long(return_value, "index", (zend_long) (sb)->index); \
498 		add_ascii_assoc_long(return_value, "crc", (zend_long) (sb)->crc); \
499 		add_ascii_assoc_long(return_value, "size", (zend_long) (sb)->size); \
500 		add_ascii_assoc_long(return_value, "mtime", (zend_long) (sb)->mtime); \
501 		add_ascii_assoc_long(return_value, "comp_size", (zend_long) (sb)->comp_size); \
502 		add_ascii_assoc_long(return_value, "comp_method", (zend_long) (sb)->comp_method); \
503 	}
504 #endif
505 /* }}} */
506 
php_zip_status(ze_zip_object * obj)507 static zend_long php_zip_status(ze_zip_object *obj) /* {{{ */
508 {
509 	int zep = obj->err_zip; /* saved err if closed */
510 
511 	if (obj->za) {
512 #if LIBZIP_VERSION_MAJOR < 1
513 		int syp;
514 
515 		zip_error_get(obj->za, &zep, &syp);
516 #else
517 		zip_error_t *err;
518 
519 		err = zip_get_error(obj->za);
520 		zep = zip_error_code_zip(err);
521 		zip_error_fini(err);
522 #endif
523 	}
524 	return zep;
525 }
526 /* }}} */
527 
php_zip_last_id(ze_zip_object * obj)528 static zend_long php_zip_last_id(ze_zip_object *obj) /* {{{ */
529 {
530 	return obj->last_id;
531 }
532 /* }}} */
533 
php_zip_status_sys(ze_zip_object * obj)534 static zend_long php_zip_status_sys(ze_zip_object *obj) /* {{{ */
535 {
536 	int syp = obj->err_sys;  /* saved err if closed */
537 
538 	if (obj->za) {
539 #if LIBZIP_VERSION_MAJOR < 1
540 		int zep;
541 
542 		zip_error_get(obj->za, &zep, &syp);
543 #else
544 		zip_error_t *err;
545 
546 		err = zip_get_error(obj->za);
547 		syp = zip_error_code_system(err);
548 		zip_error_fini(err);
549 #endif
550 	}
551 	return syp;
552 }
553 /* }}} */
554 
php_zip_get_num_files(ze_zip_object * obj)555 static zend_long php_zip_get_num_files(ze_zip_object *obj) /* {{{ */
556 {
557 	if (obj->za) {
558 		zip_int64_t num = zip_get_num_entries(obj->za, 0);
559 		return MIN(num, ZEND_LONG_MAX);
560 	}
561 	return 0;
562 }
563 /* }}} */
564 
php_zipobj_get_filename(ze_zip_object * obj,int * len)565 static char * php_zipobj_get_filename(ze_zip_object *obj, int *len) /* {{{ */
566 {
567 	if (obj && obj->filename) {
568 		*len = strlen(obj->filename);
569 		return obj->filename;
570 	}
571 	return NULL;
572 }
573 /* }}} */
574 
php_zipobj_get_zip_comment(ze_zip_object * obj,int * len)575 static char * php_zipobj_get_zip_comment(ze_zip_object *obj, int *len) /* {{{ */
576 {
577 	if (obj->za) {
578 		return (char *)zip_get_archive_comment(obj->za, len, 0);
579 	}
580 	return NULL;
581 }
582 /* }}} */
583 
584 #ifdef HAVE_GLOB /* {{{ */
585 #ifndef GLOB_ONLYDIR
586 #define GLOB_ONLYDIR (1<<30)
587 #define GLOB_EMULATE_ONLYDIR
588 #define GLOB_FLAGMASK (~GLOB_ONLYDIR)
589 #else
590 #define GLOB_FLAGMASK (~0)
591 #endif
592 #ifndef GLOB_BRACE
593 # define GLOB_BRACE 0
594 #endif
595 #ifndef GLOB_MARK
596 # define GLOB_MARK 0
597 #endif
598 #ifndef GLOB_NOSORT
599 # define GLOB_NOSORT 0
600 #endif
601 #ifndef GLOB_NOCHECK
602 # define GLOB_NOCHECK 0
603 #endif
604 #ifndef GLOB_NOESCAPE
605 # define GLOB_NOESCAPE 0
606 #endif
607 #ifndef GLOB_ERR
608 # define GLOB_ERR 0
609 #endif
610 
611 /* This is used for checking validity of passed flags (passing invalid flags causes segfault in glob()!! */
612 #define GLOB_AVAILABLE_FLAGS (0 | GLOB_BRACE | GLOB_MARK | GLOB_NOSORT | GLOB_NOCHECK | GLOB_NOESCAPE | GLOB_ERR | GLOB_ONLYDIR)
613 
614 #endif /* }}} */
615 
php_zip_glob(char * pattern,int pattern_len,zend_long flags,zval * return_value)616 int php_zip_glob(char *pattern, int pattern_len, zend_long flags, zval *return_value) /* {{{ */
617 {
618 #ifdef HAVE_GLOB
619 	int cwd_skip = 0;
620 #ifdef ZTS
621 	char cwd[MAXPATHLEN];
622 	char work_pattern[MAXPATHLEN];
623 	char *result;
624 #endif
625 	glob_t globbuf;
626 	size_t n;
627 	int ret;
628 
629 	if (pattern_len >= MAXPATHLEN) {
630 		php_error_docref(NULL, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
631 		return -1;
632 	}
633 
634 	if ((GLOB_AVAILABLE_FLAGS & flags) != flags) {
635 
636 		php_error_docref(NULL, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform");
637 		return -1;
638 	}
639 
640 #ifdef ZTS
641 	if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) {
642 		result = VCWD_GETCWD(cwd, MAXPATHLEN);
643 		if (!result) {
644 			cwd[0] = '\0';
645 		}
646 #ifdef PHP_WIN32
647 		if (IS_SLASH(*pattern)) {
648 			cwd[2] = '\0';
649 		}
650 #endif
651 		cwd_skip = strlen(cwd)+1;
652 
653 		snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern);
654 		pattern = work_pattern;
655 	}
656 #endif
657 
658 	globbuf.gl_offs = 0;
659 	if (0 != (ret = glob(pattern, flags & GLOB_FLAGMASK, NULL, &globbuf))) {
660 #ifdef GLOB_NOMATCH
661 		if (GLOB_NOMATCH == ret) {
662 			/* Some glob implementation simply return no data if no matches
663 			   were found, others return the GLOB_NOMATCH error code.
664 			   We don't want to treat GLOB_NOMATCH as an error condition
665 			   so that PHP glob() behaves the same on both types of
666 			   implementations and so that 'foreach (glob() as ...'
667 			   can be used for simple glob() calls without further error
668 			   checking.
669 			*/
670 			array_init(return_value);
671 			return 0;
672 		}
673 #endif
674 		return 0;
675 	}
676 
677 	/* now catch the FreeBSD style of "no matches" */
678 	if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
679 		array_init(return_value);
680 		return 0;
681 	}
682 
683 	/* we assume that any glob pattern will match files from one directory only
684 	   so checking the dirname of the first match should be sufficient */
685 	if (ZIP_OPENBASEDIR_CHECKPATH(globbuf.gl_pathv[0])) {
686 		return -1;
687 	}
688 
689 	array_init(return_value);
690 	for (n = 0; n < globbuf.gl_pathc; n++) {
691 		/* we need to do this every time since GLOB_ONLYDIR does not guarantee that
692 		 * all directories will be filtered. GNU libc documentation states the
693 		 * following:
694 		 * If the information about the type of the file is easily available
695 		 * non-directories will be rejected but no extra work will be done to
696 		 * determine the information for each file. I.e., the caller must still be
697 		 * able to filter directories out.
698 		 */
699 		if (flags & GLOB_ONLYDIR) {
700 			zend_stat_t s = {0};
701 
702 			if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) {
703 				continue;
704 			}
705 
706 			if (S_IFDIR != (s.st_mode & S_IFMT)) {
707 				continue;
708 			}
709 		}
710 		add_next_index_string(return_value, globbuf.gl_pathv[n]+cwd_skip);
711 	}
712 
713 	ret = globbuf.gl_pathc;
714 	globfree(&globbuf);
715 	return ret;
716 #else
717 	zend_throw_error(NULL, "Glob support is not available");
718 	return 0;
719 #endif  /* HAVE_GLOB */
720 }
721 /* }}} */
722 
php_zip_pcre(zend_string * regexp,char * path,int path_len,zval * return_value)723 int php_zip_pcre(zend_string *regexp, char *path, int path_len, zval *return_value) /* {{{ */
724 {
725 #ifdef ZTS
726 	char cwd[MAXPATHLEN];
727 	char work_path[MAXPATHLEN];
728 	char *result;
729 #endif
730 	int files_cnt;
731 	zend_string **namelist;
732 	pcre2_match_context *mctx = php_pcre_mctx();
733 
734 #ifdef ZTS
735 	if (!IS_ABSOLUTE_PATH(path, path_len)) {
736 		result = VCWD_GETCWD(cwd, MAXPATHLEN);
737 		if (!result) {
738 			cwd[0] = '\0';
739 		}
740 #ifdef PHP_WIN32
741 		if (IS_SLASH(*path)) {
742 			cwd[2] = '\0';
743 		}
744 #endif
745 		snprintf(work_path, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path);
746 		path = work_path;
747 	}
748 #endif
749 
750 	if (ZIP_OPENBASEDIR_CHECKPATH(path)) {
751 		return -1;
752 	}
753 
754 	files_cnt = php_stream_scandir(path, &namelist, NULL, (void *) php_stream_dirent_alphasort);
755 
756 	if (files_cnt > 0) {
757 		pcre2_code *re = NULL;
758 		pcre2_match_data *match_data = NULL;
759 		uint32_t i, capture_count;
760 		int rc;
761 
762 		re = pcre_get_compiled_regex(regexp, &capture_count);
763 		if (!re) {
764 			php_error_docref(NULL, E_WARNING, "Invalid expression");
765 			return -1;
766 		}
767 
768 		array_init(return_value);
769 
770 		/* only the files, directories are ignored */
771 		for (i = 0; i < files_cnt; i++) {
772 			zend_stat_t s = {0};
773 			char   fullpath[MAXPATHLEN];
774 			size_t    namelist_len = ZSTR_LEN(namelist[i]);
775 
776 			if ((namelist_len == 1 && ZSTR_VAL(namelist[i])[0] == '.') ||
777 				(namelist_len == 2 && ZSTR_VAL(namelist[i])[0] == '.' && ZSTR_VAL(namelist[i])[1] == '.')) {
778 				zend_string_release_ex(namelist[i], 0);
779 				continue;
780 			}
781 
782 			if ((path_len + namelist_len + 1) >= MAXPATHLEN) {
783 				php_error_docref(NULL, E_WARNING, "add_path string too long (max: %u, %zu given)",
784 						MAXPATHLEN - 1, (path_len + namelist_len + 1));
785 				zend_string_release_ex(namelist[i], 0);
786 				break;
787 			}
788 
789 			match_data = php_pcre_create_match_data(capture_count, re);
790 			if (!match_data) {
791 				/* Allocation failed, but can proceed to the next pattern. */
792 				zend_string_release_ex(namelist[i], 0);
793 				continue;
794 			}
795 			rc = pcre2_match(re, (PCRE2_SPTR)ZSTR_VAL(namelist[i]), ZSTR_LEN(namelist[i]), 0, 0, match_data, mctx);
796 			php_pcre_free_match_data(match_data);
797 			/* 0 means that the vector is too small to hold all the captured substring offsets */
798 			if (rc < 0) {
799 				zend_string_release_ex(namelist[i], 0);
800 				continue;
801 			}
802 
803 			snprintf(fullpath, MAXPATHLEN, "%s%c%s", path, DEFAULT_SLASH, ZSTR_VAL(namelist[i]));
804 
805 			if (0 != VCWD_STAT(fullpath, &s)) {
806 				php_error_docref(NULL, E_WARNING, "Cannot read <%s>", fullpath);
807 				zend_string_release_ex(namelist[i], 0);
808 				continue;
809 			}
810 
811 			if (S_IFDIR == (s.st_mode & S_IFMT)) {
812 				zend_string_release_ex(namelist[i], 0);
813 				continue;
814 			}
815 
816 			add_next_index_string(return_value, fullpath);
817 			zend_string_release_ex(namelist[i], 0);
818 		}
819 		efree(namelist);
820 	}
821 	return files_cnt;
822 }
823 /* }}} */
824 
825 /* {{{ ZE2 OO definitions */
826 static zend_class_entry *zip_class_entry;
827 static zend_object_handlers zip_object_handlers;
828 
829 static HashTable zip_prop_handlers;
830 
831 typedef zend_long (*zip_read_int_t)(ze_zip_object *obj);
832 typedef char *(*zip_read_const_char_t)(ze_zip_object *obj, int *len);
833 
834 typedef struct _zip_prop_handler {
835 	zip_read_int_t read_int_func;
836 	zip_read_const_char_t read_const_char_func;
837 
838 	int type;
839 } zip_prop_handler;
840 /* }}} */
841 
php_zip_register_prop_handler(HashTable * prop_handler,char * name,zip_read_int_t read_int_func,zip_read_const_char_t read_char_func,int rettype)842 static void php_zip_register_prop_handler(HashTable *prop_handler, char *name, zip_read_int_t read_int_func, zip_read_const_char_t read_char_func, int rettype) /* {{{ */
843 {
844 	zip_prop_handler hnd;
845 	zend_string *str;
846 
847 	hnd.read_const_char_func = read_char_func;
848 	hnd.read_int_func = read_int_func;
849 	hnd.type = rettype;
850 	str = zend_string_init_interned(name, strlen(name), 1);
851 	zend_hash_add_mem(prop_handler, str, &hnd, sizeof(zip_prop_handler));
852 	zend_string_release_ex(str, 1);
853 }
854 /* }}} */
855 
php_zip_property_reader(ze_zip_object * obj,zip_prop_handler * hnd,zval * rv)856 static zval *php_zip_property_reader(ze_zip_object *obj, zip_prop_handler *hnd, zval *rv) /* {{{ */
857 {
858 	const char *retchar = NULL;
859 	zend_long retint = 0;
860 	int len = 0;
861 
862 	if (hnd->read_const_char_func) {
863 		retchar = hnd->read_const_char_func(obj, &len);
864 	} else if (hnd->read_int_func) {
865 		retint = hnd->read_int_func(obj);
866 	}
867 
868 	switch (hnd->type) {
869 		case IS_STRING:
870 			if (retchar) {
871 				ZVAL_STRINGL(rv, (char *) retchar, len);
872 			} else {
873 				ZVAL_EMPTY_STRING(rv);
874 			}
875 			break;
876 		case IS_LONG:
877 			ZVAL_LONG(rv, retint);
878 			break;
879 		default:
880 			ZVAL_NULL(rv);
881 	}
882 
883 	return rv;
884 }
885 /* }}} */
886 
php_zip_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)887 static zval *php_zip_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot) /* {{{ */
888 {
889 	ze_zip_object *obj;
890 	zval *retval = NULL;
891 	zip_prop_handler *hnd = NULL;
892 
893 	obj = php_zip_fetch_object(object);
894 
895 	if (obj->prop_handler != NULL) {
896 		hnd = zend_hash_find_ptr(obj->prop_handler, name);
897 	}
898 
899 	if (hnd == NULL) {
900 		retval = zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
901 	}
902 
903 	return retval;
904 }
905 /* }}} */
906 
907 
php_zip_write_property(zend_object * object,zend_string * name,zval * value,void ** cache_slot)908 static zval *php_zip_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
909 {
910 	ze_zip_object *obj;
911 	zip_prop_handler *hnd = NULL;
912 
913 	obj = php_zip_fetch_object(object);
914 
915 	if (obj->prop_handler != NULL) {
916 		hnd = zend_hash_find_ptr(obj->prop_handler, name);
917 	}
918 
919 	if (hnd != NULL) {
920 		zend_throw_error(NULL, "Cannot write read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
921 		return &EG(error_zval);
922 	}
923 
924 	return zend_std_write_property(object, name, value, cache_slot);
925 }
926 
php_zip_read_property(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)927 static zval *php_zip_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) /* {{{ */
928 {
929 	ze_zip_object *obj;
930 	zval *retval = NULL;
931 	zip_prop_handler *hnd = NULL;
932 
933 	obj = php_zip_fetch_object(object);
934 
935 	if (obj->prop_handler != NULL) {
936 		hnd = zend_hash_find_ptr(obj->prop_handler, name);
937 	}
938 
939 	if (hnd != NULL) {
940 		retval = php_zip_property_reader(obj, hnd, rv);
941 		if (retval == NULL) {
942 			retval = &EG(uninitialized_zval);
943 		}
944 	} else {
945 		retval = zend_std_read_property(object, name, type, cache_slot, rv);
946 	}
947 
948 	return retval;
949 }
950 /* }}} */
951 
php_zip_has_property(zend_object * object,zend_string * name,int type,void ** cache_slot)952 static int php_zip_has_property(zend_object *object, zend_string *name, int type, void **cache_slot) /* {{{ */
953 {
954 	ze_zip_object *obj;
955 	zip_prop_handler *hnd = NULL;
956 	int retval = 0;
957 
958 	obj = php_zip_fetch_object(object);
959 
960 	if (obj->prop_handler != NULL) {
961 		hnd = zend_hash_find_ptr(obj->prop_handler, name);
962 	}
963 
964 	if (hnd != NULL) {
965 		zval tmp, *prop;
966 
967 		if (type == 2) {
968 			retval = 1;
969 		} else if ((prop = php_zip_property_reader(obj, hnd, &tmp)) != NULL) {
970 			if (type == 1) {
971 				retval = zend_is_true(&tmp);
972 			} else if (type == 0) {
973 				retval = (Z_TYPE(tmp) != IS_NULL);
974 			}
975 		}
976 
977 		zval_ptr_dtor(&tmp);
978 	} else {
979 		retval = zend_std_has_property(object, name, type, cache_slot);
980 	}
981 
982 	return retval;
983 }
984 /* }}} */
985 
php_zip_get_gc(zend_object * object,zval ** gc_data,int * gc_data_count)986 static HashTable *php_zip_get_gc(zend_object *object, zval **gc_data, int *gc_data_count) /* {{{ */
987 {
988 	*gc_data = NULL;
989 	*gc_data_count = 0;
990 	return zend_std_get_properties(object);
991 }
992 /* }}} */
993 
php_zip_get_properties(zend_object * object)994 static HashTable *php_zip_get_properties(zend_object *object)/* {{{ */
995 {
996 	ze_zip_object *obj;
997 	HashTable *props;
998 	zip_prop_handler *hnd;
999 	zend_string *key;
1000 
1001 	obj = php_zip_fetch_object(object);
1002 	props = zend_std_get_properties(object);
1003 
1004 	if (obj->prop_handler == NULL) {
1005 		return NULL;
1006 	}
1007 
1008 	ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(obj->prop_handler, key, hnd) {
1009 		zval *ret, val;
1010 		ret = php_zip_property_reader(obj, hnd, &val);
1011 		if (ret == NULL) {
1012 			ret = &EG(uninitialized_zval);
1013 		}
1014 		zend_hash_update(props, key, ret);
1015 	} ZEND_HASH_FOREACH_END();
1016 
1017 	return props;
1018 }
1019 /* }}} */
1020 
1021 #ifdef HAVE_PROGRESS_CALLBACK
_php_zip_progress_callback_free(void * ptr)1022 static void _php_zip_progress_callback_free(void *ptr)
1023 {
1024 	ze_zip_object *obj = ptr;
1025 
1026 	if (!Z_ISUNDEF(obj->progress_callback)) {
1027 		zval_ptr_dtor(&obj->progress_callback);
1028 		ZVAL_UNDEF(&obj->progress_callback);
1029 	}
1030 }
1031 #endif
1032 
1033 #ifdef HAVE_CANCEL_CALLBACK
_php_zip_cancel_callback_free(void * ptr)1034 static void _php_zip_cancel_callback_free(void *ptr)
1035 {
1036 	ze_zip_object *obj = ptr;
1037 
1038 	if (!Z_ISUNDEF(obj->cancel_callback)) {
1039 		zval_ptr_dtor(&obj->cancel_callback);
1040 		ZVAL_UNDEF(&obj->cancel_callback);
1041 	}
1042 }
1043 #endif
1044 
php_zip_object_free_storage(zend_object * object)1045 static void php_zip_object_free_storage(zend_object *object) /* {{{ */
1046 {
1047 	ze_zip_object * intern = php_zip_fetch_object(object);
1048 	int i;
1049 
1050 	if (!intern) {
1051 		return;
1052 	}
1053 	if (intern->za) {
1054 		if (zip_close(intern->za) != 0) {
1055 			php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context: %s", zip_strerror(intern->za));
1056 			zip_discard(intern->za);
1057 		}
1058 	}
1059 
1060 	if (intern->buffers_cnt>0) {
1061 		for (i=0; i<intern->buffers_cnt; i++) {
1062 			efree(intern->buffers[i]);
1063 		}
1064 		efree(intern->buffers);
1065 	}
1066 
1067 #ifdef HAVE_PROGRESS_CALLBACK
1068 	/* if not properly called by libzip */
1069 	_php_zip_progress_callback_free(intern);
1070 #endif
1071 
1072 #ifdef HAVE_CANCEL_CALLBACK
1073 	/* if not properly called by libzip */
1074 	_php_zip_cancel_callback_free(intern);
1075 #endif
1076 
1077 	intern->za = NULL;
1078 	zend_object_std_dtor(&intern->zo);
1079 
1080 	if (intern->filename) {
1081 		efree(intern->filename);
1082 	}
1083 }
1084 /* }}} */
1085 
php_zip_object_new(zend_class_entry * class_type)1086 static zend_object *php_zip_object_new(zend_class_entry *class_type) /* {{{ */
1087 {
1088 	ze_zip_object *intern;
1089 
1090 	intern = zend_object_alloc(sizeof(ze_zip_object), class_type);
1091 	intern->prop_handler = &zip_prop_handlers;
1092 	zend_object_std_init(&intern->zo, class_type);
1093 	object_properties_init(&intern->zo, class_type);
1094 	intern->last_id = -1;
1095 
1096 	return &intern->zo;
1097 }
1098 /* }}} */
1099 
1100 /* {{{ Resource dtors */
1101 
1102 /* {{{ php_zip_free_dir */
php_zip_free_dir(zend_resource * rsrc)1103 static void php_zip_free_dir(zend_resource *rsrc)
1104 {
1105 	zip_rsrc * zip_int = (zip_rsrc *) rsrc->ptr;
1106 
1107 	if (zip_int) {
1108 		if (zip_int->za) {
1109 			if (zip_close(zip_int->za) != 0) {
1110 				php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context");
1111 			}
1112 			zip_int->za = NULL;
1113 		}
1114 
1115 		efree(rsrc->ptr);
1116 
1117 		rsrc->ptr = NULL;
1118 	}
1119 }
1120 /* }}} */
1121 
1122 /* {{{ php_zip_free_entry */
php_zip_free_entry(zend_resource * rsrc)1123 static void php_zip_free_entry(zend_resource *rsrc)
1124 {
1125 	zip_read_rsrc *zr_rsrc = (zip_read_rsrc *) rsrc->ptr;
1126 
1127 	if (zr_rsrc) {
1128 		if (zr_rsrc->zf) {
1129 			zip_fclose(zr_rsrc->zf);
1130 			zr_rsrc->zf = NULL;
1131 		}
1132 		efree(zr_rsrc);
1133 		rsrc->ptr = NULL;
1134 	}
1135 }
1136 /* }}} */
1137 
1138 /* }}}*/
1139 
1140 /* reset macro */
1141 
1142 /* {{{ function prototypes */
1143 static PHP_MINIT_FUNCTION(zip);
1144 static PHP_MSHUTDOWN_FUNCTION(zip);
1145 static PHP_MINFO_FUNCTION(zip);
1146 /* }}} */
1147 
1148 /* {{{ zip_module_entry */
1149 zend_module_entry zip_module_entry = {
1150 	STANDARD_MODULE_HEADER,
1151 	"zip",
1152 	ext_functions,
1153 	PHP_MINIT(zip),
1154 	PHP_MSHUTDOWN(zip),
1155 	NULL,
1156 	NULL,
1157 	PHP_MINFO(zip),
1158 	PHP_ZIP_VERSION,
1159 	STANDARD_MODULE_PROPERTIES
1160 };
1161 /* }}} */
1162 
1163 #ifdef COMPILE_DL_ZIP
1164 ZEND_GET_MODULE(zip)
1165 #endif
1166 /* set macro */
1167 
1168 /* {{{ Create new zip using source uri for output */
PHP_FUNCTION(zip_open)1169 PHP_FUNCTION(zip_open)
1170 {
1171 	char resolved_path[MAXPATHLEN + 1];
1172 	zip_rsrc *rsrc_int;
1173 	int err = 0;
1174 	zend_string *filename;
1175 
1176 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &filename) == FAILURE) {
1177 		RETURN_THROWS();
1178 	}
1179 
1180 	if (ZSTR_LEN(filename) == 0) {
1181 		zend_argument_value_error(1, "cannot be empty");
1182 		RETURN_THROWS();
1183 	}
1184 
1185 	if (ZIP_OPENBASEDIR_CHECKPATH(ZSTR_VAL(filename))) {
1186 		RETURN_FALSE;
1187 	}
1188 
1189 	if(!expand_filepath(ZSTR_VAL(filename), resolved_path)) {
1190 		php_error_docref(NULL, E_WARNING, "No such file or directory");
1191 		RETURN_FALSE;
1192 	}
1193 
1194 	rsrc_int = (zip_rsrc *)emalloc(sizeof(zip_rsrc));
1195 
1196 	rsrc_int->za = zip_open(resolved_path, 0, &err);
1197 	if (rsrc_int->za == NULL) {
1198 		efree(rsrc_int);
1199 		RETURN_LONG((zend_long)err);
1200 	}
1201 
1202 	rsrc_int->index_current = 0;
1203 	rsrc_int->num_files = zip_get_num_entries(rsrc_int->za, 0);
1204 
1205 	RETURN_RES(zend_register_resource(rsrc_int, le_zip_dir));
1206 }
1207 /* }}} */
1208 
1209 /* {{{ Close a Zip archive */
PHP_FUNCTION(zip_close)1210 PHP_FUNCTION(zip_close)
1211 {
1212 	zval * zip;
1213 	zip_rsrc *z_rsrc = NULL;
1214 
1215 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip) == FAILURE) {
1216 		RETURN_THROWS();
1217 	}
1218 
1219 	if ((z_rsrc = (zip_rsrc *)zend_fetch_resource(Z_RES_P(zip), le_zip_dir_name, le_zip_dir)) == NULL) {
1220 		RETURN_THROWS();
1221 	}
1222 
1223 	/* really close the zip will break BC :-D */
1224 	zend_list_close(Z_RES_P(zip));
1225 }
1226 /* }}} */
1227 
1228 /* {{{ Returns the next file in the archive */
PHP_FUNCTION(zip_read)1229 PHP_FUNCTION(zip_read)
1230 {
1231 	zval *zip_dp;
1232 	zip_read_rsrc *zr_rsrc;
1233 	int ret;
1234 	zip_rsrc *rsrc_int;
1235 
1236 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip_dp) == FAILURE) {
1237 		RETURN_THROWS();
1238 	}
1239 
1240 	if ((rsrc_int = (zip_rsrc *)zend_fetch_resource(Z_RES_P(zip_dp), le_zip_dir_name, le_zip_dir)) == NULL) {
1241 		RETURN_THROWS();
1242 	}
1243 
1244 	if (rsrc_int && rsrc_int->za) {
1245 		if (rsrc_int->index_current >= rsrc_int->num_files) {
1246 			RETURN_FALSE;
1247 		}
1248 
1249 		zr_rsrc = emalloc(sizeof(zip_read_rsrc));
1250 
1251 		ret = zip_stat_index(rsrc_int->za, rsrc_int->index_current, 0, &zr_rsrc->sb);
1252 
1253 		if (ret != 0) {
1254 			efree(zr_rsrc);
1255 			RETURN_FALSE;
1256 		}
1257 
1258 		zr_rsrc->zf = zip_fopen_index(rsrc_int->za, rsrc_int->index_current, 0);
1259 		if (zr_rsrc->zf) {
1260 			rsrc_int->index_current++;
1261 			RETURN_RES(zend_register_resource(zr_rsrc, le_zip_entry));
1262 		} else {
1263 			efree(zr_rsrc);
1264 			RETURN_FALSE;
1265 		}
1266 
1267 	} else {
1268 		RETURN_FALSE;
1269 	}
1270 }
1271 /* }}} */
1272 
1273 /* {{{ Open a Zip File, pointed by the resource entry */
1274 /* Dummy function to follow the old API */
PHP_FUNCTION(zip_entry_open)1275 PHP_FUNCTION(zip_entry_open)
1276 {
1277 	zval * zip;
1278 	zval * zip_entry;
1279 	char *mode = NULL;
1280 	size_t mode_len = 0;
1281 	zip_read_rsrc * zr_rsrc;
1282 	zip_rsrc *z_rsrc;
1283 
1284 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|s", &zip, &zip_entry, &mode, &mode_len) == FAILURE) {
1285 		RETURN_THROWS();
1286 	}
1287 
1288 	if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) {
1289 		RETURN_THROWS();
1290 	}
1291 
1292 	if ((z_rsrc = (zip_rsrc *)zend_fetch_resource(Z_RES_P(zip), le_zip_dir_name, le_zip_dir)) == NULL) {
1293 		RETURN_THROWS();
1294 	}
1295 
1296 	if (zr_rsrc->zf != NULL) {
1297 		RETURN_TRUE;
1298 	} else {
1299 		RETURN_FALSE;
1300 	}
1301 }
1302 /* }}} */
1303 
1304 /* {{{ Close a zip entry */
PHP_FUNCTION(zip_entry_close)1305 PHP_FUNCTION(zip_entry_close)
1306 {
1307 	zval * zip_entry;
1308 	zip_read_rsrc * zr_rsrc;
1309 
1310 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip_entry) == FAILURE) {
1311 		RETURN_THROWS();
1312 	}
1313 
1314 	if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) {
1315 		RETURN_THROWS();
1316 	}
1317 
1318 	zend_list_close(Z_RES_P(zip_entry));
1319 	RETURN_TRUE;
1320 }
1321 /* }}} */
1322 
1323 /* {{{ Read from an open directory entry */
PHP_FUNCTION(zip_entry_read)1324 PHP_FUNCTION(zip_entry_read)
1325 {
1326 	zval * zip_entry;
1327 	zend_long len = 0;
1328 	zip_read_rsrc * zr_rsrc;
1329 	zend_string *buffer;
1330 	int n = 0;
1331 
1332 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &zip_entry, &len) == FAILURE) {
1333 		RETURN_THROWS();
1334 	}
1335 
1336 	if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) {
1337 		RETURN_THROWS();
1338 	}
1339 
1340 	if (len <= 0) {
1341 		len = 1024;
1342 	}
1343 
1344 	if (zr_rsrc->zf) {
1345 		buffer = zend_string_safe_alloc(1, len, 0, 0);
1346 		n = zip_fread(zr_rsrc->zf, ZSTR_VAL(buffer), ZSTR_LEN(buffer));
1347 		if (n > 0) {
1348 			ZSTR_VAL(buffer)[n] = '\0';
1349 			ZSTR_LEN(buffer) = n;
1350 			RETURN_NEW_STR(buffer);
1351 		} else {
1352 			zend_string_efree(buffer);
1353 			RETURN_EMPTY_STRING();
1354 		}
1355 	} else {
1356 		RETURN_FALSE;
1357 	}
1358 }
1359 /* }}} */
1360 
php_zip_entry_get_info(INTERNAL_FUNCTION_PARAMETERS,int opt)1361 static void php_zip_entry_get_info(INTERNAL_FUNCTION_PARAMETERS, int opt) /* {{{ */
1362 {
1363 	zval * zip_entry;
1364 	zip_read_rsrc * zr_rsrc;
1365 
1366 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zip_entry) == FAILURE) {
1367 		RETURN_THROWS();
1368 	}
1369 
1370 	if ((zr_rsrc = (zip_read_rsrc *)zend_fetch_resource(Z_RES_P(zip_entry), le_zip_entry_name, le_zip_entry)) == NULL) {
1371 		RETURN_THROWS();
1372 	}
1373 
1374 	if (!zr_rsrc->zf) {
1375 		RETURN_FALSE;
1376 	}
1377 
1378 	switch (opt) {
1379 		case 0:
1380 			RETURN_STRING((char *)zr_rsrc->sb.name);
1381 		case 1:
1382 			RETURN_LONG((zend_long) (zr_rsrc->sb.comp_size));
1383 		case 2:
1384 			RETURN_LONG((zend_long) (zr_rsrc->sb.size));
1385 		case 3:
1386 			switch (zr_rsrc->sb.comp_method) {
1387 				case 0:
1388 					RETURN_STRING("stored");
1389 				case 1:
1390 					RETURN_STRING("shrunk");
1391 				case 2:
1392 				case 3:
1393 				case 4:
1394 				case 5:
1395 					RETURN_STRING("reduced");
1396 				case 6:
1397 					RETURN_STRING("imploded");
1398 				case 7:
1399 					RETURN_STRING("tokenized");
1400 					break;
1401 				case 8:
1402 					RETURN_STRING("deflated");
1403 				case 9:
1404 					RETURN_STRING("deflatedX");
1405 					break;
1406 				case 10:
1407 					RETURN_STRING("implodedX");
1408 				default:
1409 					RETURN_FALSE;
1410 			}
1411 	}
1412 
1413 }
1414 /* }}} */
1415 
1416 /* {{{ Return the name given a ZZip entry */
PHP_FUNCTION(zip_entry_name)1417 PHP_FUNCTION(zip_entry_name)
1418 {
1419 	php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1420 }
1421 /* }}} */
1422 
1423 /* {{{ Return the compressed size of a ZZip entry */
PHP_FUNCTION(zip_entry_compressedsize)1424 PHP_FUNCTION(zip_entry_compressedsize)
1425 {
1426 	php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1427 }
1428 /* }}} */
1429 
1430 /* {{{ Return the actual filesize of a ZZip entry */
PHP_FUNCTION(zip_entry_filesize)1431 PHP_FUNCTION(zip_entry_filesize)
1432 {
1433 	php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
1434 }
1435 /* }}} */
1436 
1437 /* {{{ Return a string containing the compression method used on a particular entry */
PHP_FUNCTION(zip_entry_compressionmethod)1438 PHP_FUNCTION(zip_entry_compressionmethod)
1439 {
1440 	php_zip_entry_get_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
1441 }
1442 /* }}} */
1443 
1444 /* {{{ Create new zip using source uri for output, return TRUE on success or the error code */
PHP_METHOD(ZipArchive,open)1445 PHP_METHOD(ZipArchive, open)
1446 {
1447 	struct zip *intern;
1448 	int err = 0;
1449 	zend_long flags = 0;
1450 	char *resolved_path;
1451 	zend_string *filename;
1452 	zval *self = ZEND_THIS;
1453 	ze_zip_object *ze_obj;
1454 
1455 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &filename, &flags) == FAILURE) {
1456 		RETURN_THROWS();
1457 	}
1458 
1459 	/* We do not use ZIP_FROM_OBJECT, zip init function here */
1460 	ze_obj = Z_ZIP_P(self);
1461 
1462 	if (ZSTR_LEN(filename) == 0) {
1463 		zend_argument_value_error(1, "cannot be empty");
1464 		RETURN_THROWS();
1465 	}
1466 
1467 	if (ZIP_OPENBASEDIR_CHECKPATH(ZSTR_VAL(filename))) {
1468 		RETURN_FALSE;
1469 	}
1470 
1471 	if (!(resolved_path = expand_filepath(ZSTR_VAL(filename), NULL))) {
1472 		php_error_docref(NULL, E_WARNING, "No such file or directory");
1473 		RETURN_FALSE;
1474 	}
1475 
1476 	if (ze_obj->za) {
1477 		/* we already have an opened zip, free it */
1478 		if (zip_close(ze_obj->za) != 0) {
1479 			php_error_docref(NULL, E_WARNING, "Empty string as source");
1480 			efree(resolved_path);
1481 			RETURN_FALSE;
1482 		}
1483 		ze_obj->za = NULL;
1484 	}
1485 	if (ze_obj->filename) {
1486 		efree(ze_obj->filename);
1487 		ze_obj->filename = NULL;
1488 	}
1489 
1490 	/* open for write without option to empty the archive */
1491 #ifdef ZIP_RDONLY
1492 	if ((flags & (ZIP_TRUNCATE | ZIP_RDONLY)) == 0) {
1493 #else
1494 	if ((flags & ZIP_TRUNCATE) == 0) {
1495 #endif
1496 		zend_stat_t st = {0};
1497 
1498 		/* exists and is empty */
1499 		if (VCWD_STAT(resolved_path, &st) == 0 && st.st_size == 0) {
1500 			php_error_docref(NULL, E_DEPRECATED, "Using empty file as ZipArchive is deprecated");
1501 
1502 			/* reduce BC break introduced in libzip 1.6.0
1503 			   "Do not accept empty files as valid zip archives any longer" */
1504 			flags |= ZIP_TRUNCATE;
1505 		}
1506 	}
1507 
1508 	intern = zip_open(resolved_path, flags, &err);
1509 	if (!intern || err) {
1510 		efree(resolved_path);
1511 		RETURN_LONG((zend_long)err);
1512 	}
1513 	ze_obj->filename = resolved_path;
1514 	ze_obj->filename_len = strlen(resolved_path);
1515 	ze_obj->za = intern;
1516 	RETURN_TRUE;
1517 }
1518 /* }}} */
1519 
1520 /* {{{ Set the password for the active archive */
1521 PHP_METHOD(ZipArchive, setPassword)
1522 {
1523 	struct zip *intern;
1524 	zval *self = ZEND_THIS;
1525 	char *password;
1526 	size_t	password_len;
1527 
1528 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &password, &password_len) == FAILURE) {
1529 		RETURN_THROWS();
1530 	}
1531 
1532 	ZIP_FROM_OBJECT(intern, self);
1533 
1534 	if (password_len < 1) {
1535 		RETURN_FALSE;
1536 	}
1537 
1538 	int res = zip_set_default_password(intern, (const char *)password);
1539 	if (res == 0) {
1540 		RETURN_TRUE;
1541 	} else {
1542 		RETURN_FALSE;
1543 	}
1544 }
1545 /* }}} */
1546 
1547 /* {{{ close the zip archive */
1548 PHP_METHOD(ZipArchive, close)
1549 {
1550 	struct zip *intern;
1551 	zval *self = ZEND_THIS;
1552 	ze_zip_object *ze_obj;
1553 	int err;
1554 
1555 	if (zend_parse_parameters_none() == FAILURE) {
1556 		RETURN_THROWS();
1557 	}
1558 
1559 	ZIP_FROM_OBJECT(intern, self);
1560 
1561 	ze_obj = Z_ZIP_P(self);
1562 
1563 	err = zip_close(intern);
1564 	if (err) {
1565 		php_error_docref(NULL, E_WARNING, "%s", zip_strerror(intern));
1566 		/* Save error for property reader */
1567 		#if LIBZIP_VERSION_MAJOR < 1
1568 			zip_error_get(intern, &ze_obj->err_zip, &ze_obj->err_sys);
1569 		#else
1570 			{
1571 			zip_error_t *ziperr;
1572 
1573 			ziperr = zip_get_error(intern);
1574 			ze_obj->err_zip = zip_error_code_zip(ziperr);
1575 			ze_obj->err_sys = zip_error_code_system(ziperr);
1576 			zip_error_fini(ziperr);
1577 			}
1578 		#endif
1579 		zip_discard(intern);
1580 	} else {
1581 		ze_obj->err_zip = 0;
1582 		ze_obj->err_sys = 0;
1583 	}
1584 
1585 	/* clear cache as empty zip are not created but deleted */
1586 	php_clear_stat_cache(1, ze_obj->filename, ze_obj->filename_len);
1587 
1588 	efree(ze_obj->filename);
1589 	ze_obj->filename = NULL;
1590 	ze_obj->filename_len = 0;
1591 	ze_obj->za = NULL;
1592 
1593 	if (!err) {
1594 		RETURN_TRUE;
1595 	} else {
1596 		RETURN_FALSE;
1597 	}
1598 }
1599 /* }}} */
1600 
1601 /* {{{ close the zip archive */
1602 PHP_METHOD(ZipArchive, count)
1603 {
1604 	struct zip *intern;
1605 	zval *self = ZEND_THIS;
1606 	zip_int64_t num;
1607 
1608 	if (zend_parse_parameters_none() == FAILURE) {
1609 		RETURN_THROWS();
1610 	}
1611 
1612 	ZIP_FROM_OBJECT(intern, self);
1613 
1614 	num = zip_get_num_entries(intern, 0);
1615 	RETVAL_LONG(MIN(num, ZEND_LONG_MAX));
1616 }
1617 /* }}} */
1618 
1619 /* {{{ clear the internal status */
1620 PHP_METHOD(ZipArchive, clearError)
1621 {
1622 	zval *self = ZEND_THIS;
1623 	ze_zip_object *ze_obj;
1624 
1625 	if (zend_parse_parameters_none() == FAILURE) {
1626 		RETURN_THROWS();
1627 	}
1628 
1629 	ze_obj = Z_ZIP_P(self); /* not ZIP_FROM_OBJECT as we can use saved error after close */
1630 	if (ze_obj->za) {
1631 		zip_error_clear(ze_obj->za);
1632 	} else {
1633 		ze_obj->err_zip = 0;
1634 		ze_obj->err_sys = 0;
1635 	}
1636 }
1637 /* }}} */
1638 
1639 /* {{{ Returns the status error message, system and/or zip messages */
1640 PHP_METHOD(ZipArchive, getStatusString)
1641 {
1642 	zval *self = ZEND_THIS;
1643 #if LIBZIP_VERSION_MAJOR < 1
1644 	int zep, syp, len;
1645 	char error_string[128];
1646 #endif
1647 	ze_zip_object *ze_obj;
1648 
1649 	if (zend_parse_parameters_none() == FAILURE) {
1650 		RETURN_THROWS();
1651 	}
1652 
1653 	ze_obj = Z_ZIP_P(self); /* not ZIP_FROM_OBJECT as we can use saved error after close */
1654 
1655 #if LIBZIP_VERSION_MAJOR < 1
1656 	if (ze_obj->za) {
1657 		zip_error_get(ze_obj->za, &zep, &syp);
1658 		len = zip_error_to_str(error_string, 128, zep, syp);
1659 	} else {
1660 		len = zip_error_to_str(error_string, 128, ze_obj->err_zip, ze_obj->err_sys);
1661 	}
1662 	RETVAL_STRINGL(error_string, len);
1663 #else
1664 	if (ze_obj->za) {
1665 		zip_error_t *err;
1666 
1667 		err = zip_get_error(ze_obj->za);
1668 		RETVAL_STRING(zip_error_strerror(err));
1669 		zip_error_fini(err);
1670 	} else {
1671 		zip_error_t err;
1672 
1673 		zip_error_init(&err);
1674 		zip_error_set(&err, ze_obj->err_zip, ze_obj->err_sys);
1675 		RETVAL_STRING(zip_error_strerror(&err));
1676 		zip_error_fini(&err);
1677 	}
1678 #endif
1679 }
1680 /* }}} */
1681 
1682 /* {{{ Returns the index of the entry named filename in the archive */
1683 PHP_METHOD(ZipArchive, addEmptyDir)
1684 {
1685 	struct zip *intern;
1686 	zval *self = ZEND_THIS;
1687 	char *dirname;
1688 	size_t   dirname_len;
1689 	char *s;
1690 	zend_long flags = 0;
1691 
1692 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l",
1693 				&dirname, &dirname_len, &flags) == FAILURE) {
1694 		RETURN_THROWS();
1695 	}
1696 
1697 	ZIP_FROM_OBJECT(intern, self);
1698 
1699 	if (dirname_len<1) {
1700 		RETURN_FALSE;
1701 	}
1702 
1703 	if (dirname[dirname_len-1] != '/') {
1704 		s=(char *)safe_emalloc(dirname_len, 1, 2);
1705 		strcpy(s, dirname);
1706 		s[dirname_len] = '/';
1707 		s[dirname_len+1] = '\0';
1708 	} else {
1709 		s = dirname;
1710 	}
1711 
1712 	if ((Z_ZIP_P(self)->last_id = zip_dir_add(intern, (const char *)s, flags)) == -1) {
1713 		RETVAL_FALSE;
1714 	} else {
1715 		zip_error_clear(intern);
1716 		RETVAL_TRUE;
1717 	}
1718 
1719 	if (s != dirname) {
1720 		efree(s);
1721 	}
1722 }
1723 /* }}} */
1724 
1725 static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
1726 {
1727 	zval *self = ZEND_THIS;
1728 	char *path = ".";
1729 	size_t  path_len = 1;
1730 	zend_long glob_flags = 0;
1731 	HashTable *options = NULL;
1732 	zip_options opts;
1733 	int found;
1734 	zend_string *pattern;
1735 
1736 	/* 1 == glob, 2 == pcre */
1737 	if (type == 1) {
1738 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|lh",
1739 					&pattern, &glob_flags, &options) == FAILURE) {
1740 			RETURN_THROWS();
1741 		}
1742 	} else {
1743 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|sh",
1744 					&pattern, &path, &path_len, &options) == FAILURE) {
1745 			RETURN_THROWS();
1746 		}
1747 	}
1748 
1749 	if (ZSTR_LEN(pattern) == 0) {
1750 		zend_argument_value_error(1, "cannot be empty");
1751 		RETURN_THROWS();
1752 	}
1753 	if (options && zend_hash_num_elements(options) > 0 && (php_zip_parse_options(options, &opts) < 0)) {
1754 		RETURN_THROWS();
1755 	}
1756 
1757 	if (type == 1) {
1758 		found = php_zip_glob(ZSTR_VAL(pattern), ZSTR_LEN(pattern), glob_flags, return_value);
1759 	} else {
1760 		found = php_zip_pcre(pattern, path, path_len, return_value);
1761 	}
1762 
1763 	if (found > 0) {
1764 		int i;
1765 		zval *zval_file;
1766 		ze_zip_object *ze_obj;
1767 
1768 		ze_obj = Z_ZIP_P(self);
1769 
1770 		for (i = 0; i < found; i++) {
1771 			char *file_stripped, *entry_name;
1772 			size_t entry_name_len, file_stripped_len;
1773 			char entry_name_buf[MAXPATHLEN];
1774 			zend_string *basename = NULL;
1775 
1776 			if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(return_value), i)) != NULL) {
1777 				if (opts.remove_all_path) {
1778 					basename = php_basename(Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), NULL, 0);
1779 					file_stripped = ZSTR_VAL(basename);
1780 					file_stripped_len = ZSTR_LEN(basename);
1781 				} else if (opts.remove_path && !memcmp(Z_STRVAL_P(zval_file), opts.remove_path, opts.remove_path_len)) {
1782 					if (IS_SLASH(Z_STRVAL_P(zval_file)[opts.remove_path_len])) {
1783 						file_stripped = Z_STRVAL_P(zval_file) + opts.remove_path_len + 1;
1784 						file_stripped_len = Z_STRLEN_P(zval_file) - opts.remove_path_len - 1;
1785 					} else {
1786 						file_stripped = Z_STRVAL_P(zval_file) + opts.remove_path_len;
1787 						file_stripped_len = Z_STRLEN_P(zval_file) - opts.remove_path_len;
1788 					}
1789 				} else {
1790 					file_stripped = Z_STRVAL_P(zval_file);
1791 					file_stripped_len = Z_STRLEN_P(zval_file);
1792 				}
1793 
1794 				if (opts.add_path) {
1795 					if ((opts.add_path_len + file_stripped_len) > MAXPATHLEN) {
1796 						php_error_docref(NULL, E_WARNING, "Entry name too long (max: %d, %zd given)",
1797 						MAXPATHLEN - 1, (opts.add_path_len + file_stripped_len));
1798 						zend_array_destroy(Z_ARR_P(return_value));
1799 						RETURN_FALSE;
1800 					}
1801 					snprintf(entry_name_buf, MAXPATHLEN, "%s%s", opts.add_path, file_stripped);
1802 				} else {
1803 					snprintf(entry_name_buf, MAXPATHLEN, "%s", file_stripped);
1804 				}
1805 
1806 				entry_name = entry_name_buf;
1807 				entry_name_len = strlen(entry_name);
1808 				if (basename) {
1809 					zend_string_release_ex(basename, 0);
1810 					basename = NULL;
1811 				}
1812 
1813 				if (php_zip_add_file(ze_obj, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file),
1814 					entry_name, entry_name_len, 0, 0, -1, opts.flags) < 0) {
1815 					zend_array_destroy(Z_ARR_P(return_value));
1816 					RETURN_FALSE;
1817 				}
1818 				if (opts.comp_method >= 0) {
1819 					if (zip_set_file_compression(ze_obj->za, ze_obj->last_id, opts.comp_method, opts.comp_flags)) {
1820 						zend_array_destroy(Z_ARR_P(return_value));
1821 						RETURN_FALSE;
1822 					}
1823 				}
1824 #ifdef HAVE_ENCRYPTION
1825 				if (opts.enc_method >= 0) {
1826 					if (zip_file_set_encryption(ze_obj->za, ze_obj->last_id, opts.enc_method, opts.enc_password)) {
1827 						zend_array_destroy(Z_ARR_P(return_value));
1828 						RETURN_FALSE;
1829 					}
1830 				}
1831 #endif
1832 			}
1833 		}
1834 	}
1835 }
1836 /* }}} */
1837 
1838 /* {{{ Add files matching the glob pattern. See php's glob for the pattern syntax. */
1839 PHP_METHOD(ZipArchive, addGlob)
1840 {
1841 	php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1842 }
1843 /* }}} */
1844 
1845 /* {{{ Add files matching the pcre pattern. See php's pcre for the pattern syntax. */
1846 PHP_METHOD(ZipArchive, addPattern)
1847 {
1848 	php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
1849 }
1850 /* }}} */
1851 
1852 /* {{{ Add a file in a Zip archive using its path and the name to use. */
1853 PHP_METHOD(ZipArchive, addFile)
1854 {
1855 	zval *self = ZEND_THIS;
1856 	char *entry_name = NULL;
1857 	size_t entry_name_len = 0;
1858 	zend_long offset_start = 0, offset_len = 0;
1859 	zend_string *filename;
1860 	zend_long flags = ZIP_FL_OVERWRITE;
1861 
1862 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|slll",
1863 			&filename, &entry_name, &entry_name_len, &offset_start, &offset_len, &flags) == FAILURE) {
1864 		RETURN_THROWS();
1865 	}
1866 
1867 	if (ZSTR_LEN(filename) == 0) {
1868 		zend_argument_value_error(1, "cannot be empty");
1869 		RETURN_THROWS();
1870 	}
1871 
1872 	if (entry_name_len == 0) {
1873 		entry_name = ZSTR_VAL(filename);
1874 		entry_name_len = ZSTR_LEN(filename);
1875 	}
1876 
1877 	if (php_zip_add_file(Z_ZIP_P(self), ZSTR_VAL(filename), ZSTR_LEN(filename),
1878 			entry_name, entry_name_len, offset_start, offset_len, -1, flags) < 0) {
1879 		RETURN_FALSE;
1880 	} else {
1881 		RETURN_TRUE;
1882 	}
1883 }
1884 /* }}} */
1885 
1886 /* {{{ Add a file in a Zip archive using its path and the name to use. */
1887 PHP_METHOD(ZipArchive, replaceFile)
1888 {
1889 	zval *self = ZEND_THIS;
1890 	zend_long index;
1891 	zend_long offset_start = 0, offset_len = 0;
1892 	zend_string *filename;
1893 	zend_long flags = 0;
1894 
1895 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Pl|lll",
1896 			&filename, &index, &offset_start, &offset_len, &flags) == FAILURE) {
1897 		RETURN_THROWS();
1898 	}
1899 
1900 	if (ZSTR_LEN(filename) == 0) {
1901 		zend_argument_value_error(1, "cannot be empty");
1902 		RETURN_THROWS();
1903 	}
1904 
1905 	if (index < 0) {
1906 		zend_argument_value_error(2, "must be greater than or equal to 0");
1907 		RETURN_THROWS();
1908 	}
1909 
1910 	if (php_zip_add_file(Z_ZIP_P(self), ZSTR_VAL(filename), ZSTR_LEN(filename),
1911 			NULL, 0, offset_start, offset_len, index, flags) < 0) {
1912 		RETURN_FALSE;
1913 	} else {
1914 		RETURN_TRUE;
1915 	}
1916 }
1917 /* }}} */
1918 
1919 /* {{{ Add a file using content and the entry name */
1920 PHP_METHOD(ZipArchive, addFromString)
1921 {
1922 	struct zip *intern;
1923 	zval *self = ZEND_THIS;
1924 	zend_string *buffer;
1925 	char *name;
1926 	size_t name_len;
1927 	ze_zip_object *ze_obj;
1928 	struct zip_source *zs;
1929 	int pos = 0;
1930 	zend_long flags = ZIP_FL_OVERWRITE;
1931 
1932 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS|l",
1933 			&name, &name_len, &buffer, &flags) == FAILURE) {
1934 		RETURN_THROWS();
1935 	}
1936 
1937 	ZIP_FROM_OBJECT(intern, self);
1938 
1939 	ze_obj = Z_ZIP_P(self);
1940 	if (ze_obj->buffers_cnt) {
1941 		ze_obj->buffers = (char **)safe_erealloc(ze_obj->buffers, sizeof(char *), (ze_obj->buffers_cnt+1), 0);
1942 		pos = ze_obj->buffers_cnt++;
1943 	} else {
1944 		ze_obj->buffers = (char **)emalloc(sizeof(char *));
1945 		ze_obj->buffers_cnt++;
1946 		pos = 0;
1947 	}
1948 	ze_obj->buffers[pos] = (char *)safe_emalloc(ZSTR_LEN(buffer), 1, 1);
1949 	memcpy(ze_obj->buffers[pos], ZSTR_VAL(buffer), ZSTR_LEN(buffer) + 1);
1950 
1951 	zs = zip_source_buffer(intern, ze_obj->buffers[pos], ZSTR_LEN(buffer), 0);
1952 
1953 	if (zs == NULL) {
1954 		RETURN_FALSE;
1955 	}
1956 
1957 	ze_obj->last_id = zip_file_add(intern, name, zs, flags);
1958 	if (ze_obj->last_id == -1) {
1959 		zip_source_free(zs);
1960 		RETURN_FALSE;
1961 	} else {
1962 		zip_error_clear(intern);
1963 		RETURN_TRUE;
1964 	}
1965 }
1966 /* }}} */
1967 
1968 /* {{{ Returns the information about a the zip entry filename */
1969 PHP_METHOD(ZipArchive, statName)
1970 {
1971 	struct zip *intern;
1972 	zval *self = ZEND_THIS;
1973 	zend_long flags = 0;
1974 	struct zip_stat sb;
1975 	zend_string *name;
1976 
1977 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &name, &flags) == FAILURE) {
1978 		RETURN_THROWS();
1979 	}
1980 
1981 	ZIP_FROM_OBJECT(intern, self);
1982 
1983 	PHP_ZIP_STAT_PATH(intern, ZSTR_VAL(name), ZSTR_LEN(name), flags, sb);
1984 
1985 	RETURN_SB(&sb);
1986 }
1987 /* }}} */
1988 
1989 /* {{{ Returns the zip entry information using its index */
1990 PHP_METHOD(ZipArchive, statIndex)
1991 {
1992 	struct zip *intern;
1993 	zval *self = ZEND_THIS;
1994 	zend_long index, flags = 0;
1995 
1996 	struct zip_stat sb;
1997 
1998 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l",
1999 			&index, &flags) == FAILURE) {
2000 		RETURN_THROWS();
2001 	}
2002 
2003 	ZIP_FROM_OBJECT(intern, self);
2004 
2005 	if (zip_stat_index(intern, index, flags, &sb) != 0) {
2006 		RETURN_FALSE;
2007 	}
2008 	RETURN_SB(&sb);
2009 }
2010 /* }}} */
2011 
2012 /* {{{ Returns the index of the entry named filename in the archive */
2013 PHP_METHOD(ZipArchive, locateName)
2014 {
2015 	struct zip *intern;
2016 	zval *self = ZEND_THIS;
2017 	zend_long flags = 0;
2018 	zend_long idx = -1;
2019 	zend_string *name;
2020 
2021 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &name, &flags) == FAILURE) {
2022 		RETURN_THROWS();
2023 	}
2024 
2025 	ZIP_FROM_OBJECT(intern, self);
2026 
2027 	if (ZSTR_LEN(name) < 1) {
2028 		RETURN_FALSE;
2029 	}
2030 
2031 	idx = (zend_long)zip_name_locate(intern, (const char *)ZSTR_VAL(name), flags);
2032 
2033 	if (idx >= 0) {
2034 		RETURN_LONG(idx);
2035 	} else {
2036 		RETURN_FALSE;
2037 	}
2038 }
2039 /* }}} */
2040 
2041 /* {{{ Returns the name of the file at position index */
2042 PHP_METHOD(ZipArchive, getNameIndex)
2043 {
2044 	struct zip *intern;
2045 	zval *self = ZEND_THIS;
2046 	const char *name;
2047 	zend_long flags = 0, index = 0;
2048 
2049 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l",
2050 			&index, &flags) == FAILURE) {
2051 		RETURN_THROWS();
2052 	}
2053 
2054 	ZIP_FROM_OBJECT(intern, self);
2055 
2056 	name = zip_get_name(intern, (int) index, flags);
2057 
2058 	if (name) {
2059 		RETVAL_STRING((char *)name);
2060 	} else {
2061 		RETURN_FALSE;
2062 	}
2063 }
2064 /* }}} */
2065 
2066 /* {{{ Set or remove (NULL/'') the comment of the archive */
2067 PHP_METHOD(ZipArchive, setArchiveComment)
2068 {
2069 	struct zip *intern;
2070 	zval *self = ZEND_THIS;
2071 	size_t comment_len;
2072 	char * comment;
2073 
2074 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &comment, &comment_len) == FAILURE) {
2075 		RETURN_THROWS();
2076 	}
2077 
2078 	ZIP_FROM_OBJECT(intern, self);
2079 
2080 	if (comment_len > 0xffff) {
2081 		zend_argument_value_error(1, "must be less than 65535 bytes");
2082 		RETURN_THROWS();
2083 	}
2084 
2085 	if (zip_set_archive_comment(intern, (const char *)comment, comment_len)) {
2086 		RETURN_FALSE;
2087 	} else {
2088 		RETURN_TRUE;
2089 	}
2090 }
2091 /* }}} */
2092 
2093 /* {{{ Returns the comment of an entry using its index */
2094 PHP_METHOD(ZipArchive, getArchiveComment)
2095 {
2096 	struct zip *intern;
2097 	zval *self = ZEND_THIS;
2098 	zend_long flags = 0;
2099 	const char * comment;
2100 	int comment_len = 0;
2101 
2102 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
2103 		RETURN_THROWS();
2104 	}
2105 
2106 	ZIP_FROM_OBJECT(intern, self);
2107 
2108 	comment = zip_get_archive_comment(intern, &comment_len, (int)flags);
2109 	if(comment==NULL) {
2110 		RETURN_FALSE;
2111 	}
2112 	RETURN_STRINGL((char *)comment, (zend_long)comment_len);
2113 }
2114 /* }}} */
2115 
2116 PHP_METHOD(ZipArchive, setArchiveFlag)
2117 {
2118 	struct zip *intern;
2119 	zval *self = ZEND_THIS;
2120 	zend_long flag, value;
2121 
2122 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &flag, &value) == FAILURE) {
2123 		RETURN_THROWS();
2124 	}
2125 
2126 	ZIP_FROM_OBJECT(intern, self);
2127 
2128 	if (zip_set_archive_flag(intern, flag, (int)value)) {
2129 		RETURN_FALSE;
2130 	} else {
2131 		RETURN_TRUE;
2132 	}
2133 }
2134 
2135 PHP_METHOD(ZipArchive, getArchiveFlag)
2136 {
2137 	struct zip *intern;
2138 	zval *self = ZEND_THIS;
2139 	zend_long flag, flags = 0;
2140 
2141 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &flag, &flags) == FAILURE) {
2142 		RETURN_THROWS();
2143 	}
2144 
2145 	ZIP_FROM_OBJECT(intern, self);
2146 
2147 	RETURN_LONG(zip_get_archive_flag(intern, flag, flags));
2148 }
2149 
2150 /* {{{ Set or remove (NULL/'') the comment of an entry using its Name */
2151 PHP_METHOD(ZipArchive, setCommentName)
2152 {
2153 	struct zip *intern;
2154 	zval *self = ZEND_THIS;
2155 	size_t comment_len, name_len;
2156 	char * comment, *name;
2157 	int idx;
2158 
2159 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
2160 			&name, &name_len, &comment, &comment_len) == FAILURE) {
2161 		RETURN_THROWS();
2162 	}
2163 
2164 	if (name_len == 0) {
2165 		zend_argument_value_error(1, "cannot be empty");
2166 		RETURN_THROWS();
2167 	}
2168 
2169 	ZIP_FROM_OBJECT(intern, self);
2170 
2171 	if (comment_len > 0xffff) {
2172 		zend_argument_value_error(2, "must be less than 65535 bytes");
2173 		RETURN_THROWS();
2174 	}
2175 
2176 	idx = zip_name_locate(intern, name, 0);
2177 	if (idx < 0) {
2178 		RETURN_FALSE;
2179 	}
2180 	PHP_ZIP_SET_FILE_COMMENT(intern, idx, comment, comment_len);
2181 }
2182 /* }}} */
2183 
2184 /* {{{ Set or remove (NULL/'') the comment of an entry using its index */
2185 PHP_METHOD(ZipArchive, setCommentIndex)
2186 {
2187 	struct zip *intern;
2188 	zval *self = ZEND_THIS;
2189 	zend_long index;
2190 	size_t comment_len;
2191 	char * comment;
2192 	struct zip_stat sb;
2193 
2194 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls",
2195 			&index, &comment, &comment_len) == FAILURE) {
2196 		RETURN_THROWS();
2197 	}
2198 
2199 	ZIP_FROM_OBJECT(intern, self);
2200 
2201 	if (comment_len > 0xffff) {
2202 		zend_argument_value_error(2, "must be less than 65535 bytes");
2203 		RETURN_THROWS();
2204 	}
2205 
2206 	PHP_ZIP_STAT_INDEX(intern, index, 0, sb);
2207 	PHP_ZIP_SET_FILE_COMMENT(intern, index, comment, comment_len);
2208 }
2209 /* }}} */
2210 
2211 /* those constants/functions are only available in libzip since 0.11.2 */
2212 #ifdef ZIP_OPSYS_DEFAULT
2213 
2214 /* {{{ Set external attributes for file in zip, using its name */
2215 PHP_METHOD(ZipArchive, setExternalAttributesName)
2216 {
2217 	struct zip *intern;
2218 	zval *self = ZEND_THIS;
2219 	size_t name_len;
2220 	char *name;
2221 	zend_long flags=0, opsys, attr;
2222 	zip_int64_t idx;
2223 
2224 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|l",
2225 			&name, &name_len, &opsys, &attr, &flags) == FAILURE) {
2226 		RETURN_THROWS();
2227 	}
2228 
2229 	ZIP_FROM_OBJECT(intern, self);
2230 
2231 	if (name_len == 0) {
2232 		zend_argument_value_error(1, "cannot be empty");
2233 		RETURN_THROWS();
2234 	}
2235 
2236 	idx = zip_name_locate(intern, name, 0);
2237 
2238 	if (idx < 0) {
2239 		RETURN_FALSE;
2240 	}
2241 	if (zip_file_set_external_attributes(intern, idx, (zip_flags_t)flags,
2242 			(zip_uint8_t)(opsys&0xff), (zip_uint32_t)attr) < 0) {
2243 		RETURN_FALSE;
2244 	}
2245 	RETURN_TRUE;
2246 }
2247 /* }}} */
2248 
2249 /* {{{ Set external attributes for file in zip, using its index */
2250 PHP_METHOD(ZipArchive, setExternalAttributesIndex)
2251 {
2252 	struct zip *intern;
2253 	zval *self = ZEND_THIS;
2254 	zend_long index, flags=0, opsys, attr;
2255 	struct zip_stat sb;
2256 
2257 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll|l",
2258 			&index, &opsys, &attr, &flags) == FAILURE) {
2259 		RETURN_THROWS();
2260 	}
2261 
2262 	ZIP_FROM_OBJECT(intern, self);
2263 
2264 	PHP_ZIP_STAT_INDEX(intern, index, 0, sb);
2265 	if (zip_file_set_external_attributes(intern, (zip_uint64_t)index,
2266 			(zip_flags_t)flags, (zip_uint8_t)(opsys&0xff), (zip_uint32_t)attr) < 0) {
2267 		RETURN_FALSE;
2268 	}
2269 	RETURN_TRUE;
2270 }
2271 /* }}} */
2272 
2273 /* {{{ Get external attributes for file in zip, using its name */
2274 PHP_METHOD(ZipArchive, getExternalAttributesName)
2275 {
2276 	struct zip *intern;
2277 	zval *self = ZEND_THIS, *z_opsys, *z_attr;
2278 	size_t name_len;
2279 	char *name;
2280 	zend_long flags=0;
2281 	zip_uint8_t opsys;
2282 	zip_uint32_t attr;
2283 	zip_int64_t idx;
2284 
2285 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l",
2286 			&name, &name_len, &z_opsys, &z_attr, &flags) == FAILURE) {
2287 		RETURN_THROWS();
2288 	}
2289 
2290 	ZIP_FROM_OBJECT(intern, self);
2291 
2292 	if (name_len == 0) {
2293 		zend_argument_value_error(1, "cannot be empty");
2294 		RETURN_THROWS();
2295 	}
2296 
2297 	idx = zip_name_locate(intern, name, 0);
2298 
2299 	if (idx < 0) {
2300 		RETURN_FALSE;
2301 	}
2302 	if (zip_file_get_external_attributes(intern, idx,
2303 			(zip_flags_t)flags, &opsys, &attr) < 0) {
2304 		RETURN_FALSE;
2305 	}
2306 	ZEND_TRY_ASSIGN_REF_LONG(z_opsys, opsys);
2307 	ZEND_TRY_ASSIGN_REF_LONG(z_attr, attr);
2308 	RETURN_TRUE;
2309 }
2310 /* }}} */
2311 
2312 /* {{{ Get external attributes for file in zip, using its index */
2313 PHP_METHOD(ZipArchive, getExternalAttributesIndex)
2314 {
2315 	struct zip *intern;
2316 	zval *self = ZEND_THIS, *z_opsys, *z_attr;
2317 	zend_long index, flags=0;
2318 	zip_uint8_t opsys;
2319 	zip_uint32_t attr;
2320 	struct zip_stat sb;
2321 
2322 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lzz|l",
2323 			&index, &z_opsys, &z_attr, &flags) == FAILURE) {
2324 		RETURN_THROWS();
2325 	}
2326 
2327 	ZIP_FROM_OBJECT(intern, self);
2328 
2329 	PHP_ZIP_STAT_INDEX(intern, index, 0, sb);
2330 	if (zip_file_get_external_attributes(intern, (zip_uint64_t)index,
2331 			(zip_flags_t)flags, &opsys, &attr) < 0) {
2332 		RETURN_FALSE;
2333 	}
2334 	ZEND_TRY_ASSIGN_REF_LONG(z_opsys, opsys);
2335 	ZEND_TRY_ASSIGN_REF_LONG(z_attr, attr);
2336 	RETURN_TRUE;
2337 }
2338 /* }}} */
2339 #endif /* ifdef ZIP_OPSYS_DEFAULT */
2340 
2341 #ifdef HAVE_ENCRYPTION
2342 /* {{{ Set encryption method for file in zip, using its name */
2343 PHP_METHOD(ZipArchive, setEncryptionName)
2344 {
2345 	struct zip *intern;
2346 	zval *self = ZEND_THIS;
2347 	zend_long method;
2348 	zip_int64_t idx;
2349 	char *name, *password = NULL;
2350 	size_t name_len, password_len;
2351 
2352 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|s!",
2353 			&name, &name_len, &method, &password, &password_len) == FAILURE) {
2354 		RETURN_THROWS();
2355 	}
2356 
2357 	ZIP_FROM_OBJECT(intern, self);
2358 
2359 	if (name_len == 0) {
2360 		zend_argument_value_error(1, "cannot be empty");
2361 		RETURN_THROWS();
2362 	}
2363 
2364 	idx = zip_name_locate(intern, name, 0);
2365 
2366 	if (idx < 0) {
2367 		RETURN_FALSE;
2368 	}
2369 
2370 	if (zip_file_set_encryption(intern, idx, (zip_uint16_t)method, password)) {
2371 		RETURN_FALSE;
2372 	}
2373 	RETURN_TRUE;
2374 }
2375 /* }}} */
2376 
2377 /* {{{ Set encryption method for file in zip, using its index */
2378 PHP_METHOD(ZipArchive, setEncryptionIndex)
2379 {
2380 	struct zip *intern;
2381 	zval *self = ZEND_THIS;
2382 	zend_long index, method;
2383 	char *password = NULL;
2384 	size_t password_len;
2385 
2386 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|s!",
2387 			&index, &method, &password, &password_len) == FAILURE) {
2388 		RETURN_THROWS();
2389 	}
2390 
2391 	ZIP_FROM_OBJECT(intern, self);
2392 
2393 	if (zip_file_set_encryption(intern, index, (zip_uint16_t)method, password)) {
2394 		RETURN_FALSE;
2395 	}
2396 	RETURN_TRUE;
2397 }
2398 /* }}} */
2399 #endif
2400 
2401 /* {{{ Returns the comment of an entry using its name */
2402 PHP_METHOD(ZipArchive, getCommentName)
2403 {
2404 	struct zip *intern;
2405 	zval *self = ZEND_THIS;
2406 	size_t name_len;
2407 	int idx;
2408 	zend_long flags = 0;
2409 	zip_uint32_t comment_len = 0;
2410 	const char * comment;
2411 	char *name;
2412 
2413 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l",
2414 			&name, &name_len, &flags) == FAILURE) {
2415 		RETURN_THROWS();
2416 	}
2417 
2418 	ZIP_FROM_OBJECT(intern, self);
2419 
2420 	if (name_len == 0) {
2421 		zend_argument_value_error(1, "cannot be empty");
2422 		RETURN_THROWS();
2423 	}
2424 
2425 	idx = zip_name_locate(intern, name, 0);
2426 
2427 	if (idx < 0) {
2428 		RETURN_FALSE;
2429 	}
2430 
2431 	comment = zip_file_get_comment(intern, idx, &comment_len, (zip_flags_t)flags);
2432 	RETURN_STRINGL((char *)comment, comment_len);
2433 }
2434 /* }}} */
2435 
2436 /* {{{ Returns the comment of an entry using its index */
2437 PHP_METHOD(ZipArchive, getCommentIndex)
2438 {
2439 	struct zip *intern;
2440 	zval *self = ZEND_THIS;
2441 	zend_long index, flags = 0;
2442 	const char * comment;
2443 	zip_uint32_t comment_len = 0;
2444 	struct zip_stat sb;
2445 
2446 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l",
2447 				&index, &flags) == FAILURE) {
2448 		RETURN_THROWS();
2449 	}
2450 
2451 	ZIP_FROM_OBJECT(intern, self);
2452 
2453 	PHP_ZIP_STAT_INDEX(intern, index, 0, sb);
2454 	comment = zip_file_get_comment(intern, index, &comment_len, (zip_flags_t)flags);
2455 	RETURN_STRINGL((char *)comment, comment_len);
2456 }
2457 /* }}} */
2458 
2459 /* {{{ Set the compression of a file in zip, using its name */
2460 PHP_METHOD(ZipArchive, setCompressionName)
2461 {
2462 	struct zip *intern;
2463 	zval *this = ZEND_THIS;
2464 	size_t name_len;
2465 	char *name;
2466 	zip_int64_t idx;
2467 	zend_long comp_method, comp_flags = 0;
2468 
2469 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|l",
2470 			&name, &name_len, &comp_method, &comp_flags) == FAILURE) {
2471 		RETURN_THROWS();
2472 	}
2473 
2474 	ZIP_FROM_OBJECT(intern, this);
2475 
2476 	if (name_len == 0) {
2477 		zend_argument_value_error(1, "cannot be empty");
2478 		RETURN_THROWS();
2479 	}
2480 
2481 	idx = zip_name_locate(intern, name, 0);
2482 
2483 	if (idx < 0) {
2484 		RETURN_FALSE;
2485 	}
2486 
2487 	if (zip_set_file_compression(intern, (zip_uint64_t)idx,
2488 			(zip_int32_t)comp_method, (zip_uint32_t)comp_flags) != 0) {
2489 		RETURN_FALSE;
2490 	}
2491 	RETURN_TRUE;
2492 }
2493 /* }}} */
2494 
2495 /* {{{ Set the compression of a file in zip, using its index */
2496 PHP_METHOD(ZipArchive, setCompressionIndex)
2497 {
2498 	struct zip *intern;
2499 	zval *this = ZEND_THIS;
2500 	zend_long index;
2501 	zend_long comp_method, comp_flags = 0;
2502 
2503 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|l",
2504 			&index, &comp_method, &comp_flags) == FAILURE) {
2505 		RETURN_THROWS();
2506 	}
2507 
2508 	ZIP_FROM_OBJECT(intern, this);
2509 
2510 	if (zip_set_file_compression(intern, (zip_uint64_t)index,
2511 			(zip_int32_t)comp_method, (zip_uint32_t)comp_flags) != 0) {
2512 		RETURN_FALSE;
2513 	}
2514 	RETURN_TRUE;
2515 }
2516 /* }}} */
2517 
2518 #ifdef HAVE_SET_MTIME
2519 /* {{{ Set the modification time of a file in zip, using its name */
2520 PHP_METHOD(ZipArchive, setMtimeName)
2521 {
2522 	struct zip *intern;
2523 	zval *this = ZEND_THIS;
2524 	size_t name_len;
2525 	char *name;
2526 	zip_int64_t idx;
2527 	zend_long mtime, flags = 0;
2528 
2529 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|l",
2530 			&name, &name_len, &mtime,  &flags) == FAILURE) {
2531 		RETURN_THROWS();
2532 	}
2533 
2534 	ZIP_FROM_OBJECT(intern, this);
2535 
2536 	if (name_len == 0) {
2537 		zend_argument_value_error(1, "cannot be empty");
2538 		RETURN_THROWS();
2539 	}
2540 
2541 	idx = zip_name_locate(intern, name, 0);
2542 
2543 	if (idx < 0) {
2544 		RETURN_FALSE;
2545 	}
2546 
2547 	if (zip_file_set_mtime(intern, (zip_uint64_t)idx,
2548 			(time_t)mtime, (zip_uint32_t)flags) != 0) {
2549 		RETURN_FALSE;
2550 	}
2551 	RETURN_TRUE;
2552 }
2553 /* }}} */
2554 
2555 /* {{{ Set the modification time of a file in zip, using its index */
2556 PHP_METHOD(ZipArchive, setMtimeIndex)
2557 {
2558 	struct zip *intern;
2559 	zval *this = ZEND_THIS;
2560 	zend_long index;
2561 	zend_long mtime, flags = 0;
2562 
2563 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|l",
2564 			&index, &mtime, &flags) == FAILURE) {
2565 		RETURN_THROWS();
2566 	}
2567 
2568 	ZIP_FROM_OBJECT(intern, this);
2569 
2570 	if (zip_file_set_mtime(intern, (zip_uint64_t)index,
2571 			(time_t)mtime, (zip_uint32_t)flags) != 0) {
2572 		RETURN_FALSE;
2573 	}
2574 	RETURN_TRUE;
2575 }
2576 /* }}} */
2577 #endif
2578 
2579 /* {{{ Delete a file using its index */
2580 PHP_METHOD(ZipArchive, deleteIndex)
2581 {
2582 	struct zip *intern;
2583 	zval *self = ZEND_THIS;
2584 	zend_long index;
2585 
2586 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
2587 		RETURN_THROWS();
2588 	}
2589 
2590 	ZIP_FROM_OBJECT(intern, self);
2591 
2592 	if (index < 0) {
2593 		RETURN_FALSE;
2594 	}
2595 
2596 	if (zip_delete(intern, index) < 0) {
2597 		RETURN_FALSE;
2598 	}
2599 
2600 	RETURN_TRUE;
2601 }
2602 /* }}} */
2603 
2604 /* {{{ Delete a file using its index */
2605 PHP_METHOD(ZipArchive, deleteName)
2606 {
2607 	struct zip *intern;
2608 	zval *self = ZEND_THIS;
2609 	size_t name_len;
2610 	char *name;
2611 	struct zip_stat sb;
2612 
2613 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
2614 		RETURN_THROWS();
2615 	}
2616 
2617 	ZIP_FROM_OBJECT(intern, self);
2618 
2619 	if (name_len < 1) {
2620 		RETURN_FALSE;
2621 	}
2622 
2623 	PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb);
2624 	if (zip_delete(intern, sb.index)) {
2625 		RETURN_FALSE;
2626 	}
2627 	RETURN_TRUE;
2628 }
2629 /* }}} */
2630 
2631 /* {{{ Rename an entry selected by its index to new_name */
2632 PHP_METHOD(ZipArchive, renameIndex)
2633 {
2634 	struct zip *intern;
2635 	zval *self = ZEND_THIS;
2636 	char *new_name;
2637 	size_t new_name_len;
2638 	zend_long index;
2639 
2640 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &index, &new_name, &new_name_len) == FAILURE) {
2641 		RETURN_THROWS();
2642 	}
2643 
2644 	if (index < 0) {
2645 		RETURN_FALSE;
2646 	}
2647 
2648 	ZIP_FROM_OBJECT(intern, self);
2649 
2650 	if (new_name_len == 0) {
2651 		zend_argument_value_error(2, "cannot be empty");
2652 		RETURN_THROWS();
2653 	}
2654 
2655 	if (zip_file_rename(intern, index, (const char *)new_name, 0) != 0) {
2656 		RETURN_FALSE;
2657 	}
2658 
2659 	RETURN_TRUE;
2660 }
2661 /* }}} */
2662 
2663 /* {{{ Rename an entry selected by its name to new_name */
2664 PHP_METHOD(ZipArchive, renameName)
2665 {
2666 	struct zip *intern;
2667 	zval *self = ZEND_THIS;
2668 	struct zip_stat sb;
2669 	char *name, *new_name;
2670 	size_t name_len, new_name_len;
2671 
2672 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &new_name, &new_name_len) == FAILURE) {
2673 		RETURN_THROWS();
2674 	}
2675 
2676 	ZIP_FROM_OBJECT(intern, self);
2677 
2678 	if (new_name_len == 0) {
2679 		zend_argument_value_error(2, "cannot be empty");
2680 		RETURN_THROWS();
2681 	}
2682 
2683 	PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb);
2684 
2685 	if (zip_file_rename(intern, sb.index, (const char *)new_name, 0)) {
2686 		RETURN_FALSE;
2687 	}
2688 
2689 	RETURN_TRUE;
2690 }
2691 /* }}} */
2692 
2693 /* {{{ Changes to the file at position index are reverted */
2694 PHP_METHOD(ZipArchive, unchangeIndex)
2695 {
2696 	struct zip *intern;
2697 	zval *self = ZEND_THIS;
2698 	zend_long index;
2699 
2700 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
2701 		RETURN_THROWS();
2702 	}
2703 
2704 	ZIP_FROM_OBJECT(intern, self);
2705 
2706 	if (index < 0) {
2707 		RETURN_FALSE;
2708 	}
2709 
2710 	if (zip_unchange(intern, index) != 0) {
2711 		RETURN_FALSE;
2712 	} else {
2713 		RETURN_TRUE;
2714 	}
2715 }
2716 /* }}} */
2717 
2718 /* {{{ Changes to the file named 'name' are reverted */
2719 PHP_METHOD(ZipArchive, unchangeName)
2720 {
2721 	struct zip *intern;
2722 	zval *self = ZEND_THIS;
2723 	struct zip_stat sb;
2724 	char *name;
2725 	size_t name_len;
2726 
2727 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
2728 		RETURN_THROWS();
2729 	}
2730 
2731 	ZIP_FROM_OBJECT(intern, self);
2732 
2733 	if (name_len < 1) {
2734 		RETURN_FALSE;
2735 	}
2736 
2737 	PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb);
2738 
2739 	if (zip_unchange(intern, sb.index) != 0) {
2740 		RETURN_FALSE;
2741 	} else {
2742 		RETURN_TRUE;
2743 	}
2744 }
2745 /* }}} */
2746 
2747 /* {{{ All changes to files and global information in archive are reverted */
2748 PHP_METHOD(ZipArchive, unchangeAll)
2749 {
2750 	struct zip *intern;
2751 	zval *self = ZEND_THIS;
2752 
2753 	if (zend_parse_parameters_none() == FAILURE) {
2754 		RETURN_THROWS();
2755 	}
2756 
2757 	ZIP_FROM_OBJECT(intern, self);
2758 
2759 	if (zip_unchange_all(intern) != 0) {
2760 		RETURN_FALSE;
2761 	} else {
2762 		RETURN_TRUE;
2763 	}
2764 }
2765 /* }}} */
2766 
2767 /* {{{ Revert all global changes to the archive archive.  For now, this only reverts archive comment changes. */
2768 PHP_METHOD(ZipArchive, unchangeArchive)
2769 {
2770 	struct zip *intern;
2771 	zval *self = ZEND_THIS;
2772 
2773 	if (zend_parse_parameters_none() == FAILURE) {
2774 		RETURN_THROWS();
2775 	}
2776 
2777 	ZIP_FROM_OBJECT(intern, self);
2778 
2779 	if (zip_unchange_archive(intern) != 0) {
2780 		RETURN_FALSE;
2781 	} else {
2782 		RETURN_TRUE;
2783 	}
2784 }
2785 /* }}} */
2786 
2787 /* {{{ Extract one or more file from a zip archive */
2788 /* TODO:
2789  * - allow index or array of indices
2790  * - replace path
2791  * - patterns
2792  */
2793 PHP_METHOD(ZipArchive, extractTo)
2794 {
2795 	struct zip *intern;
2796 
2797 	zval *self = ZEND_THIS;
2798 	zend_string *files_str = NULL;
2799 	HashTable *files_ht = NULL;
2800 
2801 	php_stream_statbuf ssb;
2802 	char *pathto;
2803 	size_t pathto_len;
2804 	int ret;
2805 
2806 	ZEND_PARSE_PARAMETERS_START(1, 2)
2807 		Z_PARAM_PATH(pathto, pathto_len)
2808 		Z_PARAM_OPTIONAL
2809 		Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(files_ht, files_str)
2810 	ZEND_PARSE_PARAMETERS_END();
2811 
2812 	ZIP_FROM_OBJECT(intern, self);
2813 
2814 	if (pathto_len < 1) {
2815 		RETURN_FALSE;
2816 	}
2817 
2818 	if (php_stream_stat_path_ex(pathto, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL) < 0) {
2819 		ret = php_stream_mkdir(pathto, 0777, PHP_STREAM_MKDIR_RECURSIVE, NULL);
2820 		if (!ret) {
2821 			RETURN_FALSE;
2822 		}
2823 	}
2824 
2825 	uint32_t nelems, i;
2826 
2827 	if (files_str) {
2828 		if (!php_zip_extract_file(intern, pathto, ZSTR_VAL(files_str), ZSTR_LEN(files_str), -1)) {
2829 			RETURN_FALSE;
2830 		}
2831 	} else if (files_ht) {
2832 		nelems = zend_hash_num_elements(files_ht);
2833 		if (nelems == 0 ) {
2834 			RETURN_FALSE;
2835 		}
2836 		for (i = 0; i < nelems; i++) {
2837 			zval *zval_file;
2838 			if ((zval_file = zend_hash_index_find_deref(files_ht, i)) != NULL) {
2839 				switch (Z_TYPE_P(zval_file)) {
2840 					case IS_LONG:
2841 						break;
2842 					case IS_STRING:
2843 						if (!php_zip_extract_file(intern, pathto, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), -1)) {
2844 							RETURN_FALSE;
2845 						}
2846 						break;
2847 				}
2848 			}
2849 		}
2850 	} else {
2851 		/* Extract all files */
2852 		zip_int64_t i, filecount = zip_get_num_entries(intern, 0);
2853 
2854 		if (filecount == -1) {
2855 				php_error_docref(NULL, E_WARNING, "Illegal archive");
2856 				RETURN_FALSE;
2857 		}
2858 
2859 		for (i = 0; i < filecount; i++) {
2860 			const char *file = zip_get_name(intern, i, ZIP_FL_UNCHANGED);
2861 			if (!file || !php_zip_extract_file(intern, pathto, file, strlen(file), i)) {
2862 					RETURN_FALSE;
2863 			}
2864 		}
2865 	}
2866 
2867 	RETURN_TRUE;
2868 }
2869 /* }}} */
2870 
2871 static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
2872 {
2873 	struct zip *intern;
2874 	zval *self = ZEND_THIS;
2875 
2876 	struct zip_stat sb;
2877 	struct zip_file *zf;
2878 
2879 	zend_long index = -1;
2880 	zend_long flags = 0;
2881 	zend_long len = 0;
2882 
2883 	zend_string *filename;
2884 	zend_string *buffer;
2885 
2886 	int n = 0;
2887 
2888 	if (type == 1) {
2889 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|ll", &filename, &len, &flags) == FAILURE) {
2890 			RETURN_THROWS();
2891 		}
2892 
2893 		ZIP_FROM_OBJECT(intern, self);
2894 
2895 		PHP_ZIP_STAT_PATH(intern, ZSTR_VAL(filename), ZSTR_LEN(filename), flags, sb);
2896 	} else {
2897 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ll", &index, &len, &flags) == FAILURE) {
2898 			RETURN_THROWS();
2899 		}
2900 
2901 		ZIP_FROM_OBJECT(intern, self);
2902 
2903 		PHP_ZIP_STAT_INDEX(intern, index, 0, sb);
2904 	}
2905 
2906 	if (sb.size < 1) {
2907 		RETURN_EMPTY_STRING();
2908 	}
2909 
2910 	if (len < 1) {
2911 		len = sb.size;
2912 	}
2913 	if (index >= 0) {
2914 		zf = zip_fopen_index(intern, index, flags);
2915 	} else {
2916 		zf = zip_fopen(intern, ZSTR_VAL(filename), flags);
2917 	}
2918 
2919 	if (zf == NULL) {
2920 		RETURN_FALSE;
2921 	}
2922 
2923 	buffer = zend_string_safe_alloc(1, len, 0, 0);
2924 	n = zip_fread(zf, ZSTR_VAL(buffer), ZSTR_LEN(buffer));
2925 	if (n < 1) {
2926 		zend_string_efree(buffer);
2927 		RETURN_EMPTY_STRING();
2928 	}
2929 
2930 	zip_fclose(zf);
2931 	ZSTR_VAL(buffer)[n] = '\0';
2932 	ZSTR_LEN(buffer) = n;
2933 	RETURN_NEW_STR(buffer);
2934 }
2935 /* }}} */
2936 
2937 /* {{{ get the contents of an entry using its name */
2938 PHP_METHOD(ZipArchive, getFromName)
2939 {
2940 	php_zip_get_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2941 }
2942 /* }}} */
2943 
2944 /* {{{ get the contents of an entry using its index */
2945 PHP_METHOD(ZipArchive, getFromIndex)
2946 {
2947 	php_zip_get_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2948 }
2949 /* }}} */
2950 
2951 static void php_zip_get_stream(INTERNAL_FUNCTION_PARAMETERS, int type, bool accept_flags) /* {{{ */
2952 {
2953 	struct zip *intern;
2954 	zval *self = ZEND_THIS;
2955 	zend_long index;
2956 	zend_long flags = 0;
2957 	struct zip_stat sb;
2958 	char *mode = "rb";
2959 	zend_string *filename;
2960 	php_stream *stream;
2961 
2962 	if (type) {
2963 		if (accept_flags) {
2964 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &filename, &flags) == FAILURE) {
2965 				RETURN_THROWS();
2966 			}
2967 		} else {
2968 			if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &filename) == FAILURE) {
2969 				RETURN_THROWS();
2970 			}
2971 		}
2972 	} else {
2973 		ZEND_ASSERT(accept_flags);
2974 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &index, &flags) == FAILURE) {
2975 			RETURN_THROWS();
2976 		}
2977 	}
2978 
2979 	ZIP_FROM_OBJECT(intern, self);
2980 
2981 	if (type) {
2982 		PHP_ZIP_STAT_PATH(intern, ZSTR_VAL(filename), ZSTR_LEN(filename), flags, sb);
2983 	} else {
2984 		PHP_ZIP_STAT_INDEX(intern, index, flags, sb);
2985 	}
2986 
2987 	stream = php_stream_zip_open(intern, &sb, mode, flags STREAMS_CC);
2988 	if (stream) {
2989 		php_stream_to_zval(stream, return_value);
2990 	} else {
2991 		RETURN_FALSE;
2992 	}
2993 }
2994 
2995 /* {{{ get a stream for an entry using its name */
2996 PHP_METHOD(ZipArchive, getStreamName)
2997 {
2998 	php_zip_get_stream(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, /* accept_flags */ true);
2999 }
3000 /* }}} */
3001 
3002 /* {{{ get a stream for an entry using its index */
3003 PHP_METHOD(ZipArchive, getStreamIndex)
3004 {
3005 	php_zip_get_stream(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, /* accept_flags */ true);
3006 }
3007 /* }}} */
3008 
3009 PHP_METHOD(ZipArchive, getStream)
3010 {
3011 	php_zip_get_stream(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, /* accept_flags */ false);
3012 }
3013 
3014 #ifdef HAVE_PROGRESS_CALLBACK
3015 static void _php_zip_progress_callback(zip_t *arch, double state, void *ptr)
3016 {
3017 	zval cb_args[1];
3018 	zval cb_retval;
3019 	ze_zip_object *obj = ptr;
3020 
3021 	ZVAL_DOUBLE(&cb_args[0], state);
3022 	if (call_user_function(EG(function_table), NULL, &obj->progress_callback, &cb_retval, 1, cb_args) == SUCCESS && !Z_ISUNDEF(cb_retval)) {
3023 		zval_ptr_dtor(&cb_retval);
3024 	}
3025 }
3026 
3027 /* {{{ register a progression callback: void callback(double state); */
3028 PHP_METHOD(ZipArchive, registerProgressCallback)
3029 {
3030 	struct zip *intern;
3031 	zval *self = ZEND_THIS;
3032 	double rate;
3033 	zend_fcall_info fci;
3034 	zend_fcall_info_cache fcc;
3035 	ze_zip_object *obj;
3036 
3037 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "df", &rate, &fci, &fcc) == FAILURE) {
3038 		RETURN_THROWS();
3039 	}
3040 
3041 	ZIP_FROM_OBJECT(intern, self);
3042 
3043 	obj = Z_ZIP_P(self);
3044 
3045 	/* free if called twice */
3046 	_php_zip_progress_callback_free(obj);
3047 
3048 	/* register */
3049 	ZVAL_COPY(&obj->progress_callback, &fci.function_name);
3050 	if (zip_register_progress_callback_with_state(intern, rate, _php_zip_progress_callback, _php_zip_progress_callback_free, obj)) {
3051 		RETURN_FALSE;
3052 	}
3053 
3054 	RETURN_TRUE;
3055 }
3056 /* }}} */
3057 #endif
3058 
3059 #ifdef HAVE_CANCEL_CALLBACK
3060 static int _php_zip_cancel_callback(zip_t *arch, void *ptr)
3061 {
3062 	zval cb_retval;
3063 	int retval = 0;
3064 	ze_zip_object *obj = ptr;
3065 
3066 	if (call_user_function(EG(function_table), NULL, &obj->cancel_callback, &cb_retval, 0, NULL) == SUCCESS && !Z_ISUNDEF(cb_retval)) {
3067 		retval = zval_get_long(&cb_retval);
3068 		zval_ptr_dtor(&cb_retval);
3069 	}
3070 
3071 	return retval;
3072 }
3073 
3074 /* {{{ register a progression callback: int callback(double state); */
3075 PHP_METHOD(ZipArchive, registerCancelCallback)
3076 {
3077 	struct zip *intern;
3078 	zval *self = ZEND_THIS;
3079 	zend_fcall_info fci;
3080 	zend_fcall_info_cache fcc;
3081 	ze_zip_object *obj;
3082 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "f", &fci, &fcc) == FAILURE) {
3083 		RETURN_THROWS();
3084 	}
3085 
3086 	ZIP_FROM_OBJECT(intern, self);
3087 
3088 	obj = Z_ZIP_P(self);
3089 
3090 	/* free if called twice */
3091 	_php_zip_cancel_callback_free(obj);
3092 
3093 	/* register */
3094 	ZVAL_COPY(&obj->cancel_callback, &fci.function_name);
3095 	if (zip_register_cancel_callback_with_state(intern, _php_zip_cancel_callback, _php_zip_cancel_callback_free, obj)) {
3096 		RETURN_FALSE;
3097 	}
3098 
3099 	RETURN_TRUE;
3100 }
3101 /* }}} */
3102 #endif
3103 
3104 #ifdef HAVE_METHOD_SUPPORTED
3105 /* {{{ check if a compression method is available in used libzip */
3106 PHP_METHOD(ZipArchive, isCompressionMethodSupported)
3107 {
3108 	zend_long method;
3109 	bool enc = 1;
3110 
3111 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &method, &enc) == FAILURE) {
3112 		return;
3113 	}
3114 	RETVAL_BOOL(zip_compression_method_supported((zip_int32_t)method, enc));
3115 }
3116 /* }}} */
3117 
3118 /* {{{ check if a encryption method is available in used libzip */
3119 PHP_METHOD(ZipArchive, isEncryptionMethodSupported)
3120 {
3121 	zend_long method;
3122 	bool enc = 1;
3123 
3124 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &method, &enc) == FAILURE) {
3125 		return;
3126 	}
3127 	RETVAL_BOOL(zip_encryption_method_supported((zip_uint16_t)method, enc));
3128 }
3129 /* }}} */
3130 #endif
3131 
3132 static void php_zip_free_prop_handler(zval *el) /* {{{ */ {
3133 	pefree(Z_PTR_P(el), 1);
3134 } /* }}} */
3135 
3136 /* {{{ PHP_MINIT_FUNCTION */
3137 static PHP_MINIT_FUNCTION(zip)
3138 {
3139 	memcpy(&zip_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
3140 	zip_object_handlers.offset = XtOffsetOf(ze_zip_object, zo);
3141 	zip_object_handlers.free_obj = php_zip_object_free_storage;
3142 	zip_object_handlers.clone_obj = NULL;
3143 	zip_object_handlers.get_property_ptr_ptr = php_zip_get_property_ptr_ptr;
3144 
3145 	zip_object_handlers.get_gc          = php_zip_get_gc;
3146 	zip_object_handlers.get_properties = php_zip_get_properties;
3147 	zip_object_handlers.read_property	= php_zip_read_property;
3148 	zip_object_handlers.has_property	= php_zip_has_property;
3149 	zip_object_handlers.write_property = php_zip_write_property;
3150 
3151 	zip_class_entry = register_class_ZipArchive(zend_ce_countable);
3152 	zip_class_entry->create_object = php_zip_object_new;
3153 	zip_class_entry->default_object_handlers = &zip_object_handlers;
3154 
3155 	zend_hash_init(&zip_prop_handlers, 0, NULL, php_zip_free_prop_handler, 1);
3156 	php_zip_register_prop_handler(&zip_prop_handlers, "lastId",    php_zip_last_id, NULL, IS_LONG);
3157 	php_zip_register_prop_handler(&zip_prop_handlers, "status",    php_zip_status, NULL, IS_LONG);
3158 	php_zip_register_prop_handler(&zip_prop_handlers, "statusSys", php_zip_status_sys, NULL, IS_LONG);
3159 	php_zip_register_prop_handler(&zip_prop_handlers, "numFiles",  php_zip_get_num_files, NULL, IS_LONG);
3160 	php_zip_register_prop_handler(&zip_prop_handlers, "filename",  NULL, php_zipobj_get_filename, IS_STRING);
3161 	php_zip_register_prop_handler(&zip_prop_handlers, "comment",   NULL, php_zipobj_get_zip_comment, IS_STRING);
3162 
3163 	php_register_url_stream_wrapper("zip", &php_stream_zip_wrapper);
3164 
3165 	le_zip_dir   = zend_register_list_destructors_ex(php_zip_free_dir,   NULL, le_zip_dir_name,   module_number);
3166 	le_zip_entry = zend_register_list_destructors_ex(php_zip_free_entry, NULL, le_zip_entry_name, module_number);
3167 
3168 	return SUCCESS;
3169 }
3170 /* }}} */
3171 
3172 /* {{{ PHP_MSHUTDOWN_FUNCTION */
3173 static PHP_MSHUTDOWN_FUNCTION(zip)
3174 {
3175 	zend_hash_destroy(&zip_prop_handlers);
3176 	php_unregister_url_stream_wrapper("zip");
3177 	return SUCCESS;
3178 }
3179 /* }}} */
3180 
3181 /* {{{ PHP_MINFO_FUNCTION */
3182 static PHP_MINFO_FUNCTION(zip)
3183 {
3184 	php_info_print_table_start();
3185 
3186 	php_info_print_table_row(2, "Zip", "enabled");
3187 	php_info_print_table_row(2, "Zip version", PHP_ZIP_VERSION);
3188 #ifdef HAVE_LIBZIP_VERSION
3189 	if (strcmp(LIBZIP_VERSION, zip_libzip_version())) {
3190 		php_info_print_table_row(2, "Libzip headers version", LIBZIP_VERSION);
3191 		php_info_print_table_row(2, "Libzip library version", zip_libzip_version());
3192 	} else
3193 #endif
3194 	{
3195 		php_info_print_table_row(2, "Libzip version", LIBZIP_VERSION);
3196 	}
3197 #ifdef HAVE_METHOD_SUPPORTED
3198 	php_info_print_table_row(2, "BZIP2 compression",
3199 		zip_compression_method_supported(ZIP_CM_BZIP2, 1) ? "Yes" : "No");
3200 	php_info_print_table_row(2, "XZ compression",
3201 		zip_compression_method_supported(ZIP_CM_XZ, 1) ? "Yes" : "No");
3202 #ifdef ZIP_CM_ZSTD
3203 	php_info_print_table_row(2, "ZSTD compression",
3204 		zip_compression_method_supported(ZIP_CM_ZSTD, 1) ? "Yes" : "No");
3205 #else
3206 	php_info_print_table_row(2, "ZSTD compression", "No");
3207 #endif
3208 	php_info_print_table_row(2, "AES-128 encryption",
3209 		zip_encryption_method_supported(ZIP_EM_AES_128, 1) ? "Yes" : "No");
3210 	php_info_print_table_row(2, "AES-192 encryption",
3211 		zip_encryption_method_supported(ZIP_EM_AES_128, 1) ? "Yes" : "No");
3212 	php_info_print_table_row(2, "AES-256 encryption",
3213 		zip_encryption_method_supported(ZIP_EM_AES_128, 1) ? "Yes" : "No");
3214 #endif
3215 
3216 	php_info_print_table_end();
3217 }
3218 /* }}} */
3219