|  | @@ -1,9 +1,11 @@
 | 
	
		
			
				|  |  |  #define MODULE "httpd"
 | 
	
		
			
				|  |  | +#define DEBUG 1
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include "common.h"
 | 
	
		
			
				|  |  |  #include "fw.h"
 | 
	
		
			
				|  |  |  #include "httpd.h"
 | 
	
		
			
				|  |  |  #include "config.h"
 | 
	
		
			
				|  |  | +#include "boardinfo_esp.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include <incbin.h>
 | 
	
		
			
				|  |  |  #include <unzipLIB.h>
 | 
	
	
		
			
				|  | @@ -209,7 +211,7 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      const char * const refresher = refresher_buf ? refresher_buf : "";
 | 
	
		
			
				|  |  |      const char * const uncacher = (flags & HSP_UNCACHE)
 | 
	
		
			
				|  |  | -      ? "Clear-Site-Data: \"cache\"\r\n" : "";
 | 
	
		
			
				|  |  | +	? "Clear-Site-Data: \"cache\"\r\n" : "";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (redirect) {
 | 
	
		
			
				|  |  |  	/* \0 -> \n so don't include it */
 | 
	
	
		
			
				|  | @@ -295,6 +297,17 @@ static esp_err_t httpd_err_enomem(httpd_req_t *req)
 | 
	
		
			
				|  |  |      return HTTP_ERR(req, 503, "Out of memory");
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static esp_err_t httpd_err_not_post(httpd_req_t *req)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    return HTTP_ERR(req, 405, "Only POST allowed");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define HTTPD_ASSERT_POST(req)			\
 | 
	
		
			
				|  |  | +    do {					\
 | 
	
		
			
				|  |  | +	if ((req)->method != HTTP_POST)		\
 | 
	
		
			
				|  |  | +	    return httpd_err_not_post(req);	\
 | 
	
		
			
				|  |  | +    } while (0)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static esp_err_t httpd_update_done(httpd_req_t *req, const char *what, int err)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      char *response = NULL;
 | 
	
	
		
			
				|  | @@ -328,6 +341,8 @@ static esp_err_t httpd_firmware_update(httpd_req_t *req)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      int rv;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    HTTPD_ASSERT_POST(req);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /* XXX: use httpd_fopen_read() here */
 | 
	
		
			
				|  |  |      rv = firmware_update_start((read_func_t)httpd_req_recv, (token_t)req, false);
 | 
	
		
			
				|  |  |      if (!rv)
 | 
	
	
		
			
				|  | @@ -360,6 +375,103 @@ static esp_err_t httpd_set_config(httpd_req_t *req, const char *query)
 | 
	
		
			
				|  |  |      return httpd_update_done(req, "Configuration", rv1 ? rv1 : rv2);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static inline bool is_eol(int c)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    return c == EOF || c == '\0' || c == '\r' || c == '\n';
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static esp_err_t httpd_set_board_rev(httpd_req_t *req)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    FILE *f = NULL;
 | 
	
		
			
				|  |  | +    static const char rev_prefix[] = "max80.hw.ver";
 | 
	
		
			
				|  |  | +    static const char rev_valid[] = "MAX80 v";
 | 
	
		
			
				|  |  | +    char *rev_str;
 | 
	
		
			
				|  |  | +    int err = Z_DATA_ERROR;
 | 
	
		
			
				|  |  | +    enum sbr_parse_state {
 | 
	
		
			
				|  |  | +	ps_start,
 | 
	
		
			
				|  |  | +	ps_prefix,
 | 
	
		
			
				|  |  | +	ps_string,
 | 
	
		
			
				|  |  | +	ps_skipline
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    enum sbr_parse_state state;
 | 
	
		
			
				|  |  | +    const char *match_ptr = NULL;
 | 
	
		
			
				|  |  | +    char *p = NULL;
 | 
	
		
			
				|  |  | +    int c;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    HTTPD_ASSERT_POST(req);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rev_str = malloc(sizeof board_info.version_str);
 | 
	
		
			
				|  |  | +    if (!rev_str)
 | 
	
		
			
				|  |  | +	return httpd_err_enomem(req);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    f = httpd_fopen_read(req);
 | 
	
		
			
				|  |  | +    if (!f) {
 | 
	
		
			
				|  |  | +	free(rev_str);
 | 
	
		
			
				|  |  | +	return HTTP_ERR(req, 500, "Unable to get request handle");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    state = ps_start;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    do {
 | 
	
		
			
				|  |  | +	bool eol;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	c = getc(f);
 | 
	
		
			
				|  |  | +	eol = is_eol(c);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	switch (state) {
 | 
	
		
			
				|  |  | +	case ps_start:
 | 
	
		
			
				|  |  | +	    match_ptr = rev_prefix;
 | 
	
		
			
				|  |  | +	    state = ps_prefix;
 | 
	
		
			
				|  |  | +	    /* fall through */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	case ps_prefix:
 | 
	
		
			
				|  |  | +	    if (eol) {
 | 
	
		
			
				|  |  | +		state = ps_start;
 | 
	
		
			
				|  |  | +	    } else if (*match_ptr && c == *match_ptr) {
 | 
	
		
			
				|  |  | +		match_ptr++;
 | 
	
		
			
				|  |  | +	    } else if (!*match_ptr && c == '=') {
 | 
	
		
			
				|  |  | +		p = rev_str;
 | 
	
		
			
				|  |  | +		state = ps_string;
 | 
	
		
			
				|  |  | +	    } else {
 | 
	
		
			
				|  |  | +		state = ps_skipline;
 | 
	
		
			
				|  |  | +	    }
 | 
	
		
			
				|  |  | +	    break;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	case ps_string:
 | 
	
		
			
				|  |  | +	    if (eol) {
 | 
	
		
			
				|  |  | +		*p = '\0';
 | 
	
		
			
				|  |  | +		if (!memcmp(rev_str, rev_valid, sizeof rev_valid - 1)) {
 | 
	
		
			
				|  |  | +		    /* Otherwise input truncated or invalid */
 | 
	
		
			
				|  |  | +		    printf("[HTTP] setting board revision: %s\n", rev_str);
 | 
	
		
			
				|  |  | +		    if (!board_info_set(rev_str)) {
 | 
	
		
			
				|  |  | +			setenv_cond("status.max80.hw.ver",
 | 
	
		
			
				|  |  | +				    board_info.version_str);
 | 
	
		
			
				|  |  | +			err = Z_OK;
 | 
	
		
			
				|  |  | +		    } else {
 | 
	
		
			
				|  |  | +			err = FWUPDATE_ERR_CONFIG_SAVE;
 | 
	
		
			
				|  |  | +		    }
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		state = ps_start;
 | 
	
		
			
				|  |  | +	    } else if (p - rev_str >= sizeof board_info.version_str - 1) {
 | 
	
		
			
				|  |  | +		state = ps_skipline;
 | 
	
		
			
				|  |  | +	    } else {
 | 
	
		
			
				|  |  | +		*p++ = c;
 | 
	
		
			
				|  |  | +	    }
 | 
	
		
			
				|  |  | +	    break;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	case ps_skipline:
 | 
	
		
			
				|  |  | +	    if (eol)
 | 
	
		
			
				|  |  | +		state = ps_start;
 | 
	
		
			
				|  |  | +	    break;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +    } while (c != EOF);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    fclose(f);
 | 
	
		
			
				|  |  | +    free(rev_str);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return httpd_update_done(req, "board revision", err);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #define MIN_STATUS_REF 1	/* Minimum refresh time in s */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void httpd_get_status_extra(FILE *f, httpd_req_t *req)
 | 
	
	
		
			
				|  | @@ -444,9 +556,9 @@ static esp_err_t httpd_lang_redirect(httpd_req_t *req)
 | 
	
		
			
				|  |  |      return httpd_send_plain(req, 302, lang_buf, len, 0, 0);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#define STRING_MATCHES(str, len, what) \
 | 
	
		
			
				|  |  | +#define STRING_MATCHES(str, len, what)					\
 | 
	
		
			
				|  |  |      (((len) == sizeof(what)-1) && !memcmp((str), (what), sizeof(what)-1))
 | 
	
		
			
				|  |  | -#define STRING_MATCHES_PREFIX(str, len, what) \
 | 
	
		
			
				|  |  | +#define STRING_MATCHES_PREFIX(str, len, what)				\
 | 
	
		
			
				|  |  |      (((len) >= sizeof(what)-1) && !memcmp((str), (what), sizeof(what)-1))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static esp_err_t httpd_sys_handler(httpd_req_t *req)
 | 
	
	
		
			
				|  | @@ -477,7 +589,7 @@ static esp_err_t httpd_sys_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |      if (STRING_MATCHES(file, filelen, "getconfig"))
 | 
	
		
			
				|  |  |  	return httpd_get_config_status(req, false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (req->method == HTTP_POST && STRING_MATCHES(file, filelen, "fwupdate"))
 | 
	
		
			
				|  |  | +    if (STRING_MATCHES(file, filelen, "fwupdate"))
 | 
	
		
			
				|  |  |  	return httpd_firmware_update(req);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (STRING_MATCHES(file, filelen, "setconfig"))
 | 
	
	
		
			
				|  | @@ -489,6 +601,9 @@ static esp_err_t httpd_sys_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |      if (STRING_MATCHES(file, filelen, "setlang"))
 | 
	
		
			
				|  |  |  	return httpd_set_lang(req, query);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    if (STRING_MATCHES(file, filelen, "setboardrev"))
 | 
	
		
			
				|  |  | +	return httpd_set_board_rev(req);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      return httpd_err_enoent(req);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -868,7 +983,7 @@ void my_httpd_start(void)
 | 
	
		
			
				|  |  |      config.uri_match_fn = httpd_uri_match_prefix;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (httpd_start(&server, &config) != ESP_OK)
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | +	return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      esp_register_shutdown_handler(my_httpd_stop);
 | 
	
		
			
				|  |  |  
 |