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