205 lines
5.8 KiB
C
205 lines
5.8 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| Copyright © The PHP Group and Contributors. |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to the Modified BSD License that is |
|
|
| bundled with this package in the file LICENSE, and is available |
|
|
| through the World Wide Web at <https://www.php.net/license/>. |
|
|
| |
|
|
| SPDX-License-Identifier: BSD-3-Clause |
|
|
+----------------------------------------------------------------------+
|
|
| Author: Tim Starling <tstarling@wikimedia.org> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
#include "php.h"
|
|
#include "php_zip.h"
|
|
|
|
typedef struct _php_zip_string_source {
|
|
/* The current string being read from */
|
|
zend_string *in_str;
|
|
/* The offset into in_str of the current read position */
|
|
size_t in_offset;
|
|
/* The modification time returned in stat calls */
|
|
time_t mtime;
|
|
/* The current string being written to */
|
|
zend_string *out_str;
|
|
/* The offset into out_str of the current write position */
|
|
size_t out_offset;
|
|
/* A place to copy the result to when the archive is closed, or NULL */
|
|
zend_string **dest;
|
|
/* The error to be returned when libzip asks for the last error code */
|
|
zip_error_t error;
|
|
} php_zip_string_source;
|
|
|
|
/* The source callback function, see https://libzip.org/documentation/zip_source_function.html
|
|
* This is similar to read_data() in libzip's zip_source_buffer.c */
|
|
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 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);
|
|
}
|