225fd0d05b
Having the wrong pin would result in a server error (500). The more appropriate error code for that would be 401. This also changes the page that asks for users to login from 200 to 401.
650 lines
24 KiB
C++
650 lines
24 KiB
C++
#include "wled.h"
|
|
|
|
#include "html_ui.h"
|
|
#ifdef WLED_ENABLE_SIMPLE_UI
|
|
#include "html_simple.h"
|
|
#endif
|
|
#include "html_settings.h"
|
|
#include "html_other.h"
|
|
#ifdef WLED_ENABLE_PIXART
|
|
#include "html_pixart.h"
|
|
#endif
|
|
#ifndef WLED_DISABLE_PXMAGIC
|
|
#include "html_pxmagic.h"
|
|
#endif
|
|
#include "html_cpal.h"
|
|
|
|
/*
|
|
* Integrated HTTP web server page declarations
|
|
*/
|
|
|
|
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request);
|
|
void setStaticContentCacheHeaders(AsyncWebServerResponse *response);
|
|
|
|
// define flash strings once (saves flash memory)
|
|
static const char s_redirecting[] PROGMEM = "Redirecting...";
|
|
static const char s_content_enc[] PROGMEM = "Content-Encoding";
|
|
static const char s_unlock_ota [] PROGMEM = "Please unlock OTA in security settings!";
|
|
static const char s_unlock_cfg [] PROGMEM = "Please unlock settings using PIN code!";
|
|
|
|
//Is this an IP?
|
|
bool isIp(String str) {
|
|
for (size_t i = 0; i < str.length(); i++) {
|
|
int c = str.charAt(i);
|
|
if (c != '.' && (c < '0' || c > '9')) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
|
|
if (!correctPIN) {
|
|
if (final) request->send(401, "text/plain", FPSTR(s_unlock_cfg));
|
|
return;
|
|
}
|
|
if (!index) {
|
|
String finalname = filename;
|
|
if (finalname.charAt(0) != '/') {
|
|
finalname = '/' + finalname; // prepend slash if missing
|
|
}
|
|
|
|
request->_tempFile = WLED_FS.open(finalname, "w");
|
|
DEBUG_PRINT(F("Uploading "));
|
|
DEBUG_PRINTLN(finalname);
|
|
if (finalname.equals("/presets.json")) presetsModifiedTime = toki.second();
|
|
}
|
|
if (len) {
|
|
request->_tempFile.write(data,len);
|
|
}
|
|
if (final) {
|
|
request->_tempFile.close();
|
|
if (filename.indexOf(F("cfg.json")) >= 0) { // check for filename with or without slash
|
|
doReboot = true;
|
|
request->send(200, "text/plain", F("Configuration restore successful.\nRebooting..."));
|
|
} else {
|
|
if (filename.indexOf(F("palette")) >= 0 && filename.indexOf(F(".json")) >= 0) strip.loadCustomPalettes();
|
|
request->send(200, "text/plain", F("File Uploaded!"));
|
|
}
|
|
cacheInvalidate++;
|
|
}
|
|
}
|
|
|
|
void createEditHandler(bool enable) {
|
|
if (editHandler != nullptr) server.removeHandler(editHandler);
|
|
if (enable) {
|
|
#ifdef WLED_ENABLE_FS_EDITOR
|
|
#ifdef ARDUINO_ARCH_ESP32
|
|
editHandler = &server.addHandler(new SPIFFSEditor(WLED_FS));//http_username,http_password));
|
|
#else
|
|
editHandler = &server.addHandler(new SPIFFSEditor("","",WLED_FS));//http_username,http_password));
|
|
#endif
|
|
#else
|
|
editHandler = &server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
serveMessage(request, 501, "Not implemented", F("The FS editor is disabled in this build."), 254);
|
|
});
|
|
#endif
|
|
} else {
|
|
editHandler = &server.on("/edit", HTTP_ANY, [](AsyncWebServerRequest *request){
|
|
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_cfg), 254);
|
|
});
|
|
}
|
|
}
|
|
|
|
bool captivePortal(AsyncWebServerRequest *request)
|
|
{
|
|
if (ON_STA_FILTER(request)) return false; //only serve captive in AP mode
|
|
String hostH;
|
|
if (!request->hasHeader("Host")) return false;
|
|
hostH = request->getHeader("Host")->value();
|
|
|
|
if (!isIp(hostH) && hostH.indexOf("wled.me") < 0 && hostH.indexOf(cmDNS) < 0) {
|
|
DEBUG_PRINTLN("Captive portal");
|
|
AsyncWebServerResponse *response = request->beginResponse(302);
|
|
response->addHeader(F("Location"), F("http://4.3.2.1"));
|
|
request->send(response);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void initServer()
|
|
{
|
|
//CORS compatiblity
|
|
DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Origin"), "*");
|
|
DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Methods"), "*");
|
|
DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Headers"), "*");
|
|
|
|
#ifdef WLED_ENABLE_WEBSOCKETS
|
|
#ifndef WLED_DISABLE_2D
|
|
server.on("/liveview2D", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_liveviewws2D, PAGE_liveviewws2D_length);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
#endif
|
|
#endif
|
|
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_liveview, PAGE_liveview_length);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
|
|
//settings page
|
|
server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
serveSettings(request);
|
|
});
|
|
|
|
// "/settings/settings.js&p=x" request also handled by serveSettings()
|
|
|
|
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
|
|
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if(!handleFileRead(request, "/favicon.ico"))
|
|
{
|
|
request->send_P(200, "image/x-icon", favicon, 156);
|
|
}
|
|
});
|
|
|
|
server.on("/welcome", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
serveSettings(request);
|
|
});
|
|
|
|
server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
serveMessage(request, 200,F("Rebooting now..."),F("Please wait ~10 seconds..."),129);
|
|
doReboot = true;
|
|
});
|
|
|
|
server.on("/settings", HTTP_POST, [](AsyncWebServerRequest *request){
|
|
serveSettings(request, true);
|
|
});
|
|
|
|
server.on("/json", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
serveJson(request);
|
|
});
|
|
|
|
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) {
|
|
bool verboseResponse = false;
|
|
bool isConfig = false;
|
|
|
|
if (!requestJSONBufferLock(14)) return;
|
|
|
|
DeserializationError error = deserializeJson(doc, (uint8_t*)(request->_tempObject));
|
|
JsonObject root = doc.as<JsonObject>();
|
|
if (error || root.isNull()) {
|
|
releaseJSONBufferLock();
|
|
request->send(400, "application/json", F("{\"error\":9}")); // ERR_JSON
|
|
return;
|
|
}
|
|
if (root.containsKey("pin")) checkSettingsPIN(root["pin"].as<const char*>());
|
|
|
|
const String& url = request->url();
|
|
isConfig = url.indexOf("cfg") > -1;
|
|
if (!isConfig) {
|
|
/*
|
|
#ifdef WLED_DEBUG
|
|
DEBUG_PRINTLN(F("Serialized HTTP"));
|
|
serializeJson(root,Serial);
|
|
DEBUG_PRINTLN();
|
|
#endif
|
|
*/
|
|
verboseResponse = deserializeState(root);
|
|
} else {
|
|
if (!correctPIN && strlen(settingsPIN)>0) {
|
|
request->send(401, "application/json", F("{\"error\":1}")); // ERR_DENIED
|
|
releaseJSONBufferLock();
|
|
return;
|
|
}
|
|
verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately
|
|
}
|
|
releaseJSONBufferLock();
|
|
|
|
if (verboseResponse) {
|
|
if (!isConfig) {
|
|
serveJson(request); return; //if JSON contains "v"
|
|
} else {
|
|
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
|
|
}
|
|
}
|
|
request->send(200, "application/json", F("{\"success\":true}"));
|
|
}, JSON_BUFFER_SIZE);
|
|
server.addHandler(handler);
|
|
|
|
server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
request->send(200, "text/plain", (String)VERSION);
|
|
});
|
|
|
|
server.on("/uptime", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
request->send(200, "text/plain", (String)millis());
|
|
});
|
|
|
|
server.on("/freeheap", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
request->send(200, "text/plain", (String)ESP.getFreeHeap());
|
|
});
|
|
|
|
#ifdef WLED_ENABLE_USERMOD_PAGE
|
|
server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_usermod, PAGE_usermod_length);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
#endif
|
|
|
|
server.on("/teapot", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
serveMessage(request, 418, F("418. I'm a teapot."), F("(Tangible Embedded Advanced Project Of Twinkling)"), 254);
|
|
});
|
|
|
|
server.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request) {},
|
|
[](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data,
|
|
size_t len, bool final) {handleUpload(request, filename, index, data, len, final);}
|
|
);
|
|
|
|
#ifdef WLED_ENABLE_SIMPLE_UI
|
|
server.on("/simple.htm", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (handleFileRead(request, "/simple.htm")) return;
|
|
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_simple, PAGE_simple_L);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
#endif
|
|
|
|
server.on("/iro.js", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "application/javascript", iroJs, iroJs_length);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
|
|
server.on("/rangetouch.js", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "application/javascript", rangetouchJs, rangetouchJs_length);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
|
|
createEditHandler(correctPIN);
|
|
|
|
#ifndef WLED_DISABLE_OTA
|
|
//init ota page
|
|
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (otaLock) {
|
|
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254);
|
|
} else
|
|
serveSettings(request); // checks for "upd" in URL and handles PIN
|
|
});
|
|
|
|
server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){
|
|
if (!correctPIN) {
|
|
serveSettings(request, true); // handle PIN page POST request
|
|
return;
|
|
}
|
|
if (otaLock) {
|
|
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254);
|
|
return;
|
|
}
|
|
if (Update.hasError()) {
|
|
serveMessage(request, 500, F("Update failed!"), F("Please check your file and retry!"), 254);
|
|
} else {
|
|
serveMessage(request, 200, F("Update successful!"), F("Rebooting..."), 131);
|
|
doReboot = true;
|
|
}
|
|
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
|
|
if (!correctPIN || otaLock) return;
|
|
if(!index){
|
|
DEBUG_PRINTLN(F("OTA Update Start"));
|
|
WLED::instance().disableWatchdog();
|
|
usermods.onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init)
|
|
lastEditTime = millis(); // make sure PIN does not lock during update
|
|
#ifdef ESP8266
|
|
Update.runAsync(true);
|
|
#endif
|
|
Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
|
|
}
|
|
if(!Update.hasError()) Update.write(data, len);
|
|
if(final){
|
|
if(Update.end(true)){
|
|
DEBUG_PRINTLN(F("Update Success"));
|
|
} else {
|
|
DEBUG_PRINTLN(F("Update Failed"));
|
|
usermods.onUpdateBegin(false); // notify usermods that update has failed (some may require task init)
|
|
WLED::instance().enableWatchdog();
|
|
}
|
|
}
|
|
});
|
|
#else
|
|
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
serveMessage(request, 501, "Not implemented", F("OTA updating is disabled in this build."), 254);
|
|
});
|
|
#endif
|
|
|
|
|
|
#ifdef WLED_ENABLE_DMX
|
|
server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
request->send_P(200, "text/html", PAGE_dmxmap , dmxProcessor);
|
|
});
|
|
#else
|
|
server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
serveMessage(request, 501, "Not implemented", F("DMX support is not enabled in this build."), 254);
|
|
});
|
|
#endif
|
|
|
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (captivePortal(request)) return;
|
|
if (!showWelcomePage || request->hasArg(F("sliders"))){
|
|
serveIndex(request);
|
|
} else {
|
|
serveSettings(request);
|
|
}
|
|
});
|
|
|
|
#ifdef WLED_ENABLE_PIXART
|
|
server.on("/pixart.htm", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (handleFileRead(request, "/pixart.htm")) return;
|
|
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_pixart, PAGE_pixart_L);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
#endif
|
|
|
|
#ifndef WLED_DISABLE_PXMAGIC
|
|
server.on("/pxmagic.htm", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (handleFileRead(request, "/pxmagic.htm")) return;
|
|
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_pxmagic, PAGE_pxmagic_L);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
#endif
|
|
|
|
server.on("/cpal.htm", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
if (handleFileRead(request, "/cpal.htm")) return;
|
|
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_cpal, PAGE_cpal_L);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
|
|
#ifdef WLED_ENABLE_WEBSOCKETS
|
|
server.addHandler(&ws);
|
|
#endif
|
|
|
|
//called when the url is not defined here, ajax-in; get-settings
|
|
server.onNotFound([](AsyncWebServerRequest *request){
|
|
DEBUG_PRINTLN("Not-Found HTTP call:");
|
|
DEBUG_PRINTLN("URI: " + request->url());
|
|
if (captivePortal(request)) return;
|
|
|
|
//make API CORS compatible
|
|
if (request->method() == HTTP_OPTIONS)
|
|
{
|
|
AsyncWebServerResponse *response = request->beginResponse(200);
|
|
response->addHeader(F("Access-Control-Max-Age"), F("7200"));
|
|
request->send(response);
|
|
return;
|
|
}
|
|
|
|
if(handleSet(request, request->url())) return;
|
|
#ifndef WLED_DISABLE_ALEXA
|
|
if(espalexa.handleAlexaApiCall(request)) return;
|
|
#endif
|
|
if(handleFileRead(request, request->url())) return;
|
|
AsyncWebServerResponse *response = request->beginResponse_P(404, "text/html", PAGE_404, PAGE_404_length);
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
});
|
|
}
|
|
|
|
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request)
|
|
{
|
|
AsyncWebHeader* header = request->getHeader("If-None-Match");
|
|
if (header && header->value() == String(VERSION)) {
|
|
request->send(304);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void setStaticContentCacheHeaders(AsyncWebServerResponse *response)
|
|
{
|
|
char tmp[12];
|
|
// https://medium.com/@codebyamir/a-web-developers-guide-to-browser-caching-cc41f3b73e7c
|
|
#ifndef WLED_DEBUG
|
|
//this header name is misleading, "no-cache" will not disable cache,
|
|
//it just revalidates on every load using the "If-None-Match" header with the last ETag value
|
|
response->addHeader(F("Cache-Control"),"no-cache");
|
|
#else
|
|
response->addHeader(F("Cache-Control"),"no-store,max-age=0"); // prevent caching if debug build
|
|
#endif
|
|
sprintf_P(tmp, PSTR("%8d-%02x"), VERSION, cacheInvalidate);
|
|
response->addHeader(F("ETag"), tmp);
|
|
}
|
|
|
|
void serveIndex(AsyncWebServerRequest* request)
|
|
{
|
|
if (handleFileRead(request, "/index.htm")) return;
|
|
|
|
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
|
|
AsyncWebServerResponse *response;
|
|
#ifdef WLED_ENABLE_SIMPLE_UI
|
|
if (simplifiedUI)
|
|
response = request->beginResponse_P(200, "text/html", PAGE_simple, PAGE_simple_L);
|
|
else
|
|
#endif
|
|
response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L);
|
|
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
}
|
|
|
|
|
|
String msgProcessor(const String& var)
|
|
{
|
|
if (var == "MSG") {
|
|
String messageBody = messageHead;
|
|
messageBody += F("</h2>");
|
|
messageBody += messageSub;
|
|
uint32_t optt = optionType;
|
|
|
|
if (optt < 60) //redirect to settings after optionType seconds
|
|
{
|
|
messageBody += F("<script>setTimeout(RS,");
|
|
messageBody +=String(optt*1000);
|
|
messageBody += F(")</script>");
|
|
} else if (optt < 120) //redirect back after optionType-60 seconds, unused
|
|
{
|
|
//messageBody += "<script>setTimeout(B," + String((optt-60)*1000) + ")</script>";
|
|
} else if (optt < 180) //reload parent after optionType-120 seconds
|
|
{
|
|
messageBody += F("<script>setTimeout(RP,");
|
|
messageBody += String((optt-120)*1000);
|
|
messageBody += F(")</script>");
|
|
} else if (optt == 253)
|
|
{
|
|
messageBody += F("<br><br><form action=/settings><button class=\"bt\" type=submit>Back</button></form>"); //button to settings
|
|
} else if (optt == 254)
|
|
{
|
|
messageBody += F("<br><br><button type=\"button\" class=\"bt\" onclick=\"B()\">Back</button>");
|
|
}
|
|
return messageBody;
|
|
}
|
|
return String();
|
|
}
|
|
|
|
|
|
void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& headl, const String& subl, byte optionT)
|
|
{
|
|
messageHead = headl;
|
|
messageSub = subl;
|
|
optionType = optionT;
|
|
|
|
request->send_P(code, "text/html", PAGE_msg, msgProcessor);
|
|
}
|
|
|
|
|
|
#ifdef WLED_ENABLE_DMX
|
|
String dmxProcessor(const String& var)
|
|
{
|
|
String mapJS;
|
|
#ifdef WLED_ENABLE_DMX
|
|
if (var == "DMXVARS") {
|
|
mapJS += "\nCN=" + String(DMXChannels) + ";\n";
|
|
mapJS += "CS=" + String(DMXStart) + ";\n";
|
|
mapJS += "CG=" + String(DMXGap) + ";\n";
|
|
mapJS += "LC=" + String(strip.getLengthTotal()) + ";\n";
|
|
mapJS += "var CH=[";
|
|
for (int i=0;i<15;i++) {
|
|
mapJS += String(DMXFixtureMap[i]) + ",";
|
|
}
|
|
mapJS += "0];";
|
|
}
|
|
#endif
|
|
|
|
return mapJS;
|
|
}
|
|
#endif
|
|
|
|
|
|
void serveSettingsJS(AsyncWebServerRequest* request)
|
|
{
|
|
char buf[SETTINGS_STACK_BUF_SIZE+37];
|
|
buf[0] = 0;
|
|
byte subPage = request->arg(F("p")).toInt();
|
|
if (subPage > 10) {
|
|
strcpy_P(buf, PSTR("alert('Settings for this request are not implemented.');"));
|
|
request->send(501, "application/javascript", buf);
|
|
return;
|
|
}
|
|
if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) {
|
|
strcpy_P(buf, PSTR("alert('PIN incorrect.');"));
|
|
request->send(401, "application/javascript", buf);
|
|
return;
|
|
}
|
|
strcat_P(buf,PSTR("function GetV(){var d=document;"));
|
|
getSettingsJS(subPage, buf+strlen(buf)); // this may overflow by 35bytes!!!
|
|
strcat_P(buf,PSTR("}"));
|
|
request->send(200, "application/javascript", buf);
|
|
}
|
|
|
|
|
|
void serveSettings(AsyncWebServerRequest* request, bool post)
|
|
{
|
|
byte subPage = 0, originalSubPage = 0;
|
|
const String& url = request->url();
|
|
|
|
if (url.indexOf("sett") >= 0)
|
|
{
|
|
if (url.indexOf(".js") > 0) subPage = SUBPAGE_JS;
|
|
else if (url.indexOf(".css") > 0) subPage = SUBPAGE_CSS;
|
|
else if (url.indexOf("wifi") > 0) subPage = SUBPAGE_WIFI;
|
|
else if (url.indexOf("leds") > 0) subPage = SUBPAGE_LEDS;
|
|
else if (url.indexOf("ui") > 0) subPage = SUBPAGE_UI;
|
|
else if (url.indexOf("sync") > 0) subPage = SUBPAGE_SYNC;
|
|
else if (url.indexOf("time") > 0) subPage = SUBPAGE_TIME;
|
|
else if (url.indexOf("sec") > 0) subPage = SUBPAGE_SEC;
|
|
else if (url.indexOf("dmx") > 0) subPage = SUBPAGE_DMX;
|
|
else if (url.indexOf("um") > 0) subPage = SUBPAGE_UM;
|
|
else if (url.indexOf("2D") > 0) subPage = SUBPAGE_2D;
|
|
else if (url.indexOf("lock") > 0) subPage = SUBPAGE_LOCK;
|
|
}
|
|
else if (url.indexOf("/update") >= 0) subPage = SUBPAGE_UPDATE; // update page, for PIN check
|
|
//else if (url.indexOf("/edit") >= 0) subPage = 10;
|
|
else subPage = SUBPAGE_WELCOME;
|
|
|
|
if (!correctPIN && strlen(settingsPIN) > 0 && (subPage > 0 && subPage < 11)) {
|
|
originalSubPage = subPage;
|
|
subPage = SUBPAGE_PINREQ; // require PIN
|
|
}
|
|
|
|
// if OTA locked or too frequent PIN entry requests fail hard
|
|
if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < PIN_RETRY_COOLDOWN))
|
|
{
|
|
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254); return;
|
|
}
|
|
|
|
if (post) { //settings/set POST request, saving
|
|
if (subPage != SUBPAGE_WIFI || !(wifiLock && otaLock)) handleSettingsSet(request, subPage);
|
|
|
|
char s[32];
|
|
char s2[45] = "";
|
|
|
|
switch (subPage) {
|
|
case SUBPAGE_WIFI : strcpy_P(s, PSTR("WiFi")); strcpy_P(s2, PSTR("Please connect to the new IP (if changed)")); forceReconnect = true; break;
|
|
case SUBPAGE_LEDS : strcpy_P(s, PSTR("LED")); break;
|
|
case SUBPAGE_UI : strcpy_P(s, PSTR("UI")); break;
|
|
case SUBPAGE_SYNC : strcpy_P(s, PSTR("Sync")); break;
|
|
case SUBPAGE_TIME : strcpy_P(s, PSTR("Time")); break;
|
|
case SUBPAGE_SEC : strcpy_P(s, PSTR("Security")); if (doReboot) strcpy_P(s2, PSTR("Rebooting, please wait ~10 seconds...")); break;
|
|
case SUBPAGE_DMX : strcpy_P(s, PSTR("DMX")); break;
|
|
case SUBPAGE_UM : strcpy_P(s, PSTR("Usermods")); break;
|
|
case SUBPAGE_2D : strcpy_P(s, PSTR("2D")); break;
|
|
case SUBPAGE_PINREQ : strcpy_P(s, correctPIN ? PSTR("PIN accepted") : PSTR("PIN rejected")); break;
|
|
}
|
|
|
|
if (subPage != SUBPAGE_PINREQ) strcat_P(s, PSTR(" settings saved."));
|
|
|
|
if (subPage == SUBPAGE_PINREQ && correctPIN) {
|
|
subPage = originalSubPage; // on correct PIN load settings page the user intended
|
|
} else {
|
|
if (!s2[0]) strcpy_P(s2, s_redirecting);
|
|
|
|
bool redirectAfter9s = (subPage == SUBPAGE_WIFI || ((subPage == SUBPAGE_SEC || subPage == SUBPAGE_UM) && doReboot));
|
|
serveMessage(request, (correctPIN ? 200 : 401), s, s2, redirectAfter9s ? 129 : (correctPIN ? 1 : 3));
|
|
return;
|
|
}
|
|
}
|
|
|
|
AsyncWebServerResponse *response;
|
|
switch (subPage)
|
|
{
|
|
case SUBPAGE_WIFI : response = request->beginResponse_P(200, "text/html", PAGE_settings_wifi, PAGE_settings_wifi_length); break;
|
|
case SUBPAGE_LEDS : response = request->beginResponse_P(200, "text/html", PAGE_settings_leds, PAGE_settings_leds_length); break;
|
|
case SUBPAGE_UI : response = request->beginResponse_P(200, "text/html", PAGE_settings_ui, PAGE_settings_ui_length); break;
|
|
case SUBPAGE_SYNC : response = request->beginResponse_P(200, "text/html", PAGE_settings_sync, PAGE_settings_sync_length); break;
|
|
case SUBPAGE_TIME : response = request->beginResponse_P(200, "text/html", PAGE_settings_time, PAGE_settings_time_length); break;
|
|
case SUBPAGE_SEC : response = request->beginResponse_P(200, "text/html", PAGE_settings_sec, PAGE_settings_sec_length); break;
|
|
#ifdef WLED_ENABLE_DMX
|
|
case SUBPAGE_DMX : response = request->beginResponse_P(200, "text/html", PAGE_settings_dmx, PAGE_settings_dmx_length); break;
|
|
#endif
|
|
case SUBPAGE_UM : response = request->beginResponse_P(200, "text/html", PAGE_settings_um, PAGE_settings_um_length); break;
|
|
case SUBPAGE_UPDATE : response = request->beginResponse_P(200, "text/html", PAGE_update, PAGE_update_length); break;
|
|
#ifndef WLED_DISABLE_2D
|
|
case SUBPAGE_2D : response = request->beginResponse_P(200, "text/html", PAGE_settings_2D, PAGE_settings_2D_length); break;
|
|
#endif
|
|
case SUBPAGE_LOCK : {
|
|
correctPIN = !strlen(settingsPIN); // lock if a pin is set
|
|
createEditHandler(correctPIN);
|
|
serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1);
|
|
return;
|
|
}
|
|
case SUBPAGE_PINREQ : response = request->beginResponse_P(401, "text/html", PAGE_settings_pin, PAGE_settings_pin_length); break;
|
|
case SUBPAGE_CSS : response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length); break;
|
|
case SUBPAGE_JS : serveSettingsJS(request); return;
|
|
case SUBPAGE_WELCOME : response = request->beginResponse_P(200, "text/html", PAGE_welcome, PAGE_welcome_length); break;
|
|
default: response = request->beginResponse_P(200, "text/html", PAGE_settings, PAGE_settings_length); break;
|
|
}
|
|
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
setStaticContentCacheHeaders(response);
|
|
request->send(response);
|
|
}
|