Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,209 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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.
*/
#include "private-lib-core.h"
/*
* Produce Apache-compatible log string for wsi, like this:
*
* 2.31.234.19 - - [27/Mar/2016:03:22:44 +0800]
* "GET /aep-screen.png HTTP/1.1"
* 200 152987 "https://libwebsockets.org/index.html"
* "Mozilla/5.0 (Macint... Chrome/49.0.2623.87 Safari/537.36"
*
*/
extern const char * const method_names[];
static const char * const hver[] = {
"HTTP/1.0", "HTTP/1.1", "HTTP/2"
};
void
lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int uri_len, int meth)
{
char da[64], uri[256], ta[64];
time_t t = time(NULL);
struct lws *nwsi;
const char *me;
int l = 256, m;
struct tm *ptm = NULL;
#if defined(LWS_HAVE_LOCALTIME_R)
struct tm tm;
#endif
if (!wsi->a.vhost)
return;
/* only worry about preparing it if we store it */
if (wsi->a.vhost->log_fd == (int)LWS_INVALID_FILE)
return;
if (wsi->access_log_pending)
lws_access_log(wsi);
wsi->http.access_log.header_log = lws_malloc((unsigned int)l, "access log");
if (!wsi->http.access_log.header_log)
return;
#if defined(LWS_HAVE_LOCALTIME_R)
ptm = localtime_r(&t, &tm);
#else
ptm = localtime(&t);
#endif
if (ptm)
strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", ptm);
else
strcpy(da, "01/Jan/1970:00:00:00 +0000");
#if defined(LWS_ROLE_H2)
if (wsi->mux_substream)
me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
else
#endif
me = method_names[meth];
if (!me)
me = "(null)";
m = uri_len;
if (m > (int)sizeof(uri) - 1)
m = sizeof(uri) - 1;
strncpy(uri, uri_ptr, (unsigned int)m);
uri[m] = '\0';
nwsi = lws_get_network_wsi(wsi);
if (nwsi->sa46_peer.sa4.sin_family)
lws_sa46_write_numeric_address(&nwsi->sa46_peer, ta, sizeof(ta));
else
strncpy(ta, "unknown", sizeof(ta));
lws_snprintf(wsi->http.access_log.header_log, (size_t)l,
"%s - - [%s] \"%s %s %s\"",
ta, da, me, uri, hver[wsi->http.request_version]);
//lwsl_notice("%s\n", wsi->http.access_log.header_log);
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
if (l) {
wsi->http.access_log.user_agent =
lws_malloc((unsigned int)l + 5, "access log");
if (!wsi->http.access_log.user_agent) {
lwsl_err("OOM getting user agent\n");
lws_free_set_NULL(wsi->http.access_log.header_log);
return;
}
wsi->http.access_log.user_agent[0] = '\0';
if (lws_hdr_copy(wsi, wsi->http.access_log.user_agent, l + 4,
WSI_TOKEN_HTTP_USER_AGENT) >= 0)
for (m = 0; m < l; m++)
if (wsi->http.access_log.user_agent[m] == '\"')
wsi->http.access_log.user_agent[m] = '\'';
}
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER);
if (l) {
wsi->http.access_log.referrer = lws_malloc((unsigned int)l + 5, "referrer");
if (!wsi->http.access_log.referrer) {
lwsl_err("OOM getting referrer\n");
lws_free_set_NULL(wsi->http.access_log.user_agent);
lws_free_set_NULL(wsi->http.access_log.header_log);
return;
}
wsi->http.access_log.referrer[0] = '\0';
if (lws_hdr_copy(wsi, wsi->http.access_log.referrer,
l + 4, WSI_TOKEN_HTTP_REFERER) >= 0)
for (m = 0; m < l; m++)
if (wsi->http.access_log.referrer[m] == '\"')
wsi->http.access_log.referrer[m] = '\'';
}
wsi->access_log_pending = 1;
}
int
lws_access_log(struct lws *wsi)
{
char *p = wsi->http.access_log.user_agent, ass[512],
*p1 = wsi->http.access_log.referrer;
int l;
if (!wsi->a.vhost)
return 0;
if (wsi->a.vhost->log_fd == (int)LWS_INVALID_FILE)
return 0;
if (!wsi->access_log_pending)
return 0;
if (!wsi->http.access_log.header_log)
return 0;
if (!p)
p = "";
if (!p1)
p1 = "";
/*
* We do this in two parts to restrict an oversize referrer such that
* we will always have space left to append an empty useragent, while
* maintaining the structure of the log text
*/
l = lws_snprintf(ass, sizeof(ass) - 7, "%s %d %lu \"%s",
wsi->http.access_log.header_log,
wsi->http.access_log.response,
wsi->http.access_log.sent, p1);
if (strlen(p) > sizeof(ass) - 6 - (unsigned int)l) {
p[sizeof(ass) - 6 - (unsigned int)l] = '\0';
l--;
}
l += lws_snprintf(ass + (unsigned int)l, sizeof(ass) - 1 - (unsigned int)l, "\" \"%s\"\n", p);
ass[sizeof(ass) - 1] = '\0';
if ((int)write(wsi->a.vhost->log_fd, ass, (size_t)l) != l)
lwsl_err("Failed to write log\n");
if (wsi->http.access_log.header_log) {
lws_free(wsi->http.access_log.header_log);
wsi->http.access_log.header_log = NULL;
}
if (wsi->http.access_log.user_agent) {
lws_free(wsi->http.access_log.user_agent);
wsi->http.access_log.user_agent = NULL;
}
if (wsi->http.access_log.referrer) {
lws_free(wsi->http.access_log.referrer);
wsi->http.access_log.referrer = NULL;
}
wsi->access_log_pending = 0;
return 0;
}

View File

@ -0,0 +1,657 @@
/*
* 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.
*
* Somewhat rewritten by AG
*/
#include "private-lib-core.h"
#if defined(LWS_WITH_MINIZ)
#include <miniz.h>
#else
#include <zlib.h>
#endif
/*
* 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,
};
#define eff_size(_priv) (_priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE ? \
_priv->hdr.uncomp_size : _priv->hdr.comp_size)
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;
if (len >= (int)sizeof(buf) - 1)
return LWS_FZ_ERR_NAME_TOO_LONG;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
&amount, buf, (unsigned int)len))
return LWS_FZ_ERR_NAME_READ;
if ((int)amount != len)
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,
(lws_fileofs_t)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,
(lws_fileofs_t)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, (lws_fileofs_t)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_own,
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)
m = lws_ptr_diff(vpath, vfs_path) - 1;
lws_strncpy(rp, vfs_path, (unsigned int)m + 1);
/* open the zip file itself using the incoming fops, not fops_zip */
priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops_own, fops, rp, NULL, &local_flags);
if (!priv->zip_fop_fd) {
lwsl_err("%s: unable to open zip %s\n", __func__, rp);
goto bail1;
}
if (*vpath == '/')
vpath++;
m = lws_fops_zip_scan(priv, vpath, (int)strlen(vpath));
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 = (lws_filepos_t)((lws_fileofs_t)fd->pos + offset_from_cur_pos);
return (lws_fileofs_t)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 > eff_size(priv) - (cur - priv->content_start))
rlen = eff_size(priv) - (cur - 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;
/* fallthru */
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 > eff_size(priv) - cur)
len = eff_size(priv) - cur;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
amount, buf, len))
return LWS_FZ_ERR_READ_CONTENT;
fd->pos += *amount;
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,
NULL,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,725 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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.
*/
#include "private-lib-core.h"
#define LWS_MAX_ELEM_NAME 32
enum urldecode_stateful {
US_NAME,
US_IDLE,
US_PC1,
US_PC2,
MT_LOOK_BOUND_IN,
MT_HNAME,
MT_DISP,
MT_TYPE,
MT_IGNORE1,
MT_IGNORE2,
MT_IGNORE3,
MT_COMPLETED,
};
static struct mp_hdr {
const char * const hdr;
uint8_t hdr_len;
} mp_hdrs[] = {
{ "content-disposition: ", 21 },
{ "content-type: ", 14 },
{ "\x0d\x0a", 2 }
};
struct lws_spa;
typedef int (*lws_urldecode_stateful_cb)(struct lws_spa *spa,
const char *name, char **buf, int len, int final);
struct lws_urldecode_stateful {
char *out;
struct lws_spa *data;
struct lws *wsi;
char name[LWS_MAX_ELEM_NAME];
char temp[LWS_MAX_ELEM_NAME];
char content_type[32];
char content_disp[32];
char content_disp_filename[256];
char mime_boundary[128];
int out_len;
int pos;
int hdr_idx;
int mp;
int sum;
uint8_t matchable;
uint8_t multipart_form_data:1;
uint8_t inside_quote:1;
uint8_t subname:1;
uint8_t boundary_real_crlf:1;
enum urldecode_stateful state;
lws_urldecode_stateful_cb output;
};
struct lws_spa {
struct lws_urldecode_stateful *s;
lws_spa_create_info_t i;
int *param_length;
char finalized;
char **params;
char *storage;
char *end;
};
static struct lws_urldecode_stateful *
lws_urldecode_s_create(struct lws_spa *spa, struct lws *wsi, char *out,
int out_len, lws_urldecode_stateful_cb output)
{
struct lws_urldecode_stateful *s;
char buf[205], *p;
int m = 0;
if (spa->i.ac)
s = lwsac_use_zero(spa->i.ac, sizeof(*s), spa->i.ac_chunk_size);
else
s = lws_zalloc(sizeof(*s), "stateful urldecode");
if (!s)
return NULL;
s->out = out;
s->out_len = out_len;
s->output = output;
s->pos = 0;
s->sum = 0;
s->mp = 0;
s->state = US_NAME;
s->name[0] = '\0';
s->data = spa;
s->wsi = wsi;
if (lws_hdr_copy(wsi, buf, sizeof(buf),
WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
/* multipart/form-data;
* boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
if (!strncmp(buf, "multipart/form-data", 19) ||
!strncmp(buf, "multipart/related", 17)) {
s->multipart_form_data = 1;
s->state = MT_LOOK_BOUND_IN;
s->mp = 2;
p = strstr(buf, "boundary=");
if (p) {
p += 9;
s->mime_boundary[m++] = '\x0d';
s->mime_boundary[m++] = '\x0a';
s->mime_boundary[m++] = '-';
s->mime_boundary[m++] = '-';
if (*p == '\"')
p++;
while (m < (int)sizeof(s->mime_boundary) - 1 &&
*p && *p != ' ' && *p != ';' && *p != '\"')
s->mime_boundary[m++] = *p++;
s->mime_boundary[m] = '\0';
// lwsl_notice("boundary '%s'\n", s->mime_boundary);
}
}
}
return s;
}
static int
lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in,
int len)
{
int n, hit;
char c;
while (len--) {
if (s->pos == s->out_len - s->mp - 1) {
if (s->output(s->data, s->name, &s->out, s->pos,
LWS_UFS_CONTENT))
return -1;
s->pos = 0;
}
switch (s->state) {
/* states for url arg style */
case US_NAME:
s->inside_quote = 0;
if (*in == '=') {
s->name[s->pos] = '\0';
s->pos = 0;
s->state = US_IDLE;
in++;
continue;
}
if (*in == '&') {
s->name[s->pos] = '\0';
if (s->output(s->data, s->name, &s->out,
s->pos, LWS_UFS_FINAL_CONTENT))
return -1;
s->pos = 0;
s->state = US_IDLE;
in++;
continue;
}
if (s->pos >= (int)sizeof(s->name) - 1) {
lwsl_hexdump_notice(s->name, (size_t)s->pos);
lwsl_notice("Name too long...\n");
return -1;
}
s->name[s->pos++] = *in++;
break;
case US_IDLE:
if (*in == '%') {
s->state++;
in++;
continue;
}
if (*in == '&') {
s->out[s->pos] = '\0';
if (s->output(s->data, s->name, &s->out,
s->pos, LWS_UFS_FINAL_CONTENT))
return -1;
s->pos = 0;
s->state = US_NAME;
in++;
continue;
}
if (*in == '+') {
in++;
s->out[s->pos++] = ' ';
continue;
}
s->out[s->pos++] = *in++;
break;
case US_PC1:
n = char_to_hex(*in);
if (n < 0)
return -1;
in++;
s->sum = n << 4;
s->state++;
break;
case US_PC2:
n = char_to_hex(*in);
if (n < 0)
return -1;
in++;
s->out[s->pos++] = (char)(s->sum | n);
s->state = US_IDLE;
break;
/* states for multipart / mime style */
case MT_LOOK_BOUND_IN:
retry_as_first:
if (*in == s->mime_boundary[s->mp] &&
s->mime_boundary[s->mp]) {
in++;
s->mp++;
if (!s->mime_boundary[s->mp]) {
s->mp = 0;
s->state = MT_IGNORE1;
if (s->output(s->data, s->name,
&s->out, s->pos,
LWS_UFS_FINAL_CONTENT))
return -1;
s->pos = 0;
s->content_disp[0] = '\0';
s->name[0] = '\0';
s->content_disp_filename[0] = '\0';
s->boundary_real_crlf = 1;
}
continue;
}
if (s->mp) {
n = 0;
if (!s->boundary_real_crlf)
n = 2;
if (s->mp >= n) {
memcpy(s->out + s->pos,
s->mime_boundary + n,
(unsigned int)(s->mp - n));
s->pos += s->mp;
s->mp = 0;
goto retry_as_first;
}
}
s->out[s->pos++] = *in;
in++;
s->mp = 0;
break;
case MT_HNAME:
c =*in;
if (c >= 'A' && c <= 'Z')
c = (char)(c + 'a' - 'A');
if (!s->mp)
/* initially, any of them might match */
s->matchable = (1 << LWS_ARRAY_SIZE(mp_hdrs)) - 1;
hit = -1;
for (n = 0; n < (int)LWS_ARRAY_SIZE(mp_hdrs); n++) {
if (!(s->matchable & (1 << n)))
continue;
/* this guy is still in contention... */
if (s->mp >= mp_hdrs[n].hdr_len) {
/* he went past the end of it */
s->matchable &= (uint8_t)~(1 << n);
continue;
}
if (c != mp_hdrs[n].hdr[s->mp]) {
/* mismatched a char */
s->matchable &= (uint8_t)~(1 << n);
continue;
}
if (s->mp + 1 == mp_hdrs[n].hdr_len) {
/* we have a winner... */
hit = n;
break;
}
}
in++;
if (hit == -1 && !s->matchable) {
/* We ruled them all out */
s->state = MT_IGNORE1;
s->mp = 0;
continue;
}
s->mp++;
if (hit < 0)
continue;
/* we matched the one in hit */
s->mp = 0;
s->temp[0] = '\0';
s->subname = 0;
if (hit == 2)
s->state = MT_LOOK_BOUND_IN;
else
s->state += (unsigned int)hit + 1u;
break;
case MT_DISP:
/* form-data; name="file"; filename="t.txt" */
if (*in == '\x0d') {
if (s->content_disp_filename[0])
if (s->output(s->data, s->name,
&s->out, s->pos,
LWS_UFS_OPEN))
return -1;
s->state = MT_IGNORE2;
goto done;
}
if (*in == ';') {
s->subname = 1;
s->temp[0] = '\0';
s->mp = 0;
goto done;
}
if (*in == '\"') {
s->inside_quote = !!((s->inside_quote ^ 1) & 1);
goto done;
}
if (s->subname) {
if (*in == '=') {
s->temp[s->mp] = '\0';
s->subname = 0;
s->mp = 0;
goto done;
}
if (s->mp < (int)sizeof(s->temp) - 1 &&
(*in != ' ' || s->inside_quote))
s->temp[s->mp++] = *in;
goto done;
}
if (!s->temp[0]) {
if (s->mp < (int)sizeof(s->content_disp) - 1)
s->content_disp[s->mp++] = *in;
if (s->mp < (int)sizeof(s->content_disp))
s->content_disp[s->mp] = '\0';
goto done;
}
if (!strcmp(s->temp, "name")) {
if (s->mp < (int)sizeof(s->name) - 1)
s->name[s->mp++] = *in;
else
s->mp = (int)sizeof(s->name) - 1;
s->name[s->mp] = '\0';
goto done;
}
if (!strcmp(s->temp, "filename")) {
if (s->mp < (int)sizeof(s->content_disp_filename) - 1)
s->content_disp_filename[s->mp++] = *in;
s->content_disp_filename[s->mp] = '\0';
goto done;
}
done:
in++;
break;
case MT_TYPE:
if (*in == '\x0d')
s->state = MT_IGNORE2;
else {
if (s->mp < (int)sizeof(s->content_type) - 1)
s->content_type[s->mp++] = *in;
s->content_type[s->mp] = '\0';
}
in++;
break;
case MT_IGNORE1:
if (*in == '\x0d')
s->state = MT_IGNORE2;
if (*in == '-')
s->state = MT_IGNORE3;
in++;
break;
case MT_IGNORE2:
s->mp = 0;
if (*in == '\x0a')
s->state = MT_HNAME;
in++;
break;
case MT_IGNORE3:
if (*in == '\x0d')
s->state = MT_IGNORE2;
if (*in == '-') {
s->state = MT_COMPLETED;
s->wsi->http.rx_content_remain = 0;
}
in++;
break;
case MT_COMPLETED:
break;
}
}
return 0;
}
static int
lws_urldecode_s_destroy(struct lws_spa *spa, struct lws_urldecode_stateful *s)
{
int ret = 0;
if (s->state != US_IDLE)
ret = -1;
if (!ret)
if (s->output(s->data, s->name, &s->out, s->pos,
LWS_UFS_FINAL_CONTENT))
ret = -1;
if (s->output(s->data, s->name, NULL, 0, LWS_UFS_CLOSE))
return -1;
if (!spa->i.ac)
lws_free(s);
return ret;
}
static int
lws_urldecode_spa_lookup(struct lws_spa *spa, const char *name)
{
const char * const *pp = spa->i.param_names;
int n;
for (n = 0; n < spa->i.count_params; n++) {
if (!*pp && !spa->i.param_names_stride && spa->i.ac) {
unsigned int len = (unsigned int)strlen(name);
char **ptr = (char**)spa->i.param_names;
/* Use NULLs at end of list to dynamically create
* unknown entries */
ptr[n] = lwsac_use(spa->i.ac, len + 1, spa->i.ac_chunk_size);
if (!ptr[n])
return -1;
memcpy(ptr[n], name, len);
ptr[n][len] = '\0';
return n;
}
if (*pp && !strcmp(*pp, name))
return n;
if (spa->i.param_names_stride)
pp = (const char * const *)(((char *)pp) + spa->i.param_names_stride);
else
pp++;
}
return -1;
}
static int
lws_urldecode_spa_cb(struct lws_spa *spa, const char *name, char **buf, int len,
int final)
{
int n;
if (final == LWS_UFS_CLOSE || spa->s->content_disp_filename[0]) {
if (spa->i.opt_cb) {
n = spa->i.opt_cb(spa->i.opt_data, name,
spa->s->content_disp_filename,
buf ? *buf : NULL, len, (enum lws_spa_fileupload_states)final);
if (n < 0)
return -1;
}
return 0;
}
n = lws_urldecode_spa_lookup(spa, name);
if (n == -1 || !len) /* unrecognized */
return 0;
if (!spa->i.ac) {
if (!spa->params[n])
spa->params[n] = *buf;
if ((*buf) + len >= spa->end) {
lwsl_info("%s: exceeded storage\n", __func__);
return -1;
}
/* move it on inside storage */
(*buf) += len;
*((*buf)++) = '\0';
spa->s->out_len -= len + 1;
} else {
spa->params[n] = lwsac_use(spa->i.ac, (unsigned int)len + 1,
spa->i.ac_chunk_size);
if (!spa->params[n])
return -1;
memcpy(spa->params[n], *buf, (unsigned int)len);
spa->params[n][len] = '\0';
}
spa->param_length[n] += len;
return 0;
}
struct lws_spa *
lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *i)
{
struct lws_spa *spa;
if (i->ac)
spa = lwsac_use_zero(i->ac, sizeof(*spa), i->ac_chunk_size);
else
spa = lws_zalloc(sizeof(*spa), "spa");
if (!spa)
return NULL;
spa->i = *i;
if (!spa->i.max_storage)
spa->i.max_storage = 512;
if (i->ac)
spa->storage = lwsac_use(i->ac, (unsigned int)spa->i.max_storage,
i->ac_chunk_size);
else
spa->storage = lws_malloc((unsigned int)spa->i.max_storage, "spa");
if (!spa->storage)
goto bail2;
spa->end = spa->storage + spa->i.max_storage - 1;
if (i->count_params) {
if (i->ac)
spa->params = lwsac_use_zero(i->ac,
sizeof(char *) * (unsigned int)i->count_params, i->ac_chunk_size);
else
spa->params = lws_zalloc(sizeof(char *) * (unsigned int)i->count_params,
"spa params");
if (!spa->params)
goto bail3;
}
spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, spa->i.max_storage,
lws_urldecode_spa_cb);
if (!spa->s)
goto bail4;
if (i->count_params) {
if (i->ac)
spa->param_length = lwsac_use_zero(i->ac,
sizeof(int) * (unsigned int)i->count_params, i->ac_chunk_size);
else
spa->param_length = lws_zalloc(sizeof(int) * (unsigned int)i->count_params,
"spa param len");
if (!spa->param_length)
goto bail5;
}
// lwsl_notice("%s: Created SPA %p\n", __func__, spa);
return spa;
bail5:
lws_urldecode_s_destroy(spa, spa->s);
bail4:
if (!i->ac)
lws_free(spa->params);
bail3:
if (!i->ac)
lws_free(spa->storage);
bail2:
if (!i->ac)
lws_free(spa);
if (i->ac)
lwsac_free(i->ac);
return NULL;
}
struct lws_spa *
lws_spa_create(struct lws *wsi, const char * const *param_names,
int count_params, int max_storage,
lws_spa_fileupload_cb opt_cb, void *opt_data)
{
lws_spa_create_info_t i;
memset(&i, 0, sizeof(i));
i.count_params = count_params;
i.max_storage = max_storage;
i.opt_cb = opt_cb;
i.opt_data = opt_data;
i.param_names = param_names;
return lws_spa_create_via_info(wsi, &i);
}
int
lws_spa_process(struct lws_spa *spa, const char *in, int len)
{
if (!spa) {
lwsl_err("%s: NULL spa\n", __func__);
return -1;
}
/* we reject any junk after the last part arrived and we finalized */
if (spa->finalized)
return 0;
return lws_urldecode_s_process(spa->s, in, len);
}
int
lws_spa_get_length(struct lws_spa *spa, int n)
{
if (n >= spa->i.count_params)
return 0;
return spa->param_length[n];
}
const char *
lws_spa_get_string(struct lws_spa *spa, int n)
{
if (n >= spa->i.count_params)
return NULL;
return spa->params[n];
}
int
lws_spa_finalize(struct lws_spa *spa)
{
if (!spa)
return 0;
if (spa->s) {
lws_urldecode_s_destroy(spa, spa->s);
spa->s = NULL;
}
spa->finalized = 1;
return 0;
}
int
lws_spa_destroy(struct lws_spa *spa)
{
int n = 0;
lwsl_info("%s: destroy spa %p\n", __func__, spa);
if (spa->s)
lws_urldecode_s_destroy(spa, spa->s);
if (spa->i.ac)
lwsac_free(spa->i.ac);
else {
lws_free(spa->param_length);
lws_free(spa->params);
lws_free(spa->storage);
lws_free(spa);
}
return n;
}

View File

@ -0,0 +1,214 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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.
*/
#include "private-lib-core.h"
/*
* RFC7233 examples
*
* o The first 500 bytes (byte offsets 0-499, inclusive):
*
* bytes=0-499
*
* o The second 500 bytes (byte offsets 500-999, inclusive):
*
* bytes=500-999
*
* o The final 500 bytes (byte offsets 9500-9999, inclusive):
*
* bytes=-500
*
* Or:
*
* bytes=9500-
*
* o The first and last bytes only (bytes 0 and 9999):
*
* bytes=0-0,-1
*
* o Other valid (but not canonical) specifications of the second 500
* bytes (byte offsets 500-999, inclusive):
*
* bytes=500-600,601-999
* bytes=500-700,601-999
*/
/*
* returns 1 if the range struct represents a usable range
* if no ranges header, you get one of these for the whole
* file. Otherwise you get one for each valid range in the
* header.
*
* returns 0 if no further valid range forthcoming; rp->state
* may be LWSRS_SYNTAX or LWSRS_COMPLETED
*/
int
lws_ranges_next(struct lws_range_parsing *rp)
{
static const char * const beq = "bytes=";
while (1) {
char c = rp->buf[rp->pos];
switch (rp->state) {
case LWSRS_SYNTAX:
case LWSRS_COMPLETED:
return 0;
case LWSRS_NO_ACTIVE_RANGE:
rp->state = LWSRS_COMPLETED;
return 0;
case LWSRS_BYTES_EQ: // looking for "bytes="
if (c != beq[rp->pos]) {
rp->state = LWSRS_SYNTAX;
return -1;
}
if (rp->pos == 5)
rp->state = LWSRS_FIRST;
break;
case LWSRS_FIRST:
rp->start = 0;
rp->end = 0;
rp->start_valid = 0;
rp->end_valid = 0;
rp->state = LWSRS_STARTING;
// fallthru
case LWSRS_STARTING:
if (c == '-') {
rp->state = LWSRS_ENDING;
break;
}
if (!(c >= '0' && c <= '9')) {
rp->state = LWSRS_SYNTAX;
return 0;
}
rp->start = (unsigned long long)(((unsigned long long)rp->start * 10) + (unsigned long long)(c - '0'));
rp->start_valid = 1;
break;
case LWSRS_ENDING:
if (c == ',' || c == '\0') {
rp->state = LWSRS_FIRST;
if (c == ',')
rp->pos++;
/*
* By the end of this, start and end are
* always valid if the range still is
*/
if (!rp->start_valid) { /* eg, -500 */
if (rp->end > rp->extent)
rp->end = rp->extent;
rp->start = rp->extent - rp->end;
rp->end = rp->extent - 1;
} else
if (!rp->end_valid)
rp->end = rp->extent - 1;
rp->did_try = 1;
/* end must be >= start or ignore it */
if (rp->end < rp->start) {
if (c == ',')
break;
rp->state = LWSRS_COMPLETED;
return 0;
}
return 1; /* issue range */
}
if (!(c >= '0' && c <= '9')) {
rp->state = LWSRS_SYNTAX;
return 0;
}
rp->end = (unsigned long long)(((unsigned long long)rp->end * 10) + (unsigned long long)(c - '0'));
rp->end_valid = 1;
break;
}
rp->pos++;
}
}
void
lws_ranges_reset(struct lws_range_parsing *rp)
{
rp->pos = 0;
rp->ctr = 0;
rp->start = 0;
rp->end = 0;
rp->start_valid = 0;
rp->end_valid = 0;
rp->state = LWSRS_BYTES_EQ;
}
/*
* returns count of valid ranges
*/
int
lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
unsigned long long extent)
{
rp->agg = 0;
rp->send_ctr = 0;
rp->inside = 0;
rp->count_ranges = 0;
rp->did_try = 0;
lws_ranges_reset(rp);
rp->state = LWSRS_COMPLETED;
rp->extent = extent;
if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf),
WSI_TOKEN_HTTP_RANGE) <= 0)
return 0;
rp->state = LWSRS_BYTES_EQ;
while (lws_ranges_next(rp)) {
rp->count_ranges++;
rp->agg += rp->end - rp->start + 1;
}
lwsl_debug("%s: count %d\n", __func__, rp->count_ranges);
lws_ranges_reset(rp);
if (rp->did_try && !rp->count_ranges)
return -1; /* "not satisfiable */
lws_ranges_next(rp);
return rp->count_ranges;
}

View File

@ -0,0 +1,55 @@
#include "private-lib-core.h"
#if defined(LWS_WITH_HUBBUB)
struct lws_rewrite *
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from,
const char *to)
{
struct lws_rewrite *r = lws_malloc(sizeof(*r), "rewrite");
if (!r) {
lwsl_err("OOM\n");
return NULL;
}
if (hubbub_parser_create("UTF-8", false, &r->parser) != HUBBUB_OK) {
lws_free(r);
return NULL;
}
r->from = from;
r->from_len = strlen(from);
r->to = to;
r->to_len = strlen(to);
r->params.token_handler.handler = cb;
r->wsi = wsi;
r->params.token_handler.pw = (void *)r;
if (hubbub_parser_setopt(r->parser, HUBBUB_PARSER_TOKEN_HANDLER,
&r->params) != HUBBUB_OK) {
lws_free(r);
return NULL;
}
return r;
}
int
lws_rewrite_parse(struct lws_rewrite *r,
const unsigned char *in, int in_len)
{
if (r && hubbub_parser_parse_chunk(r->parser, in, in_len) != HUBBUB_OK)
return -1;
return 0;
}
void
lws_rewrite_destroy(struct lws_rewrite *r)
{
hubbub_parser_destroy(r->parser);
lws_free(r);
}
#endif

File diff suppressed because it is too large Load Diff