2015-09-19 21:06:56 +02:00
/*
* Copyright 2015 Robert Foss . All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* - Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* - Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the
* distribution .
* - Neither the name of the copyright holders nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT ,
* INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT ,
* STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE .
*
* @ author Robert Foss < dev @ robertfoss . se >
2016-01-28 06:52:09 +01:00
*
* Additions & fixes : Johny Mattsson < jmattsson @ dius . com . au >
2015-09-19 21:06:56 +02:00
*/
2015-12-16 06:04:58 +01:00
# include "module.h"
2015-09-02 14:41:27 +02:00
# include "lauxlib.h"
2016-03-08 22:55:53 +01:00
# include "lmem.h"
2015-09-02 14:41:27 +02:00
# include "platform.h"
# include "c_stdlib.h"
2016-01-28 06:52:09 +01:00
# include "c_stdio.h"
2015-09-02 14:41:27 +02:00
# include "c_string.h"
2016-01-20 09:37:03 +01:00
# include "ctype.h"
2015-09-02 14:41:27 +02:00
# include "user_interface.h"
# include "espconn.h"
2016-03-24 09:25:49 +01:00
# include "lwip/tcp.h"
# include "lwip/pbuf.h"
2015-09-02 14:41:27 +02:00
# include "flash_fs.h"
2016-03-01 07:54:10 +01:00
# include "task/task.h"
2015-09-02 14:41:27 +02:00
# define MIN(x, y) (((x) < (y)) ? (x) : (y))
2016-01-28 06:52:09 +01:00
# define LITLEN(strliteral) (sizeof (strliteral) -1)
2015-09-02 14:41:27 +02:00
2015-09-19 21:06:56 +02:00
# define ENDUSER_SETUP_ERR_FATAL (1 << 0)
# define ENDUSER_SETUP_ERR_NONFATAL (1 << 1)
# define ENDUSER_SETUP_ERR_NO_RETURN (1 << 2)
# define ENDUSER_SETUP_ERR_OUT_OF_MEMORY 1
# define ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND 2
# define ENDUSER_SETUP_ERR_UNKOWN_ERROR 3
# define ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN 4
2015-09-02 14:41:27 +02:00
/**
* DNS Response Packet :
*
* | DNS ID - 16 bits |
* | dns_header |
* | QNAME |
* | dns_body |
* | ip - 32 bits |
*
2015-09-16 14:56:44 +02:00
* DNS Header Part | FLAGS | | Q COUNT | | A CNT | | AUTH CNT | | ADD CNT | */
static const char dns_header [ ] = { 0x80 , 0x00 , 0x00 , 0x01 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 } ;
/* DNS Query Part | Q TYPE | | Q CLASS| */
static const char dns_body [ ] = { 0x00 , 0x01 , 0x00 , 0x01 ,
/* DNS Answer Part |LBL OFFS| | TYPE | | CLASS | | TTL | | RD LEN | */
0xC0 , 0x0C , 0x00 , 0x01 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x78 , 0x00 , 0x04 } ;
2016-03-29 17:25:05 +02:00
static const char http_html_filename [ ] = " enduser_setup.html " ;
2016-01-28 06:52:09 +01:00
static const char http_header_200 [ ] = " HTTP/1.1 200 OK \r \n Cache-control:no-cache \r \n Content-Type: text/html \r \n " ; /* Note single \r\n here! */
2016-03-08 22:55:53 +01:00
static const char http_header_204 [ ] = " HTTP/1.1 204 No Content \r \n \r \n " ;
2016-01-28 06:52:09 +01:00
static const char http_header_302 [ ] = " HTTP/1.1 302 Moved \r \n Location: / \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
< ! DOCTYPE html >
< html >
< head >
< meta http - equiv = " content-type " content = " text/html; charset=UTF-8 " >
< meta charset = " utf-8 " >
< meta name = " viewport " content = " width=380 " >
< title > WiFi Login < / title >
< style media = " screen " type = " text/css " >
* { margin : 0 ; padding : 0 }
html { height : 100 % ; background : linear - gradient ( rgba ( 196 , 102 , 0 , .2 ) , rgba ( 155 , 89 , 182 , .2 ) ) , url ( data : image / png ; base64 , iVBORw0KGgoAAAANSUhEUgAAAEYAAAA8AgMAAACm + SSwAAAADFBMVEVBR1FFS1VHTlg8Q0zU / YXIAAADVElEQVQ4yy1TTYvTUBQ9GTKiYNoodsCF4MK6U4TZChOhiguFWHyBFzqlLl4hoeNvEBeCrlrhBVKq1EUKLTP + hvi1GyguXqBdiZCBzGqg20K8L3hDQnK55 + OeJNguHx6UujYl3dL5ALn4JOIUluAqeAWciyGaSdvngOWzNT + G0UyGUOxVOAdqkjXDCbBiUyjZ5QzYEbGadYAi6kHxth + kthXNVNCDofwhGv1D4QGGiM9iAjbCHgr2iUUpDJbs + VPQ4xAr2fX7KXbkOJMdok965Ksb + 6l rjdkem8AshIuHm9Nyu19uTunYlOXDTQqi8VgeH0kBXH2xq / ouiMZPzuMukymutrBmulUTovC6HqNFW2ZOiqlpSXZOTvSUeUPxChjxol8BLbRy4gJuhV7OR4LRVBs3WQ9VVAU7SXgK2HeUrOj7bC8YsUgr3lEV / TXB7hK90EBnxaeg1Ov15bY80M736ekCGesGAaGvG0Ct4WRkVQVHIgIM9xJgvSFfPay8Q6GNv7VpR7xUnkvhnMQCJDYkYOtNLihV70tCU1Sk + BQrpoP + HLHUrJkuta40C6LP5GvBv + Hqo10ATxxFrTPvNdPr7XwgQud6RvQN / sXjBGzqbU27wcj9cgsyvSTrpyXV8gKpXeNJU3aFl7MOdldzV4 + HfO19jBa5f2IjWwx1OLHIvFHkqbBj20ro1g7nDfY1DpScvDRUNARgjMMVO0zoMjKxJ6uWCPP + YRAWbGoaN8kXYHmLjB9FXLGOazfFVCvOgqzfnicNPrHtPKlex2ye824gMza0cTZ2sS2Xm7Qst / UfFw8O6vVtmUKxZy9xFgzMys5cJ5fxZw4y37Ufk1Dsfb8MqOjYxE3ZMWxiDcO0PYUaD2ys + 8 OW1pbB7 / e3sfZeGVCL0Q2aMjjPdm2sxADuejZxHJAd8dO9DSUdA0V8 / NggRRanDkBrANn8yHlEQOn / MmwoQfQF7xgmKDnv520bS / pgylP67vf3y2V5sCwfoCEMkZClgOfJAFX9eXefR2RpnmRs4CDVPceaRfoFzCkJVJX27vWZnoqyvmtXU3 + dW1EIXIu8Qg5Qta4Zlv7drUCoWe8 / 8 MXzaEwux7ESE9h6qnHj3mIO0 / D9RvzfxPmjWiQ1vbeSk4rrHwhAre35EEVaAAAAAElFTkSuQmCC ) }
body { font - family : arial , verdana }
2016-03-08 22:55:53 +01:00
div { position : absolute ; margin : auto ; top : - 150 px ; right : 0 ; bottom : 0 ; left : 0 ; width : 320 px ; height : 304 px }
2016-01-28 06:52:09 +01:00
form { width : 320 px ; text - align : center ; position : relative }
form fieldset { background : # fff ; border : 0 none ; border - radius : 5 px ; box - shadow : 0 0 15 px 1 px rgba ( 0 , 0 , 0 , .4 ) ; padding : 20 px 30 px ; box - sizing : border - box }
form input { padding : 15 px ; border : 1 px solid # ccc ; border - radius : 3 px ; margin - bottom : 10 px ; width : 100 % ; box - sizing : border - box ; font - family : montserrat ; color : # 2 C3E50 ; font - size : 13 px }
form . action - button { border : 0 none ; border - radius : 3 px ; cursor : pointer ; }
# msform .submit:focus,form .action-button:hover{box-shadow:0 0 0 2px #fff,0 0 0 3px #27AE60;}
2016-03-08 22:55:53 +01:00
# formFrame{display: none;}
# aplist{display: block;}
2016-01-28 06:52:09 +01:00
select { width : 100 % ; margin - bottom : 20 px ; padding : 10 px 5 px ; border : 1 px solid # ccc ; display : none ; }
. fs - title { font - size : 15 px ; text - transform : uppercase ; color : # 2 C3E50 ; margin - bottom : 10 px }
. fs - subtitle { font - weight : 400 ; font - size : 13 px ; color : # 666 ; margin - bottom : 20 px }
. fs - status { font - weight : 400 ; font - size : 13 px ; color : # 666 ; margin - bottom : 10 px ; padding - top : 20 px ; border - top : 1 px solid # ccc }
. submit { width : 100 px ; background : # 27 AE60 ; color : # fff ; font - weight : 700 ; margin : 10 px 5 px ; padding : 10 px 5 px ; }
< / style >
< / head >
< body >
< div >
2016-03-08 22:55:53 +01:00
< form id = " credentialsForm " method = " get " action = " /update " target = " formFrame " >
2016-01-28 06:52:09 +01:00
< fieldset >
2016-03-08 22:55:53 +01:00
< iframe id = " formFrame " src = " " name = " formFrame " > < / iframe > < ! - - Used to submit data , needed to prevent re - direction after submission - - >
2016-01-28 06:52:09 +01:00
< h2 class = " fs-title " > WiFi Login < / h2 >
< h3 class = " fs-subtitle " > Connect gadget to your WiFi network < / h3 >
2016-03-08 22:55:53 +01:00
< input id = " wifi_ssid " autocorrect = " off " autocapitalize = " none " name = " wifi_ssid " placeholder = " WiFi Name " >
< select id = " aplist " name = " aplist " size = " 1 " disabled >
< option > Scanning for networks . . . < / option >
2016-01-28 06:52:09 +01:00
< / select >
< input name = " wifi_password " placeholder = " Password " type = " password " >
2016-03-08 22:55:53 +01:00
< input type = submit name = save class = " action-button submit " value = " Save " >
2016-01-28 06:52:09 +01:00
< h3 class = " fs-status " > Status : < span id = " status " > Updating . . . < / span > < / h3 >
< / fieldset >
2016-03-08 22:55:53 +01:00
< h3 id = " dbg " > < / h3 >
2016-01-28 06:52:09 +01:00
< / form >
< / div >
< script >
function fetch ( url , method , callback )
{
var xhr = new XMLHttpRequest ( ) ;
xhr . onreadystatechange = check_ready ;
function check_ready ( )
{
if ( xhr . readyState = = = 4 )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
callback ( xhr . status = = = 200 ? xhr . responseText : null ) ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
}
xhr . open ( method , url , true ) ;
xhr . send ( ) ;
}
2016-03-08 22:55:53 +01:00
2016-01-28 06:52:09 +01:00
function new_status ( stat )
{
if ( stat )
{
var e = document . getElementById ( " status " ) ;
e . innerHTML = stat ;
}
}
2015-09-02 14:41:27 +02:00
2016-01-28 06:52:09 +01:00
2016-03-08 22:55:53 +01:00
function new_status_repeat ( stat )
2016-01-28 06:52:09 +01:00
{
2016-03-08 22:55:53 +01:00
new_status ( stat ) ;
setTimeout ( refresh_status , 750 ) ;
2016-01-28 06:52:09 +01:00
}
2016-03-08 22:55:53 +01:00
function new_ap_list ( json )
2016-01-28 06:52:09 +01:00
{
if ( json )
{
var list = JSON . parse ( json ) ;
2016-03-08 22:55:53 +01:00
list . sort ( function ( a , b ) { return b . rssi - a . rssi ; } ) ;
var ssids = list . map ( function ( a ) { return a . ssid ; } ) . filter ( function ( item , pos , self ) { return self . indexOf ( item ) = = pos ; } ) ;
var sel = document . getElementById ( " aplist " ) ;
2016-01-28 06:52:09 +01:00
sel . innerHTML = " " ;
2016-03-08 22:55:53 +01:00
sel . setAttribute ( " size " , Math . max ( Math . min ( 3 , list . length ) , 1 ) ) ;
sel . removeAttribute ( " disabled " ) ;
2016-01-28 06:52:09 +01:00
for ( var i = 0 ; i < ssids . length ; + + i )
{
var o = document . createElement ( " option " ) ;
o . innerHTML = ssids [ i ] ;
2016-03-08 22:55:53 +01:00
sel . options . add ( o ) ;
2016-01-28 06:52:09 +01:00
}
2016-03-08 22:55:53 +01:00
sel . style . display = ' block ' ;
2016-01-28 06:52:09 +01:00
}
}
2016-03-08 22:55:53 +01:00
function new_ap_list_repeat ( json )
{
new_ap_list ( json ) ;
setTimeout ( refresh_ap_list , 3000 ) ;
}
function refresh_status ( )
{
fetch ( ' / status ' , ' GET ' , new_status_repeat ) ;
}
function refresh_ap_list ( )
{
fetch ( ' / aplist ' , ' GET ' , new_ap_list_repeat ) ;
}
2016-03-30 16:57:30 +02:00
function set_ssid_field ( ) {
var sel = document . getElementById ( " aplist " ) ;
document . getElementById ( " wifi_ssid " ) . value = sel . value ;
}
2016-03-08 22:55:53 +01:00
window . onload = function ( )
2016-01-28 06:52:09 +01:00
{
refresh_status ( ) ;
refresh_ap_list ( ) ;
2016-03-30 16:57:30 +02:00
document . getElementById ( " aplist " ) . onclick = set_ssid_field ;
2016-04-01 03:26:22 +02:00
document . getElementById ( " aplist " ) . onchange = set_ssid_field ;
2016-03-08 22:55:53 +01:00
document . getElementById ( " credentialsForm " ) . addEventListener ( " submit " , function ( ) {
fetch ( ' / status ' , ' GET ' , new_status ) ;
} ) ;
2016-01-28 06:52:09 +01:00
}
< / script >
< / body >
< / html >
# endif
static const char http_html_backup [ ] =
2016-04-01 03:26:22 +02:00
" <!DOCTYPE html><meta http-equiv=content-type content='text/html; charset=UTF-8'><meta charset=utf-8><meta name=viewport content='width=380'><title>WiFi Login</title><style media=screen type=text/css>*{margin:0;padding:0}html{height:100%;background:linear-gradient(rgba(196,102,0,.2),rgba(155,89,182,.2)),url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAA8AgMAAACm+SSwAAAADFBMVEVBR1FFS1VHTlg8Q0zU/YXIAAADVElEQVQ4yy1TTYvTUBQ9GTKiYNoodsCF4MK6U4TZChOhiguFWHyBFzqlLl4hoeNvEBeCrlrhBVKq1EUKLTP+hvi1GyguXqBdiZCBzGqg20K8L3hDQnK55+OeJNguHx6UujYl3dL5ALn4JOIUluAqeAWciyGaSdvngOWzNT+G0UyGUOxVOAdqkjXDCbBiUyjZ5QzYEbGadYAi6kHxth+kthXNVNCDofwhGv1D4QGGiM9iAjbCHgr2iUUpDJbs+VPQ4xAr2fX7KXbkOJMdok965Ksb+6lrjdkem8AshIuHm9Nyu19uTunYlOXDTQqi8VgeH0kBXH2xq/ouiMZPzuMukymutrBmulUTovC6HqNFW2ZOiqlpSXZOTvSUeUPxChjxol8BLbRy4gJuhV7OR4LRVBs3WQ9VVAU7SXgK2HeUrOj7bC8YsUgr3lEV/TXB7hK90EBnxaeg1Ov15bY80M736ekCGesGAaGvG0Ct4WRkVQVHIgIM9xJgvSFfPay8Q6GNv7VpR7xUnkvhnMQCJDYkYOtNLihV70tCU1Sk+BQrpoP+HLHUrJkuta40C6LP5GvBv+Hqo10ATxxFrTPvNdPr7XwgQud6RvQN/sXjBGzqbU27wcj9cgsyvSTrpyXV8gKpXeNJU3aFl7MOdldzV4+HfO19jBa5f2IjWwx1OLHIvFHkqbBj20ro1g7nDfY1DpScvDRUNARgjMMVO0zoMjKxJ6uWCPP+YRAWbGoaN8kXYHmLjB9FXLGOazfFVCvOgqzfnicNPrHtPKlex2ye824gMza0cTZ2sS2Xm7Qst/UfFw8O6vVtmUKxZy9xFgzMys5cJ5fxZw4y37Ufk1Dsfb8MqOjYxE3ZMWxiDcO0PYUaD2ys+8OW1pbB7/e3sfZeGVCL0Q2aMjjPdm2sxADuejZxHJAd8dO9DSUdA0V8/NggRRanDkBrANn8yHlEQOn/MmwoQfQF7xgmKDnv520bS/pgylP67vf3y2V5sCwfoCEMkZClgOfJAFX9eXefR2RpnmRs4CDVPceaRfoFzCkJVJX27vWZnoqyvmtXU3+dW1EIXIu8Qg5Qta4Zlv7drUCoWe8/8MXzaEwux7ESE9h6qnHj3mIO0/D9RvzfxPmjWiQ1vbeSk4rrHwhAre35EEVaAAAAAElFTkSuQmCC)}body{font-family:arial,verdana}div{position:absolute;margin:auto;top:-150px;right:0;bottom:0;left:0;width:320px;height:304px}form{width:320px;text-align:center;position:relative}form fieldset{background:#fff;border:0 none;border-radius:5px;box-shadow:0 0 15px 1px rgba(0,0,0,.4);padding:20px 30px;box-sizing:border-box}form input{padding:15px;border:1px solid #ccc;border-radius:3px;margin-bottom:10px;width:100%;box-sizing:border-box;font-family:montserrat;color:#2C3E50;font-size:13px}form .action-button{border:0 none;border-radius:3px;cursor:pointer}#msform .submit:focus,form .action-button:hover{box-shadow:0 0 0 2px #fff,0 0 0 3px #27AE60}#formFrame{display:none}#aplist{display:block}select{width:100%;margin-bottom:20px;padding:10px 5px;border:1px solid #ccc;display:none}.fs-title{font-size:15px;text-transform:uppercase;color:#2C3E50;margin-bottom:10px}.fs-subtitle{font-weight:400;font-size:13px;color:#666;margin-bottom:20px}.fs-status{font-weight:400;font-size:13px;color:#666;margin-bottom:10px;padding-top:20px;border-top:1px solid #ccc}.submit{width:100px;background:#27AE60;color:#fff;font-weight:700;margin:10px 5px;padding:10px 5px}</style><div><form id=credentialsForm action=/update target=formFrame><fieldset><iframe id=formFrame src=''name=formFrame></iframe><h2 class=fs-title>WiFi Login</h2><h3 class=fs-subtitle>Connect gadget to your WiFi network</h3><input id=wifi_ssid autocorrect=off autocapitalize=none name=wifi_ssid placeholder='WiFi Name'><select id=aplist name=aplist size=1 disabled><option>Scanning for networks...</select><input name=wifi_password placeholder=Password type=password> <input type=submit name=save class='action-button submit'value=Save><h3 class=fs-status>Status: <span id=status>Updating...</span></h3></fieldset><h3 id=dbg></h3></form></div><script>function fetch(t,e,n){function s(){4===i.readyState&&n(200===i.status?i.responseText:null)}var i=new XMLHttpRequest;i.onreadystatechange=s,i.open(e,t,!0),i.send()}function new_status(t){if(t){var e=document.getElementById('status');e.innerHTML=t}}function new_status_repeat(t){new_status(t),setTimeout(refresh_status,750)}function new_ap_list(t){if(t){var e=JSON.parse(t);e.sort(function(t,e){return e.rssi-t.rssi});var n=e.map(function(t){return t.ssid}).filter(function(t,e,n){return n.indexOf(t)==e}),s=document.getElementById('aplist');s.innerHTML='',s.setAttribute('size',Math.max(Math.min(3,e.length),1)),s.removeAttribute('disabled');for(var i=0;i<n.length;++i){var a=document.createElement('option');a.in
2016-01-28 06:52:09 +01:00
2016-03-24 09:25:49 +01:00
2016-01-28 06:52:09 +01:00
typedef struct scan_listener
{
2016-03-24 09:25:49 +01:00
struct tcp_pcb * conn ;
2016-01-28 06:52:09 +01:00
struct scan_listener * next ;
} scan_listener_t ;
2015-09-19 21:06:56 +02:00
typedef struct
{
struct espconn * espconn_dns_udp ;
2016-03-24 09:25:49 +01:00
struct tcp_pcb * http_pcb ;
2015-09-19 21:06:56 +02:00
char * http_payload_data ;
uint32_t http_payload_len ;
os_timer_t check_station_timer ;
2016-03-08 22:55:53 +01:00
os_timer_t shutdown_timer ;
2015-09-19 21:06:56 +02:00
int lua_connected_cb_ref ;
int lua_err_cb_ref ;
int lua_dbg_cb_ref ;
2016-01-28 06:52:09 +01:00
scan_listener_t * scan_listeners ;
2015-09-19 21:06:56 +02:00
} enduser_setup_state_t ;
static enduser_setup_state_t * state ;
2016-01-28 06:52:09 +01:00
static bool manual = false ;
2016-03-01 07:54:10 +01:00
static task_handle_t do_station_cfg_handle ;
2015-09-02 14:41:27 +02:00
2016-03-08 22:55:53 +01:00
static int enduser_setup_manual ( lua_State * L ) ;
2015-09-12 18:07:53 +02:00
static int enduser_setup_start ( lua_State * L ) ;
static int enduser_setup_stop ( lua_State * L ) ;
2016-03-08 22:55:53 +01:00
static void enduser_setup_stop_callback ( void * ptr ) ;
2015-09-19 21:06:56 +02:00
static void enduser_setup_station_start ( void ) ;
2015-09-12 18:07:53 +02:00
static void enduser_setup_ap_start ( void ) ;
static void enduser_setup_ap_stop ( void ) ;
2016-01-20 09:37:03 +01:00
static void enduser_setup_check_station ( void * p ) ;
2016-03-08 22:55:53 +01:00
static void enduser_setup_debug ( int line , const char * str ) ;
2015-09-19 21:06:56 +02:00
# define ENDUSER_SETUP_DEBUG_ENABLE 0
# if ENDUSER_SETUP_DEBUG_ENABLE
2016-03-08 22:55:53 +01:00
# define ENDUSER_SETUP_DEBUG(str) enduser_setup_debug(__LINE__, str)
2015-09-19 21:06:56 +02:00
# else
2016-03-08 22:55:53 +01:00
# define ENDUSER_SETUP_DEBUG(str) do {} while(0)
2015-09-19 21:06:56 +02:00
# endif
2016-01-28 06:52:09 +01:00
# define ENDUSER_SETUP_ERROR(str, err, err_severity) \
do { \
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( str ) ; \
2016-01-28 06:52:09 +01:00
if ( err_severity & ENDUSER_SETUP_ERR_FATAL ) enduser_setup_stop ( lua_getstate ( ) ) ; \
2016-03-08 22:55:53 +01:00
enduser_setup_error ( __LINE__ , str , err ) ; \
2016-01-28 06:52:09 +01:00
if ( ! ( err_severity & ENDUSER_SETUP_ERR_NO_RETURN ) ) \
return err ; \
} while ( 0 )
2015-09-19 21:06:56 +02:00
2016-03-08 22:55:53 +01:00
2016-01-28 06:52:09 +01:00
# define ENDUSER_SETUP_ERROR_VOID(str, err, err_severity) \
do { \
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( str ) ; \
2016-01-28 06:52:09 +01:00
if ( err_severity & ENDUSER_SETUP_ERR_FATAL ) enduser_setup_stop ( lua_getstate ( ) ) ; \
2016-03-08 22:55:53 +01:00
enduser_setup_error ( __LINE__ , str , err ) ; \
2016-01-28 06:52:09 +01:00
if ( ! ( err_severity & ENDUSER_SETUP_ERR_NO_RETURN ) ) \
return ; \
} while ( 0 )
2015-09-19 21:06:56 +02:00
2016-03-08 22:55:53 +01:00
static void enduser_setup_debug ( int line , const char * str )
2015-09-19 21:06:56 +02:00
{
2016-03-08 22:55:53 +01:00
lua_State * L = lua_getstate ( ) ;
if ( state ! = NULL & & state - > lua_dbg_cb_ref ! = LUA_NOREF )
{
lua_rawgeti ( L , LUA_REGISTRYINDEX , state - > lua_dbg_cb_ref ) ;
lua_pushfstring ( L , " %d: \t %s " , line , str ) ;
lua_call ( L , 1 , 0 ) ;
}
}
static void enduser_setup_error ( int line , const char * str , int err )
{
ENDUSER_SETUP_DEBUG ( " enduser_setup_error " ) ;
2015-09-19 21:06:56 +02:00
2016-03-08 22:55:53 +01:00
lua_State * L = lua_getstate ( ) ;
if ( state ! = NULL & & state - > lua_err_cb_ref ! = LUA_NOREF )
2015-09-19 21:06:56 +02:00
{
lua_rawgeti ( L , LUA_REGISTRYINDEX , state - > lua_err_cb_ref ) ;
lua_pushnumber ( L , err ) ;
2016-03-08 22:55:53 +01:00
lua_pushfstring ( L , " %d: \t %s " , line , str ) ;
2015-09-19 21:06:56 +02:00
lua_call ( L , 2 , 0 ) ;
}
}
2016-03-08 22:55:53 +01:00
static void enduser_setup_connected_callback ( )
2015-09-19 21:06:56 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_connected_callback " ) ;
2015-09-19 21:06:56 +02:00
2016-03-08 22:55:53 +01:00
lua_State * L = lua_getstate ( ) ;
if ( state ! = NULL & & state - > lua_connected_cb_ref ! = LUA_NOREF )
2015-09-19 21:06:56 +02:00
{
lua_rawgeti ( L , LUA_REGISTRYINDEX , state - > lua_connected_cb_ref ) ;
lua_call ( L , 0 , 0 ) ;
}
}
2015-09-02 14:41:27 +02:00
2015-09-12 18:07:53 +02:00
static void enduser_setup_check_station_start ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_check_station_start " ) ;
2015-09-19 21:06:56 +02:00
os_timer_setfn ( & ( state - > check_station_timer ) , enduser_setup_check_station , NULL ) ;
os_timer_arm ( & ( state - > check_station_timer ) , 1 * 1000 , TRUE ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-12 18:07:53 +02:00
static void enduser_setup_check_station_stop ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_check_station_stop " ) ;
2015-09-02 14:41:27 +02:00
2016-03-01 08:33:11 +01:00
if ( state ! = NULL )
2016-03-08 22:55:53 +01:00
{
2016-03-01 08:33:11 +01:00
os_timer_disarm ( & ( state - > check_station_timer ) ) ;
2016-03-08 22:55:53 +01:00
}
2015-09-02 14:41:27 +02:00
}
/**
* Check Station
*
* Check that we ' ve successfully entered station mode .
*/
2016-01-20 09:37:03 +01:00
static void enduser_setup_check_station ( void * p )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_check_station " ) ;
2016-01-20 09:37:03 +01:00
( void ) p ;
2015-09-02 14:41:27 +02:00
struct ip_info ip ;
c_memset ( & ip , 0 , sizeof ( struct ip_info ) ) ;
wifi_get_ip_info ( STATION_IF , & ip ) ;
int i ;
char has_ip = 0 ;
for ( i = 0 ; i < sizeof ( struct ip_info ) ; + + i )
{
has_ip | = ( ( char * ) & ip ) [ i ] ;
}
2015-09-16 14:56:44 +02:00
2015-09-02 14:41:27 +02:00
if ( has_ip = = 0 )
{
return ;
}
2015-09-16 14:56:44 +02:00
2016-03-08 22:55:53 +01:00
enduser_setup_check_station_stop ( ) ;
enduser_setup_connected_callback ( ) ;
/* Trigger shutdown, but allow time for HTTP client to fetch last status. */
if ( ! manual )
{
os_timer_setfn ( & ( state - > shutdown_timer ) , enduser_setup_stop_callback , NULL ) ;
os_timer_arm ( & ( state - > shutdown_timer ) , 10 * 1000 , FALSE ) ;
}
2015-09-02 14:41:27 +02:00
}
2016-03-29 08:27:55 +02:00
/* --- Connection closing handling ----------------------------------------- */
/* It is far more memory efficient to let the other end close the connection
* first and respond to that , than us initiating the closing . The latter
* seems to leave the pcb in a fin_wait state for a long time , which can
* starve us of memory over time .
*
* By instead using the poll function to schedule a hard abort a few seconds
* from now we achieve a deadline close . The downside is a ( very ) slight
* risk of dropping the connection early , but in this application that ' s
* hidden by the retries on the JavaScript side anyway .
*/
/* Callback on timeout to hard-close a connection */
static err_t force_abort ( void * arg , struct tcp_pcb * pcb )
{
( void ) arg ;
tcp_poll ( pcb , 0 , 0 ) ;
tcp_abort ( pcb ) ;
return ERR_ABRT ;
}
/* Callback to detect a remote-close of a connection */
static err_t handle_remote_close ( void * arg , struct tcp_pcb * pcb , struct pbuf * p , err_t err )
{
( void ) arg ; ( void ) err ;
if ( p ) /* server sent us data, just ACK and move on */
{
tcp_recved ( pcb , p - > tot_len ) ;
pbuf_free ( p ) ;
}
else /* hey, remote end closed, we can do a soft close safely, yay! */
{
tcp_recv ( pcb , 0 ) ;
tcp_poll ( pcb , 0 , 0 ) ;
tcp_close ( pcb ) ;
}
return ERR_OK ;
}
/* Set up a deferred close of a connection, as discussed above. */
static inline void deferred_close ( struct tcp_pcb * pcb )
{
tcp_poll ( pcb , force_abort , 15 ) ; /* ~3sec from now */
tcp_recv ( pcb , handle_remote_close ) ;
tcp_sent ( pcb , 0 ) ;
}
/* Convenience function to queue up a close-after-send. */
static err_t close_once_sent ( void * arg , struct tcp_pcb * pcb , u16_t len )
{
( void ) arg ; ( void ) len ;
deferred_close ( pcb ) ;
return ERR_OK ;
}
/* ------------------------------------------------------------------------- */
2015-09-02 14:41:27 +02:00
/**
* Search String
*
* Search string for first occurance of any char in srch_str .
*
* @ return - 1 iff no occurance of char was found .
*/
2015-09-12 18:07:53 +02:00
static int enduser_setup_srch_str ( const char * str , const char * srch_str )
2015-09-02 14:41:27 +02:00
{
2016-01-28 06:52:09 +01:00
char * found = strpbrk ( str , srch_str ) ;
if ( ! found )
2016-03-08 22:55:53 +01:00
{
2015-09-02 14:41:27 +02:00
return - 1 ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
else
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
return found - str ;
2016-03-08 22:55:53 +01:00
}
2015-09-02 14:41:27 +02:00
}
/**
* Load HTTP Payload
*
* @ return - 0 iff payload loaded successfully
* 1 iff backup html was loaded
* 2 iff out of memory
*/
2015-09-12 18:07:53 +02:00
static int enduser_setup_http_load_payload ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_load_payload " ) ;
2015-09-02 14:41:27 +02:00
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 ) ;
2015-09-19 21:06:56 +02:00
2016-01-28 06:52:09 +01:00
const char cl_hdr [ ] = " Content-length:%5d \r \n \r \n " ;
const size_t cl_len = LITLEN ( cl_hdr ) + 3 ; /* room to expand %4d */
2015-09-02 14:41:27 +02:00
if ( f = = 0 | | err = = - 1 | | err2 = = - 1 )
{
2016-03-29 17:25:05 +02:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_load_payload unable to load file enduser_setup.html, loading backup HTML. " ) ;
2015-09-02 14:41:27 +02:00
2016-01-28 06:52:09 +01:00
int payload_len = LITLEN ( http_header_200 ) + cl_len + LITLEN ( http_html_backup ) ;
2015-09-19 21:06:56 +02:00
state - > http_payload_len = payload_len ;
state - > http_payload_data = ( char * ) c_malloc ( payload_len ) ;
if ( state - > http_payload_data = = NULL )
2015-09-02 14:41:27 +02:00
{
return 2 ;
}
2015-09-19 21:06:56 +02:00
2015-09-02 14:41:27 +02:00
int offset = 0 ;
2016-01-28 06:52:09 +01:00
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 ) ) ;
2015-09-02 14:41:27 +02:00
return 1 ;
}
2015-09-19 21:06:56 +02:00
2016-01-28 06:52:09 +01:00
int payload_len = LITLEN ( http_header_200 ) + cl_len + file_len ;
2015-09-19 21:06:56 +02:00
state - > http_payload_len = payload_len ;
state - > http_payload_data = ( char * ) c_malloc ( payload_len ) ;
if ( state - > http_payload_data = = NULL )
2015-09-02 14:41:27 +02:00
{
return 2 ;
}
2015-09-19 21:06:56 +02:00
2015-09-02 14:41:27 +02:00
int offset = 0 ;
2016-01-28 06:52:09 +01:00
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 ) ;
2015-09-19 21:06:56 +02:00
fs_read ( f , & ( state - > http_payload_data [ offset ] ) , file_len ) ;
2015-09-02 14:41:27 +02:00
return 0 ;
}
/**
* De - escape URL data
*
* Parse escaped and form encoded data of request .
2016-03-08 22:55:53 +01:00
*
* @ return - return 0 iff the HTTP parameter is decoded into a valid string .
2015-09-02 14:41:27 +02:00
*/
2016-03-08 22:55:53 +01:00
static int enduser_setup_http_urldecode ( char * dst , const char * src , int src_len , int dst_len )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_urldecode " ) ;
char * dst_start = dst ;
2016-03-31 08:46:03 +02:00
char * dst_last = dst + dst_len - 1 ; /* -1 to reserve space for last \0 */
2015-09-02 14:41:27 +02:00
char a , b ;
int i ;
2016-03-31 08:46:03 +02:00
for ( i = 0 ; i < src_len & & * src & & dst < dst_last ; + + i )
2015-09-02 14:41:27 +02:00
{
if ( ( * src = = ' % ' ) & & ( ( a = src [ 1 ] ) & & ( b = src [ 2 ] ) ) & & ( isxdigit ( a ) & & isxdigit ( b ) ) )
{
if ( a > = ' a ' )
{
a - = ' a ' - ' A ' ;
}
if ( a > = ' A ' )
{
a - = ( ' A ' - 10 ) ;
}
else
{
a - = ' 0 ' ;
}
if ( b > = ' a ' )
{
b - = ' a ' - ' A ' ;
}
if ( b > = ' A ' )
{
b - = ( ' A ' - 10 ) ;
}
else
{
b - = ' 0 ' ;
}
* dst + + = 16 * a + b ;
src + = 3 ;
i + = 2 ;
} else {
char c = * src + + ;
if ( c = = ' + ' )
{
c = ' ' ;
}
* dst + + = c ;
}
}
* dst + + = ' \0 ' ;
2016-03-31 08:46:03 +02:00
return ( i < src_len ) ; /* did we fail to process all the input? */
2015-09-02 14:41:27 +02:00
}
2016-03-01 07:54:10 +01:00
/**
* Task to do the actual station configuration .
* This config * cannot * be done in the network receive callback or serious
* issues like memory corruption occur .
*/
static void do_station_cfg ( task_param_t param , uint8_t prio )
{
struct station_config * cnf = ( struct station_config * ) param ;
( void ) prio ;
/* Best-effort disconnect-reconfig-reconnect. If the device is currently
* connected , the disconnect will work but the connect will report failure
* ( though it will actually start connecting ) . If the devices is not
* connected , the disconnect may fail but the connect will succeed . A
* solid head - in - the - sand approach seems to be the best tradeoff on
* functionality - vs - code - size .
* TODO : maybe use an error callback to at least report if the set config
* call fails .
*/
wifi_station_disconnect ( ) ;
wifi_station_set_config ( cnf ) ;
wifi_station_connect ( ) ;
2016-03-08 22:55:53 +01:00
luaM_free ( lua_getstate ( ) , cnf ) ;
2016-03-01 07:54:10 +01:00
}
2015-09-02 14:41:27 +02:00
/**
* Handle HTTP Credentials
*
* @ return - return 0 iff credentials are found and handled successfully
* return 1 iff credentials aren ' t found
* return 2 iff an error occured
*/
2015-09-12 18:07:53 +02:00
static int enduser_setup_http_handle_credentials ( char * data , unsigned short data_len )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_handle_credentials " ) ;
2015-09-02 14:41:27 +02:00
2015-10-01 20:22:45 +02:00
char * name_str = ( char * ) ( ( uint32_t ) strstr ( & ( data [ 6 ] ) , " wifi_ssid= " ) ) ;
char * pwd_str = ( char * ) ( ( uint32_t ) strstr ( & ( data [ 6 ] ) , " wifi_password= " ) ) ;
2015-09-02 14:41:27 +02:00
if ( name_str = = NULL | | pwd_str = = NULL )
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " Password or SSID string not found " ) ;
2015-09-02 14:41:27 +02:00
return 1 ;
}
2016-01-28 06:52:09 +01:00
int name_field_len = LITLEN ( " wifi_ssid= " ) ;
int pwd_field_len = LITLEN ( " wifi_password= " ) ;
2015-09-02 14:41:27 +02:00
char * name_str_start = name_str + name_field_len ;
char * pwd_str_start = pwd_str + pwd_field_len ;
int name_str_len = enduser_setup_srch_str ( name_str_start , " & " ) ;
int pwd_str_len = enduser_setup_srch_str ( pwd_str_start , " & " ) ;
2016-03-08 22:55:53 +01:00
if ( name_str_len = = - 1 | | pwd_str_len = = - 1 )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " Password or SSID HTTP paramter divider not found " ) ;
2015-09-02 14:41:27 +02:00
return 1 ;
}
2015-09-19 21:06:56 +02:00
2015-09-02 14:41:27 +02:00
2016-03-08 22:55:53 +01:00
struct station_config * cnf = luaM_malloc ( lua_getstate ( ) , sizeof ( struct station_config ) ) ;
c_memset ( cnf , 0 , sizeof ( struct station_config ) ) ;
2015-09-02 14:41:27 +02:00
2016-03-08 22:55:53 +01:00
int err ;
2016-03-31 08:46:03 +02:00
err = enduser_setup_http_urldecode ( cnf - > ssid , name_str_start , name_str_len , sizeof ( cnf - > ssid ) ) ;
err | = enduser_setup_http_urldecode ( cnf - > password , pwd_str_start , pwd_str_len , sizeof ( cnf - > password ) ) ;
2016-03-08 22:55:53 +01:00
if ( err ! = 0 )
{
ENDUSER_SETUP_DEBUG ( " Unable to decode HTTP parameter to valid password or SSID " ) ;
return 1 ;
}
ENDUSER_SETUP_DEBUG ( " " ) ;
ENDUSER_SETUP_DEBUG ( " WiFi Credentials Stored " ) ;
ENDUSER_SETUP_DEBUG ( " ----------------------- " ) ;
ENDUSER_SETUP_DEBUG ( " name: " ) ;
ENDUSER_SETUP_DEBUG ( cnf - > ssid ) ;
ENDUSER_SETUP_DEBUG ( " pass: " ) ;
ENDUSER_SETUP_DEBUG ( cnf - > password ) ;
ENDUSER_SETUP_DEBUG ( " ----------------------- " ) ;
ENDUSER_SETUP_DEBUG ( " " ) ;
task_post_medium ( do_station_cfg_handle , ( task_param_t ) cnf ) ;
2015-09-02 14:41:27 +02:00
return 0 ;
}
/**
* Serve HTML
2015-09-19 21:06:56 +02:00
*
2015-09-02 14:41:27 +02:00
* @ return - return 0 iff html was served successfully
*/
2016-03-24 09:25:49 +01:00
static int enduser_setup_http_serve_header ( struct tcp_pcb * http_client , const char * header , uint32_t header_len )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_serve_header " ) ;
2015-09-19 21:06:56 +02:00
2016-03-24 09:25:49 +01:00
err_t err = tcp_write ( http_client , header , header_len , TCP_WRITE_FLAG_COPY ) ;
if ( err ! = ERR_OK )
2015-09-02 14:41:27 +02:00
{
2016-03-29 08:27:55 +02:00
deferred_close ( http_client ) ;
2016-03-24 09:25:49 +01:00
ENDUSER_SETUP_ERROR ( " http_serve_header failed on tcp_write " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_NONFATAL ) ;
2015-09-02 14:41:27 +02:00
}
2016-03-24 09:25:49 +01:00
return 0 ;
}
static err_t streamout_sent ( void * arg , struct tcp_pcb * pcb , u16_t len )
{
( void ) len ;
unsigned offs = ( unsigned ) arg ;
if ( ! state | | ! state - > http_payload_data )
2015-09-02 14:41:27 +02:00
{
2016-03-24 09:25:49 +01:00
tcp_abort ( pcb ) ;
return ERR_ABRT ;
2015-09-02 14:41:27 +02:00
}
2016-03-24 09:25:49 +01:00
unsigned wanted_len = state - > http_payload_len - offs ;
unsigned buf_free = tcp_sndbuf ( pcb ) ;
if ( buf_free < wanted_len )
wanted_len = buf_free ;
/* no-copy write */
err_t err = tcp_write ( pcb , state - > http_payload_data + offs , wanted_len , 0 ) ;
if ( err ! = ERR_OK )
2015-09-02 14:41:27 +02:00
{
2016-03-24 09:25:49 +01:00
ENDUSER_SETUP_DEBUG ( " streaming out html failed " ) ;
tcp_abort ( pcb ) ;
return ERR_ABRT ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
2016-03-24 09:25:49 +01:00
offs + = wanted_len ;
if ( offs > = state - > http_payload_len )
{
tcp_sent ( pcb , 0 ) ;
2016-03-29 08:27:55 +02:00
deferred_close ( pcb ) ;
2016-03-24 09:25:49 +01:00
}
else
tcp_arg ( pcb , ( void * ) offs ) ;
return ERR_OK ;
2015-09-02 14:41:27 +02:00
}
/**
* Serve HTML
2015-09-16 14:56:44 +02:00
*
2015-09-02 14:41:27 +02:00
* @ return - return 0 iff html was served successfully
*/
2016-03-24 09:25:49 +01:00
static int enduser_setup_http_serve_html ( struct tcp_pcb * http_client )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_serve_html " ) ;
2015-09-19 21:06:56 +02:00
if ( state - > http_payload_data = = NULL )
2015-09-02 14:41:27 +02:00
{
enduser_setup_http_load_payload ( ) ;
}
2015-09-16 14:56:44 +02:00
2016-03-24 09:25:49 +01:00
unsigned chunklen = tcp_sndbuf ( http_client ) ;
tcp_arg ( http_client , ( void * ) chunklen ) ;
tcp_recv ( http_client , 0 ) ; /* avoid confusion about the tcp_arg */
tcp_sent ( http_client , streamout_sent ) ;
/* Begin the no-copy stream-out here */
err_t err = tcp_write ( http_client , state - > http_payload_data , chunklen , 0 ) ;
if ( err ! = 0 )
2015-09-02 14:41:27 +02:00
{
2016-03-24 09:25:49 +01:00
ENDUSER_SETUP_ERROR ( " http_serve_html failed. tcp_write failed " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_NONFATAL ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-16 14:56:44 +02:00
2015-09-02 14:41:27 +02:00
return 0 ;
}
2016-03-24 09:25:49 +01:00
static void serve_status ( struct tcp_pcb * conn )
2016-01-28 06:52:09 +01:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_serve_status " ) ;
2016-01-28 06:52:09 +01:00
const char fmt [ ] =
" HTTP/1.1 200 OK \r \n "
2016-03-29 08:27:55 +02:00
" Cache-control:no-cache \r \n "
" Connection:close \r \n "
" Content-type:text/plain \r \n "
2016-01-28 06:52:09 +01:00
" Content-length: %d \r \n "
" \r \n "
2016-03-01 07:55:45 +01:00
" %s%s " ;
2016-01-28 06:52:09 +01:00
const char * state [ ] =
{
2016-03-08 22:55:53 +01:00
" Idle. " ,
" Connecting to \" %s \" . " ,
" Failed to connect to \" %s \" - Wrong password. " ,
" Failed to connect to \" %s \" - Network not found. " ,
" Failed to connect. " ,
2016-06-04 00:21:18 +02:00
" Connected to \" %s \" (%s). "
2016-01-28 06:52:09 +01:00
} ;
2016-03-08 22:55:53 +01:00
const size_t num_states = sizeof ( state ) / sizeof ( state [ 0 ] ) ;
uint8_t curr_state = wifi_station_get_connect_status ( ) ;
if ( curr_state < num_states )
2016-01-28 06:52:09 +01:00
{
2016-03-08 22:55:53 +01:00
switch ( curr_state )
{
case STATION_CONNECTING :
case STATION_WRONG_PASSWORD :
case STATION_NO_AP_FOUND :
case STATION_GOT_IP :
{
const char * s = state [ curr_state ] ;
struct station_config config ;
wifi_station_get_config ( & config ) ;
config . ssid [ 31 ] = ' \0 ' ;
2016-06-04 00:21:18 +02:00
struct ip_info ip_info ;
wifi_get_ip_info ( STATION_IF , & ip_info ) ;
char ip_addr [ 16 ] ;
ip_addr [ 0 ] = ' \0 ' ;
if ( curr_state = = STATION_GOT_IP )
{
c_sprintf ( ip_addr , " %d.%d.%d.%d " , IP2STR ( & ip_info . ip . addr ) ) ;
}
2016-03-08 22:55:53 +01:00
int state_len = c_strlen ( s ) ;
2016-06-04 00:21:18 +02:00
int ip_len = c_strlen ( ip_addr ) ;
2016-03-08 22:55:53 +01:00
int ssid_len = c_strlen ( config . ssid ) ;
2016-06-04 00:21:18 +02:00
int status_len = state_len + ssid_len + ip_len + 1 ;
2016-03-08 22:55:53 +01:00
char status_buf [ status_len ] ;
memset ( status_buf , 0 , status_len ) ;
2016-06-04 00:21:18 +02:00
status_len = c_sprintf ( status_buf , s , config . ssid , ip_addr ) ;
2016-03-08 22:55:53 +01:00
int buf_len = sizeof ( fmt ) + status_len + 10 ; //10 = (9+1), 1 byte is '\0' and 9 are reserved for length field
char buf [ buf_len ] ;
memset ( buf , 0 , buf_len ) ;
int output_len = c_sprintf ( buf , fmt , status_len , status_buf ) ;
enduser_setup_http_serve_header ( conn , buf , output_len ) ;
}
break ;
/* Handle non-formatted strings */
default :
{
const char * s = state [ curr_state ] ;
int status_len = c_strlen ( s ) ;
int buf_len = sizeof ( fmt ) + status_len + 10 ; //10 = (9+1), 1 byte is '\0' and 9 are reserved for length field
char buf [ buf_len ] ;
memset ( buf , 0 , buf_len ) ;
int output_len = c_sprintf ( buf , fmt , status_len , s ) ;
enduser_setup_http_serve_header ( conn , buf , output_len ) ;
}
break ;
}
2016-03-01 07:55:45 +01:00
}
2016-03-08 22:55:53 +01:00
else
2016-03-01 07:55:45 +01:00
{
2016-03-08 22:55:53 +01:00
enduser_setup_http_serve_header ( conn , http_header_500 , LITLEN ( http_header_500 ) ) ;
2016-01-28 06:52:09 +01:00
}
}
2015-09-02 14:41:27 +02:00
2016-01-28 06:52:09 +01:00
/* --- WiFi AP scanning support -------------------------------------------- */
static void free_scan_listeners ( void )
2015-09-02 14:41:27 +02:00
{
2016-01-28 06:52:09 +01:00
if ( ! state | | ! state - > scan_listeners )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
return ;
2016-03-08 22:55:53 +01:00
}
2015-09-02 14:41:27 +02:00
2016-01-28 06:52:09 +01:00
scan_listener_t * l = state - > scan_listeners , * next = 0 ;
while ( l )
2015-09-02 14:41:27 +02:00
{
2016-01-28 06:52:09 +01:00
next = l - > next ;
c_free ( l ) ;
l = next ;
}
state - > scan_listeners = 0 ;
}
2016-03-24 09:25:49 +01:00
static void remove_scan_listener ( scan_listener_t * l )
2016-01-28 06:52:09 +01:00
{
2016-03-24 09:25:49 +01:00
if ( state )
2016-01-28 06:52:09 +01:00
{
scan_listener_t * * sl = & state - > scan_listeners ;
while ( * sl )
2015-10-01 20:22:45 +02:00
{
2016-01-28 06:52:09 +01:00
/* Remove any and all references to the closed conn from the scan list */
2016-03-24 09:25:49 +01:00
if ( * sl = = l )
2016-01-28 06:52:09 +01:00
{
* sl = l - > next ;
c_free ( l ) ;
/* No early exit to guard against multi-entry on list */
}
else
sl = & ( * sl ) - > next ;
2015-10-01 20:22:45 +02:00
}
2016-01-28 06:52:09 +01:00
}
}
2016-03-08 22:55:53 +01:00
2016-01-28 06:52:09 +01:00
static char * escape_ssid ( char * dst , const char * src )
{
for ( int i = 0 ; i < 32 & & src [ i ] ; + + i )
{
if ( src [ i ] = = ' \\ ' | | src [ i ] = = ' " ' )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
* dst + + = ' \\ ' ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
* dst + + = src [ i ] ;
}
return dst ;
}
static void notify_scan_listeners ( const char * payload , size_t sz )
{
if ( ! state )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
return ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
for ( scan_listener_t * l = state - > scan_listeners ; l ; l = l - > next )
{
2016-03-24 09:25:49 +01:00
if ( tcp_write ( l - > conn , payload , sz , TCP_WRITE_FLAG_COPY ) ! = ERR_OK )
2016-03-08 22:55:53 +01:00
{
ENDUSER_SETUP_DEBUG ( " failed to send wifi list " ) ;
2016-03-29 08:27:55 +02:00
tcp_abort ( l - > conn ) ;
2016-03-08 22:55:53 +01:00
}
2016-03-29 08:27:55 +02:00
else
tcp_sent ( l - > conn , close_once_sent ) ; /* TODO: time-out sends? */
2016-03-24 09:25:49 +01:00
l - > conn = 0 ;
2016-01-28 06:52:09 +01:00
}
free_scan_listeners ( ) ;
}
2015-09-16 14:56:44 +02:00
2016-01-28 06:52:09 +01:00
static void on_scan_done ( void * arg , STATUS status )
{
if ( ! state | | ! state - > scan_listeners )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
return ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
if ( status = = OK )
{
unsigned num_nets = 0 ;
for ( struct bss_info * wn = arg ; wn ; wn = wn - > next . stqe_next )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
+ + num_nets ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
const char header_fmt [ ] =
" HTTP/1.1 200 OK \r \n "
" Connection:close \r \n "
2016-03-29 08:27:55 +02:00
" Cache-control:no-cache \r \n "
2016-01-28 06:52:09 +01:00
" 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 )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
goto serve_500 ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
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 )
2015-10-01 20:22:45 +02:00
{
2016-01-28 06:52:09 +01:00
if ( wn ! = arg )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
* p + + = ' , ' ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
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 + + = ' } ' ;
2015-10-01 20:22:45 +02:00
}
2016-01-28 06:52:09 +01:00
* 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 ------------------------------------------- */
2016-03-24 09:25:49 +01:00
static err_t enduser_setup_http_recvcb ( void * arg , struct tcp_pcb * http_client , struct pbuf * p , err_t err )
2016-01-28 06:52:09 +01:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_recvcb " ) ;
2016-03-29 08:27:55 +02:00
2016-03-24 09:25:49 +01:00
if ( ! state | | err ! = ERR_OK )
2016-01-28 06:52:09 +01:00
{
2016-03-24 09:25:49 +01:00
if ( ! state )
ENDUSER_SETUP_DEBUG ( " ignoring received data while stopped " ) ;
tcp_abort ( http_client ) ;
return ERR_ABRT ;
}
if ( ! p ) /* remote side closed, close our end too */
{
ENDUSER_SETUP_DEBUG ( " connection closed " ) ;
scan_listener_t * l = arg ; /* if it's waiting for scan, we have a ptr here */
if ( l )
remove_scan_listener ( l ) ;
2016-03-29 08:27:55 +02:00
deferred_close ( http_client ) ;
2016-03-24 09:25:49 +01:00
return ERR_OK ;
2015-09-02 14:41:27 +02:00
}
2016-01-28 06:52:09 +01:00
2016-03-24 09:25:49 +01:00
char * data = os_zalloc ( p - > tot_len + 1 ) ;
if ( ! data )
return ERR_MEM ;
unsigned data_len = pbuf_copy_partial ( p , data , p - > tot_len , 0 ) ;
tcp_recved ( http_client , p - > tot_len ) ;
pbuf_free ( p ) ;
2016-03-29 08:27:55 +02:00
err_t ret = ERR_OK ;
2016-01-28 06:52:09 +01:00
if ( c_strncmp ( data , " GET " , 4 ) = = 0 )
2015-09-02 14:41:27 +02:00
{
2016-01-28 06:52:09 +01:00
if ( c_strncmp ( data + 4 , " / " , 2 ) = = 0 )
2015-10-01 20:22:45 +02:00
{
2016-01-28 06:52:09 +01:00
if ( enduser_setup_http_serve_html ( http_client ) ! = 0 )
2016-03-08 22:55:53 +01:00
{
2016-03-24 09:25:49 +01:00
ENDUSER_SETUP_ERROR ( " http_recvcb failed. Unable to send HTML. " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_NONFATAL ) ;
}
else
{
2016-03-29 08:27:55 +02:00
goto free_out ; /* streaming now in progress */
2016-03-08 22:55:53 +01:00
}
2015-10-01 20:22:45 +02:00
}
2016-01-28 06:52:09 +01:00
else if ( c_strncmp ( data + 4 , " /aplist " , 8 ) = = 0 )
{
scan_listener_t * l = os_malloc ( sizeof ( scan_listener_t ) ) ;
if ( ! l )
2016-03-08 22:55:53 +01:00
{
2016-03-24 09:25:49 +01:00
ENDUSER_SETUP_ERROR ( " out of memory " , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_NONFATAL ) ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
bool already = ( state - > scan_listeners ! = NULL ) ;
2016-03-24 09:25:49 +01:00
tcp_arg ( http_client , l ) ;
/* TODO: check if also need a tcp_err() cb, or if recv() is enough */
l - > conn = http_client ;
2016-01-28 06:52:09 +01:00
l - > next = state - > scan_listeners ;
state - > scan_listeners = l ;
2015-09-16 14:56:44 +02:00
2016-01-28 06:52:09 +01:00
if ( ! already )
{
2016-03-08 22:55:53 +01:00
if ( ! wifi_station_scan ( NULL , on_scan_done ) )
2016-01-28 06:52:09 +01:00
{
2016-03-08 22:55:53 +01:00
enduser_setup_http_serve_header ( http_client , http_header_500 , LITLEN ( http_header_500 ) ) ;
2016-03-29 08:27:55 +02:00
deferred_close ( l - > conn ) ;
2016-03-24 09:25:49 +01:00
l - > conn = 0 ;
2016-03-08 22:55:53 +01:00
free_scan_listeners ( ) ;
2016-01-28 06:52:09 +01:00
}
}
2016-03-29 08:27:55 +02:00
goto free_out ; /* request queued */
2016-01-28 06:52:09 +01:00
}
else if ( c_strncmp ( data + 4 , " /status " , 8 ) = = 0 )
{
2016-03-08 22:55:53 +01:00
serve_status ( http_client ) ;
2016-01-28 06:52:09 +01:00
}
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 :
2016-03-24 09:25:49 +01:00
ENDUSER_SETUP_ERROR ( " http_recvcb failed. Failed to handle wifi credentials. " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_NONFATAL ) ;
2016-01-28 06:52:09 +01:00
break ;
}
}
2016-03-08 22:55:53 +01:00
else if ( c_strncmp ( data + 4 , " /generate_204 " , 14 ) = = 0 )
{
2016-03-29 17:25:05 +02:00
/* Convince Android devices that they have internet access to avoid pesky dialogues. */
2016-03-08 22:55:53 +01:00
enduser_setup_http_serve_header ( http_client , http_header_204 , LITLEN ( http_header_204 ) ) ;
}
2016-01-28 06:52:09 +01:00
else
2015-10-01 20:22:45 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " serving 404 " ) ;
ENDUSER_SETUP_DEBUG ( data + 4 ) ;
2016-01-28 06:52:09 +01:00
enduser_setup_http_serve_header ( http_client , http_header_404 , LITLEN ( http_header_404 ) ) ;
2015-10-01 20:22:45 +02:00
}
2015-09-02 14:41:27 +02:00
}
2016-01-28 06:52:09 +01:00
else /* not GET */
2015-09-02 14:41:27 +02:00
{
2016-01-28 06:52:09 +01:00
enduser_setup_http_serve_header ( http_client , http_header_401 , LITLEN ( http_header_401 ) ) ;
2015-09-02 14:41:27 +02:00
}
2016-01-28 06:52:09 +01:00
2016-03-29 08:27:55 +02:00
deferred_close ( http_client ) ;
free_out :
2016-03-24 09:25:49 +01:00
os_free ( data ) ;
2016-03-29 08:27:55 +02:00
return ret ;
2015-09-02 14:41:27 +02:00
}
2016-03-24 09:25:49 +01:00
static err_t enduser_setup_http_connectcb ( void * arg , struct tcp_pcb * pcb , err_t err )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_connectcb " ) ;
2016-03-24 09:25:49 +01:00
if ( ! state )
2015-09-02 14:41:27 +02:00
{
2016-03-24 09:25:49 +01:00
ENDUSER_SETUP_DEBUG ( " connect callback but no state?! " ) ;
tcp_abort ( pcb ) ;
return ERR_ABRT ;
2015-09-02 14:41:27 +02:00
}
2016-03-24 09:25:49 +01:00
tcp_accepted ( state - > http_pcb ) ;
tcp_recv ( pcb , enduser_setup_http_recvcb ) ;
2016-03-29 08:27:55 +02:00
tcp_nagle_disable ( pcb ) ;
2016-03-24 09:25:49 +01:00
return ERR_OK ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
static int enduser_setup_http_start ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_start " ) ;
2016-03-24 09:25:49 +01:00
struct tcp_pcb * pcb = tcp_new ( ) ;
if ( pcb = = NULL )
2015-09-02 14:41:27 +02:00
{
2016-03-24 09:25:49 +01:00
ENDUSER_SETUP_ERROR ( " http_start failed. Memory allocation failed (http_pcb == NULL). " , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2016-03-24 09:25:49 +01:00
if ( tcp_bind ( pcb , IP_ADDR_ANY , 80 ) ! = ERR_OK )
2015-09-02 14:41:27 +02:00
{
2016-03-24 09:25:49 +01:00
ENDUSER_SETUP_ERROR ( " http_start bind failed " , ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2016-03-24 09:25:49 +01:00
state - > http_pcb = tcp_listen ( pcb ) ;
if ( ! state - > http_pcb )
2015-09-02 14:41:27 +02:00
{
2016-03-24 09:25:49 +01:00
tcp_abort ( pcb ) ; /* original wasn't freed for us */
ENDUSER_SETUP_ERROR ( " http_start listen failed " , ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-16 14:56:44 +02:00
2016-03-24 09:25:49 +01:00
tcp_accept ( state - > http_pcb , enduser_setup_http_connectcb ) ;
/* TODO: check lwip tcp timeouts */
#if 0
2015-09-19 21:06:56 +02:00
err = espconn_regist_time ( state - > espconn_http_tcp , 2 , 0 ) ;
2015-09-02 14:41:27 +02:00
if ( err = = ESPCONN_ARG )
{
2016-01-28 06:52:09 +01:00
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 ) ;
2015-09-02 14:41:27 +02:00
}
2016-03-24 09:25:49 +01:00
# endif
2015-09-02 14:41:27 +02:00
2016-03-24 09:25:49 +01:00
int err = enduser_setup_http_load_payload ( ) ;
2015-09-02 14:41:27 +02:00
if ( err = = 1 )
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_start info. Loaded backup HTML. " ) ;
2015-09-02 14:41:27 +02:00
}
else if ( err = = 2 )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR ( " http_start failed. Unable to allocate memory for HTTP payload. " , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
return 0 ;
2015-09-02 14:41:27 +02:00
}
2015-09-12 18:07:53 +02:00
static void enduser_setup_http_stop ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_http_stop " ) ;
2015-09-02 14:41:27 +02:00
2016-03-24 09:25:49 +01:00
if ( state & & state - > http_pcb )
2015-09-02 14:41:27 +02:00
{
2016-03-24 09:25:49 +01:00
tcp_close ( state - > http_pcb ) ; /* cannot fail for listening sockets */
state - > http_pcb = 0 ;
2015-09-02 14:41:27 +02:00
}
}
2015-09-12 18:07:53 +02:00
static void enduser_setup_ap_stop ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_station_stop " ) ;
2015-09-02 14:41:27 +02:00
wifi_set_opmode ( ~ SOFTAP_MODE & wifi_get_opmode ( ) ) ;
}
2015-09-12 18:07:53 +02:00
static void enduser_setup_ap_start ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_ap_start " ) ;
2015-09-02 14:41:27 +02:00
struct softap_config cnf ;
2015-09-27 20:02:25 +02:00
c_memset ( & ( cnf ) , 0 , sizeof ( struct softap_config ) ) ;
2015-12-21 04:50:36 +01:00
# ifndef ENDUSER_SETUP_AP_SSID
# define ENDUSER_SETUP_AP_SSID "SetupGadget"
# endif
2015-09-27 20:02:25 +02:00
char ssid [ ] = ENDUSER_SETUP_AP_SSID ;
2016-01-28 06:52:09 +01:00
int ssid_name_len = c_strlen ( ssid ) ;
2015-09-27 20:02:25 +02:00
c_memcpy ( & ( cnf . ssid ) , ssid , ssid_name_len ) ;
uint8_t mac [ 6 ] ;
wifi_get_macaddr ( SOFTAP_IF , mac ) ;
cnf . ssid [ ssid_name_len ] = ' _ ' ;
2016-01-28 06:52:09 +01:00
c_sprintf ( cnf . ssid + ssid_name_len + 1 , " %02X%02X%02X " , mac [ 3 ] , mac [ 4 ] , mac [ 5 ] ) ;
cnf . ssid_len = ssid_name_len + 7 ;
2015-09-02 14:41:27 +02:00
cnf . channel = 1 ;
cnf . authmode = AUTH_OPEN ;
cnf . ssid_hidden = 0 ;
cnf . max_connection = 5 ;
cnf . beacon_interval = 100 ;
2016-01-28 06:52:09 +01:00
wifi_set_opmode ( STATIONAP_MODE ) ;
2015-09-02 14:41:27 +02:00
wifi_softap_set_config ( & cnf ) ;
}
2015-09-12 18:07:53 +02:00
static void enduser_setup_dns_recv_callback ( void * arg , char * recv_data , unsigned short recv_len )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_dns_recv_callback. " ) ;
2015-09-02 14:41:27 +02:00
struct espconn * callback_espconn = arg ;
struct ip_info ip_info ;
2015-09-19 21:06:56 +02:00
uint32_t qname_len = c_strlen ( & ( recv_data [ 12 ] ) ) + 1 ; // \0=1byte
uint32_t dns_reply_static_len = ( uint32_t ) sizeof ( dns_header ) + ( uint32_t ) sizeof ( dns_body ) + 2 + 4 ; // dns_id=2bytes, ip=4bytes
uint32_t dns_reply_len = dns_reply_static_len + qname_len ;
2015-09-02 14:41:27 +02:00
uint8_t if_mode = wifi_get_opmode ( ) ;
if ( ( if_mode & SOFTAP_MODE ) = = 0 )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR_VOID ( " dns_recv_callback failed. Interface mode not supported. " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
2015-09-02 14:41:27 +02:00
uint8_t if_index = ( if_mode = = STATION_MODE ? STATION_IF : SOFTAP_IF ) ;
if ( wifi_get_ip_info ( if_index , & ip_info ) = = false )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR_VOID ( " dns_recv_callback failed. Unable to get interface IP. " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
2015-09-02 14:41:27 +02:00
char * dns_reply = ( char * ) c_malloc ( dns_reply_len ) ;
if ( dns_reply = = NULL )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR_VOID ( " dns_recv_callback failed. Failed to allocate memory. " , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_NONFATAL ) ;
2015-09-02 14:41:27 +02:00
}
uint32_t insert_byte = 0 ;
c_memcpy ( & ( dns_reply [ insert_byte ] ) , recv_data , 2 ) ;
insert_byte + = 2 ;
c_memcpy ( & ( dns_reply [ insert_byte ] ) , dns_header , sizeof ( dns_header ) ) ;
insert_byte + = ( uint32_t ) sizeof ( dns_header ) ;
c_memcpy ( & ( dns_reply [ insert_byte ] ) , & ( recv_data [ 12 ] ) , qname_len ) ;
insert_byte + = qname_len ;
c_memcpy ( & ( dns_reply [ insert_byte ] ) , dns_body , sizeof ( dns_body ) ) ;
insert_byte + = ( uint32_t ) sizeof ( dns_body ) ;
c_memcpy ( & ( dns_reply [ insert_byte ] ) , & ( ip_info . ip ) , 4 ) ;
2015-09-19 21:06:56 +02:00
2015-11-12 05:46:26 +01:00
// SDK 1.4.0 changed behaviour, for UDP server need to look up remote ip/port
remot_info * pr = 0 ;
if ( espconn_get_connection_info ( callback_espconn , & pr , 0 ) ! = ESPCONN_OK )
{
2016-01-28 06:52:09 +01:00
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 ) ;
2015-11-12 05:46:26 +01:00
}
callback_espconn - > proto . udp - > remote_port = pr - > remote_port ;
os_memmove ( callback_espconn - > proto . udp - > remote_ip , pr - > remote_ip , 4 ) ;
2015-09-02 14:41:27 +02:00
int8_t err ;
2016-01-28 06:52:09 +01:00
err = espconn_send ( callback_espconn , dns_reply , dns_reply_len ) ;
2015-09-02 14:41:27 +02:00
c_free ( dns_reply ) ;
if ( err = = ESPCONN_MEM )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR_VOID ( " dns_recv_callback failed. Failed to allocate memory for send. " , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
else if ( err = = ESPCONN_ARG )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR_VOID ( " dns_recv_callback failed. Can't execute transmission. " , ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
else if ( err ! = 0 )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR_VOID ( " dns_recv_callback failed. espconn_send failed " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
}
2015-09-19 21:06:56 +02:00
static void enduser_setup_free ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_free " ) ;
2015-09-19 21:06:56 +02:00
if ( state = = NULL )
{
return ;
}
2016-03-08 22:55:53 +01:00
/* Make sure no running timers are left. */
os_timer_disarm ( & ( state - > check_station_timer ) ) ;
os_timer_disarm ( & ( state - > shutdown_timer ) ) ;
2015-09-19 21:06:56 +02:00
if ( state - > espconn_dns_udp ! = NULL )
{
if ( state - > espconn_dns_udp - > proto . udp ! = NULL )
{
c_free ( state - > espconn_dns_udp - > proto . udp ) ;
}
c_free ( state - > espconn_dns_udp ) ;
}
2015-09-02 14:41:27 +02:00
2015-09-19 21:06:56 +02:00
c_free ( state - > http_payload_data ) ;
2016-01-28 06:52:09 +01:00
free_scan_listeners ( ) ;
2015-09-19 21:06:56 +02:00
c_free ( state ) ;
state = NULL ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
static int enduser_setup_dns_start ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_dns_start " ) ;
2015-09-02 14:41:27 +02:00
2015-09-19 21:06:56 +02:00
if ( state - > espconn_dns_udp ! = NULL )
2015-09-02 14:41:27 +02:00
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR ( " dns_start failed. Appears to already be started (espconn_dns_udp != NULL). " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
state - > espconn_dns_udp = ( struct espconn * ) c_malloc ( sizeof ( struct espconn ) ) ;
if ( state - > espconn_dns_udp = = NULL )
2015-09-02 14:41:27 +02:00
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR ( " dns_start failed. Memory allocation failed (espconn_dns_udp == NULL). " , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
esp_udp * esp_udp_data = ( esp_udp * ) c_malloc ( sizeof ( esp_udp ) ) ;
if ( esp_udp_data = = NULL )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR ( " dns_start failed. Memory allocation failed (esp_udp == NULL). " , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
c_memset ( state - > espconn_dns_udp , 0 , sizeof ( struct espconn ) ) ;
2015-09-02 14:41:27 +02:00
c_memset ( esp_udp_data , 0 , sizeof ( esp_udp ) ) ;
2015-09-19 21:06:56 +02:00
state - > espconn_dns_udp - > proto . udp = esp_udp_data ;
state - > espconn_dns_udp - > type = ESPCONN_UDP ;
state - > espconn_dns_udp - > state = ESPCONN_NONE ;
2015-09-02 14:41:27 +02:00
esp_udp_data - > local_port = 53 ;
int8_t err ;
2015-09-19 21:06:56 +02:00
err = espconn_regist_recvcb ( state - > espconn_dns_udp , enduser_setup_dns_recv_callback ) ;
2015-09-02 14:41:27 +02:00
if ( err ! = 0 )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR ( " dns_start failed. Couldn't add receive callback, unknown error. " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
err = espconn_create ( state - > espconn_dns_udp ) ;
2015-09-02 14:41:27 +02:00
if ( err = = ESPCONN_ISCONN )
{
2016-01-28 06:52:09 +01:00
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 ) ;
2015-09-02 14:41:27 +02:00
}
else if ( err = = ESPCONN_MEM )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR ( " dns_start failed. Couldn't create connection, out of memory. " , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
else if ( err ! = 0 )
{
2016-01-28 06:52:09 +01:00
ENDUSER_SETUP_ERROR ( " dns_start failed. Couldn't create connection, unknown error. " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
return 0 ;
2015-09-02 14:41:27 +02:00
}
2015-09-12 18:07:53 +02:00
static void enduser_setup_dns_stop ( void )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_dns_stop " ) ;
2015-09-02 14:41:27 +02:00
2016-03-01 08:33:11 +01:00
if ( state ! = NULL & & state - > espconn_dns_udp ! = NULL )
2015-09-02 14:41:27 +02:00
{
2015-09-19 21:06:56 +02:00
espconn_delete ( state - > espconn_dns_udp ) ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
}
2015-09-02 14:41:27 +02:00
2015-09-19 21:06:56 +02:00
static int enduser_setup_init ( lua_State * L )
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_init " ) ;
2015-09-19 21:06:56 +02:00
if ( state ! = NULL )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_ERROR ( " init failed. Appears to already be started. " , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-19 21:06:56 +02:00
return ENDUSER_SETUP_ERR_UNKOWN_ERROR ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
2016-01-28 06:52:09 +01:00
state = ( enduser_setup_state_t * ) os_zalloc ( sizeof ( enduser_setup_state_t ) ) ;
2015-09-19 21:06:56 +02:00
if ( state = = NULL )
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_ERROR ( " init failed. Unable to allocate memory. " , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_FATAL ) ;
2015-09-19 21:06:56 +02:00
return ENDUSER_SETUP_ERR_OUT_OF_MEMORY ;
}
c_memset ( state , 0 , sizeof ( enduser_setup_state_t ) ) ;
state - > lua_connected_cb_ref = LUA_NOREF ;
state - > lua_err_cb_ref = LUA_NOREF ;
state - > lua_dbg_cb_ref = LUA_NOREF ;
if ( ! lua_isnoneornil ( L , 1 ) )
{
lua_pushvalue ( L , 1 ) ;
state - > lua_connected_cb_ref = luaL_ref ( L , LUA_REGISTRYINDEX ) ;
}
else
{
state - > lua_connected_cb_ref = LUA_NOREF ;
}
if ( ! lua_isnoneornil ( L , 2 ) )
{
lua_pushvalue ( L , 2 ) ;
state - > lua_err_cb_ref = luaL_ref ( L , LUA_REGISTRYINDEX ) ;
}
else
{
state - > lua_err_cb_ref = LUA_NOREF ;
}
if ( ! lua_isnoneornil ( L , 3 ) )
2015-09-02 14:41:27 +02:00
{
2015-09-19 21:06:56 +02:00
lua_pushvalue ( L , 3 ) ;
state - > lua_dbg_cb_ref = luaL_ref ( L , LUA_REGISTRYINDEX ) ;
}
else
{
state - > lua_dbg_cb_ref = LUA_NOREF ;
2015-09-02 14:41:27 +02:00
}
2015-09-19 21:06:56 +02:00
return 0 ;
2015-09-02 14:41:27 +02:00
}
2016-03-08 22:55:53 +01:00
static int enduser_setup_manual ( lua_State * L )
2016-01-28 06:52:09 +01:00
{
if ( ! lua_isnoneornil ( L , 1 ) )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
manual = lua_toboolean ( L , 1 ) ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
lua_pushboolean ( L , manual ) ;
return 1 ;
}
2015-09-19 21:06:56 +02:00
static int enduser_setup_start ( lua_State * L )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_start " ) ;
2015-09-19 21:06:56 +02:00
2016-03-01 07:54:10 +01:00
if ( ! do_station_cfg_handle )
2016-03-08 22:55:53 +01:00
{
2016-03-01 07:54:10 +01:00
do_station_cfg_handle = task_get_id ( do_station_cfg ) ;
2016-03-08 22:55:53 +01:00
}
2016-03-01 07:54:10 +01:00
2015-09-19 21:06:56 +02:00
if ( enduser_setup_init ( L ) )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
goto failed ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
2016-03-08 22:55:53 +01:00
enduser_setup_check_station_start ( ) ;
2016-01-28 06:52:09 +01:00
if ( ! manual )
2015-09-19 21:06:56 +02:00
{
2016-01-28 06:52:09 +01:00
enduser_setup_ap_start ( ) ;
2015-09-19 21:06:56 +02:00
}
if ( enduser_setup_dns_start ( ) )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
goto failed ;
2016-03-08 22:55:53 +01:00
}
2015-09-19 21:06:56 +02:00
if ( enduser_setup_http_start ( ) )
2016-03-08 22:55:53 +01:00
{
2016-01-28 06:52:09 +01:00
goto failed ;
2016-03-08 22:55:53 +01:00
}
2016-01-28 06:52:09 +01:00
goto out ;
2015-09-16 14:56:44 +02:00
2016-01-28 06:52:09 +01:00
failed :
2016-03-08 22:55:53 +01:00
enduser_setup_stop ( lua_getstate ( ) ) ;
2016-01-28 06:52:09 +01:00
out :
2015-09-02 14:41:27 +02:00
return 0 ;
}
2016-03-08 22:55:53 +01:00
/**
* A wrapper needed for type - reasons strictness reasons .
*/
static void enduser_setup_stop_callback ( void * ptr )
{
enduser_setup_stop ( lua_getstate ( ) ) ;
}
2015-09-12 18:07:53 +02:00
static int enduser_setup_stop ( lua_State * L )
2015-09-02 14:41:27 +02:00
{
2016-03-08 22:55:53 +01:00
ENDUSER_SETUP_DEBUG ( " enduser_setup_stop " ) ;
2016-01-28 06:52:09 +01:00
if ( ! manual )
{
enduser_setup_ap_stop ( ) ;
}
2015-09-02 14:41:27 +02:00
enduser_setup_dns_stop ( ) ;
enduser_setup_http_stop ( ) ;
2015-09-19 21:06:56 +02:00
enduser_setup_free ( ) ;
2015-09-02 14:41:27 +02:00
return 0 ;
}
2015-12-16 06:04:58 +01:00
static const LUA_REG_TYPE enduser_setup_map [ ] = {
2016-01-28 06:52:09 +01:00
{ LSTRKEY ( " manual " ) , LFUNCVAL ( enduser_setup_manual ) } ,
2015-09-02 14:41:27 +02:00
{ LSTRKEY ( " start " ) , LFUNCVAL ( enduser_setup_start ) } ,
{ LSTRKEY ( " stop " ) , LFUNCVAL ( enduser_setup_stop ) } ,
{ LNILKEY , LNILVAL }
} ;
2015-12-16 06:04:58 +01:00
NODEMCU_MODULE ( ENDUSER_SETUP , " enduser_setup " , enduser_setup_map , NULL ) ;