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