ru_php/ext/zip/zip_source.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);
}