979 lines
26 KiB
C
979 lines
26 KiB
C
#include "links.h"
|
|
|
|
#ifdef write
|
|
#undef write
|
|
#endif
|
|
|
|
my_uintptr_t decompressed_cache_size = 0;
|
|
|
|
static int display_error(struct terminal *term, unsigned char *msg, int *errp)
|
|
{
|
|
if (errp) *errp = 1;
|
|
if (!term) return 0;
|
|
if (!errp) if (find_msg_box(term, msg, NULL, NULL)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef HAVE_ANY_COMPRESSION
|
|
|
|
static void decoder_memory_init(unsigned char **p, size_t *size, off_t init_length)
|
|
{
|
|
if (init_length > 0 && init_length < MAXINT) *size = (int)init_length;
|
|
else *size = 4096;
|
|
again:
|
|
if (*size <= 4096) {
|
|
*p = mem_alloc(*size);
|
|
} else {
|
|
*p = mem_alloc_mayfail(*size);
|
|
if (!*p) {
|
|
*size >>= 1;
|
|
goto again;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int decoder_memory_expand(unsigned char **p, size_t size, size_t *addsize)
|
|
{
|
|
unsigned char *pp;
|
|
size_t add = size / 4 + 1;
|
|
if (add > MAXINT) add = MAXINT;
|
|
while (size + add < size || !(pp = mem_realloc_mayfail(*p, size + add))) {
|
|
if (add > 1) add >>= 1;
|
|
else goto ovf;
|
|
}
|
|
*addsize = add;
|
|
*p = pp;
|
|
return 0;
|
|
|
|
ovf:
|
|
*addsize = 0;
|
|
return -1;
|
|
}
|
|
|
|
static void decompress_error(struct terminal *term, struct cache_entry *ce, unsigned char *lib, unsigned char *msg, int *errp)
|
|
{
|
|
unsigned char *u, *server;
|
|
if ((u = parse_http_header(ce->head, cast_uchar "Content-Encoding", NULL))) {
|
|
mem_free(u);
|
|
if ((server = get_host_name(ce->url))) {
|
|
add_blacklist_entry(server, BL_NO_COMPRESSION);
|
|
mem_free(server);
|
|
}
|
|
}
|
|
if (!display_error(term, TEXT_(T_DECOMPRESSION_ERROR), errp)) return;
|
|
u = display_url(term, ce->url, 1);
|
|
msg_box(term, getml(u, NULL), TEXT_(T_DECOMPRESSION_ERROR), AL_CENTER, TEXT_(T_ERROR_DECOMPRESSING_), u, TEXT_(T__wITH_), lib, cast_uchar ": ", msg, MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_ZLIB
|
|
#include <zlib.h>
|
|
static int decode_gzip(struct terminal *term, struct cache_entry *ce, int defl, int *errp)
|
|
{
|
|
unsigned char err;
|
|
unsigned char memory_error;
|
|
unsigned char skip_gzip_header;
|
|
unsigned char old_zlib;
|
|
z_stream z;
|
|
off_t offset;
|
|
int r;
|
|
unsigned char *p;
|
|
struct fragment *f;
|
|
struct list_head *lf;
|
|
size_t size;
|
|
|
|
retry_after_memory_error:
|
|
memory_error = 0;
|
|
decoder_memory_init(&p, &size, ce->length);
|
|
init_again:
|
|
err = 0;
|
|
skip_gzip_header = 0;
|
|
old_zlib = 0;
|
|
memset(&z, 0, sizeof z);
|
|
z.next_in = NULL;
|
|
z.avail_in = 0;
|
|
z.next_out = p;
|
|
z.avail_out = (unsigned)size;
|
|
z.zalloc = NULL;
|
|
z.zfree = NULL;
|
|
z.opaque = NULL;
|
|
r = inflateInit2(&z, defl == 1 ? 15 : defl == 2 ? -15 : 15 + 16);
|
|
init_failed:
|
|
switch (r) {
|
|
case Z_OK: break;
|
|
case Z_MEM_ERROR: memory_error = 1;
|
|
err = 1;
|
|
goto after_inflateend;
|
|
case Z_STREAM_ERROR:
|
|
if (!defl && !old_zlib) {
|
|
if (defrag_entry(ce)) {
|
|
memory_error = 1;
|
|
err = 1;
|
|
goto after_inflateend;
|
|
}
|
|
r = inflateInit2(&z, -15);
|
|
skip_gzip_header = 1;
|
|
old_zlib = 1;
|
|
goto init_failed;
|
|
}
|
|
decompress_error(term, ce, cast_uchar "zlib", z.msg ? (unsigned char *)z.msg : (unsigned char *)"Invalid parameter", errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
case Z_VERSION_ERROR: decompress_error(term, ce, cast_uchar "zlib", z.msg ? (unsigned char *)z.msg : (unsigned char *)"Bad zlib version", errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
default: decompress_error(term, ce, cast_uchar "zlib", z.msg ? (unsigned char *)z.msg : (unsigned char *)"Unknown return value on inflateInit2", errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
}
|
|
offset = 0;
|
|
foreach(struct fragment, f, lf, ce->frag) {
|
|
if (f->offset != offset) break;
|
|
z.next_in = f->data;
|
|
z.avail_in = (unsigned)f->length;
|
|
if ((off_t)z.avail_in != f->length) overalloc();
|
|
repeat_frag:
|
|
if (skip_gzip_header == 2) {
|
|
if (z.avail_in < 8) goto finish;
|
|
z.next_in = (unsigned char *)z.next_in + 8;
|
|
z.avail_in -= 8;
|
|
skip_gzip_header = 1;
|
|
}
|
|
if (skip_gzip_header) {
|
|
/* if zlib is old, we have to skip gzip header manually
|
|
otherwise zlib 1.2.x can do it automatically */
|
|
unsigned char *head = z.next_in;
|
|
unsigned headlen = 10;
|
|
if (z.avail_in <= 11) goto finish;
|
|
if (head[0] != 0x1f || head[1] != 0x8b) {
|
|
decompress_error(term, ce, cast_uchar "zlib", TEXT_(T_COMPRESSED_ERROR), errp);
|
|
err = 1;
|
|
goto finish;
|
|
}
|
|
if (head[2] != 8 || head[3] & 0xe0) {
|
|
decompress_error(term, ce, cast_uchar "zlib", TEXT_(T_UNKNOWN_COMPRESSION_METHOD), errp);
|
|
err = 1;
|
|
goto finish;
|
|
}
|
|
if (head[3] & 0x04) {
|
|
headlen += 2 + head[10] + (head[11] << 8);
|
|
if (headlen >= z.avail_in) goto finish;
|
|
}
|
|
if (head[3] & 0x08) {
|
|
do {
|
|
headlen++;
|
|
if (headlen >= z.avail_in) goto finish;
|
|
} while (head[headlen - 1]);
|
|
}
|
|
if (head[3] & 0x10) {
|
|
do {
|
|
headlen++;
|
|
if (headlen >= z.avail_in) goto finish;
|
|
} while (head[headlen - 1]);
|
|
}
|
|
if (head[3] & 0x01) {
|
|
headlen += 2;
|
|
if (headlen >= z.avail_in) goto finish;
|
|
}
|
|
z.next_in = (unsigned char *)z.next_in + headlen;
|
|
z.avail_in -= headlen;
|
|
skip_gzip_header = 0;
|
|
}
|
|
r = inflate(&z, f->list_entry.next == &ce->frag ? Z_SYNC_FLUSH : Z_NO_FLUSH);
|
|
switch (r) {
|
|
case Z_OK: break;
|
|
case Z_BUF_ERROR: break;
|
|
case Z_STREAM_END: r = inflateEnd(&z);
|
|
if (r != Z_OK) goto end_failed;
|
|
r = inflateInit2(&z, old_zlib ? -15 : defl ? 15 : 15 + 16);
|
|
if (r != Z_OK) {
|
|
old_zlib = 0;
|
|
goto init_failed;
|
|
}
|
|
if (old_zlib) {
|
|
skip_gzip_header = 2;
|
|
}
|
|
break;
|
|
case Z_NEED_DICT:
|
|
case Z_DATA_ERROR: if (defl == 1) {
|
|
defl = 2;
|
|
r = inflateEnd(&z);
|
|
if (r != Z_OK) goto end_failed;
|
|
goto init_again;
|
|
}
|
|
decompress_error(term, ce, cast_uchar "zlib", z.msg ? (unsigned char *)z.msg : TEXT_(T_COMPRESSED_ERROR), errp);
|
|
err = 1;
|
|
goto finish;
|
|
case Z_STREAM_ERROR: decompress_error(term, ce, cast_uchar "zlib", z.msg ? (unsigned char *)z.msg : (unsigned char *)"Internal error on inflate", errp);
|
|
err = 1;
|
|
goto finish;
|
|
case Z_MEM_ERROR:
|
|
mem_error: memory_error = 1;
|
|
err = 1;
|
|
goto finish;
|
|
default: decompress_error(term, ce, cast_uchar "zlib", z.msg ? (unsigned char *)z.msg : (unsigned char *)"Unknown return value on inflate", errp);
|
|
err = 1;
|
|
break;
|
|
}
|
|
if (!z.avail_out) {
|
|
size_t addsize;
|
|
if (decoder_memory_expand(&p, size, &addsize) < 0)
|
|
goto mem_error;
|
|
z.next_out = p + size;
|
|
z.avail_out = (unsigned)addsize;
|
|
size += addsize;
|
|
}
|
|
if (z.avail_in) goto repeat_frag;
|
|
/* In zlib 1.1.3, inflate(Z_SYNC_FLUSH) doesn't work.
|
|
The following line fixes it --- for last fragment, loop until
|
|
we get an eof. */
|
|
if (r == Z_OK && f->list_entry.next == &ce->frag) goto repeat_frag;
|
|
offset += f->length;
|
|
}
|
|
finish:
|
|
r = inflateEnd(&z);
|
|
end_failed:
|
|
switch (r) {
|
|
case Z_OK: break;
|
|
case Z_STREAM_ERROR: decompress_error(term, ce, cast_uchar "zlib", z.msg ? (unsigned char *)z.msg : (unsigned char *)"Internal error on inflateEnd", errp);
|
|
err = 1;
|
|
break;
|
|
case Z_MEM_ERROR: memory_error = 1;
|
|
err = 1;
|
|
break;
|
|
default: decompress_error(term, ce, cast_uchar "zlib", z.msg ? (unsigned char *)z.msg : (unsigned char *)"Unknown return value on inflateEnd", errp);
|
|
err = 1;
|
|
break;
|
|
}
|
|
after_inflateend:
|
|
if (memory_error) {
|
|
mem_free(p);
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto retry_after_memory_error;
|
|
decompress_error(term, ce, cast_uchar "zlib", z.msg ? (unsigned char *)z.msg : TEXT_(T_OUT_OF_MEMORY), errp);
|
|
return 1;
|
|
}
|
|
if (err && (unsigned char *)z.next_out == p) {
|
|
mem_free(p);
|
|
return 1;
|
|
}
|
|
ce->decompressed = p;
|
|
ce->decompressed_len = (unsigned char *)z.next_out - (unsigned char *)p;
|
|
decompressed_cache_size += ce->decompressed_len;
|
|
ce->decompressed = mem_realloc(ce->decompressed, ce->decompressed_len);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_BROTLI
|
|
#if defined(__TINYC__) && defined(__STDC_VERSION__)
|
|
#undef __STDC_VERSION__
|
|
#endif
|
|
#include <brotli/decode.h>
|
|
static void *brotli_alloc(void *opaque, size_t size)
|
|
{
|
|
return mem_alloc_mayfail(size);
|
|
}
|
|
static void brotli_free(void *opaque, void *ptr)
|
|
{
|
|
if (ptr) mem_free(ptr);
|
|
}
|
|
static int decode_brotli(struct terminal *term, struct cache_entry *ce, int *errp)
|
|
{
|
|
unsigned char err;
|
|
BrotliDecoderState *br;
|
|
const unsigned char *next_in;
|
|
size_t avail_in;
|
|
unsigned char *next_out;
|
|
size_t avail_out;
|
|
off_t offset;
|
|
BrotliDecoderResult res;
|
|
unsigned char *p;
|
|
struct fragment *f;
|
|
struct list_head *lf;
|
|
size_t size;
|
|
|
|
err = 0;
|
|
decoder_memory_init(&p, &size, ce->length);
|
|
next_out = p;
|
|
avail_out = size;
|
|
br = BrotliDecoderCreateInstance(brotli_alloc, brotli_free, NULL);
|
|
if (!br) {
|
|
decompress_error(term, ce, cast_uchar "brotli", TEXT_(T_OUT_OF_MEMORY), errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
}
|
|
|
|
offset = 0;
|
|
foreach(struct fragment, f, lf, ce->frag) {
|
|
if (f->offset != offset) break;
|
|
next_in = f->data;
|
|
avail_in = (size_t)f->length;
|
|
if ((off_t)avail_in != f->length) overalloc();
|
|
repeat_frag:
|
|
res = BrotliDecoderDecompressStream(br, &avail_in, &next_in, &avail_out, &next_out, NULL);
|
|
if (res == BROTLI_DECODER_RESULT_ERROR) {
|
|
decompress_error(term, ce, cast_uchar "brotli", cast_uchar BrotliDecoderErrorString(BrotliDecoderGetErrorCode(br)), errp);
|
|
err = 1;
|
|
goto finish;
|
|
}
|
|
if (!avail_out) {
|
|
size_t addsize;
|
|
if (decoder_memory_expand(&p, size, &addsize) < 0) {
|
|
decompress_error(term, ce, cast_uchar "brotli", TEXT_(T_OUT_OF_MEMORY), errp);
|
|
err = 1;
|
|
goto finish;
|
|
}
|
|
next_out = p + size;
|
|
avail_out = addsize;
|
|
size += addsize;
|
|
goto repeat_frag;
|
|
}
|
|
if (avail_in) goto repeat_frag;
|
|
/*
|
|
BrotliDecoderHasMoreOutput(br) returns BROTLI_BOOL which is defined differently for different compilers, so we must not use it
|
|
if (BrotliDecoderHasMoreOutput(br)) goto repeat_frag;
|
|
*/
|
|
offset += f->length;
|
|
}
|
|
|
|
finish:
|
|
BrotliDecoderDestroyInstance(br);
|
|
after_inflateend:
|
|
if (err && next_out == p) {
|
|
mem_free(p);
|
|
return 1;
|
|
}
|
|
ce->decompressed = p;
|
|
ce->decompressed_len = next_out - p;
|
|
decompressed_cache_size += ce->decompressed_len;
|
|
ce->decompressed = mem_realloc(ce->decompressed, ce->decompressed_len);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_ZSTD
|
|
#include <zstd.h>
|
|
static int decode_zstd(struct terminal *term, struct cache_entry *ce, int *errp)
|
|
{
|
|
ZSTD_DStream *zstd_stream = NULL;
|
|
struct fragment *f;
|
|
struct list_head *lf;
|
|
off_t offset;
|
|
#ifdef HAVE_LONG_LONG
|
|
unsigned long long dec_size;
|
|
#else
|
|
size_t dec_size;
|
|
#endif
|
|
size_t ret;
|
|
unsigned char *p = NULL;
|
|
size_t size;
|
|
ZSTD_inBuffer in;
|
|
ZSTD_outBuffer out;
|
|
|
|
zstd_stream = ZSTD_createDStream();
|
|
if (!zstd_stream) {
|
|
mem_error:
|
|
decompress_error(term, ce, cast_uchar "zstd", TEXT_(T_OUT_OF_MEMORY), errp);
|
|
goto err_ret;
|
|
}
|
|
ret = ZSTD_initDStream(zstd_stream);
|
|
if (ZSTD_isError(ret)) {
|
|
decompress_error(term, ce, cast_uchar "zstd", cast_uchar ZSTD_getErrorName(ret), errp);
|
|
goto err_ret;
|
|
}
|
|
|
|
dec_size = 0;
|
|
if (!list_empty(ce->frag)) {
|
|
f = list_struct(ce->frag.next, struct fragment);
|
|
dec_size = ZSTD_getDecompressedSize(f->data, f->length);
|
|
/*debug("dec size %llx\n", dec_size);*/
|
|
if (ZSTD_isError(dec_size))
|
|
dec_size = 0;
|
|
}
|
|
|
|
decoder_memory_init(&p, &size, dec_size && dec_size < MAXINT ? (off_t)dec_size : ce->length);
|
|
|
|
out.dst = p;
|
|
out.size = size;
|
|
out.pos = 0;
|
|
|
|
offset = 0;
|
|
foreach(struct fragment, f, lf, ce->frag) {
|
|
if (f->offset != offset) break;
|
|
in.src = f->data;
|
|
in.size = f->length;
|
|
in.pos = 0;
|
|
|
|
repeat_frag:
|
|
ret = ZSTD_decompressStream(zstd_stream, &out, &in);
|
|
if (ZSTD_isError(ret)) {
|
|
decompress_error(term, ce, cast_uchar "zstd", cast_uchar ZSTD_getErrorName(ret), errp);
|
|
goto err_ret;
|
|
}
|
|
if (out.pos == out.size) {
|
|
size_t addsize;
|
|
if (decoder_memory_expand(&p, size, &addsize) < 0)
|
|
goto mem_error;
|
|
out.dst = p + size;
|
|
out.size = addsize;
|
|
out.pos = 0;
|
|
size += addsize;
|
|
goto repeat_frag;
|
|
}
|
|
if (in.pos < in.size)
|
|
goto repeat_frag;
|
|
|
|
offset += f->length;
|
|
}
|
|
|
|
ZSTD_freeDStream(zstd_stream);
|
|
zstd_stream = NULL;
|
|
|
|
ce->decompressed = p;
|
|
ce->decompressed_len = (unsigned char *)out.dst + out.pos - p;
|
|
decompressed_cache_size += ce->decompressed_len;
|
|
|
|
ce->decompressed = mem_realloc(ce->decompressed, ce->decompressed_len);
|
|
|
|
return 0;
|
|
|
|
err_ret:
|
|
if (p)
|
|
mem_free(p);
|
|
if (zstd_stream)
|
|
ZSTD_freeDStream(zstd_stream);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_BZIP2
|
|
#include <bzlib.h>
|
|
static int decode_bzip2(struct terminal *term, struct cache_entry *ce, int *errp)
|
|
{
|
|
unsigned char err;
|
|
unsigned char memory_error;
|
|
bz_stream z;
|
|
off_t offset;
|
|
int r;
|
|
unsigned char *p;
|
|
struct fragment *f;
|
|
struct list_head *lf;
|
|
size_t size;
|
|
|
|
retry_after_memory_error:
|
|
err = 0;
|
|
memory_error = 0;
|
|
decoder_memory_init(&p, &size, ce->length);
|
|
memset(&z, 0, sizeof z);
|
|
z.next_in = NULL;
|
|
z.avail_in = 0;
|
|
z.next_out = cast_char p;
|
|
z.avail_out = (unsigned)size;
|
|
z.bzalloc = NULL;
|
|
z.bzfree = NULL;
|
|
z.opaque = NULL;
|
|
r = BZ2_bzDecompressInit(&z, 0, 0);
|
|
init_failed:
|
|
switch (r) {
|
|
case BZ_OK: break;
|
|
case BZ_MEM_ERROR: memory_error = 1;
|
|
err = 1;
|
|
goto after_inflateend;
|
|
case BZ_PARAM_ERROR:
|
|
decompress_error(term, ce, cast_uchar "bzip2", cast_uchar "Invalid parameter", errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
case BZ_CONFIG_ERROR: decompress_error(term, ce, cast_uchar "bzip2", cast_uchar "Bzlib is miscompiled", errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
default: decompress_error(term, ce, cast_uchar "bzip2", cast_uchar "Unknown return value on BZ2_bzDecompressInit", errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
}
|
|
offset = 0;
|
|
foreach(struct fragment, f, lf, ce->frag) {
|
|
if (f->offset != offset) break;
|
|
z.next_in = cast_char f->data;
|
|
z.avail_in = (unsigned)f->length;
|
|
if ((off_t)z.avail_in != f->length) overalloc();
|
|
repeat_frag:
|
|
r = BZ2_bzDecompress(&z);
|
|
switch (r) {
|
|
case BZ_OK: break;
|
|
case BZ_STREAM_END:
|
|
r = BZ2_bzDecompressEnd(&z);
|
|
if (r != BZ_OK) goto end_failed;
|
|
r = BZ2_bzDecompressInit(&z, 0, 0);
|
|
if (r != BZ_OK) goto init_failed;
|
|
break;
|
|
case BZ_DATA_ERROR_MAGIC:
|
|
case BZ_DATA_ERROR: decompress_error(term, ce, cast_uchar "bzip2", TEXT_(T_COMPRESSED_ERROR), errp);
|
|
err = 1;
|
|
goto finish;
|
|
case BZ_PARAM_ERROR: decompress_error(term, ce, cast_uchar "bzip2", cast_uchar "Internal error on BZ2_bzDecompress", errp);
|
|
err = 1;
|
|
goto finish;
|
|
case BZ_MEM_ERROR:
|
|
mem_error: memory_error = 1;
|
|
err = 1;
|
|
goto finish;
|
|
default: decompress_error(term, ce, cast_uchar "bzip2", cast_uchar "Unknown return value on BZ2_bzDecompress", errp);
|
|
err = 1;
|
|
break;
|
|
}
|
|
if (!z.avail_out) {
|
|
size_t addsize;
|
|
if (decoder_memory_expand(&p, size, &addsize) < 0)
|
|
goto mem_error;
|
|
z.next_out = cast_char(p + size);
|
|
z.avail_out = (unsigned)addsize;
|
|
size += addsize;
|
|
goto repeat_frag;
|
|
}
|
|
if (z.avail_in) goto repeat_frag;
|
|
offset += f->length;
|
|
}
|
|
finish:
|
|
r = BZ2_bzDecompressEnd(&z);
|
|
end_failed:
|
|
switch (r) {
|
|
case BZ_OK: break;
|
|
case BZ_PARAM_ERROR: decompress_error(term, ce, cast_uchar "bzip2", cast_uchar "Internal error on BZ2_bzDecompressEnd", errp);
|
|
err = 1;
|
|
break;
|
|
case BZ_MEM_ERROR: memory_error = 1;
|
|
err = 1;
|
|
break;
|
|
default: decompress_error(term, ce, cast_uchar "bzip2", cast_uchar "Unknown return value on BZ2_bzDecompressEnd", errp);
|
|
err = 1;
|
|
break;
|
|
}
|
|
after_inflateend:
|
|
if (memory_error) {
|
|
mem_free(p);
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto retry_after_memory_error;
|
|
decompress_error(term, ce, cast_uchar "bzip2", TEXT_(T_OUT_OF_MEMORY), errp);
|
|
return 1;
|
|
}
|
|
if (err && (unsigned char *)z.next_out == p) {
|
|
mem_free(p);
|
|
return 1;
|
|
}
|
|
ce->decompressed = p;
|
|
ce->decompressed_len = (unsigned char *)z.next_out - (unsigned char *)p;
|
|
decompressed_cache_size += ce->decompressed_len;
|
|
ce->decompressed = mem_realloc(ce->decompressed, ce->decompressed_len);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_LZMA
|
|
#include <lzma.h>
|
|
#define internal internal_
|
|
static int decode_lzma(struct terminal *term, struct cache_entry *ce, int *errp)
|
|
{
|
|
unsigned char err;
|
|
unsigned char memory_error;
|
|
lzma_stream z = LZMA_STREAM_INIT;
|
|
off_t offset;
|
|
int r;
|
|
unsigned char *p;
|
|
struct fragment *f;
|
|
struct list_head *lf;
|
|
size_t size;
|
|
|
|
retry_after_memory_error:
|
|
err = 0;
|
|
memory_error = 0;
|
|
decoder_memory_init(&p, &size, ce->length);
|
|
z.next_in = NULL;
|
|
z.avail_in = 0;
|
|
z.next_out = p;
|
|
z.avail_out = size;
|
|
r = lzma_auto_decoder(&z, UINT64_MAX, 0);
|
|
init_failed:
|
|
switch (r) {
|
|
case LZMA_OK: break;
|
|
case LZMA_MEM_ERROR: memory_error = 1;
|
|
err = 1;
|
|
goto after_inflateend;
|
|
case LZMA_OPTIONS_ERROR:
|
|
decompress_error(term, ce, cast_uchar "lzma", cast_uchar "Invalid parameter", errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
case LZMA_PROG_ERROR: decompress_error(term, ce, cast_uchar "lzma", cast_uchar "Lzma is miscompiled", errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
default: decompress_error(term, ce, cast_uchar "lzma", cast_uchar "Unknown return value on lzma_auto_decoder", errp);
|
|
err = 1;
|
|
goto after_inflateend;
|
|
}
|
|
offset = 0;
|
|
foreach(struct fragment, f, lf, ce->frag) {
|
|
if (f->offset != offset) break;
|
|
z.next_in = f->data;
|
|
z.avail_in = (size_t)f->length;
|
|
if ((off_t)z.avail_in != f->length) overalloc();
|
|
repeat_frag:
|
|
r = lzma_code(&z, LZMA_RUN);
|
|
switch (r) {
|
|
case LZMA_OK:
|
|
case LZMA_NO_CHECK:
|
|
case LZMA_UNSUPPORTED_CHECK:
|
|
case LZMA_GET_CHECK:
|
|
break;
|
|
case LZMA_STREAM_END:
|
|
lzma_end(&z);
|
|
r = lzma_auto_decoder(&z, UINT64_MAX, 0);
|
|
if (r != LZMA_OK) goto init_failed;
|
|
break;
|
|
case LZMA_MEM_ERROR:
|
|
mem_error: memory_error = 1;
|
|
err = 1;
|
|
goto finish;
|
|
case LZMA_MEMLIMIT_ERROR:
|
|
decompress_error(term, ce, cast_uchar "lzma", cast_uchar "Memory limit was exceeded", errp);
|
|
err = 1;
|
|
goto finish;
|
|
case LZMA_FORMAT_ERROR:
|
|
case LZMA_DATA_ERROR:
|
|
case LZMA_BUF_ERROR:
|
|
decompress_error(term, ce, cast_uchar "lzma", TEXT_(T_COMPRESSED_ERROR), errp);
|
|
err = 1;
|
|
goto finish;
|
|
case LZMA_OPTIONS_ERROR:decompress_error(term, ce, cast_uchar "lzma", cast_uchar "File contains unsupported options", errp);
|
|
err = 1;
|
|
goto finish;
|
|
case LZMA_PROG_ERROR: decompress_error(term, ce, cast_uchar "lzma", cast_uchar "Lzma is miscompiled", errp);
|
|
err = 1;
|
|
goto finish;
|
|
default: decompress_error(term, ce, cast_uchar "lzma", cast_uchar "Unknown return value on lzma_code", errp);
|
|
err = 1;
|
|
break;
|
|
}
|
|
if (!z.avail_out) {
|
|
size_t addsize;
|
|
if (decoder_memory_expand(&p, size, &addsize) < 0)
|
|
goto mem_error;
|
|
z.next_out = p + size;
|
|
z.avail_out = addsize;
|
|
size += addsize;
|
|
goto repeat_frag;
|
|
}
|
|
if (z.avail_in) goto repeat_frag;
|
|
offset += f->length;
|
|
}
|
|
finish:
|
|
lzma_end(&z);
|
|
after_inflateend:
|
|
if (memory_error) {
|
|
mem_free(p);
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto retry_after_memory_error;
|
|
decompress_error(term, ce, cast_uchar "lzma", TEXT_(T_OUT_OF_MEMORY), errp);
|
|
return 1;
|
|
}
|
|
if (err && (unsigned char *)z.next_out == p) {
|
|
mem_free(p);
|
|
return 1;
|
|
}
|
|
ce->decompressed = p;
|
|
ce->decompressed_len = (unsigned char *)z.next_out - (unsigned char *)p;
|
|
decompressed_cache_size += ce->decompressed_len;
|
|
ce->decompressed = mem_realloc(ce->decompressed, ce->decompressed_len);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_LZIP
|
|
#include <lzlib.h>
|
|
static int decode_lzip(struct terminal *term, struct cache_entry *ce, int *errp)
|
|
{
|
|
unsigned char err;
|
|
unsigned char memory_error;
|
|
void *lz;
|
|
off_t offset;
|
|
int r;
|
|
enum LZ_Errno le;
|
|
unsigned char *p;
|
|
struct fragment *f;
|
|
struct list_head *lf;
|
|
size_t size;
|
|
size_t used_size;
|
|
|
|
retry_after_memory_error:
|
|
err = 0;
|
|
memory_error = 0;
|
|
decoder_memory_init(&p, &size, ce->length);
|
|
used_size = 0;
|
|
|
|
lz = LZ_decompress_open();
|
|
if (!lz) {
|
|
err = 1;
|
|
memory_error = 1;
|
|
goto after_inflateend;
|
|
}
|
|
if (LZ_decompress_errno(lz) != LZ_ok) {
|
|
lz_error:
|
|
le = LZ_decompress_errno(lz);
|
|
if (0)
|
|
mem_error: le = LZ_mem_error;
|
|
err = 1;
|
|
if (le == LZ_mem_error) {
|
|
memory_error = 1;
|
|
} else if (!ce->incomplete) {
|
|
decompress_error(term, ce, cast_uchar "lzip", cast_uchar LZ_strerror(le), errp);
|
|
}
|
|
goto finish;
|
|
}
|
|
|
|
offset = 0;
|
|
foreach(struct fragment, f, lf, ce->frag) {
|
|
unsigned char *current_ptr;
|
|
int current_len;
|
|
if (f->offset != offset) break;
|
|
current_ptr = f->data;
|
|
current_len = (int)f->length;
|
|
while (current_len) {
|
|
r = LZ_decompress_write(lz, current_ptr, current_len);
|
|
if (r == -1)
|
|
goto lz_error;
|
|
current_ptr += r;
|
|
current_len -= r;
|
|
do {
|
|
if (used_size == size) {
|
|
size_t addsize;
|
|
if (decoder_memory_expand(&p, size, &addsize) < 0)
|
|
goto mem_error;
|
|
size += addsize;
|
|
}
|
|
r = LZ_decompress_read(lz, p + used_size, (int)(size - used_size));
|
|
if (r == -1)
|
|
goto lz_error;
|
|
used_size += r;
|
|
} while (r);
|
|
}
|
|
offset += f->length;
|
|
}
|
|
r = LZ_decompress_finish(lz);
|
|
if (r == -1)
|
|
goto lz_error;
|
|
while ((r = LZ_decompress_finished(lz)) == 0) {
|
|
if (used_size == size) {
|
|
size_t addsize;
|
|
if (decoder_memory_expand(&p, size, &addsize) < 0)
|
|
goto mem_error;
|
|
size += addsize;
|
|
}
|
|
r = LZ_decompress_read(lz, p + used_size, (int)(size - used_size));
|
|
if (r == -1)
|
|
goto lz_error;
|
|
used_size += r;
|
|
}
|
|
if (r == -1)
|
|
goto lz_error;
|
|
|
|
finish:
|
|
LZ_decompress_close(lz);
|
|
after_inflateend:
|
|
if (memory_error) {
|
|
mem_free(p);
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto retry_after_memory_error;
|
|
decompress_error(term, ce, cast_uchar "lzip", TEXT_(T_OUT_OF_MEMORY), errp);
|
|
return 1;
|
|
}
|
|
if (err && !used_size) {
|
|
mem_free(p);
|
|
return 1;
|
|
}
|
|
ce->decompressed = p;
|
|
ce->decompressed_len = used_size;
|
|
decompressed_cache_size += ce->decompressed_len;
|
|
ce->decompressed = mem_realloc(ce->decompressed, ce->decompressed_len);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int get_file_by_term(struct terminal *term, struct cache_entry *ce, unsigned char **start, size_t *len, int *errp)
|
|
{
|
|
unsigned char *enc;
|
|
struct fragment *fr;
|
|
int e;
|
|
if (errp) *errp = 0;
|
|
*start = NULL;
|
|
*len = 0;
|
|
if (!ce) return 1;
|
|
if (ce->decompressed) {
|
|
#if defined(HAVE_ANY_COMPRESSION)
|
|
return_decompressed:
|
|
#endif
|
|
*start = ce->decompressed;
|
|
*len = ce->decompressed_len;
|
|
return 0;
|
|
}
|
|
enc = get_content_encoding(ce->head, ce->url, 0);
|
|
if (enc) {
|
|
#ifdef HAVE_ZLIB
|
|
if (!casestrcmp(enc, cast_uchar "gzip") || !casestrcmp(enc, cast_uchar "x-gzip") || !casestrcmp(enc, cast_uchar "deflate")) {
|
|
int defl = !casestrcmp(enc, cast_uchar "deflate");
|
|
mem_free(enc);
|
|
if (decode_gzip(term, ce, defl, errp)) goto uncompressed;
|
|
goto return_decompressed;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_BROTLI
|
|
if (!casestrcmp(enc, cast_uchar "br")) {
|
|
mem_free(enc);
|
|
if (decode_brotli(term, ce, errp)) goto uncompressed;
|
|
goto return_decompressed;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_ZSTD
|
|
if (!casestrcmp(enc, cast_uchar "zstd")) {
|
|
mem_free(enc);
|
|
if (decode_zstd(term, ce, errp)) goto uncompressed;
|
|
goto return_decompressed;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_BZIP2
|
|
if (!casestrcmp(enc, cast_uchar "bzip2")) {
|
|
mem_free(enc);
|
|
if (decode_bzip2(term, ce, errp)) goto uncompressed;
|
|
goto return_decompressed;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_LZMA
|
|
if (!casestrcmp(enc, cast_uchar "lzma") || !casestrcmp(enc, cast_uchar "lzma2")) {
|
|
mem_free(enc);
|
|
if (decode_lzma(term, ce, errp)) goto uncompressed;
|
|
goto return_decompressed;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_LZIP
|
|
if (!casestrcmp(enc, cast_uchar "lzip")) {
|
|
mem_free(enc);
|
|
if (decode_lzip(term, ce, errp)) goto uncompressed;
|
|
goto return_decompressed;
|
|
}
|
|
#endif
|
|
mem_free(enc);
|
|
goto uncompressed;
|
|
}
|
|
uncompressed:
|
|
if ((e = defrag_entry(ce)) < 0) {
|
|
unsigned char *msg = get_err_msg(e, term);
|
|
if (display_error(term, TEXT_(T_ERROR), errp)) {
|
|
unsigned char *u = display_url(term, ce->url, 1);
|
|
msg_box(term, getml(u, NULL), TEXT_(T_ERROR), AL_CENTER, TEXT_(T_ERROR_LOADING), cast_uchar " ", u, cast_uchar ":\n\n", msg, MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
|
|
}
|
|
}
|
|
if (list_empty(ce->frag)) return 1;
|
|
fr = list_struct(ce->frag.next, struct fragment);
|
|
if (fr->offset || !fr->length) return 1;
|
|
*start = fr->data;
|
|
*len = fr->length;
|
|
return 0;
|
|
}
|
|
|
|
int get_file(struct object_request *o, unsigned char **start, size_t *len)
|
|
{
|
|
struct terminal *term;
|
|
*start = NULL;
|
|
*len = 0;
|
|
if (!o) return 1;
|
|
term = find_terminal(o->term);
|
|
return get_file_by_term(term, o->ce, start, len, NULL);
|
|
}
|
|
|
|
void free_decompressed_data(struct cache_entry *e)
|
|
{
|
|
if (e->decompressed) {
|
|
if (decompressed_cache_size < e->decompressed_len)
|
|
internal_error("free_decompressed_data: decompressed_cache_size underflow %lu, %lu", (unsigned long)decompressed_cache_size, (unsigned long)e->decompressed_len);
|
|
decompressed_cache_size -= e->decompressed_len;
|
|
e->decompressed_len = 0;
|
|
mem_free(e->decompressed);
|
|
e->decompressed = NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_ANY_COMPRESSION
|
|
|
|
void add_compress_methods(unsigned char **s, int *l)
|
|
{
|
|
int cl = 0;
|
|
#ifdef HAVE_ZLIB
|
|
{
|
|
if (!cl) cl = 1; else add_to_str(s, l, cast_uchar ", ");
|
|
add_to_str(s, l, cast_uchar "ZLIB");
|
|
#ifdef zlib_version
|
|
add_to_str(s, l, cast_uchar " (");
|
|
add_to_str(s, l, (unsigned char *)zlib_version);
|
|
add_chr_to_str(s, l, ')');
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef HAVE_BROTLI
|
|
{
|
|
unsigned bv = BrotliDecoderVersion();
|
|
if (!cl) cl = 1; else add_to_str(s, l, cast_uchar ", ");
|
|
add_to_str(s, l, cast_uchar "BROTLI");
|
|
add_to_str(s, l, cast_uchar " (");
|
|
add_num_to_str(s, l, bv >> 24);
|
|
add_chr_to_str(s, l, '.');
|
|
add_num_to_str(s, l, (bv >> 12) & 0xfff);
|
|
add_chr_to_str(s, l, '.');
|
|
add_num_to_str(s, l, bv & 0xfff);
|
|
add_chr_to_str(s, l, ')');
|
|
}
|
|
#endif
|
|
#ifdef HAVE_ZSTD
|
|
{
|
|
unsigned zv = ZSTD_versionNumber();
|
|
if (!cl) cl = 1; else add_to_str(s, l, cast_uchar ", ");
|
|
add_to_str(s, l, cast_uchar "ZSTD");
|
|
add_to_str(s, l, cast_uchar " (");
|
|
add_num_to_str(s, l, zv / 10000);
|
|
add_chr_to_str(s, l, '.');
|
|
add_num_to_str(s, l, zv / 100 % 100);
|
|
add_chr_to_str(s, l, '.');
|
|
add_num_to_str(s, l, zv % 100);
|
|
add_chr_to_str(s, l, ')');
|
|
}
|
|
#endif
|
|
#ifdef HAVE_BZIP2
|
|
{
|
|
unsigned char *b = (unsigned char *)BZ2_bzlibVersion();
|
|
int bl = (int)strcspn(cast_const_char b, ",");
|
|
if (!cl) cl = 1; else add_to_str(s, l, cast_uchar ", ");
|
|
add_to_str(s, l, cast_uchar "BZIP2");
|
|
add_to_str(s, l, cast_uchar " (");
|
|
add_bytes_to_str(s, l, b, bl);
|
|
add_chr_to_str(s, l, ')');
|
|
}
|
|
#endif
|
|
#ifdef HAVE_LZMA
|
|
{
|
|
if (!cl) cl = 1; else add_to_str(s, l, cast_uchar ", ");
|
|
add_to_str(s, l, cast_uchar "LZMA");
|
|
add_to_str(s, l, cast_uchar " (");
|
|
add_to_str(s, l, cast_uchar lzma_version_string());
|
|
add_chr_to_str(s, l, ')');
|
|
}
|
|
#endif
|
|
#ifdef HAVE_LZIP
|
|
{
|
|
if (!cl) cl = 1; else add_to_str(s, l, cast_uchar ", ");
|
|
add_to_str(s, l, cast_uchar "LZIP");
|
|
add_to_str(s, l, cast_uchar " (");
|
|
add_to_str(s, l, cast_uchar LZ_version());
|
|
add_chr_to_str(s, l, ')');
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif
|