From a6917bd56337db43cb941d6c300f08c62c923f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 26 Jan 2023 23:03:34 +0100 Subject: [PATCH] code cleaning, add extract option to fs builder, fixes --- demo/Makefile | 2 +- espfsbuilder/Makefile | 3 +- espfsbuilder/main.c | 45 +++++++----- espfsbuilder/parsing.c | 110 ++++++++++++++++++++++++++-- espfsbuilder/parsing.h | 2 +- espfsbuilder/testfiles/LIMECURD.TXT | 7 ++ espfsbuilder/testfiles/mouse.jpg | Bin 0 -> 15346 bytes espfsbuilder/testout/.gitignore | 2 + spritehttpd/Makefile | 3 +- spritehttpd/include/cgiwebsocket.h | 10 +-- spritehttpd/include/httpd.h | 49 +++++++++++-- spritehttpd/lib/espfs/espfs.c | 105 ++++++++++++++------------ spritehttpd/lib/espfs/espfs.h | 11 ++- spritehttpd/src/cgiwebsocket.c | 28 +++---- spritehttpd/src/httpd.c | 106 ++++++++++++++------------- spritehttpd/src/httpdespfs.c | 28 ++++--- 16 files changed, 349 insertions(+), 162 deletions(-) create mode 100644 espfsbuilder/testfiles/LIMECURD.TXT create mode 100755 espfsbuilder/testfiles/mouse.jpg create mode 100644 espfsbuilder/testout/.gitignore diff --git a/demo/Makefile b/demo/Makefile index c822cd7..2867c1d 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -22,7 +22,7 @@ staticfiles-embed.c: staticfiles.bin clean: rm -f demo staticfiles.bin staticfiles-embed.c - make -C ../spritehttpd + make -C ../spritehttpd clean $(LIBFILE): make -C ../spritehttpd PLATFORM=posix CFLAGS="-Og -g" diff --git a/espfsbuilder/Makefile b/espfsbuilder/Makefile index e1790df..6d098a8 100644 --- a/espfsbuilder/Makefile +++ b/espfsbuilder/Makefile @@ -13,7 +13,8 @@ CFLAGS = -I. \ all: $(TARGET) $(TARGET): ${SOURCES} - cc -O3 -lz $^ -o $@ ${CFLAGS} + #cc -O3 -lz $^ -o $@ ${CFLAGS} + cc -Og -g -Wall -Wextra -lz $^ -o $@ ${CFLAGS} clean: rm $(TARGET) diff --git a/espfsbuilder/main.c b/espfsbuilder/main.c index d42592c..1ada5bd 100644 --- a/espfsbuilder/main.c +++ b/espfsbuilder/main.c @@ -62,8 +62,8 @@ size_t compressHeatshrink(const uint8_t *in, size_t insize, uint8_t *out, size_t size_t len; int ws[] = {5, 6, 8, 11, 13}; int ls[] = {3, 3, 4, 4, 4}; - HSE_poll_res pres; - HSE_sink_res sres; + HSE_poll_res pres = 0; + HSE_sink_res sres = 0; size_t r; if (level == -1) { level = 8; } level = (level - 1) / 2; //level is now 0, 1, 2, 3, 4 @@ -358,7 +358,6 @@ int main(int argc, char **argv) { int f; char inputFileName[1024]; - char *realName; struct stat statBuf; int serr; int rate; @@ -373,11 +372,13 @@ int main(int argc, char **argv) char *outfile = NULL; char *parseFile = NULL; char *stripPath = NULL; + char *extractFile = NULL; while (1) { int option_index = 0; static struct option long_options[] = { {"parse", required_argument, 0, 'p'}, + {"extract", required_argument, 0, 'e'}, {"compress", required_argument, 0, 'c'}, {"gzip", no_argument, 0, 'z'}, {"gzip-all", no_argument, 0, 'G'}, @@ -390,7 +391,7 @@ int main(int argc, char **argv) {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "c:l:g:zGhp:i:o:0123456789", + c = getopt_long(argc, argv, "c:l:g:zGS:e:hp:i:o:0123456789", long_options, &option_index); if (c == -1) { break; @@ -412,6 +413,10 @@ int main(int argc, char **argv) parseFile = strdup(optarg); break; + case 'e': + extractFile = strdup(optarg); + break; + case 'S': stripPath = strdup(optarg); break; @@ -457,8 +462,22 @@ int main(int argc, char **argv) } } + FILE *outfp = NULL; + if (outfile) { + fprintf(stderr, "Writing to %s\n", outfile); + outfp = fopen(outfile, "w+"); + if (!outfp) { + perror(outfile); + return 1; + } + s_outFd = fileno(outfp); + ftruncate(s_outFd, 0); + } else { + fprintf(stderr, "Writing to stdout\n\n"); + } + if (parseFile) { - parseEspfsFileAndShowItsContents(parseFile); + parseEspfsImage(parseFile, extractFile, s_outFd); exit(0); } @@ -481,20 +500,6 @@ int main(int argc, char **argv) } } - FILE *outfp = NULL; - if (outfile) { - fprintf(stderr, "Writing to %s\n", outfile); - outfp = fopen(outfile, "w+"); - if (!outfp) { - perror(outfile); - return 1; - } - s_outFd = fileno(outfp); - ftruncate(s_outFd, 0); - } else { - fprintf(stderr, "Writing to stdout\n\n"); - } - struct InputFileLinkedListEntry *entry = s_inputFiles; while (entry) { char *name = entry->name; @@ -545,6 +550,8 @@ int main(int argc, char **argv) fprintf(stderr, "%s - Program to create espfs images\n", argv[0]); fprintf(stderr, "Options:\n"); fprintf(stderr, "[-p|--parse FILE]\n Parse an espfs file and show a list of its contents. No other options apply in this mode.\n"); + fprintf(stderr, "[-e|--extract FILE]\n Extract a file with the given name from the parsed file (-p)\n"); + fprintf(stderr, "[-S|--strip-path PATH]\n Remove the given path from input file names before packing\n"); fprintf(stderr, "[-c|--compress COMPRESSOR]\n 0 - None, 1 - Heatshrink (default)\n"); fprintf(stderr, "[-l|--level LEVEL] or [-0 through -9]\n compression level 1-9, higher is better but uses more RAM\n"); fprintf(stderr, "[-z|--gzip]\n use gzip for files with extensions matching "DEFAULT_GZIP_EXTS"\n"); diff --git a/espfsbuilder/parsing.c b/espfsbuilder/parsing.c index c7f236e..22e6ab4 100644 --- a/espfsbuilder/parsing.c +++ b/espfsbuilder/parsing.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "parsing.h" #include "espfs.h" @@ -9,20 +10,78 @@ static size_t espfs_parse_filesize = -1; static int espfs_parse_fd = -1; +size_t decompressGzip(const uint8_t *in, size_t insize, int outfd) +{ +#define OUTBUF_LEN 10240 + uint8_t outbuf[OUTBUF_LEN]; + + z_stream stream; + int zresult; + + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.next_in = in; + stream.avail_in = insize; + stream.next_out = outbuf; + stream.avail_out = OUTBUF_LEN; + // 31 -> 15 window bits + 16 for gzip + zresult = inflateInit2(&stream, 31); + if (zresult != Z_OK) { + fprintf(stderr, "InflateInit2 failed with code %d\n", zresult); + exit(1); + } + + while (1) { + stream.avail_out = OUTBUF_LEN; + stream.next_out = outbuf; + + fprintf(stderr, "inflate chunk\n"); + + zresult = inflate(&stream, Z_FINISH); + if (zresult == Z_BUF_ERROR || zresult == Z_OK || zresult == Z_STREAM_END) { + int have = OUTBUF_LEN - stream.avail_out; + fprintf(stderr, "inflated: %d\n", have); + if (have != write(outfd, outbuf, have)) { + perror("Write output"); + exit(1); + } + if (zresult == Z_STREAM_END) { + fprintf(stderr, "Z_STREAM_END\n"); + break; + } + } else { + fprintf(stderr, "gzip error: %d\n", zresult); + exit(1); + } + } + + zresult = inflateEnd(&stream); + if (zresult != Z_OK) { + fprintf(stderr, "InflateEnd failed with code %d\n", zresult); + exit(1); + } + + fprintf(stderr, "Total decoded = %d\n", (int) stream.total_out); + return stream.total_out; +} + /** * Parse an image file and show the files contained. * This is a simple sanity test. * - * @param[in] filename - image file to parse + * @param[in] imagefile - image file to parse + * @param[in] extractfile - name of the file to extract + * @param outfd - output FD when extracting */ -void parseEspfsFileAndShowItsContents(const char *filename) +void parseEspfsImage(const char *imagefile, const char *extractfile, int outfd) { int rv; - fprintf(stderr, "Parsing: %s\n", filename); + fprintf(stderr, "Parsing: %s\n", imagefile); - FILE *f = fopen(filename, "r"); + FILE *f = fopen(imagefile, "r"); if (!f) { - perror(filename); + perror(imagefile); exit(1); } int fd = fileno(f); @@ -38,6 +97,45 @@ void parseEspfsFileAndShowItsContents(const char *filename) exit(1); } + if (extractfile) { + EspFsFile * efile = espFsOpen(extractfile); + EspFsHeader hdr; + + if (!efile) { + fprintf(stderr, "Fail to open file %s from image\n", extractfile); + exit(1); + } + + rv = espFsFileReadHeader(efile, &hdr); + if (rv != 0) { + fprintf(stderr, "Fail to read file header\n"); + exit(1); + } + + bool isGzip = hdr.flags & FLAG_GZIP; + + size_t expected_readlen = isGzip ? hdr.fileLenComp : hdr.fileLenDecomp; + + uint8_t *buff = malloc(expected_readlen); + int lenRead = espFsRead(efile, buff, expected_readlen); + if (lenRead != (int) expected_readlen) { + fprintf(stderr, "Fail to read raw file from espfs image - read len %d", lenRead); + exit(1); + } + + if (isGzip) { + fprintf(stderr, "[EspFS] File is gzipped!"); + decompressGzip(buff, lenRead, outfd); + } else { + write(outfd, buff, lenRead); + } + + fsync(outfd); + exit(0); + } + + /* Walk the image */ + EspFsWalk walk; espFsWalkInit(&walk); @@ -58,7 +156,7 @@ int httpdPlatEspfsRead(void *dest, uint32_t offset, size_t len) { // fprintf(stderr, "FS read @ %d, len %d\n", offset, (int) len); if (offset + len > espfs_parse_filesize) { - // fprintf(stderr, "Read out fo range!\n"); + fprintf(stderr, "Read out fo range!\n"); return -1; } lseek(espfs_parse_fd, offset, SEEK_SET); diff --git a/espfsbuilder/parsing.h b/espfsbuilder/parsing.h index 5b56a61..ba3f766 100644 --- a/espfsbuilder/parsing.h +++ b/espfsbuilder/parsing.h @@ -4,4 +4,4 @@ #pragma once -void parseEspfsFileAndShowItsContents(const char *filename); +void parseEspfsImage(const char *imagefile, const char *extractfile, int outfd); diff --git a/espfsbuilder/testfiles/LIMECURD.TXT b/espfsbuilder/testfiles/LIMECURD.TXT new file mode 100644 index 0000000..41c0f69 --- /dev/null +++ b/espfsbuilder/testfiles/LIMECURD.TXT @@ -0,0 +1,7 @@ +Fruit curd is a dessert spread and topping usually made with citrus fruit, such as lemon, lime, orange, or tangerine. Other flavor variations include passion fruit, mango, and berries such as raspberries, cranberries or blackberries. The basic ingredients are beaten egg yolks, sugar, fruit juice, and zest, which are gently cooked together until thick and then allowed to cool, forming a soft, smooth, intensely flavoured spread. Some recipes also include egg whites and/or butter. + +In late 19th and early 20th century England, home-made lemon curd was traditionally served with bread or scones at afternoon tea as an alternative to jam, and as a filling for cakes, small pastries, and tarts. Homemade lemon curd was usually made in relatively small amounts as it did not keep as well as jam. In more modern times, larger quantities became possible because of the use of refrigeration. Commercially manufactured curds often contain additional preservatives and thickening agents. + +Contemporary commercially made curds remain a popular spread for bread, scones, toast, waffles, crumpets, pancakes, cheesecake, or muffins. They can also be used as a flavoring for desserts or yoghurt. Lemon-meringue pie, made with lemon curd and topped with meringue, has been a popular dessert in Britain and the United States since the nineteenth century. Lemon curd can also have whipped cream folded into it for such uses as filling cream puffs. + +Curds differ from pie fillings or custards in that they contain a higher proportion of juice and zest, which gives them a more intense flavor. Also, curds containing butter have a smoother and creamier texture than both pie fillings and custards, which contain little or no butter and use cornstarch or flour for thickening. Additionally, unlike custards, curds are not usually eaten on their own. diff --git a/espfsbuilder/testfiles/mouse.jpg b/espfsbuilder/testfiles/mouse.jpg new file mode 100755 index 0000000000000000000000000000000000000000..478f22e7ec72eba083c8f8d26c6be6f7f445b445 GIT binary patch literal 15346 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 literal 0 HcmV?d00001 diff --git a/espfsbuilder/testout/.gitignore b/espfsbuilder/testout/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/espfsbuilder/testout/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/spritehttpd/Makefile b/spritehttpd/Makefile index cc1dac0..f753307 100644 --- a/spritehttpd/Makefile +++ b/spritehttpd/Makefile @@ -30,7 +30,8 @@ LIB_OBJS = ${LIB_SOURCES:.c=.o} LIB_INCLUDES = -Iinclude -Ilib/heatshrink -Ilib/espfs # TODO check what these mean -LIB_CFLAGS = -fPIC -Wall -Wextra -c +#LIB_CFLAGS = -fPIC -Wall -Wextra -c +LIB_CFLAGS = -fPIC -Wall -Wextra -c -Og -g OBJ_DIR=./obj diff --git a/spritehttpd/include/cgiwebsocket.h b/spritehttpd/include/cgiwebsocket.h index 5226886..e26e765 100644 --- a/spritehttpd/include/cgiwebsocket.h +++ b/spritehttpd/include/cgiwebsocket.h @@ -11,7 +11,7 @@ typedef struct Websock Websock; typedef struct WebsockPriv WebsockPriv; typedef void(*WsConnectedCb)(Websock *ws); -typedef void(*WsRecvCb)(Websock *ws, char *data, int len, int flags); +typedef void(*WsRecvCb)(Websock *ws, uint8_t *data, size_t len, int flags); typedef void(*WsSentCb)(Websock *ws); typedef void(*WsCloseCb)(Websock *ws); @@ -26,8 +26,8 @@ struct Websock { }; httpd_cgi_state cgiWebsocket(HttpdConnData *connData); -int cgiWebsocketSend(Websock *ws, const char *data, int len, int flags); +int cgiWebsocketSend(Websock *ws, const uint8_t *data, size_t len, int flags); void cgiWebsocketClose(Websock *ws, int reason); -httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, char *data, int len); -int cgiWebsockBroadcast(const char *resource, const char *data, int len, int flags); -void cgiWebsockMeasureBacklog(const char *resource, int *total, int *max); +httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, uint8_t *data, size_t len); +int cgiWebsockBroadcast(const char *resource, const uint8_t *data, size_t len, int flags); +void cgiWebsockMeasureBacklog(const char *resource, size_t *total, size_t *max); diff --git a/spritehttpd/include/httpd.h b/spritehttpd/include/httpd.h index bec929d..12056a7 100644 --- a/spritehttpd/include/httpd.h +++ b/spritehttpd/include/httpd.h @@ -2,6 +2,8 @@ #include #include +#include +#include /* needed for ssize_t */ #include "httpd-platform.h" #ifndef GIT_HASH @@ -87,7 +89,7 @@ typedef struct HttpdPostData HttpdPostData; extern HttpdConnData *s_connData[HTTPD_MAX_CONNECTIONS]; typedef httpd_cgi_state (* cgiSendCallback)(HttpdConnData *connData); -typedef httpd_cgi_state (* cgiRecvHandler)(HttpdConnData *connData, char *data, int len); +typedef httpd_cgi_state (* cgiRecvHandler)(HttpdConnData *connData, char *data, size_t len); struct httpd_options { uint16_t port; @@ -184,16 +186,53 @@ void httpdStartResponse(HttpdConnData *conn, int code); void httpdHeader(HttpdConnData *conn, const char *field, const char *val); void httpdEndHeaders(HttpdConnData *conn); int httpdGetHeader(HttpdConnData *conn, const char *header, char *ret, int retLen); -int httpdSend(HttpdConnData *conn, const char *data, int len); -int httpdSend_js(HttpdConnData *conn, const char *data, int len); -int httpdSend_html(HttpdConnData *conn, const char *data, int len); + +/** + * Send binary data + * + * @param conn + * @param data - data to send + * @param len - num bytes. -1 to use strlen. + * @return 1 = success + */ +int httpdSend(HttpdConnData *conn, const uint8_t *data, size_t len); + +/** + * Send a string. The length is measured using strlen. + * + * @param conn + * @param data - string + * @return 1 = success + */ +static inline int httpdSendStr(HttpdConnData *conn, const char *data) +{ + return httpdSend(conn, (const uint8_t *) data, strlen(data)); +} + +/** + * Send a string with custom length. This is a slight optimization over httpdSendStr when the length is known. + * + * @param conn + * @param data - string + * @param len - num bytes + * @return 1 = success + */ +static inline int httpdSendStrN(HttpdConnData *conn, const char *data, size_t len) +{ + return httpdSend(conn, (const uint8_t *) data, len); +} + +// TODO convert to a general escaped send function +int httpdSend_js(HttpdConnData *conn, const uint8_t *data, ssize_t len); +int httpdSend_html(HttpdConnData *conn, const uint8_t *data, ssize_t len); + bool httpdFlushSendBuffer(HttpdConnData *conn); void httpdContinue(HttpdConnData *conn); void httpdConnSendStart(HttpdConnData *conn); void httpdConnSendFinish(HttpdConnData *conn); void httpdAddCacheHeaders(HttpdConnData *connData, const char *mime); -int httpGetBacklogSize(const HttpdConnData *connData); +size_t httpGetBacklogSize(const HttpdConnData *connData); void httdResponseOptions(HttpdConnData *conn, int cors); //Platform dependent code should call these. diff --git a/spritehttpd/lib/espfs/espfs.c b/spritehttpd/lib/espfs/espfs.c index c890166..6309751 100644 --- a/spritehttpd/lib/espfs/espfs.c +++ b/spritehttpd/lib/espfs/espfs.c @@ -22,21 +22,23 @@ It's written for use with httpd, but doesn't need to be used as such. #include "espfsformat.h" #include "espfs.h" -#include "heatshrink_decoder.h" #include "logging.h" -// forward declaration for use in the stand-alone espfs tool -int httpdPlatEspfsRead(void *dest, uint32_t offset, size_t len); - +// internal fields struct EspFsFile { - uint32_t header; - char decompressor; + /// Header pointer + uint32_t headerPos; + /// Decompressor type + uint8_t decompressor; uint32_t posDecomp; uint32_t posStart; uint32_t posComp; heatshrink_decoder *decompData; }; +// forward declaration for use in the stand-alone espfs tool +int httpdPlatEspfsRead(void *dest, uint32_t offset, size_t len); + EspFsInitResult espFsInit() { @@ -60,7 +62,7 @@ int espFsFlags(EspFsFile *fh) } int8_t flags; - httpdPlatEspfsRead(&flags, fh->header + offsetof(EspFsHeader, flags), 1); + httpdPlatEspfsRead(&flags, fh->headerPos + offsetof(EspFsHeader, flags), 1); return (int) flags; } @@ -133,13 +135,13 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos) return NULL; } - hpos += sizeof(EspFsHeader); - hpos += h->nameLen; // Skip to content - EspFsFile *r = (EspFsFile *) httpdPlatMalloc(sizeof(EspFsFile)); //Alloc file desc mem if (r == NULL) { return NULL; } - r->header = hpos; + r->headerPos = hpos; r->decompressor = h->compression; + + hpos += sizeof(EspFsHeader); + hpos += h->nameLen; // Skip to content r->posComp = hpos; r->posStart = hpos; r->posDecomp = 0; @@ -148,6 +150,7 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos) if (h->compression == COMPRESS_NONE) { r->decompData = NULL; + return r; } else if (h->compression == COMPRESS_HEATSHRINK) { //File is compressed with Heatshrink. char parm; @@ -166,6 +169,7 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos) httpdPlatFree(r); return NULL; } + return NULL; } //Open a file and return a pointer to the file desc struct. @@ -179,6 +183,10 @@ EspFsFile *espFsOpenAt(uint32_t hpos) return espFsOpenFromHeader(&h, hpos); } +int espFsFileReadHeader(const EspFsFile *file, EspFsHeader *header) +{ + return httpdPlatEspfsRead(header, file->headerPos, sizeof(EspFsHeader)); +} //Open a file and return a pointer to the file desc struct. EspFsFile *espFsOpen(const char *fileName) @@ -188,7 +196,6 @@ EspFsFile *espFsOpen(const char *fileName) uint32_t hpos; char namebuf[256]; EspFsHeader h; - EspFsFile *r; //Strip initial slashes while (fileName[0] == '/') { fileName++; } @@ -241,14 +248,16 @@ EspFsFile *espFsOpen(const char *fileName) } //Read len bytes from the given file into buff. Returns the actual amount of bytes read. -int espFsRead(EspFsFile *fh, char *buff, size_t len) +int espFsRead(EspFsFile *fh, uint8_t *buff, size_t buf_cap) { - int rv; - int flen; - int fdlen; + int rv = 0; + uint32_t binary_len = 0; + uint32_t decompressed_len = 0; if (fh == NULL) { return 0; } - rv = httpdPlatEspfsRead(&flen, fh->header + offsetof(EspFsHeader, fileLenComp), 4); + espfs_dbg("[EspFS] File read, pos @%d: cap %d", (int)fh->posComp, (int) buf_cap); + + rv = httpdPlatEspfsRead(&binary_len, fh->headerPos + offsetof(EspFsHeader, fileLenComp), 4); if (rv != 0) { return 0; } @@ -256,62 +265,68 @@ int espFsRead(EspFsFile *fh, char *buff, size_t len) //Cache file length. //Do stuff depending on the way the file is compressed. if (fh->decompressor == COMPRESS_NONE) { - int toRead = flen - (int) (fh->posComp - fh->posStart); + int toRead = (int) binary_len - (int) (fh->posComp - fh->posStart); if (toRead < 0) { toRead = 0; } - if (len > toRead) { len = toRead; } - rv = httpdPlatEspfsRead(buff, fh->posComp, len); + if ((int) buf_cap > toRead) { buf_cap = toRead; } + espfs_dbg("[EspFS] Plain data, read chunk @%d: %d", (int)fh->posComp, (int) buf_cap); + rv = httpdPlatEspfsRead(buff, fh->posComp, buf_cap); if (rv != 0) { return 0; } - fh->posDecomp += len; - fh->posComp += len; - return (int) len; + fh->posDecomp += buf_cap; + fh->posComp += buf_cap; + return (int) buf_cap; } else if (fh->decompressor == COMPRESS_HEATSHRINK) { - rv = httpdPlatEspfsRead(&fdlen, fh->header + offsetof(EspFsHeader, fileLenDecomp), 4); + rv = httpdPlatEspfsRead(&decompressed_len, fh->headerPos + offsetof(EspFsHeader, fileLenDecomp), 4); if (rv != 0) { return 0; } - size_t decoded = 0; - size_t elen, rlen; - char ebuff[16]; + size_t decoded_bytes = 0; + size_t remaining_binary_len, sunk_len, decoded_chunk_len; +#define HS_DECOMP_CHUNK_LEN 16 + char ebuff[HS_DECOMP_CHUNK_LEN]; heatshrink_decoder *dec = fh->decompData; - if (fh->posDecomp == fdlen) { - return 0; - } // We must ensure that whole file is decompressed and written to output buffer. // This means even when there is no input data (elen==0) try to poll decoder until // posDecomp equals decompressed file length - while (decoded < len) { + while (decoded_bytes < buf_cap) { //Feed data into the decompressor //ToDo: Check ret val of heatshrink fns for errors - elen = flen - (fh->posComp - fh->posStart); - if (elen > 0) { - rv = httpdPlatEspfsRead(ebuff, fh->posComp, 16); + remaining_binary_len = binary_len - (fh->posComp - fh->posStart); + if (remaining_binary_len > 0) { + const size_t chunk = remaining_binary_len < HS_DECOMP_CHUNK_LEN ? + remaining_binary_len : HS_DECOMP_CHUNK_LEN; + + espfs_dbg("[EspFS] HS data, read chunk @%d: %d", (int)fh->posComp, (int) chunk); + rv = httpdPlatEspfsRead(ebuff, fh->posComp, chunk); if (rv != 0) { return 0; } - heatshrink_decoder_sink(dec, (uint8_t *) ebuff, (elen > 16) ? 16 : elen, &rlen); - fh->posComp += rlen; + heatshrink_decoder_sink(dec, (uint8_t *) ebuff, chunk, &sunk_len); + espfs_dbg("[EspFS] HS sunk %d bytes", (int) sunk_len); + fh->posComp += sunk_len; } //Grab decompressed data and put into buff - heatshrink_decoder_poll(dec, (uint8_t *) buff, len - decoded, &rlen); - fh->posDecomp += rlen; - buff += rlen; - decoded += rlen; - - if (elen == 0) { - if (fh->posDecomp == fdlen) { + decoded_chunk_len = 0; + heatshrink_decoder_poll(dec, buff, buf_cap - decoded_bytes, &decoded_chunk_len); + fh->posDecomp += decoded_chunk_len; + buff += decoded_chunk_len; + decoded_bytes += decoded_chunk_len; + + espfs_dbg("[EspFS] HS pulled %d bytes, remaining binary len %d", (int) decoded_chunk_len, (int) remaining_binary_len); + if (remaining_binary_len == 0) { + if (fh->posDecomp == decompressed_len) { heatshrink_decoder_finish(dec); } - return (int) decoded; + return (int) decoded_bytes; } } - return (int) len; + return (int) buf_cap; } return 0; } diff --git a/spritehttpd/lib/espfs/espfs.h b/spritehttpd/lib/espfs/espfs.h index e2b5e1e..9ff29dc 100644 --- a/spritehttpd/lib/espfs/espfs.h +++ b/spritehttpd/lib/espfs/espfs.h @@ -3,6 +3,7 @@ #include #include #include "espfsformat.h" +#include "heatshrink_decoder.h" typedef enum { ESPFS_INIT_RESULT_OK, @@ -17,6 +18,14 @@ struct EspFsWalk { }; typedef struct EspFsWalk EspFsWalk; +/** + * Read a file header + * + * @param[in] file - file to read + * @param[out] header - header is read here + * @return 0 = success + */ +int espFsFileReadHeader(const EspFsFile *file, EspFsHeader *header); /** Init filesystem walk */ void espFsWalkInit(EspFsWalk *walk); /** @@ -38,5 +47,5 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos); EspFsInitResult espFsInit(); EspFsFile *espFsOpen(const char *fileName); int espFsFlags(EspFsFile *fh); -int espFsRead(EspFsFile *fh, char *buff, size_t len); +int espFsRead(EspFsFile *fh, uint8_t *buff, size_t len); void espFsClose(EspFsFile *fh); diff --git a/spritehttpd/src/cgiwebsocket.c b/spritehttpd/src/cgiwebsocket.c index f6c906b..c0f3c9e 100644 --- a/spritehttpd/src/cgiwebsocket.c +++ b/spritehttpd/src/cgiwebsocket.c @@ -88,7 +88,7 @@ struct WebsockPriv { static Websock *llStart = NULL; -static int sendFrameHead(Websock *ws, int opcode, int len) +static int sendFrameHead(Websock *ws, int opcode, size_t len) { uint8_t buf[14]; int i = 0; @@ -111,10 +111,10 @@ static int sendFrameHead(Websock *ws, int opcode, int len) buf[i++] = len; } // ws_dbg("WS: Sent frame head for payload of %d bytes.", len); - return httpdSend(ws->conn, (char *) buf, i); + return httpdSend(ws->conn, buf, i); } -int cgiWebsocketSend(Websock *ws, const char *data, int len, int flags) +int cgiWebsocketSend(Websock *ws, const uint8_t *data, size_t len, int flags) { int r = 0; int fl = 0; @@ -135,7 +135,7 @@ int cgiWebsocketSend(Websock *ws, const char *data, int len, int flags) } //Broadcast data to all websockets at a specific url. Returns the amount of connections sent to. -int cgiWebsockBroadcast(const char *resource, const char *data, int len, int flags) +int cgiWebsockBroadcast(const char *resource, const uint8_t *data, size_t len, int flags) { Websock *lw = llStart; int ret = 0; @@ -166,15 +166,15 @@ int cgiWebsockBroadcast(const char *resource, const char *data, int len, int fla } /** this is used for estimation how full the ram is */ -void cgiWebsockMeasureBacklog(const char *resource, int *total, int *max) +void cgiWebsockMeasureBacklog(const char *resource, size_t *total, size_t *max) { Websock *lw = llStart; - int bMax = 0; - int bTotal = 0; + size_t bMax = 0; + size_t bTotal = 0; while (lw != NULL) { if (strcmp(lw->conn->url, resource) == 0) { //lw->conn - int bs = httpGetBacklogSize(lw->conn); + size_t bs = httpGetBacklogSize(lw->conn); bTotal += bs; if (bs > bMax) { bMax = bs; } } @@ -186,7 +186,7 @@ void cgiWebsockMeasureBacklog(const char *resource, int *total, int *max) void cgiWebsocketClose(Websock *ws, int reason) { - char rs[2] = {reason >> 8, reason & 0xff}; + uint8_t rs[2] = {reason >> 8, reason & 0xff}; sendFrameHead(ws, FLAG_FIN | OPCODE_CLOSE, 2); httpdSend(ws->conn, rs, 2); ws->priv->closedHere = 1; @@ -210,13 +210,13 @@ static void websockFree(Websock *ws) if (ws->priv) { httpdPlatFree(ws->priv); } } -httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, char *data, int len) +httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, uint8_t *data, size_t len) { - int i, j, sl; + int j, sl; httpd_cgi_state r = HTTPD_CGI_MORE; int wasHeaderByte; Websock *ws = (Websock *) connData->cgiData; - for (i = 0; i < len; i++) { + for (size_t i = 0; i < len; i++) { // httpd_printf("Ws: State %d byte 0x%02X\n", ws->priv->wsStatus, data[i]); wasHeaderByte = 1; if (ws->priv->wsStatus == ST_FLAGS) { @@ -261,7 +261,7 @@ httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, char *data, int len) //First, unmask the data sl = len - i; // ws_dbg("Ws: Frame payload. wasHeaderByte %d fr.len %d sl %d cmd 0x%x", wasHeaderByte, (int)ws->priv->fr.len, (int)sl, ws->priv->fr.flags); - if (sl > ws->priv->fr.len) { sl = ws->priv->fr.len; } + if ((uint64_t) sl > ws->priv->fr.len) { sl = (int) ws->priv->fr.len; } for (j = 0; j < sl; j++) { data[i + j] ^= (ws->priv->fr.mask[(ws->priv->maskCtr++) & 3]); } // if (DEBUG_WS) { @@ -283,7 +283,7 @@ httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, char *data, int len) } else if ((ws->priv->fr.flags & OPCODE_MASK) == OPCODE_TEXT || (ws->priv->fr.flags & OPCODE_MASK) == OPCODE_BINARY || (ws->priv->fr.flags & OPCODE_MASK) == OPCODE_CONTINUE) { - if (sl > ws->priv->fr.len) { sl = ws->priv->fr.len; } + if ((uint64_t) sl > ws->priv->fr.len) { sl = ws->priv->fr.len; } if (!(ws->priv->fr.len8 & IS_MASKED)) { //We're a server; client should send us masked packets. cgiWebsocketClose(ws, 1002); diff --git a/spritehttpd/src/httpd.c b/spritehttpd/src/httpd.c index 51970c3..8a1726e 100644 --- a/spritehttpd/src/httpd.c +++ b/spritehttpd/src/httpd.c @@ -162,7 +162,7 @@ const char *httpdGetVersion(void) return HTTPDVER; } -int httpGetBacklogSize(const HttpdConnData *conn) +size_t httpGetBacklogSize(const HttpdConnData *conn) { HttpSendBacklogItem *bl = conn->priv->sendBacklog; if (!bl) { return 0; } @@ -193,6 +193,9 @@ static HttpdConnData *httpdFindConnData(ConnTypePtr conn, const char *remIp, int //Retires a connection for re-use static void httpdRetireConn(HttpdConnData *conn) { + if (!conn) { + return; + } if (conn->priv->sendBacklog != NULL) { HttpSendBacklogItem *i, *j; i = conn->priv->sendBacklog; @@ -205,14 +208,14 @@ static void httpdRetireConn(HttpdConnData *conn) if (conn->post->buff != NULL) { httpdPlatFree(conn->post->buff); } if (conn->post != NULL) { httpdPlatFree(conn->post); } if (conn->priv != NULL) { httpdPlatFree(conn->priv); } - if (conn) { httpdPlatFree(conn); } + httpdPlatFree(conn); for (int i = 0; i < HTTPD_MAX_CONNECTIONS; i++) { if (s_connData[i] == conn) { s_connData[i] = NULL; } } } //Stupid li'l helper function that returns the value of a hex char. -static int httpdHexVal(char c) +static char httpdHexVal(char c) { if (c >= '0' && c <= '9') { return c - '0'; } if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } @@ -227,13 +230,14 @@ static int httpdHexVal(char c) int httpdUrlDecode(const char *val, int valLen, char *ret, int retLen) { int s = 0, d = 0; - int esced = 0, escVal = 0; + int esced = 0; + char escVal = 0; while (s < valLen && d < retLen) { if (esced == 1) { escVal = httpdHexVal(val[s]) << 4; esced = 2; } else if (esced == 2) { - escVal += httpdHexVal(val[s]); + escVal |= httpdHexVal(val[s]); ret[d++] = escVal; esced = 0; } else if (val[s] == '%') { @@ -261,18 +265,18 @@ int httpdFindArg(const char *line, const char *arg, char *buff, int buffLen) const int arglen = (int) strlen(arg); p = line; while (p != NULL && *p != '\n' && *p != '\r' && *p != 0) { - router_dbg("findArg: %s", p); + router_dbg("findArg: %s", p); if (strstarts(p, arg) && p[arglen] == '=') { p += arglen + 1; //move p to start of value e = strstr(p, "&"); if (e == NULL) { e = p + strlen(p); } - router_dbg("findArg: val %s len %d", p, (e - p)); - return httpdUrlDecode(p, (e - p), buff, buffLen); + router_dbg("findArg: val %s len %d", p, (int) (e - p)); + return httpdUrlDecode(p, (int)(e - p), buff, buffLen); } p = strstr(p, "&"); if (p != NULL) { p += 1; } } - router_error("Finding arg %s in %s: Not found :/", arg, line); + router_error("Finding arg %s in %s: Not found :/", arg, line); return -1; //not found } @@ -339,28 +343,28 @@ void httpdStartResponse(HttpdConnData *conn, int code) code2str(code), serverName, connStr); - httpdSend(conn, buff, l); + httpdSendStrN(conn, buff, l); if (0 == (conn->priv->flags & HFL_NOCORS)) { // CORS headers - httpdSend(conn, "Access-Control-Allow-Origin: *\r\n", -1); - httpdSend(conn, "Access-Control-Allow-Methods: GET,POST,OPTIONS\r\n", -1); + httpdSendStr(conn, "Access-Control-Allow-Origin: *\r\n"); + httpdSendStr(conn, "Access-Control-Allow-Methods: GET,POST,OPTIONS\r\n"); } } //Send a http header. void httpdHeader(HttpdConnData *conn, const char *field, const char *val) { - httpdSend(conn, field, -1); - httpdSend(conn, ": ", -1); - httpdSend(conn, val, -1); - httpdSend(conn, "\r\n", -1); + httpdSendStr(conn, field); + httpdSendStr(conn, ": "); + httpdSendStr(conn, val); + httpdSendStr(conn, "\r\n"); } //Finish the headers. void httpdEndHeaders(HttpdConnData *conn) { - httpdSend(conn, "\r\n", -1); + httpdSendStr(conn, "\r\n"); conn->priv->flags |= HFL_SENDINGBODY; } @@ -371,8 +375,8 @@ void httpdRedirect(HttpdConnData *conn, const char *newUrl) httpdStartResponse(conn, 302); httpdHeader(conn, "Location", newUrl); httpdEndHeaders(conn); - httpdSend(conn, "Moved to ", -1); - httpdSend(conn, newUrl, -1); + httpdSendStr(conn, "Moved to "); + httpdSendStr(conn, newUrl); } //Use this as a cgi function to redirect one url to another. @@ -392,7 +396,7 @@ static httpd_cgi_state cgiNotFound(HttpdConnData *connData) if (connData->conn == NULL) { return HTTPD_CGI_DONE; } httpdStartResponse(connData, 404); httpdEndHeaders(connData); - httpdSend(connData, "404 File not found.", -1); + httpdSendStr(connData, "404 File not found."); return HTTPD_CGI_DONE; } @@ -406,7 +410,6 @@ httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData) static const char hostFmt[] = "http://%s/"; char *buff; int isIP = 0; - int x; if (connData->conn == NULL) { //Connection aborted. Clean up. return HTTPD_CGI_DONE; @@ -419,7 +422,7 @@ httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData) //Quick and dirty code to see if host is an IP if (strlen(connData->hostName) > 8) { isIP = 1; - for (x = 0; x < strlen(connData->hostName); x++) { + for (size_t x = 0; x < strlen(connData->hostName); x++) { if (connData->hostName[x] != '.' && (connData->hostName[x] < '0' || connData->hostName[x] > '9')) { isIP = 0; } } } @@ -443,10 +446,9 @@ httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData) //Add data to the send buffer. len is the length of the data. If len is -1 //the data is seen as a C-string. //Returns 1 for success, 0 for out-of-memory. -int httpdSend(HttpdConnData *conn, const char *data, int len) +int httpdSend(HttpdConnData *conn, const uint8_t *data, size_t len) { if (conn->conn == NULL) { return 0; } - if (len < 0) { len = strlen(data); } if (len == 0) { return 0; } if (conn->priv->flags & HFL_CHUNKED && conn->priv->flags & HFL_SENDINGBODY && conn->priv->chunkHdr == NULL) { if (conn->priv->sendBuffLen + len + 6 > HTTPD_MAX_SENDBUFF_LEN) { return 0; } @@ -464,19 +466,21 @@ int httpdSend(HttpdConnData *conn, const char *data, int len) static char httpdHexNibble(int val) { val &= 0xf; - if (val < 10) { return '0' + val; } - return 'A' + (val - 10); + if (val < 10) { return (char) ('0' + val); } + return (char) ('A' + (val - 10)); } -#define httpdSend_orDie(conn, data, len) do { if (!httpdSend((conn), (data), (len))) return false; } while (0) +#define httpdSend_orDie(conn, data, len) do { if (!httpdSend((conn), (const uint8_t *)(data), (len))) return false; } while (0) + +#define httpdSendStr_orDie(conn, data) do { if (!httpdSendStr((conn), (data))) return false; } while (0) /* encode for HTML. returns 0 or 1 - 1 = success */ -int httpdSend_html(HttpdConnData *conn, const char *data, int len) +int httpdSend_html(HttpdConnData *conn, const uint8_t *data, ssize_t len) { int start = 0, end = 0; - char c; + uint8_t c; if (conn->conn == NULL) { return 0; } - if (len < 0) { len = (int) strlen(data); } + if (len < 0) { len = (int) strlen((const char *) data); } if (len == 0) { return 0; } for (end = 0; end < len; end++) { @@ -491,10 +495,10 @@ int httpdSend_html(HttpdConnData *conn, const char *data, int len) start = end + 1; } - if (c == '"') httpdSend_orDie(conn, """, 5); - else if (c == '\'') httpdSend_orDie(conn, "'", 5); - else if (c == '<') httpdSend_orDie(conn, "<", 4); - else if (c == '>') httpdSend_orDie(conn, ">", 4); + if (c == '"') httpdSendStr_orDie(conn, """); + else if (c == '\'') httpdSendStr_orDie(conn, "'"); + else if (c == '<') httpdSendStr_orDie(conn, "<"); + else if (c == '>') httpdSendStr_orDie(conn, ">"); } if (start < end) httpdSend_orDie(conn, data + start, end - start); @@ -502,12 +506,12 @@ int httpdSend_html(HttpdConnData *conn, const char *data, int len) } /* encode for JS. returns 0 or 1 - 1 = success */ -int httpdSend_js(HttpdConnData *conn, const char *data, int len) +int httpdSend_js(HttpdConnData *conn, const uint8_t *data, ssize_t len) { int start = 0, end = 0; - char c; + uint8_t c; if (conn->conn == NULL) { return 0; } - if (len < 0) { len = (int) strlen(data); } + if (len < 0) { len = (int) strlen((const char *) data); } if (len == 0) { return 0; } for (end = 0; end < len; end++) { @@ -522,13 +526,13 @@ int httpdSend_js(HttpdConnData *conn, const char *data, int len) start = end + 1; } - if (c == '"') httpdSend_orDie(conn, "\\\"", 2); - else if (c == '\'') httpdSend_orDie(conn, "\\'", 2); - else if (c == '\\') httpdSend_orDie(conn, "\\\\", 2); - else if (c == '<') httpdSend_orDie(conn, "\\u003C", 6); - else if (c == '>') httpdSend_orDie(conn, "\\u003E", 6); - else if (c == '\n') httpdSend_orDie(conn, "\\n", 2); - else if (c == '\r') httpdSend_orDie(conn, "\\r", 2); + if (c == '"') httpdSendStr_orDie(conn, "\\\""); + else if (c == '\'') httpdSendStr_orDie(conn, "\\'"); + else if (c == '\\') httpdSendStr_orDie(conn, "\\\\"); + else if (c == '<') httpdSendStr_orDie(conn, "\\u003C"); + else if (c == '>') httpdSendStr_orDie(conn, "\\u003E"); + else if (c == '\n') httpdSendStr_orDie(conn, "\\n"); + else if (c == '\r') httpdSendStr_orDie(conn, "\\r"); } if (start < end) httpdSend_orDie(conn, data + start, end - start); @@ -546,7 +550,7 @@ bool httpdFlushSendBuffer(HttpdConnData *conn) if (conn->priv->chunkHdr != NULL) { //We're sending chunked data, and the chunk needs fixing up. //Finish chunk with cr/lf - httpdSend(conn, "\r\n", 2); + httpdSendStr(conn, "\r\n"); //Calculate length of chunk len = ((&conn->priv->sendBuff[conn->priv->sendBuffLen]) - conn->priv->chunkHdr) - 8; //Fix up chunk header to correct value @@ -687,7 +691,7 @@ static void httpdProcessRequest(HttpdConnData *conn) int r; int i = 0; if (conn->url == NULL) { - router_warn("WtF? url = NULL"); + router_warn("WtF? url = NULL"); return; //Shouldn't happen } @@ -698,7 +702,7 @@ static void httpdProcessRequest(HttpdConnData *conn) httpdEndHeaders(conn); httpdCgiIsDone(conn); - router_dbg("CORS preflight resp sent."); + router_dbg("CORS preflight resp sent."); return; } @@ -708,7 +712,7 @@ static void httpdProcessRequest(HttpdConnData *conn) //Look up URL in the built-in URL table. while (builtInUrls[i].url != NULL) { int match = 0; - const char const *route = builtInUrls[i].url; + const char *route = builtInUrls[i].url; //See if there's a literal match if (streq(route, conn->url)) { match = 1; } //See if there's a wildcard match (*) @@ -723,7 +727,7 @@ static void httpdProcessRequest(HttpdConnData *conn) match = 1; } if (match) { - router_dbg("Matched route #%d, url=%s", i, route); + router_dbg("Matched route #%d, url=%s", i, route); conn->cgiData = NULL; conn->cgi = builtInUrls[i].cgiCb; conn->cgiArg = builtInUrls[i].cgiArg; @@ -735,7 +739,7 @@ static void httpdProcessRequest(HttpdConnData *conn) if (builtInUrls[i].url == NULL) { //Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just //generate a built-in 404 to handle this. - router_warn("%s not found. 404!", conn->url); + router_warn("%s not found. 404!", conn->url); conn->cgi = cgiNotFound; } @@ -1075,7 +1079,7 @@ httpd_thread_handle_t *httpdInit(const HttpdBuiltInUrl *fixedUrls, struct httpd_ return httpdPlatStart(options); } -void httpdJoin(httpd_thread_handle_t * handle) +void httpdJoin(httpd_thread_handle_t *handle) { httpdPlatJoin(handle); } diff --git a/spritehttpd/src/httpdespfs.c b/spritehttpd/src/httpdespfs.c index 65de77f..984fc0c 100644 --- a/spritehttpd/src/httpdespfs.c +++ b/spritehttpd/src/httpdespfs.c @@ -86,7 +86,7 @@ serveStaticFile(HttpdConnData *connData, const char *filepath) { EspFsFile *file = connData->cgiData; int len; - char buff[FILE_CHUNK_LEN + 1]; + uint8_t buff[FILE_CHUNK_LEN + 1]; char acceptEncodingBuffer[64 + 1]; int isGzip; @@ -127,7 +127,7 @@ serveStaticFile(HttpdConnData *connData, const char *filepath) httpdGetHeader(connData, "Accept-Encoding", acceptEncodingBuffer, 64); if (strstr(acceptEncodingBuffer, "gzip") == NULL) { //No Accept-Encoding: gzip header present - httpdSend(connData, gzipNonSupportedMessage, -1); + httpdSendStr(connData, gzipNonSupportedMessage); espFsClose(file); return HTTPD_CGI_DONE; } @@ -202,9 +202,13 @@ tplSend(HttpdConnData *conn, const char *str, int len) if (conn == NULL) { return 0; } TplData *tpd = conn->cgiData; - if (tpd == NULL || tpd->tokEncode == ENCODE_PLAIN) { return httpdSend(conn, str, len); } - if (tpd->tokEncode == ENCODE_HTML) { return httpdSend_html(conn, str, len); } - if (tpd->tokEncode == ENCODE_JS) { return httpdSend_js(conn, str, len); } + if (tpd == NULL || tpd->tokEncode == ENCODE_PLAIN) { + return httpdSendStrN(conn, str, len); + } else if (tpd->tokEncode == ENCODE_HTML) { + return httpdSend_html(conn, (const uint8_t *) str, len); + } else if (tpd->tokEncode == ENCODE_JS) { + return httpdSend_js(conn, (const uint8_t *) str, len); + } return 0; } @@ -281,7 +285,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData) sp = tpd->buff_sp; x = tpd->buff_x; } else { - len = espFsRead(tpd->file, buff, FILE_CHUNK_LEN); + len = espFsRead(tpd->file, (uint8_t *) buff, FILE_CHUNK_LEN); tpd->buff_len = len; e = buff; @@ -295,7 +299,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData) //Inside ordinary text. if (buff[x] == '%') { //Send raw data up to now - if (sp != 0) { httpdSend(connData, e, sp); } + if (sp != 0) { httpdSendStrN(connData, e, sp); } sp = 0; //Go collect token chars. tpd->tokenPos = 0; @@ -307,7 +311,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData) if (tpd->tokenPos == 0) { //This is the second % of a %% escape string. //Send a single % and resume with the normal program flow. - httpdSend(connData, "%", 1); + httpdSendStrN(connData, "%", 1); } else { if (!tpd->chunk_resume) { //This is an actual token. @@ -365,12 +369,12 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData) c != '.' && c != '_' && c != '-' && c != ':' )) { // looks like we collected some garbage - httpdSend(connData, "%", 1); + httpdSendStrN(connData, "%", 1); if (tpd->tokenPos > 0) { - httpdSend(connData, tpd->token, tpd->tokenPos); + httpdSendStrN(connData, tpd->token, tpd->tokenPos); } // the bad char - httpdSend(connData, &c, 1); + httpdSendStrN(connData, &c, 1); //Go collect normal chars again. e = &buff[x + 1]; @@ -389,7 +393,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData) } //Send remaining bit. - if (sp != 0) { httpdSend(connData, e, sp); } + if (sp != 0) { httpdSendStrN(connData, e, sp); } if (len != FILE_CHUNK_LEN) { //We're done. ((TplCallback) (connData->cgiArg))(connData, NULL, &tpd->tplArg);