Add basic authentification and OTA update

only web update is supported
This commit is contained in:
luc 2015-11-14 21:48:21 +08:00
parent 18d3550edc
commit 627052ceeb
19 changed files with 887 additions and 28 deletions

BIN
Page1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 48 KiB

BIN
Page2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 25 KiB

BIN
Page3.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 23 KiB

BIN
Page4.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 34 KiB

BIN
Page6.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 40 KiB

BIN
Page7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
Page8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -46,7 +46,7 @@ Additionnaly:
*Tools:
--Use IDE to upload directly (latest version of board manager module generate one binary)
-- to flash the htm files present in data directory you need to use another tool, installation and usage is explained here: http://arduino.esp8266.com/versions/1.6.5-1160-gef26c5f/doc/reference.html#file-system
Once flashed you also can use the web updater to flash new FW in System Configuration Page
*Connection
--Connect GPIO0 to ground to be in update mode
@ -78,6 +78,8 @@ Baud rate: 9600
Web port:80
Data port: 8888
Web Page refresh: 3 secondes
User: admin
Password: admin
These are the pages defined using template:
Home page :
@ -94,6 +96,10 @@ Printer Status Page for more than 64K SPIFFS, fancy one:
<img src=https://raw.githubusercontent.com/luc-github/ESP8266/master/page5.png><br>
Extra Settings Page, for web UI and for printer:
<img src=https://raw.githubusercontent.com/luc-github/ESP8266/master/Page6.png><br>
Change password Page:
<img src=https://raw.githubusercontent.com/luc-github/ESP8266/master/Page7.png><br>
Login Page:
<img src=https://raw.githubusercontent.com/luc-github/ESP8266/master/Page8.png><br>
the template files are stored on SPIFFS:
<img src=https://raw.githubusercontent.com/luc-github/ESP8266/master/files.png><br>
and uploaded using [IDE](http://arduino.esp8266.com/versions/1.6.5-1160-gef26c5f/doc/reference.html#file-system)
@ -110,6 +116,14 @@ Currently, I tested on ESP01 using 64K SPIFFS ( please use data directory conten
*SSDP : on Station and AP mode (done)
*Captive portal : on AP mode only (not yet functionnal)
##Basic Authentification
Can be disabled in FW
default user: admin
default password: admin
#OTA support
Currently only web update is supported not telnet one
##Commands/msg from/to serial(not fully implemented):
*from module to printer by serial communication
-M117 [Message], Error/status message from module (done)

View File

@ -176,6 +176,7 @@ bool CONFIG::reset_config()
if(!CONFIG::write_buffer(EP_XY_FEEDRATE,(const byte *)&DEFAULT_XY_FEEDRATE,INTEGER_LENGTH))return false;
if(!CONFIG::write_buffer(EP_Z_FEEDRATE,(const byte *)&DEFAULT_Z_FEEDRATE,INTEGER_LENGTH))return false;
if(!CONFIG::write_buffer(EP_E_FEEDRATE,(const byte *)&DEFAULT_E_FEEDRATE,INTEGER_LENGTH))return false;
if(!CONFIG::write_string(EP_ADMIN_PWD,FPSTR(DEFAULT_ADMIN)))return false;
return true;
}

View File

@ -29,6 +29,9 @@
//CAPTIVE_PORTAL_FEATURE: In SoftAP redirect all unknow call to main page
#define CAPTIVE_PORTAL_FEATURE
//AUTHENTICATION_FEATURE: protect pages by login password
#define AUTHENTICATION_FEATURE
#ifndef CONFIG_h
#define CONFIG_h
@ -38,7 +41,7 @@ extern "C" {
#include "user_interface.h"
}
//version and sources location
#define FW_VERSION "0.4"
#define FW_VERSION "0.5"
#define REPOSITORY "https://github.com/luc-github/ESP8266"
@ -73,6 +76,7 @@ extern "C" {
#define EP_XY_FEEDRATE 164//4 bytes = int
#define EP_Z_FEEDRATE 168//4 bytes = int
#define EP_E_FEEDRATE 172//4 bytes = int
#define EP_ADMIN_PWD 176//21 bytes 20+1 = string ; warning does not support multibyte char like chinese
@ -99,6 +103,7 @@ const int DEFAULT_DATA_PORT = 8888;
const int DEFAULT_XY_FEEDRATE=1000;
const int DEFAULT_Z_FEEDRATE =100;
const int DEFAULT_E_FEEDRATE=400;
const char DEFAULT_ADMIN [] PROGMEM = "admin";
//sizes
#define EEPROM_SIZE 256 //max is 512
@ -106,6 +111,8 @@ const int DEFAULT_E_FEEDRATE=400;
#define MIN_SSID_LENGTH 1
#define MAX_PASSWORD_LENGTH 64
#define MIN_PASSWORD_LENGTH 8
#define MAX_ADMIN_PASSWORD_LENGTH 16
#define MIN_ADMIN_PASSWORD_LENGTH 1
#define IP_LENGTH 4
#define INTEGER_LENGTH 4
#define MAX_HOSTNAME_LENGTH 32

View File

@ -14,7 +14,7 @@ th{text-align:left;}
@media (min-width:1200px){.container{width:1170px;}}
.nav{ width:100%; color:#cccccc;padding-left:10;padding-right:10;list-style:none;background-color:#333333;border-radius:6px ;margin-bottom:20px;}
a{position:relative;display:block;padding:10px 15px;text-decoration:none;color:#cccccc;}
.active{color:#ffffff;background-color:#000000;}
.active{color:#ffffff;background-color:#000000;}
.active a,a:hover,a:focus{color:#FFFFFF;}
.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05);}
.panel-body{padding:15px;}
@ -46,3 +46,5 @@ caption{padding-top:8px;padding-bottom:8px;color:#777777;text-align:left;}
.btn-danger:focus,.btn-danger:active,.btn-danger:hover,.btn-danger.focus,.btn-danger.active,.btn-danger.hover{color: #ffffff;background-color:#c9302c;border-color:#761c19;}
.btnimg {cursor:hand; border-radius:6px ;;border:1px solid #FFFFFF;}
.btnimg:hover{background-color:#F0F0F0;border-color:#00FFFF;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;}
.btnroundimg {cursor:hand; border-radius:30px;}
.btnroundimg:hover{background-color:#F0F0F0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;}

View File

@ -11,12 +11,19 @@ $INCLUDE[css.inc]$
<div class="container"><table class="nav">
<tr width=100%>
<td class="$MENU_HOME$"><a href="http://$WEB_ADDRESS$">Home</a></td>
<td class="$MENU_SYSTEM$"><a href="http://$WEB_ADDRESS$/CONFIGSYS">System Configuration</a></td>
<td class="$MENU_AP$"><a href="http://$WEB_ADDRESS$/CONFIGAP">AP Configuration</a></td>
<td class="$MENU_STA$"><a href="http://$WEB_ADDRESS$/CONFIGSTA">Station Configuration</a></td>
<td class="$MENU_PRINTER$"><a href="http://$WEB_ADDRESS$/PRINTER">Printer Status</a></td>
<td class="$MENU_SYSTEM$"><a href="http://$WEB_ADDRESS$/CONFIGSYS">System</a></td>
<td class="$MENU_AP$"><a href="http://$WEB_ADDRESS$/CONFIGAP">Access Point</a></td>
<td class="$MENU_STA$"><a href="http://$WEB_ADDRESS$/CONFIGSTA">Station</a></td>
<td class="$MENU_PRINTER$"><a href="http://$WEB_ADDRESS$/PRINTER">3D Printer</a></td>
<td class="$MENU_SETTINGS$"><a href="http://$WEB_ADDRESS$/SETTINGS">Extra Settings</a></td>
<td width=100%>&nbsp;</td>
<td><a href="/PASSWORD" style="$DISCONNECT_VISIBILITY$">Admin</a></td>
<td style="padding:0px;"><a href="/LOGIN?DISCONNECT=YES"><div class="btnroundimg" style="$DISCONNECT_VISIBILITY$"><svg width="30" height="30" viewBox="0 0 40 40">
<circle style="fill:white" cx="20" cy="20" r="20"/>
<circle style="fill:white;stroke:black;stroke-width:4" cx="20" cy="15" r="10"/>
<rect style="fill:black;stroke-width:4;stroke:black" width="20" height="15" x="10" y="17" />
<circle style="fill:white" cx="20" cy="22" r="2.5"/>
<polygon points="20,23 16,30 24,30" style="fill:white"/></svg></div></a></td>
<td>FW: V$FW_VER$</td>
<td><a href="https://github.com/luc-github/ESP8266" >Github</a></td>
</tr>

21
esp8266/data/login.tpl Normal file
View File

@ -0,0 +1,21 @@
$INCLUDE[header.inc]$
<div class="panel">
<div class="panel-heading">Log in</div>
<div class="panel-body">
<form method="POST">
<div class="form-group $USER_STATUS$"><label class="control-label" for="USER">User :</label><br>
<input type="hidden" name="return" value="$RETURN$">
<input type="text" class="form-control" id="USER" name="USER" placeholder="User (1~16)" min="1" max="16" value="$USER$" style="width: auto;"></div>
<div class="form-group $USER_PASSWORD_STATUS$"><label class="control-label" for="PASSWORD">Password :</label><br>
<input type="password" class="form-control" id="PASSWORD" name="PASSWORD" placeholder="Password (1~16)" min="1" max="16" value="$USER_PASSWORD$" style="width: auto;"></div>
<div class="alert alert-danger" role="alert" style="$ERROR_MSG_VISIBILITY$" >
$ERROR_MSG$
</div>
<hr><input style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" name="SUBMIT" value="Apply">
</form>
<div class="alert alert-success" role="alert" style="$SUCCESS_MSG_VISIBILITY$" >
$SUCCESS_MSG$
</div>
</div>
</div>
$INCLUDE[footer.inc]$

111
esp8266/data/password.tpl Normal file
View File

@ -0,0 +1,111 @@
$INCLUDE[header.inc]$
<div class="panel">
<div class="panel-heading">Change Password</div>
<div class="panel-body">
<form method="POST">
<div id="divpassword1" class="form-group $USER_PASSWORD_STATUS$" ><label class="control-label" for="PASSWORD1">Password: </label><br>
<input type="password" class="form-control" onkeyup="checkpassword()" id="PASSWORD1" name="PASSWORD" placeholder="Password (1~16)" value="$USER_PASSWORD$" max="16" m1n="1" style="width: auto;"></div>
<div id="divpassword2" class="form-group $USER_PASSWORD_STATUS2$"><label class="control-label"for="PASSWORD2">Confirm Password :</label><br>
<input type="password" class="form-control" onkeyup="checkpassword()" id="PASSWORD2" name="PASSWORD2" placeholder="Password (1~16)" max="16" minn="1" value="$USER_PASSWORD2$" style="width: auto;"></div>
<div class="alert alert-danger" role="alert" id="alerterror" style="$ERROR_MSG_VISIBILITY$" >
$ERROR_MSG$
</div>
<hr><input style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" id="BTNSUBMIT" name="SUBMIT" value="Apply">
</form>
<div class="alert alert-success" id="alertsuccess" role="alert" style="$SUCCESS_MSG_VISIBILITY$" >
$SUCCESS_MSG$
</div>
</div>
</div>
<script>
function checkpassword()
{
var msg="";
var haserror=false;
var hassuccess=false;
var password1 = document.getElementById('PASSWORD1').value;
var password2 = document.getElementById('PASSWORD2').value;
if (password1.length<1)
{
msg+="Password too short<br>";
document.getElementById("divpassword1").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password1.length>16)
{
msg+="Password too long<br>";
document.getElementById("divpassword1").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password2.length<1)
{
msg+="Confirmation Password too short<br>";
document.getElementById("divpassword1").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password2.length>16)
{
msg+="Confirmation Password too long<br>";
document.getElementById("divpassword1").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password2!=password1)
{
msg+="Passwords do not matches<br>";
document.getElementById("divpassword2").className = "form-group has-error";
haserror=true;
hassuccess=false;
}
if (password1.length>0 && password1.length<17)
{
document.getElementById("divpassword1").className = "form-group has-success";
}
if (password2==password1 && password1.length>0 && password1.length<17)
{
haserror=false;
hassuccess=true;
document.getElementById("divpassword1").className = "form-group has-success";
document.getElementById("divpassword2").className = "form-group has-success";
msg="Passwords matches"
}
if (haserror)
{
document.getElementById("alerterror").style.visibility="visible";
document.getElementById("alerterror").style.width="auto";
document.getElementById("alerterror").style.height="auto";
document.getElementById("alerterror").style.padding="15px";
document.getElementById("alerterror").style.marginBottom="20px";
document.getElementById("alertsuccess").style.visibility="hidden";
document.getElementById("alertsuccess").style.width="0px";
document.getElementById("alertsuccess").style.height="0px";
document.getElementById("alertsuccess").style.padding="0px";
document.getElementById("alertsuccess").style.margin="0px";
document.getElementById("BTNSUBMIT").style.visibility="hidden";
document.getElementById("alerterror").innerHTML=msg;
}
if (hassuccess)
{
document.getElementById("alertsuccess").style.visibility="visible";
document.getElementById("alertsuccess").style.width="auto";
document.getElementById("alertsuccess").style.height="auto";
document.getElementById("alertsuccess").style.padding="15px";
document.getElementById("alertsuccess").style.marginBottom="20px";
document.getElementById("alerterror").style.visibility="hidden";
document.getElementById("alerterror").style.width="0px";
document.getElementById("alerterror").style.height="0px";
document.getElementById("alerterror").style.padding="0px";
document.getElementById("alerterror").style.margin="0px";
document.getElementById("BTNSUBMIT").style.visibility="visible";
document.getElementById("alertsuccess").innerHTML=msg;
}
}
</script>
$INCLUDE[footer.inc]$

View File

@ -1,5 +1,16 @@
$INCLUDE[header.inc]$
<div class="panel">
<STYLE>
input[type="file"]::-webkit-file-upload-button{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer;
background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;
* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #5bc0de;border-color: #46b8da;}
input[type="file"]::-webkit-file-upload-button:focus{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer;
background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;
* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #1b6d85;}
input[type="file"]::-webkit-file-upload-button:hover{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer;
background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;
* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #269abc;}
</STYLE>
<div id='system' class="panel">
<div class="panel-heading">System</div>
<div class="panel-body">
<form method="POST">
@ -20,11 +31,74 @@ $SLEEP_MODE_OPTIONS_LIST$
<div class="alert alert-danger" role="alert" style="$ERROR_MSG_VISIBILITY$" >
$ERROR_MSG$
</div>
<hr><input style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" name="SUBMIT" value="Apply">
<hr><input id='btnsubmit' style="$SUBMIT_BUTTON_VISIBILITY$" type="submit" class="btn btn-primary" name="SUBMIT" value="Apply">
</form>
<div class="alert alert-success" role="alert" style="$SUCCESS_MSG_VISIBILITY$" >
$SUCCESS_MSG$
</div>
</div>
</div>
<div class="panel">
<div class="panel-heading">Firmware Update</div>
<div class="panel-body">
<table><tr>
<td><input type="file" id="file-select" name="myfiles[]" multiple /></td>
<td><input class="btn btn-primary" type="button" id="upload-button" onclick="Sendfile();" value="Update"/></td>
<td><progress style="visibility:hidden;" name='prg' id='prg'></progress></td>
<td><div id='msg' style='visibility:hidden;'>Restarting, please wait....</div></td></tr></table>
</div>
</div>
<script>
function Sendfile(){
if (!confirm("Confirm Firmware Update ?"))return;
var files = document.getElementById('file-select').files;
if (files.length==0)return;
document.getElementById('upload-button').value = "Uploading...";
document.getElementById('prg').style.visibility = "visible";
var formData = new FormData();
for (var i = 0; i < files.length; i++) {
var file = files[i];
formData.append('myfiles[]', file, "/"+file.name);}
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', '/UPDATE', true);
xmlhttp.onload = function () {
if (xmlhttp.status === 200) {
document.getElementById('upload-button').value = 'Upload';
document.getElementById('upload-button').style.visibility = 'hidden';
document.getElementById('upload-button').style.width = '0px';
document.getElementById('system').style.visibility = 'hidden';
document.getElementById('system').style.height = '0px';
document.getElementById('msg').style.visibility = "visible";
document.getElementById('file-select').value="";
document.getElementById('file-select').style.visibility = 'hidden';
document.getElementById('file-select').style.width = '0px';
document.getElementById('btnsubmit').style.visibility = 'hidden';
var jsonresponse = JSON.parse(xmlhttp.responseText);
if (jsonresponse.status=='1' || jsonresponse.status=='4' || jsonresponse.status=='1')alert("Update failed");
if (jsonresponse.status=='2')alert('Update canceled!');
else if (jsonresponse.status=='3')
{
var i = 0;
var interval;
var x = document.getElementById("prg");
x.max=40;
interval = setInterval(function(){
i=i+1;
var x = document.getElementById("prg");
x.value=i;
if (i>40)
{
clearInterval(interval);
location.reload();
}
},1000);
}
else alert('Update failed!');
} else alert('An error occurred!');
}
xmlhttp.send(formData);
}
</script>
$INCLUDE[footer.inc]$

View File

@ -110,6 +110,11 @@ void setup() {
//start interfaces
web_interface = new WEBINTERFACE_CLASS(wifi_config.iweb_port);
data_server = new WiFiServer (wifi_config.idata_port);
//here the list of headers to be recorded
const char * headerkeys[] = {"Cookie"} ;
size_t headerkeyssize = sizeof(headerkeys)/sizeof(char*);
//ask server to track these headers
web_interface->WebServer.collectHeaders(headerkeys, headerkeyssize );
web_interface->WebServer.begin();
data_server->begin();
data_server->setNoDelay(true);

View File

@ -36,6 +36,14 @@ extern "C" {
#ifdef SSDP_FEATURE
#include <ESP8266SSDP.h>
#endif
#define MAX_AUTH_IP 10
#define UPLOAD_STATUS_NONE 0
#define UPLOAD_STATUS_FAILED 1
#define UPLOAD_STATUS_CANCELLED 2
#define UPLOAD_STATUS_SUCCESSFUL 3
#define UPLOAD_STATUS_ONGOING 4
const char PAGE_404 [] PROGMEM ="<HTML>\n<HEAD>\n<title>Redirecting...</title> \n</HEAD>\n<BODY>\n<CENTER>Unknown page - you will be redirected...\n<BR><BR>\nif not redirected, <a href='http://$WEB_ADDRESS$'>click here</a>\n<BR><BR>\n<PROGRESS name='prg' id='prg'>\n\n<script>\nvar i = 0; \nvar x = document.getElementById(\"prg\"); \nx.max=5; \nvar interval=setInterval(function(){\ni=i+1; \nvar x = document.getElementById(\"prg\"); \nx.value=i; \nif (i>5) \n{\nclearInterval(interval);\nwindow.location.href='/';\n}\n},1000);\n</script>\n</CENTER>\n</BODY>\n</HTML>\n\n";
const char PAGE_RESTART [] PROGMEM ="<HTML>\n<HEAD>\n<title>Restarting...</title> \n</HEAD>\n<BODY>\n<CENTER>Restarting, please wait....\n<BR>\n<PROGRESS name='prg' id='prg'>\n</CENTER>\n<script>\nvar i = 0;\nvar interval; \nvar x = document.getElementById(\"prg\"); \nx.max=40; \ninterval = setInterval(function(){\ni=i+1; \nvar x = document.getElementById(\"prg\"); \nx.value=i; \nif (i>40) \n{\nclearInterval(interval);\nwindow.location.href='/';\n}\n},1000);\n</script>\n</BODY>\n</HTML>\n";
const char RESTARTCMD [] PROGMEM ="<script>setTimeout(function(){window.location.href='/RESTART'},3000);</script>";
@ -182,6 +190,16 @@ const char KEY_Z_FEEDRATE_STATUS [] PROGMEM = "$Z_FEEDRATE_STATUS$";
const char KEY_E_FEEDRATE_STATUS [] PROGMEM = "$E_FEEDRATE_STATUS$";
const char VALUE_SETTINGS [] PROGMEM = "Extra Settings";
const char KEY_REFRESH_PAGE_STATUS [] PROGMEM = "$REFRESH_PAGE_STATUS$";
const char KEY_DISCONNECT_VISIBILITY [] PROGMEM = "$DISCONNECT_VISIBILITY$";
const char VALUE_LOGIN [] PROGMEM = "Login page";
const char KEY_USER_STATUS [] PROGMEM = "$USER_STATUS$";
const char KEY_USER_PASSWORD_STATUS [] PROGMEM = "$USER_PASSWORD_STATUS$";
const char KEY_USER_PASSWORD_STATUS2 [] PROGMEM = "$USER_PASSWORD_STATUS2$";
const char KEY_USER [] PROGMEM = "$USER$";
const char KEY_USER_PASSWORD [] PROGMEM = "$USER_PASSWORD$";
const char KEY_USER_PASSWORD2 [] PROGMEM = "$USER_PASSWORD2$";
const char KEY_RETURN [] PROGMEM = "$RETURN$";
const char VALUE_CHANGE_PASSWORD [] PROGMEM = "Change Password";
bool WEBINTERFACE_CLASS::isHostnameValid(const char * hostname)
{ //limited size
@ -225,6 +243,20 @@ bool WEBINTERFACE_CLASS::isPasswordValid(const char * password)
return true;
}
bool WEBINTERFACE_CLASS::isAdminPasswordValid(const char * password)
{
char c;
//limited size
if ((strlen(password)>MAX_ADMIN_PASSWORD_LENGTH)|| (strlen(password)<MIN_ADMIN_PASSWORD_LENGTH)) return false;
//no space allowed
for (int i=0;i < strlen(password);i++)
{
c= password[i];
if (c==' ') return false;
}
return true;
}
bool WEBINTERFACE_CLASS::isIPValid(const char * IP)
{ //limited size
int internalcount=0;
@ -259,12 +291,14 @@ bool WEBINTERFACE_CLASS::isIPValid(const char * IP)
if (IP[strlen(IP)-1]=='.')return false;
return true;
}
//TODO should be in some tool class
char * intTostr(int value){
static char result [12];
sprintf(result,"%d",value);
return result;
}
//TODO : should be in webserver class
bool processTemplate(const char * filename, STORESTRINGS_CLASS & KeysList , STORESTRINGS_CLASS & ValuesList )
{
@ -489,6 +523,9 @@ void handle_web_interface_root()
struct softap_config apconfig;
struct ip_info info;
uint8_t mac [WL_MAC_ADDR_LENGTH];
KeysList.add(FPSTR(KEY_DISCONNECT_VISIBILITY));
if (web_interface->is_authenticated())ValuesList.add(FPSTR(VALUE_ITEM_VISIBLE));
else ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
//Free Mem, put at the end to reflect situation
KeysList.add(FPSTR(KEY_FREE_MEM));
ValuesList.add(intTostr(system_get_free_heap_size()));
@ -833,6 +870,12 @@ void handle_web_interface_configSys()
const __FlashStringHelper *smodemdisplaylist[]={FPSTR(VALUE_NONE),FPSTR(VALUE_LIGHT),FPSTR(VALUE_MODEM),FPSTR(VALUE_MODEM)};
STORESTRINGS_CLASS KeysList ;
STORESTRINGS_CLASS ValuesList ;
if (!web_interface->is_authenticated())
{
String header = "HTTP/1.1 301 OK\r\nLocation: /LOGIN?return=CONFIGSYS\r\nCache-Control: no-cache\r\n\r\n";
web_interface->WebServer.sendContent(header);
return;
}
//Free Mem, put at the end to reflect situation
KeysList.add(FPSTR(KEY_FREE_MEM));
ValuesList.add(intTostr(system_get_free_heap_size()));
@ -1067,6 +1110,195 @@ void handle_web_interface_configSys()
ValuesList.clear();
}
void handle_password()
{
String stmp,smsg;
String sPassword,sPassword2;
bool msg_alert_error=false;
bool msg_alert_success=false;
int ipos;
STORESTRINGS_CLASS KeysList ;
STORESTRINGS_CLASS ValuesList ;
if (!web_interface->is_authenticated())
{
String header = "HTTP/1.1 301 OK\r\nLocation: /LOGIN?return=PASSWORD\r\nCache-Control: no-cache\r\n\r\n";
web_interface->WebServer.sendContent(header);
return;
}
//Free Mem, put at the end to reflect situation
KeysList.add(FPSTR(KEY_FREE_MEM));
ValuesList.add(intTostr(system_get_free_heap_size()));
//IP
stmp=FPSTR(KEY_IP);
KeysList.add(stmp);
if (wifi_get_opmode()==WIFI_STA ) stmp=WiFi.localIP().toString();
else stmp=WiFi.softAPIP().toString();
ValuesList.add(stmp);
//Web address = ip + port
KeysList.add(FPSTR(KEY_WEB_ADDRESS));
if (wifi_config.iweb_port!=80)
{
stmp+=":";
stmp+=intTostr(wifi_config.iweb_port);
}
ValuesList.add(stmp);
//mode
if (wifi_get_opmode()==WIFI_STA )
{
KeysList.add(FPSTR(KEY_MODE));
ValuesList.add(FPSTR(VALUE_STA));
}
else
{
if (wifi_get_opmode()==WIFI_AP )
{
KeysList.add(FPSTR(KEY_MODE));
ValuesList.add(FPSTR(VALUE_AP));
}
else
{
KeysList.add(FPSTR(KEY_MODE));
ValuesList.add(FPSTR(VALUE_AP_STA));
}
}
//FW Version
KeysList.add(FPSTR(KEY_FW_VER));
ValuesList.add(FPSTR(VALUE_FW_VERSION));
//page title
KeysList.add(FPSTR(KEY_PAGE_TITLE));
ValuesList.add(FPSTR(VALUE_CHANGE_PASSWORD));
//tpl file name with extension
KeysList.add(FPSTR(KEY_FILE_NAME));
ValuesList.add("password.tpl");
//tpl file name without extension
KeysList.add(FPSTR(KEY_SHORT_FILE_NAME));
ValuesList.add("password");
//menu item
KeysList.add(FPSTR(KEY_MENU_AP));
ValuesList.add(FPSTR(VALUE_ACTIVE));
//check is it is a submission or a display
smsg="";
if (web_interface->WebServer.hasArg("SUBMIT"))
{ //is there a correct list of values?
if (web_interface->WebServer.hasArg("PASSWORD") && web_interface->WebServer.hasArg("PASSWORD2"))
{
//Password
web_interface->urldecode(sPassword,web_interface->WebServer.arg("PASSWORD").c_str());
web_interface->urldecode(sPassword2,web_interface->WebServer.arg("PASSWORD2").c_str());
if (!web_interface->isAdminPasswordValid(sPassword.c_str()) )
{
msg_alert_error=true;
smsg+="Error : Incorrect password<BR>";
KeysList.add(FPSTR(KEY_USER_PASSWORD_STATUS));
ValuesList.add(FPSTR(VALUE_HAS_ERROR));
}
if (sPassword!=sPassword2)
{
msg_alert_error=true;
smsg+="Error : Passwords do not match<BR>";
KeysList.add(FPSTR(KEY_USER_PASSWORD_STATUS2));
ValuesList.add(FPSTR(VALUE_HAS_ERROR));
}
}
else
{
msg_alert_error=true;
smsg="Error : Missing data";
}
//if no error apply the change
if (msg_alert_error==false)
{
//save
if(!CONFIG::write_string(EP_ADMIN_PWD,sPassword.c_str()))
{
msg_alert_error=true;
smsg="Error : Cannot write to EEPROM";
}
else
{
msg_alert_success=true;
smsg="Changes saved to EEPROM";
}
}
}
else //no submit need to get data from EEPROM
{
//password
sPassword="";
sPassword2="";
}
//Display values
//password
KeysList.add(FPSTR(KEY_USER_PASSWORD));
ValuesList.add(sPassword);
KeysList.add(FPSTR(KEY_USER_PASSWORD2));
ValuesList.add(sPassword2);
if (msg_alert_error)
{
KeysList.add(FPSTR(KEY_ERROR_MSG));
ValuesList.add(smsg);
KeysList.add(FPSTR(KEY_SUCCESS_MSG));
ValuesList.add("");
KeysList.add(FPSTR(KEY_ERROR_MSG_VISIBILITY ));
ValuesList.add(FPSTR(VALUE_ITEM_VISIBLE));
KeysList.add(FPSTR(KEY_SUCCESS_MSG_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
KeysList.add(FPSTR(KEY_SUBMIT_BUTTON_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_VISIBLE));
KeysList.add(FPSTR(KEY_SERVICE_PAGE));
ValuesList.add("");
}
else if (msg_alert_success)
{
KeysList.add(FPSTR(KEY_ERROR_MSG));
ValuesList.add("");
KeysList.add(FPSTR(KEY_SUCCESS_MSG));
ValuesList.add(smsg);
KeysList.add(FPSTR(KEY_ERROR_MSG_VISIBILITY ));
ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
KeysList.add(FPSTR(KEY_SUCCESS_MSG_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_VISIBLE));
KeysList.add(FPSTR(KEY_SUBMIT_BUTTON_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
KeysList.add(FPSTR(KEY_SERVICE_PAGE));
ValuesList.add("");
//Add all green
KeysList.add(FPSTR(KEY_USER_PASSWORD_STATUS));
ValuesList.add(FPSTR(VALUE_HAS_SUCCESS));
KeysList.add(FPSTR(KEY_USER_PASSWORD_STATUS2));
ValuesList.add(FPSTR(VALUE_HAS_SUCCESS));
}
else
{
KeysList.add(FPSTR(KEY_ERROR_MSG));
ValuesList.add("");
KeysList.add(FPSTR(KEY_SUCCESS_MSG));
ValuesList.add("");
KeysList.add(FPSTR(KEY_ERROR_MSG_VISIBILITY ));
ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
KeysList.add(FPSTR(KEY_SUCCESS_MSG_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
KeysList.add(FPSTR(KEY_SUBMIT_BUTTON_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_VISIBLE));
KeysList.add(FPSTR(KEY_SERVICE_PAGE));
ValuesList.add("");
}
//process the template file and provide list of variables
if(KeysList.size()==ValuesList.size()) //Sanity check
processTemplate("/password.tpl", KeysList , ValuesList);
//need to clean to speed up memory recovery
KeysList.clear();
ValuesList.clear();
}
void handle_web_interface_configAP()
{
String stmp,smsg;
@ -1088,6 +1320,12 @@ void handle_web_interface_configAP()
const __FlashStringHelper * iauthdisplaylist []={FPSTR(VALUE_NONE),FPSTR(VALUE_WPA),FPSTR(VALUE_WPA2),FPSTR(VALUE_WPAWPA2),FPSTR(VALUE_MAX),FPSTR(VALUE_MAX)};
STORESTRINGS_CLASS KeysList ;
STORESTRINGS_CLASS ValuesList ;
if (!web_interface->is_authenticated())
{
String header = "HTTP/1.1 301 OK\r\nLocation: /LOGIN?return=CONFIGAP\r\nCache-Control: no-cache\r\n\r\n";
web_interface->WebServer.sendContent(header);
return;
}
//Free Mem, put at the end to reflect situation
KeysList.add(FPSTR(KEY_FREE_MEM));
ValuesList.add(intTostr(system_get_free_heap_size()));
@ -1471,7 +1709,6 @@ if (msg_alert_error)
ValuesList.clear();
}
void handle_web_interface_configSTA()
{
String stmp,smsg;
@ -1489,6 +1726,12 @@ void handle_web_interface_configSTA()
const __FlashStringHelper * inetworkdisplaylist []={FPSTR(VALUE_11B),FPSTR(VALUE_11G),FPSTR(VALUE_11N),FPSTR(VALUE_11B)};
STORESTRINGS_CLASS KeysList ;
STORESTRINGS_CLASS ValuesList ;
if (!web_interface->is_authenticated())
{
String header = "HTTP/1.1 301 OK\r\nLocation: /LOGIN?return=CONFIGSTA\r\nCache-Control: no-cache\r\n\r\n";
web_interface->WebServer.sendContent(header);
return;
}
//Free Mem, put at the end to reflect situation
KeysList.add(FPSTR(KEY_FREE_MEM));
ValuesList.add(intTostr(system_get_free_heap_size()));
@ -1861,7 +2104,6 @@ if (msg_alert_error)
ValuesList.clear();
}
void handle_web_interface_printer()
{
String stmp,smsg;
@ -1869,6 +2111,12 @@ void handle_web_interface_printer()
bool msg_alert_success=false;
STORESTRINGS_CLASS KeysList ;
STORESTRINGS_CLASS ValuesList ;
if (!web_interface->is_authenticated())
{
String header = "HTTP/1.1 301 OK\r\nLocation: /LOGIN?return=PRINTER\r\nCache-Control: no-cache\r\n\r\n";
web_interface->WebServer.sendContent(header);
return;
}
//Free Mem, put at the end to reflect situation
KeysList.add(FPSTR(KEY_FREE_MEM));
ValuesList.add(intTostr(system_get_free_heap_size()));
@ -1964,6 +2212,12 @@ void handle_web_settings()
int ixy_feedrate,iz_feedrate,ie_feedrate;
STORESTRINGS_CLASS KeysList ;
STORESTRINGS_CLASS ValuesList ;
if (!web_interface->is_authenticated())
{
String header = "HTTP/1.1 301 OK\r\nLocation: /LOGIN?return=SETTINGS\r\nCache-Control: no-cache\r\n\r\n";
web_interface->WebServer.sendContent(header);
return;
}
//Free Mem, put at the end to reflect situation
KeysList.add(FPSTR(KEY_FREE_MEM));
ValuesList.add(intTostr(system_get_free_heap_size()));
@ -2163,10 +2417,9 @@ void handle_web_settings()
ValuesList.clear();
}
void handle_web_interface_status()
{
web_interface->is_authenticated();
Serial.println("M114");
int tagpos,tagpos2;
String buffer2send;
@ -2340,8 +2593,9 @@ String getContentType(String filename){
else if(filename.endsWith(".txt")) return "text/plain";
return "application/octet-stream";
}
void handleFileUpload(){
if(web_interface->WebServer.uri() != "/FILES") return;
void SPIFFSFileupload()
{
HTTPUpload& upload = (web_interface->WebServer).upload();
if(upload.status == UPLOAD_FILE_START){
String filename = upload.filename;
@ -2359,7 +2613,53 @@ void handleFileUpload(){
else Serial.println("Cannot open file");
}
void WebUpdateUpload()
{
HTTPUpload& upload = (web_interface->WebServer).upload();
if(upload.status == UPLOAD_FILE_START){
Serial.println("M117 Update Firmware");
web_interface->_upload_status= UPLOAD_STATUS_ONGOING;
WiFiUDP::stopAll();
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if(!Update.begin(maxSketchSpace)){//start with max available size
}
} else if(upload.status == UPLOAD_FILE_WRITE){
web_interface->_upload_status= UPLOAD_STATUS_ONGOING;
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
}
} else if(upload.status == UPLOAD_FILE_END){
if(Update.end(true)){ //true to set the size to the current progress
//Now Reboot
web_interface->_upload_status=UPLOAD_STATUS_SUCCESSFUL;
}
} else if(upload.status == UPLOAD_FILE_ABORTED){
Update.end();
web_interface->_upload_status=UPLOAD_STATUS_CANCELLED;
}
yield();
}
void handleFileUpload(){
if(web_interface->WebServer.uri() == "/FILES") SPIFFSFileupload();
if(web_interface->WebServer.uri() == "/UPDATE") WebUpdateUpload();
}
void handleUpdate(){
web_interface->is_authenticated();
String jsonfile = "{\"status\":\"" ;
jsonfile+=intTostr(web_interface->_upload_status);
jsonfile+="\"}";
//send status
web_interface->WebServer.send(200, "application/json", jsonfile);
//if success restart
if (web_interface->_upload_status==UPLOAD_STATUS_SUCCESSFUL)web_interface->restartmodule=true;
}
void handleFileList() {
if (!web_interface->is_authenticated())
{
return;
}
String path = "/";
String status="Ok";
if(web_interface->WebServer.hasArg("action")) {
@ -2409,6 +2709,10 @@ void handleFileList() {
}
void handleSDFileList() {
if (!web_interface->is_authenticated())
{
return;
}
String jsonfile = "[";
for (int i=0;i<web_interface->fileslist.size();i++)
{
@ -2421,11 +2725,16 @@ void handleSDFileList() {
web_interface->WebServer.send(200, "application/json", jsonfile);
}
//do a redirect to avoid to many query
//and handle not registred path
void handle_not_found()
{
if (!web_interface->is_authenticated())
{
String header = "HTTP/1.1 301 OK\r\nLocation: /\r\nCache-Control: no-cache\r\n\r\n";
web_interface->WebServer.sendContent(header);
return;
}
String path = web_interface->WebServer.uri();
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
@ -2513,6 +2822,193 @@ else
}
}
void handle_login()
{
String stmp,smsg;
String sReturn;
String sUser,sPassword;
bool msg_alert_error=false;
bool msg_alert_success=false;
STORESTRINGS_CLASS KeysList ;
STORESTRINGS_CLASS ValuesList ;
if (web_interface->WebServer.hasArg("DISCONNECT")){
String header = "HTTP/1.1 301 OK\r\nSet-Cookie: ESPSESSIONID=0\r\nLocation: /LOGIN\r\nCache-Control: no-cache\r\n\r\n";
web_interface->WebServer.sendContent(header);
return;
}
//check is it is a submission or a display
smsg="";
if (web_interface->WebServer.hasArg("return")) web_interface->urldecode(sReturn,web_interface->WebServer.arg("return").c_str());
if (web_interface->WebServer.hasArg("SUBMIT"))
{ //is there a correct list of values?
if ( web_interface->WebServer.hasArg("PASSWORD")&& web_interface->WebServer.hasArg("USER"))
{
//USER
web_interface->urldecode(sUser,web_interface->WebServer.arg("USER").c_str());
#ifdef AUTHENTICATION_FEATURE
if (sUser!="admin")
{
msg_alert_error=true;
smsg+="Error : Incorrect User<BR>";
KeysList.add(FPSTR(KEY_USER_STATUS));
ValuesList.add(FPSTR(VALUE_HAS_ERROR));
}
//Password
web_interface->urldecode(sPassword,web_interface->WebServer.arg("PASSWORD").c_str());
String scurrentPassword;
if (!CONFIG::read_string(EP_ADMIN_PWD, scurrentPassword , MAX_ADMIN_PASSWORD_LENGTH) )scurrentPassword=FPSTR(DEFAULT_ADMIN);
if (strcmp(sPassword.c_str(),scurrentPassword.c_str())!=0)
{
msg_alert_error=true;
smsg+="Error : Incorrect password<BR>";
KeysList.add(FPSTR(KEY_USER_PASSWORD_STATUS));
ValuesList.add(FPSTR(VALUE_HAS_ERROR));
}
#endif
}
else
{
msg_alert_error=true;
smsg="Error : Missing data";
}
//if no error login is ok
if (msg_alert_error==false)
{
#ifdef AUTHENTICATION_FEATURE
auth_ip * current_auth = new auth_ip;
current_auth->ip=web_interface->WebServer.client().remoteIP();
strcpy(current_auth->sessionID,web_interface->create_session_ID());
current_auth->last_time=millis();
if (web_interface->AddAuthIP(current_auth))
{
String header = "HTTP/1.1 301 OK\r\nSet-Cookie: ESPSESSIONID=";
header+=current_auth->sessionID;
header+="\r\nLocation: /";
header+=sReturn;
header+="\r\nCache-Control: no-cache\r\n\r\n";
web_interface->WebServer.sendContent(header);
return;
}
else
{
delete current_auth;
msg_alert_error=true;
smsg="Error : Too many connections";
}
#endif
}
}
else //no submit need to get data from EEPROM
{
sUser=String();
//password
sPassword=String();
}
//Display values
KeysList.add(FPSTR(KEY_RETURN));
ValuesList.add(sReturn);
//Free Mem, put at the end to reflect situation
KeysList.add(FPSTR(KEY_FREE_MEM));
ValuesList.add(intTostr(system_get_free_heap_size()));
KeysList.add(FPSTR(KEY_DISCONNECT_VISIBILITY));
if (web_interface->is_authenticated())ValuesList.add(FPSTR(VALUE_ITEM_VISIBLE));
else ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
//IP
stmp=FPSTR(KEY_IP);
KeysList.add(stmp);
if (wifi_get_opmode()==WIFI_STA ) stmp=WiFi.localIP().toString();
else stmp=WiFi.softAPIP().toString();
ValuesList.add(stmp);
//Web address = ip + port
KeysList.add(FPSTR(KEY_WEB_ADDRESS));
if (wifi_config.iweb_port!=80)
{
stmp+=":";
stmp+=intTostr(wifi_config.iweb_port);
}
ValuesList.add(stmp);
//mode
if (wifi_get_opmode()==WIFI_STA )
{
KeysList.add(FPSTR(KEY_MODE));
ValuesList.add(FPSTR(VALUE_STA));
}
else
{
if (wifi_get_opmode()==WIFI_AP )
{
KeysList.add(FPSTR(KEY_MODE));
ValuesList.add(FPSTR(VALUE_AP));
}
else
{
KeysList.add(FPSTR(KEY_MODE));
ValuesList.add(FPSTR(VALUE_AP_STA));
}
}
//FW Version
KeysList.add(FPSTR(KEY_FW_VER));
ValuesList.add(FPSTR(VALUE_FW_VERSION));
//page title
KeysList.add(FPSTR(KEY_PAGE_TITLE));
ValuesList.add(FPSTR(VALUE_LOGIN));
//tpl file name with extension
KeysList.add(FPSTR(KEY_FILE_NAME));
ValuesList.add("login.tpl");
//tpl file name without extension
KeysList.add(FPSTR(KEY_SHORT_FILE_NAME));
ValuesList.add("login");
//User
KeysList.add(FPSTR(KEY_USER));
ValuesList.add(sUser);
//password
KeysList.add(FPSTR(KEY_USER_PASSWORD));
ValuesList.add(sPassword);
if (msg_alert_error)
{
KeysList.add(FPSTR(KEY_ERROR_MSG));
ValuesList.add(smsg);
KeysList.add(FPSTR(KEY_SUCCESS_MSG));
ValuesList.add("");
KeysList.add(FPSTR(KEY_ERROR_MSG_VISIBILITY ));
ValuesList.add(FPSTR(VALUE_ITEM_VISIBLE));
KeysList.add(FPSTR(KEY_SUCCESS_MSG_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
KeysList.add(FPSTR(KEY_SUBMIT_BUTTON_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_VISIBLE));
KeysList.add(FPSTR(KEY_SERVICE_PAGE));
ValuesList.add("");
}
else
{
KeysList.add(FPSTR(KEY_ERROR_MSG));
ValuesList.add("");
KeysList.add(FPSTR(KEY_SUCCESS_MSG));
ValuesList.add("");
KeysList.add(FPSTR(KEY_ERROR_MSG_VISIBILITY ));
ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
KeysList.add(FPSTR(KEY_SUCCESS_MSG_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_HIDDEN));
KeysList.add(FPSTR(KEY_SUBMIT_BUTTON_VISIBILITY));
ValuesList.add(FPSTR(VALUE_ITEM_VISIBLE));
KeysList.add(FPSTR(KEY_SERVICE_PAGE));
ValuesList.add("");
}
//process the template file and provide list of variables
if(KeysList.size()==ValuesList.size()) //Sanity check
processTemplate("/login.tpl", KeysList , ValuesList);
//need to clean to speed up memory recovery
KeysList.clear();
ValuesList.clear();
}
void handle_restart()
{
if (SPIFFS.exists("/restart.tpl"))
@ -2581,18 +3077,22 @@ void handle_restart()
void handle_web_command()
{
if (!web_interface->is_authenticated())
{
return;
}
//check we have proper parameter
if (web_interface->WebServer.hasArg("COM"))
{
String scmd;
//decode command
web_interface->urldecode(scmd,web_interface->WebServer.arg("COM").c_str());
scmd.trim();
//send command to serial
Serial.println(scmd);
//give an ack - we need to be polite, right ?
web_interface->WebServer.send(200,"text/plain","Ok");
}
if (web_interface->WebServer.hasArg("COM"))
{
String scmd;
//decode command
web_interface->urldecode(scmd,web_interface->WebServer.arg("COM").c_str());
scmd.trim();
//send command to serial
Serial.println(scmd);
//give an ack - we need to be polite, right ?
web_interface->WebServer.send(200,"text/plain","Ok");
}
}
@ -2648,8 +3148,11 @@ WEBINTERFACE_CLASS::WEBINTERFACE_CLASS (int port):WebServer(port)
WebServer.on("/PRINTER",HTTP_ANY, handle_web_interface_printer);
WebServer.on("/CMD",HTTP_ANY, handle_web_command);
WebServer.on("/RESTART",HTTP_GET, handle_restart);
WebServer.on("/UPDATE",HTTP_ANY, handleUpdate);
WebServer.on("/FILES", HTTP_ANY, handleFileList);
WebServer.on("/SDFILES", HTTP_ANY, handleSDFileList);
WebServer.on("/LOGIN", HTTP_ANY, handle_login);
WebServer.on("/PASSWORD", HTTP_ANY, handle_password);
WebServer.onFileUpload(handleFileUpload);
//Captive portal Feature
#ifdef CAPTIVE_PORTAL_FEATURE
@ -2676,6 +3179,9 @@ WEBINTERFACE_CLASS::WEBINTERFACE_CLASS (int port):WebServer(port)
fileslist.setlenght(30);//12 for filename + space + size
fileslist.setsize(70); // 70 files to limite to 2K
fsUploadFile=(fs::File)0;
_head=NULL;
_nb_ip=0;
_upload_status=UPLOAD_STATUS_NONE;
}
//Destructor
WEBINTERFACE_CLASS::~WEBINTERFACE_CLASS()
@ -2684,7 +3190,103 @@ WEBINTERFACE_CLASS::~WEBINTERFACE_CLASS()
error_msg.clear();
status_msg.clear();
fileslist.clear();
while (_head)
{
auth_ip * current = _head;
_head=_head->_next;
delete current;
}
_nb_ip=0;
}
//Session ID based on IP and time using 16 char
char * WEBINTERFACE_CLASS::create_session_ID(){
static char sessionID[17];
//reset SESSIONID
for (int i=0;i<17;i++)sessionID[i]='\0';
//get time
uint32_t now = millis();
//get remote IP
IPAddress remoteIP=WebServer.client().remoteIP();
//generate SESSIONID
if (0>sprintf(sessionID,"%02X%02X%02X%02X%02X%02X%02X%02X",remoteIP[0],remoteIP[1],remoteIP[2],remoteIP[3],(uint8_t) ((now >> 0) & 0xff),(uint8_t) ((now >> 8) & 0xff),(uint8_t) ((now >> 16) & 0xff),(uint8_t) ((now >> 24) & 0xff)))strcpy(sessionID,"NONE");
return sessionID;
}
//check authentification
bool WEBINTERFACE_CLASS::is_authenticated()
{
#ifdef AUTHENTICATION_FEATURE
if (WebServer.hasHeader("Cookie")){
String cookie = WebServer.header("Cookie");
int pos = cookie.indexOf("ESPSESSIONID=");
if (pos!= -1) {
int pos2 = cookie.indexOf(";",pos);
String sessionID = cookie.substring(pos+strlen("ESPSESSIONID="),pos2);
IPAddress ip = WebServer.client().remoteIP();
//check if cookie can be reset and clean table in same time
return ResetAuthIP(ip,sessionID.c_str());
}
}
return false;
#else
return true;
#endif
}
//add the information in the linked list if possible
bool WEBINTERFACE_CLASS::AddAuthIP(auth_ip * item)
{
if (_nb_ip>MAX_AUTH_IP) return false;
item->_next=_head;
_head=item;
_nb_ip++;
return true;
}
bool WEBINTERFACE_CLASS::ResetAuthIP(IPAddress ip,const char * sessionID)
{
bool done=false;
auth_ip * current = _head;
auth_ip * previous = NULL;
//get time
uint32_t now = millis();
while (current)
{
if ((millis()-current->last_time)>400000)
{
//remove
if (current==_head)
{
_head=current->_next;
_nb_ip--;
delete current;
current=_head;
}
else
{
previous->_next=current->_next;
_nb_ip--;
delete current;
current=previous->_next;
}
}
else
{
if (ip==current->ip)
{
if (strcmp(sessionID,current->sessionID)==0)
{
//reset time
current->last_time=millis();
return true;
}
}
previous = current;
current=current->_next;
}
}
return done;
}
WEBINTERFACE_CLASS * web_interface;

View File

@ -29,6 +29,13 @@
#define MAX_EXTRUDERS 4
struct auth_ip{
IPAddress ip;
char sessionID[17];
uint32_t last_time;
auth_ip * _next;
};
class WEBINTERFACE_CLASS
{
public:
@ -39,6 +46,7 @@ class WEBINTERFACE_CLASS
void urldecode( String & dst, const char *src);
bool isSSIDValid(const char * ssid);
bool isPasswordValid(const char * password);
bool isAdminPasswordValid(const char * password);
bool isHostnameValid(const char * hostname);
bool isIPValid(const char * IP);
String answer4M105;
@ -51,8 +59,15 @@ class WEBINTERFACE_CLASS
STORESTRINGS_CLASS info_msg;
STORESTRINGS_CLASS status_msg;
bool restartmodule;
char * create_session_ID();
bool is_authenticated();
bool AddAuthIP(auth_ip * item);
bool ResetAuthIP(IPAddress ip,const char * sessionID);
uint8_t _upload_status;
private:
auth_ip * _head;
uint8_t _nb_ip;
};
extern WEBINTERFACE_CLASS * web_interface;

BIN
page5.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 46 KiB