|
@@ -9,6 +9,9 @@
|
|
|
#include <unzipLIB.h>
|
|
|
|
|
|
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" */
|
|
|
+#define MAX_LANG_LEN 16
|
|
|
|
|
|
/* Looping version of httpd_send(); this is a hidden function in the server */
|
|
|
static esp_err_t httpd_send_all(httpd_req_t *req, const void *buf, size_t len)
|
|
@@ -163,11 +166,21 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
|
const char *closer = flags & HSP_CLOSE ? "Connection: close\r\n" : "";
|
|
|
bool redirect = rcode >= 300 && rcode <= 399;
|
|
|
|
|
|
- static const char refresher[] = "Refresh: %u;url=/";
|
|
|
- char refresh_str[sizeof(refresher)-2 + sizeof(unsigned int)*3];
|
|
|
- refresh_str[0] = '\0';
|
|
|
- if (refresh)
|
|
|
- snprintf(refresh_str, sizeof refresh_str, refresher, refresh);
|
|
|
+ char *refresher = NULL;
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!refresher)
|
|
|
+ refresher = (char *)"";
|
|
|
|
|
|
if (redirect) {
|
|
|
size_t blenadj = sizeof("3xx Redirect \r"); /* \0 -> \n so don't include it */
|
|
@@ -183,7 +196,7 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
|
"\r\n"
|
|
|
"%3u Redirect ",
|
|
|
rcode, text_plain, blen + blenadj,
|
|
|
- now, (int)blen, body, closer, refresh_str, rcode);
|
|
|
+ now, (int)blen, body, closer, refresher, rcode);
|
|
|
} else {
|
|
|
size_t blenadj = (flags & HSP_CRLF) ? 2 : 0;
|
|
|
|
|
@@ -196,9 +209,12 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
|
"%s%s"
|
|
|
"\r\n",
|
|
|
rcode, text_plain, blen + blenadj, now,
|
|
|
- closer, refresh_str);
|
|
|
+ closer, refresher);
|
|
|
}
|
|
|
|
|
|
+ if (!*refresher)
|
|
|
+ free(refresher);
|
|
|
+
|
|
|
if (!header)
|
|
|
return ESP_ERR_NO_MEM;
|
|
|
|
|
@@ -217,7 +233,8 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
|
return err ? err : (flags & HSP_CLOSE_SOCKET) ? ESP_FAIL : ESP_OK;
|
|
|
}
|
|
|
|
|
|
-#define HTTP_ERR(r,e,s) httpd_send_plain((r), (e), s, sizeof(s)-1, HSP_CRLF, 0)
|
|
|
+#define SL(s) (s), (sizeof(s)-1)
|
|
|
+#define HTTP_ERR(r,e,s) httpd_send_plain((r), (e), SL(s), HSP_CRLF, 0)
|
|
|
|
|
|
static esp_err_t httpd_err_enoent(httpd_req_t *req)
|
|
|
{
|
|
@@ -297,67 +314,86 @@ static esp_err_t httpd_set_config(httpd_req_t *req, const char *query)
|
|
|
return httpd_update_done(req, "Configuration", rv1 ? rv1 : rv2);
|
|
|
}
|
|
|
|
|
|
+static esp_err_t httpd_get_config(httpd_req_t *req)
|
|
|
+{
|
|
|
+ FILE *f = httpd_fopen_write(req);
|
|
|
+ if (!f)
|
|
|
+ return HTTP_ERR(req, 500, "Unable to get request handle");
|
|
|
+
|
|
|
+ httpd_resp_set_type(req, text_plain);
|
|
|
+ httpd_resp_set_hdr(req, "Cache-Control", "no-cache");
|
|
|
+
|
|
|
+ int rv = write_config(f);
|
|
|
+ fclose(f);
|
|
|
+ return rv ? ESP_FAIL : ESP_OK;
|
|
|
+}
|
|
|
+
|
|
|
static esp_err_t httpd_set_lang(httpd_req_t *req, const char *query)
|
|
|
{
|
|
|
- if (query && *query) {
|
|
|
- setenv_config("LANG", query);
|
|
|
+ if (query) {
|
|
|
+ int qlen = strlen(query);
|
|
|
+ setenv_config("LANG", qlen && qlen <= MAX_LANG_LEN ? query : NULL);
|
|
|
read_config(NULL, true); /* Save configuration */
|
|
|
}
|
|
|
|
|
|
- return httpd_send_plain(req, 302, "/", 1, 0, 0);
|
|
|
+ return httpd_send_plain(req, 200, SL("Language set"), HSP_CRLF, 1);
|
|
|
+}
|
|
|
+
|
|
|
+static esp_err_t httpd_get_lang(httpd_req_t *req)
|
|
|
+{
|
|
|
+ const char *lang = getenv_def("LANG", "");
|
|
|
+
|
|
|
+ return httpd_send_plain(req, 200, lang, strlen(lang), HSP_CRLF, 0);
|
|
|
+}
|
|
|
+
|
|
|
+static esp_err_t httpd_lang_redirect(httpd_req_t *req)
|
|
|
+{
|
|
|
+ char lang_buf[sizeof("/lang/") + MAX_LANG_LEN];
|
|
|
+ int len = snprintf(lang_buf, sizeof lang_buf, "/lang/%s",
|
|
|
+ getenv_def("LANG", fallback_language));
|
|
|
+
|
|
|
+ return httpd_send_plain(req, 302, lang_buf, len, 0, 0);
|
|
|
}
|
|
|
|
|
|
#define STRING_MATCHES(str, len, what) \
|
|
|
(((len) == sizeof(what)-1) && !memcmp((str), (what), sizeof(what)-1))
|
|
|
|
|
|
-static esp_err_t httpd_sys_post_handler(httpd_req_t *req)
|
|
|
+static esp_err_t httpd_sys_handler(httpd_req_t *req)
|
|
|
{
|
|
|
httpd_print_request(req);
|
|
|
|
|
|
- if (!httpd_req_get_hdr_value_len(req, "Content-Length"))
|
|
|
+ if (req->method == HTTP_POST &&
|
|
|
+ !httpd_req_get_hdr_value_len(req, "Content-Length")) {
|
|
|
return HTTP_ERR(req, 411, "Length required");
|
|
|
+ }
|
|
|
|
|
|
- if (strncmp(req->uri, "/sys/", 5))
|
|
|
+ const char *query = strchrnul(req->uri, '?');
|
|
|
+
|
|
|
+ if (query < req->uri+5 || memcmp(req->uri, "/sys/", 5))
|
|
|
return httpd_err_enoent(req); /* This should never happen */
|
|
|
|
|
|
const char *file = req->uri + 5;
|
|
|
- size_t filelen = strcspn(file, "?");
|
|
|
- const char *query = NULL;
|
|
|
- if (file[filelen] == '?')
|
|
|
- query = &file[filelen] + 1;
|
|
|
+ size_t filelen = query - file;
|
|
|
|
|
|
- if (STRING_MATCHES(file, filelen, "fwupdate"))
|
|
|
- return httpd_firmware_update(req);
|
|
|
-
|
|
|
- if (STRING_MATCHES(file, filelen, "setconfig"))
|
|
|
- return httpd_set_config(req, query);
|
|
|
+ query = *query == '?' ? query+1 : NULL;
|
|
|
|
|
|
if (STRING_MATCHES(file, filelen, "lang"))
|
|
|
- return httpd_set_lang(req, query);
|
|
|
-
|
|
|
- return httpd_err_enoent(req);
|
|
|
-}
|
|
|
+ return httpd_lang_redirect(req);
|
|
|
|
|
|
-static esp_err_t httpd_get_config(httpd_req_t *req)
|
|
|
-{
|
|
|
- FILE *f = httpd_fopen_write(req);
|
|
|
- if (!f)
|
|
|
- return HTTP_ERR(req, 500, "Unable to get request handle");
|
|
|
+ if (STRING_MATCHES(file, filelen, "getconfig"))
|
|
|
+ return httpd_get_config(req);
|
|
|
|
|
|
- httpd_resp_set_type(req, text_plain);
|
|
|
- httpd_resp_set_hdr(req, "Cache-Control", "no-store");
|
|
|
+ if (req->method == HTTP_POST && STRING_MATCHES(file, filelen, "fwupdate"))
|
|
|
+ return httpd_firmware_update(req);
|
|
|
|
|
|
- int rv = write_config(f);
|
|
|
- fclose(f);
|
|
|
- return rv ? ESP_FAIL : ESP_OK;
|
|
|
-}
|
|
|
+ if (STRING_MATCHES(file, filelen, "setconfig"))
|
|
|
+ return httpd_set_config(req, query);
|
|
|
|
|
|
-static esp_err_t httpd_sys_get_handler(httpd_req_t *req)
|
|
|
-{
|
|
|
- httpd_print_request(req);
|
|
|
+ if (STRING_MATCHES(file, filelen, "getlang"))
|
|
|
+ return httpd_get_lang(req);
|
|
|
|
|
|
- if (!strcmp(req->uri, "/sys/getconfig"))
|
|
|
- return httpd_get_config(req);
|
|
|
+ if (STRING_MATCHES(file, filelen, "setlang"))
|
|
|
+ return httpd_set_lang(req, query);
|
|
|
|
|
|
return httpd_err_enoent(req);
|
|
|
}
|
|
@@ -398,8 +434,6 @@ static const struct mime_type mime_types[] = {
|
|
|
|
|
|
static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
{
|
|
|
- static const char index_filename[] = "index.html";
|
|
|
- static const char fallback_lang[] = "en";
|
|
|
size_t buffer_size = UNZ_BUFSIZE;
|
|
|
const char *uri, *enduri;
|
|
|
bool add_index;
|
|
@@ -438,8 +472,8 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
|
|
|
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_lang
|
|
|
- ? sizeof fallback_lang : lang_size) + 1;
|
|
|
+ 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;
|
|
|
|
|
@@ -489,8 +523,8 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
}
|
|
|
break;
|
|
|
case 2:
|
|
|
- filename = filebase - sizeof fallback_lang;
|
|
|
- memcpy(filename, fallback_lang, sizeof fallback_lang - 1);
|
|
|
+ filename = filebase - sizeof fallback_language;
|
|
|
+ memcpy(filename, fallback_language, sizeof fallback_language - 1);
|
|
|
break;
|
|
|
default:
|
|
|
filename = filebase;
|
|
@@ -593,7 +627,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
}
|
|
|
|
|
|
if (unzOpenCurrentFile(unz) != UNZ_OK) {
|
|
|
- err = httpd_resp_send_err(req, 500, "Cannot open file in archive");
|
|
|
+ err = HTTP_ERR(req, 500, "Cannot open file in archive");
|
|
|
goto out;
|
|
|
}
|
|
|
file_open = true;
|
|
@@ -670,7 +704,7 @@ static const httpd_uri_t uri_handlers[] = {
|
|
|
{
|
|
|
.uri = "sys",
|
|
|
.method = HTTP_GET,
|
|
|
- .handler = httpd_sys_get_handler,
|
|
|
+ .handler = httpd_sys_handler,
|
|
|
.user_ctx = NULL
|
|
|
},
|
|
|
{
|
|
@@ -688,7 +722,7 @@ static const httpd_uri_t uri_handlers[] = {
|
|
|
{
|
|
|
.uri = "sys",
|
|
|
.method = HTTP_POST,
|
|
|
- .handler = httpd_sys_post_handler,
|
|
|
+ .handler = httpd_sys_handler,
|
|
|
.user_ctx = NULL
|
|
|
},
|
|
|
};
|