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