Merge branch 'stable' into beta

This commit is contained in:
Dmitry Borisenko
2020-08-19 01:22:42 +03:00
4 changed files with 1015 additions and 2 deletions

326
src/FSEditor.cpp Normal file
View File

@@ -0,0 +1,326 @@
#include "FSEditor.h"
#ifdef ESP32
#include "LITTLEFS.h"
#define LittleFS LITTLEFS
#endif
#ifdef ESP8266
#include <LittleFS.h>
#endif
#define FS_MAXLENGTH_FILEPATH 32
const char *excludeListFile = "/.exclude.files";
typedef struct ExcludeListS {
char *item;
ExcludeListS *next;
} ExcludeList;
static ExcludeList *excludes = NULL;
static bool matchWild(const char *pattern, const char *testee) {
const char *nxPat = NULL, *nxTst = NULL;
while (*testee) {
if ((*pattern == '?') || (*pattern == *testee)) {
pattern++;
testee++;
continue;
}
if (*pattern == '*') {
nxPat = pattern++;
nxTst = testee;
continue;
}
if (nxPat) {
pattern = nxPat + 1;
testee = ++nxTst;
continue;
}
return false;
}
while (*pattern == '*') {
pattern++;
}
return (*pattern == 0);
}
static bool addExclude(const char *item) {
size_t len = strlen(item);
if (!len) {
return false;
}
ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList));
if (!e) {
return false;
}
e->item = (char *)malloc(len + 1);
if (!e->item) {
free(e);
return false;
}
memcpy(e->item, item, len + 1);
e->next = excludes;
excludes = e;
return true;
}
static void loadExcludeList(fs::FS &_fs, const char *filename) {
static char linebuf[FS_MAXLENGTH_FILEPATH];
fs::File excludeFile = _fs.open(filename, "r");
if (!excludeFile) {
return;
}
if (excludeFile.isDirectory()) {
excludeFile.close();
return;
}
if (excludeFile.size() > 0) {
uint8_t idx;
bool isOverflowed = false;
while (excludeFile.available()) {
linebuf[0] = '\0';
idx = 0;
int lastChar;
do {
lastChar = excludeFile.read();
if (lastChar != '\r') {
linebuf[idx++] = (char)lastChar;
}
} while ((lastChar >= 0) && (lastChar != '\n') && (idx < FS_MAXLENGTH_FILEPATH));
if (isOverflowed) {
isOverflowed = (lastChar != '\n');
continue;
}
isOverflowed = (idx >= FS_MAXLENGTH_FILEPATH);
linebuf[idx - 1] = '\0';
if (!addExclude(linebuf)) {
excludeFile.close();
return;
}
}
}
excludeFile.close();
}
static bool isExcluded(fs::FS &_fs, const char *filename) {
if (excludes == NULL) {
loadExcludeList(_fs, excludeListFile);
}
if (strcmp(excludeListFile, filename) == 0) return true;
ExcludeList *e = excludes;
while (e) {
if (matchWild(e->item, filename)) {
return true;
}
e = e->next;
}
return false;
}
// WEB HANDLER IMPLEMENTATION
#ifdef ESP32
FSEditor::FSEditor(const fs::FS &fs, const String &username, const String &password)
#else
FSEditor::FSEditor(const String &username, const String &password, const fs::FS &fs)
#endif
: _fs(fs), _username(username), _password(password), _authenticated(false), _startTime(0) {
}
bool FSEditor::canHandle(AsyncWebServerRequest *request) {
if (request->url().equalsIgnoreCase("/edit")) {
if (request->method() == HTTP_GET) {
if (request->hasParam("list"))
return true;
if (request->hasParam("edit")) {
request->_tempFile = _fs.open(request->arg("edit"), "r");
if (!request->_tempFile) {
return false;
}
if (request->_tempFile.isDirectory()) {
request->_tempFile.close();
return false;
}
}
if (request->hasParam("download")) {
request->_tempFile = _fs.open(request->arg("download"), "r");
if (!request->_tempFile) {
return false;
}
if (request->_tempFile.isDirectory()) {
request->_tempFile.close();
return false;
}
}
request->addInterestingHeader("If-Modified-Since");
return true;
} else if (request->method() == HTTP_POST)
return true;
else if (request->method() == HTTP_DELETE)
return true;
else if (request->method() == HTTP_PUT)
return true;
}
return false;
}
void FSEditor::handleRequest(AsyncWebServerRequest *request) {
if (_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if (request->method() == HTTP_GET) {
if (request->hasParam("list")) {
String path = request->getParam("list")->value();
#ifdef ESP32
File dir = _fs.open(path);
#else
fs::Dir dir = _fs.openDir(path);
#endif
path = String();
String output = "[";
#ifdef ESP32
File entry = dir.openNextFile();
while (entry) {
#else
while (dir.next()) {
fs::File entry = dir.openFile("r");
#endif
String fname = entry.fullName();
if (fname.charAt(0) != '/') fname = "/" + fname;
if (isExcluded(_fs, fname.c_str())) {
#ifdef ESP32
entry = dir.openNextFile();
#endif
continue;
}
if (output != "[") output += ',';
output += "{\"type\":\"";
output += "file";
output += "\",\"name\":\"";
output += String(fname);
output += "\",\"size\":";
output += String(entry.size());
output += "}";
#ifdef ESP32
entry = dir.openNextFile();
#else
entry.close();
#endif
}
#ifdef ESP32
dir.close();
#endif
output += "]";
request->send(200, "application/json", output);
output = String();
} else if (request->hasParam("edit") || request->hasParam("download")) {
request->send(request->_tempFile, request->_tempFile.fullName(), String(), request->hasParam("download"));
} else {
const char *buildTime = __DATE__ " " __TIME__ " GMT";
if (request->header("If-Modified-Since").equals(buildTime)) {
request->send(304);
} else {
AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/edit.htm", "text/html");
// response->addHeader("Content-Encoding", "gzip");
response->addHeader("Last-Modified", buildTime);
request->send(response);
}
}
} else if (request->method() == HTTP_DELETE) {
if (request->hasParam("path", true)) {
if (!(_fs.remove(request->getParam("path", true)->value()))) {
#ifdef ESP32
_fs.rmdir(request->getParam("path", true)->value()); // try rmdir for littlefs
#endif
}
request->send(200, "", "DELETE: " + request->getParam("path", true)->value());
} else
request->send(404);
} else if (request->method() == HTTP_POST) {
if (request->hasParam("data", true, true) && _fs.exists(request->getParam("data", true, true)->value()))
request->send(200, "", "UPLOADED: " + request->getParam("data", true, true)->value());
else if (request->hasParam("rawname", true) && request->hasParam("raw0", true)) {
String rawnam = request->getParam("rawname", true)->value();
if (_fs.exists(rawnam)) _fs.remove(rawnam); // delete it to allow a mode
int k = 0;
uint16_t i = 0;
fs::File f = _fs.open(rawnam, "a");
while (request->hasParam("raw" + String(k), true)) { //raw0 .. raw1
if (f) {
i += f.print(request->getParam("raw" + String(k), true)->value());
}
k++;
}
f.close();
request->send(200, "", "IPADWRITE: " + rawnam + ":" + String(i));
} else {
request->send(500);
}
} else if (request->method() == HTTP_PUT) {
if (request->hasParam("path", true)) {
String filename = request->getParam("path", true)->value();
if (_fs.exists(filename)) {
request->send(200);
} else {
/*******************************************************/
#ifdef ESP32
if (strchr(filename.c_str(), '/')) {
// For file creation, silently make subdirs as needed. If any fail,
// it will be caught by the real file open later on
char *pathStr = strdup(filename.c_str());
if (pathStr) {
// Make dirs up to the final fnamepart
char *ptr = strchr(pathStr, '/');
while (ptr) {
*ptr = 0;
_fs.mkdir(pathStr);
*ptr = '/';
ptr = strchr(ptr + 1, '/');
}
}
free(pathStr);
}
#endif
/*******************************************************/
fs::File f = _fs.open(filename, "w");
if (f) {
f.write((uint8_t)0x00);
f.close();
request->send(200, "", "CREATE: " + filename);
} else {
request->send(500);
}
}
} else
request->send(400);
}
}
void FSEditor::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!index) {
if (!_username.length() || request->authenticate(_username.c_str(), _password.c_str())) {
_authenticated = true;
request->_tempFile = _fs.open(filename, "w");
_startTime = millis();
}
}
if (_authenticated && request->_tempFile) {
if (len) {
request->_tempFile.write(data, len);
}
if (final) {
request->_tempFile.close();
}
}
}

View File

@@ -2,6 +2,7 @@
#include "Utils/FileUtils.h"
#include "Utils/WebUtils.h"
#include "FSEditor.h"
namespace HttpServer {
@@ -15,9 +16,9 @@ void init() {
String login = jsonReadStr(configSetupJson, "weblogin");
String pass = jsonReadStr(configSetupJson, "webpass");
#ifdef ESP32
server.addHandler(new SPIFFSEditor(LittleFS, login, pass));
server.addHandler(new FSEditor(LittleFS, login, pass));
#else
server.addHandler(new SPIFFSEditor(login, pass));
server.addHandler(new FSEditor(login, pass));
#endif
server.serveStatic("/css/", LittleFS, "/css/").setCacheControl("max-age=600");