2017-12-21 02:09:18 +00:00
|
|
|
/*
|
|
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
|
|
*
|
|
|
|
* Original code used in this source file:
|
|
|
|
*
|
|
|
|
* https://github.com/PerBothner/DomTerm.git @912add15f3d0aec
|
|
|
|
*
|
|
|
|
* ./lws-term/io.c
|
|
|
|
* ./lws-term/junzip.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 Per Bothner <per@bothner.com>
|
|
|
|
*
|
|
|
|
* MIT License
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* ( copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
* SOFTWARE.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* lws rewrite:
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation:
|
|
|
|
* version 2.1 of the License.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
* MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
2018-06-07 11:33:24 +00:00
|
|
|
#include "core/private.h"
|
2017-12-21 02:09:18 +00:00
|
|
|
|
|
|
|
#include <zlib.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This code works with zip format containers which may have files compressed
|
|
|
|
* with gzip deflate (type 8) or store uncompressed (type 0).
|
|
|
|
*
|
|
|
|
* Linux zip produces such zipfiles by default, eg
|
|
|
|
*
|
|
|
|
* $ zip ../myzip.zip file1 file2 file3
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define ZIP_COMPRESSION_METHOD_STORE 0
|
|
|
|
#define ZIP_COMPRESSION_METHOD_DEFLATE 8
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
lws_filepos_t filename_start;
|
|
|
|
uint32_t crc32;
|
|
|
|
uint32_t comp_size;
|
|
|
|
uint32_t uncomp_size;
|
|
|
|
uint32_t offset;
|
|
|
|
uint32_t mod_time;
|
|
|
|
uint16_t filename_len;
|
|
|
|
uint16_t extra;
|
|
|
|
uint16_t method;
|
|
|
|
uint16_t file_com_len;
|
|
|
|
} lws_fops_zip_hdr_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
struct lws_fop_fd fop_fd; /* MUST BE FIRST logical fop_fd into
|
|
|
|
* file inside zip: fops_zip fops */
|
|
|
|
lws_fop_fd_t zip_fop_fd; /* logical fop fd on to zip file
|
|
|
|
* itself: using platform fops */
|
|
|
|
lws_fops_zip_hdr_t hdr;
|
|
|
|
z_stream inflate;
|
|
|
|
lws_filepos_t content_start;
|
|
|
|
lws_filepos_t exp_uncomp_pos;
|
|
|
|
union {
|
|
|
|
uint8_t trailer8[8];
|
|
|
|
uint32_t trailer32[2];
|
|
|
|
} u;
|
|
|
|
uint8_t rbuf[128]; /* decompression chunk size */
|
|
|
|
int entry_count;
|
|
|
|
|
|
|
|
unsigned int decompress:1; /* 0 = direct from file */
|
|
|
|
unsigned int add_gzip_container:1;
|
|
|
|
} *lws_fops_zip_t;
|
|
|
|
|
|
|
|
struct lws_plat_file_ops fops_zip;
|
|
|
|
#define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD))
|
|
|
|
|
|
|
|
static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 };
|
|
|
|
|
|
|
|
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_REL_OFFSET_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_DIR_OFFSET = 16,
|
|
|
|
ZE_ZIP_COMMENT_LENGTH = 20,
|
|
|
|
ZE_DIRECTORY_LENGTH = 22,
|
|
|
|
|
|
|
|
ZL_REL_OFFSET_CONTENT = 28,
|
|
|
|
ZL_HEADER_LENGTH = 30,
|
|
|
|
|
|
|
|
LWS_FZ_ERR_SEEK_END_RECORD = 1,
|
|
|
|
LWS_FZ_ERR_READ_END_RECORD,
|
|
|
|
LWS_FZ_ERR_END_RECORD_MAGIC,
|
|
|
|
LWS_FZ_ERR_END_RECORD_SANITY,
|
|
|
|
LWS_FZ_ERR_CENTRAL_SEEK,
|
|
|
|
LWS_FZ_ERR_CENTRAL_READ,
|
|
|
|
LWS_FZ_ERR_CENTRAL_SANITY,
|
|
|
|
LWS_FZ_ERR_NAME_TOO_LONG,
|
|
|
|
LWS_FZ_ERR_NAME_SEEK,
|
|
|
|
LWS_FZ_ERR_NAME_READ,
|
|
|
|
LWS_FZ_ERR_CONTENT_SANITY,
|
|
|
|
LWS_FZ_ERR_CONTENT_SEEK,
|
|
|
|
LWS_FZ_ERR_SCAN_SEEK,
|
|
|
|
LWS_FZ_ERR_NOT_FOUND,
|
|
|
|
LWS_FZ_ERR_ZLIB_INIT,
|
|
|
|
LWS_FZ_ERR_READ_CONTENT,
|
|
|
|
LWS_FZ_ERR_SEEK_COMPRESSED,
|
|
|
|
};
|
|
|
|
|
|
|
|
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)));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
|
|
|
|
{
|
|
|
|
lws_filepos_t amount;
|
|
|
|
uint8_t buf[96];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0)
|
|
|
|
return LWS_FZ_ERR_SEEK_END_RECORD;
|
|
|
|
|
|
|
|
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
|
|
|
|
ZE_DIRECTORY_LENGTH))
|
|
|
|
return LWS_FZ_ERR_READ_END_RECORD;
|
|
|
|
|
|
|
|
if (amount != ZE_DIRECTORY_LENGTH)
|
|
|
|
return LWS_FZ_ERR_READ_END_RECORD;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We require the zip to have the last record right at the end
|
|
|
|
* Linux zip always does this if no zip comment.
|
|
|
|
*/
|
|
|
|
if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6)
|
|
|
|
return LWS_FZ_ERR_END_RECORD_MAGIC;
|
|
|
|
|
|
|
|
i = get_u16(buf + ZE_NUM_ENTRIES);
|
|
|
|
|
|
|
|
if (get_u16(buf + ZE_DESK_NUMBER) ||
|
|
|
|
get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
|
|
|
|
i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK))
|
|
|
|
return LWS_FZ_ERR_END_RECORD_SANITY;
|
|
|
|
|
|
|
|
/* end record is OK... look for our file in the central dir */
|
|
|
|
|
|
|
|
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
|
|
|
|
get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0)
|
|
|
|
return LWS_FZ_ERR_CENTRAL_SEEK;
|
|
|
|
|
|
|
|
while (i--) {
|
|
|
|
priv->content_start = lws_vfs_tell(priv->zip_fop_fd);
|
|
|
|
|
|
|
|
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
|
|
|
|
ZC_DIRECTORY_LENGTH))
|
|
|
|
return LWS_FZ_ERR_CENTRAL_READ;
|
|
|
|
|
|
|
|
if (amount != ZC_DIRECTORY_LENGTH)
|
|
|
|
return LWS_FZ_ERR_CENTRAL_READ;
|
|
|
|
|
|
|
|
if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50)
|
|
|
|
return LWS_FZ_ERR_CENTRAL_SANITY;
|
|
|
|
|
|
|
|
lwsl_debug("cstart 0x%lx\n", (unsigned long)priv->content_start);
|
|
|
|
|
|
|
|
priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH);
|
|
|
|
priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH);
|
|
|
|
priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd);
|
|
|
|
|
|
|
|
priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD);
|
|
|
|
priv->hdr.crc32 = get_u32(buf + ZC_CRC32);
|
|
|
|
priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE);
|
|
|
|
priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE);
|
|
|
|
priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER);
|
|
|
|
priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME);
|
|
|
|
priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH);
|
|
|
|
|
|
|
|
if (priv->hdr.filename_len != len)
|
|
|
|
goto next;
|
|
|
|
|
2018-06-07 11:33:24 +00:00
|
|
|
if (len >= (int)sizeof(buf) - 1)
|
2017-12-21 02:09:18 +00:00
|
|
|
return LWS_FZ_ERR_NAME_TOO_LONG;
|
|
|
|
|
|
|
|
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
|
|
|
|
&amount, buf, len))
|
|
|
|
return LWS_FZ_ERR_NAME_READ;
|
2018-06-07 11:33:24 +00:00
|
|
|
if ((int)amount != len)
|
2017-12-21 02:09:18 +00:00
|
|
|
return LWS_FZ_ERR_NAME_READ;
|
|
|
|
|
|
|
|
buf[len] = '\0';
|
|
|
|
lwsl_debug("check %s vs %s\n", buf, name);
|
|
|
|
|
|
|
|
if (strcmp((const char *)buf, name))
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
/* we found a match */
|
|
|
|
if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset) < 0)
|
|
|
|
return LWS_FZ_ERR_NAME_SEEK;
|
|
|
|
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
|
|
|
|
&amount, buf,
|
|
|
|
ZL_HEADER_LENGTH))
|
|
|
|
return LWS_FZ_ERR_NAME_READ;
|
|
|
|
if (amount != ZL_HEADER_LENGTH)
|
|
|
|
return LWS_FZ_ERR_NAME_READ;
|
|
|
|
|
|
|
|
priv->content_start = priv->hdr.offset +
|
|
|
|
ZL_HEADER_LENGTH +
|
|
|
|
priv->hdr.filename_len +
|
|
|
|
get_u16(buf + ZL_REL_OFFSET_CONTENT);
|
|
|
|
|
|
|
|
lwsl_debug("content supposed to start at 0x%lx\n",
|
|
|
|
(unsigned long)priv->content_start);
|
|
|
|
|
|
|
|
if (priv->content_start > priv->zip_fop_fd->len)
|
|
|
|
return LWS_FZ_ERR_CONTENT_SANITY;
|
|
|
|
|
|
|
|
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
|
|
|
|
priv->content_start) < 0)
|
|
|
|
return LWS_FZ_ERR_CONTENT_SEEK;
|
|
|
|
|
|
|
|
/* we are aligned at the start of the content */
|
|
|
|
|
|
|
|
priv->exp_uncomp_pos = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
next:
|
|
|
|
if (i && lws_vfs_file_seek_set(priv->zip_fop_fd,
|
|
|
|
priv->content_start +
|
|
|
|
ZC_DIRECTORY_LENGTH +
|
|
|
|
priv->hdr.filename_len +
|
|
|
|
priv->hdr.extra +
|
|
|
|
priv->hdr.file_com_len) < 0)
|
|
|
|
return LWS_FZ_ERR_SCAN_SEEK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LWS_FZ_ERR_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
lws_fops_zip_reset_inflate(lws_fops_zip_t priv)
|
|
|
|
{
|
|
|
|
if (priv->decompress)
|
|
|
|
inflateEnd(&priv->inflate);
|
|
|
|
|
|
|
|
priv->inflate.zalloc = Z_NULL;
|
|
|
|
priv->inflate.zfree = Z_NULL;
|
|
|
|
priv->inflate.opaque = Z_NULL;
|
|
|
|
priv->inflate.avail_in = 0;
|
|
|
|
priv->inflate.next_in = Z_NULL;
|
|
|
|
|
|
|
|
if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) {
|
|
|
|
lwsl_err("inflate init failed\n");
|
|
|
|
return LWS_FZ_ERR_ZLIB_INIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->content_start) < 0)
|
|
|
|
return LWS_FZ_ERR_CONTENT_SEEK;
|
|
|
|
|
|
|
|
priv->exp_uncomp_pos = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static lws_fop_fd_t
|
|
|
|
lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
|
|
|
|
const char *vpath, lws_fop_flags_t *flags)
|
|
|
|
{
|
|
|
|
lws_fop_flags_t local_flags = 0;
|
|
|
|
lws_fops_zip_t priv;
|
|
|
|
char rp[192];
|
|
|
|
int m;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vpath points at the / after the fops signature in vfs_path, eg
|
|
|
|
* with a vfs_path "/var/www/docs/manual.zip/index.html", vpath
|
|
|
|
* will come pointing at "/index.html"
|
|
|
|
*/
|
|
|
|
|
|
|
|
priv = lws_zalloc(sizeof(*priv), "fops_zip priv");
|
|
|
|
if (!priv)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
priv->fop_fd.fops = &fops_zip;
|
|
|
|
|
|
|
|
m = sizeof(rp) - 1;
|
|
|
|
if ((vpath - vfs_path - 1) < m)
|
2018-06-07 11:33:24 +00:00
|
|
|
m = lws_ptr_diff(vpath, vfs_path) - 1;
|
|
|
|
lws_strncpy(rp, vfs_path, m + 1);
|
2017-12-21 02:09:18 +00:00
|
|
|
|
|
|
|
/* open the zip file itself using the incoming fops, not fops_zip */
|
|
|
|
|
|
|
|
priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags);
|
|
|
|
if (!priv->zip_fop_fd) {
|
|
|
|
lwsl_err("unable to open zip %s\n", rp);
|
|
|
|
goto bail1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*vpath == '/')
|
|
|
|
vpath++;
|
|
|
|
|
2018-06-07 11:33:24 +00:00
|
|
|
m = lws_fops_zip_scan(priv, vpath, (int)strlen(vpath));
|
2017-12-21 02:09:18 +00:00
|
|
|
if (m) {
|
|
|
|
lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
|
|
|
|
goto bail2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the directory metadata tells us modification time, so pass it on */
|
|
|
|
priv->fop_fd.mod_time = priv->hdr.mod_time;
|
|
|
|
*flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
|
|
|
|
priv->fop_fd.flags = *flags;
|
|
|
|
|
|
|
|
/* The zip fop_fd is left pointing at the start of the content.
|
|
|
|
*
|
|
|
|
* 1) Content could be uncompressed (STORE), and we can always serve
|
|
|
|
* that directly
|
|
|
|
*
|
|
|
|
* 2) Content could be compressed (GZIP), and the client can handle
|
|
|
|
* receiving GZIP... we can wrap it in a GZIP header and trailer
|
|
|
|
* and serve the content part directly. The flag indicating we
|
|
|
|
* are providing GZIP directly is set so lws will send the right
|
|
|
|
* headers.
|
|
|
|
*
|
|
|
|
* 3) Content could be compressed (GZIP) but the client can't handle
|
|
|
|
* receiving GZIP... we can decompress it and serve as it is
|
|
|
|
* inflated piecemeal.
|
|
|
|
*
|
|
|
|
* 4) Content may be compressed some unknown way... fail
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
|
|
|
|
/*
|
|
|
|
* it is stored uncompressed, leave it indicated as
|
|
|
|
* uncompressed, and just serve it from inside the
|
|
|
|
* zip with no gzip container;
|
|
|
|
*/
|
|
|
|
|
|
|
|
lwsl_info("direct zip serving (stored)\n");
|
|
|
|
|
|
|
|
priv->fop_fd.len = priv->hdr.uncomp_size;
|
|
|
|
|
|
|
|
return &priv->fop_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
|
|
|
|
priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can serve the gzipped file contents directly as gzip
|
|
|
|
* from inside the zip container; client says it is OK.
|
|
|
|
*
|
|
|
|
* To convert to standalone gzip, we have to add a 10-byte
|
|
|
|
* constant header and a variable 8-byte trailer around the
|
|
|
|
* content.
|
|
|
|
*
|
|
|
|
* The 8-byte trailer is prepared now and held in the priv.
|
|
|
|
*/
|
|
|
|
|
|
|
|
lwsl_info("direct zip serving (gzipped)\n");
|
|
|
|
|
|
|
|
priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
|
|
|
|
sizeof(priv->u);
|
|
|
|
|
|
|
|
if (lws_is_be()) {
|
|
|
|
uint8_t *p = priv->u.trailer8;
|
|
|
|
|
|
|
|
*p++ = (uint8_t)priv->hdr.crc32;
|
|
|
|
*p++ = (uint8_t)(priv->hdr.crc32 >> 8);
|
|
|
|
*p++ = (uint8_t)(priv->hdr.crc32 >> 16);
|
|
|
|
*p++ = (uint8_t)(priv->hdr.crc32 >> 24);
|
|
|
|
*p++ = (uint8_t)priv->hdr.uncomp_size;
|
|
|
|
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
|
|
|
|
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
|
|
|
|
*p = (uint8_t)(priv->hdr.uncomp_size >> 24);
|
|
|
|
} else {
|
|
|
|
priv->u.trailer32[0] = priv->hdr.crc32;
|
|
|
|
priv->u.trailer32[1] = priv->hdr.uncomp_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
*flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
|
|
|
|
priv->fop_fd.flags = *flags;
|
|
|
|
priv->add_gzip_container = 1;
|
|
|
|
|
|
|
|
return &priv->fop_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
|
|
|
|
|
|
|
|
/* we must decompress it to serve it */
|
|
|
|
|
|
|
|
lwsl_info("decompressed zip serving\n");
|
|
|
|
|
|
|
|
priv->fop_fd.len = priv->hdr.uncomp_size;
|
|
|
|
|
|
|
|
if (lws_fops_zip_reset_inflate(priv)) {
|
|
|
|
lwsl_err("inflate init failed\n");
|
|
|
|
goto bail2;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->decompress = 1;
|
|
|
|
|
|
|
|
return &priv->fop_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we can't handle it ... */
|
|
|
|
|
|
|
|
lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
|
|
|
|
priv->hdr.method);
|
|
|
|
|
|
|
|
bail2:
|
|
|
|
lws_vfs_file_close(&priv->zip_fop_fd);
|
|
|
|
bail1:
|
|
|
|
free(priv);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ie, we are closing the fop_fd for the file inside the gzip */
|
|
|
|
|
|
|
|
static int
|
|
|
|
lws_fops_zip_close(lws_fop_fd_t *fd)
|
|
|
|
{
|
|
|
|
lws_fops_zip_t priv = fop_fd_to_priv(*fd);
|
|
|
|
|
|
|
|
if (priv->decompress)
|
|
|
|
inflateEnd(&priv->inflate);
|
|
|
|
|
|
|
|
lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
|
|
|
|
|
|
|
|
free(priv);
|
|
|
|
*fd = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static lws_fileofs_t
|
|
|
|
lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
|
|
|
|
{
|
|
|
|
fd->pos += offset_from_cur_pos;
|
|
|
|
|
|
|
|
return fd->pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
|
|
|
|
lws_filepos_t len)
|
|
|
|
{
|
|
|
|
lws_fops_zip_t priv = fop_fd_to_priv(fd);
|
|
|
|
lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (priv->decompress) {
|
|
|
|
|
|
|
|
if (priv->exp_uncomp_pos != fd->pos) {
|
|
|
|
/*
|
|
|
|
* there has been a seek in the uncompressed fop_fd
|
|
|
|
* we have to restart the decompression and loop eating
|
|
|
|
* the decompressed data up to the seek point
|
|
|
|
*/
|
|
|
|
lwsl_info("seek in decompressed\n");
|
|
|
|
|
|
|
|
lws_fops_zip_reset_inflate(priv);
|
|
|
|
|
|
|
|
while (priv->exp_uncomp_pos != fd->pos) {
|
|
|
|
rlen = len;
|
|
|
|
if (rlen > fd->pos - priv->exp_uncomp_pos)
|
|
|
|
rlen = fd->pos - priv->exp_uncomp_pos;
|
|
|
|
if (lws_fops_zip_read(fd, amount, buf, rlen))
|
|
|
|
return LWS_FZ_ERR_SEEK_COMPRESSED;
|
|
|
|
}
|
|
|
|
*amount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->inflate.avail_out = (unsigned int)len;
|
|
|
|
priv->inflate.next_out = buf;
|
|
|
|
|
|
|
|
spin:
|
|
|
|
if (!priv->inflate.avail_in) {
|
|
|
|
rlen = sizeof(priv->rbuf);
|
|
|
|
if (rlen > priv->hdr.comp_size -
|
|
|
|
(cur - priv->content_start))
|
|
|
|
rlen = priv->hdr.comp_size -
|
|
|
|
(priv->hdr.comp_size -
|
|
|
|
priv->content_start);
|
|
|
|
|
|
|
|
if (priv->zip_fop_fd->fops->LWS_FOP_READ(
|
|
|
|
priv->zip_fop_fd, &ramount, priv->rbuf,
|
|
|
|
rlen))
|
|
|
|
return LWS_FZ_ERR_READ_CONTENT;
|
|
|
|
|
|
|
|
cur += ramount;
|
|
|
|
|
|
|
|
priv->inflate.avail_in = (unsigned int)ramount;
|
|
|
|
priv->inflate.next_in = priv->rbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = inflate(&priv->inflate, Z_NO_FLUSH);
|
|
|
|
if (ret == Z_STREAM_ERROR)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
switch (ret) {
|
|
|
|
case Z_NEED_DICT:
|
|
|
|
ret = Z_DATA_ERROR;
|
2018-06-07 11:33:24 +00:00
|
|
|
/* fallthru */
|
2017-12-21 02:09:18 +00:00
|
|
|
case Z_DATA_ERROR:
|
|
|
|
case Z_MEM_ERROR:
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!priv->inflate.avail_in && priv->inflate.avail_out &&
|
|
|
|
cur != priv->content_start + priv->hdr.comp_size)
|
|
|
|
goto spin;
|
|
|
|
|
|
|
|
*amount = len - priv->inflate.avail_out;
|
|
|
|
|
|
|
|
priv->exp_uncomp_pos += *amount;
|
|
|
|
fd->pos += *amount;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->add_gzip_container) {
|
|
|
|
|
|
|
|
lwsl_info("%s: gzip + container\n", __func__);
|
|
|
|
*amount = 0;
|
|
|
|
|
|
|
|
/* place the canned header at the start */
|
|
|
|
|
|
|
|
if (len && fd->pos < sizeof(hd)) {
|
|
|
|
rlen = sizeof(hd) - fd->pos;
|
|
|
|
if (rlen > len)
|
|
|
|
rlen = len;
|
|
|
|
/* provide stuff from canned header */
|
|
|
|
memcpy(buf, hd + fd->pos, (size_t)rlen);
|
|
|
|
fd->pos += rlen;
|
|
|
|
buf += rlen;
|
|
|
|
len -= rlen;
|
|
|
|
*amount += rlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* serve gzipped data direct from zipfile */
|
|
|
|
|
|
|
|
if (len && fd->pos >= sizeof(hd) &&
|
|
|
|
fd->pos < priv->hdr.comp_size + sizeof(hd)) {
|
|
|
|
|
|
|
|
rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos -
|
|
|
|
priv->content_start);
|
|
|
|
if (rlen > len)
|
|
|
|
rlen = len;
|
|
|
|
|
|
|
|
if (rlen &&
|
|
|
|
priv->zip_fop_fd->pos < (priv->hdr.comp_size +
|
|
|
|
priv->content_start)) {
|
|
|
|
if (lws_vfs_file_read(priv->zip_fop_fd,
|
|
|
|
&ramount, buf, rlen))
|
|
|
|
return LWS_FZ_ERR_READ_CONTENT;
|
|
|
|
*amount += ramount;
|
|
|
|
fd->pos += ramount; // virtual pos
|
|
|
|
buf += ramount;
|
|
|
|
len -= ramount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* place the prepared trailer at the end */
|
|
|
|
|
|
|
|
if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) &&
|
|
|
|
fd->pos < priv->hdr.comp_size + sizeof(hd) +
|
|
|
|
sizeof(priv->u)) {
|
|
|
|
cur = fd->pos - priv->hdr.comp_size - sizeof(hd);
|
|
|
|
rlen = sizeof(priv->u) - cur;
|
|
|
|
if (rlen > len)
|
|
|
|
rlen = len;
|
|
|
|
|
|
|
|
memcpy(buf, priv->u.trailer8 + cur, (size_t)rlen);
|
|
|
|
|
|
|
|
*amount += rlen;
|
|
|
|
fd->pos += rlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwsl_info("%s: store\n", __func__);
|
|
|
|
|
|
|
|
if (len > priv->hdr.uncomp_size - (cur - priv->content_start))
|
|
|
|
len = priv->hdr.comp_size - (priv->hdr.comp_size -
|
|
|
|
priv->content_start);
|
|
|
|
|
|
|
|
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
|
|
|
|
amount, buf, len))
|
|
|
|
return LWS_FZ_ERR_READ_CONTENT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lws_plat_file_ops fops_zip = {
|
|
|
|
lws_fops_zip_open,
|
|
|
|
lws_fops_zip_close,
|
|
|
|
lws_fops_zip_seek_cur,
|
|
|
|
lws_fops_zip_read,
|
|
|
|
NULL,
|
|
|
|
{ { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
|
|
|
|
NULL,
|
|
|
|
};
|