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