19 m_dnsServer.processNextRequest();
33bool FSWebServer::checkDir(
char *dirname, uint8_t levels)
35 if (dirname[0] !=
'/')
37 File root = m_filesystem->open(dirname,
"r");
43 if (!root.isDirectory())
48 File file = root.openNextFile();
51 if (file.isDirectory())
55 strcat(dir, file.name());
57 checkDir(dir, levels - 1);
61 DebugPrintf(
" FILE: %s\tSIZE: %d\n", file.name(), file.size());
63 file = root.openNextFile();
72 strcpy(m_basePath, path);
74 m_fsOK = checkDir(m_basePath, 2);
76#ifdef INCLUDE_EDIT_HTM
77 webserver->on(
"/status", HTTP_GET, std::bind(&FSWebServer::handleStatus,
this));
78 webserver->on(
"/list", HTTP_GET, std::bind(&FSWebServer::handleFileList,
this));
79 webserver->on(
"/edit", HTTP_GET, std::bind(&FSWebServer::handleGetEdit,
this));
80 webserver->on(
"/edit", HTTP_PUT, std::bind(&FSWebServer::handleFileCreate,
this));
81 webserver->on(
"/edit", HTTP_DELETE, std::bind(&FSWebServer::handleFileDelete,
this));
83 webserver->onNotFound(std::bind(&FSWebServer::handleRequest,
this));
84 webserver->on(
"/favicon.ico", HTTP_GET, std::bind(&FSWebServer::replyOK,
this));
85 webserver->on(
"/", HTTP_GET, std::bind(&FSWebServer::handleIndex,
this));
86#ifdef INCLUDE_SETUP_HTM
87 webserver->on(
"/setup", HTTP_GET, std::bind(&FSWebServer::handleSetup,
this));
89 webserver->on(
"/scan", HTTP_GET, std::bind(&FSWebServer::handleScanNetworks,
this));
90 webserver->on(
"/connect", HTTP_POST, std::bind(&FSWebServer::doWifiConnection,
this));
91 webserver->on(
"/restart", HTTP_GET, std::bind(&FSWebServer::doRestart,
this));
92 webserver->on(
"/ipaddress", HTTP_GET, std::bind(&FSWebServer::getIpAddress,
this));
95 webserver->on(
"/redirect", HTTP_GET, std::bind(&FSWebServer::captivePortal,
this));
97 webserver->on(
"/connecttest.txt", HTTP_GET, std::bind(&FSWebServer::captivePortal,
this));
99 webserver->on(
"/hotspot-detect.html", HTTP_GET, std::bind(&FSWebServer::captivePortal,
this));
101 webserver->on(
"/generate_204", HTTP_GET, std::bind(&FSWebServer::captivePortal,
this));
102 webserver->on(
"/gen_204", HTTP_GET, std::bind(&FSWebServer::captivePortal,
this));
107 webserver->on(
"/edit", HTTP_POST, std::bind(&FSWebServer::replyOK,
this), std::bind(&FSWebServer::handleFileUpload,
this));
123 m_apWebpage = (
char *)realloc(m_apWebpage,
sizeof(url));
124 strcpy(m_apWebpage, url);
130 WiFi.mode(WIFI_AP_STA);
131 WiFi.persistent(
false);
132 WiFi.softAP(ssid, psk);
134 m_dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
135 m_dnsServer.start(53,
"*", WiFi.softAPIP());
136 return WiFi.softAPIP();
148 struct station_config conf;
149 wifi_station_get_config_default(&conf);
150 _ssid =
reinterpret_cast<const char *
>(conf.ssid);
151 _pass =
reinterpret_cast<const char *
>(conf.password);
155 esp_wifi_get_config(WIFI_IF_STA, &conf);
157 _ssid =
reinterpret_cast<const char *
>(conf.sta.ssid);
158 _pass =
reinterpret_cast<const char *
>(conf.sta.password);
161 if (strlen(_ssid) && strlen(_pass))
163 WiFi.begin(_ssid, _pass);
164 Serial.print(F(
"Connecting to "));
165 Serial.println(_ssid);
166 uint32_t startTime = millis();
167 while (WiFi.status() != WL_CONNECTED)
171 if (WiFi.status() == WL_CONNECTED)
177 if (millis() - startTime > m_timeout)
182 if (apSSID !=
nullptr && apPsw !=
nullptr)
188 ip = WiFi.softAPIP();
189 Serial.print(F(
"\nAP mode.\nServer IP address: "));
200bool FSWebServer::captivePortal()
202 IPAddress ip =
webserver->client().localIP();
203 char serverLoc[
sizeof(
"https:://255.255.255.255/") +
sizeof(m_apWebpage) + 1];
204 snprintf(serverLoc,
sizeof(serverLoc),
"http://%d.%d.%d.%d%s", ip[0], ip[1], ip[2], ip[3], m_apWebpage);
207 if (strcmp(serverLoc,
webserver->hostHeader().c_str()))
209 webserver->sendHeader(F(
"Location"), serverLoc,
true);
210 webserver->send(302, F(
"text/html"),
"");
217void FSWebServer::handleRequest()
225 String _url = WebServer::urlDecode(
webserver->uri());
226#elif defined(ESP8266)
227 String _url = ESP8266WebServer::urlDecode(
webserver->uri());
233 if (handleFileRead(_url))
239void FSWebServer::getIpAddress()
241 webserver->send(200,
"text/json", WiFi.localIP().toString());
244void FSWebServer::doRestart()
246 webserver->send(200,
"text/json",
"Going to restart ESP");
251void FSWebServer::doWifiConnection()
254 bool persistent =
true;
255 WiFi.mode(WIFI_AP_STA);
269 String pers =
webserver->arg(
"persistent");
270 if (pers.equals(
"false"))
276 if (WiFi.status() == WL_CONNECTED)
279 IPAddress ip = WiFi.localIP();
280 String resp =
"ESP is currently connected to a WiFi network.<br><br>"
281 "Actual connection will be closed and a new attempt will be done with <b>";
283 resp +=
"</b> WiFi network.";
284 webserver->send(200,
"text/plain", resp);
287 Serial.println(
"Disconnect from current WiFi network");
291 if (ssid.length() && pass.length())
294 Serial.print(
"\nConnecting to ");
295 Serial.println(ssid);
296 WiFi.begin(ssid.c_str(), pass.c_str());
298 uint32_t beginTime = millis();
299 while (WiFi.status() != WL_CONNECTED)
303 if (millis() - beginTime > m_timeout)
307 if (WiFi.status() == WL_CONNECTED)
310 IPAddress ip = WiFi.localIP();
311 Serial.print(
"\nConnected to Wifi! IP address: ");
313 String serverLoc = F(
"http://");
314 for (
int i = 0; i < 4; i++)
315 serverLoc += i ?
"." + String(ip[i]) : String(ip[i]);
317 String resp =
"Restart ESP and then reload this page from <a href='";
319 resp +=
"/setup'>the new LAN address</a>";
321 webserver->send(200,
"text/plain", resp);
328 struct station_config stationConf;
329 wifi_station_get_config_default(&stationConf);
331 memset(&stationConf, 0,
sizeof(stationConf));
332 os_memcpy(&stationConf.ssid, ssid.c_str(), ssid.length());
333 os_memcpy(&stationConf.password, pass.c_str(), pass.length());
334 wifi_set_opmode(STATION_MODE);
335 wifi_station_set_config(&stationConf);
337 wifi_config_t stationConf;
338 esp_wifi_get_config(WIFI_IF_STA, &stationConf);
340 memset(&stationConf, 0,
sizeof(stationConf));
341 memcpy(&stationConf.sta.ssid, ssid.c_str(), ssid.length());
342 memcpy(&stationConf.sta.password, pass.c_str(), pass.length());
343 esp_wifi_set_config(WIFI_IF_STA, &stationConf);
348 struct station_config stationConf;
349 wifi_station_get_config_default(&stationConf);
351 memset(&stationConf, 0,
sizeof(stationConf));
352 wifi_station_set_config(&stationConf);
354 wifi_config_t stationConf;
355 esp_wifi_get_config(WIFI_IF_STA, &stationConf);
357 memset(&stationConf, 0,
sizeof(stationConf));
358 esp_wifi_set_config(WIFI_IF_STA, &stationConf);
363 webserver->send(500,
"text/plain",
"Connection error, maybe the password is wrong?");
365 webserver->send(500,
"text/plain",
"Wrong credentials provided");
368void FSWebServer::setCrossOrigin()
370 webserver->sendHeader(F(
"Access-Control-Allow-Origin"), F(
"*"));
371 webserver->sendHeader(F(
"Access-Control-Max-Age"), F(
"600"));
372 webserver->sendHeader(F(
"Access-Control-Allow-Methods"), F(
"PUT,POST,GET,OPTIONS"));
373 webserver->sendHeader(F(
"Access-Control-Allow-Headers"), F(
"*"));
376void FSWebServer::handleScanNetworks()
378 String jsonList =
"[";
380 int n = WiFi.scanNetworks();
393 for (
int i = 0; i < n; ++i)
395 String ssid = WiFi.SSID(i);
396 int rssi = WiFi.RSSI(i);
398 String security = WiFi.encryptionType(i) == AUTH_OPEN ?
"none" :
"enabled";
400 String security = WiFi.encryptionType(i) == WIFI_AUTH_OPEN ?
"none" :
"enabled";
402 jsonList +=
"{\"ssid\":\"";
404 jsonList +=
"\",\"strength\":\"";
406 jsonList +=
"\",\"security\":";
407 jsonList += security ==
"none" ?
"false" :
"true";
408 jsonList += ssid.equals(WiFi.SSID()) ?
",\"selected\": true" :
"";
409 jsonList += i < n - 1 ?
"}," :
"}";
413 webserver->send(200,
"text/json", jsonList);
418#ifdef INCLUDE_SETUP_HTM
421 File file = m_filesystem->open(
"/config.json",
"r");
422 int sz = file.size() * 1.33;
423 int docSize = max(sz, 2048);
424 DynamicJsonDocument doc((
size_t)docSize);
428 DeserializationError error = deserializeJson(doc, file);
431 DebugPrintln(F(
"Failed to deserialize file, may be corrupted"));
440 DebugPrintln(F(
"File not found, will be created new configuration file"));
446 if (doc.containsKey(label))
449 JsonObject obj = doc.createNestedObject(label);
450 obj[
"selected"] = array[0];
451 JsonArray arr = obj.createNestedArray(
"values");
452 for (
int i=0; i<size; i++) {
456 file = m_filesystem->open(
"/config.json",
"w");
457 if (serializeJsonPretty(doc, file) == 0)
465void FSWebServer::removeWhiteSpaces(String& str) {
466 const char noChars[] = {
'\n',
'\r',
'\t'};
469 for (
int i=0; i<
sizeof(noChars); i++) {
470 pos = str.indexOf(noChars[i]);
472 str.replace(String(noChars[i]),
"");
473 pos = str.indexOf(noChars[i]);
477 pos = str.indexOf(
" ");
479 str.replace(
" ",
" ");
480 pos = str.indexOf(
" ");
484void FSWebServer::handleSetup()
486 webserver->sendHeader(PSTR(
"Content-Encoding"),
"gzip");
491void FSWebServer::handleIndex()
493 if (m_filesystem->exists(
"/index.htm"))
495 handleFileRead(
"/index.htm");
497 else if (m_filesystem->exists(
"/index.html"))
499 handleFileRead(
"/index.html");
501#ifdef INCLUDE_SETUP_HTM
512bool FSWebServer::handleFileRead(
const String &uri)
514 String path = m_basePath;
518 if (path.endsWith(
"/"))
522 String pathWithGz = path +
".gz";
524 if (m_filesystem->exists(pathWithGz) || m_filesystem->exists(path))
526 if (m_filesystem->exists(pathWithGz))
530 const char *contentType = getContentType(path.c_str());
531 File file = m_filesystem->open(path,
"r");
532 if (
webserver->streamFile(file, contentType) != file.size())
546void FSWebServer::handleFileUpload()
552 HTTPUpload &upload =
webserver->upload();
553 if (upload.status == UPLOAD_FILE_START)
555 String filename = upload.filename;
558 if (!filename.startsWith(
"/"))
560 filename =
"/" + filename;
562 checkForUnsupportedPath(filename, result);
563 if (result.length() > 0)
565 replyToCLient(
ERROR, PSTR(
"INVALID FILENAME"));
569 DebugPrintf_P(PSTR(
"handleFileUpload Name: %s\n"), filename.c_str());
570 m_uploadFile = m_filesystem->open(filename,
"w");
573 replyToCLient(
ERROR, PSTR(
"CREATE FAILED"));
576 DebugPrintf_P(PSTR(
"Upload: START, filename: %s\n"), filename.c_str());
578 else if (upload.status == UPLOAD_FILE_WRITE)
582 size_t bytesWritten = m_uploadFile.write(upload.buf, upload.currentSize);
583 if (bytesWritten != upload.currentSize)
585 replyToCLient(
ERROR, PSTR(
"WRITE FAILED"));
589 DebugPrintf_P(PSTR(
"Upload: WRITE, Bytes: %d\n"), upload.currentSize);
591 else if (upload.status == UPLOAD_FILE_END)
595 m_uploadFile.close();
597 DebugPrintf_P(PSTR(
"Upload: END, Size: %d\n"), upload.totalSize);
601void FSWebServer::replyOK()
603 replyToCLient(OK,
"");
606void FSWebServer::replyToCLient(
int msg_type = 0,
const char *msg =
"")
608 webserver->sendHeader(
"Access-Control-Allow-Origin",
"*");
633void FSWebServer::checkForUnsupportedPath(String &filename, String &error)
635 if (!filename.startsWith(
"/"))
637 error += PSTR(
" !! NO_LEADING_SLASH !! ");
639 if (filename.indexOf(
"//") != -1)
641 error += PSTR(
" !! DOUBLE_SLASH !! ");
643 if (filename.endsWith(
"/"))
645 error += PSTR(
" ! TRAILING_SLASH ! ");
651const char *FSWebServer::getContentType(
const char *filename)
654 return PSTR(
"application/octet-stream");
655 else if (strstr(filename,
".htm"))
656 return PSTR(
"text/html");
657 else if (strstr(filename,
".html"))
658 return PSTR(
"text/html");
659 else if (strstr(filename,
".css"))
660 return PSTR(
"text/css");
661 else if (strstr(filename,
".sass"))
662 return PSTR(
"text/css");
663 else if (strstr(filename,
".js"))
664 return PSTR(
"application/javascript");
665 else if (strstr(filename,
".png"))
666 return PSTR(
"image/png");
667 else if (strstr(filename,
".svg"))
668 return PSTR(
"image/svg+xml");
669 else if (strstr(filename,
".gif"))
670 return PSTR(
"image/gif");
671 else if (strstr(filename,
".jpg"))
672 return PSTR(
"image/jpeg");
673 else if (strstr(filename,
".ico"))
674 return PSTR(
"image/x-icon");
675 else if (strstr(filename,
".xml"))
676 return PSTR(
"text/xml");
677 else if (strstr(filename,
".pdf"))
678 return PSTR(
"application/x-pdf");
679 else if (strstr(filename,
".zip"))
680 return PSTR(
"application/x-zip");
681 else if (strstr(filename,
".gz"))
682 return PSTR(
"application/x-gzip");
683 return PSTR(
"text/plain");
687#ifdef INCLUDE_EDIT_HTM
693void FSWebServer::handleFileList()
697 return replyToCLient(
BAD_REQUEST,
"DIR ARG MISSING");
702 if (path !=
"/" && !m_filesystem->exists(path))
707 File root = m_filesystem->open(path,
"r");
712 if (root.isDirectory())
714 File file = root.openNextFile();
717 String filename = file.name();
718 if (filename.lastIndexOf(
"/") > -1)
720 filename.remove(0, filename.lastIndexOf(
"/") + 1);
726 output +=
"{\"type\":\"";
727 output += (file.isDirectory()) ?
"dir" :
"file";
728 output +=
"\",\"size\":\"";
729 output += file.size();
730 output +=
"\",\"name\":\"";
733 file = root.openNextFile();
737 webserver->send(200,
"text/json", output);
751void FSWebServer::handleFileCreate()
756 replyToCLient(
BAD_REQUEST, PSTR(
"PATH ARG MISSING"));
770 if (path.endsWith(
"/"))
773 path.remove(path.length() - 1);
774 if (!m_filesystem->mkdir(path))
776 replyToCLient(
ERROR, PSTR(
"MKDIR FAILED"));
783 File file = m_filesystem->open(path,
"w");
791 replyToCLient(
ERROR, PSTR(
"CREATE FAILED"));
795 replyToCLient(
CUSTOM, path.c_str());
805 if (!m_filesystem->exists(src))
807 replyToCLient(
BAD_REQUEST, PSTR(
"BSRC FILE NOT FOUND"));
811 DebugPrintf_P(PSTR(
"handleFileCreate: %s from %s\n"), path.c_str(), src.c_str());
812 if (path.endsWith(
"/"))
814 path.remove(path.length() - 1);
816 if (src.endsWith(
"/"))
818 src.remove(src.length() - 1);
820 if (!m_filesystem->rename(src, path))
822 replyToCLient(
ERROR, PSTR(
"RENAME FAILED"));
836void FSWebServer::handleFileDelete()
840 if (path.isEmpty() || path ==
"/")
847 if (!m_filesystem->exists(path))
853 File root = m_filesystem->open(path,
"r");
855 if (!root.isDirectory())
858 m_filesystem->remove(path);
863 m_filesystem->rmdir(path);
874void FSWebServer::handleGetEdit()
876#ifdef INCLUDE_EDIT_HTM
877 webserver->sendHeader(PSTR(
"Content-Encoding"),
"gzip");
878 webserver->send_P(200,
"text/html", edit_htm_gz,
sizeof(edit_htm_gz));
880 replyToCLient(
NOT_FOUND, PSTR(
"FILE_NOT_FOUND"));
887void FSWebServer::handleStatus()
891 size_t totalBytes = 1024;
892 size_t usedBytes = 0;
896 m_filesystem->info(fs_info);
897 totalBytes = fs_info.totalBytes;
898 usedBytes = fs_info.usedBytes;
906 json =
"{\"type\":\"Filesystem\", \"isOk\":";
909 json += PSTR(
"\"true\", \"totalBytes\":\"");
911 json += PSTR(
"\", \"usedBytes\":\"");
917 json += PSTR(
",\"unsupportedFiles\":\"\"}");
918 webserver->send(200,
"application/json", json);
IPAddress startWiFi(uint32_t timeout, const char *apSSID, const char *apPsw)
void addDropdownList(const char *label, const char **array, size_t size)
WebServerClass * webserver
IPAddress setAPmode(const char *ssid, const char *psk)
void addHandler(const Uri &uri, HTTPMethod method, WebServerClass::THandlerFunction fn)
FSWebServer(fs::FS &fs, WebServerClass &server)
WebServerClass * getRequest()
void setCaptiveWebage(const char *url)
bool begin(const char *path=nullptr)
#define DebugPrintf_P(x,...)
#define DebugPrintf(x,...)