code cleaning, add extract option to fs builder, fixes

master
Ondřej Hruška 1 year ago
parent aa0754d302
commit a6917bd563
  1. 2
      demo/Makefile
  2. 3
      espfsbuilder/Makefile
  3. 45
      espfsbuilder/main.c
  4. 110
      espfsbuilder/parsing.c
  5. 2
      espfsbuilder/parsing.h
  6. 7
      espfsbuilder/testfiles/LIMECURD.TXT
  7. BIN
      espfsbuilder/testfiles/mouse.jpg
  8. 2
      espfsbuilder/testout/.gitignore
  9. 3
      spritehttpd/Makefile
  10. 10
      spritehttpd/include/cgiwebsocket.h
  11. 49
      spritehttpd/include/httpd.h
  12. 105
      spritehttpd/lib/espfs/espfs.c
  13. 11
      spritehttpd/lib/espfs/espfs.h
  14. 28
      spritehttpd/src/cgiwebsocket.c
  15. 106
      spritehttpd/src/httpd.c
  16. 28
      spritehttpd/src/httpdespfs.c

@ -22,7 +22,7 @@ staticfiles-embed.c: staticfiles.bin
clean: clean:
rm -f demo staticfiles.bin staticfiles-embed.c rm -f demo staticfiles.bin staticfiles-embed.c
make -C ../spritehttpd make -C ../spritehttpd clean
$(LIBFILE): $(LIBFILE):
make -C ../spritehttpd PLATFORM=posix CFLAGS="-Og -g" make -C ../spritehttpd PLATFORM=posix CFLAGS="-Og -g"

@ -13,7 +13,8 @@ CFLAGS = -I. \
all: $(TARGET) all: $(TARGET)
$(TARGET): ${SOURCES} $(TARGET): ${SOURCES}
cc -O3 -lz $^ -o $@ ${CFLAGS} #cc -O3 -lz $^ -o $@ ${CFLAGS}
cc -Og -g -Wall -Wextra -lz $^ -o $@ ${CFLAGS}
clean: clean:
rm $(TARGET) rm $(TARGET)

@ -62,8 +62,8 @@ size_t compressHeatshrink(const uint8_t *in, size_t insize, uint8_t *out, size_t
size_t len; size_t len;
int ws[] = {5, 6, 8, 11, 13}; int ws[] = {5, 6, 8, 11, 13};
int ls[] = {3, 3, 4, 4, 4}; int ls[] = {3, 3, 4, 4, 4};
HSE_poll_res pres; HSE_poll_res pres = 0;
HSE_sink_res sres; HSE_sink_res sres = 0;
size_t r; size_t r;
if (level == -1) { level = 8; } if (level == -1) { level = 8; }
level = (level - 1) / 2; //level is now 0, 1, 2, 3, 4 level = (level - 1) / 2; //level is now 0, 1, 2, 3, 4
@ -358,7 +358,6 @@ int main(int argc, char **argv)
{ {
int f; int f;
char inputFileName[1024]; char inputFileName[1024];
char *realName;
struct stat statBuf; struct stat statBuf;
int serr; int serr;
int rate; int rate;
@ -373,11 +372,13 @@ int main(int argc, char **argv)
char *outfile = NULL; char *outfile = NULL;
char *parseFile = NULL; char *parseFile = NULL;
char *stripPath = NULL; char *stripPath = NULL;
char *extractFile = NULL;
while (1) { while (1) {
int option_index = 0; int option_index = 0;
static struct option long_options[] = { static struct option long_options[] = {
{"parse", required_argument, 0, 'p'}, {"parse", required_argument, 0, 'p'},
{"extract", required_argument, 0, 'e'},
{"compress", required_argument, 0, 'c'}, {"compress", required_argument, 0, 'c'},
{"gzip", no_argument, 0, 'z'}, {"gzip", no_argument, 0, 'z'},
{"gzip-all", no_argument, 0, 'G'}, {"gzip-all", no_argument, 0, 'G'},
@ -390,7 +391,7 @@ int main(int argc, char **argv)
{0, 0, 0, 0} {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); long_options, &option_index);
if (c == -1) { if (c == -1) {
break; break;
@ -412,6 +413,10 @@ int main(int argc, char **argv)
parseFile = strdup(optarg); parseFile = strdup(optarg);
break; break;
case 'e':
extractFile = strdup(optarg);
break;
case 'S': case 'S':
stripPath = strdup(optarg); stripPath = strdup(optarg);
break; 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) { if (parseFile) {
parseEspfsFileAndShowItsContents(parseFile); parseEspfsImage(parseFile, extractFile, s_outFd);
exit(0); 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; struct InputFileLinkedListEntry *entry = s_inputFiles;
while (entry) { while (entry) {
char *name = entry->name; 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, "%s - Program to create espfs images\n", argv[0]);
fprintf(stderr, "Options:\n"); 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, "[-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, "[-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, "[-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"); fprintf(stderr, "[-z|--gzip]\n use gzip for files with extensions matching "DEFAULT_GZIP_EXTS"\n");

@ -2,6 +2,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <zlib.h>
#include "parsing.h" #include "parsing.h"
#include "espfs.h" #include "espfs.h"
@ -9,20 +10,78 @@
static size_t espfs_parse_filesize = -1; static size_t espfs_parse_filesize = -1;
static int espfs_parse_fd = -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. * Parse an image file and show the files contained.
* This is a simple sanity test. * 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; 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) { if (!f) {
perror(filename); perror(imagefile);
exit(1); exit(1);
} }
int fd = fileno(f); int fd = fileno(f);
@ -38,6 +97,45 @@ void parseEspfsFileAndShowItsContents(const char *filename)
exit(1); 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; EspFsWalk walk;
espFsWalkInit(&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); // fprintf(stderr, "FS read @ %d, len %d\n", offset, (int) len);
if (offset + len > espfs_parse_filesize) { if (offset + len > espfs_parse_filesize) {
// fprintf(stderr, "Read out fo range!\n"); fprintf(stderr, "Read out fo range!\n");
return -1; return -1;
} }
lseek(espfs_parse_fd, offset, SEEK_SET); lseek(espfs_parse_fd, offset, SEEK_SET);

@ -4,4 +4,4 @@
#pragma once #pragma once
void parseEspfsFileAndShowItsContents(const char *filename); void parseEspfsImage(const char *imagefile, const char *extractfile, int outfd);

@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,2 @@
*
!.gitignore

@ -30,7 +30,8 @@ LIB_OBJS = ${LIB_SOURCES:.c=.o}
LIB_INCLUDES = -Iinclude -Ilib/heatshrink -Ilib/espfs LIB_INCLUDES = -Iinclude -Ilib/heatshrink -Ilib/espfs
# TODO check what these mean # 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 OBJ_DIR=./obj

@ -11,7 +11,7 @@ typedef struct Websock Websock;
typedef struct WebsockPriv WebsockPriv; typedef struct WebsockPriv WebsockPriv;
typedef void(*WsConnectedCb)(Websock *ws); 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(*WsSentCb)(Websock *ws);
typedef void(*WsCloseCb)(Websock *ws); typedef void(*WsCloseCb)(Websock *ws);
@ -26,8 +26,8 @@ struct Websock {
}; };
httpd_cgi_state cgiWebsocket(HttpdConnData *connData); 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); void cgiWebsocketClose(Websock *ws, int reason);
httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, char *data, int len); httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, uint8_t *data, size_t len);
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);
void cgiWebsockMeasureBacklog(const char *resource, int *total, int *max); void cgiWebsockMeasureBacklog(const char *resource, size_t *total, size_t *max);

@ -2,6 +2,8 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <sys/types.h> /* needed for ssize_t */
#include "httpd-platform.h" #include "httpd-platform.h"
#ifndef GIT_HASH #ifndef GIT_HASH
@ -87,7 +89,7 @@ typedef struct HttpdPostData HttpdPostData;
extern HttpdConnData *s_connData[HTTPD_MAX_CONNECTIONS]; extern HttpdConnData *s_connData[HTTPD_MAX_CONNECTIONS];
typedef httpd_cgi_state (* cgiSendCallback)(HttpdConnData *connData); 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 { struct httpd_options {
uint16_t port; uint16_t port;
@ -184,16 +186,53 @@ void httpdStartResponse(HttpdConnData *conn, int code);
void httpdHeader(HttpdConnData *conn, const char *field, const char *val); void httpdHeader(HttpdConnData *conn, const char *field, const char *val);
void httpdEndHeaders(HttpdConnData *conn); void httpdEndHeaders(HttpdConnData *conn);
int httpdGetHeader(HttpdConnData *conn, const char *header, char *ret, int retLen); 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); bool httpdFlushSendBuffer(HttpdConnData *conn);
void httpdContinue(HttpdConnData *conn); void httpdContinue(HttpdConnData *conn);
void httpdConnSendStart(HttpdConnData *conn); void httpdConnSendStart(HttpdConnData *conn);
void httpdConnSendFinish(HttpdConnData *conn); void httpdConnSendFinish(HttpdConnData *conn);
void httpdAddCacheHeaders(HttpdConnData *connData, const char *mime); void httpdAddCacheHeaders(HttpdConnData *connData, const char *mime);
int httpGetBacklogSize(const HttpdConnData *connData); size_t httpGetBacklogSize(const HttpdConnData *connData);
void httdResponseOptions(HttpdConnData *conn, int cors); void httdResponseOptions(HttpdConnData *conn, int cors);
//Platform dependent code should call these. //Platform dependent code should call these.

@ -22,21 +22,23 @@ It's written for use with httpd, but doesn't need to be used as such.
#include "espfsformat.h" #include "espfsformat.h"
#include "espfs.h" #include "espfs.h"
#include "heatshrink_decoder.h"
#include "logging.h" #include "logging.h"
// forward declaration for use in the stand-alone espfs tool // internal fields
int httpdPlatEspfsRead(void *dest, uint32_t offset, size_t len);
struct EspFsFile { struct EspFsFile {
uint32_t header; /// Header pointer
char decompressor; uint32_t headerPos;
/// Decompressor type
uint8_t decompressor;
uint32_t posDecomp; uint32_t posDecomp;
uint32_t posStart; uint32_t posStart;
uint32_t posComp; uint32_t posComp;
heatshrink_decoder *decompData; 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() EspFsInitResult espFsInit()
{ {
@ -60,7 +62,7 @@ int espFsFlags(EspFsFile *fh)
} }
int8_t flags; int8_t flags;
httpdPlatEspfsRead(&flags, fh->header + offsetof(EspFsHeader, flags), 1); httpdPlatEspfsRead(&flags, fh->headerPos + offsetof(EspFsHeader, flags), 1);
return (int) flags; return (int) flags;
} }
@ -133,13 +135,13 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos)
return NULL; return NULL;
} }
hpos += sizeof(EspFsHeader);
hpos += h->nameLen; // Skip to content
EspFsFile *r = (EspFsFile *) httpdPlatMalloc(sizeof(EspFsFile)); //Alloc file desc mem EspFsFile *r = (EspFsFile *) httpdPlatMalloc(sizeof(EspFsFile)); //Alloc file desc mem
if (r == NULL) { return NULL; } if (r == NULL) { return NULL; }
r->header = hpos; r->headerPos = hpos;
r->decompressor = h->compression; r->decompressor = h->compression;
hpos += sizeof(EspFsHeader);
hpos += h->nameLen; // Skip to content
r->posComp = hpos; r->posComp = hpos;
r->posStart = hpos; r->posStart = hpos;
r->posDecomp = 0; r->posDecomp = 0;
@ -148,6 +150,7 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos)
if (h->compression == COMPRESS_NONE) { if (h->compression == COMPRESS_NONE) {
r->decompData = NULL; r->decompData = NULL;
return r;
} else if (h->compression == COMPRESS_HEATSHRINK) { } else if (h->compression == COMPRESS_HEATSHRINK) {
//File is compressed with Heatshrink. //File is compressed with Heatshrink.
char parm; char parm;
@ -166,6 +169,7 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos)
httpdPlatFree(r); httpdPlatFree(r);
return NULL; return NULL;
} }
return NULL;
} }
//Open a file and return a pointer to the file desc struct. //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); 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. //Open a file and return a pointer to the file desc struct.
EspFsFile *espFsOpen(const char *fileName) EspFsFile *espFsOpen(const char *fileName)
@ -188,7 +196,6 @@ EspFsFile *espFsOpen(const char *fileName)
uint32_t hpos; uint32_t hpos;
char namebuf[256]; char namebuf[256];
EspFsHeader h; EspFsHeader h;
EspFsFile *r;
//Strip initial slashes //Strip initial slashes
while (fileName[0] == '/') { fileName++; } 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. //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 rv = 0;
int flen; uint32_t binary_len = 0;
int fdlen; uint32_t decompressed_len = 0;
if (fh == NULL) { return 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) { if (rv != 0) {
return 0; return 0;
} }
@ -256,62 +265,68 @@ int espFsRead(EspFsFile *fh, char *buff, size_t len)
//Cache file length. //Cache file length.
//Do stuff depending on the way the file is compressed. //Do stuff depending on the way the file is compressed.
if (fh->decompressor == COMPRESS_NONE) { 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 (toRead < 0) { toRead = 0; }
if (len > toRead) { len = toRead; } if ((int) buf_cap > toRead) { buf_cap = toRead; }
rv = httpdPlatEspfsRead(buff, fh->posComp, len); 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) { if (rv != 0) {
return 0; return 0;
} }
fh->posDecomp += len; fh->posDecomp += buf_cap;
fh->posComp += len; fh->posComp += buf_cap;
return (int) len; return (int) buf_cap;
} else if (fh->decompressor == COMPRESS_HEATSHRINK) { } 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) { if (rv != 0) {
return 0; return 0;
} }
size_t decoded = 0; size_t decoded_bytes = 0;
size_t elen, rlen; size_t remaining_binary_len, sunk_len, decoded_chunk_len;
char ebuff[16]; #define HS_DECOMP_CHUNK_LEN 16
char ebuff[HS_DECOMP_CHUNK_LEN];
heatshrink_decoder *dec = fh->decompData; heatshrink_decoder *dec = fh->decompData;
if (fh->posDecomp == fdlen) {
return 0;
}
// We must ensure that whole file is decompressed and written to output buffer. // 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 // This means even when there is no input data (elen==0) try to poll decoder until
// posDecomp equals decompressed file length // posDecomp equals decompressed file length
while (decoded < len) { while (decoded_bytes < buf_cap) {
//Feed data into the decompressor //Feed data into the decompressor
//ToDo: Check ret val of heatshrink fns for errors //ToDo: Check ret val of heatshrink fns for errors
elen = flen - (fh->posComp - fh->posStart); remaining_binary_len = binary_len - (fh->posComp - fh->posStart);
if (elen > 0) { if (remaining_binary_len > 0) {
rv = httpdPlatEspfsRead(ebuff, fh->posComp, 16); 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) { if (rv != 0) {
return 0; return 0;
} }
heatshrink_decoder_sink(dec, (uint8_t *) ebuff, (elen > 16) ? 16 : elen, &rlen); heatshrink_decoder_sink(dec, (uint8_t *) ebuff, chunk, &sunk_len);
fh->posComp += rlen; espfs_dbg("[EspFS] HS sunk %d bytes", (int) sunk_len);
fh->posComp += sunk_len;
} }
//Grab decompressed data and put into buff //Grab decompressed data and put into buff
heatshrink_decoder_poll(dec, (uint8_t *) buff, len - decoded, &rlen); decoded_chunk_len = 0;
fh->posDecomp += rlen; heatshrink_decoder_poll(dec, buff, buf_cap - decoded_bytes, &decoded_chunk_len);
buff += rlen; fh->posDecomp += decoded_chunk_len;
decoded += rlen; buff += decoded_chunk_len;
decoded_bytes += decoded_chunk_len;
if (elen == 0) {
if (fh->posDecomp == fdlen) { 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); heatshrink_decoder_finish(dec);
} }
return (int) decoded; return (int) decoded_bytes;
} }
} }
return (int) len; return (int) buf_cap;
} }
return 0; return 0;
} }

@ -3,6 +3,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include "espfsformat.h" #include "espfsformat.h"
#include "heatshrink_decoder.h"
typedef enum { typedef enum {
ESPFS_INIT_RESULT_OK, ESPFS_INIT_RESULT_OK,
@ -17,6 +18,14 @@ struct EspFsWalk {
}; };
typedef struct EspFsWalk 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 */ /** Init filesystem walk */
void espFsWalkInit(EspFsWalk *walk); void espFsWalkInit(EspFsWalk *walk);
/** /**
@ -38,5 +47,5 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos);
EspFsInitResult espFsInit(); EspFsInitResult espFsInit();
EspFsFile *espFsOpen(const char *fileName); EspFsFile *espFsOpen(const char *fileName);
int espFsFlags(EspFsFile *fh); 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); void espFsClose(EspFsFile *fh);

@ -88,7 +88,7 @@ struct WebsockPriv {
static Websock *llStart = NULL; 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]; uint8_t buf[14];
int i = 0; int i = 0;
@ -111,10 +111,10 @@ static int sendFrameHead(Websock *ws, int opcode, int len)
buf[i++] = len; buf[i++] = len;
} }
// ws_dbg("WS: Sent frame head for payload of %d bytes.", 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 r = 0;
int fl = 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. //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; Websock *lw = llStart;
int ret = 0; 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 */ /** 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; Websock *lw = llStart;
int bMax = 0; size_t bMax = 0;
int bTotal = 0; size_t bTotal = 0;
while (lw != NULL) { while (lw != NULL) {
if (strcmp(lw->conn->url, resource) == 0) { if (strcmp(lw->conn->url, resource) == 0) {
//lw->conn //lw->conn
int bs = httpGetBacklogSize(lw->conn); size_t bs = httpGetBacklogSize(lw->conn);
bTotal += bs; bTotal += bs;
if (bs > bMax) { bMax = 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) 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); sendFrameHead(ws, FLAG_FIN | OPCODE_CLOSE, 2);
httpdSend(ws->conn, rs, 2); httpdSend(ws->conn, rs, 2);
ws->priv->closedHere = 1; ws->priv->closedHere = 1;
@ -210,13 +210,13 @@ static void websockFree(Websock *ws)
if (ws->priv) { httpdPlatFree(ws->priv); } 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; httpd_cgi_state r = HTTPD_CGI_MORE;
int wasHeaderByte; int wasHeaderByte;
Websock *ws = (Websock *) connData->cgiData; 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]); // httpd_printf("Ws: State %d byte 0x%02X\n", ws->priv->wsStatus, data[i]);
wasHeaderByte = 1; wasHeaderByte = 1;
if (ws->priv->wsStatus == ST_FLAGS) { if (ws->priv->wsStatus == ST_FLAGS) {
@ -261,7 +261,7 @@ httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, char *data, int len)
//First, unmask the data //First, unmask the data
sl = len - i; 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); // 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]); } for (j = 0; j < sl; j++) { data[i + j] ^= (ws->priv->fr.mask[(ws->priv->maskCtr++) & 3]); }
// if (DEBUG_WS) { // 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 || } 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_BINARY ||
(ws->priv->fr.flags & OPCODE_MASK) == OPCODE_CONTINUE) { (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)) { if (!(ws->priv->fr.len8 & IS_MASKED)) {
//We're a server; client should send us masked packets. //We're a server; client should send us masked packets.
cgiWebsocketClose(ws, 1002); cgiWebsocketClose(ws, 1002);

@ -162,7 +162,7 @@ const char *httpdGetVersion(void)
return HTTPDVER; return HTTPDVER;
} }
int httpGetBacklogSize(const HttpdConnData *conn) size_t httpGetBacklogSize(const HttpdConnData *conn)
{ {
HttpSendBacklogItem *bl = conn->priv->sendBacklog; HttpSendBacklogItem *bl = conn->priv->sendBacklog;
if (!bl) { return 0; } if (!bl) { return 0; }
@ -193,6 +193,9 @@ static HttpdConnData *httpdFindConnData(ConnTypePtr conn, const char *remIp, int
//Retires a connection for re-use //Retires a connection for re-use
static void httpdRetireConn(HttpdConnData *conn) static void httpdRetireConn(HttpdConnData *conn)
{ {
if (!conn) {
return;
}
if (conn->priv->sendBacklog != NULL) { if (conn->priv->sendBacklog != NULL) {
HttpSendBacklogItem *i, *j; HttpSendBacklogItem *i, *j;
i = conn->priv->sendBacklog; 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->buff != NULL) { httpdPlatFree(conn->post->buff); }
if (conn->post != NULL) { httpdPlatFree(conn->post); } if (conn->post != NULL) { httpdPlatFree(conn->post); }
if (conn->priv != NULL) { httpdPlatFree(conn->priv); } if (conn->priv != NULL) { httpdPlatFree(conn->priv); }
if (conn) { httpdPlatFree(conn); } httpdPlatFree(conn);
for (int i = 0; i < HTTPD_MAX_CONNECTIONS; i++) { for (int i = 0; i < HTTPD_MAX_CONNECTIONS; i++) {
if (s_connData[i] == conn) { s_connData[i] = NULL; } if (s_connData[i] == conn) { s_connData[i] = NULL; }
} }
} }
//Stupid li'l helper function that returns the value of a hex char. //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 >= '0' && c <= '9') { return c - '0'; }
if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } 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 httpdUrlDecode(const char *val, int valLen, char *ret, int retLen)
{ {
int s = 0, d = 0; int s = 0, d = 0;
int esced = 0, escVal = 0; int esced = 0;
char escVal = 0;
while (s < valLen && d < retLen) { while (s < valLen && d < retLen) {
if (esced == 1) { if (esced == 1) {
escVal = httpdHexVal(val[s]) << 4; escVal = httpdHexVal(val[s]) << 4;
esced = 2; esced = 2;
} else if (esced == 2) { } else if (esced == 2) {
escVal += httpdHexVal(val[s]); escVal |= httpdHexVal(val[s]);
ret[d++] = escVal; ret[d++] = escVal;
esced = 0; esced = 0;
} else if (val[s] == '%') { } 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); const int arglen = (int) strlen(arg);
p = line; p = line;
while (p != NULL && *p != '\n' && *p != '\r' && *p != 0) { 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] == '=') { if (strstarts(p, arg) && p[arglen] == '=') {
p += arglen + 1; //move p to start of value p += arglen + 1; //move p to start of value
e = strstr(p, "&"); e = strstr(p, "&");
if (e == NULL) { e = p + strlen(p); } if (e == NULL) { e = p + strlen(p); }
router_dbg("findArg: val %s len %d", p, (e - p)); router_dbg("findArg: val %s len %d", p, (int) (e - p));
return httpdUrlDecode(p, (e - p), buff, buffLen); return httpdUrlDecode(p, (int)(e - p), buff, buffLen);
} }
p = strstr(p, "&"); p = strstr(p, "&");
if (p != NULL) { p += 1; } 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 return -1; //not found
} }
@ -339,28 +343,28 @@ void httpdStartResponse(HttpdConnData *conn, int code)
code2str(code), code2str(code),
serverName, serverName,
connStr); connStr);
httpdSend(conn, buff, l); httpdSendStrN(conn, buff, l);
if (0 == (conn->priv->flags & HFL_NOCORS)) { if (0 == (conn->priv->flags & HFL_NOCORS)) {
// CORS headers // CORS headers
httpdSend(conn, "Access-Control-Allow-Origin: *\r\n", -1); httpdSendStr(conn, "Access-Control-Allow-Origin: *\r\n");
httpdSend(conn, "Access-Control-Allow-Methods: GET,POST,OPTIONS\r\n", -1); httpdSendStr(conn, "Access-Control-Allow-Methods: GET,POST,OPTIONS\r\n");
} }
} }
//Send a http header. //Send a http header.
void httpdHeader(HttpdConnData *conn, const char *field, const char *val) void httpdHeader(HttpdConnData *conn, const char *field, const char *val)
{ {
httpdSend(conn, field, -1); httpdSendStr(conn, field);
httpdSend(conn, ": ", -1); httpdSendStr(conn, ": ");
httpdSend(conn, val, -1); httpdSendStr(conn, val);
httpdSend(conn, "\r\n", -1); httpdSendStr(conn, "\r\n");
} }
//Finish the headers. //Finish the headers.
void httpdEndHeaders(HttpdConnData *conn) void httpdEndHeaders(HttpdConnData *conn)
{ {
httpdSend(conn, "\r\n", -1); httpdSendStr(conn, "\r\n");
conn->priv->flags |= HFL_SENDINGBODY; conn->priv->flags |= HFL_SENDINGBODY;
} }
@ -371,8 +375,8 @@ void httpdRedirect(HttpdConnData *conn, const char *newUrl)
httpdStartResponse(conn, 302); httpdStartResponse(conn, 302);
httpdHeader(conn, "Location", newUrl); httpdHeader(conn, "Location", newUrl);
httpdEndHeaders(conn); httpdEndHeaders(conn);
httpdSend(conn, "Moved to ", -1); httpdSendStr(conn, "Moved to ");
httpdSend(conn, newUrl, -1); httpdSendStr(conn, newUrl);
} }
//Use this as a cgi function to redirect one url to another. //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; } if (connData->conn == NULL) { return HTTPD_CGI_DONE; }
httpdStartResponse(connData, 404); httpdStartResponse(connData, 404);
httpdEndHeaders(connData); httpdEndHeaders(connData);
httpdSend(connData, "404 File not found.", -1); httpdSendStr(connData, "404 File not found.");
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
@ -406,7 +410,6 @@ httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData)
static const char hostFmt[] = "http://%s/"; static const char hostFmt[] = "http://%s/";
char *buff; char *buff;
int isIP = 0; int isIP = 0;
int x;
if (connData->conn == NULL) { if (connData->conn == NULL) {
//Connection aborted. Clean up. //Connection aborted. Clean up.
return HTTPD_CGI_DONE; 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 //Quick and dirty code to see if host is an IP
if (strlen(connData->hostName) > 8) { if (strlen(connData->hostName) > 8) {
isIP = 1; 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; } 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 //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. //the data is seen as a C-string.
//Returns 1 for success, 0 for out-of-memory. //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 (conn->conn == NULL) { return 0; }
if (len < 0) { len = strlen(data); }
if (len == 0) { return 0; } if (len == 0) { return 0; }
if (conn->priv->flags & HFL_CHUNKED && conn->priv->flags & HFL_SENDINGBODY && conn->priv->chunkHdr == NULL) { 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; } 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) static char httpdHexNibble(int val)
{ {
val &= 0xf; val &= 0xf;
if (val < 10) { return '0' + val; } if (val < 10) { return (char) ('0' + val); }
return 'A' + (val - 10); 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 */ /* 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; int start = 0, end = 0;
char c; uint8_t c;
if (conn->conn == NULL) { return 0; } 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; } if (len == 0) { return 0; }
for (end = 0; end < len; end++) { for (end = 0; end < len; end++) {
@ -491,10 +495,10 @@ int httpdSend_html(HttpdConnData *conn, const char *data, int len)
start = end + 1; start = end + 1;
} }
if (c == '"') httpdSend_orDie(conn, "&#34;", 5); if (c == '"') httpdSendStr_orDie(conn, "&#34;");
else if (c == '\'') httpdSend_orDie(conn, "&#39;", 5); else if (c == '\'') httpdSendStr_orDie(conn, "&#39;");
else if (c == '<') httpdSend_orDie(conn, "&lt;", 4); else if (c == '<') httpdSendStr_orDie(conn, "&lt;");
else if (c == '>') httpdSend_orDie(conn, "&gt;", 4); else if (c == '>') httpdSendStr_orDie(conn, "&gt;");
} }
if (start < end) httpdSend_orDie(conn, data + start, end - start); 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 */ /* 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; int start = 0, end = 0;
char c; uint8_t c;
if (conn->conn == NULL) { return 0; } 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; } if (len == 0) { return 0; }
for (end = 0; end < len; end++) { for (end = 0; end < len; end++) {
@ -522,13 +526,13 @@ int httpdSend_js(HttpdConnData *conn, const char *data, int len)
start = end + 1; start = end + 1;
} }
if (c == '"') httpdSend_orDie(conn, "\\\"", 2); if (c == '"') httpdSendStr_orDie(conn, "\\\"");
else if (c == '\'') httpdSend_orDie(conn, "\\'", 2); else if (c == '\'') httpdSendStr_orDie(conn, "\\'");
else if (c == '\\') httpdSend_orDie(conn, "\\\\", 2); else if (c == '\\') httpdSendStr_orDie(conn, "\\\\");
else if (c == '<') httpdSend_orDie(conn, "\\u003C", 6); else if (c == '<') httpdSendStr_orDie(conn, "\\u003C");
else if (c == '>') httpdSend_orDie(conn, "\\u003E", 6); else if (c == '>') httpdSendStr_orDie(conn, "\\u003E");
else if (c == '\n') httpdSend_orDie(conn, "\\n", 2); else if (c == '\n') httpdSendStr_orDie(conn, "\\n");
else if (c == '\r') httpdSend_orDie(conn, "\\r", 2); else if (c == '\r') httpdSendStr_orDie(conn, "\\r");
} }
if (start < end) httpdSend_orDie(conn, data + start, end - start); if (start < end) httpdSend_orDie(conn, data + start, end - start);
@ -546,7 +550,7 @@ bool httpdFlushSendBuffer(HttpdConnData *conn)
if (conn->priv->chunkHdr != NULL) { if (conn->priv->chunkHdr != NULL) {
//We're sending chunked data, and the chunk needs fixing up. //We're sending chunked data, and the chunk needs fixing up.
//Finish chunk with cr/lf //Finish chunk with cr/lf
httpdSend(conn, "\r\n", 2); httpdSendStr(conn, "\r\n");
//Calculate length of chunk //Calculate length of chunk
len = ((&conn->priv->sendBuff[conn->priv->sendBuffLen]) - conn->priv->chunkHdr) - 8; len = ((&conn->priv->sendBuff[conn->priv->sendBuffLen]) - conn->priv->chunkHdr) - 8;
//Fix up chunk header to correct value //Fix up chunk header to correct value
@ -687,7 +691,7 @@ static void httpdProcessRequest(HttpdConnData *conn)
int r; int r;
int i = 0; int i = 0;
if (conn->url == NULL) { if (conn->url == NULL) {
router_warn("WtF? url = NULL"); router_warn("WtF? url = NULL");
return; //Shouldn't happen return; //Shouldn't happen
} }
@ -698,7 +702,7 @@ static void httpdProcessRequest(HttpdConnData *conn)
httpdEndHeaders(conn); httpdEndHeaders(conn);
httpdCgiIsDone(conn); httpdCgiIsDone(conn);
router_dbg("CORS preflight resp sent."); router_dbg("CORS preflight resp sent.");
return; return;
} }
@ -708,7 +712,7 @@ static void httpdProcessRequest(HttpdConnData *conn)
//Look up URL in the built-in URL table. //Look up URL in the built-in URL table.
while (builtInUrls[i].url != NULL) { while (builtInUrls[i].url != NULL) {
int match = 0; int match = 0;
const char const *route = builtInUrls[i].url; const char *route = builtInUrls[i].url;
//See if there's a literal match //See if there's a literal match
if (streq(route, conn->url)) { match = 1; } if (streq(route, conn->url)) { match = 1; }
//See if there's a wildcard match (*) //See if there's a wildcard match (*)
@ -723,7 +727,7 @@ static void httpdProcessRequest(HttpdConnData *conn)
match = 1; match = 1;
} }
if (match) { if (match) {
router_dbg("Matched route #%d, url=%s", i, route); router_dbg("Matched route #%d, url=%s", i, route);
conn->cgiData = NULL; conn->cgiData = NULL;
conn->cgi = builtInUrls[i].cgiCb; conn->cgi = builtInUrls[i].cgiCb;
conn->cgiArg = builtInUrls[i].cgiArg; conn->cgiArg = builtInUrls[i].cgiArg;
@ -735,7 +739,7 @@ static void httpdProcessRequest(HttpdConnData *conn)
if (builtInUrls[i].url == NULL) { if (builtInUrls[i].url == NULL) {
//Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just //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. //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; conn->cgi = cgiNotFound;
} }
@ -1075,7 +1079,7 @@ httpd_thread_handle_t *httpdInit(const HttpdBuiltInUrl *fixedUrls, struct httpd_
return httpdPlatStart(options); return httpdPlatStart(options);
} }
void httpdJoin(httpd_thread_handle_t * handle) void httpdJoin(httpd_thread_handle_t *handle)
{ {
httpdPlatJoin(handle); httpdPlatJoin(handle);
} }

@ -86,7 +86,7 @@ serveStaticFile(HttpdConnData *connData, const char *filepath)
{ {
EspFsFile *file = connData->cgiData; EspFsFile *file = connData->cgiData;
int len; int len;
char buff[FILE_CHUNK_LEN + 1]; uint8_t buff[FILE_CHUNK_LEN + 1];
char acceptEncodingBuffer[64 + 1]; char acceptEncodingBuffer[64 + 1];
int isGzip; int isGzip;
@ -127,7 +127,7 @@ serveStaticFile(HttpdConnData *connData, const char *filepath)
httpdGetHeader(connData, "Accept-Encoding", acceptEncodingBuffer, 64); httpdGetHeader(connData, "Accept-Encoding", acceptEncodingBuffer, 64);
if (strstr(acceptEncodingBuffer, "gzip") == NULL) { if (strstr(acceptEncodingBuffer, "gzip") == NULL) {
//No Accept-Encoding: gzip header present //No Accept-Encoding: gzip header present
httpdSend(connData, gzipNonSupportedMessage, -1); httpdSendStr(connData, gzipNonSupportedMessage);
espFsClose(file); espFsClose(file);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
@ -202,9 +202,13 @@ tplSend(HttpdConnData *conn, const char *str, int len)
if (conn == NULL) { return 0; } if (conn == NULL) { return 0; }
TplData *tpd = conn->cgiData; TplData *tpd = conn->cgiData;
if (tpd == NULL || tpd->tokEncode == ENCODE_PLAIN) { return httpdSend(conn, str, len); } if (tpd == NULL || tpd->tokEncode == ENCODE_PLAIN) {
if (tpd->tokEncode == ENCODE_HTML) { return httpdSend_html(conn, str, len); } return httpdSendStrN(conn, str, len);
if (tpd->tokEncode == ENCODE_JS) { return httpdSend_js(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; return 0;
} }
@ -281,7 +285,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData)
sp = tpd->buff_sp; sp = tpd->buff_sp;
x = tpd->buff_x; x = tpd->buff_x;
} else { } else {
len = espFsRead(tpd->file, buff, FILE_CHUNK_LEN); len = espFsRead(tpd->file, (uint8_t *) buff, FILE_CHUNK_LEN);
tpd->buff_len = len; tpd->buff_len = len;
e = buff; e = buff;
@ -295,7 +299,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData)
//Inside ordinary text. //Inside ordinary text.
if (buff[x] == '%') { if (buff[x] == '%') {
//Send raw data up to now //Send raw data up to now
if (sp != 0) { httpdSend(connData, e, sp); } if (sp != 0) { httpdSendStrN(connData, e, sp); }
sp = 0; sp = 0;
//Go collect token chars. //Go collect token chars.
tpd->tokenPos = 0; tpd->tokenPos = 0;
@ -307,7 +311,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData)
if (tpd->tokenPos == 0) { if (tpd->tokenPos == 0) {
//This is the second % of a %% escape string. //This is the second % of a %% escape string.
//Send a single % and resume with the normal program flow. //Send a single % and resume with the normal program flow.
httpdSend(connData, "%", 1); httpdSendStrN(connData, "%", 1);
} else { } else {
if (!tpd->chunk_resume) { if (!tpd->chunk_resume) {
//This is an actual token. //This is an actual token.
@ -365,12 +369,12 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData)
c != '.' && c != '_' && c != '-' && c != ':' c != '.' && c != '_' && c != '-' && c != ':'
)) { )) {
// looks like we collected some garbage // looks like we collected some garbage
httpdSend(connData, "%", 1); httpdSendStrN(connData, "%", 1);
if (tpd->tokenPos > 0) { if (tpd->tokenPos > 0) {
httpdSend(connData, tpd->token, tpd->tokenPos); httpdSendStrN(connData, tpd->token, tpd->tokenPos);
} }
// the bad char // the bad char
httpdSend(connData, &c, 1); httpdSendStrN(connData, &c, 1);
//Go collect normal chars again. //Go collect normal chars again.
e = &buff[x + 1]; e = &buff[x + 1];
@ -389,7 +393,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData)
} }
//Send remaining bit. //Send remaining bit.
if (sp != 0) { httpdSend(connData, e, sp); } if (sp != 0) { httpdSendStrN(connData, e, sp); }
if (len != FILE_CHUNK_LEN) { if (len != FILE_CHUNK_LEN) {
//We're done. //We're done.
((TplCallback) (connData->cgiArg))(connData, NULL, &tpd->tplArg); ((TplCallback) (connData->cgiArg))(connData, NULL, &tpd->tplArg);

Loading…
Cancel
Save