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