diff --git a/Page1.png b/Page1.png
index c495615c..ff519e3c 100644
Binary files a/Page1.png and b/Page1.png differ
diff --git a/Page2.png b/Page2.png
index a45ec602..d42242a8 100644
Binary files a/Page2.png and b/Page2.png differ
diff --git a/Page3.png b/Page3.png
index e13829d2..93051361 100644
Binary files a/Page3.png and b/Page3.png differ
diff --git a/Page4.png b/Page4.png
index 0dd26a6c..7544e911 100644
Binary files a/Page4.png and b/Page4.png differ
diff --git a/Page6.png b/Page6.png
index 9f9b4ad5..c7522cb6 100644
Binary files a/Page6.png and b/Page6.png differ
diff --git a/Page7.png b/Page7.png
new file mode 100644
index 00000000..fc32ed01
Binary files /dev/null and b/Page7.png differ
diff --git a/Page8.png b/Page8.png
new file mode 100644
index 00000000..fe4826b3
Binary files /dev/null and b/Page8.png differ
diff --git a/README.md b/README.md
index eb879c47..fef050a4 100644
--- a/README.md
+++ b/README.md
@@ -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:

Extra Settings Page, for web UI and for printer:

+Change password Page:
+
+Login Page:
+
the template files are stored on SPIFFS:

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)
diff --git a/esp8266/config.cpp b/esp8266/config.cpp
index db636506..849bafb8 100644
--- a/esp8266/config.cpp
+++ b/esp8266/config.cpp
@@ -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;
}
diff --git a/esp8266/config.h b/esp8266/config.h
index f6b4633f..4124fa69 100644
--- a/esp8266/config.h
+++ b/esp8266/config.h
@@ -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
diff --git a/esp8266/data/css.inc b/esp8266/data/css.inc
index f4aff0c9..fbb98a17 100644
--- a/esp8266/data/css.inc
+++ b/esp8266/data/css.inc
@@ -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;}
diff --git a/esp8266/data/header.inc b/esp8266/data/header.inc
index 1fbdee0c..5d90c9c0 100644
--- a/esp8266/data/header.inc
+++ b/esp8266/data/header.inc
@@ -11,12 +11,19 @@ $INCLUDE[css.inc]$
-
-
-
-
+
+
+
+
|
+ Admin |
+ |
FW: V$FW_VER$ |
Github |
diff --git a/esp8266/data/login.tpl b/esp8266/data/login.tpl
new file mode 100644
index 00000000..705e079b
--- /dev/null
+++ b/esp8266/data/login.tpl
@@ -0,0 +1,21 @@
+$INCLUDE[header.inc]$
+
+$INCLUDE[footer.inc]$
diff --git a/esp8266/data/password.tpl b/esp8266/data/password.tpl
new file mode 100644
index 00000000..9ecfcff7
--- /dev/null
+++ b/esp8266/data/password.tpl
@@ -0,0 +1,111 @@
+$INCLUDE[header.inc]$
+
+
+$INCLUDE[footer.inc]$
diff --git a/esp8266/data/system.tpl b/esp8266/data/system.tpl
index 8fe99276..81cd3b6b 100644
--- a/esp8266/data/system.tpl
+++ b/esp8266/data/system.tpl
@@ -1,5 +1,16 @@
$INCLUDE[header.inc]$
-
+
+
+
+
$INCLUDE[footer.inc]$
diff --git a/esp8266/esp8266.ino b/esp8266/esp8266.ino
index a5e59fea..7083b355 100644
--- a/esp8266/esp8266.ino
+++ b/esp8266/esp8266.ino
@@ -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);
diff --git a/esp8266/webinterface.cpp b/esp8266/webinterface.cpp
index d5843a88..508d50f2 100644
--- a/esp8266/webinterface.cpp
+++ b/esp8266/webinterface.cpp
@@ -36,6 +36,14 @@ extern "C" {
#ifdef SSDP_FEATURE
#include
#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 ="\n\nRedirecting... \n\n\nUnknown page - you will be redirected...\n
\nif not redirected, click here\n
\n\n\n\n\n";
const char PAGE_RESTART [] PROGMEM ="\n\nRestarting... \n\n\nRestarting, please wait....\n
\n\n\n\n\n";
const char RESTARTCMD [] PROGMEM ="";
@@ -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)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
";
+ 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
";
+ 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;ifileslist.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
";
+ 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
";
+ 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;
diff --git a/esp8266/webinterface.h b/esp8266/webinterface.h
index 2a6b34a0..93da1b9b 100644
--- a/esp8266/webinterface.h
+++ b/esp8266/webinterface.h
@@ -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;
diff --git a/page5.png b/page5.png
index c3fe3b49..6db7bb5b 100644
Binary files a/page5.png and b/page5.png differ