mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-23 00:00:06 +01:00

Also introduce CMake LWS_WITH_ZIP_FOPS defaulting to ON that builds junzip.c and make sure this is exported in lws_config.h (included by libwebsockets.h) Improve lws handling of stdint.h import or simulate it.
267 lines
6 KiB
C
267 lines
6 KiB
C
// Unzip library by Per Bothner and Joonas Pihlajamaa.
|
|
// See junzip.h for license and details.
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <zlib.h>
|
|
|
|
#include "private-libwebsockets.h"
|
|
|
|
enum {
|
|
ZC_SIGNATURE = 0,
|
|
ZC_VERSION_MADE_BY = 4,
|
|
ZC_VERSION_NEEDED_TO_EXTRACT = 6,
|
|
ZC_GENERAL_PURPOSE_BIT_FLAG = 8,
|
|
ZC_COMPRESSION_METHOD = 10,
|
|
ZC_LAST_MOD_FILE_TIME = 12,
|
|
ZC_LAST_MOD_FILE_DATE = 14,
|
|
ZC_CRC32 = 16,
|
|
ZC_COMPRESSED_SIZE = 20,
|
|
ZC_UNCOMPRESSED_SIZE = 24,
|
|
ZC_FILE_NAME_LENGTH = 28,
|
|
ZC_EXTRA_FIELD_LENGTH = 30,
|
|
ZC_FILE_COMMENT_LENGTH = 32,
|
|
ZC_DISK_NUMBER_START = 34,
|
|
ZC_INTERNAL_FILE_ATTRIBUTES = 36,
|
|
ZC_EXTERNAL_FILE_ATTRIBUTES = 38,
|
|
ZC_RELATIVE_OFFSET_OF_LOCAL_HEADER = 42,
|
|
ZC_DIRECTORY_LENGTH = 46,
|
|
|
|
ZE_SIGNATURE_OFFSET = 0,
|
|
ZE_DESK_NUMBER = 4,
|
|
ZE_CENTRAL_DIRECTORY_DISK_NUMBER = 6,
|
|
ZE_NUM_ENTRIES_THIS_DISK = 8,
|
|
ZE_NUM_ENTRIES = 10,
|
|
ZE_CENTRAL_DIRECTORY_SIZE = 12,
|
|
ZE_CENTRAL_DIRECTORY_OFFSET = 16,
|
|
ZE_ZIP_COMMENT_LENGTH = 20,
|
|
ZE_DIRECTORY_LENGTH = 22,
|
|
};
|
|
|
|
static uint16_t
|
|
get_u16(void *p)
|
|
{
|
|
const uint8_t *c = (const uint8_t *)p;
|
|
|
|
return (uint16_t)((c[0] | (c[1] << 8)));
|
|
}
|
|
|
|
static uint32_t
|
|
get_u32(void *p)
|
|
{
|
|
const uint8_t *c = (const uint8_t *)p;
|
|
|
|
return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
|
|
}
|
|
|
|
static int
|
|
zf_seek_set(jzfile_t *zfile, size_t offset)
|
|
{
|
|
int new_position = offset;
|
|
|
|
if (new_position < 0 || new_position > zfile->length)
|
|
return -1;
|
|
zfile->position = new_position;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
zf_seek_cur(jzfile_t *zfile, size_t offset)
|
|
{
|
|
int new_position = zfile->position + offset;
|
|
|
|
if (new_position < 0 || new_position > zfile->length)
|
|
return -1;
|
|
zfile->position = new_position;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
zf_seek_end(jzfile_t *zfile, size_t offset)
|
|
{
|
|
int new_position = zfile->length + offset;
|
|
|
|
if (new_position < 0 || new_position > zfile->length)
|
|
return -1;
|
|
zfile->position = new_position;
|
|
|
|
return 0;
|
|
}
|
|
|
|
size_t
|
|
zf_read(jzfile_t *zfile, void *buf, size_t size)
|
|
{
|
|
size_t avail = zfile->length - zfile->position;
|
|
|
|
if (size > avail)
|
|
size = avail;
|
|
memcpy(buf, zfile->start + zfile->position, size);
|
|
zfile->position += size;
|
|
|
|
return size;
|
|
}
|
|
|
|
/* Read ZIP file end record. Will move within file. */
|
|
int
|
|
jzReadEndRecord(jzfile_t *zip)
|
|
{
|
|
unsigned char *ptr = zf_current(zip);
|
|
|
|
if (zf_seek_end(zip, -ZE_DIRECTORY_LENGTH))
|
|
return Z_ERRNO;
|
|
|
|
while (ptr[0] != 0x50 || ptr[1] != 0x4B || ptr[2] != 5 || ptr[3] != 6)
|
|
if (ptr-- == zip->start)
|
|
return Z_ERRNO;
|
|
|
|
zip->numEntries = get_u16(ptr + ZE_NUM_ENTRIES);
|
|
zip->centralDirectoryOffset= get_u32(ptr + ZE_CENTRAL_DIRECTORY_OFFSET);
|
|
|
|
if (get_u16(ptr + ZE_DESK_NUMBER) ||
|
|
get_u16(ptr + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
|
|
zip->numEntries != get_u16(ptr + ZE_NUM_ENTRIES_THIS_DISK))
|
|
return Z_ERRNO;
|
|
|
|
return Z_OK;
|
|
}
|
|
|
|
/* Read ZIP file global directory. Will move within file. */
|
|
int
|
|
jzReadCentralDirectory(jzfile_t *zip, jzcb_t callback)
|
|
{
|
|
jzfile_hdr_t h;
|
|
int i;
|
|
|
|
if (zf_seek_set(zip, zip->centralDirectoryOffset))
|
|
return Z_ERRNO;
|
|
|
|
for (i = 0; i < zip->numEntries; i++) {
|
|
unsigned char *ptr = zf_current(zip);
|
|
|
|
if (zf_available(zip) < ZC_DIRECTORY_LENGTH)
|
|
return Z_ERRNO;
|
|
|
|
zf_seek_cur(zip, ZC_DIRECTORY_LENGTH);
|
|
if (get_u32(ptr + ZC_SIGNATURE) != 0x02014B50)
|
|
return Z_ERRNO;
|
|
|
|
// Construct jzfile_hdr_t from global file h
|
|
h.compressionMethod = get_u16(ptr + ZC_COMPRESSION_METHOD);
|
|
h.crc32 = get_u32(ptr + ZC_CRC32);
|
|
h.compressedSize = get_u32(ptr + ZC_COMPRESSED_SIZE);
|
|
h.uncompressedSize = get_u32(ptr + ZC_UNCOMPRESSED_SIZE);
|
|
h.fileNameLength = get_u16(ptr + ZC_FILE_NAME_LENGTH);
|
|
h.extraFieldLength = get_u16(ptr + ZC_EXTRA_FIELD_LENGTH);
|
|
h.offset = get_u32(ptr + ZC_RELATIVE_OFFSET_OF_LOCAL_HEADER);
|
|
|
|
h.fileNameStart = zf_tell(zip);
|
|
if (zf_seek_cur(zip, h.fileNameLength + h.extraFieldLength +
|
|
get_u16(ptr + ZC_FILE_COMMENT_LENGTH)))
|
|
return Z_ERRNO;
|
|
|
|
if (!callback(zip, i, &h))
|
|
break; // end if callback returns zero
|
|
}
|
|
|
|
return Z_OK;
|
|
}
|
|
|
|
int jzSeekData(jzfile_t *zip, jzfile_hdr_t *entry)
|
|
{
|
|
size_t offset = entry->offset;
|
|
|
|
offset += ZIP_LOCAL_FILE_HEADER_LENGTH;
|
|
offset += entry->fileNameLength + entry->extraFieldLength;
|
|
|
|
if (offset < 0 || offset > zip->length)
|
|
return Z_STREAM_END;
|
|
|
|
zip->position = offset;
|
|
|
|
return Z_OK;
|
|
}
|
|
|
|
/* Read data from file stream, described by h, to preallocated buffer */
|
|
int
|
|
jzReadData(jzfile_t *zip, jzfile_hdr_t *h, void *buffer)
|
|
{
|
|
unsigned char *bytes = (unsigned char *)buffer;
|
|
long compressedLeft, uncompressedLeft;
|
|
z_stream strm;
|
|
int ret;
|
|
|
|
switch (h->compressionMethod) {
|
|
case 0: /* Store - just read it */
|
|
if (zf_read(zip, buffer, h->uncompressedSize) <
|
|
h->uncompressedSize)
|
|
return Z_ERRNO;
|
|
break;
|
|
case 8: /* Deflate - using zlib */
|
|
strm.zalloc = Z_NULL;
|
|
strm.zfree = Z_NULL;
|
|
strm.opaque = Z_NULL;
|
|
|
|
strm.avail_in = 0;
|
|
strm.next_in = Z_NULL;
|
|
|
|
/*
|
|
* Use inflateInit2 with negative window bits to
|
|
* indicate raw data
|
|
*/
|
|
if ((ret = inflateInit2(&strm, -MAX_WBITS)) != Z_OK)
|
|
return ret; /* Zlib errors are negative */
|
|
|
|
/* Inflate compressed data */
|
|
for (compressedLeft = h->compressedSize,
|
|
uncompressedLeft = h->uncompressedSize;
|
|
compressedLeft && uncompressedLeft && ret != Z_STREAM_END;
|
|
compressedLeft -= strm.avail_in) {
|
|
/* Read next chunk */
|
|
unsigned char *ptr = zf_current(zip);
|
|
|
|
strm.avail_in = compressedLeft;
|
|
zf_seek_cur(zip, compressedLeft);
|
|
if (strm.avail_in == 0) {
|
|
inflateEnd(&strm);
|
|
|
|
return Z_ERRNO;
|
|
}
|
|
|
|
strm.next_in = ptr;
|
|
strm.avail_out = uncompressedLeft;
|
|
strm.next_out = bytes;
|
|
|
|
compressedLeft -= strm.avail_in;
|
|
/* inflate will change avail_in */
|
|
|
|
ret = inflate(&strm, Z_NO_FLUSH);
|
|
|
|
if (ret == Z_STREAM_ERROR)
|
|
return ret;
|
|
|
|
switch (ret) {
|
|
case Z_NEED_DICT:
|
|
ret = Z_DATA_ERROR;
|
|
/* and fall through */
|
|
case Z_DATA_ERROR: case Z_MEM_ERROR:
|
|
(void)inflateEnd(&strm);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* bytes uncompressed */
|
|
bytes += uncompressedLeft - strm.avail_out;
|
|
uncompressedLeft = strm.avail_out;
|
|
}
|
|
|
|
inflateEnd(&strm);
|
|
break;
|
|
default:
|
|
return Z_ERRNO;
|
|
}
|
|
|
|
return Z_OK;
|
|
}
|