|
@@ -134,23 +134,56 @@ static const char *http_dos_date(uint32_t dos_date)
|
|
|
return http_date(set_weekday(&tm));
|
|
|
}
|
|
|
|
|
|
+static const char text_plain[] = "text/plain; charset=\"UTF-8\"";
|
|
|
+
|
|
|
+enum hsp_flags {
|
|
|
+ HSP_CLOSE = 1,
|
|
|
+ HSP_CRLF = 2
|
|
|
+};
|
|
|
+
|
|
|
static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
|
unsigned int rcode,
|
|
|
const char *body, size_t blen,
|
|
|
- bool close_conn)
|
|
|
+ enum hsp_flags flags)
|
|
|
{
|
|
|
char *header = NULL;
|
|
|
esp_err_t err;
|
|
|
+ int hlen;
|
|
|
+ const char *now = http_now();
|
|
|
+
|
|
|
+ if (rcode > 499)
|
|
|
+ flags |= HSP_CLOSE;
|
|
|
+
|
|
|
+ const char *closer = flags & HSP_CLOSE ? "Connection: close\r\n" : "";
|
|
|
+ bool redirect = rcode >= 300 && rcode <= 399;
|
|
|
+
|
|
|
+ if (redirect) {
|
|
|
+ size_t blenadj = sizeof("3xx Redirect \r"); /* \0 -> \n so don't include it */
|
|
|
+ flags |= HSP_CRLF;
|
|
|
+
|
|
|
+ hlen = asprintf(&header,
|
|
|
+ "HTTP/1.1 %u\r\n"
|
|
|
+ "Content-Type: %s\r\n"
|
|
|
+ "Content-Length: %zu\r\n"
|
|
|
+ "Date %s\r\n"
|
|
|
+ "Location: %.*s\r\n"
|
|
|
+ "%s"
|
|
|
+ "\r\n"
|
|
|
+ "%3u Redirect ",
|
|
|
+ rcode, text_plain, blen + blenadj,
|
|
|
+ now, (int)blen, body, closer, rcode);
|
|
|
+ } else {
|
|
|
+ size_t blenadj = (flags & HSP_CRLF) ? 2 : 0;
|
|
|
|
|
|
- int hlen = asprintf(&header,
|
|
|
+ hlen = asprintf(&header,
|
|
|
"HTTP/1.1 %u\r\n"
|
|
|
- "Content-Type: text/plain; charset=\"UTF-8\"\r\n"
|
|
|
+ "Content-Type: %s\r\n"
|
|
|
"Content-Length: %zu\r\n"
|
|
|
"Date: %s\r\n"
|
|
|
"%s"
|
|
|
"\r\n",
|
|
|
- rcode, blen, http_now(),
|
|
|
- close_conn ? "Connection: close\r\n" : "");
|
|
|
+ rcode, text_plain, blen + blenadj, now, closer);
|
|
|
+ }
|
|
|
|
|
|
if (!header)
|
|
|
return ESP_ERR_NO_MEM;
|
|
@@ -159,6 +192,9 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
|
if (!err && blen) {
|
|
|
err = httpd_send_all(req, body, blen);
|
|
|
}
|
|
|
+ if (!err && (flags & HSP_CRLF)) {
|
|
|
+ err = httpd_send_all(req, "\r\n", 2);
|
|
|
+ }
|
|
|
|
|
|
if (header)
|
|
|
free(header);
|
|
@@ -166,8 +202,7 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-#define HTTP_ERR(r,e,s) httpd_send_plain((r), (e), s "\r\n", \
|
|
|
- sizeof(s) + 1, (e) > 399)
|
|
|
+#define HTTP_ERR(r,e,s) httpd_send_plain((r), (e), s, sizeof(s)-1, HSP_CRLF)
|
|
|
|
|
|
static esp_err_t httpd_err_404(httpd_req_t *req)
|
|
|
{
|
|
@@ -179,22 +214,33 @@ static esp_err_t httpd_send_ok(httpd_req_t *req)
|
|
|
return HTTP_ERR(req, 200, "OK");
|
|
|
}
|
|
|
|
|
|
-static esp_err_t httpd_update_done(httpd_req_t *req, const char *what)
|
|
|
+static esp_err_t httpd_update_done(httpd_req_t *req, const char *what, int err)
|
|
|
{
|
|
|
char *response = NULL;
|
|
|
- int len = asprintf(&response,
|
|
|
- "%s complete\r\n"
|
|
|
+ int len;
|
|
|
+ unsigned int reboot_time = reboot_delayed();
|
|
|
+
|
|
|
+ if (err) {
|
|
|
+ len = asprintf(&response,
|
|
|
+ "%s update failed: %s\r\n"
|
|
|
"Rebooting in %u seconds\r\n",
|
|
|
- what, reboot_delayed());
|
|
|
+ what, firmware_errstr(err), reboot_time);
|
|
|
+ } else {
|
|
|
+ len = asprintf(&response,
|
|
|
+ "%s update complete\r\n"
|
|
|
+ "Rebooting in %u seconds\r\n",
|
|
|
+ what, reboot_time);
|
|
|
+ }
|
|
|
|
|
|
if (!response)
|
|
|
len = 0;
|
|
|
|
|
|
- esp_err_t err = httpd_send_plain(req, 200, response, len, true);
|
|
|
+ esp_err_t rv = httpd_send_plain(req, err ? 400 : 200, response, len,
|
|
|
+ HSP_CLOSE|HSP_CRLF);
|
|
|
if (response)
|
|
|
free(response);
|
|
|
|
|
|
- return err;
|
|
|
+ return rv;
|
|
|
}
|
|
|
|
|
|
static esp_err_t httpd_firmware_update(httpd_req_t *req)
|
|
@@ -203,13 +249,7 @@ static esp_err_t httpd_firmware_update(httpd_req_t *req)
|
|
|
|
|
|
/* XXX: use httpd_fopen_read() here */
|
|
|
rv = firmware_update((read_func_t)httpd_req_recv, (token_t)req);
|
|
|
-
|
|
|
- if (rv == FWUPDATE_ERR_IN_PROGRESS)
|
|
|
- return HTTP_ERR(req, 409, "Firmware update already in progress");
|
|
|
- else if (rv)
|
|
|
- return HTTP_ERR(req, 500, "Firmware update failed");
|
|
|
-
|
|
|
- return httpd_update_done(req, "Firmware update");
|
|
|
+ return httpd_update_done(req, "Firmware", rv);
|
|
|
}
|
|
|
|
|
|
static esp_err_t httpd_set_config(httpd_req_t *req)
|
|
@@ -217,13 +257,18 @@ static esp_err_t httpd_set_config(httpd_req_t *req)
|
|
|
FILE *f = httpd_fopen_read(req);
|
|
|
if (!f)
|
|
|
return HTTP_ERR(req, 500, "Unable to get request handle");
|
|
|
-
|
|
|
+
|
|
|
int rv = read_config(f);
|
|
|
fclose(f);
|
|
|
- if (rv || save_config())
|
|
|
- return HTTP_ERR(req, 503, "Unable to update configuration");
|
|
|
+ if (rv) {
|
|
|
+ rv = FWUPDATE_ERR_CONFIG_READ;
|
|
|
+ } else {
|
|
|
+ rv = save_config();
|
|
|
+ if (rv)
|
|
|
+ rv = FWUPDATE_ERR_CONFIG_SAVE;
|
|
|
+ }
|
|
|
|
|
|
- return httpd_update_done(req, "Configuration update");
|
|
|
+ return httpd_update_done(req, "Configuration", rv);
|
|
|
}
|
|
|
|
|
|
static esp_err_t httpd_sys_post_handler(httpd_req_t *req)
|
|
@@ -249,7 +294,7 @@ static esp_err_t httpd_get_config(httpd_req_t *req)
|
|
|
if (!f)
|
|
|
return HTTP_ERR(req, 500, "Unable to get request handle");
|
|
|
|
|
|
- httpd_resp_set_type(req, "text/plain; charset=\"UTF-8\"");
|
|
|
+ httpd_resp_set_type(req, text_plain);
|
|
|
|
|
|
int rv = write_config(f);
|
|
|
fclose(f);
|
|
@@ -301,7 +346,8 @@ 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";
|
|
|
- const size_t buffer_size = UNZ_BUFSIZE;
|
|
|
+ static const char default_lang[] = "en";
|
|
|
+ size_t buffer_size = UNZ_BUFSIZE;
|
|
|
const char *uri, *enduri;
|
|
|
bool add_index;
|
|
|
char *buffer = NULL;
|
|
@@ -315,17 +361,41 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
while (*uri == '/')
|
|
|
uri++; /* Skip leading slashes */
|
|
|
|
|
|
- enduri = strchr(uri, '\0');
|
|
|
+ const char *first_slash = NULL, *last_slash = NULL;
|
|
|
+
|
|
|
+ for (enduri = uri; *enduri; enduri++) {
|
|
|
+ if (*enduri == '/') {
|
|
|
+ last_slash = enduri;
|
|
|
+ if (!first_slash)
|
|
|
+ first_slash = enduri;
|
|
|
+ }
|
|
|
+ }
|
|
|
if (enduri == uri) {
|
|
|
add_index = true;
|
|
|
- } else if (enduri[-1] == '/') {
|
|
|
+ } else if (last_slash == enduri-1) {
|
|
|
add_index = true;
|
|
|
enduri--; /* Drop terminal slash */
|
|
|
+ if (first_slash == last_slash)
|
|
|
+ first_slash = NULL;
|
|
|
} else {
|
|
|
add_index = false; /* Try the plain filename first */
|
|
|
}
|
|
|
|
|
|
MSG("requesting: /%.*s\n", enduri - uri, uri);
|
|
|
+ if (first_slash)
|
|
|
+ MSG("first_slash = %.*s\n", enduri - first_slash, first_slash);
|
|
|
+ else
|
|
|
+ MSG("first_slash = NULL\n");
|
|
|
+
|
|
|
+ const char *lang = getenv("LANG");
|
|
|
+ const size_t lang_size = lang ? strlen(lang)+1 : 0;
|
|
|
+ const size_t lang_space = (lang_size < sizeof default_lang
|
|
|
+ ? sizeof default_lang : lang_size) + 1;
|
|
|
+ const size_t filename_buffer_size =
|
|
|
+ (enduri - uri) + lang_space + 2 + sizeof index_filename;
|
|
|
+
|
|
|
+ if (buffer_size < filename_buffer_size)
|
|
|
+ buffer_size = filename_buffer_size;
|
|
|
|
|
|
buffer = malloc(buffer_size);
|
|
|
zip = malloc(sizeof *zip);
|
|
@@ -334,13 +404,9 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- if (enduri - uri + 1 + sizeof index_filename >= buffer_size) {
|
|
|
- err = HTTP_ERR(req, 414, "URI too long");
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- char *p = mempcpy(buffer, uri, enduri - uri);
|
|
|
- *p = '\0';
|
|
|
+ char * const filebase = buffer + lang_space;
|
|
|
+ char * const endbase = mempcpy(filebase, uri, enduri - uri);
|
|
|
+ filebase[-1] = '/';
|
|
|
|
|
|
unz = unzOpen(NULL, (void *)gwwwzipData, gwwwzipSize,
|
|
|
zip, NULL, NULL, NULL, NULL);
|
|
@@ -350,35 +416,67 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- while (1) {
|
|
|
- if (add_index) {
|
|
|
- if (p > buffer)
|
|
|
- *p++ = '/';
|
|
|
- memcpy(p, index_filename, sizeof index_filename);
|
|
|
- p += sizeof index_filename - 1; /* Point to final NUL */
|
|
|
+ char *filename, *endfile;
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ endfile = endbase;
|
|
|
}
|
|
|
-
|
|
|
- MSG("trying to open: %s\n", buffer);
|
|
|
- if (unzLocateFile(unz, buffer, 1) == UNZ_OK)
|
|
|
+ *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 default_lang;
|
|
|
+ memcpy(filename, default_lang, sizeof default_lang - 1);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ filename = filebase;
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- if (add_index) {
|
|
|
- err = httpd_err_404(req);
|
|
|
- goto out;
|
|
|
+ if (!filename)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ filename[-1] = '/';
|
|
|
+ MSG("trying to open: %s\n", filename);
|
|
|
+ if (unzLocateFile(unz, filename, 1) == UNZ_OK) {
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t filelen = endfile - filename;
|
|
|
|
|
|
- add_index = true; /* Try again with the index filename */
|
|
|
+ if (!found) {
|
|
|
+ err = httpd_err_404(req);
|
|
|
+ goto out;
|
|
|
+ } else if (m) {
|
|
|
+ err = httpd_send_plain(req, 302 - (m == 1), filename-1, filelen+1, 0);
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
/* Note: p points to the end of the filename string */
|
|
|
- size_t filelen = p - buffer;
|
|
|
|
|
|
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(p - len, mime_type->ext, len))
|
|
|
+ if (len < filelen && !memcmp(endfile - len, mime_type->ext, len))
|
|
|
break;
|
|
|
|
|
|
mime_type++;
|