diff --git a/app/libc/c_stdio.c b/app/libc/c_stdio.c index e52f2ffc..36aae1ce 100644 --- a/app/libc/c_stdio.c +++ b/app/libc/c_stdio.c @@ -1094,12 +1094,14 @@ exponent(char *p, int exp, int fmtch) #endif /* FLOATINGPT */ -void c_sprintf(char *s, char *fmt, ...) +int c_sprintf(char *s, const char *fmt, ...) { + int n; va_list arg; va_start(arg, fmt); - vsprintf(s, fmt, arg); + n = vsprintf(s, fmt, arg); va_end(arg); + return n; } #endif diff --git a/app/libc/c_stdio.h b/app/libc/c_stdio.h index c652f0bf..631f5c3a 100644 --- a/app/libc/c_stdio.h +++ b/app/libc/c_stdio.h @@ -60,7 +60,7 @@ extern void output_redirect(const char *str); #define c_sprintf os_sprintf #else #include "c_stdarg.h" -void c_sprintf(char* s,char *fmt, ...); +int c_sprintf(char* s,const char *fmt, ...); #endif // #define c_vsprintf ets_vsprintf diff --git a/app/modules/enduser_setup.c b/app/modules/enduser_setup.c index 5a40bc3b..a425d913 100644 --- a/app/modules/enduser_setup.c +++ b/app/modules/enduser_setup.c @@ -29,12 +29,15 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. * * @author Robert Foss + * + * Additions & fixes: Johny Mattsson */ #include "module.h" #include "lauxlib.h" #include "platform.h" #include "c_stdlib.h" +#include "c_stdio.h" #include "c_string.h" #include "ctype.h" #include "user_interface.h" @@ -42,6 +45,7 @@ #include "flash_fs.h" #define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define LITLEN(strliteral) (sizeof (strliteral) -1) #define ENDUSER_SETUP_ERR_FATAL (1 << 0) #define ENDUSER_SETUP_ERR_NONFATAL (1 << 1) @@ -70,14 +74,163 @@ static const char dns_body[] = { 0x00, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04 }; static const char http_html_filename[] = "index.html"; -static const char http_header_200[] = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"; -static const char http_header_404[] = "HTTP/1.1 404 Not Found\r\n"; -static const char http_html_backup[] = "Connect gadget to you WiFi

WiFi Login

Connect gadget to your WiFi

"; +static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nContent-Type: text/html\r\n"; /* Note single \r\n here! */ +static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\n\r\n"; +static const char http_header_401[] = "HTTP/1.1 401 Bad request\r\n\r\n"; +static const char http_header_404[] = "HTTP/1.1 404 Not found\r\n\r\n"; +static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\n\r\n"; +/* The below is the un-minified version of the http_html_backup[] string. + * Minified using https://kangax.github.io/html-minifier/ + * Note: using method="get" due to iOS not always sending body in same + * packet as the HTTP header, and us thus missing it in that case + */ +#if 0 + + + + + + + WiFi Login + + + +
+
+
+

WiFi Login

+

Connect gadget to your WiFi network

+ + + + + +

Status: Updating...

+
+
+
+ + + +#endif +static const char http_html_backup[] = +"WiFi Login

WiFi Login

Connect gadget to your WiFi network

Status: Updating...

"; + +typedef struct scan_listener +{ + struct scan_listener *next; + int remote_port; + uint8 remote_ip[4]; +} scan_listener_t; typedef struct { - lua_State *lua_L; struct espconn *espconn_dns_udp; struct espconn *espconn_http_tcp; char *http_payload_data; @@ -86,9 +239,11 @@ typedef struct int lua_connected_cb_ref; int lua_err_cb_ref; int lua_dbg_cb_ref; + scan_listener_t *scan_listeners; } enduser_setup_state_t; static enduser_setup_state_t *state; +static bool manual = false; static int enduser_setup_start(lua_State* L); static int enduser_setup_stop(lua_State* L); @@ -104,7 +259,7 @@ static void enduser_setup_debug(lua_State *L, const char *str); #if ENDUSER_SETUP_DEBUG_ENABLE #define ENDUSER_SETUP_DEBUG(l, str) enduser_setup_debug(l, str) #else -#define ENDUSER_SETUP_DEBUG(l, str) +#define ENDUSER_SETUP_DEBUG(l, str) do {} while(0) #endif @@ -119,13 +274,21 @@ static void enduser_setup_debug(lua_State *L, const char *str) } -#define ENDUSER_SETUP_ERROR(str, err, err_severity) if (err_severity & ENDUSER_SETUP_ERR_FATAL) enduser_setup_stop(state->lua_L);\ - enduser_setup_error(state->lua_L, str, err);\ - if (!(err_severity & ENDUSER_SETUP_ERR_NO_RETURN)) return err +#define ENDUSER_SETUP_ERROR(str, err, err_severity) \ + do { \ + if (err_severity & ENDUSER_SETUP_ERR_FATAL) enduser_setup_stop(lua_getstate());\ + enduser_setup_error(lua_getstate(), str, err);\ + if (!(err_severity & ENDUSER_SETUP_ERR_NO_RETURN)) \ + return err; \ + } while (0) -#define ENDUSER_SETUP_ERROR_VOID(str, err, err_severity) if (err_severity & ENDUSER_SETUP_ERR_FATAL) enduser_setup_stop(state->lua_L);\ - enduser_setup_error(state->lua_L, str, err);\ - if (!(err_severity & ENDUSER_SETUP_ERR_NO_RETURN)) return +#define ENDUSER_SETUP_ERROR_VOID(str, err, err_severity) \ + do { \ + if (err_severity & ENDUSER_SETUP_ERR_FATAL) enduser_setup_stop(lua_getstate());\ + enduser_setup_error(lua_getstate(), str, err);\ + if (!(err_severity & ENDUSER_SETUP_ERR_NO_RETURN)) \ + return; \ + } while (0) static void enduser_setup_error(lua_State *L, const char *str, int err) @@ -136,7 +299,7 @@ static void enduser_setup_error(lua_State *L, const char *str, int err) { lua_rawgeti (L, LUA_REGISTRYINDEX, state->lua_err_cb_ref); lua_pushnumber(L, err); - lua_pushstring(L, str); + lua_pushfstring(L, "enduser_setup: %s", str); lua_call (L, 2, 0); } } @@ -156,8 +319,7 @@ static void enduser_setup_connected_callback(lua_State *L) static void enduser_setup_check_station_start(void) { - - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_check_station_start"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_check_station_start"); os_timer_setfn(&(state->check_station_timer), enduser_setup_check_station, NULL); os_timer_arm(&(state->check_station_timer), 1*1000, TRUE); @@ -166,7 +328,7 @@ static void enduser_setup_check_station_start(void) static void enduser_setup_check_station_stop(void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_check_station_stop"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_check_station_stop"); os_timer_disarm(&(state->check_station_timer)); } @@ -197,10 +359,7 @@ static void enduser_setup_check_station(void *p) return; } - struct station_config cnf; - wifi_station_get_config(&cnf); - - enduser_setup_connected_callback(state->lua_L); + enduser_setup_connected_callback(lua_getstate()); enduser_setup_stop(NULL); } @@ -214,25 +373,11 @@ static void enduser_setup_check_station(void *p) */ static int enduser_setup_srch_str(const char *str, const char *srch_str) { - int srch_str_len = c_strlen(srch_str); - int first_hit = INT_MAX; - - int i; - for (i = 0; i < srch_str_len; ++i) - { - char *char_ptr = strchr(str, srch_str[i]); - if (char_ptr == NULL) - { - continue; - } - int char_idx = char_ptr - str; - first_hit = MIN(first_hit, char_idx); - } - if (first_hit == INT_MAX) - { + char *found = strpbrk (str, srch_str); + if (!found) return -1; - } - return first_hit; + else + return found - str; } @@ -245,18 +390,21 @@ static int enduser_setup_srch_str(const char *str, const char *srch_str) */ static int enduser_setup_http_load_payload(void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_load_payload"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_load_payload"); int f = fs_open(http_html_filename, fs_mode2flag("r")); int err = fs_seek(f, 0, FS_SEEK_END); int file_len = (int) fs_tell(f); int err2 = fs_seek(f, 0, FS_SEEK_SET); + const char cl_hdr[] = "Content-length:%5d\r\n\r\n"; + const size_t cl_len = LITLEN(cl_hdr) + 3; /* room to expand %4d */ + if (f == 0 || err == -1 || err2 == -1) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_load_payload unable to load file index.html, loading backup HTML."); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_load_payload unable to load file index.html, loading backup HTML."); - int payload_len = sizeof(http_header_200) + sizeof(http_html_backup); + int payload_len = LITLEN(http_header_200) + cl_len + LITLEN(http_html_backup); state->http_payload_len = payload_len; state->http_payload_data = (char *) c_malloc(payload_len); if (state->http_payload_data == NULL) @@ -265,14 +413,15 @@ static int enduser_setup_http_load_payload(void) } int offset = 0; - c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), sizeof(http_header_200)); - offset += sizeof(http_header_200); - c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), sizeof(http_html_backup)); + c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200)); + offset += LITLEN(http_header_200); + offset += c_sprintf(state->http_payload_data + offset, cl_hdr, LITLEN(http_html_backup)); + c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), LITLEN(http_html_backup)); return 1; } - int payload_len = sizeof(http_header_200) + file_len; + int payload_len = LITLEN(http_header_200) + cl_len + file_len; state->http_payload_len = payload_len; state->http_payload_data = (char *) c_malloc(payload_len); if (state->http_payload_data == NULL) @@ -281,8 +430,9 @@ static int enduser_setup_http_load_payload(void) } int offset = 0; - c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), sizeof(http_header_200)); - offset += sizeof(http_header_200); + c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200)); + offset += LITLEN(http_header_200); + offset += c_sprintf(state->http_payload_data + offset, cl_hdr, file_len); fs_read(f, &(state->http_payload_data[offset]), file_len); return 0; @@ -351,7 +501,7 @@ static void enduser_setup_http_urldecode(char *dst, const char *src, int src_len */ static int enduser_setup_http_handle_credentials(char *data, unsigned short data_len) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_handle_credentials"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_handle_credentials"); char *name_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_ssid=")); char *pwd_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_password=")); @@ -360,8 +510,8 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data return 1; } - int name_field_len = sizeof("wifi_ssid=") - 1; - int pwd_field_len = sizeof("wifi_password=") - 1; + int name_field_len = LITLEN("wifi_ssid="); + int pwd_field_len = LITLEN("wifi_password="); char *name_str_start = name_str + name_field_len; char *pwd_str_start = pwd_str + pwd_field_len; @@ -377,36 +527,26 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data enduser_setup_http_urldecode(cnf.ssid, name_str_start, name_str_len); enduser_setup_http_urldecode(cnf.password, pwd_str_start, pwd_str_len); - int err = wifi_set_opmode(STATION_MODE | wifi_get_opmode()); - if (err == FALSE) + if (!wifi_station_disconnect()) { - wifi_set_opmode(~STATION_MODE & wifi_get_opmode()); - ENDUSER_SETUP_ERROR("enduser_setup_station_start failed. wifi_set_opmode failed.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("station_start failed. wifi_station_disconnect failed.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); } - err = wifi_station_set_config(&cnf); - if (err == FALSE) + if (!wifi_station_set_config(&cnf)) { - wifi_set_opmode(~STATION_MODE & wifi_get_opmode()); - ENDUSER_SETUP_ERROR("enduser_setup_station_start failed. wifi_station_set_config failed.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("station_start failed. wifi_station_set_config failed.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } - err = wifi_station_disconnect(); - if (err == FALSE) + if (wifi_station_connect()) { - ENDUSER_SETUP_ERROR("enduser_setup_station_start failed. wifi_station_disconnect failed.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); - } - err = wifi_station_connect(); - if (err == FALSE) - { - ENDUSER_SETUP_ERROR("enduser_setup_station_start failed. wifi_station_connect failed.\n", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("station_start failed. wifi_station_connect failed.\n", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } - ENDUSER_SETUP_DEBUG(state->lua_L, "WiFi Credentials Stored"); - ENDUSER_SETUP_DEBUG(state->lua_L, "-----------------------"); - ENDUSER_SETUP_DEBUG(state->lua_L, "name: "); - ENDUSER_SETUP_DEBUG(state->lua_L, cnf.ssid); - ENDUSER_SETUP_DEBUG(state->lua_L, "pass: "); - ENDUSER_SETUP_DEBUG(state->lua_L, cnf.password); - ENDUSER_SETUP_DEBUG(state->lua_L, "-----------------------"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "WiFi Credentials Stored"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "-----------------------"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "name: "); + ENDUSER_SETUP_DEBUG(lua_getstate(), cnf.ssid); + ENDUSER_SETUP_DEBUG(lua_getstate(), "pass: "); + ENDUSER_SETUP_DEBUG(lua_getstate(), cnf.password); + ENDUSER_SETUP_DEBUG(lua_getstate(), "-----------------------"); return 0; } @@ -417,22 +557,22 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data * * @return - return 0 iff html was served successfully */ -static int enduser_setup_http_serve_header(struct espconn *http_client, char *header, uint32_t header_len) +static int enduser_setup_http_serve_header(struct espconn *http_client, const char *header, uint32_t header_len) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_serve_404"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_serve_header"); - int8_t err = espconn_sent(http_client, header, header_len); + int8_t err = espconn_send(http_client, (char *)header, header_len); if (err == ESPCONN_MEM) { - ENDUSER_SETUP_ERROR("enduser_setup_http_serve_header failed. espconn_send out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); + ENDUSER_SETUP_ERROR("http_serve_header failed. espconn_send out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); } else if (err == ESPCONN_ARG) { - ENDUSER_SETUP_ERROR("enduser_setup_http_serve_header failed. espconn_send can't find network transmission", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_NONFATAL); + ENDUSER_SETUP_ERROR("http_serve_header failed. espconn_send can't find network transmission", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_NONFATAL); } else if (err != 0) { - ENDUSER_SETUP_ERROR("enduser_setup_http_serve_header failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + ENDUSER_SETUP_ERROR("http_serve_header failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); } return 0; @@ -446,31 +586,67 @@ static int enduser_setup_http_serve_header(struct espconn *http_client, char *he */ static int enduser_setup_http_serve_html(struct espconn *http_client) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_serve_html"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_serve_html"); if (state->http_payload_data == NULL) { enduser_setup_http_load_payload(); } - int8_t err = espconn_sent(http_client, state->http_payload_data, state->http_payload_len); + int8_t err = espconn_send(http_client, state->http_payload_data, state->http_payload_len); if (err == ESPCONN_MEM) { - ENDUSER_SETUP_ERROR("enduser_setup_http_serve_html failed. espconn_send out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); + ENDUSER_SETUP_ERROR("http_serve_html failed. espconn_send out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); } else if (err == ESPCONN_ARG) { - ENDUSER_SETUP_ERROR("enduser_setup_http_serve_html failed. espconn_send can't find network transmission", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_NONFATAL); + ENDUSER_SETUP_ERROR("http_serve_html failed. espconn_send can't find network transmission", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_NONFATAL); } else if (err != 0) { - ENDUSER_SETUP_ERROR("enduser_setup_http_serve_html failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + ENDUSER_SETUP_ERROR("http_serve_html failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); } return 0; } +static void serve_status (struct espconn *conn) +{ + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_serve_status"); + + const char fmt[] = + "HTTP/1.1 200 OK\r\n" + "Cache-control: no-cache\r\n" + "Content-type: text/plain\r\n" + "Content-length: %d\r\n" + "\r\n" + "%s"; + const char *state[] = + { + "Idle", + "Connecting...", + "Failed to connect - wrong password", + "Failed to connect - network not found", + "Failed to connect", + "WiFi successfully connected!" /* TODO: include SSID */ + }; + const size_t num_states = sizeof(state)/sizeof(state[0]); + + uint8_t which = wifi_station_get_connect_status (); + if (which < num_states) + { + const char *s = state[which]; + int len = c_strlen (s); + char buf[sizeof (fmt) + 10 + len]; /* more than enough for the formatted */ + len = c_sprintf (buf, fmt, len, s); + enduser_setup_http_serve_header (conn, buf, len); + } + else + enduser_setup_http_serve_header (conn, http_header_500, LITLEN(http_header_500)); +} + + /** * Disconnect HTTP client * @@ -478,90 +654,263 @@ static int enduser_setup_http_serve_html(struct espconn *http_client) */ static void enduser_setup_http_disconnect(struct espconn *espconn) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_disconnect"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_disconnect"); //TODO: Construct and maintain os task queue(?) to be able to issue system_os_task with espconn_disconnect. } -static void enduser_setup_http_recvcb(void *arg, char *data, unsigned short data_len) +/* --- WiFi AP scanning support -------------------------------------------- */ + +static void free_scan_listeners (void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_recvcb"); - struct espconn *http_client = (struct espconn *) arg; - int retval; + if (!state || !state->scan_listeners) + return; - if (c_strncmp(data, "POST ", 5) == 0) + scan_listener_t *l = state->scan_listeners , *next = 0; + while (l) { - retval = enduser_setup_http_handle_credentials(data, data_len); - if (retval == 0) - { - enduser_setup_http_serve_header(http_client, (char *) http_header_200, sizeof(http_header_200)); - enduser_setup_http_disconnect(http_client); - return; - } - else if (retval == 2) - { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); - } + next = l->next; + c_free (l); + l = next; + } + state->scan_listeners = 0; +} - if (retval != 1) + +static inline bool same_remote (uint8 addr1[4], int port1, uint8 addr2[4], int port2) +{ + return (port1 == port2) && (c_memcmp (addr1, addr2, 4) == 0); +} + + +static void on_disconnect (void *arg) +{ + struct espconn *conn = arg; + if (conn->type == ESPCONN_TCP && state && state->scan_listeners) + { + scan_listener_t **sl = &state->scan_listeners; + while (*sl) { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_http_recvcb failed. Unknown error code.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + /* Remove any and all references to the closed conn from the scan list */ + scan_listener_t *l = *sl; + if (same_remote ( + l->remote_ip, l->remote_port, + conn->proto.tcp->remote_ip, conn->proto.tcp->remote_port)) + { + *sl = l->next; + c_free (l); + /* No early exit to guard against multi-entry on list */ + } + else + sl = &(*sl)->next; } } - else if (c_strncmp(data, "GET ", 4) == 0) - { - /* Reject requests that probably aren't relevant to free up resources. */ - if (c_strncmp(data, "GET / ", 6) != 0) - { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_recvcb received too specific request."); - enduser_setup_http_serve_header(http_client, (char *) http_header_404, sizeof(http_header_404)); - enduser_setup_http_disconnect(http_client); - return; - } +} - retval = enduser_setup_http_serve_html(http_client); - if (retval != 0) - { - enduser_setup_http_disconnect(http_client); - ENDUSER_SETUP_ERROR_VOID("enduser_setup_http_recvcb failed. Unable to send HTML.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); - } - } - else +static char *escape_ssid (char *dst, const char *src) +{ + for (int i = 0; i < 32 && src[i]; ++i) { - enduser_setup_http_serve_header(http_client, (char *) http_header_404, sizeof(http_header_404)); - enduser_setup_http_disconnect(http_client); + if (src[i] == '\\' || src[i] == '"') + *dst++ = '\\'; + *dst++ = src[i]; + } + return dst; +} + + +static void notify_scan_listeners (const char *payload, size_t sz) +{ + if (!state) + return; + if (!state->espconn_http_tcp) + goto cleanup; + + struct espconn *conn = state->espconn_http_tcp; + for (scan_listener_t *l = state->scan_listeners; l; l = l->next) + { + c_memcpy (conn->proto.tcp->remote_ip, l->remote_ip, 4); + conn->proto.tcp->remote_port = l->remote_port; + if (espconn_send(conn, (char *)payload, sz) != ESPCONN_OK) + ENDUSER_SETUP_DEBUG(lua_getstate(), "failed to send wifi list"); + enduser_setup_http_disconnect(conn); + } + +cleanup: + free_scan_listeners (); +} + + +static void on_scan_done (void *arg, STATUS status) +{ + if (!state || !state->scan_listeners) + return; + + if (status == OK) + { + unsigned num_nets = 0; + for (struct bss_info *wn = arg; wn; wn = wn->next.stqe_next) + ++num_nets; + + const char header_fmt[] = + "HTTP/1.1 200 OK\r\n" + "Connection:close\r\n" + "Cache-control: no-cache\r\n" + "Content-type:application/json\r\n" + "Content-length:%4d\r\n" + "\r\n"; + const size_t hdr_sz = sizeof (header_fmt) +1 -1; /* +expand %4d, -\0 */ + + /* To be able to safely escape a pathological SSID, we need 2*32 bytes */ + const size_t max_entry_sz = sizeof("{\"ssid\":\"\",\"rssi\":},") + 2*32 + 6; + const size_t alloc_sz = hdr_sz + num_nets * max_entry_sz + 3; + char *http = os_zalloc (alloc_sz); + if (!http) + goto serve_500; + + char *p = http + hdr_sz; /* start body where we know it will be */ + /* p[0] will be clobbered when we print the header, so fill it in last */ + ++p; + for (struct bss_info *wn = arg; wn; wn = wn->next.stqe_next) + { + if (wn != arg) + *p++ = ','; + + const char entry_start[] = "{\"ssid\":\""; + strcpy (p, entry_start); + p += sizeof (entry_start) -1; + + p = escape_ssid (p, wn->ssid); + + const char entry_mid[] = "\",\"rssi\":"; + strcpy (p, entry_mid); + p += sizeof (entry_mid) -1; + + p += c_sprintf (p, "%d", wn->rssi); + + *p++ = '}'; + } + *p++ = ']'; + + size_t body_sz = (p - http) - hdr_sz; + c_sprintf (http, header_fmt, body_sz); + http[hdr_sz] = '['; /* Rewrite the \0 with the correct start of body */ + + notify_scan_listeners (http, hdr_sz + body_sz); + c_free (http); return; } + +serve_500: + notify_scan_listeners (http_header_500, LITLEN(http_header_500)); +} + +/* ---- end WiFi AP scan support ------------------------------------------- */ + + +static void enduser_setup_http_recvcb(void *arg, char *data, unsigned short data_len) +{ + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_recvcb"); + if (!state) + { + ENDUSER_SETUP_DEBUG(lua_getstate(), "ignoring received data while stopped"); + return; + } + + struct espconn *http_client = (struct espconn *) arg; + if (c_strncmp(data, "GET ", 4) == 0) + { + if (c_strncmp(data + 4, "/ ", 2) == 0) + { + if (enduser_setup_http_serve_html(http_client) != 0) + ENDUSER_SETUP_ERROR_VOID("http_recvcb failed. Unable to send HTML.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + } + else if (c_strncmp(data + 4, "/aplist ", 8) == 0) + { + scan_listener_t *l = os_malloc (sizeof (scan_listener_t)); + if (!l) + ENDUSER_SETUP_ERROR_VOID("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); + + c_memcpy (l->remote_ip, http_client->proto.tcp->remote_ip, 4); + l->remote_port = http_client->proto.tcp->remote_port; + + bool already = (state->scan_listeners != NULL); + + l->next = state->scan_listeners; + state->scan_listeners = l; + + if (!already) + { + if (!wifi_station_scan (NULL, on_scan_done)) + { + enduser_setup_http_serve_header (http_client, http_header_500, LITLEN(http_header_500)); + free_scan_listeners (); + } + } + return; + } + else if (c_strncmp(data + 4, "/status ", 8) == 0) + { + serve_status (http_client); + } + else if (c_strncmp(data + 4, "/update?", 8) == 0) + { + switch (enduser_setup_http_handle_credentials(data, data_len)) + { + case 0: + enduser_setup_http_serve_header(http_client, http_header_302, LITLEN(http_header_302)); + break; + case 1: + enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401)); + break; + default: + ENDUSER_SETUP_ERROR_VOID("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + break; + } + } + else + { + ENDUSER_SETUP_DEBUG(lua_getstate(), "serving 404"); + enduser_setup_http_serve_header(http_client, http_header_404, LITLEN(http_header_404)); + } + } + else /* not GET */ + { + enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401)); + } + + enduser_setup_http_disconnect (http_client); } static void enduser_setup_http_connectcb(void *arg) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_connectcb"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_connectcb"); struct espconn *callback_espconn = (struct espconn *) arg; int8_t err = 0; err |= espconn_regist_recvcb(callback_espconn, enduser_setup_http_recvcb); + err |= espconn_regist_disconcb(callback_espconn, on_disconnect); if (err != 0) { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_http_connectcb failed. Callback registration failed.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + ENDUSER_SETUP_ERROR_VOID("http_connectcb failed. Callback registration failed.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); } } static int enduser_setup_http_start(void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_start"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_start"); state->espconn_http_tcp = (struct espconn *) c_malloc(sizeof(struct espconn)); if (state->espconn_http_tcp == NULL) { - ENDUSER_SETUP_ERROR("enduser_setup_http_start failed. Memory allocation failed (espconn_http_tcp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("http_start failed. Memory allocation failed (espconn_http_tcp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); } esp_tcp *esp_tcp_data = (esp_tcp *) c_malloc(sizeof(esp_tcp)); if (esp_tcp_data == NULL) { - ENDUSER_SETUP_ERROR("enduser_setup_http_start failed. Memory allocation failed (esp_udp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("http_start failed. Memory allocation failed (esp_udp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); } c_memset(state->espconn_http_tcp, 0, sizeof(struct espconn)); @@ -575,41 +924,41 @@ static int enduser_setup_http_start(void) err = espconn_regist_connectcb(state->espconn_http_tcp, enduser_setup_http_connectcb); if (err != 0) { - ENDUSER_SETUP_ERROR("enduser_setup_http_start failed. Couldn't add receive callback.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("http_start failed. Couldn't add receive callback.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } err = espconn_accept(state->espconn_http_tcp); if (err == ESPCONN_ISCONN) { - ENDUSER_SETUP_ERROR("enduser_setup_http_start failed. Couldn't create connection, already listening for that connection.", ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("http_start failed. Couldn't create connection, already listening for that connection.", ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN, ENDUSER_SETUP_ERR_FATAL); } else if (err == ESPCONN_MEM) { - ENDUSER_SETUP_ERROR("enduser_setup_http_start failed. Couldn't create connection, out of memory.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("http_start failed. Couldn't create connection, out of memory.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); } else if (err == ESPCONN_ARG) { - ENDUSER_SETUP_ERROR("enduser_setup_http_start failed. Can't find connection from espconn argument", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("http_start failed. Can't find connection from espconn argument", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL); } else if (err != 0) { - ENDUSER_SETUP_ERROR("enduser_setup_http_start failed. Unknown error", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("http_start failed. Unknown error", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } err = espconn_regist_time(state->espconn_http_tcp, 2, 0); if (err == ESPCONN_ARG) { - ENDUSER_SETUP_ERROR("enduser_setup_http_start failed. Unable to set TCP timeout.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_NONFATAL | ENDUSER_SETUP_ERR_NO_RETURN); + ENDUSER_SETUP_ERROR("http_start failed. Unable to set TCP timeout.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_NONFATAL | ENDUSER_SETUP_ERR_NO_RETURN); } err = enduser_setup_http_load_payload(); if (err == 1) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_start info. Loaded backup HTML."); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_start info. Loaded backup HTML."); } else if (err == 2) { - ENDUSER_SETUP_ERROR("enduser_setup_http_start failed. Unable to allocate memory for HTTP payload.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("http_start failed. Unable to allocate memory for HTTP payload.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); } return 0; @@ -618,7 +967,7 @@ static int enduser_setup_http_start(void) static void enduser_setup_http_stop(void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_http_stop"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_http_stop"); if (state != NULL && state->espconn_http_tcp != NULL) { @@ -628,7 +977,7 @@ static void enduser_setup_http_stop(void) static void enduser_setup_ap_stop(void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_station_stop"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_station_stop"); wifi_set_opmode(~SOFTAP_MODE & wifi_get_opmode()); } @@ -636,7 +985,7 @@ static void enduser_setup_ap_stop(void) static void enduser_setup_ap_start(void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_ap_start"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_ap_start"); struct softap_config cnf; c_memset(&(cnf), 0, sizeof(struct softap_config)); @@ -646,33 +995,27 @@ static void enduser_setup_ap_start(void) #endif char ssid[] = ENDUSER_SETUP_AP_SSID; - int ssid_name_len = c_strlen(ENDUSER_SETUP_AP_SSID); + int ssid_name_len = c_strlen(ssid); c_memcpy(&(cnf.ssid), ssid, ssid_name_len); uint8_t mac[6]; wifi_get_macaddr(SOFTAP_IF, mac); cnf.ssid[ssid_name_len] = '_'; - cnf.ssid[ssid_name_len + 1] = (char) (65 + (0x0F & mac[3])); - cnf.ssid[ssid_name_len + 2] = (char) (65 + ((0xF0 & mac[3]) >> 4)); - cnf.ssid[ssid_name_len + 3] = (char) (65 + (0x0F & mac[4])); - cnf.ssid[ssid_name_len + 4] = (char) (65 + ((0xF0 & mac[4]) >> 4)); - cnf.ssid[ssid_name_len + 5] = (char) (65 + (0x0F & mac[5])); - cnf.ssid[ssid_name_len + 6] = (char) (65 + ((0xF0 & mac[5]) >> 4)); - - cnf.ssid_len = c_strlen(ssid) + 7; + c_sprintf(cnf.ssid + ssid_name_len + 1, "%02X%02X%02X", mac[3], mac[4], mac[5]); + cnf.ssid_len = ssid_name_len + 7; cnf.channel = 1; cnf.authmode = AUTH_OPEN; cnf.ssid_hidden = 0; cnf.max_connection = 5; cnf.beacon_interval = 100; - wifi_set_opmode(SOFTAP_MODE | wifi_get_opmode()); // Changed behavior: Need to be in AP mode before AP config will save + wifi_set_opmode(STATIONAP_MODE); wifi_softap_set_config(&cnf); } static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned short recv_len) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_dns_recv_callback."); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_dns_recv_callback."); struct espconn *callback_espconn = arg; struct ip_info ip_info; @@ -684,20 +1027,20 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned uint8_t if_mode = wifi_get_opmode(); if ((if_mode & SOFTAP_MODE) == 0) { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_dns_recv_callback failed. Interface mode not supported.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Interface mode not supported.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } uint8_t if_index = (if_mode == STATION_MODE? STATION_IF : SOFTAP_IF); if (wifi_get_ip_info(if_index , &ip_info) == false) { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_dns_recv_callback failed. Unable to get interface IP.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Unable to get interface IP.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } char *dns_reply = (char *) c_malloc(dns_reply_len); if (dns_reply == NULL) { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_dns_recv_callback failed. Failed to allocate memory.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Failed to allocate memory.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); } uint32_t insert_byte = 0; @@ -715,32 +1058,32 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned remot_info *pr = 0; if (espconn_get_connection_info(callback_espconn, &pr, 0) != ESPCONN_OK) { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_dns_recv_callback failed. Unable to get IP of UDP sender.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Unable to get IP of UDP sender.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL); } callback_espconn->proto.udp->remote_port = pr->remote_port; os_memmove(callback_espconn->proto.udp->remote_ip, pr->remote_ip, 4); int8_t err; - err = espconn_sent(callback_espconn, dns_reply, dns_reply_len); + err = espconn_send(callback_espconn, dns_reply, dns_reply_len); c_free(dns_reply); if (err == ESPCONN_MEM) { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_dns_recv_callback failed. Failed to allocate memory for send.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Failed to allocate memory for send.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); } else if (err == ESPCONN_ARG) { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_dns_recv_callback failed. Can't execute transmission.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Can't execute transmission.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL); } else if (err != 0) { - ENDUSER_SETUP_ERROR_VOID("enduser_setup_dns_recv_callback failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } } static void enduser_setup_free(void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_free"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_free"); if (state == NULL) { @@ -765,6 +1108,9 @@ static void enduser_setup_free(void) c_free(state->espconn_http_tcp); } c_free(state->http_payload_data); + + free_scan_listeners (); + c_free(state); state = NULL; } @@ -772,22 +1118,22 @@ static void enduser_setup_free(void) static int enduser_setup_dns_start(void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_dns_start"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_dns_start"); if (state->espconn_dns_udp != NULL) { - ENDUSER_SETUP_ERROR("enduser_setup_dns_start failed. Appears to already be started (espconn_dns_udp != NULL).", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("dns_start failed. Appears to already be started (espconn_dns_udp != NULL).", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } state->espconn_dns_udp = (struct espconn *) c_malloc(sizeof(struct espconn)); if (state->espconn_dns_udp == NULL) { - ENDUSER_SETUP_ERROR("enduser_setup_dns_start failed. Memory allocation failed (espconn_dns_udp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("dns_start failed. Memory allocation failed (espconn_dns_udp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); } esp_udp *esp_udp_data = (esp_udp *) c_malloc(sizeof(esp_udp)); if (esp_udp_data == NULL) { - ENDUSER_SETUP_ERROR("enduser_setup_dns_start failed. Memory allocation failed (esp_udp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("dns_start failed. Memory allocation failed (esp_udp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); } c_memset(state->espconn_dns_udp, 0, sizeof(struct espconn)); @@ -801,21 +1147,21 @@ static int enduser_setup_dns_start(void) err = espconn_regist_recvcb(state->espconn_dns_udp, enduser_setup_dns_recv_callback); if (err != 0) { - ENDUSER_SETUP_ERROR("enduser_setup_dns_start failed. Couldn't add receive callback, unknown error.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("dns_start failed. Couldn't add receive callback, unknown error.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } err = espconn_create(state->espconn_dns_udp); if (err == ESPCONN_ISCONN) { - ENDUSER_SETUP_ERROR("enduser_setup_dns_start failed. Couldn't create connection, already listening for that connection.", ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("dns_start failed. Couldn't create connection, already listening for that connection.", ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN, ENDUSER_SETUP_ERR_FATAL); } else if (err == ESPCONN_MEM) { - ENDUSER_SETUP_ERROR("enduser_setup_dns_start failed. Couldn't create connection, out of memory.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("dns_start failed. Couldn't create connection, out of memory.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL); } else if (err != 0) { - ENDUSER_SETUP_ERROR("enduser_setup_dns_start failed. Couldn't create connection, unknown error.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); + ENDUSER_SETUP_ERROR("dns_start failed. Couldn't create connection, unknown error.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); } return 0; @@ -824,7 +1170,7 @@ static int enduser_setup_dns_start(void) static void enduser_setup_dns_stop(void) { - ENDUSER_SETUP_DEBUG(state->lua_L, "enduser_setup_dns_stop"); + ENDUSER_SETUP_DEBUG(lua_getstate(), "enduser_setup_dns_stop"); if (state->espconn_dns_udp != NULL) { @@ -839,19 +1185,18 @@ static int enduser_setup_init(lua_State *L) if (state != NULL) { - enduser_setup_error(L, "enduser_setup_init failed. Appears to already be started.", ENDUSER_SETUP_ERR_UNKOWN_ERROR); + enduser_setup_error(L, "init failed. Appears to already be started.", ENDUSER_SETUP_ERR_UNKOWN_ERROR); return ENDUSER_SETUP_ERR_UNKOWN_ERROR; } - state = (enduser_setup_state_t *) os_malloc(sizeof(enduser_setup_state_t)); + state = (enduser_setup_state_t *) os_zalloc(sizeof(enduser_setup_state_t)); if (state == NULL) { - enduser_setup_error(L, "enduser_setup_init failed. Unable to allocate memory.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY); + enduser_setup_error(L, "init failed. Unable to allocate memory.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY); return ENDUSER_SETUP_ERR_OUT_OF_MEMORY; } c_memset(state, 0, sizeof(enduser_setup_state_t)); - state->lua_L = L; state->lua_connected_cb_ref = LUA_NOREF; state->lua_err_cb_ref = LUA_NOREF; state->lua_dbg_cb_ref = LUA_NOREF; @@ -890,39 +1235,50 @@ static int enduser_setup_init(lua_State *L) } +static int enduser_setup_manual (lua_State *L) +{ + if (!lua_isnoneornil (L, 1)) + manual = lua_toboolean (L, 1); + lua_pushboolean (L, manual); + return 1; +} + + static int enduser_setup_start(lua_State *L) { ENDUSER_SETUP_DEBUG(L, "enduser_setup_start"); if(enduser_setup_init(L)) - { - enduser_setup_stop(L); - return 0; - } + goto failed; - enduser_setup_check_station_start(); - enduser_setup_ap_start(); + if (!manual) + { + enduser_setup_check_station_start(); + enduser_setup_ap_start(); + } if(enduser_setup_dns_start()) - { - enduser_setup_stop(L); - return 0; - } + goto failed; if(enduser_setup_http_start()) - { - enduser_setup_stop(L); - return 0; - } + goto failed; + goto out; + +failed: + enduser_setup_stop(L); +out: return 0; } static int enduser_setup_stop(lua_State* L) { - enduser_setup_check_station_stop(); - enduser_setup_ap_stop(); + if (!manual) + { + enduser_setup_check_station_stop(); + enduser_setup_ap_stop(); + } enduser_setup_dns_stop(); enduser_setup_http_stop(); enduser_setup_free(); @@ -932,6 +1288,7 @@ static int enduser_setup_stop(lua_State* L) static const LUA_REG_TYPE enduser_setup_map[] = { + { LSTRKEY( "manual" ), LFUNCVAL( enduser_setup_manual )}, { LSTRKEY( "start" ), LFUNCVAL( enduser_setup_start )}, { LSTRKEY( "stop" ), LFUNCVAL( enduser_setup_stop )}, { LNILKEY, LNILVAL} diff --git a/docs/en/modules/enduser-setup.md b/docs/en/modules/enduser-setup.md index 47dead73..41d7918d 100644 --- a/docs/en/modules/enduser-setup.md +++ b/docs/en/modules/enduser-setup.md @@ -6,6 +6,23 @@ This module provides a simple way of configuring ESP8266 chips without using a s After running [`enduser_setup.start()`](#enduser_setupstart) a portal like the above can be accessed through a wireless network called SetupGadget_XXXXXX. The portal is used to submit the credentials for the WiFi of the enduser. After an IP address has been successfully obtained this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. +## enduser_setup.manual() + +Controls whether manual AP configuration is used. + +By default the `enduser_setup` module automatically configures an open access point when starting, and stops it when the device has been successfully joined to a WiFi network. If manual mode has been enabled, neither of this is done. The device must be manually configured for `wifi.SOFTAP` mode prior to calling `enduser_setup.start()`. Additionally, the portal is not stopped after the device has successfully joined to a WiFi network. + +Most importantly, *the `onConfigured()` callback is not supported in manual mode*. This limitation may disappear in the future. + +#### Syntax +`enduser_setup.manual([on_off])` + +#### Parameters + - `on_off` a boolean value indicating whether to use manual mode; if not given, the function only returns the current setting. + +#### Returns +The current setting, true if manual mode is enabled, false if it is not. + ## enduser_setup.start() Starts the captive portal. @@ -44,4 +61,4 @@ Stops the captive portal. none #### Returns -`nil` \ No newline at end of file +`nil` diff --git a/ld/nodemcu.ld b/ld/nodemcu.ld index afeacc8f..29daaa13 100644 --- a/ld/nodemcu.ld +++ b/ld/nodemcu.ld @@ -89,31 +89,9 @@ SECTIONS KEEP(*(.lua_rotable)) LONG(0) LONG(0) /* Null-terminate the array */ - /* These are *only* pulled in by Lua, and therefore safe to put in flash */ - */libc.a:lib_a-isalnum.o(.text* .literal*) - */libc.a:lib_a-isalpha.o(.text* .literal*) - */libc.a:lib_a-iscntrl.o(.text* .literal*) - */libc.a:lib_a-isspace.o(.text* .literal*) - */libc.a:lib_a-islower.o(.text* .literal*) - */libc.a:lib_a-isupper.o(.text* .literal*) - */libc.a:lib_a-ispunct.o(.text* .literal*) - */libc.a:lib_a-isxdigit.o(.text* .literal*) - */libc.a:lib_a-locale.o(.text* .literal*) - */libc.a:lib_a-tolower.o(.text* .literal*) - */libc.a:lib_a-toupper.o(.text* .literal*) - */libc.a:lib_a-strcasecmp.o(.text* .literal*) - */libc.a:lib_a-strcoll.o(.text* .literal*) - */libc.a:lib_a-strchr.o(.text* .literal*) - */libc.a:lib_a-strrchr.o(.text* .literal*) - */libc.a:lib_a-strcat.o(.text* .literal*) - */libc.a:lib_a-strncat.o(.text* .literal*) - */libc.a:lib_a-strcspn.o(.text* .literal*) - */libc.a:lib_a-strtol.o(.text* .literal*) - */libc.a:lib_a-strtoul.o(.text* .literal*) - */libc.a:lib_a-strpbrk.o(.text* .literal*) - */libc.a:lib_a-memchr.o(.text* .literal*) - */libc.a:lib_a-setjmp.o(.text* .literal*) - /* end Lua C lib functions */ + /* SDK doesn't use libc functions, and are therefore safe to put in flash */ + */libc.a(.text* .literal*) + /* end libc functions */ _irom0_text_end = ABSOLUTE(.); _flash_used_end = ABSOLUTE(.);