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