|
|
|
@ -15,11 +15,11 @@ Connector to let httpd use the espfs filesystem to serve the files in it. |
|
|
|
|
#include <string.h> |
|
|
|
|
#include "httpd.h" |
|
|
|
|
#include "httpd-platform.h" |
|
|
|
|
#include "httpd-espfs.h" |
|
|
|
|
#include "espfs.h" |
|
|
|
|
#include "espfsformat.h" |
|
|
|
|
#include "cgi-espfs.h" |
|
|
|
|
#include "httpd-logging.h" |
|
|
|
|
#include "httpd-utils.h" |
|
|
|
|
#include "espfs.h" |
|
|
|
|
#include "espfsformat.h" |
|
|
|
|
|
|
|
|
|
#define FILE_CHUNK_LEN 1024 |
|
|
|
|
|
|
|
|
@ -82,8 +82,7 @@ EspFsFile *tryOpenIndex(const char *path) |
|
|
|
|
return NULL; // failed to guess the right name
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
httpd_cgi_state |
|
|
|
|
serveStaticFile(HttpdConnData *connData, const char *filepath) |
|
|
|
|
static httpd_cgi_state serveStaticFile(HttpdConnData *connData, const char *filepath) |
|
|
|
|
{ |
|
|
|
|
EspFsFile *file = connData->cgiData; |
|
|
|
|
int len; |
|
|
|
@ -165,7 +164,7 @@ serveStaticFile(HttpdConnData *connData, const char *filepath) |
|
|
|
|
//This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding
|
|
|
|
|
//path in the filesystem and if it exists, passes the file through. This simulates what a normal
|
|
|
|
|
//webserver would do with static files.
|
|
|
|
|
httpd_cgi_state cgiEspFsHook(HttpdConnData *connData) |
|
|
|
|
httpd_cgi_state cgiEspFsStaticFile(HttpdConnData *connData) |
|
|
|
|
{ |
|
|
|
|
const char *filepath = (connData->cgiArg == NULL) ? connData->url : (char *) connData->cgiArg; |
|
|
|
|
return serveStaticFile(connData, filepath); |
|
|
|
@ -182,11 +181,14 @@ typedef enum { |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
EspFsFile *file; |
|
|
|
|
void *tplArg; |
|
|
|
|
char token[64]; |
|
|
|
|
|
|
|
|
|
// this is the struct passed to user
|
|
|
|
|
TplData td; |
|
|
|
|
|
|
|
|
|
int tokenPos; |
|
|
|
|
|
|
|
|
|
char buff[FILE_CHUNK_LEN + 1]; |
|
|
|
|
char token[HTTPD_ESPFS_TOKEN_LEN]; |
|
|
|
|
|
|
|
|
|
bool chunk_resume; |
|
|
|
|
int buff_len; |
|
|
|
@ -194,99 +196,111 @@ typedef struct { |
|
|
|
|
int buff_sp; |
|
|
|
|
char *buff_e; |
|
|
|
|
TplEncode tokEncode; |
|
|
|
|
} TplData; |
|
|
|
|
} TplDataInternal; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int tplSend(HttpdConnData *conn, const char *str, int len) |
|
|
|
|
int tplSendN(TplData *td, const char *str, int len) |
|
|
|
|
{ |
|
|
|
|
if (conn == NULL) { return 0; } |
|
|
|
|
TplData *tpd = conn->cgiData; |
|
|
|
|
|
|
|
|
|
if (tpd == NULL || tpd->tokEncode == ENCODE_PLAIN) { |
|
|
|
|
return httpdSendStrN(conn, str, len); |
|
|
|
|
} else if (tpd->tokEncode == ENCODE_HTML) { |
|
|
|
|
return httpdSend_html(conn, str, len); |
|
|
|
|
} else if (tpd->tokEncode == ENCODE_JS) { |
|
|
|
|
return httpdSend_js(conn, str, len); |
|
|
|
|
if (td == NULL) { return 0; } |
|
|
|
|
TplDataInternal *tdi = container_of(td, TplDataInternal, td); |
|
|
|
|
|
|
|
|
|
if (tdi->tokEncode == ENCODE_PLAIN) { |
|
|
|
|
return httpdSendStrN(td->conn, str, len); |
|
|
|
|
} else if (tdi->tokEncode == ENCODE_HTML) { |
|
|
|
|
return httpdSend_html(td->conn, str, len); |
|
|
|
|
} else if (tdi->tokEncode == ENCODE_JS) { |
|
|
|
|
return httpdSend_js(td->conn, str, len); |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData) |
|
|
|
|
httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn) |
|
|
|
|
{ |
|
|
|
|
TplData *tpd = connData->cgiData; |
|
|
|
|
TplDataInternal *tdi = conn->cgiData; |
|
|
|
|
|
|
|
|
|
int len; |
|
|
|
|
int x, sp = 0; |
|
|
|
|
char *e = NULL; |
|
|
|
|
int tokOfs; |
|
|
|
|
|
|
|
|
|
if (connData->conn == NULL) { |
|
|
|
|
if (conn->conn == NULL) { |
|
|
|
|
//Connection aborted. Clean up.
|
|
|
|
|
((TplCallback) (connData->cgiArg))(connData, NULL, &tpd->tplArg); |
|
|
|
|
espFsClose(tpd->file); |
|
|
|
|
httpdPlatFree(tpd); |
|
|
|
|
if (tdi) { |
|
|
|
|
TplCallback callback = (TplCallback) conn->cgiArg; |
|
|
|
|
if (callback) { |
|
|
|
|
tdi->td.conn = NULL; // indicate that this is the final call
|
|
|
|
|
tdi->td.token = NULL; |
|
|
|
|
callback(&tdi->td); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
espFsClose(tdi->file); |
|
|
|
|
httpdPlatFree(tdi); |
|
|
|
|
} |
|
|
|
|
return HTTPD_CGI_DONE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (tpd == NULL) { |
|
|
|
|
if (tdi == NULL) { |
|
|
|
|
//First call to this cgi. Open the file so we can read it.
|
|
|
|
|
tpd = (TplData *) httpdPlatMalloc(sizeof(TplData)); |
|
|
|
|
if (tpd == NULL) { |
|
|
|
|
tdi = (TplDataInternal *) httpdPlatMalloc(sizeof(TplDataInternal)); |
|
|
|
|
if (tdi == NULL) { |
|
|
|
|
espfs_error("Failed to malloc tpl struct"); |
|
|
|
|
return HTTPD_CGI_NOTFOUND; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tpd->chunk_resume = false; |
|
|
|
|
tdi->chunk_resume = false; |
|
|
|
|
|
|
|
|
|
const char *filepath = connData->url; |
|
|
|
|
const char *filepath = conn->url; |
|
|
|
|
// check for custom template URL
|
|
|
|
|
if (connData->cgiArg2 != NULL) { |
|
|
|
|
filepath = connData->cgiArg2; |
|
|
|
|
if (conn->cgiArg2 != NULL) { |
|
|
|
|
filepath = conn->cgiArg2; |
|
|
|
|
espfs_dbg("Using filepath %s", filepath); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tpd->file = espFsOpen(filepath); |
|
|
|
|
tdi->file = espFsOpen(filepath); |
|
|
|
|
|
|
|
|
|
if (tpd->file == NULL) { |
|
|
|
|
if (tdi->file == NULL) { |
|
|
|
|
// maybe a folder, look for index file
|
|
|
|
|
tpd->file = tryOpenIndex(filepath); |
|
|
|
|
if (tpd->file == NULL) { |
|
|
|
|
httpdPlatFree(tpd); |
|
|
|
|
tdi->file = tryOpenIndex(filepath); |
|
|
|
|
if (tdi->file == NULL) { |
|
|
|
|
espfs_error("cgiEspFsTemplate: Couldn't find template for path: %s", conn->url); |
|
|
|
|
httpdPlatFree(tdi); |
|
|
|
|
return HTTPD_CGI_NOTFOUND; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tpd->tplArg = NULL; |
|
|
|
|
tpd->tokenPos = -1; |
|
|
|
|
if (espFsFlags(tpd->file) & FLAG_GZIP) { |
|
|
|
|
espfs_error("cgiEspFsTemplate: Trying to use gzip-compressed file %s as template!", connData->url); |
|
|
|
|
espFsClose(tpd->file); |
|
|
|
|
httpdPlatFree(tpd); |
|
|
|
|
if (espFsFlags(tdi->file) & FLAG_GZIP) { |
|
|
|
|
espfs_error("cgiEspFsTemplate: Trying to use gzip-compressed file %s as template!", conn->url); |
|
|
|
|
espFsClose(tdi->file); |
|
|
|
|
httpdPlatFree(tdi); |
|
|
|
|
return HTTPD_CGI_NOTFOUND; |
|
|
|
|
} |
|
|
|
|
connData->cgiData = tpd; |
|
|
|
|
httpdStartResponse(connData, 200); |
|
|
|
|
const char *mime = httpdGetMimetype(connData->url); |
|
|
|
|
httpdHeader(connData, "Content-Type", mime); |
|
|
|
|
httpdAddCacheHeaders(connData, mime); |
|
|
|
|
httpdEndHeaders(connData); |
|
|
|
|
|
|
|
|
|
tdi->td.conn = conn; |
|
|
|
|
tdi->td.userData = NULL; |
|
|
|
|
|
|
|
|
|
tdi->tokenPos = -1; |
|
|
|
|
conn->cgiData = tdi; |
|
|
|
|
httpdStartResponse(conn, 200); |
|
|
|
|
const char *mime = httpdGetMimetype(conn->url); |
|
|
|
|
httpdHeader(conn, "Content-Type", mime); |
|
|
|
|
httpdAddCacheHeaders(conn, mime); |
|
|
|
|
httpdEndHeaders(conn); |
|
|
|
|
return HTTPD_CGI_MORE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
char *buff = tpd->buff; |
|
|
|
|
char *buff = tdi->buff; |
|
|
|
|
|
|
|
|
|
// resume the parser state from the last token,
|
|
|
|
|
// if subst. func wants more data to be sent.
|
|
|
|
|
if (tpd->chunk_resume) { |
|
|
|
|
if (tdi->chunk_resume) { |
|
|
|
|
//espfs_dbg("Resuming tpl parser for multi-part subst");
|
|
|
|
|
len = tpd->buff_len; |
|
|
|
|
e = tpd->buff_e; |
|
|
|
|
sp = tpd->buff_sp; |
|
|
|
|
x = tpd->buff_x; |
|
|
|
|
len = tdi->buff_len; |
|
|
|
|
e = tdi->buff_e; |
|
|
|
|
sp = tdi->buff_sp; |
|
|
|
|
x = tdi->buff_x; |
|
|
|
|
} else { |
|
|
|
|
len = espFsRead(tpd->file, (uint8_t *) buff, FILE_CHUNK_LEN); |
|
|
|
|
tpd->buff_len = len; |
|
|
|
|
len = espFsRead(tdi->file, (uint8_t *) buff, FILE_CHUNK_LEN); |
|
|
|
|
tdi->buff_len = len; |
|
|
|
|
|
|
|
|
|
e = buff; |
|
|
|
|
sp = 0; |
|
|
|
@ -295,111 +309,113 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *connData) |
|
|
|
|
|
|
|
|
|
if (len > 0) { |
|
|
|
|
for (; x < len; x++) { |
|
|
|
|
if (tpd->tokenPos == -1) { |
|
|
|
|
char c = buff[x]; |
|
|
|
|
|
|
|
|
|
if (tdi->tokenPos == -1) { |
|
|
|
|
//Inside ordinary text.
|
|
|
|
|
if (buff[x] == '%') { |
|
|
|
|
if (c == '%') { |
|
|
|
|
//Send raw data up to now
|
|
|
|
|
if (sp != 0) { httpdSendStrN(connData, e, sp); } |
|
|
|
|
if (sp != 0) { httpdSendStrN(conn, e, sp); } |
|
|
|
|
sp = 0; |
|
|
|
|
//Go collect token chars.
|
|
|
|
|
tpd->tokenPos = 0; |
|
|
|
|
tdi->tokenPos = 0; |
|
|
|
|
} else { |
|
|
|
|
sp++; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if (buff[x] == '%') { |
|
|
|
|
if (tpd->tokenPos == 0) { |
|
|
|
|
if (c == '%') { |
|
|
|
|
if (tdi->tokenPos == 0) { |
|
|
|
|
//This is the second % of a %% escape string.
|
|
|
|
|
//Send a single % and resume with the normal program flow.
|
|
|
|
|
httpdSendStrN(connData, "%", 1); |
|
|
|
|
httpdSendStrN(conn, "%", 1); |
|
|
|
|
} else { |
|
|
|
|
if (!tpd->chunk_resume) { |
|
|
|
|
if (!tdi->chunk_resume) { |
|
|
|
|
//This is an actual token.
|
|
|
|
|
tpd->token[tpd->tokenPos++] = 0; //zero-terminate token
|
|
|
|
|
|
|
|
|
|
tokOfs = 0; |
|
|
|
|
tpd->tokEncode = ENCODE_PLAIN; |
|
|
|
|
if (strneq(tpd->token, "html:", 5)) { |
|
|
|
|
tokOfs = 5; |
|
|
|
|
tpd->tokEncode = ENCODE_HTML; |
|
|
|
|
} else if (strneq(tpd->token, "h:", 2)) { |
|
|
|
|
tokOfs = 2; |
|
|
|
|
tpd->tokEncode = ENCODE_HTML; |
|
|
|
|
} else if (strneq(tpd->token, "js:", 3)) { |
|
|
|
|
tokOfs = 3; |
|
|
|
|
tpd->tokEncode = ENCODE_JS; |
|
|
|
|
} else if (strneq(tpd->token, "j:", 2)) { |
|
|
|
|
tokOfs = 2; |
|
|
|
|
tpd->tokEncode = ENCODE_JS; |
|
|
|
|
tdi->token[tdi->tokenPos++] = 0; //zero-terminate token
|
|
|
|
|
|
|
|
|
|
int prefixLen = 0; |
|
|
|
|
tdi->tokEncode = ENCODE_PLAIN; |
|
|
|
|
if (strneq(tdi->token, "html:", 5)) { |
|
|
|
|
prefixLen = 5; |
|
|
|
|
tdi->tokEncode = ENCODE_HTML; |
|
|
|
|
} else if (strneq(tdi->token, "h:", 2)) { |
|
|
|
|
prefixLen = 2; |
|
|
|
|
tdi->tokEncode = ENCODE_HTML; |
|
|
|
|
} else if (strneq(tdi->token, "js:", 3)) { |
|
|
|
|
prefixLen = 3; |
|
|
|
|
tdi->tokEncode = ENCODE_JS; |
|
|
|
|
} else if (strneq(tdi->token, "j:", 2)) { |
|
|
|
|
prefixLen = 2; |
|
|
|
|
tdi->tokEncode = ENCODE_JS; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// do the shifting
|
|
|
|
|
if (tokOfs > 0) { |
|
|
|
|
for (int i = tokOfs; i <= tpd->tokenPos; i++) { |
|
|
|
|
tpd->token[i - tokOfs] = tpd->token[i]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
tdi->td.token = &tdi->token[prefixLen]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tpd->chunk_resume = false; |
|
|
|
|
tdi->chunk_resume = false; |
|
|
|
|
|
|
|
|
|
httpd_cgi_state status = ((TplCallback) (connData->cgiArg))(connData, tpd->token, &tpd->tplArg); |
|
|
|
|
TplCallback callback = (TplCallback) conn->cgiArg; |
|
|
|
|
httpd_cgi_state status = callback(&tdi->td); |
|
|
|
|
if (status == HTTPD_CGI_MORE) { |
|
|
|
|
// espfs_dbg("Multi-part tpl subst, saving parser state");
|
|
|
|
|
// wants to send more in this token's place.....
|
|
|
|
|
tpd->chunk_resume = true; |
|
|
|
|
tpd->buff_len = len; |
|
|
|
|
tpd->buff_e = e; |
|
|
|
|
tpd->buff_sp = sp; |
|
|
|
|
tpd->buff_x = x; |
|
|
|
|
tdi->chunk_resume = true; |
|
|
|
|
tdi->buff_len = len; |
|
|
|
|
tdi->buff_e = e; |
|
|
|
|
tdi->buff_sp = sp; |
|
|
|
|
tdi->buff_x = x; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
//Go collect normal chars again.
|
|
|
|
|
e = &buff[x + 1]; |
|
|
|
|
tpd->tokenPos = -1; |
|
|
|
|
tdi->tokenPos = -1; |
|
|
|
|
} else { |
|
|
|
|
// Add char to the token buf
|
|
|
|
|
char c = buff[x]; |
|
|
|
|
bool outOfSpace = tpd->tokenPos >= ((int) sizeof(tpd->token) - 1); |
|
|
|
|
if (outOfSpace || |
|
|
|
|
(!(c >= 'a' && c <= 'z') && |
|
|
|
|
!(c >= 'A' && c <= 'Z') && |
|
|
|
|
!(c >= '0' && c <= '9') && |
|
|
|
|
c != '.' && c != '_' && c != '-' && c != ':' |
|
|
|
|
)) { |
|
|
|
|
// looks like we collected some garbage
|
|
|
|
|
httpdSendStrN(connData, "%", 1); |
|
|
|
|
if (tpd->tokenPos > 0) { |
|
|
|
|
httpdSendStrN(connData, tpd->token, tpd->tokenPos); |
|
|
|
|
bool outOfSpace = tdi->tokenPos >= ((int) sizeof(tdi->token) - 1); |
|
|
|
|
if (outOfSpace) { |
|
|
|
|
// looks like we collected some garbage, put it back
|
|
|
|
|
httpdSendStrN(conn, "%", 1); |
|
|
|
|
if (tdi->tokenPos > 0) { |
|
|
|
|
httpdSendStrN(conn, tdi->token, tdi->tokenPos); |
|
|
|
|
} |
|
|
|
|
// the bad char
|
|
|
|
|
httpdSendStrN(connData, &c, 1); |
|
|
|
|
httpdSendStrN(conn, &c, 1); |
|
|
|
|
|
|
|
|
|
//Go collect normal chars again.
|
|
|
|
|
e = &buff[x + 1]; |
|
|
|
|
tpd->tokenPos = -1; |
|
|
|
|
tdi->tokenPos = -1; |
|
|
|
|
} else { |
|
|
|
|
// collect it
|
|
|
|
|
tpd->token[tpd->tokenPos++] = c; |
|
|
|
|
tdi->token[tdi->tokenPos++] = c; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (tpd->chunk_resume) { |
|
|
|
|
if (tdi->chunk_resume) { |
|
|
|
|
return HTTPD_CGI_MORE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//Send remaining bit.
|
|
|
|
|
if (sp != 0) { httpdSendStrN(connData, e, sp); } |
|
|
|
|
if (sp != 0) { |
|
|
|
|
httpdSendStrN(conn, e, sp); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (len != FILE_CHUNK_LEN) { |
|
|
|
|
//We're done.
|
|
|
|
|
((TplCallback) (connData->cgiArg))(connData, NULL, &tpd->tplArg); |
|
|
|
|
|
|
|
|
|
TplCallback callback = (TplCallback) conn->cgiArg; |
|
|
|
|
if (callback) { |
|
|
|
|
tdi->td.conn = NULL; |
|
|
|
|
tdi->td.token = NULL; |
|
|
|
|
callback(&tdi->td); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
espfs_info("Template sent."); |
|
|
|
|
espFsClose(tpd->file); |
|
|
|
|
httpdPlatFree(tpd); |
|
|
|
|
espFsClose(tdi->file); |
|
|
|
|
httpdPlatFree(tdi); |
|
|
|
|
return HTTPD_CGI_DONE; |
|
|
|
|
} else { |
|
|
|
|
//Ok, till next time.
|