From cabd42f2b4884be316bce301edd9a09ef3887eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 16:34:00 +0200 Subject: [PATCH 01/10] base of xmarco-based config, used for wifi and network --- CMakeLists.txt | 2 +- front-end | 2 +- prepro.sh | 3 + user/cgi_network.c | 172 +++--------------------------------- user/cgi_wifi.c | 199 +++--------------------------------------- user/config_xmacros.c | 99 +++++++++++++++++++++ user/config_xmacros.h | 85 ++++++++++++++++++ user/wifimgr.c | 172 +++++++++++++++++++++++++++++++----- user/wifimgr.h | 72 +++++++++++---- 9 files changed, 415 insertions(+), 391 deletions(-) create mode 100755 prepro.sh create mode 100644 user/config_xmacros.c create mode 100644 user/config_xmacros.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e65181..6345770 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ set(SOURCE_FILES user/character_sets.h user/utf8.h user/utf8.c - user/cgi_logging.h) + user/cgi_logging.h user/config_xmacros.h user/config_xmacros.c) include_directories(include) include_directories(libesphttpd/esphttpclient) diff --git a/front-end b/front-end index d2fabc4..32c889b 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit d2fabc40f1874cabd7f7ceca690a5874887c3c5d +Subproject commit 32c889b714dae859f51e6b46829fd85be59d9ed0 diff --git a/prepro.sh b/prepro.sh new file mode 100755 index 0000000..997d376 --- /dev/null +++ b/prepro.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +xtensa-lx106-elf-gcc -E -Iuser -Ilibesphttpd/include -Iesp_iot_sdk_v1.5.2/include -Iinclude $@ diff --git a/user/cgi_network.c b/user/cgi_network.c index db86874..6ed9d40 100644 --- a/user/cgi_network.c +++ b/user/cgi_network.c @@ -9,6 +9,7 @@ configuring the network settings #include "persist.h" #include "helpers.h" #include "cgi_logging.h" +#include "config_xmacros.h" #define SET_REDIR_SUC "/cfg/network" #define SET_REDIR_ERR SET_REDIR_SUC"?err=" @@ -48,138 +49,9 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData) // ---- AP DHCP server lease time ---- - if (GET_ARG("ap_dhcp_time")) { - cgi_dbg("Setting DHCP lease time to: %s min.", buff); - int min = atoi(buff); - if (min >= 1 && min <= 2880) { - if (wificonf->ap_dhcp_time != min) { - wificonf->ap_dhcp_time = (u16) min; - wifi_change_flags.ap = true; - } - } else { - cgi_warn("Lease time %s out of allowed range 1-2880.", buff); - redir_url += sprintf(redir_url, "ap_dhcp_time,"); - } - } - - // ---- AP DHCP start and end IP ---- - - if (GET_ARG("ap_dhcp_start")) { - cgi_dbg("Setting DHCP range start IP to: \"%s\"", buff); - u32 ip = ipaddr_addr(buff); - if (ip != 0) { - if (wificonf->ap_dhcp_range.start_ip.addr != ip) { - wificonf->ap_dhcp_range.start_ip.addr = ip; - wifi_change_flags.ap = true; - } - } else { - cgi_warn("Bad IP: %s", buff); - redir_url += sprintf(redir_url, "ap_dhcp_start,"); - } - } - - if (GET_ARG("ap_dhcp_end")) { - cgi_dbg("Setting DHCP range end IP to: \"%s\"", buff); - u32 ip = ipaddr_addr(buff); - if (ip != 0) { - if (wificonf->ap_dhcp_range.end_ip.addr != ip) { - wificonf->ap_dhcp_range.end_ip.addr = ip; - wifi_change_flags.ap = true; - } - } else { - cgi_warn("Bad IP: %s", buff); - redir_url += sprintf(redir_url, "ap_dhcp_end,"); - } - } - - // ---- AP local address & config ---- - - if (GET_ARG("ap_addr_ip")) { - cgi_dbg("Setting AP local IP to: \"%s\"", buff); - u32 ip = ipaddr_addr(buff); - if (ip != 0) { - if (wificonf->ap_addr.ip.addr != ip) { - wificonf->ap_addr.ip.addr = ip; - wificonf->ap_addr.gw.addr = ip; // always the same, we're the router here - wifi_change_flags.ap = true; - } - } else { - cgi_warn("Bad IP: %s", buff); - redir_url += sprintf(redir_url, "ap_addr_ip,"); - } - } - - if (GET_ARG("ap_addr_mask")) { - cgi_dbg("Setting AP local IP netmask to: \"%s\"", buff); - u32 ip = ipaddr_addr(buff); - if (ip != 0) { - if (wificonf->ap_addr.netmask.addr != ip) { - // ideally this should be checked to match the IP. - // Let's hope users know what they're doing - wificonf->ap_addr.netmask.addr = ip; - wifi_change_flags.ap = true; - } - } else { - cgi_warn("Bad IP mask: %s", buff); - redir_url += sprintf(redir_url, "ap_addr_mask,"); - } - } - - // ---- Station enable/disable DHCP ---- - - // DHCP enable / disable (disable means static IP is enabled) - if (GET_ARG("sta_dhcp_enable")) { - cgi_dbg("DHCP enable = %s", buff); - int enable = atoi(buff); - if (wificonf->sta_dhcp_enable != enable) { - wificonf->sta_dhcp_enable = (bool)enable; - wifi_change_flags.sta = true; - } - } - - // ---- Station IP config (Static IP) ---- - - if (GET_ARG("sta_addr_ip")) { - cgi_dbg("Setting Station mode static IP to: \"%s\"", buff); - u32 ip = ipaddr_addr(buff); - if (ip != 0) { - if (wificonf->sta_addr.ip.addr != ip) { - wificonf->sta_addr.ip.addr = ip; - wifi_change_flags.sta = true; - } - } else { - cgi_warn("Bad IP: %s", buff); - redir_url += sprintf(redir_url, "sta_addr_ip,"); - } - } - - if (GET_ARG("sta_addr_mask")) { - cgi_dbg("Setting Station mode static IP netmask to: \"%s\"", buff); - u32 ip = ipaddr_addr(buff); - if (ip != 0 && ip != 0xFFFFFFFFUL) { - if (wificonf->sta_addr.netmask.addr != ip) { - wificonf->sta_addr.netmask.addr = ip; - wifi_change_flags.sta = true; - } - } else { - cgi_warn("Bad IP mask: %s", buff); - redir_url += sprintf(redir_url, "sta_addr_mask,"); - } - } - - if (GET_ARG("sta_addr_gw")) { - cgi_dbg("Setting Station mode static IP default gateway to: \"%s\"", buff); - u32 ip = ipaddr_addr(buff); - if (ip != 0) { - if (wificonf->sta_addr.gw.addr != ip) { - wificonf->sta_addr.gw.addr = ip; - wifi_change_flags.sta = true; - } - } else { - cgi_warn("Bad gw IP: %s", buff); - redir_url += sprintf(redir_url, "sta_addr_gw,"); - } - } +#define X XSET_CGI_FUNC + XTABLE_WIFI +#undef X (void) redir_url; @@ -216,7 +88,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData) //Template code for the WLAN page. httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg) { - char buff[20]; + char buff[64]; u8 mac[6]; if (token == NULL) { @@ -226,34 +98,12 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke strcpy(buff, ""); // fallback - if (streq(token, "ap_dhcp_time")) { - sprintf(buff, "%d", wificonf->ap_dhcp_time); - } - else if (streq(token, "ap_dhcp_start")) { - sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr)); - } - else if (streq(token, "ap_dhcp_end")) { - sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr)); - } - else if (streq(token, "ap_addr_ip")) { - sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr)); - } - else if (streq(token, "ap_addr_mask")) { - sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr)); - } - else if (streq(token, "sta_dhcp_enable")) { - sprintf(buff, "%d", wificonf->sta_dhcp_enable); - } - else if (streq(token, "sta_addr_ip")) { - sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr)); - } - else if (streq(token, "sta_addr_mask")) { - sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr)); - } - else if (streq(token, "sta_addr_gw")) { - sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr)); - } - else if (streq(token, "sta_mac")) { +#define X XGET_CGI_FUNC + XTABLE_WIFI +#undef X + + // non-config + if (streq(token, "sta_mac")) { wifi_get_macaddr(STATION_IF, mac); sprintf(buff, MACSTR, MAC2STR(mac)); } diff --git a/user/cgi_wifi.c b/user/cgi_wifi.c index 7378977..41be75d 100644 --- a/user/cgi_wifi.c +++ b/user/cgi_wifi.c @@ -19,6 +19,7 @@ Cgi/template routines for the /wifi url. #include "wifimgr.h" #include "persist.h" #include "helpers.h" +#include "config_xmacros.h" #include "cgi_logging.h" #define SET_REDIR_SUC "/cfg/wifi" @@ -66,46 +67,6 @@ int ICACHE_FLASH_ATTR rssi2perc(int rssi) return r; } -/** - * Convert Auth type to string - */ -const ICACHE_FLASH_ATTR char *auth2str(AUTH_MODE auth) -{ - switch (auth) { - case AUTH_OPEN: - return "Open"; - case AUTH_WEP: - return "WEP"; - case AUTH_WPA_PSK: - return "WPA"; - case AUTH_WPA2_PSK: - return "WPA2"; - case AUTH_WPA_WPA2_PSK: - return "WPA/WPA2"; - default: - return "Unknown"; - } -} - -/** - * Convert WiFi opmode to string - */ -const ICACHE_FLASH_ATTR char *opmode2str(WIFI_MODE opmode) -{ - switch (opmode) { - case NULL_MODE: - return "Disabled"; - case STATION_MODE: - return "Client"; - case SOFTAP_MODE: - return "AP only"; - case STATIONAP_MODE: - return "Client+AP"; - default: - return "Unknown"; - } -} - /** * Callback the code calls when a wlan ap scan is done. Basically stores the result in * the static cgiWifiAps struct. @@ -363,18 +324,13 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) bool sta_turned_on = false; bool sta_ssid_pw_changed = false; +#define X XSET_CGI_FUNC + XTABLE_WIFI +#undef X + // ---- WiFi opmode ---- - if (GET_ARG("opmode")) { - cgi_dbg("Setting WiFi opmode to: %s", buff); - int mode = atoi(buff); - if (mode > NULL_MODE && mode < MAX_MODE) { - wificonf->opmode = (WIFI_MODE) mode; - } else { - cgi_warn("Bad opmode value \"%s\"", buff); - redir_url += sprintf(redir_url, "opmode,"); - } - } + // those are helpers, not a real prop if (GET_ARG("ap_enable")) { cgi_dbg("Enable AP: %s", buff); @@ -399,114 +355,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) } } - // ---- AP transmit power ---- - - if (GET_ARG("tpw")) { - cgi_dbg("Setting AP power to: %s", buff); - int tpw = atoi(buff); - if (tpw >= 0 && tpw <= 82) { // 0 actually isn't 0 but quite low. 82 is very strong - if (wificonf->tpw != tpw) { - wificonf->tpw = (u8) tpw; - wifi_change_flags.ap = true; - } - } else { - cgi_warn("tpw %s out of allowed range 0-82.", buff); - redir_url += sprintf(redir_url, "tpw,"); - } - } - - // ---- AP channel (applies in AP-only mode) ---- - - if (GET_ARG("ap_channel")) { - cgi_info("ap_channel = %s", buff); - int channel = atoi(buff); - if (channel > 0 && channel < 15) { - if (wificonf->ap_channel != channel) { - wificonf->ap_channel = (u8) channel; - wifi_change_flags.ap = true; - } - } else { - cgi_warn("Bad channel value \"%s\", allowed 1-14", buff); - redir_url += sprintf(redir_url, "ap_channel,"); - } - } - - // ---- SSID name in AP mode ---- - - if (GET_ARG("ap_ssid")) { - // Replace all invalid ASCII with underscores - int i; - for (i = 0; i < 32; i++) { - char c = buff[i]; - if (c == 0) break; - if (c < 32 || c >= 127) buff[i] = '_'; - } - buff[i] = 0; - - if (strlen(buff) > 0) { - if (!streq(wificonf->ap_ssid, buff)) { - cgi_info("Setting SSID to \"%s\"", buff); - strncpy_safe(wificonf->ap_ssid, buff, SSID_LEN); - wifi_change_flags.ap = true; - } - } else { - cgi_warn("Bad SSID len."); - redir_url += sprintf(redir_url, "ap_ssid,"); - } - } - - // ---- AP password ---- - - if (GET_ARG("ap_password")) { - // Users are free to use any stupid shit in ther password, - // but it may lock them out. - if (strlen(buff) == 0 || (strlen(buff) >= 8 && strlen(buff) < PASSWORD_LEN-1)) { - if (!streq(wificonf->ap_password, buff)) { - cgi_info("Setting AP password to \"%s\"", buff); - strncpy_safe(wificonf->ap_password, buff, PASSWORD_LEN); - wifi_change_flags.ap = true; - } - } else { - cgi_warn("Bad password len."); - redir_url += sprintf(redir_url, "ap_password,"); - } - } - - // ---- Hide AP network (do not announce) ---- - - if (GET_ARG("ap_hidden")) { - cgi_dbg("AP hidden = %s", buff); - int hidden = atoi(buff); - if (hidden != wificonf->ap_hidden) { - wificonf->ap_hidden = (hidden != 0); - wifi_change_flags.ap = true; - } - } - - // ---- Station SSID (to connect to) ---- - - if (GET_ARG("sta_ssid")) { - if (!streq(wificonf->sta_ssid, buff)) { - // No verification needed, at worst it fails to connect - cgi_info("Setting station SSID to: \"%s\"", buff); - strncpy_safe(wificonf->sta_ssid, buff, SSID_LEN); - wifi_change_flags.sta = true; - sta_ssid_pw_changed = true; - } - } - - // ---- Station password (empty for none is allowed) ---- - - if (GET_ARG("sta_password")) { - if (!streq(wificonf->sta_password, buff)) { - // No verification needed, at worst it fails to connect - cgi_info("Setting station password to: \"%s\"", buff); - strncpy_safe(wificonf->sta_password, buff, PASSWORD_LEN); - wifi_change_flags.sta = true; - sta_ssid_pw_changed = true; - } - } - (void)redir_url; if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { @@ -552,7 +400,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) return HTTPD_CGI_DONE; } - //Template code for the WLAN page. httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) { @@ -567,39 +414,17 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, strcpy(buff, ""); // fallback - if (streq(token, "opmode_name")) { - strcpy(buff, opmode2str(wificonf->opmode)); - } - else if (streq(token, "opmode")) { - sprintf(buff, "%d", wificonf->opmode); - } - else if (streq(token, "sta_enable")) { +#define X XGET_CGI_FUNC + XTABLE_WIFI +#undef X + + // non-config + if (streq(token, "sta_enable")) { sprintf(buff, "%d", (wificonf->opmode & STATION_MODE) != 0); } else if (streq(token, "ap_enable")) { sprintf(buff, "%d", (wificonf->opmode & SOFTAP_MODE) != 0); } - else if (streq(token, "tpw")) { - sprintf(buff, "%d", wificonf->tpw); - } - else if (streq(token, "ap_channel")) { - sprintf(buff, "%d", wificonf->ap_channel); - } - else if (streq(token, "ap_ssid")) { - sprintf(buff, "%s", wificonf->ap_ssid); - } - else if (streq(token, "ap_password")) { - sprintf(buff, "%s", wificonf->ap_password); - } - else if (streq(token, "ap_hidden")) { - sprintf(buff, "%d", wificonf->ap_hidden); - } - else if (streq(token, "sta_ssid")) { - sprintf(buff, "%s", wificonf->sta_ssid); - } - else if (streq(token, "sta_password")) { - sprintf(buff, "%s", wificonf->sta_password); - } else if (streq(token, "sta_rssi")) { sprintf(buff, "%d", wifi_station_get_rssi()); } diff --git a/user/config_xmacros.c b/user/config_xmacros.c new file mode 100644 index 0000000..55105f8 --- /dev/null +++ b/user/config_xmacros.c @@ -0,0 +1,99 @@ +// +// Created by MightyPork on 2017/10/22. +// + +#include "config_xmacros.h" +#include "cgi_logging.h" + +void ICACHE_FLASH_ATTR xget_dec(char *buff, u32 value) +{ + sprintf(buff, "%d", value); +} + +void ICACHE_FLASH_ATTR xget_bool(char *buff, bool value) +{ + sprintf(buff, "%d", value?1:0); +} + +void ICACHE_FLASH_ATTR xget_ustring(char *buff, const u8 *value) +{ + sprintf(buff, "%s", (const char *) value); +} + +void ICACHE_FLASH_ATTR xget_string(char *buff, const char *value) +{ + sprintf(buff, "%s", value); +} + +void ICACHE_FLASH_ATTR xget_ip(char *buff, const struct ip_addr *value) +{ + sprintf(buff, IPSTR, GOOD_IP2STR(value->addr)); +} + +// ------------- XSET ------------- + +enum xset_result ICACHE_FLASH_ATTR +xset_ip(const char *name, struct ip_addr *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + u32 ip = ipaddr_addr(buff); + if (ip != 0 && ip != 0xFFFFFFFFUL) { + if (field->addr != ip) { + field->addr = ip; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad IP: %s", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_bool(const char *name, bool *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + bool enable = (atoi(buff) != 0); + + if (*field != enable) { + *field = enable; + return XSET_SET; + } + return XSET_UNCHANGED; +} + +enum xset_result ICACHE_FLASH_ATTR +xset_u8(const char *name, u8 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + u32 val = (u32) atoi(buff); + + if (val <= 255) { + if (*field != val) { + *field = (u8) val; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad value, max 255: %s", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_string(const char *name, char *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + u32 maxlen = (u32) arg; + + if (arg > 0 && (u32)strlen(buff) > maxlen) { + cgi_warn("String too long, max %d", maxlen); + return XSET_FAIL; + } + + if (!streq(field, buff)) { + strncpy_safe(field, buff, (u32)arg); + return XSET_SET; + } + return XSET_UNCHANGED; +} diff --git a/user/config_xmacros.h b/user/config_xmacros.h new file mode 100644 index 0000000..bad4d18 --- /dev/null +++ b/user/config_xmacros.h @@ -0,0 +1,85 @@ +// +// Created by MightyPork on 2017/10/22. +// + +#ifndef ESPTERM_CONFIG_XMACROS_H +#define ESPTERM_CONFIG_XMACROS_H + +#include +#include + +#define XJOIN(a, b) a##b + +/**Do nothing xnotify */ +#define xnoop() + +/** + * XGET interface + * + * @param buff - buffer where the value should be printed + * @param value - value to render to the buffer + */ + +static inline bool xget_dummy(char *buff, u32 value) +{ + sprintf(buff, "unused %d", value); + return false; +} + +void xget_dec(char *buff, u32 value); +void xget_bool(char *buff, bool value); +void xget_ustring(char *buff, const u8 *value); +void xget_string(char *buff, const char *value); +void xget_ip(char *buff, const struct ip_addr *value); +void xget_dhcp(char *buff, const struct dhcps_lease *value); + + +/** + * XSET interface + * + * @param name - field name (for debug) + * @param field - pointer to the target field + * @param buff - field with the value to be set + * @param arg - arbitrary argument, used to modify behavior + * + * @return xset_result + */ + +enum xset_result { + XSET_FAIL = 0, + XSET_SET = 1, + XSET_UNCHANGED = 2 +}; + +// Dummy for unimplemented setters +static inline enum xset_result xset_dummy(const char *name, void *field, const char *buff, const void *arg) +{ + return XSET_UNCHANGED; +} + +enum xset_result xset_ip(const char *name, struct ip_addr *field, const char *buff, const void *arg); +enum xset_result xset_bool(const char *name, bool *field, const char *buff, const void *arg); +enum xset_result xset_u8(const char *name, u8 *field, const char *buff, const void *arg); + +/** + * @param arg - max string length + */ +enum xset_result xset_string(const char *name, char *field, const char *buff, const void *arg); + +/** + * Helper template macro for CGI functions that load GET args to structs using XTABLE + * + * If 'name' is found in connData->getArgs, xset() is called. + * If the result is SET, xnotify() is fired. Else, 'name,' is appended to the redir_url buffer. + */ +#define XSET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ + if (GET_ARG(#name)) { \ + enum xset_result res = xset(#name, cast &wificonf->name, buff, (const void*) (xsarg)); \ + if (res == XSET_SET) { xnotify(); } \ + else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \ + } + +#define XGET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ + if (streq(token, #name)) xget(buff, deref wificonf->name); + +#endif //ESPTERM_CONFIG_XMACROS_H diff --git a/user/wifimgr.c b/user/wifimgr.c index 2588365..46d028a 100644 --- a/user/wifimgr.c +++ b/user/wifimgr.c @@ -4,10 +4,127 @@ #include "wifimgr.h" #include "persist.h" +#include "cgi_logging.h" +#include "config_xmacros.h" WiFiConfigBundle * const wificonf = &persist.current.wificonf; WiFiConfChangeFlags wifi_change_flags; +enum xset_result ICACHE_FLASH_ATTR +xset_wifi_lease_time(const char *name, u16 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s min", name, buff); + int min = atoi(buff); + if (min >= 1 && min <= 2880) { + if (*field != min) { + *field = (u16) min; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Lease time %s out of allowed range 1-2880.", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_wifi_opmode(const char *name, u8 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + int mode = atoi(buff); + if (mode > NULL_MODE && mode < MAX_MODE) { + if (*field != mode) { + *field = (WIFI_MODE) mode; + return XSET_SET; + } + return XSET_UNCHANGED; // opmode does not use flags + } else { + cgi_warn("Bad opmode value \"%s\"", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_wifi_tpw(const char *name, u8 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + int tpw = atoi(buff); + if (tpw >= 0 && tpw <= 82) { // 0 actually isn't 0 but quite low. 82 is very strong + if (*field != tpw) { + *field = (u8) tpw; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("tpw %s out of allowed range 0-82.", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_wifi_ap_channel(const char *name, u8 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + int channel = atoi(buff); + if (channel > 0 && channel < 15) { + if (*field != channel) { + *field = (u8) channel; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad channel value \"%s\", allowed 1-14", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_wifi_ssid(const char *name, u8 **field, const char *buff, const void *arg) +{ + u8 buff2[SSID_LEN]; + + bool want_subs = arg!=0; + + int i; + for (i = 0; i < SSID_LEN; i++) { + char c = buff[i]; + if (c == 0) break; + if (want_subs && (c < 32 || c >= 127)) c = '_'; + buff2[i] = (u8) c; + } + buff2[i] = 0; + + cgi_dbg("Setting %s = %s", name, buff); + if (strlen((char *)buff2) > 0) { + if (!streq(*field, buff2)) { + strncpy_safe(*field, buff2, SSID_LEN); + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad SSID len."); + return XSET_FAIL; + } +} + +/** Set PW - allow len 0 or 8-64 */ +enum xset_result ICACHE_FLASH_ATTR +xset_wifi_pwd(const char *name, u8 **field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + if (strlen(buff) == 0 || (strlen(buff) >= 8 && strlen(buff) < PASSWORD_LEN-1)) { + if (!streq(*field, buff)) { + strncpy_safe(*field, buff, PASSWORD_LEN); + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad password len."); + return XSET_FAIL; + } +} + + int ICACHE_FLASH_ATTR getStaIpAsString(char *buffer) { WIFI_MODE x = wifi_get_opmode(); @@ -42,13 +159,11 @@ wifimgr_restore_defaults(void) wificonf->ap_password[0] = 0; // PSK2 always if password is not null. wificonf->ap_hidden = false; - IP4_ADDR(&wificonf->ap_addr.ip, 192, 168, 4, 1); - IP4_ADDR(&wificonf->ap_addr.netmask, 255, 255, 255, 0); - wificonf->ap_addr.gw.addr = wificonf->ap_addr.gw.addr; + IP4_ADDR(&wificonf->ap_addr_ip, 192, 168, 4, 1); + IP4_ADDR(&wificonf->ap_addr_mask, 255, 255, 255, 0); - IP4_ADDR(&wificonf->ap_dhcp_range.start_ip, 192, 168, 4, 100); - IP4_ADDR(&wificonf->ap_dhcp_range.end_ip, 192, 168, 4, 200); - wificonf->ap_dhcp_range.enable = 1; // this will never get changed, idk why it's even there + IP4_ADDR(&wificonf->ap_dhcp_start, 192, 168, 4, 100); + IP4_ADDR(&wificonf->ap_dhcp_end, 192, 168, 4, 200); wificonf->ap_dhcp_time = 120; // --- Client config --- @@ -56,9 +171,9 @@ wifimgr_restore_defaults(void) wificonf->sta_password[0] = 0; wificonf->sta_dhcp_enable = true; - IP4_ADDR(&wificonf->sta_addr.ip, 192, 168, 0, (mac[5] == 1 ? 2 : mac[5])); // avoid being the same as "default gw" - IP4_ADDR(&wificonf->sta_addr.netmask, 255, 255, 255, 0); - IP4_ADDR(&wificonf->sta_addr.gw, 192, 168, 0, 1); + IP4_ADDR(&wificonf->sta_addr_ip, 192, 168, 0, (mac[5] == 1 ? 2 : mac[5])); // avoid being the same as "default gw" + IP4_ADDR(&wificonf->sta_addr_mask, 255, 255, 255, 0); + IP4_ADDR(&wificonf->sta_addr_gw, 192, 168, 0, 1); // a common default... } static void ICACHE_FLASH_ATTR @@ -83,13 +198,18 @@ configure_station(void) } else { wifi_info("[WiFi] Setting up static IP..."); - wifi_dbg("[WiFi] Client.ip = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr)); - wifi_dbg("[WiFi] Client.mask = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr)); - wifi_dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr)); + wifi_dbg("[WiFi] Client.ip = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_ip.addr)); + wifi_dbg("[WiFi] Client.mask = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_mask.addr)); + wifi_dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_gw.addr)); wifi_station_dhcpc_stop(); // Load static IP config - if (!wifi_set_ip_info(STATION_IF, &wificonf->sta_addr)) { + struct ip_info ipstruct; + ipstruct.ip.addr = wificonf->sta_addr_ip.addr; + ipstruct.netmask.addr = wificonf->sta_addr_mask.addr; + ipstruct.gw.addr = wificonf->sta_addr_gw.addr; + + if (!wifi_set_ip_info(STATION_IF, &ipstruct)) { error("[WiFi] Error setting static IP!"); return; } @@ -112,7 +232,7 @@ configure_ap(void) strcpy((char *) conf.password, (char *) wificonf->ap_password); conf.authmode = (wificonf->ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK); conf.ssid_len = (uint8_t) strlen((char *) conf.ssid); - conf.ssid_hidden = wificonf->ap_hidden; + conf.ssid_hidden = (uint8) wificonf->ap_hidden; conf.max_connection = 4; // default 4 (max possible) conf.beacon_interval = 100; // default 100 ms @@ -127,24 +247,32 @@ configure_ap(void) // Set IP wifi_info("[WiFi] Configuring SoftAP local IP..."); - wifi_dbg("[WiFi] SoftAP.ip = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr)); - wifi_dbg("[WiFi] SoftAP.mask = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr)); - wifi_dbg("[WiFi] SoftAP.gw = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.gw.addr)); + wifi_dbg("[WiFi] SoftAP.ip = "IPSTR, GOOD_IP2STR(wificonf->ap_addr_ip.addr)); + wifi_dbg("[WiFi] SoftAP.mask = "IPSTR, GOOD_IP2STR(wificonf->ap_addr_mask.addr)); wifi_softap_dhcps_stop(); // Configure DHCP - if (!wifi_set_ip_info(SOFTAP_IF, &wificonf->ap_addr)) { + struct ip_info ipstruct; + ipstruct.ip.addr = wificonf->ap_addr_ip.addr; + ipstruct.netmask.addr = wificonf->ap_addr_mask.addr; + ipstruct.gw.addr = wificonf->ap_addr_ip.addr; + + if (!wifi_set_ip_info(SOFTAP_IF, &ipstruct)) { error("[WiFi] IP set fail!"); return; } wifi_info("[WiFi] Configuring SoftAP DHCP server..."); - wifi_dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr)); - wifi_dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr)); + wifi_dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_start.addr)); + wifi_dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_end.addr)); wifi_dbg("[WiFi] DHCP.lease = %d minutes", wificonf->ap_dhcp_time); - if (!wifi_softap_set_dhcps_lease(&wificonf->ap_dhcp_range)) { + struct dhcps_lease dhcpstruct; + dhcpstruct.start_ip = wificonf->ap_dhcp_start; + dhcpstruct.end_ip = wificonf->ap_dhcp_end; + dhcpstruct.enable = 1; // ??? + if (!wifi_softap_set_dhcps_lease(&dhcpstruct)) { error("[WiFi] DHCP address range set fail!"); return; } @@ -190,7 +318,7 @@ wifimgr_apply_settings(void) } if (opmode != wificonf->opmode) { - wifi_set_opmode_current(wificonf->opmode); + wifi_set_opmode_current((WIFI_MODE) wificonf->opmode); } // Configure the client diff --git a/user/wifimgr.h b/user/wifimgr.h index 146c3c8..22e70b0 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -19,6 +19,43 @@ #define WIFICONF_VERSION 0 +#define wifimgr_notify_ap() { wifi_change_flags.ap = true; } +#define wifimgr_notify_sta() { wifi_change_flags.ap = true; } + +//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY +#define XTABLE_WIFI \ + X(u8, opmode, , , xget_dec, , xset_wifi_opmode, NULL, xnoop) \ + \ + X(u8, tpw, , , xget_dec, , xset_wifi_tpw, NULL, wifimgr_notify_ap) \ + X(u8, ap_channel, , , xget_dec, , xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ + X(u8, ap_ssid, [SSID_LEN], , xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ + X(u8, ap_password, [PASSWORD_LEN], , xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ + X(bool, ap_hidden, , , xget_bool, , xset_bool, NULL, wifimgr_notify_ap) \ + \ + X(u16, ap_dhcp_time, , , xget_dec, , xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ + X(u32, unused1, , , xget_dummy, , xset_dummy, NULL, xnoop) \ + X(struct ip_addr, ap_dhcp_start, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_dhcp_end, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + \ + X(struct ip_addr, ap_addr_ip, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_mask, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + \ + \ + X(u32, unused2, , , xget_dummy, , xset_dummy, NULL, xnoop) \ + X(u8, sta_ssid, [SSID_LEN], , xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ + X(u8, sta_password, [PASSWORD_LEN], , xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ + X(bool, sta_dhcp_enable, , , xget_bool, , xset_bool, NULL, wifimgr_notify_sta) \ + \ + X(struct ip_addr, sta_addr_ip, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_mask, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_gw, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ + \ + \ + X(u8, config_version, , , xget_dec, , xset_u8, NULL, xnoop) + +// unused1 - replaces 'enabled' bit from old dhcps_lease struct +// unused2 - gap after 'ap_gw' which isn't used and doesn't make sense + /** * A structure holding all configured WiFi parameters * and the active state. @@ -26,25 +63,15 @@ * This block can be used eg. for WiFi config backup. */ typedef struct { - WIFI_MODE opmode : 8; - u8 tpw; - - // AP config - u8 ap_channel; - u8 ap_ssid[SSID_LEN]; - u8 ap_password[PASSWORD_LEN]; - bool ap_hidden; - // - u16 ap_dhcp_time; // in minutes - struct dhcps_lease ap_dhcp_range; - struct ip_info ap_addr; - - // Client config - u8 sta_ssid[SSID_LEN]; - u8 sta_password[PASSWORD_LEN]; - bool sta_dhcp_enable; - struct ip_info sta_addr; - u8 config_version; +#define X( \ + type, name, suffix, \ + deref, xget, \ + cast, xset, xsarg, \ + xnotify) type name suffix; + + XTABLE_WIFI + +#undef X } WiFiConfigBundle; typedef struct { @@ -62,6 +89,13 @@ void wifimgr_apply_settings(void); int getStaIpAsString(char *buffer); +enum xset_result xset_wifi_lease_time(const char *name, u16 *field, const char *buff, const void *arg); +enum xset_result xset_wifi_opmode(const char *name, u8 *field, const char *buff, const void *arg); +enum xset_result xset_wifi_tpw(const char *name, u8 *field, const char *buff, const void *arg); +enum xset_result xset_wifi_ap_channel(const char *name, u8 *field, const char *buff, const void *arg); +enum xset_result xset_wifi_ssid(const char *name, u8 **field, const char *buff, const void *arg); +enum xset_result xset_wifi_pwd(const char *name, u8 **field, const char *buff, const void *arg); + #if DEBUG_WIFI #define wifi_warn warn #define wifi_dbg dbg From 9146c9880ac02594c9a6cec1380a21f9d32a19c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 16:45:45 +0200 Subject: [PATCH 02/10] slightly cleaner XTABLE_WIFI --- user/wifimgr.h | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/user/wifimgr.h b/user/wifimgr.h index 22e70b0..ee28d40 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -23,35 +23,36 @@ #define wifimgr_notify_sta() { wifi_change_flags.ap = true; } //....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY +// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) #define XTABLE_WIFI \ - X(u8, opmode, , , xget_dec, , xset_wifi_opmode, NULL, xnoop) \ + X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, xnoop) \ \ - X(u8, tpw, , , xget_dec, , xset_wifi_tpw, NULL, wifimgr_notify_ap) \ - X(u8, ap_channel, , , xget_dec, , xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ - X(u8, ap_ssid, [SSID_LEN], , xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ - X(u8, ap_password, [PASSWORD_LEN], , xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ - X(bool, ap_hidden, , , xget_bool, , xset_bool, NULL, wifimgr_notify_ap) \ + X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ + X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ + X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ + X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ + X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap) \ \ - X(u16, ap_dhcp_time, , , xget_dec, , xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ - X(u32, unused1, , , xget_dummy, , xset_dummy, NULL, xnoop) \ - X(struct ip_addr, ap_dhcp_start, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_dhcp_end, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ + X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ + X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ - X(struct ip_addr, ap_addr_ip, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_addr_mask, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ \ - X(u32, unused2, , , xget_dummy, , xset_dummy, NULL, xnoop) \ - X(u8, sta_ssid, [SSID_LEN], , xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ - X(u8, sta_password, [PASSWORD_LEN], , xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ - X(bool, sta_dhcp_enable, , , xget_bool, , xset_bool, NULL, wifimgr_notify_sta) \ + X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ + X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ + X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta) \ \ - X(struct ip_addr, sta_addr_ip, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_mask, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_gw, , &, xget_ip, , xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ \ \ - X(u8, config_version, , , xget_dec, , xset_u8, NULL, xnoop) + X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, xnoop) // unused1 - replaces 'enabled' bit from old dhcps_lease struct // unused2 - gap after 'ap_gw' which isn't used and doesn't make sense From 2c80b59d7118d9ee9f56d89e1359900e79669230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 16:46:28 +0200 Subject: [PATCH 03/10] formatting --- user/wifimgr.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/user/wifimgr.h b/user/wifimgr.h index ee28d40..a90510f 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -25,34 +25,34 @@ //....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY // Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) #define XTABLE_WIFI \ - X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, xnoop) \ + X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, xnoop) \ \ - X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ - X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ - X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ - X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ - X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap) \ + X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ + X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ + X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ + X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ + X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap) \ \ - X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ - X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ - X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ + X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ + X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ - X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ \ - X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ - X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ - X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ - X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta) \ + X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ + X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ + X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta) \ \ - X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ \ \ - X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, xnoop) + X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, xnoop) // unused1 - replaces 'enabled' bit from old dhcps_lease struct // unused2 - gap after 'ap_gw' which isn't used and doesn't make sense From 3eac0ee7946d3d69e67d3f05cc01e4ea0f07f26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 16:50:45 +0200 Subject: [PATCH 04/10] fmt again --- user/config_xmacros.h | 3 +++ user/wifimgr.h | 9 ++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/user/config_xmacros.h b/user/config_xmacros.h index bad4d18..9da5daf 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -82,4 +82,7 @@ enum xset_result xset_string(const char *name, char *field, const char *buff, co #define XGET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ if (streq(token, #name)) xget(buff, deref wificonf->name); +#define XSTRUCT_FIELD(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ + type name suffix; + #endif //ESPTERM_CONFIG_XMACROS_H diff --git a/user/wifimgr.h b/user/wifimgr.h index a90510f..2302571 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -8,6 +8,7 @@ #define ESP_VT100_FIRMWARE_WIFI_MANAGER_H #include +#include "config_xmacros.h" #include "cgi_wifi.h" #define SSID_LEN 32 @@ -64,14 +65,8 @@ * This block can be used eg. for WiFi config backup. */ typedef struct { -#define X( \ - type, name, suffix, \ - deref, xget, \ - cast, xset, xsarg, \ - xnotify) type name suffix; - +#define X XSTRUCT_FIELD XTABLE_WIFI - #undef X } WiFiConfigBundle; From f0bc70553a9649944220f8bb54520bd98a3f00af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 17:55:52 +0200 Subject: [PATCH 05/10] use xmacros for system --- user/cgi_network.c | 2 + user/cgi_system.c | 135 +++++++++++++++++------------------------- user/cgi_wifi.c | 2 + user/config_xmacros.c | 21 ++++++- user/config_xmacros.h | 18 +++--- user/routes.c | 4 +- user/syscfg.c | 104 +++++++++++++++++++++++++++++++- user/syscfg.h | 44 +++++++++++--- user/wifimgr.c | 6 ++ user/wifimgr.h | 42 ++++++------- 10 files changed, 255 insertions(+), 123 deletions(-) diff --git a/user/cgi_network.c b/user/cgi_network.c index 6ed9d40..e417dd7 100644 --- a/user/cgi_network.c +++ b/user/cgi_network.c @@ -49,6 +49,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData) // ---- AP DHCP server lease time ---- +#define XSTRUCT wificonf #define X XSET_CGI_FUNC XTABLE_WIFI #undef X @@ -98,6 +99,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke strcpy(buff, ""); // fallback +#define XSTRUCT wificonf #define X XGET_CGI_FUNC XTABLE_WIFI #undef X diff --git a/user/cgi_system.c b/user/cgi_system.c index d23d20b..3f939a7 100755 --- a/user/cgi_system.c +++ b/user/cgi_system.c @@ -12,6 +12,9 @@ #define SET_REDIR_SUC "/cfg/system" #define SET_REDIR_ERR SET_REDIR_SUC"?err=" +// Select which struct we want to use for X tables +#define XSTRUCT sysconf + static ETSTimer tmr; static void ICACHE_FLASH_ATTR tmrCb(void *arg) @@ -95,106 +98,77 @@ cgiSystemCfgSetParams(HttpdConnData *connData) memcpy(admin_backup, &persist.admin, sizeof(AdminConfigBlock)); memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle)); + // flags for the template builder + bool admin = false, tpl = false; do { - if (!GET_ARG("pw")) { - break; // if no PW in GET, not trying to configure anything protected - } - - if (!streq(buff, persist.admin.pw)) { - warn("Bad admin pw!"); - redir_url += sprintf(redir_url, "pw,"); - break; - } - - // authenticated OK - if (GET_ARG("pwlock")) { - cgi_dbg("pwlock: %s", buff); - int pwlock = atoi(buff); - if (pwlock < 0 || pwlock >= PWLOCK_MAX) { - cgi_warn("Bad pwlock %s", buff); - redir_url += sprintf(redir_url, "pwlock,"); - break; + // Check admin PW + if (GET_ARG("pw")) { + if (!streq(buff, persist.admin.pw)) { + warn("Bad admin pw!"); + redir_url += sprintf(redir_url, "pw,"); + break; // Abort + } else { + admin = true; } - - sysconf->pwlock = (enum pwlock) pwlock; } - if (GET_ARG("access_pw")) { - cgi_dbg("access_pw: %s", buff); - + // Changing admin PW + if (admin && GET_ARG("admin_pw")) { if (strlen(buff)) { - strcpy(buff2, buff); - if (!GET_ARG("access_pw2")) { - cgi_warn("Missing repeated access_pw %s", buff); - redir_url += sprintf(redir_url, "access_pw2,"); - break; - } - - if (!streq(buff, buff2)) { - cgi_warn("Bad repeated access_pw %s", buff); - redir_url += sprintf(redir_url, "access_pw2,"); - break; - } - - if (strlen(buff) >= 64) { - cgi_warn("Too long access_pw %s", buff); - redir_url += sprintf(redir_url, "access_pw,"); - break; - } - - cgi_dbg("Changing access PW!"); - strncpy(sysconf->access_pw, buff, 64); - } - } - - if (GET_ARG("access_name")) { - cgi_dbg("access_name: %s", buff); - - if (!strlen(buff) || strlen(buff) >= 32) { - cgi_warn("Too long access_name %s", buff); - redir_url += sprintf(redir_url, "access_name,"); - break; - } - - strncpy(sysconf->access_name, buff, 32); - } - - if (GET_ARG("admin_pw")) { - cgi_dbg("admin_pw: %s", buff); + cgi_dbg("admin_pw: %s", buff); - if (strlen(buff)) { strcpy(buff2, buff); if (!GET_ARG("admin_pw2")) { cgi_warn("Missing repeated admin_pw %s", buff); redir_url += sprintf(redir_url, "admin_pw2,"); - break; + break; // Abort } if (!streq(buff, buff2)) { cgi_warn("Bad repeated admin_pw %s", buff); redir_url += sprintf(redir_url, "admin_pw2,"); - break; + break; // Abort } if (strlen(buff) >= 64) { cgi_warn("Too long admin_pw %s", buff); redir_url += sprintf(redir_url, "admin_pw,"); - break; + break; // Abort } cgi_dbg("Changing admin PW!"); strncpy(persist.admin.pw, buff, 64); + + break; // this is the only field in this form } } - } while (0); - if (GET_ARG("overclock")) { - cgi_dbg("overclock = %s", buff); - int enable = atoi(buff); - if (sysconf->overclock != enable) { - sysconf->overclock = (bool)enable; + // Reject filled but unconfirmed access PW + if (admin && GET_ARG("access_pw")) { + if (strlen(buff)) { + cgi_dbg("access_pw: %s", buff); + + strcpy(buff2, buff); + if (!GET_ARG("access_pw2")) { + cgi_warn("Missing repeated access_pw %s", buff); + redir_url += sprintf(redir_url, "access_pw2,"); + break; // Abort + } + + if (!streq(buff, buff2)) { + cgi_warn("Bad repeated access_pw %s", buff); + redir_url += sprintf(redir_url, "access_pw2,"); + break; // Abort + } + } } - } + + // Settings in the system config block +#define X XSET_CGI_FUNC + XTABLE_SYSCONF +#undef X + + } while (0); (void)redir_url; @@ -236,13 +210,13 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg) strcpy(buff, ""); // fallback - if (streq(token, "pwlock")) { - sprintf(buff, "%d", sysconf->pwlock); - } - else if (streq(token, "access_name")) { - sprintf(buff, "%s", sysconf->access_name); - } - else if (streq(token, "def_access_name")) { + const bool admin = false, tpl=true; + +#define X XGET_CGI_FUNC + XTABLE_SYSCONF +#undef X + + if (streq(token, "def_access_name")) { sprintf(buff, "%s", DEF_ACCESS_NAME); } else if (streq(token, "def_access_pw")) { @@ -251,9 +225,6 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg) else if (streq(token, "def_admin_pw")) { sprintf(buff, "%s", DEFAULT_ADMIN_PW); } - else if (streq(token, "overclock")) { - sprintf(buff, "%d", sysconf->overclock); - } tplSend(connData, buff, -1); return HTTPD_CGI_DONE; diff --git a/user/cgi_wifi.c b/user/cgi_wifi.c index 41be75d..fe09fda 100644 --- a/user/cgi_wifi.c +++ b/user/cgi_wifi.c @@ -324,6 +324,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) bool sta_turned_on = false; bool sta_ssid_pw_changed = false; +#define XSTRUCT wificonf #define X XSET_CGI_FUNC XTABLE_WIFI #undef X @@ -414,6 +415,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, strcpy(buff, ""); // fallback +#define XSTRUCT wificonf #define X XGET_CGI_FUNC XTABLE_WIFI #undef X diff --git a/user/config_xmacros.c b/user/config_xmacros.c index 55105f8..d52f6cf 100644 --- a/user/config_xmacros.c +++ b/user/config_xmacros.c @@ -81,7 +81,7 @@ xset_u8(const char *name, u8 *field, const char *buff, const void *arg) } enum xset_result ICACHE_FLASH_ATTR -xset_string(const char *name, char *field, const char *buff, const void *arg) +xset_string(const char *name, s8 **field, const char *buff, const void *arg) { cgi_dbg("Setting %s = %s", name, buff); u32 maxlen = (u32) arg; @@ -97,3 +97,22 @@ xset_string(const char *name, char *field, const char *buff, const void *arg) } return XSET_UNCHANGED; } + + +enum xset_result ICACHE_FLASH_ATTR +xset_ustring(const char *name, u8 **field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + u32 maxlen = (u32) arg; + + if (arg > 0 && (u32)strlen(buff) > maxlen) { + cgi_warn("String too long, max %d", maxlen); + return XSET_FAIL; + } + + if (!streq((char *)field, buff)) { + strncpy_safe(field, buff, (u32)arg); + return XSET_SET; + } + return XSET_UNCHANGED; +} diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 9da5daf..53495b0 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -64,7 +64,8 @@ enum xset_result xset_u8(const char *name, u8 *field, const char *buff, const vo /** * @param arg - max string length */ -enum xset_result xset_string(const char *name, char *field, const char *buff, const void *arg); +enum xset_result xset_string(const char *name, s8 **field, const char *buff, const void *arg); +enum xset_result xset_ustring(const char *name, u8 **field, const char *buff, const void *arg); /** * Helper template macro for CGI functions that load GET args to structs using XTABLE @@ -72,17 +73,20 @@ enum xset_result xset_string(const char *name, char *field, const char *buff, co * If 'name' is found in connData->getArgs, xset() is called. * If the result is SET, xnotify() is fired. Else, 'name,' is appended to the redir_url buffer. */ -#define XSET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ - if (GET_ARG(#name)) { \ - enum xset_result res = xset(#name, cast &wificonf->name, buff, (const void*) (xsarg)); \ +#define XSET_CGI_FUNC(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ + if ((allow) && GET_ARG(#name)) { \ + enum xset_result res = xset(#name, cast &XSTRUCT->name, buff, (const void*) (xsarg)); \ if (res == XSET_SET) { xnotify(); } \ else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \ } -#define XGET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ - if (streq(token, #name)) xget(buff, deref wificonf->name); +#define XGET_CGI_FUNC(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ + if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name); -#define XSTRUCT_FIELD(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify) \ +#define XSTRUCT_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ type name suffix; +#define XDUMP_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ + { xget(buff, deref XSTRUCT->name); dbg(#name " = %s", buff); } + #endif //ESPTERM_CONFIG_XMACROS_H diff --git a/user/routes.c b/user/routes.c index 4b6b502..895133e 100644 --- a/user/routes.c +++ b/user/routes.c @@ -23,8 +23,8 @@ static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { if (no == 0) { - os_strcpy(user, sysconf->access_name); - os_strcpy(pass, sysconf->access_pw); + os_strcpy(user, (const char *) sysconf->access_name); + os_strcpy(pass, (const char *) sysconf->access_pw); return 1; } if (no == 1) { diff --git a/user/syscfg.c b/user/syscfg.c index cd22d77..9a2a6f2 100644 --- a/user/syscfg.c +++ b/user/syscfg.c @@ -6,12 +6,112 @@ #include "persist.h" #include "uart_driver.h" #include "serial.h" +#include "cgi_logging.h" SystemConfigBundle * const sysconf = &persist.current.sysconf; +enum xset_result ICACHE_FLASH_ATTR +xset_sys_baudrate(const char *name, u32 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + int baud = atoi(buff); + if (baud == BIT_RATE_300 || + baud == BIT_RATE_600 || + baud == BIT_RATE_1200 || + baud == BIT_RATE_2400 || + baud == BIT_RATE_4800 || + baud == BIT_RATE_9600 || + baud == BIT_RATE_19200 || + baud == BIT_RATE_38400 || + baud == BIT_RATE_57600 || + baud == BIT_RATE_74880 || + baud == BIT_RATE_115200 || + baud == BIT_RATE_230400 || + baud == BIT_RATE_460800 || + baud == BIT_RATE_921600 || + baud == BIT_RATE_1843200 || + baud == BIT_RATE_3686400) { + if (*field != baud) { + *field = (u32) baud; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad baud rate %s", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_sys_parity(const char *name, u8 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + int parity = atoi(buff); + if (parity >= 0 && parity <= 2) { + if (*field != parity) { + *field = (UartParityMode) parity; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad parity %s", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_sys_stopbits(const char *name, u8 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + int stopbits = atoi(buff); + if (stopbits >= 1 && stopbits <= 3) { + if (*field != stopbits) { + *field = (UartParityMode) stopbits; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad stopbits %s", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_sys_pwlock(const char *name, u8 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + + int pwlock = atoi(buff); + if (pwlock >= 0 && pwlock < PWLOCK_MAX) { + if (*field != pwlock) { + *field = (enum pwlock) pwlock; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad pwlock %s", buff); + return XSET_FAIL; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_sys_accesspw(const char *name, u8 **field, const char *buff, const void *arg) +{ + // Do not overwrite pw if empty + if (strlen(buff) == 0) return XSET_UNCHANGED; + return xset_ustring(name, field, buff, arg); +} + + void ICACHE_FLASH_ATTR sysconf_apply_settings(void) { +// char buff[64]; +//#define XSTRUCT sysconf +//#define X XDUMP_FIELD +// XTABLE_SYSCONF +//#undef X + bool changed = false; if (sysconf->config_version < 1) { dbg("Upgrading syscfg to v 1"); @@ -40,7 +140,7 @@ sysconf_restore_defaults(void) sysconf->config_version = SYSCONF_VERSION; sysconf->access_pw[0] = 0; sysconf->pwlock = PWLOCK_NONE; - strcpy(sysconf->access_pw, DEF_ACCESS_PW); - strcpy(sysconf->access_name, DEF_ACCESS_NAME); + strcpy((char *)sysconf->access_pw, DEF_ACCESS_PW); + strcpy((char *)sysconf->access_name, DEF_ACCESS_NAME); sysconf->overclock = false; } diff --git a/user/syscfg.h b/user/syscfg.h index e316b68..70ea94b 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -6,6 +6,7 @@ #define ESP_VT100_FIRMWARE_SYSCFG_H #include +#include "config_xmacros.h" // Size designed for the wifi config structure // Must be constant to avoid corrupting user config after upgrade @@ -24,15 +25,36 @@ enum pwlock { PWLOCK_MAX = 5, }; + +//....Type................Name..Suffix...............Deref..XGET........Allow..Cast..XSET.........................NOTIFY +// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) +#define XTABLE_SYSCONF \ + X(u32, uart_baudrate, /**/, /**/, xget_dec, 1, /**/, xset_sys_baudrate, NULL, xnoop) \ + X(u8, uart_parity, /**/, /**/, xget_dec, 1, /**/, xset_sys_parity, NULL, xnoop) \ + X(u8, uart_stopbits, /**/, /**/, xget_dec, 1, /**/, xset_sys_stopbits, NULL, xnoop) \ + \ + X(u8, config_version, /**/, /**/, xget_dec, 0, /**/, xset_u8, NULL, xnoop) \ + \ + X(u8, pwlock, /**/, /**/, xget_dec, admin|tpl, /**/, xset_sys_pwlock, NULL, xnoop) \ + X(u8, access_pw, [64], /**/, xget_ustring, admin, (u8**), xset_sys_accesspw, NULL, xnoop) \ + X(u8, access_name, [32], /**/, xget_ustring, admin|tpl, (u8**), xset_ustring, NULL, xnoop) \ + \ + X(bool, overclock, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, xnoop) \ + + typedef struct { - u32 uart_baudrate; - u8 uart_parity; - u8 uart_stopbits; - u8 config_version; - enum pwlock pwlock : 8; // page access lock - char access_pw[64]; // access password - char access_name[32]; // access name - bool overclock; +#define X XSTRUCT_FIELD + XTABLE_SYSCONF +#undef X + +// u32 uart_baudrate; +// u8 uart_parity; +// u8 uart_stopbits; +// u8 config_version; +// enum pwlock pwlock : 8; // page access lock +// char access_pw[64]; // access password +// char access_name[32]; // access name +// bool overclock; } SystemConfigBundle; extern SystemConfigBundle * const sysconf; @@ -41,4 +63,10 @@ void sysconf_apply_settings(void); void sysconf_restore_defaults(void); +enum xset_result xset_sys_baudrate(const char *name, u32 *field, const char *buff, const void *arg); +enum xset_result xset_sys_parity(const char *name, u8 *field, const char *buff, const void *arg); +enum xset_result xset_sys_stopbits(const char *name, u8 *field, const char *buff, const void *arg); +enum xset_result xset_sys_pwlock(const char *name, u8 *field, const char *buff, const void *arg); +enum xset_result xset_sys_accesspw(const char *name, u8 **field, const char *buff, const void *arg); + #endif //ESP_VT100_FIRMWARE_SYSCFG_H diff --git a/user/wifimgr.c b/user/wifimgr.c index 46d028a..b0376c9 100644 --- a/user/wifimgr.c +++ b/user/wifimgr.c @@ -300,6 +300,12 @@ wifimgr_apply_settings(void) { wifi_info("[WiFi] Initializing..."); +// char buff[64]; +//#define XSTRUCT wificonf +//#define X XDUMP_FIELD +// XTABLE_WIFICONF +//#undef X + // !!! Update to current version !!! // Force wifi cycle diff --git a/user/wifimgr.h b/user/wifimgr.h index 2302571..5e0952f 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -23,37 +23,37 @@ #define wifimgr_notify_ap() { wifi_change_flags.ap = true; } #define wifimgr_notify_sta() { wifi_change_flags.ap = true; } -//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY +//....Type................Name..Suffix...............Deref..XGET.......Allow..Cast..XSET.........................NOTIFY // Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) #define XTABLE_WIFI \ - X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, xnoop) \ + X(u8, opmode, /**/, /**/, xget_dec, 1, /**/, xset_wifi_opmode, NULL, xnoop) \ \ - X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ - X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ - X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ - X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ - X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap) \ + X(u8, tpw, /**/, /**/, xget_dec, 1, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ + X(u8, ap_channel, /**/, /**/, xget_dec, 1, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ + X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ + X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ + X(bool, ap_hidden, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, wifimgr_notify_ap) \ \ - X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ - X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ - X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(u16, ap_dhcp_time, /**/, /**/, xget_dec, 1, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ + X(u32, unused1, /**/, /**/, xget_dummy, 0, /**/, xset_dummy, NULL, xnoop) \ + X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ - X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ \ \ - X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, xnoop) \ - X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ - X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ - X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta) \ + X(u32, unused2, /**/, /**/, xget_dummy, 0, /**/, xset_dummy, NULL, xnoop) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ + X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ + X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, wifimgr_notify_sta) \ \ - X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ \ \ - X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, xnoop) + X(u8, config_version, /**/, /**/, xget_dec, 0, /**/, xset_u8, NULL, xnoop) // unused1 - replaces 'enabled' bit from old dhcps_lease struct // unused2 - gap after 'ap_gw' which isn't used and doesn't make sense From 1ab4101ac0649f95362c89179627b455928d2f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 20:30:24 +0200 Subject: [PATCH 06/10] use xmacros for terminal config --- front-end | 2 +- user/apars_osc.c | 2 +- user/cgi_sockets.c | 4 +- user/cgi_system.c | 15 +- user/cgi_term_cfg.c | 486 ++++-------------------------------------- user/config_xmacros.c | 26 +++ user/config_xmacros.h | 10 +- user/screen.c | 315 ++++++++++++++++++++++----- user/screen.h | 77 +++++-- user/syscfg.h | 19 +- user/wifimgr.h | 42 ++-- 11 files changed, 427 insertions(+), 571 deletions(-) diff --git a/front-end b/front-end index 32c889b..560f578 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 32c889b714dae859f51e6b46829fd85be59d9ed0 +Subproject commit 560f5783bc0eb5bf845a55cc30a3928f1d01b85f diff --git a/user/apars_osc.c b/user/apars_osc.c index 387b329..0bb7352 100644 --- a/user/apars_osc.c +++ b/user/apars_osc.c @@ -59,7 +59,7 @@ apars_handle_osc(char *buffer) } else if (n >= 91 && n <= 95) { // ESPTerm: action button message - strncpy(termconf_live.btn_msg[n - 91], buffer, TERM_BTN_MSG_LEN); + screen_set_button_message(n - 90, buffer); } else { ansi_noimpl("OSC %d ; %s ST", n, buffer); diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index 565afa5..4263cd1 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -271,8 +271,8 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int case 'b': // action button press btnNum = (u8) (data[1]); - if (btnNum > 0 && btnNum < 10) { - UART_SendAsync(termconf_live.btn_msg[btnNum-1], -1); + if (btnNum > 0 && btnNum <= TERM_BTN_COUNT) { + UART_SendAsync(TERM_BM_N(&termconf_live, btnNum-1), -1); } break; diff --git a/user/cgi_system.c b/user/cgi_system.c index 3f939a7..b780268 100755 --- a/user/cgi_system.c +++ b/user/cgi_system.c @@ -12,9 +12,6 @@ #define SET_REDIR_SUC "/cfg/system" #define SET_REDIR_ERR SET_REDIR_SUC"?err=" -// Select which struct we want to use for X tables -#define XSTRUCT sysconf - static ETSTimer tmr; static void ICACHE_FLASH_ATTR tmrCb(void *arg) @@ -99,7 +96,9 @@ cgiSystemCfgSetParams(HttpdConnData *connData) memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle)); // flags for the template builder - bool admin = false, tpl = false; + bool uart_changed = false; //!< this is set in uart notify, for use in terminal settings (dummy here) + bool admin = false; + const bool tpl = false; // this optionally disables some fields do { // Check admin PW if (GET_ARG("pw")) { @@ -164,13 +163,16 @@ cgiSystemCfgSetParams(HttpdConnData *connData) } // Settings in the system config block +#define XSTRUCT sysconf #define X XSET_CGI_FUNC XTABLE_SYSCONF #undef X +#undef XSTRUCT } while (0); (void)redir_url; + (void)uart_changed; // unused if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { // All was OK @@ -210,11 +212,14 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg) strcpy(buff, ""); // fallback - const bool admin = false, tpl=true; + const bool admin = false; + const bool tpl=true; +#define XSTRUCT sysconf #define X XGET_CGI_FUNC XTABLE_SYSCONF #undef X +#undef XSTRUCT if (streq(token, "def_access_name")) { sprintf(buff, "%s", DEF_ACCESS_NAME); diff --git a/user/cgi_term_cfg.c b/user/cgi_term_cfg.c index 13064a8..9972981 100644 --- a/user/cgi_term_cfg.c +++ b/user/cgi_term_cfg.c @@ -15,22 +15,6 @@ Cgi/template routines for configuring non-wifi settings #define SET_REDIR_SUC "/cfg/term" #define SET_REDIR_ERR SET_REDIR_SUC"?err=" -/** convert hex number to int */ -static ICACHE_FLASH_ATTR u32 -decodehex(const char *buf) { - u32 n = 0; - char c; - while ((c = *buf++) != 0) { - if (c >= '0' && c <= '9') c -= '0'; - else if (c >= 'a' && c <= 'f') c -= 'a'-10; - else if (c >= 'A' && c <= 'F') c -= 'A'-10; - else c = 0; - n *= 16; - n += c; - } - return n; -} - /** * Universal CGI endpoint to set Terminal params. */ @@ -39,15 +23,11 @@ cgiTermCfgSetParams(HttpdConnData *connData) { char buff[50]; char redir_url_buf[100]; - int32 n, w, h; ScreenNotifyTopics topics = 0; - bool shall_clear_screen = false; - bool shall_init_uart = false; - char *redir_url = redir_url_buf; redir_url += sprintf(redir_url, SET_REDIR_ERR); - // we'll test if anything was printed by looking for \0 in failed_keys_buf + // we'll test if anything was printed by looking for \0 in redir_url SystemConfigBundle *sysconf_backup = malloc(sizeof(SystemConfigBundle)); TerminalConfigBundle *termconf_backup = malloc(sizeof(TerminalConfigBundle)); @@ -59,328 +39,29 @@ cgiTermCfgSetParams(HttpdConnData *connData) return HTTPD_CGI_DONE; } - // width and height must always go together so we can do max size validation - if (GET_ARG("term_width")) { - do { - cgi_dbg("Default screen width: %s", buff); - w = atoi(buff); - if (w < 1) { - cgi_warn("Bad width: \"%s\"", buff); - redir_url += sprintf(redir_url, "term_width,"); - break; - } - - if (!GET_ARG("term_height")) { - cgi_warn("Missing height arg!"); - // this wont happen normally when the form is used - redir_url += sprintf(redir_url, "term_height,"); - break; - } - - cgi_dbg("Default screen height: %s", buff); - h = atoi(buff); - if (h < 1) { - cgi_warn("Bad height: \"%s\"", buff); - redir_url += sprintf(redir_url, "term_height,"); - break; - } - - if (w * h > MAX_SCREEN_SIZE) { - cgi_warn("Bad dimensions: %d x %d (total %d)", w, h, w * h); - redir_url += sprintf(redir_url, "term_width,term_height,"); - break; - } - - if (termconf->width != w || termconf->height != h) { - termconf->width = w; - termconf->height = h; - shall_clear_screen = true; - topics |= TOPIC_CHANGE_SCREEN_OPTS | TOPIC_CHANGE_CONTENT_ALL; - } - } while (0); - } - - if (GET_ARG("default_bg")) { - cgi_dbg("Screen default BG: %s", buff); - - if (buff[0] == '#') { - // decode hex - n = decodehex(buff+1); - n += 256; - } else { - n = atoi(buff); - } - - if (termconf->default_bg != n) { - termconf->default_bg = n; // this is current not sent through socket, no use to notify - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - } - - if (GET_ARG("default_fg")) { - cgi_dbg("Screen default FG: %s", buff); - - if (buff[0] == '#') { - // decode hex - n = decodehex(buff+1); - n += 256; - } else { - n = atoi(buff); - } - - if (termconf->default_fg != n) { - termconf->default_fg = n; // this is current not sent through socket, no use to notify - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - } - - if (GET_ARG("parser_tout_ms")) { - cgi_dbg("Parser timeout: %s ms", buff); - n = atoi(buff); - if (n >= 0) { - termconf->parser_tout_ms = n; - } else { - cgi_warn("Bad parser timeout %s", buff); - redir_url += sprintf(redir_url, "parser_tout_ms,"); - } - } - - if (GET_ARG("display_tout_ms")) { - cgi_dbg("Display update idle timeout: %s ms", buff); - n = atoi(buff); - if (n >= 0) { - termconf->display_tout_ms = n; - } else { - cgi_warn("Bad update timeout %s", buff); - redir_url += sprintf(redir_url, "display_tout_ms,"); - } - } - - if (GET_ARG("display_cooldown_ms")) { - cgi_dbg("Display update cooldown: %s ms", buff); - n = atoi(buff); - if (n > 0) { - termconf->display_cooldown_ms = n; - } else { - cgi_warn("Bad cooldown %s", buff); - redir_url += sprintf(redir_url, "display_cooldown_ms,"); - } - } - - if (GET_ARG("fn_alt_mode")) { - cgi_dbg("FN alt mode: %s", buff); - n = atoi(buff); - termconf->fn_alt_mode = (bool)n; - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - - if (GET_ARG("want_all_fn")) { - cgi_dbg("AllFN mode: %s", buff); - n = atoi(buff); - termconf->want_all_fn = (bool)n; - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - - if (GET_ARG("crlf_mode")) { - cgi_dbg("CRLF mode: %s", buff); - n = atoi(buff); - termconf->crlf_mode = (bool)n; - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - - if (GET_ARG("show_buttons")) { - cgi_dbg("Show buttons: %s", buff); - n = atoi(buff); - termconf->show_buttons = (bool)n; - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - - if (GET_ARG("show_config_links")) { - cgi_dbg("Show config links: %s", buff); - n = atoi(buff); - termconf->show_config_links = (bool)n; - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - - if (GET_ARG("loopback")) { - cgi_dbg("Loopback: %s", buff); - n = atoi(buff); - termconf->loopback = (bool)n; - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - - if (GET_ARG("debugbar")) { - cgi_dbg("Debugbar: %s", buff); - n = atoi(buff); - termconf->debugbar = (bool)n; - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - - if (GET_ARG("allow_decopt_12")) { - cgi_dbg("DECOPT 12: %s", buff); - n = atoi(buff); - termconf->allow_decopt_12 = (bool)n; - } - - if (GET_ARG("ascii_debug")) { - cgi_dbg("ascii_debug: %s", buff); - n = atoi(buff); - termconf->ascii_debug = (bool)n; - shall_clear_screen = true; - } - - if (GET_ARG("theme")) { - cgi_dbg("Screen color theme: %s", buff); - n = atoi(buff); - if (n >= 0) { - termconf->theme = (u8) n; - // this can't be notified, page must reload. - } else { - cgi_warn("Bad theme num: %s", buff); - redir_url += sprintf(redir_url, "theme,"); - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } - } - - if (GET_ARG("cursor_shape")) { - cgi_dbg("Cursor shape: %s", buff); - n = atoi(buff); - if (n >= 0 && n <= 6 && n != 1) { - termconf->cursor_shape = (enum CursorShape) n; - topics |= TOPIC_CHANGE_SCREEN_OPTS; - } else { - cgi_warn("Bad cursor_shape num: %s", buff); - redir_url += sprintf(redir_url, "cursor_shape,"); - } - } - - if (GET_ARG("term_title")) { - cgi_dbg("Terminal title default text: \"%s\"", buff); - strncpy_safe(termconf->title, buff, TERM_TITLE_LEN); // ATTN those must match the values in - topics |= TOPIC_CHANGE_TITLE; - } - - if (GET_ARG("backdrop")) { - cgi_dbg("Terminal backdrop url: \"%s\"", buff); - strncpy_safe(termconf->backdrop, buff, TERM_BACKDROP_LEN); // ATTN those must match the values in - topics |= TOPIC_CHANGE_BACKDROP; - } - - for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) { - sprintf(buff, "btn%d", btn_i); - if (GET_ARG(buff)) { - cgi_dbg("Button%d default text: \"%s\"", btn_i, buff); - strncpy_safe(termconf->btn[btn_i-1], buff, TERM_BTN_LEN); - topics |= TOPIC_CHANGE_BUTTONS; - } - - sprintf(buff, "bm%d", btn_i); - if (GET_ARG(buff)) { - cgi_dbg("Button%d message (ASCII): \"%s\"", btn_i, buff); - - // parse: comma,space or semicolon separated decimal values of ASCII codes - char c; - char *cp = buff; - int char_i = 0; - int acu = 0; - bool lastsp = 1; - char buff_bm[TERM_BTN_MSG_LEN]; - while ((c = *cp++) != 0) { - if (c == ' ' || c == ',' || c == ';') { - if(lastsp) continue; - - if (acu==0 || acu>255) { - cgi_warn("Bad value! %d", acu); - redir_url += sprintf(redir_url, "bm%d,", btn_i); - break; - } - - if (char_i >= TERM_BTN_MSG_LEN-1) { - cgi_warn("Too long! %d", acu); - redir_url += sprintf(redir_url, "bm%d,", btn_i); - break; - } - - cgi_dbg("acu %d", acu); - buff_bm[char_i++] = (char)acu; - - // prepare for next char - acu = 0; - lastsp = 1; - } else if (c>='0'&&c<='9') { - lastsp = 0; - acu *= 10; - acu += c - '0'; - } else { - cgi_warn("Bad syntax!"); - redir_url += sprintf(redir_url, "bm%d,", btn_i); - break; - } - } - if (lastsp && char_i == 0) { - cgi_warn("Required!"); - redir_url += sprintf(redir_url, "bm%d,", btn_i); - } - if (!lastsp) { - buff_bm[char_i++] = (char)acu; - } - buff_bm[char_i] = 0; - cgi_dbg("%s, chari = %d", buff_bm, char_i); - - strncpy(termconf->btn_msg[btn_i-1], buff_bm, TERM_BTN_MSG_LEN); - } - } - - if (GET_ARG("uart_baud")) { - cgi_dbg("Baud rate: %s", buff); - int baud = atoi(buff); - if (baud == BIT_RATE_300 || - baud == BIT_RATE_600 || - baud == BIT_RATE_1200 || - baud == BIT_RATE_2400 || - baud == BIT_RATE_4800 || - baud == BIT_RATE_9600 || - baud == BIT_RATE_19200 || - baud == BIT_RATE_38400 || - baud == BIT_RATE_57600 || - baud == BIT_RATE_74880 || - baud == BIT_RATE_115200 || - baud == BIT_RATE_230400 || - baud == BIT_RATE_460800 || - baud == BIT_RATE_921600 || - baud == BIT_RATE_1843200 || - baud == BIT_RATE_3686400) { - sysconf->uart_baudrate = (u32) baud; - shall_init_uart = true; - } else { - cgi_warn("Bad baud rate %s", buff); - redir_url += sprintf(redir_url, "uart_baud,"); - } - } +#define XSTRUCT termconf +#define X XSET_CGI_FUNC + XTABLE_TERMCONF +#undef X +#undef XSTRUCT - if (GET_ARG("uart_parity")) { - cgi_dbg("Parity: %s", buff); - int parity = atoi(buff); - if (parity >= 0 && parity <= 2) { - sysconf->uart_parity = (UartParityMode) parity; - shall_init_uart = true; - } else { - cgi_warn("Bad parity %s", buff); - redir_url += sprintf(redir_url, "uart_parity,"); - } - } - - if (GET_ARG("uart_stopbits")) { - cgi_dbg("Stop bits: %s", buff); - int stopbits = atoi(buff); - if (stopbits >= 1 && stopbits <= 3) { - sysconf->uart_stopbits = (UartStopBitsNum) stopbits; - shall_init_uart = true; - } else { - cgi_warn("Bad stopbits %s", buff); - redir_url += sprintf(redir_url, "uart_stopbits,"); - } - } + // width and height must always go together so we can do max size validation + u32 siz = termconf->width*termconf->height; + if (siz == 0 || siz > MAX_SCREEN_SIZE) { + cgi_warn("Bad dimensions: %d x %d (total %d)", termconf->width, termconf->height, termconf->width*termconf->height); + redir_url += sprintf(redir_url, "term_width,term_height,"); + } + + // Settings from SysConf - UART + bool uart_changed = false; + // restrict what keys are allowed +#define admin 0 +#define tpl 0 +#define XSTRUCT sysconf +#define X XSET_CGI_FUNC + XTABLE_SYSCONF +#undef X +#undef XSTRUCT (void)redir_url; @@ -390,17 +71,23 @@ cgiTermCfgSetParams(HttpdConnData *connData) persist_store(); + bool shall_clear_screen = false; + shall_clear_screen |= (termconf_backup->width != termconf->width); + shall_clear_screen |= (termconf_backup->height != termconf->height); + shall_clear_screen |= (termconf_backup->ascii_debug != termconf->ascii_debug); + if (shall_clear_screen) { terminal_apply_settings(); } else { terminal_apply_settings_noclear(); } - if (shall_init_uart) { - serialInit(); + if (uart_changed) { + sysconf_apply_settings(); } - if (topics) screen_notifyChange(topics); + // Trigger a full reload + screen_notifyChange(TOPIC_INITIAL); httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied."); } else { @@ -424,7 +111,6 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) { #define BUFLEN 100 // large enough for backdrop char buff[BUFLEN]; - char buff2[10]; if (token == NULL) { // We're done @@ -433,109 +119,11 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) strcpy(buff, ""); // fallback - if (streq(token, "term_width")) { - sprintf(buff, "%d", termconf->width); - } - else if (streq(token, "term_height")) { - sprintf(buff, "%d", termconf->height); - } - else if (streq(token, "parser_tout_ms")) { - sprintf(buff, "%d", termconf->parser_tout_ms); - } - else if (streq(token, "display_tout_ms")) { - sprintf(buff, "%d", termconf->display_tout_ms); - } - else if (streq(token, "display_cooldown_ms")) { - sprintf(buff, "%d", termconf->display_cooldown_ms); - } - else if (streq(token, "fn_alt_mode")) { - sprintf(buff, "%d", (int)termconf->fn_alt_mode); - } - else if (streq(token, "want_all_fn")) { - sprintf(buff, "%d", (int)termconf->want_all_fn); - } - else if (streq(token, "crlf_mode")) { - sprintf(buff, "%d", (int)termconf->crlf_mode); - } - else if (streq(token, "show_buttons")) { - sprintf(buff, "%d", (int)termconf->show_buttons); - } - else if (streq(token, "show_config_links")) { - sprintf(buff, "%d", (int)termconf->show_config_links); - } - else if (streq(token, "allow_decopt_12")) { - sprintf(buff, "%d", (int)termconf->allow_decopt_12); - } - else if (streq(token, "ascii_debug")) { - sprintf(buff, "%d", (int)termconf->ascii_debug); - } - else if (streq(token, "loopback")) { - sprintf(buff, "%d", (int)termconf->loopback); - } - else if (streq(token, "debugbar")) { - sprintf(buff, "%d", (int)termconf->debugbar); - } - else if (streq(token, "theme")) { - sprintf(buff, "%d", termconf->theme); - } - else if (streq(token, "default_bg")) { - if (termconf->default_bg < 256) { - sprintf(buff, "%d", termconf->default_bg); - } else { - sprintf(buff, "#%06X", termconf->default_bg - 256); - } - } - else if (streq(token, "default_fg")) { - if (termconf->default_fg < 256) { - sprintf(buff, "%d", termconf->default_fg); - } else { - sprintf(buff, "#%06X", termconf->default_fg - 256); - } - } - else if (streq(token, "cursor_shape")) { - sprintf(buff, "%d", termconf->cursor_shape); - } - else if (streq(token, "term_title")) { - strncpy_safe(buff, termconf->title, BUFLEN); - } - else if (streq(token, "backdrop")) { - strncpy_safe(buff, termconf->backdrop, BUFLEN); - } - else if (streq(token, "uart_baud")) { - sprintf(buff, "%d", sysconf->uart_baudrate); - } - else if (streq(token, "uart_parity")) { - sprintf(buff, "%d", sysconf->uart_parity); - } - else if (streq(token, "uart_stopbits")) { - sprintf(buff, "%d", sysconf->uart_stopbits); - } - else { - for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) { - sprintf(buff2, "btn%d", btn_i); - if (streq(token, buff2)) { - strncpy_safe(buff, termconf->btn[btn_i-1], BUFLEN); - break; - } - - sprintf(buff2, "bm%d", btn_i); - if (streq(token, buff2)) { - char c; - char *bp = buff; - char *cp = termconf->btn_msg[btn_i-1]; - int n = 0; - while((c = *cp++) != 0) { - if(n>0) { - *bp = ','; - bp++; - } - bp += sprintf(bp, "%d", (int)c); - n++; - } - break; - } - } - } +#define XSTRUCT termconf +#define X XGET_CGI_FUNC + XTABLE_TERMCONF +#undef X +#undef XSTRUCT tplSend(connData, buff, -1); return HTTPD_CGI_DONE; diff --git a/user/config_xmacros.c b/user/config_xmacros.c index d52f6cf..f47e329 100644 --- a/user/config_xmacros.c +++ b/user/config_xmacros.c @@ -80,6 +80,32 @@ xset_u8(const char *name, u8 *field, const char *buff, const void *arg) } } +enum xset_result ICACHE_FLASH_ATTR +xset_u32(const char *name, u32 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + u32 val = (u32) atoi(buff); + + if (*field != val) { + *field = (u32) val; + return XSET_SET; + } + return XSET_UNCHANGED; +} + +enum xset_result ICACHE_FLASH_ATTR +xset_u16(const char *name, u16 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + u16 val = (u16) atoi(buff); + + if (*field != val) { + *field = (u16) val; + return XSET_SET; + } + return XSET_UNCHANGED; +} + enum xset_result ICACHE_FLASH_ATTR xset_string(const char *name, s8 **field, const char *buff, const void *arg) { diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 53495b0..59cef6f 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -60,6 +60,8 @@ static inline enum xset_result xset_dummy(const char *name, void *field, const c enum xset_result xset_ip(const char *name, struct ip_addr *field, const char *buff, const void *arg); enum xset_result xset_bool(const char *name, bool *field, const char *buff, const void *arg); enum xset_result xset_u8(const char *name, u8 *field, const char *buff, const void *arg); +enum xset_result xset_u32(const char *name, u32 *field, const char *buff, const void *arg); +enum xset_result xset_u16(const char *name, u16 *field, const char *buff, const void *arg); /** * @param arg - max string length @@ -73,17 +75,17 @@ enum xset_result xset_ustring(const char *name, u8 **field, const char *buff, co * If 'name' is found in connData->getArgs, xset() is called. * If the result is SET, xnotify() is fired. Else, 'name,' is appended to the redir_url buffer. */ -#define XSET_CGI_FUNC(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ +#define XSET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ if ((allow) && GET_ARG(#name)) { \ enum xset_result res = xset(#name, cast &XSTRUCT->name, buff, (const void*) (xsarg)); \ - if (res == XSET_SET) { xnotify(); } \ + if (res == XSET_SET) { xnotify; } \ else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \ } -#define XGET_CGI_FUNC(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ +#define XGET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name); -#define XSTRUCT_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ +#define XSTRUCT_FIELD(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ type name suffix; #define XDUMP_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ diff --git a/user/screen.c b/user/screen.c index a675a71..9160d08 100644 --- a/user/screen.c +++ b/user/screen.c @@ -8,6 +8,7 @@ #include "character_sets.h" #include "utf8.h" #include "cgi_sockets.h" +#include "cgi_logging.h" TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle termconf_live; @@ -106,8 +107,19 @@ bool cursor_saved = false; static struct { bool alternate_active; char title[TERM_TITLE_LEN]; - char btn[TERM_BTN_COUNT][TERM_BTN_LEN]; - char btn_msg[TERM_BTN_COUNT][TERM_BTN_MSG_LEN]; + + char btn1[TERM_BTN_LEN]; + char btn2[TERM_BTN_LEN]; + char btn3[TERM_BTN_LEN]; + char btn4[TERM_BTN_LEN]; + char btn5[TERM_BTN_LEN]; + + char btn1_msg[TERM_BTN_MSG_LEN]; + char btn2_msg[TERM_BTN_MSG_LEN]; + char btn3_msg[TERM_BTN_MSG_LEN]; + char btn4_msg[TERM_BTN_MSG_LEN]; + char btn5_msg[TERM_BTN_MSG_LEN]; + u32 width; u32 height; int vm0; @@ -143,46 +155,194 @@ static struct { int x_min, y_min, x_max, y_max; } scr_dirty; -#define reset_screen_dirty() do { \ - scr_dirty.x_min = W; \ - scr_dirty.x_max = -1; \ - scr_dirty.y_min = H; \ - scr_dirty.y_max = -1; \ - } while(0) +static void ICACHE_FLASH_ATTR reset_screen_dirty(void) +{ + scr_dirty.x_min = W; + scr_dirty.x_max = -1; + scr_dirty.y_min = H; + scr_dirty.y_max = -1; +} -#define expand_dirty(y0, y1, x0, x1) do { \ - seri_dbg("Expand: X: (%d..%d) -> %d..%d, Y: (%d..%d) -> %d..%d", scr_dirty.x_min, scr_dirty.x_max, x0, x1, scr_dirty.y_min, scr_dirty.y_max, y0, y1); \ - if ((int)(y0) < scr_dirty.y_min) scr_dirty.y_min = (y0); \ - if ((int)(x0) < scr_dirty.x_min) scr_dirty.x_min = (x0); \ - if ((int)(y1) > scr_dirty.y_max) scr_dirty.y_max = (y1); \ - if ((int)(x1) > scr_dirty.x_max) scr_dirty.x_max = (x1); \ - } while(0) +static void ICACHE_FLASH_ATTR expand_dirty(int y0, int y1, int x0, int x1) +{ + seri_dbg("Expand: X: (%d..%d) -> %d..%d, Y: (%d..%d) -> %d..%d", + scr_dirty.x_min, scr_dirty.x_max, x0, x1, scr_dirty.y_min, scr_dirty.y_max, y0, y1); + + if ((y0) < scr_dirty.y_min) scr_dirty.y_min = (y0); + if ((x0) < scr_dirty.x_min) scr_dirty.x_min = (x0); + if ((y1) > scr_dirty.y_max) scr_dirty.y_max = (y1); + if ((x1) > scr_dirty.x_max) scr_dirty.x_max = (x1); +} + +#define NOTIFY_LOCK() { notifyLock++; } -#define NOTIFY_LOCK() do { \ - notifyLock++; \ - } while(0) - -#define NOTIFY_DONE(updateTopics) do { \ - lockTopics |= (updateTopics); \ - if (notifyLock > 0) notifyLock--; \ - if (notifyLock == 0) { \ - screen_notifyChange(lockTopics); \ - lockTopics = 0;\ - } \ - } while(0) +static void ICACHE_FLASH_ATTR NOTIFY_DONE(u32 updateTopics) +{ + lockTopics |= (updateTopics); + if (notifyLock > 0) notifyLock--; + if (notifyLock == 0) { + screen_notifyChange(lockTopics); + lockTopics = 0; + } +} /** Clear the hanging attribute if the cursor is no longer >= W */ -#define clear_invalid_hanging() do { \ - if (cursor.hanging && cursor.x != W-1) { \ - cursor.hanging = false; \ - screen_notifyChange(TOPIC_CHANGE_CURSOR); \ - } \ - } while(false) +static void ICACHE_FLASH_ATTR clear_invalid_hanging(void) +{ + if (cursor.hanging && cursor.x != W-1) { + cursor.hanging = false; + screen_notifyChange(TOPIC_CHANGE_CURSOR); + } +} #define cursor_inside_region() (cursor.y >= TOP && cursor.y <= BTM) //region --- Settings --- +/** Export color for config */ +void ICACHE_FLASH_ATTR +xget_term_color(char *buff, u32 value) +{ + if (value < 256) { + sprintf(buff, "%d", value); + } else { + sprintf(buff, "#%06X", value - 256); + } +} + +/** Export button message as stirng for config */ +void ICACHE_FLASH_ATTR +xget_term_bm(char *buff, char *value) +{ + u8 c; + char *bp = buff; + char *cp = value; + int n = 0; + while((c = (u8) *cp++) != 0) { + if(n>0) { + *bp = ','; + bp++; + } + bp += sprintf(bp, "%d", c); + n++; + } +} + +enum xset_result ICACHE_FLASH_ATTR +xset_term_bm(const char *name, s8 **field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + + // parse: comma,space or semicolon separated decimal values of ASCII codes + char c; + const char *cp = buff; + int char_i = 0; + int acu = 0; + bool lastsp = 1; + char buff_bm[TERM_BTN_MSG_LEN]; + while ((c = *cp++) != 0) { + if (c == ' ' || c == ',' || c == ';') { + if(lastsp) continue; + + if (acu==0 || acu>255) { + cgi_warn("Bad value! %d", acu); + return XSET_FAIL; + } + + if (char_i >= TERM_BTN_MSG_LEN-1) { + cgi_warn("Too long! %d", acu); + return XSET_FAIL; + } + + cgi_dbg("acu %d", acu); + buff_bm[char_i++] = (char)acu; + + // prepare for next char + acu = 0; + lastsp = 1; + } else if (c>='0'&&c<='9') { + lastsp = 0; + acu *= 10; + acu += c - '0'; + } else { + cgi_warn("Bad syntax!"); + return XSET_FAIL; + } + } + if (lastsp && char_i == 0) { + cgi_warn("Required!"); + return XSET_FAIL; + } + if (!lastsp) { + buff_bm[char_i++] = (char)acu; + } + buff_bm[char_i] = 0; + cgi_dbg("%s, chari = %d", buff_bm, char_i); + + if (!streq(*field, buff_bm)) { + strncpy((char*)*field, buff_bm, TERM_BTN_MSG_LEN); + return XSET_SET; + } + return XSET_UNCHANGED; +} + +/** convert hex number to int */ +static ICACHE_FLASH_ATTR u32 +decodehex(const char *buf) { + u32 n = 0; + char c; + while ((c = *buf++) != 0) { + if (c >= '0' && c <= '9') c -= '0'; + else if (c >= 'a' && c <= 'f') c -= 'a'-10; + else if (c >= 'A' && c <= 'F') c -= 'A'-10; + else c = 0; + n *= 16; + n += c; + } + return n; +} + +enum xset_result ICACHE_FLASH_ATTR +xset_term_color(const char *name, u32 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + u32 n; + + if (buff[0] == '#') { + // decode hex + n = decodehex(buff+1); + n += 256; + } else { + n = atoi(buff); + } + + if (*field != n) { + *field = n; + return XSET_SET; + } + + return XSET_UNCHANGED; +} + +enum xset_result ICACHE_FLASH_ATTR +xset_term_cursorshape(const char *name, u32 *field, const char *buff, const void *arg) +{ + cgi_dbg("Setting %s = %s", name, buff); + u32 n = atoi(buff); + + if (n >= 0 && n <= 6 && n != 1) { + if (*field != n) { + *field = n; + return XSET_SET; + } + return XSET_UNCHANGED; + } else { + cgi_warn("Bad cursor_shape num: %s", buff); + return XSET_FAIL; + } +} + + /** * Restore hard defaults */ @@ -194,10 +354,19 @@ terminal_restore_defaults(void) termconf->default_bg = 0; termconf->default_fg = 7; sprintf(termconf->title, SCR_DEF_TITLE); - for(int i=1; i <= TERM_BTN_COUNT; i++) { - sprintf(termconf->btn[i-1], "%d", i); - sprintf(termconf->btn_msg[i-1], "%c", i); - } + + strcpy(termconf->btn1, "1"); + strcpy(termconf->btn2, "2"); + strcpy(termconf->btn3, "3"); + strcpy(termconf->btn4, "4"); + strcpy(termconf->btn5, "5"); + + strcpy(termconf->bm1, "\x01"); + strcpy(termconf->bm2, "\x02"); + strcpy(termconf->bm3, "\x03"); + strcpy(termconf->bm4, "\x04"); + strcpy(termconf->bm5, "\x05"); + termconf->theme = 0; termconf->parser_tout_ms = SCR_DEF_PARSER_TOUT_MS; termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; @@ -232,6 +401,13 @@ terminal_apply_settings_noclear(void) { bool changed = false; +// char buff[64]; +//#define XSTRUCT termconf +//#define X XDUMP_FIELD +// XTABLE_TERMCONF +//#undef X +// return; + // Migrate if (termconf->config_version < 1) { persist_dbg("termconf: Updating to version %d", 1); @@ -393,10 +569,11 @@ screen_reset_do(bool size, bool labels) strcpy(termconf_live.title, termconf->title); strcpy(termconf_live.backdrop, termconf->backdrop); - for (int i = 1; i <= TERM_BTN_COUNT; i++) { - strcpy(termconf_live.btn[i], termconf->btn[i]); - strcpy(termconf_live.btn_msg[i], termconf->btn_msg[i]); - } + strcpy(termconf_live.btn1, termconf->btn1); + strcpy(termconf_live.btn2, termconf->btn2); + strcpy(termconf_live.btn3, termconf->btn3); + strcpy(termconf_live.btn4, termconf->btn4); + strcpy(termconf_live.btn5, termconf->btn5); termconf_live.show_buttons = termconf->show_buttons; termconf_live.show_config_links = termconf->show_config_links; @@ -452,8 +629,11 @@ screen_swap_state(bool alternate) ansi_dbg("Swap to alternate"); // store old state memcpy(state_backup.title, termconf_live.title, TERM_TITLE_LEN); - memcpy(state_backup.btn, termconf_live.btn, sizeof(termconf_live.btn)); - memcpy(state_backup.btn_msg, termconf_live.btn_msg, sizeof(termconf_live.btn_msg)); + + // copy multiple fields at once + memcpy(state_backup.btn1, termconf_live.btn1, sizeof(termconf_live.btn1)*TERM_BTN_COUNT); + memcpy(state_backup.btn1_msg, termconf_live.bm1, sizeof(termconf_live.bm1)*TERM_BTN_COUNT); + memcpy(state_backup.tab_stops, scr.tab_stops, sizeof(scr.tab_stops)); state_backup.vm0 = scr.vm0; state_backup.vm1 = scr.vm1; @@ -465,8 +645,11 @@ screen_swap_state(bool alternate) else { ansi_dbg("Unswap from alternate"); memcpy(termconf_live.title, state_backup.title, TERM_TITLE_LEN); - memcpy(termconf_live.btn, state_backup.btn, sizeof(termconf_live.btn)); - memcpy(termconf_live.btn_msg, state_backup.btn_msg, sizeof(termconf_live.btn_msg)); + + // copy multiple fields at once + memcpy(termconf_live.btn1, state_backup.btn1, sizeof(termconf_live.btn1)*TERM_BTN_COUNT); + memcpy(termconf_live.bm1, state_backup.btn1_msg, sizeof(termconf_live.bm1)*TERM_BTN_COUNT); + memcpy(scr.tab_stops, state_backup.tab_stops, sizeof(scr.tab_stops)); scr.vm0 = state_backup.vm0; scr.vm1 = state_backup.vm1; @@ -989,8 +1172,8 @@ screen_resize(int rows, int cols) if (W == cols && H == rows) return; // Do nothing NOTIFY_LOCK(); - W = cols; - H = rows; + W = (u32) cols; + H = (u32) rows; screen_reset_on_resize(); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS|TOPIC_CHANGE_CONTENT_ALL|TOPIC_CHANGE_CURSOR); } @@ -1012,7 +1195,28 @@ void ICACHE_FLASH_ATTR screen_set_button_text(int num, const char *text) { NOTIFY_LOCK(); - strncpy(termconf_live.btn[num-1], text, TERM_BTN_LEN); + if (num == 1) strncpy(termconf_live.btn1, text, TERM_BTN_LEN); + else if (num == 2) strncpy(termconf_live.btn2, text, TERM_BTN_LEN); + else if (num == 3) strncpy(termconf_live.btn3, text, TERM_BTN_LEN); + else if (num == 4) strncpy(termconf_live.btn4, text, TERM_BTN_LEN); + else if (num == 5) strncpy(termconf_live.btn5, text, TERM_BTN_LEN); + NOTIFY_DONE(TOPIC_CHANGE_BUTTONS); +} + +/** + * Helper function to set terminal button label + * @param num - button number 1-5 + * @param str - button text + */ +void ICACHE_FLASH_ATTR +screen_set_button_message(int num, const char *msg) +{ + NOTIFY_LOCK(); + if (num == 1) strncpy(termconf_live.bm1, msg, TERM_BTN_MSG_LEN); + else if (num == 2) strncpy(termconf_live.bm2, msg, TERM_BTN_MSG_LEN); + else if (num == 3) strncpy(termconf_live.bm3, msg, TERM_BTN_MSG_LEN); + else if (num == 4) strncpy(termconf_live.bm4, msg, TERM_BTN_MSG_LEN); + else if (num == 5) strncpy(termconf_live.bm5, msg, TERM_BTN_MSG_LEN); NOTIFY_DONE(TOPIC_CHANGE_BUTTONS); } @@ -1121,7 +1325,7 @@ void ICACHE_FLASH_ATTR screen_cursor_shape(enum CursorShape shape) { NOTIFY_LOCK(); - if (shape == CURSOR_DEFAULT) shape = termconf->cursor_shape; + if (shape == CURSOR_DEFAULT) shape = (enum CursorShape) termconf->cursor_shape; termconf_live.cursor_shape = shape; NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS); } @@ -1356,7 +1560,7 @@ screen_back_index(int count) cursor.x = new_x; } else { cursor.x = 0; - screen_insert_characters(-new_x); + screen_insert_characters((unsigned int) -new_x); topics |= TOPIC_CHANGE_CONTENT_PART; } NOTIFY_DONE(topics); @@ -1548,7 +1752,7 @@ do_save_private_opt(int n, bool save) { ScreenNotifyTopics topics = TOPIC_INTERNAL; if (!save) NOTIFY_LOCK(); -#define SAVE_RESTORE(sf, of) do { if (save) sf=(of); else of=(sf); } while(0) +#define SAVE_RESTORE(sf, of) do { if (save) (sf)=(of); else (of)=(sf); } while(0) switch (n) { case 1: SAVE_RESTORE(opt_backup.cursors_alt_mode, scr.cursors_alt_mode); @@ -2044,7 +2248,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, BEGIN_TOPIC(TOPIC_CHANGE_TITLE, TERM_TITLE_LEN+4+1) bufput_c(TOPICMARK_TITLE); - int len = (int) strlen(termconf_live.title); + size_t len = strlen(termconf_live.title); if (len > 0) { memcpy(bb, termconf_live.title, len); bb += len; @@ -2059,20 +2263,21 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, bufput_utf8(TERM_BTN_COUNT); for (int i = 0; i < TERM_BTN_COUNT; i++) { - int len = (int) strlen(termconf_live.btn[i]); + size_t len = strlen(TERM_BTN_N(&termconf_live, i)); if (len > 0) { - memcpy(bb, termconf_live.btn[i], len); + memcpy(bb, TERM_BTN_N(&termconf_live, i), len); bb += len; remain -= len; } bufput_c('\x01'); } + END_TOPIC BEGIN_TOPIC(TOPIC_CHANGE_BACKDROP, TERM_BACKDROP_LEN+1+1) bufput_c(TOPICMARK_BACKDROP); - int len = (int) strlen(termconf_live.backdrop); + size_t len = strlen(termconf_live.backdrop); if (len > 0) { memcpy(bb, termconf_live.backdrop, len); bb += len; diff --git a/user/screen.h b/user/screen.h index 50c1fb5..d52f89b 100644 --- a/user/screen.h +++ b/user/screen.h @@ -5,6 +5,7 @@ #include #include #include +#include "config_xmacros.h" /** * This module handles the virtual screen and operations on it. @@ -78,30 +79,59 @@ enum CursorShape { #define TERMCONF_SIZE 400 #define TERMCONF_VERSION 4 +//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY................Allow +// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) +#define XTABLE_TERMCONF \ + X(u32, width, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ + X(u32, height, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ + X(u32, default_bg, /**/, /**/, xget_term_color, /**/, xset_term_color, NULL, /**/, 1) \ + X(u32, default_fg, /**/, /**/, xget_term_color, /**/, xset_term_color, NULL, /**/, 1) \ + X(char, title, [TERM_TITLE_LEN], /**/, xget_string, (s8**), xset_string, TERM_TITLE_LEN, /**/, 1) \ + X(char, btn1, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn2, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn3, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn4, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn5, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(u8, theme, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ + X(u32, parser_tout_ms, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ + X(u32, display_tout_ms, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ + X(bool, fn_alt_mode, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ + X(u32, display_cooldown_ms, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ + X(bool, loopback, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(bool, show_buttons, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(bool, show_config_links, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(char, bm1, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(char, bm2, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(char, bm3, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(char, bm4, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(char, bm5, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(u32, cursor_shape, /**/, /**/, xget_dec, /**/, xset_term_cursorshape, NULL, /**/, 1) \ + X(bool, crlf_mode, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(bool, want_all_fn, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(bool, debugbar, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(bool, allow_decopt_12, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(bool, ascii_debug, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(char, backdrop, [TERM_BACKDROP_LEN], /**/, xget_string, (s8**), xset_string, TERM_BACKDROP_LEN, /**/, 1) + +#define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*n)) +#define TERM_BTN_N(tc, n) ((tc)->btn1+(TERM_BTN_LEN*n)) + +/** Export color for config */ +void xget_term_color(char *buff, u32 value); +/** Export button message as stirng for config */ +void xget_term_bm(char *buff, char *value); +/** Set button message */ +enum xset_result xset_term_bm(const char *name, s8 **field, const char *buff, const void *arg); +/** Set color */ +enum xset_result xset_term_color(const char *name, u32 *field, const char *buff, const void *arg); +/** Set cursor shape */ +enum xset_result xset_term_cursorshape(const char *name, u32 *field, const char *buff, const void *arg); + typedef struct { - u32 width; - u32 height; - u32 default_bg; // 00-FFh - ANSI colors, (00:00:00-FF:FF:FF)+256 - True Color - u32 default_fg; - char title[TERM_TITLE_LEN]; - char btn[TERM_BTN_COUNT][TERM_BTN_LEN]; - u8 theme; - u32 parser_tout_ms; - u32 display_tout_ms; - bool fn_alt_mode; // xterm compatibility mode (alternate codes for some FN keys) - u8 config_version; - u32 display_cooldown_ms; - bool loopback; - bool show_buttons; - bool show_config_links; - char btn_msg[TERM_BTN_COUNT][TERM_BTN_MSG_LEN]; - enum CursorShape cursor_shape; - bool crlf_mode; - bool want_all_fn; - bool debugbar; - bool allow_decopt_12; - bool ascii_debug; - char backdrop[TERM_BACKDROP_LEN]; +#define X XSTRUCT_FIELD + XTABLE_TERMCONF +#undef X } TerminalConfigBundle; // Live config @@ -150,6 +180,7 @@ void screen_resize(int rows, int cols); void screen_set_title(const char *title); /** Set a button text */ void screen_set_button_text(int num, const char *text); +void screen_set_button_message(int num, const char *msg); /** Change backdrop */ void screen_set_backdrop(const char *url); diff --git a/user/syscfg.h b/user/syscfg.h index 70ea94b..9d7300a 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -25,21 +25,20 @@ enum pwlock { PWLOCK_MAX = 5, }; - -//....Type................Name..Suffix...............Deref..XGET........Allow..Cast..XSET.........................NOTIFY +//....Type................Name..Suffix...............Deref..XGET..........Cast..XSET.........................NOTIFY....Allow // Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) #define XTABLE_SYSCONF \ - X(u32, uart_baudrate, /**/, /**/, xget_dec, 1, /**/, xset_sys_baudrate, NULL, xnoop) \ - X(u8, uart_parity, /**/, /**/, xget_dec, 1, /**/, xset_sys_parity, NULL, xnoop) \ - X(u8, uart_stopbits, /**/, /**/, xget_dec, 1, /**/, xset_sys_stopbits, NULL, xnoop) \ + X(u32, uart_baudrate, /**/, /**/, xget_dec, /**/, xset_sys_baudrate, NULL, uart_changed=true, 1) \ + X(u8, uart_parity, /**/, /**/, xget_dec, /**/, xset_sys_parity, NULL, uart_changed=true, 1) \ + X(u8, uart_stopbits, /**/, /**/, xget_dec, /**/, xset_sys_stopbits, NULL, uart_changed=true, 1) \ \ - X(u8, config_version, /**/, /**/, xget_dec, 0, /**/, xset_u8, NULL, xnoop) \ + X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ \ - X(u8, pwlock, /**/, /**/, xget_dec, admin|tpl, /**/, xset_sys_pwlock, NULL, xnoop) \ - X(u8, access_pw, [64], /**/, xget_ustring, admin, (u8**), xset_sys_accesspw, NULL, xnoop) \ - X(u8, access_name, [32], /**/, xget_ustring, admin|tpl, (u8**), xset_ustring, NULL, xnoop) \ + X(u8, pwlock, /**/, /**/, xget_dec, /**/, xset_sys_pwlock, NULL, /**/, admin|tpl) \ + X(u8, access_pw, [64], /**/, xget_ustring, (u8**), xset_sys_accesspw, NULL, /**/, admin) \ + X(u8, access_name, [32], /**/, xget_ustring, (u8**), xset_ustring, NULL, /**/, admin|tpl) \ \ - X(bool, overclock, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, xnoop) \ + X(bool, overclock, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ typedef struct { diff --git a/user/wifimgr.h b/user/wifimgr.h index 5e0952f..06d145e 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -23,37 +23,37 @@ #define wifimgr_notify_ap() { wifi_change_flags.ap = true; } #define wifimgr_notify_sta() { wifi_change_flags.ap = true; } -//....Type................Name..Suffix...............Deref..XGET.......Allow..Cast..XSET.........................NOTIFY +//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY................Allow // Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) #define XTABLE_WIFI \ - X(u8, opmode, /**/, /**/, xget_dec, 1, /**/, xset_wifi_opmode, NULL, xnoop) \ + X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, /**/, 1) \ \ - X(u8, tpw, /**/, /**/, xget_dec, 1, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap) \ - X(u8, ap_channel, /**/, /**/, xget_dec, 1, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap) \ - X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap) \ - X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap) \ - X(bool, ap_hidden, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, wifimgr_notify_ap) \ + X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap(), 1) \ + X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap(), 1) \ + X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \ + X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap(), 1) \ + X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap(), 1) \ \ - X(u16, ap_dhcp_time, /**/, /**/, xget_dec, 1, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap) \ - X(u32, unused1, /**/, /**/, xget_dummy, 0, /**/, xset_dummy, NULL, xnoop) \ - X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap(), 1) \ + X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, /**/, 0) \ + X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap(), 1) \ + X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap(), 1) \ \ - X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ - X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_ap) \ + X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap(), 1) \ + X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap(), 1) \ \ \ - X(u32, unused2, /**/, /**/, xget_dummy, 0, /**/, xset_dummy, NULL, xnoop) \ - X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta) \ - X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, 1, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta) \ - X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, 1, /**/, xset_bool, NULL, wifimgr_notify_sta) \ + X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, /**/, 0) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \ + X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta(), 1) \ + X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta(), 1) \ \ - X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ - X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, 1, /**/, xset_ip, NULL, wifimgr_notify_sta) \ + X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta(), 1) \ + X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta(), 1) \ + X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta(), 1) \ \ \ - X(u8, config_version, /**/, /**/, xget_dec, 0, /**/, xset_u8, NULL, xnoop) + X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) // unused1 - replaces 'enabled' bit from old dhcps_lease struct // unused2 - gap after 'ap_gw' which isn't used and doesn't make sense From 97004659279aaa36ea463cd37ca5f4522705c193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 22:23:09 +0200 Subject: [PATCH 07/10] fixed many bugs --- front-end | 2 +- user/cgi_term_cfg.c | 7 +++++++ user/config_xmacros.c | 6 +++--- user/config_xmacros.h | 14 ++++++++------ user/screen.c | 22 +++++++++++----------- user/screen.h | 26 +++++++++++++------------- user/syscfg.c | 2 +- user/syscfg.h | 6 +++--- user/wifimgr.c | 12 ++++++------ user/wifimgr.h | 12 ++++++------ 10 files changed, 59 insertions(+), 50 deletions(-) diff --git a/front-end b/front-end index 560f578..8327ff0 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 560f5783bc0eb5bf845a55cc30a3928f1d01b85f +Subproject commit 8327ff010893935c0c517c729727464f66badd89 diff --git a/user/cgi_term_cfg.c b/user/cgi_term_cfg.c index 9972981..df5be73 100644 --- a/user/cgi_term_cfg.c +++ b/user/cgi_term_cfg.c @@ -123,6 +123,13 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg) #define X XGET_CGI_FUNC XTABLE_TERMCONF #undef X +#undef XSTRUCT + + // for uart +#define XSTRUCT sysconf +#define X XGET_CGI_FUNC + XTABLE_SYSCONF +#undef X #undef XSTRUCT tplSend(connData, buff, -1); diff --git a/user/config_xmacros.c b/user/config_xmacros.c index f47e329..ccac330 100644 --- a/user/config_xmacros.c +++ b/user/config_xmacros.c @@ -107,7 +107,7 @@ xset_u16(const char *name, u16 *field, const char *buff, const void *arg) } enum xset_result ICACHE_FLASH_ATTR -xset_string(const char *name, s8 **field, const char *buff, const void *arg) +xset_string(const char *name, char *field, const char *buff, const void *arg) { cgi_dbg("Setting %s = %s", name, buff); u32 maxlen = (u32) arg; @@ -126,7 +126,7 @@ xset_string(const char *name, s8 **field, const char *buff, const void *arg) enum xset_result ICACHE_FLASH_ATTR -xset_ustring(const char *name, u8 **field, const char *buff, const void *arg) +xset_ustring(const char *name, uchar *field, const char *buff, const void *arg) { cgi_dbg("Setting %s = %s", name, buff); u32 maxlen = (u32) arg; @@ -136,7 +136,7 @@ xset_ustring(const char *name, u8 **field, const char *buff, const void *arg) return XSET_FAIL; } - if (!streq((char *)field, buff)) { + if (!streq(field, buff)) { strncpy_safe(field, buff, (u32)arg); return XSET_SET; } diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 59cef6f..6fa3859 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -8,6 +8,8 @@ #include #include +typedef unsigned char uchar; + #define XJOIN(a, b) a##b /**Do nothing xnotify */ @@ -63,11 +65,10 @@ enum xset_result xset_u8(const char *name, u8 *field, const char *buff, const vo enum xset_result xset_u32(const char *name, u32 *field, const char *buff, const void *arg); enum xset_result xset_u16(const char *name, u16 *field, const char *buff, const void *arg); -/** - * @param arg - max string length - */ -enum xset_result xset_string(const char *name, s8 **field, const char *buff, const void *arg); -enum xset_result xset_ustring(const char *name, u8 **field, const char *buff, const void *arg); +// static string arrays are not &'d, so we don't get ** +/** @param arg - max string length */ +enum xset_result xset_string(const char *name, char *field, const char *buff, const void *arg); +enum xset_result xset_ustring(const char *name, u8 *field, const char *buff, const void *arg); /** * Helper template macro for CGI functions that load GET args to structs using XTABLE @@ -77,7 +78,8 @@ enum xset_result xset_ustring(const char *name, u8 **field, const char *buff, co */ #define XSET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ if ((allow) && GET_ARG(#name)) { \ - enum xset_result res = xset(#name, cast &XSTRUCT->name, buff, (const void*) (xsarg)); \ + type *_p = (type *) &XSTRUCT->name; \ + enum xset_result res = xset(#name, cast _p, buff, (const void*) (xsarg)); \ if (res == XSET_SET) { xnotify; } \ else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \ } diff --git a/user/screen.c b/user/screen.c index 9160d08..7a9f54f 100644 --- a/user/screen.c +++ b/user/screen.c @@ -214,22 +214,22 @@ xget_term_color(char *buff, u32 value) void ICACHE_FLASH_ATTR xget_term_bm(char *buff, char *value) { - u8 c; + char c; char *bp = buff; char *cp = value; int n = 0; - while((c = (u8) *cp++) != 0) { + while((c = *cp++) != 0) { if(n>0) { *bp = ','; bp++; } - bp += sprintf(bp, "%d", c); + bp += sprintf(bp, "%d", (u8)c); n++; } } enum xset_result ICACHE_FLASH_ATTR -xset_term_bm(const char *name, s8 **field, const char *buff, const void *arg) +xset_term_bm(const char *name, char *field, const char *buff, const void *arg) { cgi_dbg("Setting %s = %s", name, buff); @@ -279,8 +279,8 @@ xset_term_bm(const char *name, s8 **field, const char *buff, const void *arg) buff_bm[char_i] = 0; cgi_dbg("%s, chari = %d", buff_bm, char_i); - if (!streq(*field, buff_bm)) { - strncpy((char*)*field, buff_bm, TERM_BTN_MSG_LEN); + if (!streq(field, buff_bm)) { + strncpy(field, buff_bm, TERM_BTN_MSG_LEN); return XSET_SET; } return XSET_UNCHANGED; @@ -361,11 +361,11 @@ terminal_restore_defaults(void) strcpy(termconf->btn4, "4"); strcpy(termconf->btn5, "5"); - strcpy(termconf->bm1, "\x01"); - strcpy(termconf->bm2, "\x02"); - strcpy(termconf->bm3, "\x03"); - strcpy(termconf->bm4, "\x04"); - strcpy(termconf->bm5, "\x05"); + strcpy((char*)termconf->bm1, "\x01"); + strcpy((char*)termconf->bm2, "\x02"); + strcpy((char*)termconf->bm3, "\x03"); + strcpy((char*)termconf->bm4, "\x04"); + strcpy((char*)termconf->bm5, "\x05"); termconf->theme = 0; termconf->parser_tout_ms = SCR_DEF_PARSER_TOUT_MS; diff --git a/user/screen.h b/user/screen.h index d52f89b..c9bcc00 100644 --- a/user/screen.h +++ b/user/screen.h @@ -86,12 +86,12 @@ enum CursorShape { X(u32, height, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ X(u32, default_bg, /**/, /**/, xget_term_color, /**/, xset_term_color, NULL, /**/, 1) \ X(u32, default_fg, /**/, /**/, xget_term_color, /**/, xset_term_color, NULL, /**/, 1) \ - X(char, title, [TERM_TITLE_LEN], /**/, xget_string, (s8**), xset_string, TERM_TITLE_LEN, /**/, 1) \ - X(char, btn1, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn2, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn3, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn4, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn5, [TERM_BTN_LEN], /**/, xget_string, (s8**), xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, title, [TERM_TITLE_LEN], /**/, xget_string, /**/, xset_string, TERM_TITLE_LEN, /**/, 1) \ + X(char, btn1, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn2, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn3, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn4, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn5, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ X(u8, theme, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ X(u32, parser_tout_ms, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ X(u32, display_tout_ms, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ @@ -101,18 +101,18 @@ enum CursorShape { X(bool, loopback, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ X(bool, show_buttons, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ X(bool, show_config_links, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(char, bm1, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ - X(char, bm2, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ - X(char, bm3, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ - X(char, bm4, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ - X(char, bm5, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, (s8**), xset_term_bm, NULL, /**/, 1) \ + X(char, bm1, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ + X(char, bm2, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ + X(char, bm3, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ + X(char, bm4, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ + X(char, bm5, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ X(u32, cursor_shape, /**/, /**/, xget_dec, /**/, xset_term_cursorshape, NULL, /**/, 1) \ X(bool, crlf_mode, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ X(bool, want_all_fn, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ X(bool, debugbar, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ X(bool, allow_decopt_12, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ X(bool, ascii_debug, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(char, backdrop, [TERM_BACKDROP_LEN], /**/, xget_string, (s8**), xset_string, TERM_BACKDROP_LEN, /**/, 1) + X(char, backdrop, [TERM_BACKDROP_LEN], /**/, xget_string, /**/, xset_string, TERM_BACKDROP_LEN, /**/, 1) #define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*n)) #define TERM_BTN_N(tc, n) ((tc)->btn1+(TERM_BTN_LEN*n)) @@ -122,7 +122,7 @@ void xget_term_color(char *buff, u32 value); /** Export button message as stirng for config */ void xget_term_bm(char *buff, char *value); /** Set button message */ -enum xset_result xset_term_bm(const char *name, s8 **field, const char *buff, const void *arg); +enum xset_result xset_term_bm(const char *name, char *field, const char *buff, const void *arg); /** Set color */ enum xset_result xset_term_color(const char *name, u32 *field, const char *buff, const void *arg); /** Set cursor shape */ diff --git a/user/syscfg.c b/user/syscfg.c index 9a2a6f2..fc5f17c 100644 --- a/user/syscfg.c +++ b/user/syscfg.c @@ -95,7 +95,7 @@ xset_sys_pwlock(const char *name, u8 *field, const char *buff, const void *arg) } enum xset_result ICACHE_FLASH_ATTR -xset_sys_accesspw(const char *name, u8 **field, const char *buff, const void *arg) +xset_sys_accesspw(const char *name, uchar *field, const char *buff, const void *arg) { // Do not overwrite pw if empty if (strlen(buff) == 0) return XSET_UNCHANGED; diff --git a/user/syscfg.h b/user/syscfg.h index 9d7300a..0dacd65 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -35,8 +35,8 @@ enum pwlock { X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ \ X(u8, pwlock, /**/, /**/, xget_dec, /**/, xset_sys_pwlock, NULL, /**/, admin|tpl) \ - X(u8, access_pw, [64], /**/, xget_ustring, (u8**), xset_sys_accesspw, NULL, /**/, admin) \ - X(u8, access_name, [32], /**/, xget_ustring, (u8**), xset_ustring, NULL, /**/, admin|tpl) \ + X(uchar, access_pw, [64], /**/, xget_ustring, /**/, xset_sys_accesspw, NULL, /**/, admin) \ + X(uchar, access_name, [32], /**/, xget_ustring, /**/, xset_ustring, NULL, /**/, admin|tpl) \ \ X(bool, overclock, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ @@ -66,6 +66,6 @@ enum xset_result xset_sys_baudrate(const char *name, u32 *field, const char *buf enum xset_result xset_sys_parity(const char *name, u8 *field, const char *buff, const void *arg); enum xset_result xset_sys_stopbits(const char *name, u8 *field, const char *buff, const void *arg); enum xset_result xset_sys_pwlock(const char *name, u8 *field, const char *buff, const void *arg); -enum xset_result xset_sys_accesspw(const char *name, u8 **field, const char *buff, const void *arg); +enum xset_result xset_sys_accesspw(const char *name, uchar *field, const char *buff, const void *arg); #endif //ESP_VT100_FIRMWARE_SYSCFG_H diff --git a/user/wifimgr.c b/user/wifimgr.c index b0376c9..efb1cd2 100644 --- a/user/wifimgr.c +++ b/user/wifimgr.c @@ -79,7 +79,7 @@ xset_wifi_ap_channel(const char *name, u8 *field, const char *buff, const void * } enum xset_result ICACHE_FLASH_ATTR -xset_wifi_ssid(const char *name, u8 **field, const char *buff, const void *arg) +xset_wifi_ssid(const char *name, uchar *field, const char *buff, const void *arg) { u8 buff2[SSID_LEN]; @@ -96,8 +96,8 @@ xset_wifi_ssid(const char *name, u8 **field, const char *buff, const void *arg) cgi_dbg("Setting %s = %s", name, buff); if (strlen((char *)buff2) > 0) { - if (!streq(*field, buff2)) { - strncpy_safe(*field, buff2, SSID_LEN); + if (!streq(field, buff2)) { + strncpy_safe(field, buff2, SSID_LEN); return XSET_SET; } return XSET_UNCHANGED; @@ -109,12 +109,12 @@ xset_wifi_ssid(const char *name, u8 **field, const char *buff, const void *arg) /** Set PW - allow len 0 or 8-64 */ enum xset_result ICACHE_FLASH_ATTR -xset_wifi_pwd(const char *name, u8 **field, const char *buff, const void *arg) +xset_wifi_pwd(const char *name, uchar *field, const char *buff, const void *arg) { cgi_dbg("Setting %s = %s", name, buff); if (strlen(buff) == 0 || (strlen(buff) >= 8 && strlen(buff) < PASSWORD_LEN-1)) { - if (!streq(*field, buff)) { - strncpy_safe(*field, buff, PASSWORD_LEN); + if (!streq(field, buff)) { + strncpy_safe(field, buff, PASSWORD_LEN); return XSET_SET; } return XSET_UNCHANGED; diff --git a/user/wifimgr.h b/user/wifimgr.h index 06d145e..85b2b77 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -30,8 +30,8 @@ \ X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap(), 1) \ X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap(), 1) \ - X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \ - X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_ap(), 1) \ + X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, /**/, xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \ + X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, /**/, xset_wifi_pwd, NULL, wifimgr_notify_ap(), 1) \ X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap(), 1) \ \ X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap(), 1) \ @@ -44,8 +44,8 @@ \ \ X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, /**/, 0) \ - X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, (u8**), xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \ - X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, (u8**), xset_wifi_pwd, NULL, wifimgr_notify_sta(), 1) \ + X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, /**/, xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \ + X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, /**/, xset_wifi_pwd, NULL, wifimgr_notify_sta(), 1) \ X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta(), 1) \ \ X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta(), 1) \ @@ -89,8 +89,8 @@ enum xset_result xset_wifi_lease_time(const char *name, u16 *field, const char * enum xset_result xset_wifi_opmode(const char *name, u8 *field, const char *buff, const void *arg); enum xset_result xset_wifi_tpw(const char *name, u8 *field, const char *buff, const void *arg); enum xset_result xset_wifi_ap_channel(const char *name, u8 *field, const char *buff, const void *arg); -enum xset_result xset_wifi_ssid(const char *name, u8 **field, const char *buff, const void *arg); -enum xset_result xset_wifi_pwd(const char *name, u8 **field, const char *buff, const void *arg); +enum xset_result xset_wifi_ssid(const char *name, uchar *field, const char *buff, const void *arg); +enum xset_result xset_wifi_pwd(const char *name, uchar *field, const char *buff, const void *arg); #if DEBUG_WIFI #define wifi_warn warn From 4e285694d113a30f1ec9e7741f230ce108d8dea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 22 Oct 2017 23:41:07 +0200 Subject: [PATCH 08/10] ini export! --- user/cgi_network.c | 4 +- user/cgi_persist.c | 133 ++++++++++++++++++++++++++++++++++++++++++ user/cgi_persist.h | 1 + user/cgi_wifi.c | 4 +- user/config_xmacros.h | 10 ++-- user/routes.c | 1 + user/screen.h | 62 ++++++++++---------- user/syscfg.h | 16 ++--- user/wifimgr.h | 46 +++++++-------- 9 files changed, 206 insertions(+), 71 deletions(-) diff --git a/user/cgi_network.c b/user/cgi_network.c index e417dd7..a3fe1a8 100644 --- a/user/cgi_network.c +++ b/user/cgi_network.c @@ -51,7 +51,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData) #define XSTRUCT wificonf #define X XSET_CGI_FUNC - XTABLE_WIFI + XTABLE_WIFICONF #undef X (void) redir_url; @@ -101,7 +101,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke #define XSTRUCT wificonf #define X XGET_CGI_FUNC - XTABLE_WIFI + XTABLE_WIFICONF #undef X // non-config diff --git a/user/cgi_persist.c b/user/cgi_persist.c index 82a9d04..4f5e56f 100644 --- a/user/cgi_persist.c +++ b/user/cgi_persist.c @@ -7,6 +7,9 @@ Cgi/template routines for configuring non-wifi settings #include "persist.h" #include "helpers.h" #include "cgi_logging.h" +#include "version.h" +#include "screen.h" +#include "config_xmacros.h" #define SET_REDIR_SUC "/cfg/system" @@ -74,3 +77,133 @@ cgiPersistRestoreHard(HttpdConnData *connData) httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20factory%20defaults."); return HTTPD_CGI_DONE; } + + + +#define httpdSend_orDie(conn, data, len) do { if (!httpdSend((conn), (data), (len))) return false; } while (0) + +/* encode for double-quoted string */ +int ICACHE_FLASH_ATTR httpdSend_dblquot(HttpdConnData *conn, const char *data, int len) +{ + int start = 0, end = 0; + char c; + if (conn->conn==NULL) return 0; + if (len < 0) len = (int) strlen(data); + if (len==0) return 0; + + for (end = 0; end < len; end++) { + c = data[end]; + if (c == 0) { + // we found EOS + break; + } + + if (c == '"' || c == '\'' || c == '\\' || c == '\n' || c == '\r' || c == '\x1b') { + if (start < end) httpdSend_orDie(conn, data + start, end - start); + start = end + 1; + } + + if (c == '"') httpdSend_orDie(conn, "\\\"", 2); + else if (c == '\'') httpdSend_orDie(conn, "\\'", 2); + else if (c == '\\') httpdSend_orDie(conn, "\\\\", 2); + else if (c == '\n') httpdSend_orDie(conn, "\\n", 2); + else if (c == '\r') httpdSend_orDie(conn, "\\r", 2); + else if (c == '\x1b') httpdSend_orDie(conn, "\\e", 2); + } + + if (start < end) httpdSend_orDie(conn, data + start, end - start); + return 1; +} + + +/** + * Export settings to INI + * + * @param connData + * @return status + */ +httpd_cgi_state ICACHE_FLASH_ATTR +cgiPersistExport(HttpdConnData *connData) +{ + char buff[256]; + u8 mac[6]; + + int step = (int) connData->cgiData; + + if (connData->conn == NULL) { + //Connection aborted. Clean up. + return HTTPD_CGI_DONE; + } + + if (step == 0) { + wifi_get_macaddr(SOFTAP_IF, mac); + sprintf(buff, "attachment; filename=ESPTerm_%02X%02X%02X_%s.ini", + mac[3], mac[4], mac[5], FW_VERSION); + + httpdStartResponse(connData, 200); + httpdHeader(connData, "Content-Disposition", buff); + httpdHeader(connData, "Content-Type", "text/plain"); + httpdEndHeaders(connData); + + sprintf(buff, "# == ESPTerm config export ==\r\n" + "# Device: %02X%02X%02X - %s\r\n" + "# Version: %s\r\n", + mac[3], mac[4], mac[5], + termconf->title, + FW_VERSION); + + httpdSend(connData, buff, -1); + } + + bool quoted; +#define X(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ + if (allow) { \ + xget(buff, deref XSTRUCT->name); \ + \ + quoted = false; \ + quoted |= streq(#type, "char"); \ + quoted |= streq(#type, "uchar"); \ + if (strstarts(#name, "bm")) quoted=false; \ + \ + httpdSend(connData, "\r\n"#name " = ", -1); \ + if (quoted) { \ + httpdSend(connData, "\"", 1); \ + httpdSend_dblquot(connData, buff, -1); \ + httpdSend(connData, "\"", 1); \ + } else { \ + httpdSend(connData, buff, -1); \ + } \ + } + +#define admin 1 +#define tpl 1 + if (step == 1) { + httpdSend(connData, "\r\n[system]", -1); +#define XSTRUCT sysconf + XTABLE_SYSCONF +#undef XSTRUCT + httpdSend(connData, "\r\n", -1); + } + + if (step == 2) { + httpdSend(connData, "\r\n[wifi]", -1); +#define XSTRUCT wificonf + XTABLE_WIFICONF +#undef XSTRUCT + httpdSend(connData, "\r\n", -1); + } + + if (step == 3) { + httpdSend(connData, "\r\n[terminal]", -1); +#define XSTRUCT termconf + XTABLE_TERMCONF +#undef XSTRUCT + httpdSend(connData, "\r\n", -1); + + return HTTPD_CGI_DONE; + } + +#undef X + connData->cgiData = (void *) (step + 1); + return HTTPD_CGI_MORE; +} diff --git a/user/cgi_persist.h b/user/cgi_persist.h index 4875407..b6f626e 100644 --- a/user/cgi_persist.h +++ b/user/cgi_persist.h @@ -6,5 +6,6 @@ httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData); +httpd_cgi_state cgiPersistExport(HttpdConnData *connData); #endif diff --git a/user/cgi_wifi.c b/user/cgi_wifi.c index fe09fda..ca62a78 100644 --- a/user/cgi_wifi.c +++ b/user/cgi_wifi.c @@ -326,7 +326,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) #define XSTRUCT wificonf #define X XSET_CGI_FUNC - XTABLE_WIFI + XTABLE_WIFICONF #undef X // ---- WiFi opmode ---- @@ -417,7 +417,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, #define XSTRUCT wificonf #define X XGET_CGI_FUNC - XTABLE_WIFI + XTABLE_WIFICONF #undef X // non-config diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 6fa3859..09c639a 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -76,21 +76,21 @@ enum xset_result xset_ustring(const char *name, u8 *field, const char *buff, con * If 'name' is found in connData->getArgs, xset() is called. * If the result is SET, xnotify() is fired. Else, 'name,' is appended to the redir_url buffer. */ -#define XSET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ +#define XSET_CGI_FUNC(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ if ((allow) && GET_ARG(#name)) { \ type *_p = (type *) &XSTRUCT->name; \ - enum xset_result res = xset(#name, cast _p, buff, (const void*) (xsarg)); \ + enum xset_result res = xset(#name, _p, buff, (const void*) (xsarg)); \ if (res == XSET_SET) { xnotify; } \ else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \ } -#define XGET_CGI_FUNC(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ +#define XGET_CGI_FUNC(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name); -#define XSTRUCT_FIELD(type, name, suffix, deref, xget, cast, xset, xsarg, xnotify, allow) \ +#define XSTRUCT_FIELD(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ type name suffix; -#define XDUMP_FIELD(type, name, suffix, deref, xget, allow, cast, xset, xsarg, xnotify) \ +#define XDUMP_FIELD(type, name, suffix, deref, xget, allow, xset, xsarg, xnotify) \ { xget(buff, deref XSTRUCT->name); dbg(#name " = %s", buff); } #endif //ESPTERM_CONFIG_XMACROS_H diff --git a/user/routes.c b/user/routes.c index 895133e..ea9748c 100644 --- a/user/routes.c +++ b/user/routes.c @@ -131,6 +131,7 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = { ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"), ROUTE_CGI("/cfg/system/set", cgiSystemCfgSetParams), + ROUTE_CGI("/cfg/system/export", cgiPersistExport), ROUTE_CGI("/cfg/system/write_defaults", cgiPersistWriteDefaults), ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults), ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard), diff --git a/user/screen.h b/user/screen.h index c9bcc00..a335f19 100644 --- a/user/screen.h +++ b/user/screen.h @@ -82,37 +82,37 @@ enum CursorShape { //....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY................Allow // Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) #define XTABLE_TERMCONF \ - X(u32, width, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ - X(u32, height, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ - X(u32, default_bg, /**/, /**/, xget_term_color, /**/, xset_term_color, NULL, /**/, 1) \ - X(u32, default_fg, /**/, /**/, xget_term_color, /**/, xset_term_color, NULL, /**/, 1) \ - X(char, title, [TERM_TITLE_LEN], /**/, xget_string, /**/, xset_string, TERM_TITLE_LEN, /**/, 1) \ - X(char, btn1, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn2, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn3, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn4, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ - X(char, btn5, [TERM_BTN_LEN], /**/, xget_string, /**/, xset_string, TERM_BTN_LEN, /**/, 1) \ - X(u8, theme, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ - X(u32, parser_tout_ms, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ - X(u32, display_tout_ms, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ - X(bool, fn_alt_mode, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ - X(u32, display_cooldown_ms, /**/, /**/, xget_dec, /**/, xset_u32, NULL, /**/, 1) \ - X(bool, loopback, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(bool, show_buttons, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(bool, show_config_links, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(char, bm1, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ - X(char, bm2, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ - X(char, bm3, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ - X(char, bm4, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ - X(char, bm5, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, /**/, xset_term_bm, NULL, /**/, 1) \ - X(u32, cursor_shape, /**/, /**/, xget_dec, /**/, xset_term_cursorshape, NULL, /**/, 1) \ - X(bool, crlf_mode, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(bool, want_all_fn, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(bool, debugbar, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(bool, allow_decopt_12, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(bool, ascii_debug, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ - X(char, backdrop, [TERM_BACKDROP_LEN], /**/, xget_string, /**/, xset_string, TERM_BACKDROP_LEN, /**/, 1) + X(u32, width, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \ + X(u32, height, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \ + X(u32, default_bg, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \ + X(u32, default_fg, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \ + X(char, title, [TERM_TITLE_LEN], /**/, xget_string, xset_string, TERM_TITLE_LEN, /**/, 1) \ + X(char, btn1, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn2, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn3, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn4, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \ + X(char, btn5, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \ + X(u8, theme, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \ + X(u32, parser_tout_ms, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \ + X(u32, display_tout_ms, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \ + X(bool, fn_alt_mode, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ + X(u8, config_version, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \ + X(u32, display_cooldown_ms, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \ + X(bool, loopback, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ + X(bool, show_buttons, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ + X(bool, show_config_links, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ + X(char, bm1, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \ + X(char, bm2, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \ + X(char, bm3, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \ + X(char, bm4, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \ + X(char, bm5, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \ + X(u32, cursor_shape, /**/, /**/, xget_dec, xset_term_cursorshape, NULL, /**/, 1) \ + X(bool, crlf_mode, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ + X(bool, want_all_fn, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ + X(bool, debugbar, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ + X(bool, allow_decopt_12, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ + X(bool, ascii_debug, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ + X(char, backdrop, [TERM_BACKDROP_LEN], /**/, xget_string, xset_string, TERM_BACKDROP_LEN, /**/, 1) #define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*n)) #define TERM_BTN_N(tc, n) ((tc)->btn1+(TERM_BTN_LEN*n)) diff --git a/user/syscfg.h b/user/syscfg.h index 0dacd65..e9ce0d0 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -28,17 +28,17 @@ enum pwlock { //....Type................Name..Suffix...............Deref..XGET..........Cast..XSET.........................NOTIFY....Allow // Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) #define XTABLE_SYSCONF \ - X(u32, uart_baudrate, /**/, /**/, xget_dec, /**/, xset_sys_baudrate, NULL, uart_changed=true, 1) \ - X(u8, uart_parity, /**/, /**/, xget_dec, /**/, xset_sys_parity, NULL, uart_changed=true, 1) \ - X(u8, uart_stopbits, /**/, /**/, xget_dec, /**/, xset_sys_stopbits, NULL, uart_changed=true, 1) \ + X(u32, uart_baudrate, /**/, /**/, xget_dec, xset_sys_baudrate, NULL, uart_changed=true, 1) \ + X(u8, uart_parity, /**/, /**/, xget_dec, xset_sys_parity, NULL, uart_changed=true, 1) \ + X(u8, uart_stopbits, /**/, /**/, xget_dec, xset_sys_stopbits, NULL, uart_changed=true, 1) \ \ - X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) \ + X(u8, config_version, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \ \ - X(u8, pwlock, /**/, /**/, xget_dec, /**/, xset_sys_pwlock, NULL, /**/, admin|tpl) \ - X(uchar, access_pw, [64], /**/, xget_ustring, /**/, xset_sys_accesspw, NULL, /**/, admin) \ - X(uchar, access_name, [32], /**/, xget_ustring, /**/, xset_ustring, NULL, /**/, admin|tpl) \ + X(u8, pwlock, /**/, /**/, xget_dec, xset_sys_pwlock, NULL, /**/, admin|tpl) \ + X(uchar, access_pw, [64], /**/, xget_ustring, xset_sys_accesspw, NULL, /**/, admin) \ + X(uchar, access_name, [32], /**/, xget_ustring, xset_ustring, NULL, /**/, admin|tpl) \ \ - X(bool, overclock, /**/, /**/, xget_bool, /**/, xset_bool, NULL, /**/, 1) \ + X(bool, overclock, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \ typedef struct { diff --git a/user/wifimgr.h b/user/wifimgr.h index 85b2b77..07d3d61 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -23,37 +23,37 @@ #define wifimgr_notify_ap() { wifi_change_flags.ap = true; } #define wifimgr_notify_sta() { wifi_change_flags.ap = true; } -//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET.........................NOTIFY................Allow +//....Type................Name..Suffix...............Deref..XGET...........XSET.........................NOTIFY................Allow // Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) -#define XTABLE_WIFI \ - X(u8, opmode, /**/, /**/, xget_dec, /**/, xset_wifi_opmode, NULL, /**/, 1) \ +#define XTABLE_WIFICONF \ + X(u8, opmode, /**/, /**/, xget_dec, xset_wifi_opmode, NULL, /**/, 1) \ \ - X(u8, tpw, /**/, /**/, xget_dec, /**/, xset_wifi_tpw, NULL, wifimgr_notify_ap(), 1) \ - X(u8, ap_channel, /**/, /**/, xget_dec, /**/, xset_wifi_ap_channel, NULL, wifimgr_notify_ap(), 1) \ - X(u8, ap_ssid, [SSID_LEN], /**/, xget_ustring, /**/, xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \ - X(u8, ap_password, [PASSWORD_LEN], /**/, xget_ustring, /**/, xset_wifi_pwd, NULL, wifimgr_notify_ap(), 1) \ - X(bool, ap_hidden, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_ap(), 1) \ + X(u8, tpw, /**/, /**/, xget_dec, xset_wifi_tpw, NULL, wifimgr_notify_ap(), 1) \ + X(u8, ap_channel, /**/, /**/, xget_dec, xset_wifi_ap_channel, NULL, wifimgr_notify_ap(), 1) \ + X(uchar, ap_ssid, [SSID_LEN], /**/, xget_ustring, xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \ + X(uchar, ap_password, [PASSWORD_LEN], /**/, xget_ustring, xset_wifi_pwd, NULL, wifimgr_notify_ap(), 1) \ + X(bool, ap_hidden, /**/, /**/, xget_bool, xset_bool, NULL, wifimgr_notify_ap(), 1) \ \ - X(u16, ap_dhcp_time, /**/, /**/, xget_dec, /**/, xset_wifi_lease_time, NULL, wifimgr_notify_ap(), 1) \ - X(u32, unused1, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, /**/, 0) \ - X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap(), 1) \ - X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap(), 1) \ + X(u16, ap_dhcp_time, /**/, /**/, xget_dec, xset_wifi_lease_time, NULL, wifimgr_notify_ap(), 1) \ + X(u32, unused1, /**/, /**/, xget_dummy, xset_dummy, NULL, /**/, 0) \ + X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \ + X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \ \ - X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap(), 1) \ - X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_ap(), 1) \ + X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \ + X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \ \ \ - X(u32, unused2, /**/, /**/, xget_dummy, /**/, xset_dummy, NULL, /**/, 0) \ - X(u8, sta_ssid, [SSID_LEN], /**/, xget_ustring, /**/, xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \ - X(u8, sta_password, [PASSWORD_LEN], /**/, xget_ustring, /**/, xset_wifi_pwd, NULL, wifimgr_notify_sta(), 1) \ - X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, /**/, xset_bool, NULL, wifimgr_notify_sta(), 1) \ + X(u32, unused2, /**/, /**/, xget_dummy, xset_dummy, NULL, /**/, 0) \ + X(uchar, sta_ssid, [SSID_LEN], /**/, xget_ustring, xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \ + X(uchar, sta_password, [PASSWORD_LEN], /**/, xget_ustring, xset_wifi_pwd, NULL, wifimgr_notify_sta(), 1) \ + X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, xset_bool, NULL, wifimgr_notify_sta(), 1) \ \ - X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta(), 1) \ - X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta(), 1) \ - X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, /**/, xset_ip, NULL, wifimgr_notify_sta(), 1) \ + X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_sta(), 1) \ + X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_sta(), 1) \ + X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_sta(), 1) \ \ \ - X(u8, config_version, /**/, /**/, xget_dec, /**/, xset_u8, NULL, /**/, 1) + X(u8, config_version, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) // unused1 - replaces 'enabled' bit from old dhcps_lease struct // unused2 - gap after 'ap_gw' which isn't used and doesn't make sense @@ -66,7 +66,7 @@ */ typedef struct { #define X XSTRUCT_FIELD - XTABLE_WIFI + XTABLE_WIFICONF #undef X } WiFiConfigBundle; From ba2b73a287074897481016f2b4cd7ae517c17650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 23 Oct 2017 01:40:56 +0200 Subject: [PATCH 09/10] ini upload and parse (no loading to conf yet) --- CMakeLists.txt | 3 + build_parser.sh | 4 + esphttpdconfig.mk.example | 1 + libesphttpd | 2 +- user/cgi_persist.c | 61 ++++- user/cgi_persist.h | 1 + user/ini_parser.c | 526 ++++++++++++++++++++++++++++++++++++++ user/ini_parser.h | 65 +++++ user/ini_parser.rl | 284 ++++++++++++++++++++ user/routes.c | 1 + user/syscfg.h | 2 +- 11 files changed, 946 insertions(+), 4 deletions(-) create mode 100644 user/ini_parser.c create mode 100644 user/ini_parser.h create mode 100644 user/ini_parser.rl diff --git a/CMakeLists.txt b/CMakeLists.txt index 6345770..82e7a13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,9 @@ set(SOURCE_FILES user/jstring.c user/jstring.h user/character_sets.h + user/ini_parser.h + user/ini_parser.c + user/ini_parser.rl user/utf8.h user/utf8.c user/cgi_logging.h user/config_xmacros.h user/config_xmacros.c) diff --git a/build_parser.sh b/build_parser.sh index ebcbdc2..af37cf3 100755 --- a/build_parser.sh +++ b/build_parser.sh @@ -3,6 +3,10 @@ echo "-- Building parser from Ragel source --" ragel -L -G0 user/ansi_parser.rl -o user/ansi_parser.c +ragel -L -G0 user/ini_parser.rl -o user/ini_parser.c sed -i "s/static const char _ansi_actions\[\]/static const char _ansi_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c sed -i "s/static const char _ansi_eof_actions\[\]/static const char _ansi_eof_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c + +sed -i "s/static const char _ini_actions\[\]/static const char _ini_actions\[\] ESP_CONST_DATA/" user/ini_parser.c +sed -i "s/static const char _ini_eof_actions\[\]/static const char _ini_eof_actions\[\] ESP_CONST_DATA/" user/ini_parser.c diff --git a/esphttpdconfig.mk.example b/esphttpdconfig.mk.example index d95d005..d6f0315 100644 --- a/esphttpdconfig.mk.example +++ b/esphttpdconfig.mk.example @@ -40,6 +40,7 @@ ESP_SPI_FLASH_SIZE_K = 1024 GLOBAL_CFLAGS = \ -DASYNC_LOG=1 \ + -DDEBUG_INI=0 \ -DDEBUG_D2D=0 \ -DDEBUG_ROUTER=0 \ -DDEBUG_CAPTDNS=0 \ diff --git a/libesphttpd b/libesphttpd index e4ecf07..d6651ca 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit e4ecf0724e36c828be5222eddce58a6a5cd2386f +Subproject commit d6651ca0d11b7950078247b8305f5b23a6be6c6c diff --git a/user/cgi_persist.c b/user/cgi_persist.c index 4f5e56f..0b6b9dc 100644 --- a/user/cgi_persist.c +++ b/user/cgi_persist.c @@ -10,6 +10,7 @@ Cgi/template routines for configuring non-wifi settings #include "version.h" #include "screen.h" #include "config_xmacros.h" +#include "ini_parser.h" #define SET_REDIR_SUC "/cfg/system" @@ -155,10 +156,17 @@ cgiPersistExport(HttpdConnData *connData) httpdSend(connData, buff, -1); } + // do not export SSID if unchanged - embeds unique ID that should + // not be overwritten in target. + + char defSSID[20]; + sprintf(defSSID, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]); + bool quoted; #define X(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ - if (allow) { \ + do { if (allow) { \ xget(buff, deref XSTRUCT->name); \ + if (streq(#name, "ap_ssid") && streq(buff, defSSID)) break; \ \ quoted = false; \ quoted |= streq(#type, "char"); \ @@ -173,7 +181,7 @@ cgiPersistExport(HttpdConnData *connData) } else { \ httpdSend(connData, buff, -1); \ } \ - } + } } while(0); #define admin 1 #define tpl 1 @@ -207,3 +215,52 @@ cgiPersistExport(HttpdConnData *connData) connData->cgiData = (void *) (step + 1); return HTTPD_CGI_MORE; } + + +void iniCb(const char *section, const char *key, const char *value, void *userData) +{ + dbg(">>> SET: [%s] %s = %s", section, key, value); +} + + +httpd_cgi_state ICACHE_FLASH_ATTR +postRecvHdl(HttpdConnData *connData, char *data, int len) +{ + ini_parse(data, (size_t) len); + ini_parse_end(); + return HTTPD_CGI_DONE; +} + +/** + * Import settings from INI + * + * @param connData + * @return status + */ +httpd_cgi_state ICACHE_FLASH_ATTR +cgiPersistImport(HttpdConnData *connData) +{ + if (connData->conn == NULL) { + //Connection aborted. Clean up. + return HTTPD_CGI_DONE; + } + + httpdStartResponse(connData, 200); + httpdHeader(connData, "Content-Type", "text/plain"); + httpdEndHeaders(connData); + + char *start = strstr(connData->post->buff, "\r\n\r\n"); + if (start == NULL) { + error("Malformed attachment POST!"); + goto end; + } + + ini_parse_begin(iniCb, NULL); + ini_parse(start, (size_t) connData->post->buffLen - (start - connData->post->buff)); + + connData->recvHdl = postRecvHdl; + +end: + // TODO redirect + return HTTPD_CGI_DONE; +} diff --git a/user/cgi_persist.h b/user/cgi_persist.h index b6f626e..e37c0df 100644 --- a/user/cgi_persist.h +++ b/user/cgi_persist.h @@ -7,5 +7,6 @@ httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData); httpd_cgi_state cgiPersistExport(HttpdConnData *connData); +httpd_cgi_state cgiPersistImport(HttpdConnData *connData); #endif diff --git a/user/ini_parser.c b/user/ini_parser.c new file mode 100644 index 0000000..fc99c5c --- /dev/null +++ b/user/ini_parser.c @@ -0,0 +1,526 @@ + +/* #line 1 "user/ini_parser.rl" */ + +/* Ragel constants block */ +#include "ini_parser.h" + +// Ragel setup + +/* #line 10 "user/ini_parser.c" */ +static const char _ini_actions[] ESP_CONST_DATA = { + 0, 1, 1, 1, 2, 1, 3, 1, + 4, 1, 5, 1, 6, 1, 7, 1, + 8, 1, 9, 1, 10, 1, 11, 1, + 13, 2, 0, 4, 2, 12, 4 +}; + +static const char _ini_eof_actions[] ESP_CONST_DATA = { + 0, 23, 5, 5, 15, 15, 15, 15, + 19, 19, 0, 0, 0, 0, 0, 0, + 0 +}; + +static const int ini_start = 1; +static const int ini_first_final = 12; +static const int ini_error = 0; + +static const int ini_en_section = 2; +static const int ini_en_keyvalue = 4; +static const int ini_en_comment = 8; +static const int ini_en_discard2eol = 10; +static const int ini_en_main = 1; + + +/* #line 10 "user/ini_parser.rl" */ + + +// Persistent state +static int8_t cs = -1; //!< Ragel's Current State variable +static uint32_t buff_i = 0; //!< Write pointer for the buffers +static char value_quote = 0; //!< Quote character of the currently collected value +static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character +static IniParserCallback keyCallback = NULL; //!< Currently assigned callback +static void *userdata = NULL; //!< Currently assigned user data for the callback + +// Buffers +static char keybuf[INI_KEY_MAX]; +static char secbuf[INI_KEY_MAX]; +static char valbuf[INI_VALUE_MAX]; + +// See header for doxygen! + +void ICACHE_FLASH_ATTR +ini_parse_reset_partial(void) +{ + buff_i = 0; + value_quote = 0; + value_nextesc = false; +} + +void ICACHE_FLASH_ATTR +ini_parse_reset(void) +{ + ini_parse_reset_partial(); + keybuf[0] = secbuf[0] = valbuf[0] = 0; + +/* #line 67 "user/ini_parser.c" */ + { + cs = ini_start; + } + +/* #line 41 "user/ini_parser.rl" */ +} + +void ICACHE_FLASH_ATTR +ini_parser_error(const char* msg) +{ + ini_error("Parser error: %s", msg); + ini_parse_reset_partial(); +} + + +void ICACHE_FLASH_ATTR +ini_parse_begin(IniParserCallback callback, void *userData) +{ + keyCallback = callback; + userdata = userData; + ini_parse_reset(); +} + + +void ICACHE_FLASH_ATTR +*ini_parse_end(void) +{ + ini_parse("\n", 1); + if (keyCallback) { + keyCallback = NULL; + } + + void *ud = userdata; + userdata = NULL; + return ud; +} + + +void ICACHE_FLASH_ATTR +ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData) +{ + ini_parse_begin(callback, userData); + ini_parse(text, len); + ini_parse_end(); +} + +static void ICACHE_FLASH_ATTR +rtrim_buf(char *buf, int32_t end) +{ + if (end > 0) { + while ((uint8_t)buf[--end] < 33); + end++; // go past the last character + } + + buf[end] = 0; +} + + +void ICACHE_FLASH_ATTR +ini_parse(const char *newstr, size_t len) +{ + int32_t i; + char c; + bool isnl; + bool isquot; + + // Load new data to Ragel vars + const uint8_t *p; + const uint8_t *eof; + const uint8_t *pe; + + if (len == 0) while(newstr[++len] != 0); // alternative to strlen + + p = (const uint8_t *) newstr; + eof = NULL; + pe = (const uint8_t *) (newstr + len); + + // Init Ragel on the first run + if (cs == -1) { + ini_parse_reset(); + } + + // The parser + +/* #line 152 "user/ini_parser.c" */ + { + const char *_acts; + unsigned int _nacts; + + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + switch ( cs ) { +case 1: + switch( (*p) ) { + case 32u: goto tr1; + case 35u: goto tr3; + case 58u: goto tr0; + case 59u: goto tr3; + case 61u: goto tr0; + case 91u: goto tr4; + } + if ( (*p) < 9u ) { + if ( (*p) <= 8u ) + goto tr0; + } else if ( (*p) > 13u ) { + if ( 14u <= (*p) && (*p) <= 31u ) + goto tr0; + } else + goto tr1; + goto tr2; +case 0: + goto _out; +case 12: + goto tr0; +case 2: + switch( (*p) ) { + case 9u: goto tr6; + case 32u: goto tr6; + case 93u: goto tr5; + } + if ( (*p) <= 31u ) + goto tr5; + goto tr7; +case 3: + if ( (*p) == 93u ) + goto tr8; + if ( (*p) > 8u ) { + if ( 10u <= (*p) && (*p) <= 31u ) + goto tr5; + } else + goto tr5; + goto tr7; +case 13: + goto tr5; +case 4: + switch( (*p) ) { + case 10u: goto tr10; + case 58u: goto tr11; + case 61u: goto tr11; + } + goto tr9; +case 5: + switch( (*p) ) { + case 9u: goto tr13; + case 10u: goto tr14; + case 13u: goto tr15; + case 32u: goto tr13; + } + goto tr12; +case 6: + switch( (*p) ) { + case 10u: goto tr14; + case 13u: goto tr15; + } + goto tr12; +case 14: + goto tr10; +case 7: + if ( (*p) == 10u ) + goto tr14; + goto tr10; +case 8: + switch( (*p) ) { + case 10u: goto tr17; + case 13u: goto tr18; + } + goto tr16; +case 15: + goto tr19; +case 9: + if ( (*p) == 10u ) + goto tr17; + goto tr19; +case 10: + switch( (*p) ) { + case 10u: goto tr21; + case 13u: goto tr22; + } + goto tr20; +case 16: + goto tr23; +case 11: + if ( (*p) == 10u ) + goto tr21; + goto tr23; + } + + tr23: cs = 0; goto _again; + tr0: cs = 0; goto f0; + tr5: cs = 0; goto f4; + tr10: cs = 0; goto f7; + tr19: cs = 0; goto f11; + tr1: cs = 1; goto _again; + tr6: cs = 2; goto _again; + tr7: cs = 3; goto f5; + tr9: cs = 4; goto f8; + tr13: cs = 5; goto _again; + tr11: cs = 5; goto f9; + tr12: cs = 6; goto f10; + tr15: cs = 7; goto _again; + tr16: cs = 8; goto _again; + tr18: cs = 9; goto _again; + tr20: cs = 10; goto _again; + tr22: cs = 11; goto _again; + tr2: cs = 12; goto f1; + tr3: cs = 12; goto f2; + tr4: cs = 12; goto f3; + tr8: cs = 13; goto f6; + tr14: cs = 14; goto f10; + tr17: cs = 15; goto f12; + tr21: cs = 16; goto f13; + + f5: _acts = _ini_actions + 1; goto execFuncs; + f6: _acts = _ini_actions + 3; goto execFuncs; + f4: _acts = _ini_actions + 5; goto execFuncs; + f1: _acts = _ini_actions + 7; goto execFuncs; + f8: _acts = _ini_actions + 9; goto execFuncs; + f9: _acts = _ini_actions + 11; goto execFuncs; + f10: _acts = _ini_actions + 13; goto execFuncs; + f7: _acts = _ini_actions + 15; goto execFuncs; + f12: _acts = _ini_actions + 17; goto execFuncs; + f11: _acts = _ini_actions + 19; goto execFuncs; + f13: _acts = _ini_actions + 21; goto execFuncs; + f0: _acts = _ini_actions + 23; goto execFuncs; + f3: _acts = _ini_actions + 25; goto execFuncs; + f2: _acts = _ini_actions + 28; goto execFuncs; + +execFuncs: + _nacts = *_acts++; + while ( _nacts-- > 0 ) { + switch ( *_acts++ ) { + case 0: +/* #line 130 "user/ini_parser.rl" */ + { + buff_i = 0; + {cs = 2;goto _again;} + } + break; + case 1: +/* #line 135 "user/ini_parser.rl" */ + { + if (buff_i >= INI_KEY_MAX) { + ini_parser_error("Section name too long"); + {cs = 10;goto _again;} + } + keybuf[buff_i++] = (*p); + } + break; + case 2: +/* #line 143 "user/ini_parser.rl" */ + { + // we need a separate buffer for the result, otherwise a failed + // partial parse would corrupt the section string + rtrim_buf(keybuf, buff_i); + for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c; + secbuf[i] = 0; + {cs = 1;goto _again;} + } + break; + case 3: +/* #line 155 "user/ini_parser.rl" */ + { + ini_parser_error("Syntax error in [section]"); + if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;} + } + break; + case 4: +/* #line 162 "user/ini_parser.rl" */ + { + buff_i = 0; + keybuf[buff_i++] = (*p); // add the first char + {cs = 4;goto _again;} + } + break; + case 5: +/* #line 168 "user/ini_parser.rl" */ + { + if (buff_i >= INI_KEY_MAX) { + ini_parser_error("Key too long"); + {cs = 10;goto _again;} + } + keybuf[buff_i++] = (*p); + } + break; + case 6: +/* #line 176 "user/ini_parser.rl" */ + { + rtrim_buf(keybuf, buff_i); + + // --- Value begin --- + buff_i = 0; + value_quote = 0; + value_nextesc = false; + } + break; + case 7: +/* #line 185 "user/ini_parser.rl" */ + { + isnl = ((*p) == '\r' || (*p) == '\n'); + isquot = ((*p) == '\'' || (*p) == '"'); + + // detect our starting quote + if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) { + value_quote = (*p); + goto valueCharDone; + } + + if (buff_i >= INI_VALUE_MAX) { + ini_parser_error("Value too long"); + {cs = 10;goto _again;} + } + + // end of string - clean up and report + if ((!value_nextesc && (*p) == value_quote) || isnl) { + if (isnl && value_quote) { + ini_parser_error("Unterminated string"); + {cs = 1;goto _again;} + } + + // unquoted: trim from the end + if (!value_quote) { + rtrim_buf(valbuf, buff_i); + } else { + valbuf[buff_i] = 0; + } + + if (keyCallback) { + keyCallback(secbuf, keybuf, valbuf, userdata); + } + + // we don't want to discard to eol if the string was terminated by eol + // - would delete the next line + + if (isnl) {cs = 1;goto _again;} else {cs = 10;goto _again;} + } + + c = (*p); + // escape... + if (value_nextesc) { + if ((*p) == 'n') c = '\n'; + else if ((*p) == 'r') c = '\r'; + else if ((*p) == 't') c = '\t'; + else if ((*p) == 'e') c = '\033'; + } + + // collecting characters... + if (value_nextesc || (*p) != '\\') { // is quoted, or is not a quoting backslash - literal character + valbuf[buff_i++] = c; + } + + value_nextesc = (!value_nextesc && (*p) == '\\'); +valueCharDone:; + } + break; + case 8: +/* #line 247 "user/ini_parser.rl" */ + { + ini_parser_error("Syntax error in key=value"); + if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;} + } + break; + case 9: +/* #line 257 "user/ini_parser.rl" */ + { {cs = 1;goto _again;} } + break; + case 10: +/* #line 258 "user/ini_parser.rl" */ + { + ini_parser_error("Syntax error in comment"); + if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;} + } + break; + case 11: +/* #line 265 "user/ini_parser.rl" */ + { {cs = 1;goto _again;} } + break; + case 12: +/* #line 273 "user/ini_parser.rl" */ + { {cs = 8;goto _again;} } + break; + case 13: +/* #line 276 "user/ini_parser.rl" */ + { + ini_parser_error("Syntax error in root"); + {cs = 10;goto _again;} + } + break; +/* #line 458 "user/ini_parser.c" */ + } + } + goto _again; + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + const char *__acts = _ini_actions + _ini_eof_actions[cs]; + unsigned int __nacts = (unsigned int) *__acts++; + while ( __nacts-- > 0 ) { + switch ( *__acts++ ) { + case 3: +/* #line 155 "user/ini_parser.rl" */ + { + ini_parser_error("Syntax error in [section]"); + if((*p) == '\n') {cs = 1; if ( p == pe ) + goto _test_eof; +goto _again;} else {cs = 10; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 8: +/* #line 247 "user/ini_parser.rl" */ + { + ini_parser_error("Syntax error in key=value"); + if((*p) == '\n') {cs = 1; if ( p == pe ) + goto _test_eof; +goto _again;} else {cs = 10; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 10: +/* #line 258 "user/ini_parser.rl" */ + { + ini_parser_error("Syntax error in comment"); + if((*p) == '\n') {cs = 1; if ( p == pe ) + goto _test_eof; +goto _again;} else {cs = 10; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 13: +/* #line 276 "user/ini_parser.rl" */ + { + ini_parser_error("Syntax error in root"); + {cs = 10; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; +/* #line 517 "user/ini_parser.c" */ + } + } + } + + _out: {} + } + +/* #line 283 "user/ini_parser.rl" */ + +} diff --git a/user/ini_parser.h b/user/ini_parser.h new file mode 100644 index 0000000..2be0f5b --- /dev/null +++ b/user/ini_parser.h @@ -0,0 +1,65 @@ +#ifndef INIPARSE_STREAM_H +#define INIPARSE_STREAM_H + +#include + +#ifdef DEBUG_INI +#define ini_error(fmt, ...) error("[INI] "#fmt, ##__VA_ARGS__) +#else +#define ini_error(fmt, ...) +#endif + +// buffer sizes +#define INI_KEY_MAX 64 +#define INI_VALUE_MAX 256 + +/** + * INI parser callback, called for each found key-value pair. + * + * @param section - current section, empty string for global keys + * @param key - found key (trimmed of whitespace) + * @param value - value, trimmed of quotes or whitespace + * @param userData - opaque user data pointer, general purpose + */ +typedef void (*IniParserCallback)(const char *section, const char *key, const char *value, void *userData); + +/** + * Begin parsing a stream + * + * @param callback - key callback to assign + * @param userData - optional user data that willb e passed to the callback + */ +void ini_parse_begin(IniParserCallback callback, void *userData); + +/** + * End parse stream. + * Flushes what remains in the buffer and removes callback. + * + * @returns userData or NULL if none + */ +void* ini_parse_end(void); + +/** + * Parse a string (needn't be complete line or file) + * + * @param data - string to parse + * @param len - string length (0 = use strlen) + */ +void ini_parse(const char *data, size_t len); + +/** + * Parse a complete file loaded to string + * + * @param text - entire file as string + * @param len - file length (0 = use strlen) + * @param callback - key callback + * @param userData - optional user data for key callback + */ +void ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData); + +/** + * Explicitly reset the parser + */ +void ini_parse_reset(void); + +#endif // INIPARSE_STREAM_H diff --git a/user/ini_parser.rl b/user/ini_parser.rl new file mode 100644 index 0000000..e85e035 --- /dev/null +++ b/user/ini_parser.rl @@ -0,0 +1,284 @@ + +/* Ragel constants block */ +#include "ini_parser.h" + +// Ragel setup +%%{ + machine ini; + write data; + alphtype unsigned char; +}%% + +// Persistent state +static int8_t cs = -1; //!< Ragel's Current State variable +static uint32_t buff_i = 0; //!< Write pointer for the buffers +static char value_quote = 0; //!< Quote character of the currently collected value +static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character +static IniParserCallback keyCallback = NULL; //!< Currently assigned callback +static void *userdata = NULL; //!< Currently assigned user data for the callback + +// Buffers +static char keybuf[INI_KEY_MAX]; +static char secbuf[INI_KEY_MAX]; +static char valbuf[INI_VALUE_MAX]; + +// See header for doxygen! + +void ICACHE_FLASH_ATTR +ini_parse_reset_partial(void) +{ + buff_i = 0; + value_quote = 0; + value_nextesc = false; +} + +void ICACHE_FLASH_ATTR +ini_parse_reset(void) +{ + ini_parse_reset_partial(); + keybuf[0] = secbuf[0] = valbuf[0] = 0; + %% write init; +} + +void ICACHE_FLASH_ATTR +ini_parser_error(const char* msg) +{ + ini_error("Parser error: %s", msg); + ini_parse_reset_partial(); +} + + +void ICACHE_FLASH_ATTR +ini_parse_begin(IniParserCallback callback, void *userData) +{ + keyCallback = callback; + userdata = userData; + ini_parse_reset(); +} + + +void ICACHE_FLASH_ATTR +*ini_parse_end(void) +{ + ini_parse("\n", 1); + if (keyCallback) { + keyCallback = NULL; + } + + void *ud = userdata; + userdata = NULL; + return ud; +} + + +void ICACHE_FLASH_ATTR +ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData) +{ + ini_parse_begin(callback, userData); + ini_parse(text, len); + ini_parse_end(); +} + +static void ICACHE_FLASH_ATTR +rtrim_buf(char *buf, int32_t end) +{ + if (end > 0) { + while ((uint8_t)buf[--end] < 33); + end++; // go past the last character + } + + buf[end] = 0; +} + + +void ICACHE_FLASH_ATTR +ini_parse(const char *newstr, size_t len) +{ + int32_t i; + char c; + bool isnl; + bool isquot; + + // Load new data to Ragel vars + const uint8_t *p; + const uint8_t *eof; + const uint8_t *pe; + + if (len == 0) while(newstr[++len] != 0); // alternative to strlen + + p = (const uint8_t *) newstr; + eof = NULL; + pe = (const uint8_t *) (newstr + len); + + // Init Ragel on the first run + if (cs == -1) { + ini_parse_reset(); + } + + // The parser + %%{ +#/ * + ispace = [ \t]; # inline space + wchar = any - 0..8 - 10..31; + #apos = '\''; + #quot = '\"'; + nonl = [^\r\n]; + nl = '\r'? '\n'; + + # ---- [SECTION] ---- + + action sectionStart { + buff_i = 0; + fgoto section; + } + + action sectionChar { + if (buff_i >= INI_KEY_MAX) { + ini_parser_error("Section name too long"); + fgoto discard2eol; + } + keybuf[buff_i++] = fc; + } + + action sectionEnd { + // we need a separate buffer for the result, otherwise a failed + // partial parse would corrupt the section string + rtrim_buf(keybuf, buff_i); + for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c; + secbuf[i] = 0; + fgoto main; + } + + section := + ( + ispace* <: ((wchar - ']')+ @sectionChar) ']' @sectionEnd + ) $!{ + ini_parser_error("Syntax error in [section]"); + if(fc == '\n') fgoto main; else fgoto discard2eol; + }; + + # ---- KEY=VALUE ---- + + action keyStart { + buff_i = 0; + keybuf[buff_i++] = fc; // add the first char + fgoto keyvalue; + } + + action keyChar { + if (buff_i >= INI_KEY_MAX) { + ini_parser_error("Key too long"); + fgoto discard2eol; + } + keybuf[buff_i++] = fc; + } + + action keyEnd { + rtrim_buf(keybuf, buff_i); + + // --- Value begin --- + buff_i = 0; + value_quote = 0; + value_nextesc = false; + } + + action valueChar { + isnl = (fc == '\r' || fc == '\n'); + isquot = (fc == '\'' || fc == '"'); + + // detect our starting quote + if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) { + value_quote = fc; + goto valueCharDone; + } + + if (buff_i >= INI_VALUE_MAX) { + ini_parser_error("Value too long"); + fgoto discard2eol; + } + + // end of string - clean up and report + if ((!value_nextesc && fc == value_quote) || isnl) { + if (isnl && value_quote) { + ini_parser_error("Unterminated string"); + fgoto main; + } + + // unquoted: trim from the end + if (!value_quote) { + rtrim_buf(valbuf, buff_i); + } else { + valbuf[buff_i] = 0; + } + + if (keyCallback) { + keyCallback(secbuf, keybuf, valbuf, userdata); + } + + // we don't want to discard to eol if the string was terminated by eol + // - would delete the next line + + if (isnl) fgoto main; else fgoto discard2eol; + } + + c = fc; + // escape... + if (value_nextesc) { + if (fc == 'n') c = '\n'; + else if (fc == 'r') c = '\r'; + else if (fc == 't') c = '\t'; + else if (fc == 'e') c = '\033'; + } + + // collecting characters... + if (value_nextesc || fc != '\\') { // is quoted, or is not a quoting backslash - literal character + valbuf[buff_i++] = c; + } + + value_nextesc = (!value_nextesc && fc == '\\'); +valueCharDone:; + } + + # use * for key, first char is already consumed. + keyvalue := + ( + ([^\n=:]* @keyChar %keyEnd) + [=:] ispace* <: nonl* @valueChar nl @valueChar + ) $!{ + ini_parser_error("Syntax error in key=value"); + if(fc == '\n') fgoto main; else fgoto discard2eol; + }; + + # ---- COMMENT ---- + + comment := + ( + nonl* nl + @{ fgoto main; } + ) $!{ + ini_parser_error("Syntax error in comment"); + if(fc == '\n') fgoto main; else fgoto discard2eol; + }; + + # ---- CLEANUP ---- + + discard2eol := nonl* nl @{ fgoto main; }; + + # ---- ROOT ---- + + main := + (space* + ( + '[' @sectionStart | + [#;] @{ fgoto comment; } | + (wchar - [\t =:]) @keyStart + ) + ) $!{ + ini_parser_error("Syntax error in root"); + fgoto discard2eol; + }; + + write exec; +#*/ + }%% +} diff --git a/user/routes.c b/user/routes.c index ea9748c..add3c97 100644 --- a/user/routes.c +++ b/user/routes.c @@ -132,6 +132,7 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = { ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"), ROUTE_CGI("/cfg/system/set", cgiSystemCfgSetParams), ROUTE_CGI("/cfg/system/export", cgiPersistExport), + ROUTE_CGI("/cfg/system/import", cgiPersistImport), ROUTE_CGI("/cfg/system/write_defaults", cgiPersistWriteDefaults), ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults), ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard), diff --git a/user/syscfg.h b/user/syscfg.h index e9ce0d0..2bb0684 100644 --- a/user/syscfg.h +++ b/user/syscfg.h @@ -25,7 +25,7 @@ enum pwlock { PWLOCK_MAX = 5, }; -//....Type................Name..Suffix...............Deref..XGET..........Cast..XSET.........................NOTIFY....Allow +//....Type................Name..Suffix...............Deref..XGET............XSET.........................NOTIFY....Allow // Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays) #define XTABLE_SYSCONF \ X(u32, uart_baudrate, /**/, /**/, xget_dec, xset_sys_baudrate, NULL, uart_changed=true, 1) \ From 2f4cd08c6030f61784af93fcca4dccc997da3fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 23 Oct 2017 22:43:04 +0200 Subject: [PATCH 10/10] import now fully working --- esphttpdconfig.mk.example | 2 +- front-end | 2 +- user/cgi_persist.c | 198 +++++++++++++++++++++++++++++++++++--- user/config_xmacros.h | 10 ++ user/screen.c | 6 +- user/serial.c | 2 +- user/wifimgr.c | 15 +++ user/wifimgr.h | 1 + 8 files changed, 216 insertions(+), 20 deletions(-) diff --git a/esphttpdconfig.mk.example b/esphttpdconfig.mk.example index d6f0315..97c8999 100644 --- a/esphttpdconfig.mk.example +++ b/esphttpdconfig.mk.example @@ -40,7 +40,7 @@ ESP_SPI_FLASH_SIZE_K = 1024 GLOBAL_CFLAGS = \ -DASYNC_LOG=1 \ - -DDEBUG_INI=0 \ + -DDEBUG_INI=1 \ -DDEBUG_D2D=0 \ -DDEBUG_ROUTER=0 \ -DDEBUG_CAPTDNS=0 \ diff --git a/front-end b/front-end index 8327ff0..a96b522 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 8327ff010893935c0c517c729727464f66badd89 +Subproject commit a96b522ca899c687d70b9ad92f708cd416b8d460 diff --git a/user/cgi_persist.c b/user/cgi_persist.c index 0b6b9dc..46393ad 100644 --- a/user/cgi_persist.c +++ b/user/cgi_persist.c @@ -13,6 +13,7 @@ Cgi/template routines for configuring non-wifi settings #include "ini_parser.h" #define SET_REDIR_SUC "/cfg/system" +#define SET_REDIR_ERR SET_REDIR_SUC"?err=" static bool ICACHE_FLASH_ATTR verify_admin_pw(const char *pw) @@ -79,7 +80,7 @@ cgiPersistRestoreHard(HttpdConnData *connData) return HTTPD_CGI_DONE; } - +// -------------- Export -------------- #define httpdSend_orDie(conn, data, len) do { if (!httpdSend((conn), (data), (len))) return false; } while (0) @@ -217,17 +218,154 @@ cgiPersistExport(HttpdConnData *connData) } -void iniCb(const char *section, const char *key, const char *value, void *userData) +// -------------- IMPORT -------------- + +struct IniUpload { + TerminalConfigBundle *term_backup; + WiFiConfigBundle *wifi_backup; + SystemConfigBundle *sys_backup; + bool term_ok; + bool wifi_ok; + bool sys_ok; +}; + +static void ICACHE_FLASH_ATTR iniCb(const char *section, const char *key, const char *value, void *userData) { - dbg(">>> SET: [%s] %s = %s", section, key, value); + HttpdConnData *connData = (HttpdConnData *)userData; + struct IniUpload *state; + if (!connData || !connData->cgiData) { + error("userData or state is NULL!"); + return; + } + state = connData->cgiData; + +// cgi_dbg("%s.%s = %s", section, key, value); + +#define X XSET_ASSIGN + + bool suc = true; + bool found = false; + + // notify flag, unused here + bool uart_changed = false; + + if (streq(section, "terminal")) { +#define XSTRUCT termconf + XTABLE_TERMCONF +#undef XSTRUCT + if (!suc) state->term_ok = false; + } + else if (streq(section, "wifi")) { +#define XSTRUCT wificonf + XTABLE_WIFICONF +#undef XSTRUCT + if (!suc) state->wifi_ok = false; + } + else if (streq(section, "system")) { +#define XSTRUCT sysconf + XTABLE_SYSCONF +#undef XSTRUCT + if (!suc) state->sys_ok = false; + } + + if (!found) cgi_warn("Unknown key %s.%s!", section, key); + +#undef X } +static void ICACHE_FLASH_ATTR +freeIniUploadStruct(HttpdConnData *connData) +{ + cgi_dbg("Free struct..."); + struct IniUpload *state; + if (connData && connData->cgiData) { + state = connData->cgiData; + if (state->sys_backup != NULL) free(state->sys_backup); + if (state->wifi_backup != NULL) free(state->wifi_backup); + if (state->term_backup != NULL) free(state->term_backup); + free(state); + connData->cgiData = NULL; + } +} -httpd_cgi_state ICACHE_FLASH_ATTR +static httpd_cgi_state ICACHE_FLASH_ATTR postRecvHdl(HttpdConnData *connData, char *data, int len) { + struct IniUpload *state = connData->cgiData; + if (!state) return HTTPD_CGI_DONE; + + // Discard the boundary marker (there is only one) + char *bdr = connData->post->multipartBoundary; + char *foundat = strstr(data, bdr); + if (foundat != NULL) { + *foundat = '#'; // make it a comment + } + + cgi_dbg("INI parse - postRecvHdl"); + ini_parse(data, (size_t) len); + + cgi_dbg("Closing parser"); ini_parse_end(); + + cgi_dbg("INI parse - end."); + + // abort if bad screen size + bool tooLarge = (termconf->width*termconf->height > MAX_SCREEN_SIZE); + state->term_ok &= !tooLarge; + if (tooLarge) cgi_warn("Bad term screen size!"); + + bool suc = state->term_ok && state->wifi_ok && state->sys_ok; + + cgi_dbg("Evaluating results..."); + + if (!state->term_ok) cgi_warn("Terminal settings rejected."); + if (!state->wifi_ok) cgi_warn("WiFi settings rejected."); + if (!state->sys_ok) cgi_warn("System settings rejected."); + + if (!suc) { + cgi_warn("Some validation failed, reverting all!"); + memcpy(termconf, state->term_backup, sizeof(TerminalConfigBundle)); + memcpy(wificonf, state->wifi_backup, sizeof(WiFiConfigBundle)); + memcpy(sysconf, state->sys_backup, sizeof(SystemConfigBundle)); + } + else { + cgi_dbg("Applying terminal settings"); + terminal_apply_settings(); + cgi_dbg("Applying system settings"); + sysconf_apply_settings(); + cgi_dbg("Applying WiFi settings (scheduling...)"); + wifimgr_apply_settings_later(1000); + + cgi_dbg("Persisting results"); + persist_store(); + } + + cgi_dbg("Redirect"); + char buff[100]; + char *b = buff; + if (suc) { + httpdRedirect(connData, SET_REDIR_SUC"?msg=Settings%20loaded%20and%20applied."); + } else { + b += sprintf(b, SET_REDIR_SUC"?errmsg=Errors%%20in:%%20"); + bool comma = false; + if (!state->sys_ok) { + b += sprintf(b, "System%%20config"); + comma = true; + } + if (!state->wifi_ok) { + if (comma) b += sprintf(b, ",%%20"); + b += sprintf(b, "WiFi%%20config"); + } + if (!state->term_ok) { + if (comma) b += sprintf(b, ",%%20"); + b += sprintf(b, "Terminal%%20config"); + } + httpdRedirect(connData, buff); + } + + // Clean up. + freeIniUploadStruct(connData); return HTTPD_CGI_DONE; } @@ -240,27 +378,59 @@ postRecvHdl(HttpdConnData *connData, char *data, int len) httpd_cgi_state ICACHE_FLASH_ATTR cgiPersistImport(HttpdConnData *connData) { + struct IniUpload *state = NULL; + if (connData->conn == NULL) { //Connection aborted. Clean up. + freeIniUploadStruct(connData); return HTTPD_CGI_DONE; } - httpdStartResponse(connData, 200); - httpdHeader(connData, "Content-Type", "text/plain"); - httpdEndHeaders(connData); - char *start = strstr(connData->post->buff, "\r\n\r\n"); if (start == NULL) { error("Malformed attachment POST!"); - goto end; + + httpdStartResponse(connData, 400); + httpdHeader(connData, "Content-Type", "text/plain"); + httpdEndHeaders(connData); + httpdSend(connData, "Bad format.", -1); + return HTTPD_CGI_DONE; } - ini_parse_begin(iniCb, NULL); - ini_parse(start, (size_t) connData->post->buffLen - (start - connData->post->buff)); + cgi_info("Starting INI parser for uploaded file..."); + + state = malloc(sizeof(struct IniUpload)); + if (!state) { + error("state struct alloc fail"); + return HTTPD_CGI_DONE; + } + state->sys_backup = NULL; + state->wifi_backup = NULL; + state->term_backup = NULL; + connData->cgiData = state; + + cgi_dbg("Allocating backup buffers"); + state->sys_backup = malloc(sizeof(SystemConfigBundle)); + state->wifi_backup = malloc(sizeof(WiFiConfigBundle)); + state->term_backup = malloc(sizeof(TerminalConfigBundle)); + + cgi_dbg("Copying orig data"); + memcpy(state->sys_backup, sysconf, sizeof(SystemConfigBundle)); + memcpy(state->wifi_backup, wificonf, sizeof(WiFiConfigBundle)); + memcpy(state->term_backup, termconf, sizeof(TerminalConfigBundle)); + + state->sys_ok = true; + state->wifi_ok = true; + state->term_ok = true; + + cgi_dbg("Parser starts!"); + ini_parse_begin(iniCb, connData); + + size_t datalen = (size_t) connData->post->buffLen - (start - connData->post->buff); + ini_parse(start, datalen); connData->recvHdl = postRecvHdl; -end: - // TODO redirect - return HTTPD_CGI_DONE; + // continues in recvHdl + return HTTPD_CGI_MORE; } diff --git a/user/config_xmacros.h b/user/config_xmacros.h index 09c639a..3c26f06 100644 --- a/user/config_xmacros.h +++ b/user/config_xmacros.h @@ -84,6 +84,16 @@ enum xset_result xset_ustring(const char *name, u8 *field, const char *buff, con else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \ } +/** used for INI */ +#define XSET_ASSIGN(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ + if (streq(#name, key)) { \ + found = true; \ + type *_p = (type *) &XSTRUCT->name; \ + enum xset_result res = xset(#name, _p, value, (const void*) (xsarg)); \ + if (res == XSET_SET) { xnotify; } \ + else if (res == XSET_FAIL) { suc = false; } \ + } + #define XGET_CGI_FUNC(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \ if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name); diff --git a/user/screen.c b/user/screen.c index 7a9f54f..1853c41 100644 --- a/user/screen.c +++ b/user/screen.c @@ -250,11 +250,11 @@ xset_term_bm(const char *name, char *field, const char *buff, const void *arg) } if (char_i >= TERM_BTN_MSG_LEN-1) { - cgi_warn("Too long! %d", acu); + cgi_warn("Too long! %d", char_i); return XSET_FAIL; } - cgi_dbg("acu %d", acu); +// cgi_dbg("acu %d", acu); buff_bm[char_i++] = (char)acu; // prepare for next char @@ -277,7 +277,7 @@ xset_term_bm(const char *name, char *field, const char *buff, const void *arg) buff_bm[char_i++] = (char)acu; } buff_bm[char_i] = 0; - cgi_dbg("%s, chari = %d", buff_bm, char_i); +// cgi_dbg("%s, chari = %d", buff_bm, char_i); if (!streq(field, buff_bm)) { strncpy(field, buff_bm, TERM_BTN_MSG_LEN); diff --git a/user/serial.c b/user/serial.c index 9c9edce..d7382a2 100644 --- a/user/serial.c +++ b/user/serial.c @@ -42,7 +42,7 @@ buf_pop(void *unused) LOCAL void my_putc(char c) { - UART_WriteCharCRLF(UART1, (u8) c, 10); + UART_WriteCharCRLF(UART1, (u8) c, 200); } /** diff --git a/user/wifimgr.c b/user/wifimgr.c index efb1cd2..7083b3f 100644 --- a/user/wifimgr.c +++ b/user/wifimgr.c @@ -292,6 +292,21 @@ configure_ap(void) } } +static ETSTimer tim; + +static void ICACHE_FLASH_ATTR +wifimgr_apply_settings_later_Cb(void *unused) +{ + wifimgr_apply_settings(); +} + +void ICACHE_FLASH_ATTR +wifimgr_apply_settings_later(uint32_t delay_ms) +{ + wifi_info("[WiFi] Scheduling settings apply in %d ms", delay_ms); + TIMER_START(&tim, wifimgr_apply_settings_later_Cb, delay_ms, 0); +} + /** * Register the WiFi event listener, cycle WiFi, apply settings */ diff --git a/user/wifimgr.h b/user/wifimgr.h index 07d3d61..5637b51 100644 --- a/user/wifimgr.h +++ b/user/wifimgr.h @@ -82,6 +82,7 @@ extern WiFiConfigBundle * const wificonf; void wifimgr_restore_defaults(void); void wifimgr_apply_settings(void); +void wifimgr_apply_settings_later(uint32_t delay_ms); int getStaIpAsString(char *buffer);