Commit fee81bf9 authored by Philip Gladstone's avatar Philip Gladstone Committed by Marcel Stör
Browse files

Handle tcp fragmentation and also fix XSS problem. (#3275)

parent f527ad86
...@@ -90,14 +90,14 @@ static const char dns_body[] = { 0x00, 0x01, 0x00, 0x01, ...@@ -90,14 +90,14 @@ static const char dns_body[] = { 0x00, 0x01, 0x00, 0x01,
static const char http_html_gz_filename[] = "enduser_setup.html.gz"; static const char http_html_gz_filename[] = "enduser_setup.html.gz";
static const char http_html_filename[] = "enduser_setup.html"; static const char http_html_filename[] = "enduser_setup.html";
static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html\r\n"; /* Note single \r\n here! */ static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html; charset=utf-8\r\n"; /* Note single \r\n here! */
static const char http_header_204[] = "HTTP/1.1 204 No Content\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; static const char http_header_204[] = "HTTP/1.1 204 No Content\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_302_trying[] = "HTTP/1.1 302 Moved\r\nLocation: /?trying=true\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; static const char http_header_302_trying[] = "HTTP/1.1 302 Moved\r\nLocation: /?trying=true\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_400[] = "HTTP/1.1 400 Bad request\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; static const char http_header_400[] = "HTTP/1.1 400 Bad request\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_404[] = "HTTP/1.1 404 Not found\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; static const char http_header_404[] = "HTTP/1.1 404 Not found\r\nContent-Length:10\r\nConnection:close\r\n\r\nNot found\n";
static const char http_header_405[] = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; static const char http_header_405[] = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\nContent-Length:6\r\nConnection:close\r\n\r\nError\n";
static const char http_header_content_len_fmt[] = "Content-length:%5d\r\n\r\n"; static const char http_header_content_len_fmt[] = "Content-length:%5d\r\n\r\n";
static const char http_html_gzip_contentencoding[] = "Content-Encoding: gzip\r\n"; static const char http_html_gzip_contentencoding[] = "Content-Encoding: gzip\r\n";
...@@ -105,12 +105,30 @@ static const char http_html_gzip_contentencoding[] = "Content-Encoding: gzip\r\n ...@@ -105,12 +105,30 @@ static const char http_html_gzip_contentencoding[] = "Content-Encoding: gzip\r\n
/* Externally defined: static const char enduser_setup_html_default[] = ... */ /* Externally defined: static const char enduser_setup_html_default[] = ... */
#include "enduser_setup/enduser_setup.html.gz.def.h" #include "enduser_setup/enduser_setup.html.gz.def.h"
// The tcp_arg can be either a pointer to the scan_listener_t or http_request_buffer_t.
// The enum defines which one it is.
typedef enum {
SCAN_LISTENER_STRUCT_TYPE = 1,
HTTP_REQUEST_BUFFER_STRUCT_TYPE = 2
} struct_type_t;
typedef struct {
struct_type_t struct_type;
} tcp_arg_t;
typedef struct scan_listener typedef struct scan_listener
{ {
struct_type_t struct_type;
struct tcp_pcb *conn; struct tcp_pcb *conn;
struct scan_listener *next; struct scan_listener *next;
} scan_listener_t; } scan_listener_t;
typedef struct {
struct_type_t struct_type;
size_t length;
char data[0];
} http_request_buffer_t;
typedef struct typedef struct
{ {
struct espconn *espconn_dns_udp; struct espconn *espconn_dns_udp;
...@@ -398,8 +416,8 @@ static err_t close_once_sent (void *arg, struct tcp_pcb *pcb, u16_t len) ...@@ -398,8 +416,8 @@ static err_t close_once_sent (void *arg, struct tcp_pcb *pcb, u16_t len)
/** /**
* Get length of param value * Get length of param value
* *
* This is being called with a fragment of the parameters passed in the * This is being called with a fragment of the parameters passed in the
* URL for GET requests or part of the body of a POST request. * URL for GET requests or part of the body of a POST request.
* The string will look like one of these * The string will look like one of these
* "SecretPassword HTTP/1.1" * "SecretPassword HTTP/1.1"
...@@ -1124,14 +1142,13 @@ static void enduser_setup_handle_POST(struct tcp_pcb *http_client, char* data, s ...@@ -1124,14 +1142,13 @@ static void enduser_setup_handle_POST(struct tcp_pcb *http_client, char* data, s
if (strncmp(data + 5, "/setwifi ", 9) == 0) // User clicked the submit button if (strncmp(data + 5, "/setwifi ", 9) == 0) // User clicked the submit button
{ {
char* body=strstr(data, "\r\n\r\n"); char* body=strstr(data, "\r\n\r\n");
char *content_length_str = strstr(data, "Content-Length: "); if( body == NULL)
if( body == NULL || content_length_str == NULL)
{ {
enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400)); enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400));
return; return;
} }
int bodylength = atoi(content_length_str + 16);
body += 4; // length of the double CRLF found above body += 4; // length of the double CRLF found above
int bodylength = (data + data_len) - body;
switch (enduser_setup_http_handle_credentials(body, bodylength)) switch (enduser_setup_http_handle_credentials(body, bodylength))
{ {
case 0: { case 0: {
...@@ -1148,6 +1165,8 @@ static void enduser_setup_handle_POST(struct tcp_pcb *http_client, char* data, s ...@@ -1148,6 +1165,8 @@ static void enduser_setup_handle_POST(struct tcp_pcb *http_client, char* data, s
ENDUSER_SETUP_ERROR_VOID("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); ENDUSER_SETUP_ERROR_VOID("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
break; break;
} }
} else {
enduser_setup_http_serve_header(http_client, http_header_404, LITLEN(http_header_404));
} }
} }
...@@ -1333,27 +1352,154 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s ...@@ -1333,27 +1352,154 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
return ERR_ABRT; return ERR_ABRT;
} }
tcp_arg_t *tcp_arg_ptr = arg;
if (!p) /* remote side closed, close our end too */ if (!p) /* remote side closed, close our end too */
{ {
ENDUSER_SETUP_DEBUG("connection closed"); ENDUSER_SETUP_DEBUG("connection closed");
scan_listener_t *l = arg; /* if it's waiting for scan, we have a ptr here */ if (tcp_arg_ptr) {
if (l) if (tcp_arg_ptr->struct_type == SCAN_LISTENER_STRUCT_TYPE) {
remove_scan_listener (l); remove_scan_listener ((scan_listener_t *)tcp_arg_ptr);
} else if (tcp_arg_ptr->struct_type == HTTP_REQUEST_BUFFER_STRUCT_TYPE) {
free(tcp_arg_ptr);
}
}
deferred_close (http_client); deferred_close (http_client);
return ERR_OK; return ERR_OK;
} }
char *data = calloc (1, p->tot_len + 1); http_request_buffer_t *hrb;
if (!data) if (!tcp_arg_ptr) {
return ERR_MEM; hrb = calloc(1, sizeof(*hrb));
if (!hrb) {
goto general_fail;
}
hrb->struct_type = HTTP_REQUEST_BUFFER_STRUCT_TYPE;
tcp_arg(http_client, hrb);
} else if (tcp_arg_ptr->struct_type == HTTP_REQUEST_BUFFER_STRUCT_TYPE) {
hrb = (http_request_buffer_t *) tcp_arg_ptr;
} else {
goto general_fail;
}
// Append the new data
size_t newlen = hrb->length + p->tot_len;
void *old_hrb = hrb;
hrb = realloc(hrb, sizeof(*hrb) + newlen + 1);
tcp_arg(http_client, hrb);
if (!hrb) {
free(old_hrb);
goto general_fail;
}
pbuf_copy_partial(p, hrb->data + hrb->length, p->tot_len, 0);
hrb->data[newlen] = 0;
hrb->length = newlen;
unsigned data_len = pbuf_copy_partial (p, data, p->tot_len, 0);
tcp_recved (http_client, p->tot_len); tcp_recved (http_client, p->tot_len);
pbuf_free (p); pbuf_free (p);
// see if we have the whole request.
// Rely on the fact that the header should not contain a null character
char *end_of_header = strstr(hrb->data, "\r\n\r\n");
if (end_of_header == 0) {
return ERR_OK;
}
end_of_header += 4;
// We have the entire header, now see if there is any content. If we don't find the
// content-length header, then there is no content and we can process immediately.
// The content-length header can also be missing if the browser is using chunked
// encoding.
bool is_chunked = FALSE;
for (const char *hdr = hrb->data; hdr && hdr < end_of_header; hdr = strchr(hdr, '\n')) {
hdr += 1; // Skip the \n
if (strncasecmp(hdr, "transfer-encoding:", 18) == 0) {
const char *field = hdr + 18;
while (*field != '\n') {
if (memcmp(field, "chunked", 7) == 0) {
is_chunked = TRUE;
break;
}
field++;
}
}
if (strncasecmp(hdr, "Content-length:", 15) == 0) {
// There is a content-length header
const char *field = hdr + 15;
size_t extra = strtol(field + 1, 0, 10);
if (extra + (end_of_header - hrb->data) > hrb->length) {
return ERR_OK;
}
}
}
if (is_chunked) {
// More complex to determine if the whole body has arrived
// Format is one or more chunks each preceded by their length (in hex)
// A zero length chunk ends the body
const char *ptr = end_of_header;
bool seen_end = FALSE;
while (ptr < hrb->data + hrb->length && ptr > hrb->data) {
size_t chunk_len = strtol(ptr, 0, 16);
// Skip to end of chunk length (note that there can be parameters after the length)
ptr = strchr(ptr, '\n');
if (!ptr) {
// Don't have the entire chunk header
return ERR_OK;
}
ptr++;
ptr += chunk_len;
if (chunk_len == 0) {
seen_end = TRUE;
break;
}
if (ptr + 2 > hrb->data + hrb->length) {
// We don't have the CRLF yet
return ERR_OK;
}
if (memcmp(ptr, "\r\n", 2)) {
// Bail out here -- something bad happened
goto general_fail;
}
ptr += 2;
}
if (!seen_end) {
// Still waiting for the end chunk
return ERR_OK;
}
// Now rewrite the buffer to eliminate all the chunk headers
const char *src = end_of_header;
char *dst = end_of_header;
while (src < hrb->data + hrb->length && src > hrb->data) {
size_t chunk_len = strtol(src, 0, 16);
src = strchr(src, '\n');
src++;
if (chunk_len == 0) {
break;
}
memmove(dst, src, chunk_len);
dst += chunk_len;
src += chunk_len + 2;
}
*dst = '\0'; // Move the null termination down
hrb->length = dst - hrb->data; // Adjust the length down
}
err_t ret = ERR_OK; err_t ret = ERR_OK;
char *data = hrb->data;
size_t data_len = hrb->length;
tcp_arg(http_client, 0); // Forget the data pointer.
#if ENDUSER_SETUP_DEBUG_SHOW_HTTP_REQUEST #if ENDUSER_SETUP_DEBUG_SHOW_HTTP_REQUEST
ENDUSER_SETUP_DEBUG(data); ENDUSER_SETUP_DEBUG(data);
#endif #endif
...@@ -1364,7 +1510,7 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s ...@@ -1364,7 +1510,7 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
{ {
if (enduser_setup_http_serve_html(http_client) != 0) if (enduser_setup_http_serve_html(http_client) != 0)
{ {
ENDUSER_SETUP_ERROR("http_recvcb failed. Unable to send HTML.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); goto general_fail;
} }
else else
{ {
...@@ -1376,27 +1522,29 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s ...@@ -1376,27 +1522,29 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
/* Don't do an AP Scan while station is trying to connect to Wi-Fi */ /* Don't do an AP Scan while station is trying to connect to Wi-Fi */
if (state->connecting == 0) if (state->connecting == 0)
{ {
scan_listener_t *l = malloc (sizeof (scan_listener_t)); scan_listener_t *sl = malloc (sizeof (scan_listener_t));
if (!l) if (!sl)
{ {
ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL);
} }
sl->struct_type = SCAN_LISTENER_STRUCT_TYPE;
bool already = (state->scan_listeners != NULL); bool already = (state->scan_listeners != NULL);
tcp_arg (http_client, l); tcp_arg (http_client, sl);
/* TODO: check if also need a tcp_err() cb, or if recv() is enough */ /* TODO: check if also need a tcp_err() cb, or if recv() is enough */
l->conn = http_client; sl->conn = http_client;
l->next = state->scan_listeners; sl->next = state->scan_listeners;
state->scan_listeners = l; state->scan_listeners = sl;
if (!already) if (!already)
{ {
if (!wifi_station_scan(NULL, on_scan_done)) if (!wifi_station_scan(NULL, on_scan_done))
{ {
enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500)); enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500));
deferred_close (l->conn); deferred_close (sl->conn);
l->conn = 0; sl->conn = 0;
free_scan_listeners(); free_scan_listeners();
} }
} }
...@@ -1410,13 +1558,12 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s ...@@ -1410,13 +1558,12 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
} }
else if (strncmp(data + 4, "/status.json", 12) == 0) else if (strncmp(data + 4, "/status.json", 12) == 0)
{ {
enduser_setup_serve_status_as_json(http_client); enduser_setup_serve_status_as_json(http_client);
} }
else if (strncmp(data + 4, "/status", 7) == 0) else if (strncmp(data + 4, "/status", 7) == 0)
{ {
enduser_setup_serve_status(http_client); enduser_setup_serve_status(http_client);
} }
else if (strncmp(data + 4, "/update?", 8) == 0) else if (strncmp(data + 4, "/update?", 8) == 0)
{ {
switch (enduser_setup_http_handle_credentials(data, data_len)) switch (enduser_setup_http_handle_credentials(data, data_len))
...@@ -1459,8 +1606,11 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s ...@@ -1459,8 +1606,11 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
deferred_close (http_client); deferred_close (http_client);
free_out: free_out:
free (data); free (hrb);
return ret; return ret;
general_fail:
ENDUSER_SETUP_ERROR("http_recvcb failed. Unable to send HTML.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
} }
...@@ -1476,6 +1626,7 @@ static err_t enduser_setup_http_connectcb(void *arg, struct tcp_pcb *pcb, err_t ...@@ -1476,6 +1626,7 @@ static err_t enduser_setup_http_connectcb(void *arg, struct tcp_pcb *pcb, err_t
} }
tcp_accepted (state->http_pcb); tcp_accepted (state->http_pcb);
tcp_arg(pcb, 0); // Initialize to known value
tcp_recv (pcb, enduser_setup_http_recvcb); tcp_recv (pcb, enduser_setup_http_recvcb);
tcp_nagle_disable (pcb); tcp_nagle_disable (pcb);
return ERR_OK; return ERR_OK;
......
...@@ -274,8 +274,13 @@ ...@@ -274,8 +274,13 @@
return b.rssi - a.rssi; return b.rssi - a.rssi;
}); });
var ops = '<option>Select a Network...</option>'; var ops = '<option>Select a Network...</option>';
var seen = {};
for (var i = 0; i < list.length; ++i) { for (var i = 0; i < list.length; ++i) {
ops += '<option>' + list[i].ssid + '</option>'; var ssid = list[i].ssid;
if (!seen[ssid]) {
seen[ssid] = 1;
ops += '<option>' + ssid.replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;") + '</option>';
}
} }
ap.innerHTML = ops; ap.innerHTML = ops;
ab.disabled = false; ab.disabled = false;
......
<!DOCTYPE html>
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>WiFi Login</title>
<style type=text/css>
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
font-family: sans-serif;
text-align: center;
background: #444d44;
}
#content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 320px;
height: 480px;
margin: auto;
}
input,
button,
select {
-webkit-appearance: none;
border-radius: 0;
}
fieldset {
border: 0;
box-shadow: 0 0 15px 1px rgba(0, 0, 0, .4);
box-sizing: border-box;
padding: 20px 30px;
background: #fff;
min-height: 320px;
margin: -1px;
}
input {
border: 1px solid #ccc;
margin-bottom: 10px;
width: 100%;
box-sizing: border-box;
color: #222;
font: 16px monospace;
padding: 15px;
}
select {
font: 16px monospace;
background-color: transparent;
padding: 15px;
}
button {
color: #fff;
border: 0;
border-radius: 3px;
cursor: pointer;
display: block;
font: 16px sans-serif;
text-decoration: none;
padding: 10px 5px;
background: #31b457;
width: 100%;
}
button:focus,
button:hover {
box-shadow: 0 0 0 2px #fff, 0 0 0 3px #31b457;
}
h3 {
font-size: 16px;
color: #666;
margin-bottom: 20px;
}
h4 {
color: #ccc;
padding: 10px;
}
.utility {
float: right;
clear: both;
max-width: 75%;
font-size: 13px;
color: #222;
margin: 10px 0;
padding: 5px 10px;
background: #ccc;
}
.utility:focus,
.utility:hover {
box-shadow: 0 0 0 2px #fff, 0 0 0 3px #ccc;
}
#dropdown,
#f2,
#f3,
#bk2 {
display: none;
}
#dropdown {
position: relative;
width: 100%;
overflow: auto;
height: 51px;
margin-bottom: 10px;
}
#aplist {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
bottom: 0;
border: 1px solid #ccc;
font-family: monospace;
padding: 10px 5px;
}
#arrow {
color: #888;
position: absolute;
right: 8px;
top: 15px;
}
#i {
text-align: center;
}
</style>
</head>
<body>
<div id=content>
<fieldset>
<div id=deviceId></div>
<div id=f1>
<h3>Connect device to your Wi-Fi</h3>
<button id=networks type=button class=utility></button>
<div id=dropdown>
<span id=arrow>&#x25bc;</span>
<select id=aplist name=aplist></select>
</div>
<input id=ssid type=text autocorrect=off autocapitalize=none placeholder='Wi-Fi Name' />
<input id=wifi_password type=text autocorrect=off autocapitalize=none autocomplete=off placeholder=Password />
<button id=submit type=button>Save</button>
</div>
<div id=f2>
<h1>Success!</h1>
<div id=i>
<h3>Your device has successfully connected to the Wi-Fi network.<br /><br/>You may now close this web page.</h3>
</div>
</div>
<div id=f3>
<h2>Trying...</h2>
<button id=bk2 type=button class='utility'>Go Back to Wi-Fi Setup</button>
</div>
</fieldset>
<h4 id='st'>Updating Status...</h4>
</div>
<script>
var $ = function (selector) { return document.querySelector(selector); };
var ab = $('#networks'), ap = $('#aplist');
var stopAll = false, ra, rs, submitted = false;
function show(f, y) {
if (y == null) y = f;
$(f).style.display = y == f ? 'block' : 'none';
}
function hide(f) {
$(f).style.display = 'none';
}
function to(cb, x) {
return setTimeout(cb, 1000 * x);
}
function refr() {
if (!stopAll)
fetch('/status.json?n=' + Math.random(), 'GET', newSt, 2);
}
function cur(f) {
show('#f1', f);
show('#f2', f);
show('#f3', f);
}
function newSt(s, d) {
clearTimeout(rs);
rs = to(refr, 3);
if (s != 200) {
$('#st').innerText = 'Awaiting Status (' + s + ')';
} else {
if (typeof d === 'string') {
d = JSON.parse(d);
}
$('#deviceId').innerText = "Device: " + d.deviceid;
var c = d.pairing;
var st = [
'Idle',
'Connecting...',
'Failed - wrong password',
'Failed - network not found',
'Failed',
'Wi-Fi successfully connected!'
][d.status];
if (st == null)
st = "";
if (!submitted && d.status > 1 && d.status < 5)
st = "Need a valid Network and Password";
$('#st').innerText = st;
if (d.status === 5) {
cur('#f2');
stopAll = true;
clearTimeout(ra);
} else if (d.status > 1) {
cur('#f1');
}
}
}
function submit() {
submitted = true;
var url = '/update?wifi_ssid=' + encodeURIComponent($('#ssid').value) + '&wifi_password=' + encodeURIComponent($('#wifi_password').value);
clearTimeout(rs);
fetch(url, 'GET', refr, 2);
cur('#f3');
}
function fetch(url, method, callback, time_out) {
var xhr = new XMLHttpRequest();
xhr.onloadend = function () {
callback(xhr.status, xhr.responseText);
}
xhr.ontimeout = function () {
callback(-1, null);
}
xhr.open(method, url, true);
xhr.setRequestHeader('Accept', 'application/json');
xhr.timeout = (time_out || 10) * 1000;
xhr.send();
}
function gotAp(s, json) {
var list;
if (s === 200 && json != null) {
if (typeof json === 'string' && json.length > 0) {
list = JSON.parse(json);
} else if (typeof json === 'object') {
list = json;
}
list.sort(function (a, b) {
return b.rssi - a.rssi;
});
var ops = '<option>Select a Network...</option>';
for (var i = 0; i < list.length; ++i) {
ops += '<option>' + list[i].ssid + '</option>';
}
ap.innerHTML = ops;
ab.disabled = false;
togAp(null, true);
ab.onclick = togAp;
} else {
ab.innerText = 'No networks found (' + s + ')';
ra = to(refrAp, 5);
}
}
function togAp(ev, force) {
if (!force || ap.style.display == 'block') {
hide('#dropdown');
show('#ssid');
ab.innerText = 'Scan for Networks';
ab.onclick = refrAp;
} else {
show('#dropdown');
hide('#ssid');
ab.innerText = 'Manual Entry';
}
}
function refrAp() {
ab.innerText = 'Searching for networks...';
ab.disabled = true;
ap.innerHTML = '<option disabled>Scanning...</option>';
if (!stopAll)
fetch('/aplist?n=' + Math.random(), 'GET', gotAp, 10);
}
window.onload = function() {
ab.innerText = 'Scan for Networks';
ab.onclick = refrAp;
$('#aplist').onchange = function () {
$('#ssid').value = $('#aplist').value;
};
$('#submit').onclick = submit;
$('#bk2').onclick = function () {
cur('#f1')
}
rs = to(refr, 0.5);
}
</script>
</body>
</html>
\ No newline at end of file
...@@ -6,13 +6,6 @@ ...@@ -6,13 +6,6 @@
This module provides a simple way of configuring ESP8266 chips without using a This module provides a simple way of configuring ESP8266 chips without using a
serial interface or pre-programming WiFi credentials onto the chip. serial interface or pre-programming WiFi credentials onto the chip.
!!! attention "ATTENTION Apple users"
Due to bug [#2931](https://github.com/nodemcu/nodemcu-firmware/issues/2931) the configuration does currently not work for many Safari browsers (iOS & macOS).
As a **workaround** there is alternative HTML file which uses another method to transfer the login credentials. It does not support sending arbitrary additional configuration parameters and likewise no `eus_params.lua` will be written. The WiFi credentials will be stored in the ESP flash.
Just copy [enduser_setup_apple.html](../../app/modules/enduser_setup/enduser_setup_apple.html) to the ESP file system and rename it to `enduser_setup.html`.
After running [`enduser_setup.start()`](#enduser_setupstart), a wireless After running [`enduser_setup.start()`](#enduser_setupstart), a wireless
network named "SetupGadget_XXXXXX" will starting. This prefix can be overridden network named "SetupGadget_XXXXXX" will starting. This prefix can be overridden
in `user_config.h` by defining `ENDUSER_SETUP_AP_SSID`. Connect to that SSID in `user_config.h` by defining `ENDUSER_SETUP_AP_SSID`. Connect to that SSID
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment