Merge branch 'xmacro-config' into work

work
Ondřej Hruška 7 years ago
commit 3e4181f18e
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 5
      CMakeLists.txt
  2. 4
      build_parser.sh
  3. 1
      esphttpdconfig.mk.example
  4. 2
      front-end
  5. 2
      libesphttpd
  6. 3
      prepro.sh
  7. 2
      user/apars_osc.c
  8. 174
      user/cgi_network.c
  9. 360
      user/cgi_persist.c
  10. 2
      user/cgi_persist.h
  11. 4
      user/cgi_sockets.c
  12. 130
      user/cgi_system.c
  13. 487
      user/cgi_term_cfg.c
  14. 201
      user/cgi_wifi.c
  15. 144
      user/config_xmacros.c
  16. 106
      user/config_xmacros.h
  17. 526
      user/ini_parser.c
  18. 65
      user/ini_parser.h
  19. 284
      user/ini_parser.rl
  20. 6
      user/routes.c
  21. 313
      user/screen.c
  22. 77
      user/screen.h
  23. 2
      user/serial.c
  24. 104
      user/syscfg.c
  25. 43
      user/syscfg.h
  26. 193
      user/wifimgr.c
  27. 69
      user/wifimgr.h

@ -146,9 +146,12 @@ set(SOURCE_FILES
user/jstring.c user/jstring.c
user/jstring.h user/jstring.h
user/character_sets.h user/character_sets.h
user/ini_parser.h
user/ini_parser.c
user/ini_parser.rl
user/utf8.h user/utf8.h
user/utf8.c user/utf8.c
user/cgi_logging.h) user/cgi_logging.h user/config_xmacros.h user/config_xmacros.c)
include_directories(include) include_directories(include)
include_directories(libesphttpd/esphttpclient) include_directories(libesphttpd/esphttpclient)

@ -3,6 +3,10 @@
echo "-- Building parser from Ragel source --" echo "-- Building parser from Ragel source --"
ragel -L -G0 user/ansi_parser.rl -o user/ansi_parser.c 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_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 _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

@ -40,6 +40,7 @@ ESP_SPI_FLASH_SIZE_K = 1024
GLOBAL_CFLAGS = \ GLOBAL_CFLAGS = \
-DASYNC_LOG=1 \ -DASYNC_LOG=1 \
-DDEBUG_INI=1 \
-DDEBUG_D2D=0 \ -DDEBUG_D2D=0 \
-DDEBUG_ROUTER=0 \ -DDEBUG_ROUTER=0 \
-DDEBUG_CAPTDNS=0 \ -DDEBUG_CAPTDNS=0 \

@ -1 +1 @@
Subproject commit d2fabc40f1874cabd7f7ceca690a5874887c3c5d Subproject commit a96b522ca899c687d70b9ad92f708cd416b8d460

@ -1 +1 @@
Subproject commit e4ecf0724e36c828be5222eddce58a6a5cd2386f Subproject commit d6651ca0d11b7950078247b8305f5b23a6be6c6c

@ -0,0 +1,3 @@
#!/usr/bin/env bash
xtensa-lx106-elf-gcc -E -Iuser -Ilibesphttpd/include -Iesp_iot_sdk_v1.5.2/include -Iinclude $@

@ -59,7 +59,7 @@ apars_handle_osc(char *buffer)
} }
else if (n >= 91 && n <= 95) { else if (n >= 91 && n <= 95) {
// ESPTerm: action button message // ESPTerm: action button message
strncpy(termconf_live.btn_msg[n - 91], buffer, TERM_BTN_MSG_LEN); screen_set_button_message(n - 90, buffer);
} }
else { else {
ansi_noimpl("OSC %d ; %s ST", n, buffer); ansi_noimpl("OSC %d ; %s ST", n, buffer);

@ -9,6 +9,7 @@ configuring the network settings
#include "persist.h" #include "persist.h"
#include "helpers.h" #include "helpers.h"
#include "cgi_logging.h" #include "cgi_logging.h"
#include "config_xmacros.h"
#define SET_REDIR_SUC "/cfg/network" #define SET_REDIR_SUC "/cfg/network"
#define SET_REDIR_ERR SET_REDIR_SUC"?err=" #define SET_REDIR_ERR SET_REDIR_SUC"?err="
@ -48,138 +49,10 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
// ---- AP DHCP server lease time ---- // ---- AP DHCP server lease time ----
if (GET_ARG("ap_dhcp_time")) { #define XSTRUCT wificonf
cgi_dbg("Setting DHCP lease time to: %s min.", buff); #define X XSET_CGI_FUNC
int min = atoi(buff); XTABLE_WIFICONF
if (min >= 1 && min <= 2880) { #undef X
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,");
}
}
(void) redir_url; (void) redir_url;
@ -216,7 +89,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
//Template code for the WLAN page. //Template code for the WLAN page.
httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg) httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg)
{ {
char buff[20]; char buff[64];
u8 mac[6]; u8 mac[6];
if (token == NULL) { if (token == NULL) {
@ -226,34 +99,13 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke
strcpy(buff, ""); // fallback strcpy(buff, ""); // fallback
if (streq(token, "ap_dhcp_time")) { #define XSTRUCT wificonf
sprintf(buff, "%d", wificonf->ap_dhcp_time); #define X XGET_CGI_FUNC
} XTABLE_WIFICONF
else if (streq(token, "ap_dhcp_start")) { #undef X
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr));
} // non-config
else if (streq(token, "ap_dhcp_end")) { if (streq(token, "sta_mac")) {
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")) {
wifi_get_macaddr(STATION_IF, mac); wifi_get_macaddr(STATION_IF, mac);
sprintf(buff, MACSTR, MAC2STR(mac)); sprintf(buff, MACSTR, MAC2STR(mac));
} }

@ -7,8 +7,13 @@ Cgi/template routines for configuring non-wifi settings
#include "persist.h" #include "persist.h"
#include "helpers.h" #include "helpers.h"
#include "cgi_logging.h" #include "cgi_logging.h"
#include "version.h"
#include "screen.h"
#include "config_xmacros.h"
#include "ini_parser.h"
#define SET_REDIR_SUC "/cfg/system" #define SET_REDIR_SUC "/cfg/system"
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
static bool ICACHE_FLASH_ATTR static bool ICACHE_FLASH_ATTR
verify_admin_pw(const char *pw) verify_admin_pw(const char *pw)
@ -74,3 +79,358 @@ cgiPersistRestoreHard(HttpdConnData *connData)
httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20factory%20defaults."); httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20factory%20defaults.");
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
// -------------- Export --------------
#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);
}
// 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) \
do { if (allow) { \
xget(buff, deref XSTRUCT->name); \
if (streq(#name, "ap_ssid") && streq(buff, defSSID)) break; \
\
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); \
} \
} } while(0);
#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;
}
// -------------- 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)
{
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;
}
}
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;
}
/**
* Import settings from INI
*
* @param connData
* @return status
*/
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;
}
char *start = strstr(connData->post->buff, "\r\n\r\n");
if (start == NULL) {
error("Malformed attachment POST!");
httpdStartResponse(connData, 400);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
httpdSend(connData, "Bad format.", -1);
return HTTPD_CGI_DONE;
}
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;
// continues in recvHdl
return HTTPD_CGI_MORE;
}

@ -6,5 +6,7 @@
httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData);
httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData);
httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData); httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData);
httpd_cgi_state cgiPersistExport(HttpdConnData *connData);
httpd_cgi_state cgiPersistImport(HttpdConnData *connData);
#endif #endif

@ -271,8 +271,8 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int
case 'b': case 'b':
// action button press // action button press
btnNum = (u8) (data[1]); btnNum = (u8) (data[1]);
if (btnNum > 0 && btnNum < 10) { if (btnNum > 0 && btnNum <= TERM_BTN_COUNT) {
UART_SendAsync(termconf_live.btn_msg[btnNum-1], -1); UART_SendAsync(TERM_BM_N(&termconf_live, btnNum-1), -1);
} }
break; break;

@ -95,108 +95,84 @@ cgiSystemCfgSetParams(HttpdConnData *connData)
memcpy(admin_backup, &persist.admin, sizeof(AdminConfigBlock)); memcpy(admin_backup, &persist.admin, sizeof(AdminConfigBlock));
memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle)); memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle));
// flags for the template builder
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 { do {
if (!GET_ARG("pw")) { // Check admin PW
break; // if no PW in GET, not trying to configure anything protected if (GET_ARG("pw")) {
}
if (!streq(buff, persist.admin.pw)) { if (!streq(buff, persist.admin.pw)) {
warn("Bad admin pw!"); warn("Bad admin pw!");
redir_url += sprintf(redir_url, "pw,"); redir_url += sprintf(redir_url, "pw,");
break; break; // Abort
} } else {
admin = true;
// 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;
} }
sysconf->pwlock = (enum pwlock) pwlock;
} }
if (GET_ARG("access_pw")) { // Changing admin PW
cgi_dbg("access_pw: %s", buff); if (admin && GET_ARG("admin_pw")) {
if (strlen(buff)) { 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); strcpy(buff2, buff);
if (!GET_ARG("admin_pw2")) { if (!GET_ARG("admin_pw2")) {
cgi_warn("Missing repeated admin_pw %s", buff); cgi_warn("Missing repeated admin_pw %s", buff);
redir_url += sprintf(redir_url, "admin_pw2,"); redir_url += sprintf(redir_url, "admin_pw2,");
break; break; // Abort
} }
if (!streq(buff, buff2)) { if (!streq(buff, buff2)) {
cgi_warn("Bad repeated admin_pw %s", buff); cgi_warn("Bad repeated admin_pw %s", buff);
redir_url += sprintf(redir_url, "admin_pw2,"); redir_url += sprintf(redir_url, "admin_pw2,");
break; break; // Abort
} }
if (strlen(buff) >= 64) { if (strlen(buff) >= 64) {
cgi_warn("Too long admin_pw %s", buff); cgi_warn("Too long admin_pw %s", buff);
redir_url += sprintf(redir_url, "admin_pw,"); redir_url += sprintf(redir_url, "admin_pw,");
break; break; // Abort
} }
cgi_dbg("Changing admin PW!"); cgi_dbg("Changing admin PW!");
strncpy(persist.admin.pw, buff, 64); strncpy(persist.admin.pw, buff, 64);
break; // this is the only field in this form
} }
} }
} while (0);
if (GET_ARG("overclock")) { // Reject filled but unconfirmed access PW
cgi_dbg("overclock = %s", buff); if (admin && GET_ARG("access_pw")) {
int enable = atoi(buff); if (strlen(buff)) {
if (sysconf->overclock != enable) { cgi_dbg("access_pw: %s", buff);
sysconf->overclock = (bool)enable;
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 XSTRUCT sysconf
#define X XSET_CGI_FUNC
XTABLE_SYSCONF
#undef X
#undef XSTRUCT
} while (0);
(void)redir_url; (void)redir_url;
(void)uart_changed; // unused
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// All was OK // All was OK
@ -236,13 +212,16 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg)
strcpy(buff, ""); // fallback strcpy(buff, ""); // fallback
if (streq(token, "pwlock")) { const bool admin = false;
sprintf(buff, "%d", sysconf->pwlock); const bool tpl=true;
}
else if (streq(token, "access_name")) { #define XSTRUCT sysconf
sprintf(buff, "%s", sysconf->access_name); #define X XGET_CGI_FUNC
} XTABLE_SYSCONF
else if (streq(token, "def_access_name")) { #undef X
#undef XSTRUCT
if (streq(token, "def_access_name")) {
sprintf(buff, "%s", DEF_ACCESS_NAME); sprintf(buff, "%s", DEF_ACCESS_NAME);
} }
else if (streq(token, "def_access_pw")) { else if (streq(token, "def_access_pw")) {
@ -251,9 +230,6 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg)
else if (streq(token, "def_admin_pw")) { else if (streq(token, "def_admin_pw")) {
sprintf(buff, "%s", DEFAULT_ADMIN_PW); sprintf(buff, "%s", DEFAULT_ADMIN_PW);
} }
else if (streq(token, "overclock")) {
sprintf(buff, "%d", sysconf->overclock);
}
tplSend(connData, buff, -1); tplSend(connData, buff, -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;

@ -15,22 +15,6 @@ Cgi/template routines for configuring non-wifi settings
#define SET_REDIR_SUC "/cfg/term" #define SET_REDIR_SUC "/cfg/term"
#define SET_REDIR_ERR SET_REDIR_SUC"?err=" #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. * Universal CGI endpoint to set Terminal params.
*/ */
@ -39,15 +23,11 @@ cgiTermCfgSetParams(HttpdConnData *connData)
{ {
char buff[50]; char buff[50];
char redir_url_buf[100]; char redir_url_buf[100];
int32 n, w, h;
ScreenNotifyTopics topics = 0; ScreenNotifyTopics topics = 0;
bool shall_clear_screen = false;
bool shall_init_uart = false;
char *redir_url = redir_url_buf; char *redir_url = redir_url_buf;
redir_url += sprintf(redir_url, SET_REDIR_ERR); 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)); SystemConfigBundle *sysconf_backup = malloc(sizeof(SystemConfigBundle));
TerminalConfigBundle *termconf_backup = malloc(sizeof(TerminalConfigBundle)); TerminalConfigBundle *termconf_backup = malloc(sizeof(TerminalConfigBundle));
@ -59,328 +39,29 @@ cgiTermCfgSetParams(HttpdConnData *connData)
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
// width and height must always go together so we can do max size validation #define XSTRUCT termconf
if (GET_ARG("term_width")) { #define X XSET_CGI_FUNC
do { XTABLE_TERMCONF
cgi_dbg("Default screen width: %s", buff); #undef X
w = atoi(buff); #undef XSTRUCT
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); // width and height must always go together so we can do max size validation
h = atoi(buff); u32 siz = termconf->width*termconf->height;
if (h < 1) { if (siz == 0 || siz > MAX_SCREEN_SIZE) {
cgi_warn("Bad height: \"%s\"", buff); cgi_warn("Bad dimensions: %d x %d (total %d)", termconf->width, termconf->height, termconf->width*termconf->height);
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,"); 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); // Settings from SysConf - UART
} bool uart_changed = false;
} // restrict what keys are allowed
#define admin 0
if (GET_ARG("uart_baud")) { #define tpl 0
cgi_dbg("Baud rate: %s", buff); #define XSTRUCT sysconf
int baud = atoi(buff); #define X XSET_CGI_FUNC
if (baud == BIT_RATE_300 || XTABLE_SYSCONF
baud == BIT_RATE_600 || #undef X
baud == BIT_RATE_1200 || #undef XSTRUCT
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,");
}
}
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,");
}
}
(void)redir_url; (void)redir_url;
@ -390,17 +71,23 @@ cgiTermCfgSetParams(HttpdConnData *connData)
persist_store(); 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) { if (shall_clear_screen) {
terminal_apply_settings(); terminal_apply_settings();
} else { } else {
terminal_apply_settings_noclear(); terminal_apply_settings_noclear();
} }
if (shall_init_uart) { if (uart_changed) {
serialInit(); 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."); httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
} else { } else {
@ -424,7 +111,6 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
{ {
#define BUFLEN 100 // large enough for backdrop #define BUFLEN 100 // large enough for backdrop
char buff[BUFLEN]; char buff[BUFLEN];
char buff2[10];
if (token == NULL) { if (token == NULL) {
// We're done // We're done
@ -433,109 +119,18 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
strcpy(buff, ""); // fallback strcpy(buff, ""); // fallback
if (streq(token, "term_width")) { #define XSTRUCT termconf
sprintf(buff, "%d", termconf->width); #define X XGET_CGI_FUNC
} XTABLE_TERMCONF
else if (streq(token, "term_height")) { #undef X
sprintf(buff, "%d", termconf->height); #undef XSTRUCT
}
else if (streq(token, "parser_tout_ms")) { // for uart
sprintf(buff, "%d", termconf->parser_tout_ms); #define XSTRUCT sysconf
} #define X XGET_CGI_FUNC
else if (streq(token, "display_tout_ms")) { XTABLE_SYSCONF
sprintf(buff, "%d", termconf->display_tout_ms); #undef X
} #undef XSTRUCT
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;
}
}
}
tplSend(connData, buff, -1); tplSend(connData, buff, -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;

@ -19,6 +19,7 @@ Cgi/template routines for the /wifi url.
#include "wifimgr.h" #include "wifimgr.h"
#include "persist.h" #include "persist.h"
#include "helpers.h" #include "helpers.h"
#include "config_xmacros.h"
#include "cgi_logging.h" #include "cgi_logging.h"
#define SET_REDIR_SUC "/cfg/wifi" #define SET_REDIR_SUC "/cfg/wifi"
@ -66,46 +67,6 @@ int ICACHE_FLASH_ATTR rssi2perc(int rssi)
return r; 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 * Callback the code calls when a wlan ap scan is done. Basically stores the result in
* the static cgiWifiAps struct. * the static cgiWifiAps struct.
@ -363,18 +324,14 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
bool sta_turned_on = false; bool sta_turned_on = false;
bool sta_ssid_pw_changed = false; bool sta_ssid_pw_changed = false;
#define XSTRUCT wificonf
#define X XSET_CGI_FUNC
XTABLE_WIFICONF
#undef X
// ---- WiFi opmode ---- // ---- WiFi opmode ----
if (GET_ARG("opmode")) { // those are helpers, not a real prop
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,");
}
}
if (GET_ARG("ap_enable")) { if (GET_ARG("ap_enable")) {
cgi_dbg("Enable AP: %s", buff); cgi_dbg("Enable AP: %s", buff);
@ -399,114 +356,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; (void)redir_url;
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
@ -552,7 +401,6 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
//Template code for the WLAN page. //Template code for the WLAN page.
httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg)
{ {
@ -567,39 +415,18 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
strcpy(buff, ""); // fallback strcpy(buff, ""); // fallback
if (streq(token, "opmode_name")) { #define XSTRUCT wificonf
strcpy(buff, opmode2str(wificonf->opmode)); #define X XGET_CGI_FUNC
} XTABLE_WIFICONF
else if (streq(token, "opmode")) { #undef X
sprintf(buff, "%d", wificonf->opmode);
} // non-config
else if (streq(token, "sta_enable")) { if (streq(token, "sta_enable")) {
sprintf(buff, "%d", (wificonf->opmode & STATION_MODE) != 0); sprintf(buff, "%d", (wificonf->opmode & STATION_MODE) != 0);
} }
else if (streq(token, "ap_enable")) { else if (streq(token, "ap_enable")) {
sprintf(buff, "%d", (wificonf->opmode & SOFTAP_MODE) != 0); 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")) { else if (streq(token, "sta_rssi")) {
sprintf(buff, "%d", wifi_station_get_rssi()); sprintf(buff, "%d", wifi_station_get_rssi());
} }

@ -0,0 +1,144 @@
//
// 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_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, 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;
}
enum xset_result ICACHE_FLASH_ATTR
xset_ustring(const char *name, uchar *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;
}

@ -0,0 +1,106 @@
//
// Created by MightyPork on 2017/10/22.
//
#ifndef ESPTERM_CONFIG_XMACROS_H
#define ESPTERM_CONFIG_XMACROS_H
#include <esp8266.h>
#include <helpers.h>
typedef unsigned char uchar;
#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);
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);
// 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
*
* 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, xset, xsarg, xnotify, allow) \
if ((allow) && GET_ARG(#name)) { \
type *_p = (type *) &XSTRUCT->name; \
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","); } \
}
/** 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);
#define XSTRUCT_FIELD(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
type name suffix;
#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

@ -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" */
}

@ -0,0 +1,65 @@
#ifndef INIPARSE_STREAM_H
#define INIPARSE_STREAM_H
#include <esp8266.h>
#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

@ -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;
#*/
}%%
}

@ -23,8 +23,8 @@
static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen)
{ {
if (no == 0) { if (no == 0) {
os_strcpy(user, sysconf->access_name); os_strcpy(user, (const char *) sysconf->access_name);
os_strcpy(pass, sysconf->access_pw); os_strcpy(pass, (const char *) sysconf->access_pw);
return 1; return 1;
} }
if (no == 1) { if (no == 1) {
@ -131,6 +131,8 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"), ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"),
ROUTE_CGI("/cfg/system/set", cgiSystemCfgSetParams), 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/write_defaults", cgiPersistWriteDefaults),
ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults), ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults),
ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard), ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard),

@ -8,6 +8,7 @@
#include "character_sets.h" #include "character_sets.h"
#include "utf8.h" #include "utf8.h"
#include "cgi_sockets.h" #include "cgi_sockets.h"
#include "cgi_logging.h"
TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle * const termconf = &persist.current.termconf;
TerminalConfigBundle termconf_live; TerminalConfigBundle termconf_live;
@ -106,8 +107,19 @@ bool cursor_saved = false;
static struct { static struct {
bool alternate_active; bool alternate_active;
char title[TERM_TITLE_LEN]; 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 width;
u32 height; u32 height;
int vm0; int vm0;
@ -143,46 +155,194 @@ static struct {
int x_min, y_min, x_max, y_max; int x_min, y_min, x_max, y_max;
} scr_dirty; } scr_dirty;
#define reset_screen_dirty() do { \ static void ICACHE_FLASH_ATTR reset_screen_dirty(void)
scr_dirty.x_min = W; \ {
scr_dirty.x_max = -1; \ scr_dirty.x_min = W;
scr_dirty.y_min = H; \ scr_dirty.x_max = -1;
scr_dirty.y_max = -1; \ scr_dirty.y_min = H;
} while(0) scr_dirty.y_max = -1;
}
#define expand_dirty(y0, y1, x0, x1) do { \ 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 ((int)(y0) < scr_dirty.y_min) scr_dirty.y_min = (y0); \ seri_dbg("Expand: X: (%d..%d) -> %d..%d, Y: (%d..%d) -> %d..%d",
if ((int)(x0) < scr_dirty.x_min) scr_dirty.x_min = (x0); \ scr_dirty.x_min, scr_dirty.x_max, x0, x1, scr_dirty.y_min, scr_dirty.y_max, y0, y1);
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)
#define NOTIFY_LOCK() do { \ if ((y0) < scr_dirty.y_min) scr_dirty.y_min = (y0);
notifyLock++; \ if ((x0) < scr_dirty.x_min) scr_dirty.x_min = (x0);
} while(0) if ((y1) > scr_dirty.y_max) scr_dirty.y_max = (y1);
if ((x1) > scr_dirty.x_max) scr_dirty.x_max = (x1);
}
#define NOTIFY_DONE(updateTopics) do { \ #define NOTIFY_LOCK() { notifyLock++; }
lockTopics |= (updateTopics); \
if (notifyLock > 0) notifyLock--; \ static void ICACHE_FLASH_ATTR NOTIFY_DONE(u32 updateTopics)
if (notifyLock == 0) { \ {
screen_notifyChange(lockTopics); \ lockTopics |= (updateTopics);
lockTopics = 0;\ if (notifyLock > 0) notifyLock--;
} \ if (notifyLock == 0) {
} while(0) screen_notifyChange(lockTopics);
lockTopics = 0;
}
}
/** Clear the hanging attribute if the cursor is no longer >= W */ /** Clear the hanging attribute if the cursor is no longer >= W */
#define clear_invalid_hanging() do { \ static void ICACHE_FLASH_ATTR clear_invalid_hanging(void)
if (cursor.hanging && cursor.x != W-1) { \ {
cursor.hanging = false; \ if (cursor.hanging && cursor.x != W-1) {
screen_notifyChange(TOPIC_CHANGE_CURSOR); \ cursor.hanging = false;
} \ screen_notifyChange(TOPIC_CHANGE_CURSOR);
} while(false) }
}
#define cursor_inside_region() (cursor.y >= TOP && cursor.y <= BTM) #define cursor_inside_region() (cursor.y >= TOP && cursor.y <= BTM)
//region --- Settings --- //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)
{
char c;
char *bp = buff;
char *cp = value;
int n = 0;
while((c = *cp++) != 0) {
if(n>0) {
*bp = ',';
bp++;
}
bp += sprintf(bp, "%d", (u8)c);
n++;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_term_bm(const char *name, char *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", char_i);
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(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 * Restore hard defaults
*/ */
@ -194,10 +354,19 @@ terminal_restore_defaults(void)
termconf->default_bg = 0; termconf->default_bg = 0;
termconf->default_fg = 7; termconf->default_fg = 7;
sprintf(termconf->title, SCR_DEF_TITLE); sprintf(termconf->title, SCR_DEF_TITLE);
for(int i=1; i <= TERM_BTN_COUNT; i++) {
sprintf(termconf->btn[i-1], "%d", i); strcpy(termconf->btn1, "1");
sprintf(termconf->btn_msg[i-1], "%c", i); strcpy(termconf->btn2, "2");
} strcpy(termconf->btn3, "3");
strcpy(termconf->btn4, "4");
strcpy(termconf->btn5, "5");
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->theme = 0;
termconf->parser_tout_ms = SCR_DEF_PARSER_TOUT_MS; termconf->parser_tout_ms = SCR_DEF_PARSER_TOUT_MS;
termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS; termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS;
@ -232,6 +401,13 @@ terminal_apply_settings_noclear(void)
{ {
bool changed = false; bool changed = false;
// char buff[64];
//#define XSTRUCT termconf
//#define X XDUMP_FIELD
// XTABLE_TERMCONF
//#undef X
// return;
// Migrate // Migrate
if (termconf->config_version < 1) { if (termconf->config_version < 1) {
persist_dbg("termconf: Updating to version %d", 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.title, termconf->title);
strcpy(termconf_live.backdrop, termconf->backdrop); strcpy(termconf_live.backdrop, termconf->backdrop);
for (int i = 1; i <= TERM_BTN_COUNT; i++) { strcpy(termconf_live.btn1, termconf->btn1);
strcpy(termconf_live.btn[i], termconf->btn[i]); strcpy(termconf_live.btn2, termconf->btn2);
strcpy(termconf_live.btn_msg[i], termconf->btn_msg[i]); 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_buttons = termconf->show_buttons;
termconf_live.show_config_links = termconf->show_config_links; termconf_live.show_config_links = termconf->show_config_links;
@ -452,8 +629,11 @@ screen_swap_state(bool alternate)
ansi_dbg("Swap to alternate"); ansi_dbg("Swap to alternate");
// store old state // store old state
memcpy(state_backup.title, termconf_live.title, TERM_TITLE_LEN); 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)); memcpy(state_backup.tab_stops, scr.tab_stops, sizeof(scr.tab_stops));
state_backup.vm0 = scr.vm0; state_backup.vm0 = scr.vm0;
state_backup.vm1 = scr.vm1; state_backup.vm1 = scr.vm1;
@ -465,8 +645,11 @@ screen_swap_state(bool alternate)
else { else {
ansi_dbg("Unswap from alternate"); ansi_dbg("Unswap from alternate");
memcpy(termconf_live.title, state_backup.title, TERM_TITLE_LEN); 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)); memcpy(scr.tab_stops, state_backup.tab_stops, sizeof(scr.tab_stops));
scr.vm0 = state_backup.vm0; scr.vm0 = state_backup.vm0;
scr.vm1 = state_backup.vm1; scr.vm1 = state_backup.vm1;
@ -989,8 +1172,8 @@ screen_resize(int rows, int cols)
if (W == cols && H == rows) return; // Do nothing if (W == cols && H == rows) return; // Do nothing
NOTIFY_LOCK(); NOTIFY_LOCK();
W = cols; W = (u32) cols;
H = rows; H = (u32) rows;
screen_reset_on_resize(); screen_reset_on_resize();
NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS|TOPIC_CHANGE_CONTENT_ALL|TOPIC_CHANGE_CURSOR); 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) screen_set_button_text(int num, const char *text)
{ {
NOTIFY_LOCK(); 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); NOTIFY_DONE(TOPIC_CHANGE_BUTTONS);
} }
@ -1121,7 +1325,7 @@ void ICACHE_FLASH_ATTR
screen_cursor_shape(enum CursorShape shape) screen_cursor_shape(enum CursorShape shape)
{ {
NOTIFY_LOCK(); 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; termconf_live.cursor_shape = shape;
NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS); NOTIFY_DONE(TOPIC_CHANGE_SCREEN_OPTS);
} }
@ -1356,7 +1560,7 @@ screen_back_index(int count)
cursor.x = new_x; cursor.x = new_x;
} else { } else {
cursor.x = 0; cursor.x = 0;
screen_insert_characters(-new_x); screen_insert_characters((unsigned int) -new_x);
topics |= TOPIC_CHANGE_CONTENT_PART; topics |= TOPIC_CHANGE_CONTENT_PART;
} }
NOTIFY_DONE(topics); NOTIFY_DONE(topics);
@ -1548,7 +1752,7 @@ do_save_private_opt(int n, bool save)
{ {
ScreenNotifyTopics topics = TOPIC_INTERNAL; ScreenNotifyTopics topics = TOPIC_INTERNAL;
if (!save) NOTIFY_LOCK(); 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) { switch (n) {
case 1: case 1:
SAVE_RESTORE(opt_backup.cursors_alt_mode, scr.cursors_alt_mode); 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) BEGIN_TOPIC(TOPIC_CHANGE_TITLE, TERM_TITLE_LEN+4+1)
bufput_c(TOPICMARK_TITLE); bufput_c(TOPICMARK_TITLE);
int len = (int) strlen(termconf_live.title); size_t len = strlen(termconf_live.title);
if (len > 0) { if (len > 0) {
memcpy(bb, termconf_live.title, len); memcpy(bb, termconf_live.title, len);
bb += len; bb += len;
@ -2059,20 +2263,21 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics,
bufput_utf8(TERM_BTN_COUNT); bufput_utf8(TERM_BTN_COUNT);
for (int i = 0; i < TERM_BTN_COUNT; i++) { 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) { if (len > 0) {
memcpy(bb, termconf_live.btn[i], len); memcpy(bb, TERM_BTN_N(&termconf_live, i), len);
bb += len; bb += len;
remain -= len; remain -= len;
} }
bufput_c('\x01'); bufput_c('\x01');
} }
END_TOPIC END_TOPIC
BEGIN_TOPIC(TOPIC_CHANGE_BACKDROP, TERM_BACKDROP_LEN+1+1) BEGIN_TOPIC(TOPIC_CHANGE_BACKDROP, TERM_BACKDROP_LEN+1+1)
bufput_c(TOPICMARK_BACKDROP); bufput_c(TOPICMARK_BACKDROP);
int len = (int) strlen(termconf_live.backdrop); size_t len = strlen(termconf_live.backdrop);
if (len > 0) { if (len > 0) {
memcpy(bb, termconf_live.backdrop, len); memcpy(bb, termconf_live.backdrop, len);
bb += len; bb += len;

@ -5,6 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <esp8266.h> #include <esp8266.h>
#include <httpd.h> #include <httpd.h>
#include "config_xmacros.h"
/** /**
* This module handles the virtual screen and operations on it. * This module handles the virtual screen and operations on it.
@ -78,30 +79,59 @@ enum CursorShape {
#define TERMCONF_SIZE 400 #define TERMCONF_SIZE 400
#define TERMCONF_VERSION 4 #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, 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))
/** 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, 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 */
enum xset_result xset_term_cursorshape(const char *name, u32 *field, const char *buff, const void *arg);
typedef struct { typedef struct {
u32 width; #define X XSTRUCT_FIELD
u32 height; XTABLE_TERMCONF
u32 default_bg; // 00-FFh - ANSI colors, (00:00:00-FF:FF:FF)+256 - True Color #undef X
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];
} TerminalConfigBundle; } TerminalConfigBundle;
// Live config // Live config
@ -150,6 +180,7 @@ void screen_resize(int rows, int cols);
void screen_set_title(const char *title); void screen_set_title(const char *title);
/** Set a button text */ /** Set a button text */
void screen_set_button_text(int num, const char *text); void screen_set_button_text(int num, const char *text);
void screen_set_button_message(int num, const char *msg);
/** Change backdrop */ /** Change backdrop */
void screen_set_backdrop(const char *url); void screen_set_backdrop(const char *url);

@ -42,7 +42,7 @@ buf_pop(void *unused)
LOCAL void my_putc(char c) LOCAL void my_putc(char c)
{ {
UART_WriteCharCRLF(UART1, (u8) c, 10); UART_WriteCharCRLF(UART1, (u8) c, 200);
} }
/** /**

@ -6,12 +6,112 @@
#include "persist.h" #include "persist.h"
#include "uart_driver.h" #include "uart_driver.h"
#include "serial.h" #include "serial.h"
#include "cgi_logging.h"
SystemConfigBundle * const sysconf = &persist.current.sysconf; 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, uchar *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 void ICACHE_FLASH_ATTR
sysconf_apply_settings(void) sysconf_apply_settings(void)
{ {
// char buff[64];
//#define XSTRUCT sysconf
//#define X XDUMP_FIELD
// XTABLE_SYSCONF
//#undef X
bool changed = false; bool changed = false;
if (sysconf->config_version < 1) { if (sysconf->config_version < 1) {
dbg("Upgrading syscfg to v 1"); dbg("Upgrading syscfg to v 1");
@ -40,7 +140,7 @@ sysconf_restore_defaults(void)
sysconf->config_version = SYSCONF_VERSION; sysconf->config_version = SYSCONF_VERSION;
sysconf->access_pw[0] = 0; sysconf->access_pw[0] = 0;
sysconf->pwlock = PWLOCK_NONE; sysconf->pwlock = PWLOCK_NONE;
strcpy(sysconf->access_pw, DEF_ACCESS_PW); strcpy((char *)sysconf->access_pw, DEF_ACCESS_PW);
strcpy(sysconf->access_name, DEF_ACCESS_NAME); strcpy((char *)sysconf->access_name, DEF_ACCESS_NAME);
sysconf->overclock = false; sysconf->overclock = false;
} }

@ -6,6 +6,7 @@
#define ESP_VT100_FIRMWARE_SYSCFG_H #define ESP_VT100_FIRMWARE_SYSCFG_H
#include <esp8266.h> #include <esp8266.h>
#include "config_xmacros.h"
// Size designed for the wifi config structure // Size designed for the wifi config structure
// Must be constant to avoid corrupting user config after upgrade // Must be constant to avoid corrupting user config after upgrade
@ -24,15 +25,35 @@ enum pwlock {
PWLOCK_MAX = 5, PWLOCK_MAX = 5,
}; };
//....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) \
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, 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) \
typedef struct { typedef struct {
u32 uart_baudrate; #define X XSTRUCT_FIELD
u8 uart_parity; XTABLE_SYSCONF
u8 uart_stopbits; #undef X
u8 config_version;
enum pwlock pwlock : 8; // page access lock // u32 uart_baudrate;
char access_pw[64]; // access password // u8 uart_parity;
char access_name[32]; // access name // u8 uart_stopbits;
bool overclock; // 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; } SystemConfigBundle;
extern SystemConfigBundle * const sysconf; extern SystemConfigBundle * const sysconf;
@ -41,4 +62,10 @@ void sysconf_apply_settings(void);
void sysconf_restore_defaults(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, uchar *field, const char *buff, const void *arg);
#endif //ESP_VT100_FIRMWARE_SYSCFG_H #endif //ESP_VT100_FIRMWARE_SYSCFG_H

@ -4,10 +4,127 @@
#include "wifimgr.h" #include "wifimgr.h"
#include "persist.h" #include "persist.h"
#include "cgi_logging.h"
#include "config_xmacros.h"
WiFiConfigBundle * const wificonf = &persist.current.wificonf; WiFiConfigBundle * const wificonf = &persist.current.wificonf;
WiFiConfChangeFlags wifi_change_flags; 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, uchar *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, 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);
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad password len.");
return XSET_FAIL;
}
}
int ICACHE_FLASH_ATTR getStaIpAsString(char *buffer) int ICACHE_FLASH_ATTR getStaIpAsString(char *buffer)
{ {
WIFI_MODE x = wifi_get_opmode(); 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_password[0] = 0; // PSK2 always if password is not null.
wificonf->ap_hidden = false; wificonf->ap_hidden = false;
IP4_ADDR(&wificonf->ap_addr.ip, 192, 168, 4, 1); IP4_ADDR(&wificonf->ap_addr_ip, 192, 168, 4, 1);
IP4_ADDR(&wificonf->ap_addr.netmask, 255, 255, 255, 0); IP4_ADDR(&wificonf->ap_addr_mask, 255, 255, 255, 0);
wificonf->ap_addr.gw.addr = wificonf->ap_addr.gw.addr;
IP4_ADDR(&wificonf->ap_dhcp_range.start_ip, 192, 168, 4, 100); IP4_ADDR(&wificonf->ap_dhcp_start, 192, 168, 4, 100);
IP4_ADDR(&wificonf->ap_dhcp_range.end_ip, 192, 168, 4, 200); IP4_ADDR(&wificonf->ap_dhcp_end, 192, 168, 4, 200);
wificonf->ap_dhcp_range.enable = 1; // this will never get changed, idk why it's even there
wificonf->ap_dhcp_time = 120; wificonf->ap_dhcp_time = 120;
// --- Client config --- // --- Client config ---
@ -56,9 +171,9 @@ wifimgr_restore_defaults(void)
wificonf->sta_password[0] = 0; wificonf->sta_password[0] = 0;
wificonf->sta_dhcp_enable = true; 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_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_mask, 255, 255, 255, 0);
IP4_ADDR(&wificonf->sta_addr.gw, 192, 168, 0, 1); IP4_ADDR(&wificonf->sta_addr_gw, 192, 168, 0, 1); // a common default...
} }
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
@ -83,13 +198,18 @@ configure_station(void)
} }
else { else {
wifi_info("[WiFi] Setting up static IP..."); wifi_info("[WiFi] Setting up static IP...");
wifi_dbg("[WiFi] Client.ip = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.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.netmask.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_dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_gw.addr));
wifi_station_dhcpc_stop(); wifi_station_dhcpc_stop();
// Load static IP config // 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!"); error("[WiFi] Error setting static IP!");
return; return;
} }
@ -112,7 +232,7 @@ configure_ap(void)
strcpy((char *) conf.password, (char *) wificonf->ap_password); strcpy((char *) conf.password, (char *) wificonf->ap_password);
conf.authmode = (wificonf->ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK); conf.authmode = (wificonf->ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK);
conf.ssid_len = (uint8_t) strlen((char *) conf.ssid); 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.max_connection = 4; // default 4 (max possible)
conf.beacon_interval = 100; // default 100 ms conf.beacon_interval = 100; // default 100 ms
@ -127,24 +247,32 @@ configure_ap(void)
// Set IP // Set IP
wifi_info("[WiFi] Configuring SoftAP local 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.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.mask = "IPSTR, GOOD_IP2STR(wificonf->ap_addr_mask.addr));
wifi_dbg("[WiFi] SoftAP.gw = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.gw.addr));
wifi_softap_dhcps_stop(); wifi_softap_dhcps_stop();
// Configure DHCP // 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!"); error("[WiFi] IP set fail!");
return; return;
} }
wifi_info("[WiFi] Configuring SoftAP DHCP server..."); 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.start = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_start.addr));
wifi_dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.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); 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!"); error("[WiFi] DHCP address range set fail!");
return; return;
} }
@ -164,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 * Register the WiFi event listener, cycle WiFi, apply settings
*/ */
@ -172,6 +315,12 @@ wifimgr_apply_settings(void)
{ {
wifi_info("[WiFi] Initializing..."); wifi_info("[WiFi] Initializing...");
// char buff[64];
//#define XSTRUCT wificonf
//#define X XDUMP_FIELD
// XTABLE_WIFICONF
//#undef X
// !!! Update to current version !!! // !!! Update to current version !!!
// Force wifi cycle // Force wifi cycle
@ -190,7 +339,7 @@ wifimgr_apply_settings(void)
} }
if (opmode != wificonf->opmode) { if (opmode != wificonf->opmode) {
wifi_set_opmode_current(wificonf->opmode); wifi_set_opmode_current((WIFI_MODE) wificonf->opmode);
} }
// Configure the client // Configure the client

@ -8,6 +8,7 @@
#define ESP_VT100_FIRMWARE_WIFI_MANAGER_H #define ESP_VT100_FIRMWARE_WIFI_MANAGER_H
#include <esp8266.h> #include <esp8266.h>
#include "config_xmacros.h"
#include "cgi_wifi.h" #include "cgi_wifi.h"
#define SSID_LEN 32 #define SSID_LEN 32
@ -19,6 +20,44 @@
#define WIFICONF_VERSION 0 #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...........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_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(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(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(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(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
/** /**
* A structure holding all configured WiFi parameters * A structure holding all configured WiFi parameters
* and the active state. * and the active state.
@ -26,25 +65,9 @@
* This block can be used eg. for WiFi config backup. * This block can be used eg. for WiFi config backup.
*/ */
typedef struct { typedef struct {
WIFI_MODE opmode : 8; #define X XSTRUCT_FIELD
u8 tpw; XTABLE_WIFICONF
#undef X
// 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;
} WiFiConfigBundle; } WiFiConfigBundle;
typedef struct { typedef struct {
@ -59,9 +82,17 @@ extern WiFiConfigBundle * const wificonf;
void wifimgr_restore_defaults(void); void wifimgr_restore_defaults(void);
void wifimgr_apply_settings(void); void wifimgr_apply_settings(void);
void wifimgr_apply_settings_later(uint32_t delay_ms);
int getStaIpAsString(char *buffer); 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, 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 #if DEBUG_WIFI
#define wifi_warn warn #define wifi_warn warn
#define wifi_dbg dbg #define wifi_dbg dbg

Loading…
Cancel
Save