From 4342269e448aa0e23b32c087d63f6b26e0c140fa Mon Sep 17 00:00:00 2001 From: Mit4el Date: Thu, 27 Apr 2023 01:29:23 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D1=83=D0=B7=D0=B5=D1=80=20/edit.=20=D0=9E=D1=81=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BD=D0=BE=D0=B5:=20=D0=BB=D0=B5=D0=B3=D1=87?= =?UTF-8?q?=D0=B5,=20=D0=BF=D0=B0=D0=BF=D0=BA=D0=B8,=20=D0=B2=D0=B8=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data_svelte/edit.htm.gz | Bin 5286 -> 6352 bytes data_svelte_lite/edit.htm.gz | Bin 5286 -> 6352 bytes include/StandWebServer.h | 9 +- src/StandWebServer.cpp | 769 ++++++++++++++++++++++++++++------- 4 files changed, 619 insertions(+), 159 deletions(-) diff --git a/data_svelte/edit.htm.gz b/data_svelte/edit.htm.gz index d41d10c1c56f308ad3cac07e98993703426b577d..06931fac0664792be15627e4bb544cefd041e4f9 100644 GIT binary patch literal 6352 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 0) + { + path = path.substring(0, path.lastIndexOf('/')); + } + else + { + path = String(); // No slash => the top folder does not exist + } + } +// DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path); + return path; } -// Здесь функции для работы с файловой системой -void handleFileUpload() { - if (HTTP.uri() != "/edit") return; - HTTPUpload& upload = HTTP.upload(); - if (upload.status == UPLOAD_FILE_START) { +/* + Handle a file upload request +*/ +void handleFileUpload() +{ + if (HTTP.uri() != "/edit") + { + return; + } + HTTPUpload &upload = HTTP.upload(); + if (upload.status == UPLOAD_FILE_START) + { String filename = upload.filename; - if (!filename.startsWith("/")) filename = "/" + filename; - fsUploadFile = FileFS.open(filename, "w"); - filename = String(); - } else if (upload.status == UPLOAD_FILE_WRITE) { - // Serial.print("handleFileUpload Data: "); Serial.println(upload.currentSize); - if (fsUploadFile) - fsUploadFile.write(upload.buf, upload.currentSize); - } else if (upload.status == UPLOAD_FILE_END) { - if (fsUploadFile) - fsUploadFile.close(); + // Make sure paths always start with "/" + if (!filename.startsWith("/")) + { + filename = "/" + filename; + } +// DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); + uploadFile = FileFS.open(filename, "w"); + if (!uploadFile) + { + return replyServerError(F("CREATE FAILED")); + } +// DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); + } + else if (upload.status == UPLOAD_FILE_WRITE) + { + if (uploadFile) + { + size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize); + if (bytesWritten != upload.currentSize) + { + return replyServerError(F("WRITE FAILED")); + } + } +// DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); + } + else if (upload.status == UPLOAD_FILE_END) + { + if (uploadFile) + { + uploadFile.close(); + } +// DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize); } } -void handleFileDelete() { - if (HTTP.args() == 0) return HTTP.send(500, "text/plain", "BAD ARGS"); + +#ifdef ESP8266 +void deleteRecursive(String path) +{ + File file = FileFS.open(path, "r"); + bool isDir = file.isDirectory(); + file.close(); + + // If it's a plain file, delete it + if (!isDir) + { + FileFS.remove(path); + return; + } + Dir dir = FileFS.openDir(path); + while (dir.next()) + { + deleteRecursive(path + '/' + dir.fileName()); + } + + // Then delete the folder itself + FileFS.rmdir(path); +} +#endif + +#ifdef ESP32 +struct treename{ + uint8_t type; + char *name; +}; + + +void deleteRecursive( String path ){ + fs::File dir = FileFS.open( path ); + + if(!dir.isDirectory()){ + Serial.printf("%s is a file\n", path); + dir.close(); + Serial.printf( "result of removing file %s: %d\n", path, FileFS.remove( path ) ); + return; + } + + Serial.printf("%s is a directory\n", path); + + fs::File entry, nextentry; + + while ( entry = dir.openNextFile() ){ + +if ( entry.isDirectory() ){ + deleteRecursive( entry.path() ); + } else{ + String tmpname = path+"/"+strdup( entry.name() ); // buffer file name + entry.close(); + Serial.printf( "result of removing file %s: %d\n", tmpname, FileFS.remove( tmpname ) ); + } + + } + + dir.close(); + Serial.printf( "result of removing directory %s: %d\n", path, FileFS.rmdir( path ) ); + +} +#endif +/* + Handle a file deletion request + Operation | req.responseText + ---------------+-------------------------------------------------------------- + Delete file | parent of deleted file, or remaining ancestor + Delete folder | parent of deleted folder, or remaining ancestor +*/ +void handleFileDelete() +{ String path = HTTP.arg(0); - if (path == "/") - return HTTP.send(500, "text/plain", "BAD PATH"); + if (path.isEmpty() || path == "/") + { + return replyBadRequest("BAD PATH"); + } + +// DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); if (!FileFS.exists(path)) - return HTTP.send(404, "text/plain", "FileNotFound"); - FileFS.remove(path); - HTTP.send(200, "text/plain", ""); - path = String(); + { + return replyNotFound(FPSTR(FILE_NOT_FOUND)); + } + deleteRecursive(path); + + + + replyOKWithMsg(lastExistingParent(path)); } -void handleFileCreate() { - if (HTTP.args() == 0) - return HTTP.send(500, "text/plain", "BAD ARGS"); - String path = HTTP.arg(0); +/* + Handle the creation/rename of a new file + Operation | req.responseText + ---------------+-------------------------------------------------------------- + Create file | parent of created file + Create folder | parent of created folder + Rename file | parent of source file + Move file | parent of source file, or remaining ancestor + Rename folder | parent of source folder + Move folder | parent of source folder, or remaining ancestor +*/ +void handleFileCreate() +{ + String path = HTTP.arg("path"); + if (path.isEmpty()) + { + return replyBadRequest(F("PATH ARG MISSING")); + } + +#ifdef USE_SPIFFS + if (checkForUnsupportedPath(path).length() > 0) + { + return replyServerError(F("INVALID FILENAME")); + } +#endif + if (path == "/") - return HTTP.send(500, "text/plain", "BAD PATH"); + { + return replyBadRequest("BAD PATH"); + } if (FileFS.exists(path)) - return HTTP.send(500, "text/plain", "FILE EXISTS"); - File file = FileFS.open(path, "w"); - if (file) - file.close(); + { + return replyBadRequest(F("PATH FILE EXISTS")); + } + + String src = HTTP.arg("src"); + if (src.isEmpty()) + { + // No source specified: creation +// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); + if (path.endsWith("/")) + { + // Create a folder + path.remove(path.length() - 1); + if (!FileFS.mkdir(path)) + { + return replyServerError(F("MKDIR FAILED")); + } + } + else + { + // Create a file + File file = FileFS.open(path, "w"); + if (file) + { +#ifdef ESP8266 + file.write((const char *)0); +#endif +#ifdef ESP32 + file.write(0); +#endif + file.close(); + } + else + { + return replyServerError(F("CREATE FAILED")); + } + } + if (path.lastIndexOf('/') > -1) + { + path = path.substring(0, path.lastIndexOf('/')); + } + replyOKWithMsg(path); + } else - return HTTP.send(500, "text/plain", "CREATE FAILED"); - HTTP.send(200, "text/plain", ""); - path = String(); + { + // Source specified: rename + if (src == "/") + { + return replyBadRequest("BAD SRC"); + } + if (!FileFS.exists(src)) + { + return replyBadRequest(F("SRC FILE NOT FOUND")); + } + +// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); + + if (path.endsWith("/")) + { + path.remove(path.length() - 1); + } + if (src.endsWith("/")) + { + src.remove(src.length() - 1); + } + if (!FileFS.rename(src, path)) + { + return replyServerError(F("RENAME FAILED")); + } + replyOKWithMsg(lastExistingParent(src)); + } } -void handleFileList() { - File dir = FileFS.open("/", "r"); - String output = "["; - File entry; - while (entry = dir.openNextFile()) { - if (output != "[") output += ','; - bool isDir = entry.isDirectory(); +/* + Return the list of files in the directory specified by the "dir" query string parameter. + Also demonstrates the use of chunked responses. +*/ +#ifdef ESP8266 +void handleFileList() +{ + if (!HTTP.hasArg("dir")) + { + return replyBadRequest(F("DIR ARG MISSING")); + } + + String path = HTTP.arg("dir"); + if (path != "/" && !FileFS.exists(path)) + { + return replyBadRequest("BAD PATH"); + } + +// DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); + Dir dir = FileFS.openDir(path); + path.clear(); + + // use HTTP/1.1 Chunked response to avoid building a huge temporary string + if (!HTTP.chunkedResponseModeStart(200, "text/json")) + { + HTTP.send(505, F("text/html"), F("HTTP1.1 required")); + return; + } + + // use the same string for every line + String output; + output.reserve(64); + while (dir.next()) + { +#ifdef USE_SPIFFS + String error = checkForUnsupportedPath(dir.fileName()); + if (error.length() > 0) + { +// DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); + continue; + } +#endif + if (output.length()) + { + // send string from previous iteration + // as an HTTP chunk + HTTP.sendContent(output); + output = ','; + } + else + { + output = '['; + } + output += "{\"type\":\""; - output += (isDir) ? "dir" : "file"; - output += "\",\"name\":\""; - output += String(entry.name()); + if (dir.isDirectory()) + { + output += "dir"; + } + else + { + output += F("file\",\"size\":\""); + output += dir.fileSize(); + } + + output += F("\",\"name\":\""); + // Always return names without leading "/" + if (dir.fileName()[0] == '/') + { + output += &(dir.fileName()[1]); + } + else + { + output += dir.fileName(); + } + output += "\"}"; - entry.close(); } + + // send last string output += "]"; - //Serial.println(output); - HTTP.send(200, "text/json", output); + HTTP.sendContent(output); + HTTP.chunkedResponseFinalize(); +} +#endif + +#ifdef ESP32 +void handleFileList() { + if (!HTTP.hasArg("dir")) { + HTTP.send(500, "text/plain", "BAD ARGS"); + return; + } + + String path = HTTP.arg("dir"); +// DBG_OUTPUT_PORT.println("handleFileList: " + path); + + + File root = FileFS.open(path); + path = String(); + + String output = "["; + if(root.isDirectory()){ + File file = root.openNextFile(); + while(file){ + if (output != "[") { + output += ','; + } + output += "{\"type\":\""; + // output += (file.isDirectory()) ? "dir" : "file"; + if (file.isDirectory()) + { + output += "dir"; + } + else + { + output += F("file\",\"size\":\""); + output += file.size(); + } + + output += "\",\"name\":\""; + output += String(file.name()); + output += "\"}"; + file = root.openNextFile(); + } + } + output += "]"; + HTTP.send(200, "text/json", output); + +} +#endif + + +/* + The "Not Found" handler catches all URI not explicitly declared in code + First try to find and return the requested file from the filesystem, + and if it fails, return a 404 page with debug information +*/ +void handleNotFound() +{ +#ifdef ESP8266 + String uri = ESP8266WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks +#endif +#ifdef ESP32 + String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks +#endif + if (handleFileRead(uri)) + { + return; + } + + // Dump debug data + String message; + message.reserve(100); + message = F("Error: File not found\n\nURI: "); + message += uri; + message += F("\nMethod: "); + message += (HTTP.method() == HTTP_GET) ? "GET" : "POST"; + message += F("\nArguments: "); + message += HTTP.args(); + message += '\n'; + for (uint8_t i = 0; i < HTTP.args(); i++) + { + message += F(" NAME:"); + message += HTTP.argName(i); + message += F("\n VALUE:"); + message += HTTP.arg(i); + message += '\n'; + } + message += "path="; + message += HTTP.arg("path"); + message += '\n'; +// DBG_OUTPUT_PORT.print(message); + + return replyNotFound(message); } -void printDirectory(File dir, String& out) { - while (true) { - File entry = dir.openNextFile(); - if (!entry) { - break; - } - if (entry.isDirectory()) { - out += entry.name(); - out += "/"; - printDirectory(entry, out); - } else { - out += entry.name(); - out += "\r\n"; - } +/* + This specific handler returns the index.htm (or a gzipped version) from the /edit folder. + If the file is not present but the flag INCLUDE_FALLBACK_INDEX_HTM has been set, falls back to the version + embedded in the program code. + Otherwise, fails with a 404 page with debug information +*/ +void handleGetEdit() +{ + if (handleFileRead(F("/edit.htm"))) + { + return; } + +#ifdef INCLUDE_FALLBACK_INDEX_HTM + server.sendHeader(F("Content-Encoding"), "gzip"); + server.send(200, "text/html", index_htm_gz, index_htm_gz_len); +#else + replyNotFound(FPSTR(FILE_NOT_FOUND)); +#endif } #endif