|
@@ -10,7 +10,7 @@
|
|
|
|
|
|
static httpd_handle_t httpd;
|
|
|
static const char fallback_language[] = "en"; /* For unknown language */
|
|
|
-static const char index_filename[] = "index.html"; /* For a directory "file" */
|
|
|
+static const char redir_filename[] = "_redir"; /* For a directory "file" */
|
|
|
#define MAX_LANG_LEN 16
|
|
|
|
|
|
/* Looping version of httpd_send(); this is a hidden function in the server */
|
|
@@ -160,32 +160,48 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
|
int hlen;
|
|
|
const char *now = http_now();
|
|
|
|
|
|
+ MSG("http_send_plain %u \"%.*s\"\n", rcode, (int)blen, body);
|
|
|
+
|
|
|
if (rcode > 499)
|
|
|
flags |= HSP_CLOSE;
|
|
|
|
|
|
const char *closer = flags & HSP_CLOSE ? "Connection: close\r\n" : "";
|
|
|
bool redirect = rcode >= 300 && rcode <= 399;
|
|
|
|
|
|
- char *refresher = NULL;
|
|
|
+ char *refresher = (char *)"";
|
|
|
if (refresh) {
|
|
|
size_t referer_length = httpd_req_get_hdr_value_len(req, "Referer");
|
|
|
- if (referer_length) {
|
|
|
- size_t refbufsize = sizeof("Refresh: ;url=\r\n")
|
|
|
- + 3*sizeof(unsigned int) + referer_length;
|
|
|
- refresher = malloc(refbufsize);
|
|
|
- size_t rlen = snprintf(refresher, refbufsize, "Refresh: %u;url=", refresh);
|
|
|
- httpd_req_get_hdr_value_str(req, "Referer", refresher+rlen,
|
|
|
- refbufsize-rlen);
|
|
|
- memcpy(refresher+rlen+referer_length, "\r\n", 3);
|
|
|
+ const size_t refhdrsize = sizeof("Refresh: ;url=") +
|
|
|
+ 3*sizeof(unsigned int);
|
|
|
+ refresher = malloc(refhdrsize + referer_length + 4);
|
|
|
+ if (!refresher) {
|
|
|
+ refresher = (char *)"";
|
|
|
+ } else {
|
|
|
+ size_t rlen = snprintf(refresher, refhdrsize,
|
|
|
+ "Refresh: %u;url=", refresh);
|
|
|
+ if (referer_length) {
|
|
|
+ httpd_req_get_hdr_value_str(req, "Referer", refresher+rlen,
|
|
|
+ referer_length+1);
|
|
|
+ rlen += referer_length;
|
|
|
+ } else {
|
|
|
+ refresher[rlen++] = '/';
|
|
|
+ }
|
|
|
+ memcpy(refresher+rlen, "\r\n", 3);
|
|
|
}
|
|
|
}
|
|
|
- if (!refresher)
|
|
|
- refresher = (char *)"";
|
|
|
|
|
|
if (redirect) {
|
|
|
size_t blenadj = sizeof("3xx Redirect \r"); /* \0 -> \n so don't include it */
|
|
|
flags |= HSP_CRLF;
|
|
|
|
|
|
+ /* Drop any CR LF already in the redirect string */
|
|
|
+ for (size_t bchk = 0; bchk < blen; bchk++) {
|
|
|
+ if (body[bchk] == '\r' || body[bchk] == '\n') {
|
|
|
+ blen = bchk;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
hlen = asprintf(&header,
|
|
|
"HTTP/1.1 %u\r\n"
|
|
|
"Content-Type: %s\r\n"
|
|
@@ -204,7 +220,7 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
|
"HTTP/1.1 %u\r\n"
|
|
|
"Content-Type: %s\r\n"
|
|
|
"Content-Length: %zu\r\n"
|
|
|
- "Cache-Control: no-store\r\n"
|
|
|
+ "Cache-Control: no-cache\r\n"
|
|
|
"Date: %s\r\n"
|
|
|
"%s%s"
|
|
|
"\r\n",
|
|
@@ -408,6 +424,7 @@ struct mime_type {
|
|
|
};
|
|
|
|
|
|
#define MT_CHARSET 1 /* Add charset to Content-Type */
|
|
|
+#define MT_REDIR 2 /* It is a redirect */
|
|
|
|
|
|
static const struct mime_type mime_types[] = {
|
|
|
{ ".html", 5, MT_CHARSET, "text/html" },
|
|
@@ -429,6 +446,7 @@ static const struct mime_type mime_types[] = {
|
|
|
{ ".xml", 4, MT_CHARSET, "text/xml" },
|
|
|
{ ".bin", 4, 0, "application/octet-stream" },
|
|
|
{ ".fw", 3, 0, "application/octet-stream" },
|
|
|
+ { "_redir", 6, MT_REDIR, NULL },
|
|
|
{ NULL, 0, MT_CHARSET, "text/plain" } /* default */
|
|
|
};
|
|
|
|
|
@@ -436,7 +454,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
{
|
|
|
size_t buffer_size = UNZ_BUFSIZE;
|
|
|
const char *uri, *enduri;
|
|
|
- bool add_index;
|
|
|
+ bool is_dir;
|
|
|
char *buffer = NULL;
|
|
|
ZIPFILE *zip = NULL;
|
|
|
unzFile unz = NULL;
|
|
@@ -460,22 +478,18 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
}
|
|
|
}
|
|
|
if (enduri == uri) {
|
|
|
- add_index = true;
|
|
|
+ is_dir = true;
|
|
|
} else if (last_slash == enduri-1) {
|
|
|
- add_index = true;
|
|
|
+ is_dir = true;
|
|
|
enduri--; /* Drop terminal slash */
|
|
|
if (first_slash == last_slash)
|
|
|
first_slash = NULL;
|
|
|
} else {
|
|
|
- add_index = false; /* Try the plain filename first */
|
|
|
+ is_dir = false; /* Try the plain filename first */
|
|
|
}
|
|
|
|
|
|
- const char *lang = getenv_def("LANG", "");
|
|
|
- const size_t lang_size = lang ? strlen(lang)+1 : 0;
|
|
|
- const size_t lang_space = (lang_size < sizeof fallback_language
|
|
|
- ? sizeof fallback_language : lang_size) + 1;
|
|
|
const size_t filename_buffer_size =
|
|
|
- (enduri - uri) + lang_space + 2 + sizeof index_filename;
|
|
|
+ (enduri - uri) + 2 + sizeof redir_filename;
|
|
|
|
|
|
if (buffer_size < filename_buffer_size)
|
|
|
buffer_size = filename_buffer_size;
|
|
@@ -487,7 +501,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- char * const filebase = buffer + lang_space;
|
|
|
+ char * const filebase = buffer + 1;
|
|
|
char * const endbase = mempcpy(filebase, uri, enduri - uri);
|
|
|
filebase[-1] = '/';
|
|
|
|
|
@@ -499,76 +513,97 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- char *filename, *endfile;
|
|
|
+ char * const filename = filebase; /* Separate for future needs */
|
|
|
+ char *endfile = endbase;
|
|
|
|
|
|
unsigned int m;
|
|
|
bool found = false;
|
|
|
- for (m = add_index; m < 6; m += (add_index+1)) {
|
|
|
- if (m & 1) {
|
|
|
- char *sx = endbase - (endbase[-1] == '/');
|
|
|
- *sx++ = '/';
|
|
|
- endfile = mempcpy(sx, index_filename, sizeof index_filename) - 1;
|
|
|
- } else {
|
|
|
+ for (m = is_dir ? 2 : 0; m < 3; m++) {
|
|
|
+ char *sx;
|
|
|
+ switch (m) {
|
|
|
+ default: /* filename = /url */
|
|
|
endfile = endbase;
|
|
|
- }
|
|
|
- *endfile = '\0';
|
|
|
-
|
|
|
- switch (m >> 1) {
|
|
|
- case 1:
|
|
|
- if (!lang) {
|
|
|
- filename = NULL;
|
|
|
- } else {
|
|
|
- filename = filebase - lang_size;
|
|
|
- memcpy(filename, lang, lang_size-1);
|
|
|
- }
|
|
|
break;
|
|
|
- case 2:
|
|
|
- filename = filebase - sizeof fallback_language;
|
|
|
- memcpy(filename, fallback_language, sizeof fallback_language - 1);
|
|
|
+ case 1: /* filename = /url_redir */
|
|
|
+ sx = endbase;
|
|
|
+ endfile = mempcpy(sx, redir_filename, sizeof redir_filename) - 1;
|
|
|
break;
|
|
|
- default:
|
|
|
- filename = filebase;
|
|
|
+ case 2: /* filename = /url/_redir */
|
|
|
+ sx = endbase - (endbase[-1] == '/');
|
|
|
+ *sx++ = '/';
|
|
|
+ endfile = mempcpy(sx, redir_filename, sizeof redir_filename) - 1;
|
|
|
break;
|
|
|
}
|
|
|
+ *endfile = '\0';
|
|
|
|
|
|
if (!filename)
|
|
|
continue;
|
|
|
|
|
|
filename[-1] = '/';
|
|
|
- MSG("trying to open: %s\n", filename);
|
|
|
+ MSG("trying to open: %s... ", filename);
|
|
|
if (unzLocateFile(unz, filename, 1) == UNZ_OK) {
|
|
|
+ CMSG("found\n");
|
|
|
found = true;
|
|
|
break;
|
|
|
+ } else {
|
|
|
+ CMSG("not found\n");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- size_t filelen = endfile - filename;
|
|
|
-
|
|
|
if (!found) {
|
|
|
err = httpd_err_enoent(req);
|
|
|
goto out;
|
|
|
- } else if (m) {
|
|
|
- err = httpd_send_plain(req, 302 - (m == 1), filename-1, filelen+1, 0, 0);
|
|
|
- goto out;
|
|
|
}
|
|
|
|
|
|
- /* Note: p points to the end of the filename string */
|
|
|
+ size_t filelen = endfile - filename;
|
|
|
|
|
|
const struct mime_type *mime_type = mime_types;
|
|
|
/* The default entry with length 0 will always match */
|
|
|
while (mime_type->ext_len) {
|
|
|
len = mime_type->ext_len;
|
|
|
|
|
|
- if (len < filelen && !memcmp(endfile - len, mime_type->ext, len))
|
|
|
+ if (len <= filelen && !memcmp(endfile - len, mime_type->ext, len))
|
|
|
break;
|
|
|
|
|
|
mime_type++;
|
|
|
}
|
|
|
|
|
|
+ MSG("found %s ext %s type %s\n",
|
|
|
+ filename,
|
|
|
+ mime_type->ext ? mime_type->ext : "(none)",
|
|
|
+ mime_type->mime ? mime_type->mime : "(none)");
|
|
|
+
|
|
|
unz_file_info fileinfo;
|
|
|
memset(&fileinfo, 0, sizeof fileinfo);
|
|
|
unzGetCurrentFileInfo(unz, &fileinfo, NULL, 0, NULL, 0, NULL, 0);
|
|
|
|
|
|
+ MSG("len %u compressed %u\n",
|
|
|
+ fileinfo.uncompressed_size,
|
|
|
+ fileinfo.compressed_size);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Is it a redirect?
|
|
|
+ */
|
|
|
+ if (mime_type->flags & MT_REDIR) {
|
|
|
+ if (fileinfo.uncompressed_size > buffer_size ||
|
|
|
+ unzOpenCurrentFile(unz) != UNZ_OK) {
|
|
|
+ err = HTTP_ERR(req, 500, "Cannot open file in archive");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ file_open = true;
|
|
|
+
|
|
|
+ len = fileinfo.uncompressed_size;
|
|
|
+ if (unzReadCurrentFile(unz, buffer, len) != len) {
|
|
|
+ err = ESP_ERR_HTTPD_RESULT_TRUNC;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ MSG("redirect: %.*s\n", (int)len, buffer);
|
|
|
+
|
|
|
+ err = httpd_send_plain(req, 302, buffer, len, 0, 0);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* Hopefully the combination of date and CRC
|
|
|
* is strong enough to quality for a "strong" ETag
|
|
@@ -627,7 +662,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
}
|
|
|
|
|
|
if (unzOpenCurrentFile(unz) != UNZ_OK) {
|
|
|
- err = HTTP_ERR(req, 500, "Cannot open file in archive");
|
|
|
+ err = HTTP_ERR(req, 400, "Cannot open file in archive");
|
|
|
goto out;
|
|
|
}
|
|
|
file_open = true;
|