diff --git a/ext/zip/config.m4 b/ext/zip/config.m4 index 29ae030d37b66..590be44f6501d 100644 --- a/ext/zip/config.m4 +++ b/ext/zip/config.m4 @@ -49,7 +49,7 @@ if test "$PHP_ZIP" != "no"; then AC_DEFINE([HAVE_ZIP], [1], [Define to 1 if the PHP extension 'zip' is available.]) - PHP_NEW_EXTENSION([zip], [php_zip.c zip_stream.c], [$ext_shared]) + PHP_NEW_EXTENSION([zip], [php_zip.c zip_source.c zip_stream.c], [$ext_shared]) PHP_ADD_EXTENSION_DEP(zip, pcre) PHP_SUBST([ZIP_SHARED_LIBADD]) diff --git a/ext/zip/config.w32 b/ext/zip/config.w32 index 3f05d8454c1ee..8dd22c7761cfb 100644 --- a/ext/zip/config.w32 +++ b/ext/zip/config.w32 @@ -8,7 +8,7 @@ if (PHP_ZIP != "no") { (PHP_ZIP_SHARED && CHECK_LIB("libzip.lib", "zip", PHP_ZIP) || CHECK_LIB("libzip_a.lib", "zip", PHP_ZIP) && CHECK_LIB("libbz2_a.lib", "zip", PHP_ZIP) && CHECK_LIB("zlib_a.lib", "zip", PHP_ZIP) && CHECK_LIB("liblzma_a.lib", "zip", PHP_ZIP)) ) { - EXTENSION('zip', 'php_zip.c zip_stream.c'); + EXTENSION('zip', 'php_zip.c zip_source.c zip_stream.c'); ADD_EXTENSION_DEP('zip', 'pcre'); if (get_define("LIBS_ZIP").match("libzip_a(?:_debug)?\.lib")) { diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 77d5dacbff94e..7f77624206d1e 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -575,30 +575,8 @@ static char * php_zipobj_get_zip_comment(ze_zip_object *obj, int *len) /* {{{ */ } /* }}} */ -/* Add a string to the list of buffers to be released when the object is destroyed.*/ -static void php_zipobj_add_buffer(ze_zip_object *obj, zend_string *str) /* {{{ */ -{ - size_t pos = obj->buffers_cnt++; - obj->buffers = safe_erealloc(obj->buffers, sizeof(*obj->buffers), obj->buffers_cnt, 0); - obj->buffers[pos] = zend_string_copy(str); -} -/* }}} */ - -static void php_zipobj_release_buffers(ze_zip_object *obj) /* {{{ */ -{ - if (obj->buffers_cnt > 0) { - for (size_t i = 0; i < obj->buffers_cnt; i++) { - zend_string_release(obj->buffers[i]); - } - efree(obj->buffers); - obj->buffers = NULL; - } - obj->buffers_cnt = 0; -} -/* }}} */ - /* Close and free the zip_t */ -static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */ +static bool php_zipobj_close(ze_zip_object *obj, zend_string **out_str) /* {{{ */ { struct zip *intern = obj->za; bool success = false; @@ -630,9 +608,17 @@ static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */ obj->filename_len = 0; } - php_zipobj_release_buffers(obj); + if (obj->out_str) { + if (out_str) { + *out_str = obj->out_str; + } else { + zend_string_release(obj->out_str); + } + obj->out_str = NULL; + } obj->za = NULL; + obj->from_string = false; return success; } /* }}} */ @@ -1084,7 +1070,7 @@ static void php_zip_object_free_storage(zend_object *object) /* {{{ */ { ze_zip_object * intern = php_zip_fetch_object(object); - php_zipobj_close(intern); + php_zipobj_close(intern, NULL); #ifdef HAVE_PROGRESS_CALLBACK /* if not properly called by libzip */ @@ -1491,7 +1477,7 @@ PHP_METHOD(ZipArchive, open) } /* If we already have an opened zip, free it */ - php_zipobj_close(ze_obj); + php_zipobj_close(ze_obj, NULL); /* open for write without option to empty the archive */ if ((flags & (ZIP_TRUNCATE | ZIP_RDONLY)) == 0) { @@ -1515,6 +1501,7 @@ PHP_METHOD(ZipArchive, open) ze_obj->filename = resolved_path; ze_obj->filename_len = strlen(resolved_path); ze_obj->za = intern; + ze_obj->from_string = false; RETURN_TRUE; } /* }}} */ @@ -1522,19 +1509,26 @@ PHP_METHOD(ZipArchive, open) /* {{{ Create new read-only zip using given string */ PHP_METHOD(ZipArchive, openString) { - zend_string *buffer; + zend_string *buffer = NULL; + zend_long flags = 0; zval *self = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &buffer) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sl", &buffer, &flags) == FAILURE) { RETURN_THROWS(); } + if (!buffer) { + buffer = ZSTR_EMPTY_ALLOC(); + } + ze_zip_object *ze_obj = Z_ZIP_P(self); + php_zipobj_close(ze_obj, NULL); + zip_error_t err; zip_error_init(&err); - zip_source_t * zip_source = zip_source_buffer_create(ZSTR_VAL(buffer), ZSTR_LEN(buffer), 0, &err); + zip_source_t * zip_source = php_zip_create_string_source(buffer, &ze_obj->out_str, &err); if (!zip_source) { ze_obj->err_zip = zip_error_code_zip(&err); @@ -1543,9 +1537,7 @@ PHP_METHOD(ZipArchive, openString) RETURN_LONG(ze_obj->err_zip); } - php_zipobj_close(ze_obj); - - struct zip *intern = zip_open_from_source(zip_source, ZIP_RDONLY, &err); + struct zip *intern = zip_open_from_source(zip_source, flags, &err); if (!intern) { ze_obj->err_zip = zip_error_code_zip(&err); ze_obj->err_sys = zip_error_code_system(&err); @@ -1554,7 +1546,7 @@ PHP_METHOD(ZipArchive, openString) RETURN_LONG(ze_obj->err_zip); } - php_zipobj_add_buffer(ze_obj, buffer); + ze_obj->from_string = true; ze_obj->za = intern; zip_error_fini(&err); RETURN_TRUE; @@ -1593,11 +1585,36 @@ PHP_METHOD(ZipArchive, close) ZIP_FROM_OBJECT(intern, self); - RETURN_BOOL(php_zipobj_close(Z_ZIP_P(self))); + RETURN_BOOL(php_zipobj_close(Z_ZIP_P(self), NULL)); } /* }}} */ -/* {{{ close the zip archive */ +/* {{{ close the zip archive and get the result as a string */ +PHP_METHOD(ZipArchive, closeToString) +{ + struct zip *intern; + zval *self = ZEND_THIS; + + ZEND_PARSE_PARAMETERS_NONE(); + + ZIP_FROM_OBJECT(intern, self); + + if (!Z_ZIP_P(self)->from_string) { + zend_throw_error(NULL, "ZipArchive::closeToString can only be called on " + "an archive opened with ZipArchive::openString"); + RETURN_THROWS(); + } + + zend_string * ret = NULL; + bool success = php_zipobj_close(Z_ZIP_P(self), &ret); + if (success) { + RETURN_STR(ret ? ret : ZSTR_EMPTY_ALLOC()); + } + RETURN_FALSE; +} +/* }}} */ + +/* {{{ get the number of entries */ PHP_METHOD(ZipArchive, count) { struct zip *intern; @@ -1911,9 +1928,7 @@ PHP_METHOD(ZipArchive, addFromString) ZIP_FROM_OBJECT(intern, self); ze_obj = Z_ZIP_P(self); - php_zipobj_add_buffer(ze_obj, buffer); - - zs = zip_source_buffer(intern, ZSTR_VAL(buffer), ZSTR_LEN(buffer), 0); + zs = php_zip_create_string_source(buffer, NULL, NULL); if (zs == NULL) { RETURN_FALSE; diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 0b96d6ec3eaa4..fdb68092faa13 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -68,11 +68,11 @@ typedef struct _ze_zip_read_rsrc { /* Extends zend object */ typedef struct _ze_zip_object { struct zip *za; - zend_string **buffers; HashTable *prop_handler; char *filename; size_t filename_len; - size_t buffers_cnt; + zend_string * out_str; + bool from_string; zip_int64_t last_id; int err_zip; int err_sys; @@ -96,6 +96,8 @@ php_stream *php_stream_zip_open(struct zip *arch, struct zip_stat *sb, const cha extern const php_stream_wrapper php_stream_zip_wrapper; +zip_source_t * php_zip_create_string_source(zend_string *str, zend_string **dest, zip_error_t *err); + #define LIBZIP_ATLEAST(m,n,p) (((m<<16) + (n<<8) + p) <= ((LIBZIP_VERSION_MAJOR<<16) + (LIBZIP_VERSION_MINOR<<8) + LIBZIP_VERSION_MICRO)) #endif /* PHP_ZIP_H */ diff --git a/ext/zip/php_zip.stub.php b/ext/zip/php_zip.stub.php index 42dfb006fc61a..a3f7004e9d373 100644 --- a/ext/zip/php_zip.stub.php +++ b/ext/zip/php_zip.stub.php @@ -646,7 +646,7 @@ class ZipArchive implements Countable /** @tentative-return-type */ public function open(string $filename, int $flags = 0): bool|int {} - public function openString(string $data): bool|int {} + public function openString(string $data = '', int $flags = 0): bool|int {} /** * @tentative-return-type @@ -656,6 +656,8 @@ public function setPassword(#[\SensitiveParameter] string $password): bool {} /** @tentative-return-type */ public function close(): bool {} + public function closeToString(): bool|string {} + /** @tentative-return-type */ public function count(): int {} diff --git a/ext/zip/php_zip_arginfo.h b/ext/zip/php_zip_arginfo.h index 89dd33b6f1a03..fbb42bdda3365 100644 --- a/ext/zip/php_zip_arginfo.h +++ b/ext/zip/php_zip_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_zip.stub.php instead. - * Stub hash: e04b3e90c42074ac364ea25a0e794815bd4271e5 */ + * Stub hash: 6ecef8cd9a1695fb5a1936188908bb9a7e7c9530 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_open, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -45,8 +45,9 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_open, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_openString, 0, 1, MAY_BE_BOOL|MAY_BE_LONG) - ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_openString, 0, 0, MAY_BE_BOOL|MAY_BE_LONG) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data, IS_STRING, 0, "\'\'") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_setPassword, 0, 1, _IS_BOOL, 0) @@ -56,6 +57,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_close, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_closeToString, 0, 0, MAY_BE_BOOL|MAY_BE_STRING) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -320,6 +324,7 @@ ZEND_METHOD(ZipArchive, open); ZEND_METHOD(ZipArchive, openString); ZEND_METHOD(ZipArchive, setPassword); ZEND_METHOD(ZipArchive, close); +ZEND_METHOD(ZipArchive, closeToString); ZEND_METHOD(ZipArchive, count); ZEND_METHOD(ZipArchive, getStatusString); ZEND_METHOD(ZipArchive, clearError); @@ -399,6 +404,7 @@ static const zend_function_entry class_ZipArchive_methods[] = { ZEND_ME(ZipArchive, openString, arginfo_class_ZipArchive_openString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, setPassword, arginfo_class_ZipArchive_setPassword, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, close, arginfo_class_ZipArchive_close, ZEND_ACC_PUBLIC) + ZEND_ME(ZipArchive, closeToString, arginfo_class_ZipArchive_closeToString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, count, arginfo_class_ZipArchive_count, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, getStatusString, arginfo_class_ZipArchive_getStatusString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, clearError, arginfo_class_ZipArchive_clearError, ZEND_ACC_PUBLIC) diff --git a/ext/zip/tests/ZipArchive_closeToString_basic.phpt b/ext/zip/tests/ZipArchive_closeToString_basic.phpt new file mode 100644 index 0000000000000..a7854e410fb2d --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeToString_basic.phpt @@ -0,0 +1,25 @@ +--TEST-- +ZipArchive::closeToString() basic +--EXTENSIONS-- +zip +--FILE-- +openString(); +$zip->addFromString('test1', '1'); +$zip->addFromString('test2', '2'); +$contents = $zip->closeToString(); +echo $contents ? "OK\n" : "FAILED\n"; + +$zip = new ZipArchive(); +$zip->openString($contents); +var_dump($zip->getFromName('test1')); +var_dump($zip->getFromName('test2')); +var_dump($zip->getFromName('nonexistent')); + +?> +--EXPECT-- +OK +string(1) "1" +string(1) "2" +bool(false) diff --git a/ext/zip/tests/ZipArchive_closeToString_error.phpt b/ext/zip/tests/ZipArchive_closeToString_error.phpt new file mode 100644 index 0000000000000..8b260aa1bc075 --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeToString_error.phpt @@ -0,0 +1,45 @@ +--TEST-- +ZipArchive::closeToString() error cases +--EXTENSIONS-- +zip +--FILE-- +openString(); +var_dump($zip->open(__DIR__ . '/test.zip')); +try { + $zip->closeToString(); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +echo "2.\n"; +$zip = new ZipArchive(); +$zip->openString('...'); +echo $zip->getStatusString() . "\n"; +try { + $zip->closeToString(); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +echo "3.\n"; +$input = file_get_contents(__DIR__ . '/test.zip'); +$zip = new ZipArchive(); +$zip->openString($input); +$zip->addFromString('entry1.txt', ''); +$result = $zip->closeToString(); +echo gettype($result) . "\n"; +var_dump($input !== $result); +?> +--EXPECT-- +1. +bool(true) +ZipArchive::closeToString can only be called on an archive opened with ZipArchive::openString +2. +Not a zip archive +Invalid or uninitialized Zip object +3. +string +bool(true) diff --git a/ext/zip/tests/ZipArchive_closeToString_false.phpt b/ext/zip/tests/ZipArchive_closeToString_false.phpt new file mode 100644 index 0000000000000..5087461c8ffda --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeToString_false.phpt @@ -0,0 +1,22 @@ +--TEST-- +ZipArchive::closeToString() false return +--EXTENSIONS-- +zip +--FILE-- +openString($input)); +$zip->setCompressionIndex(0, ZipArchive::CM_DEFLATE); +var_dump($zip->closeToString()); +echo $zip->getStatusString() . "\n"; +?> +--EXPECTREGEX-- +bool\(true\) + +Warning: ZipArchive::closeToString\(\): (Zip archive inconsistent|Unexpected length of data).* +bool\(false\) +(Zip archive inconsistent|Unexpected length of data) diff --git a/ext/zip/tests/ZipArchive_closeToString_variation.phpt b/ext/zip/tests/ZipArchive_closeToString_variation.phpt new file mode 100644 index 0000000000000..056ef4c20ec27 --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeToString_variation.phpt @@ -0,0 +1,14 @@ +--TEST-- +ZipArchive::closeToString() variations +--EXTENSIONS-- +zip +--FILE-- +openString(); +var_dump($zip->closeToString()); +echo $zip->getStatusString() . "\n"; +?> +--EXPECT-- +string(0) "" +No error diff --git a/ext/zip/tests/ZipArchive_openString.phpt b/ext/zip/tests/ZipArchive_openString.phpt index f787b4a84933d..253ffd23e8e45 100644 --- a/ext/zip/tests/ZipArchive_openString.phpt +++ b/ext/zip/tests/ZipArchive_openString.phpt @@ -5,7 +5,7 @@ zip --FILE-- openString(file_get_contents(__DIR__."/test_procedural.zip")); +$zip->openString(file_get_contents(__DIR__."/test_procedural.zip"), ZipArchive::RDONLY); for ($i = 0; $i < $zip->numFiles; $i++) { $stat = $zip->statIndex($i); diff --git a/ext/zip/tests/wrong-file-size.zip b/ext/zip/tests/wrong-file-size.zip new file mode 100644 index 0000000000000..fc9fa1a434c7d Binary files /dev/null and b/ext/zip/tests/wrong-file-size.zip differ diff --git a/ext/zip/zip_source.c b/ext/zip/zip_source.c new file mode 100644 index 0000000000000..a21c910fdc6cd --- /dev/null +++ b/ext/zip/zip_source.c @@ -0,0 +1,181 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "php.h" +#include "php_zip.h" + +typedef struct _php_zip_string_source { + zend_string *in_str; + size_t in_offset; + time_t mtime; + zend_string *out_str; + size_t out_offset; + zend_string **dest; + zip_error_t error; +} php_zip_string_source; + +static zip_int64_t php_zip_string_cb(void *userdata, void *data, zip_uint64_t len, zip_source_cmd_t cmd) +{ + php_zip_string_source *ctx = userdata; + switch (cmd) { + case ZIP_SOURCE_SUPPORTS: + return zip_source_make_command_bitmap( + ZIP_SOURCE_FREE, +#if defined(LIBZIP_VERSION_MAJOR) && LIBZIP_VERSION_MAJOR >= 1 && LIBZIP_VERSION_MINOR >= 10 + ZIP_SOURCE_SUPPORTS_REOPEN, +#endif + ZIP_SOURCE_OPEN, + ZIP_SOURCE_READ, + ZIP_SOURCE_CLOSE, + ZIP_SOURCE_STAT, + ZIP_SOURCE_ERROR, + ZIP_SOURCE_SEEK, + ZIP_SOURCE_TELL, + ZIP_SOURCE_BEGIN_WRITE, + ZIP_SOURCE_WRITE, + ZIP_SOURCE_COMMIT_WRITE, + ZIP_SOURCE_ROLLBACK_WRITE, + ZIP_SOURCE_SEEK_WRITE, + ZIP_SOURCE_TELL_WRITE, + ZIP_SOURCE_REMOVE, + -1 + ); + + case ZIP_SOURCE_FREE: + zend_string_release(ctx->out_str); + zend_string_release(ctx->in_str); + efree(ctx); + return 0; + + /* Read ops */ + + case ZIP_SOURCE_OPEN: + ctx->in_offset = 0; + return 0; + + case ZIP_SOURCE_READ: { + size_t remaining = ZSTR_LEN(ctx->in_str) - ctx->in_offset; + len = MIN(len, remaining); + if (len) { + memcpy(data, ZSTR_VAL(ctx->in_str) + ctx->in_offset, len); + ctx->in_offset += len; + } + return len; + } + + case ZIP_SOURCE_CLOSE: + return 0; + + case ZIP_SOURCE_STAT: { + zip_stat_t *st; + if (len < sizeof(*st)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + + st = (zip_stat_t *)data; + zip_stat_init(st); + st->mtime = ctx->mtime; + st->size = ZSTR_LEN(ctx->in_str); + st->comp_size = st->size; + st->comp_method = ZIP_CM_STORE; + st->encryption_method = ZIP_EM_NONE; + st->valid = ZIP_STAT_MTIME | ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_ENCRYPTION_METHOD; + + return sizeof(*st); + } + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + /* Seekable read ops */ + + case ZIP_SOURCE_SEEK: { + zip_int64_t new_offset = zip_source_seek_compute_offset( + ctx->in_offset, ZSTR_LEN(ctx->in_str), data, len, &ctx->error); + if (new_offset < 0) { + return -1; + } + ctx->in_offset = (size_t)new_offset; + return 0; + } + + case ZIP_SOURCE_TELL: + if (ctx->in_offset > ZIP_INT64_MAX) { + zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW); + return -1; + } + return (zip_int64_t)ctx->in_offset; + + /* Write ops */ + + case ZIP_SOURCE_BEGIN_WRITE: + zend_string_release(ctx->out_str); + ctx->out_str = ZSTR_EMPTY_ALLOC(); + return 0; + + case ZIP_SOURCE_WRITE: + if (ctx->out_offset > SIZE_MAX - len) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + if (ctx->out_offset + len > ZSTR_LEN(ctx->out_str)) { + ctx->out_str = zend_string_realloc(ctx->out_str, ctx->out_offset + len, false); + } + memcpy(ZSTR_VAL(ctx->out_str) + ctx->out_offset, data, len); + ctx->out_offset += len; + return len; + + case ZIP_SOURCE_COMMIT_WRITE: + ZSTR_VAL(ctx->out_str)[ZSTR_LEN(ctx->out_str)] = '\0'; + zend_string_release(ctx->in_str); + ctx->in_str = ctx->out_str; + ctx->out_str = ZSTR_EMPTY_ALLOC(); + if (ctx->dest) { + *(ctx->dest) = zend_string_copy(ctx->in_str); + } + return 0; + + case ZIP_SOURCE_ROLLBACK_WRITE: + zend_string_release(ctx->out_str); + ctx->out_str = ZSTR_EMPTY_ALLOC(); + return 0; + + case ZIP_SOURCE_SEEK_WRITE: { + zip_int64_t new_offset = zip_source_seek_compute_offset( + ctx->out_offset, ZSTR_LEN(ctx->out_str), data, len, &ctx->error); + if (new_offset < 0) { + return -1; + } + ctx->out_offset = new_offset; + return 0; + } + + case ZIP_SOURCE_TELL_WRITE: + if (ctx->out_offset > ZIP_INT64_MAX) { + zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW); + return -1; + } + return (zip_int64_t)ctx->out_offset; + + case ZIP_SOURCE_REMOVE: + zend_string_release(ctx->in_str); + ctx->in_str = ZSTR_EMPTY_ALLOC(); + ctx->in_offset = 0; + return 0; + + default: + zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } +} + +zip_source_t * php_zip_create_string_source(zend_string *str, zend_string **dest, zip_error_t *err) /* {{{ */ +{ + php_zip_string_source *ctx = ecalloc(1, sizeof(php_zip_string_source)); + ctx->in_str = zend_string_copy(str); + ctx->out_str = ZSTR_EMPTY_ALLOC(); + ctx->dest = dest; + ctx->mtime = time(NULL); + return zip_source_function_create(php_zip_string_cb, (void*)ctx, err); +}