diff --git a/lib/webdav/AsyncWebDav.h b/lib/webdav/AsyncWebDav.h index f830699..20805ad 100644 --- a/lib/webdav/AsyncWebDav.h +++ b/lib/webdav/AsyncWebDav.h @@ -1,13 +1,14 @@ #include #include -#define FLASH_TEXT(name) const char *name +#define FLASH_TEXT(name) const char *name -namespace webdav_constants { +namespace webdav_constants +{ FLASH_TEXT(MULTISTATUS_START) = ""; FLASH_TEXT(MULTISTATUS_END) = ""; FLASH_TEXT(RESPONSE_START) = ""; - FLASH_TEXT(RESPONSE_END) = ""; + FLASH_TEXT(RESPONSE_END) = "\n"; FLASH_TEXT(HREF_START) = ""; FLASH_TEXT(HREF_END) = ""; FLASH_TEXT(PROPSTAT_START) = ""; @@ -26,32 +27,148 @@ namespace webdav_constants { FLASH_TEXT(MODDATE_START) = ""; FLASH_TEXT(MODDATE_END) = ""; FLASH_TEXT(STATUS_OK) = "HTTP/1.1 200 OK"; -} +} // namespace webdav_constants -void listFiles(AsyncResponseStream * response, const char *folderPath, Dir * dir) +class WebdavFileListCallback +{ +public: + WebdavFileListCallback(const String &path) + : path_(path), headerWritten_(false), finished_(false) + { + dir_ = SPIFFS.openDir(path); + } + + size_t operator()(uint8_t *buffer, size_t maxLen, size_t index) + { + Serial.print("index "); + Serial.println(index); + + using namespace webdav_constants; + uint8_t *bufferStart = buffer; + + if (finished_) + return 0; + + if (!headerWritten_) + { + toBuffer(buffer, MULTISTATUS_START); + headerWritten_ = true; + } + + bool fileFound = false; + while (dir_.next()) + { + if (isFirstFileOfTrainingGroup()) + { + fileFound = true; + break; + } + } + + if (fileFound) + { + toBuffer(buffer, path_.c_str()); + toBuffer(buffer, RESPONSE_START); + toBuffer(buffer, HREF_START); + String fileBaseName = dir_.fileName().substring(0, dir_.fileName().indexOf('_')); + fileBaseName += ".st"; + toBuffer(buffer, fileBaseName.c_str()); + toBuffer(buffer, HREF_END); + toBuffer(buffer, PROPSTAT_START); + toBuffer(buffer, PROP_START); + if (dir_.isDirectory()) + { + toBuffer(buffer, RESOURCETYPE_START); + toBuffer(buffer, RESOURCE_COLLECTION); + toBuffer(buffer, RESOURCETYPE_END); + } + else + { + toBuffer(buffer, CONTENTLEN_START); + String fileSizeStr(getFileSize(dir_.fileName())); + toBuffer(buffer, fileSizeStr.c_str()); + toBuffer(buffer, CONTENTLEN_END); + } + + toBuffer(buffer, PROP_END); + toBuffer(buffer, STATUS_OK); + toBuffer(buffer, PROPSTAT_END); + toBuffer(buffer, webdav_constants::RESPONSE_END); + } + else + { + toBuffer(buffer, MULTISTATUS_END); + finished_ = true; + } + + size_t bytesWritten = buffer - bufferStart; + assert(bytesWritten < maxLen, "Written too much!"); + Serial.print("Bytes written "); + Serial.println(bytesWritten); + Serial.print("Max bytes "); + Serial.println(maxLen); + return bytesWritten; + } + +private: + bool isFirstFileOfTrainingGroup() + { + return !dir_.isDirectory() && dir_.fileName().endsWith("_0"); + } + + size_t getFileSize(const String &fileZero) + { + size_t size = 0; + auto fileBase = fileZero.substring(0, fileZero.indexOf('_')); + auto newDirInstance = SPIFFS.openDir(path_); + while (newDirInstance.next()) + if (newDirInstance.isFile() && newDirInstance.fileName().startsWith(fileBase)) + size += newDirInstance.fileSize(); + return size; + } + + void toBuffer(uint8_t *&buffer, const char *text) + { + auto len = strlen(text); + memcpy(buffer, text, len); + buffer += len; + } + + Dir dir_; + const String path_; + bool headerWritten_; + bool finished_; +}; + +void listFiles(AsyncResponseStream *response, const char *folderPath, Dir *dir) { using namespace webdav_constants; response->println(MULTISTATUS_START); + Serial.println("Before rewind"); dir->rewind(); Serial.println("After rewind"); - while (dir->next()) { + while (dir->next()) + { Serial.println("Inside dir loop"); Serial.println(folderPath); Serial.println(dir->fileName()); response->print(RESPONSE_START); response->print(HREF_START); - response->print(folderPath); + //response->print(folderPath); response->print(dir->fileName()); response->print(HREF_END); response->print(PROPSTAT_START); response->print(PROP_START); - if (dir->isDirectory()) { + if (dir->isDirectory()) + { response->print(RESOURCETYPE_START); response->print(RESOURCE_COLLECTION); response->print(RESOURCETYPE_END); - } else { + } + else + { response->print(CONTENTLEN_START); response->print(dir->fileSize(), DEC); response->print(CONTENTLEN_END); @@ -61,17 +178,18 @@ void listFiles(AsyncResponseStream * response, const char *folderPath, Dir * dir response->print(STATUS_OK); response->print(PROPSTAT_END); response->println(webdav_constants::RESPONSE_END); + break; } response->println(MULTISTATUS_END); } - class SpiffsWebDavHandler : public AsyncWebHandler { public: - SpiffsWebDavHandler(const String & prefix, const String & folder) + SpiffsWebDavHandler(const String &prefix, const String &folder) : prefix_(prefix), folder_(folder) - {} + { + } virtual bool canHandle(AsyncWebServerRequest *request) override final { @@ -82,13 +200,15 @@ public: virtual void handleRequest(AsyncWebServerRequest *request) override final { - if (request->url() == prefix_ + "/" && request->method() == HTTP_PROPFIND) { + if (request->url() == prefix_ + "/" && (request->method() == HTTP_GET || request->method() == HTTP_PROPFIND)) + { Serial.println("Propfind start"); - AsyncResponseStream * response = request->beginResponseStream("application/xml"); - Dir dir = SPIFFS.openDir(folder_); - listFiles(response, "/", &dir); + //AsyncResponseStream *response = request->beginResponseStream("application/xml"); + auto response = request->beginChunkedResponse("application/xml", + WebdavFileListCallback(folder_)); request->send(response); - } else if(request->url() == prefix_ + "/" && request->method() == HTTP_GET) { + } /* + else if(request->url() == prefix_ + "/" && request->method() == HTTP_GET) { AsyncResponseStream * response = request->beginResponseStream("text/plain", 1460*10); Dir dir = SPIFFS.openDir(folder_); Serial.print("Opening folder "); @@ -99,32 +219,42 @@ public: response->println(dir.fileName()); } request->send(response); - } - else if (request->method() == HTTP_GET) { + }*/ + else if (request->method() == HTTP_GET) + { auto path = folder_ + request->url().substring(prefix_.length()); - if (SPIFFS.exists(path)) { + if (SPIFFS.exists(path)) + { request->send(SPIFFS, path, "application/x-msgpack"); - } else { + } + else + { request->send(404, "text/plain", "Webdav: File not found"); } - } else if (request->method() == HTTP_DELETE) { + } + else if (request->method() == HTTP_DELETE) + { auto path = folder_ + request->url().substring(prefix_.length()); - if (SPIFFS.exists(path)) { - if(SPIFFS.remove(path)) + if (SPIFFS.exists(path)) + { + if (SPIFFS.remove(path)) request->send(204, "text/plain", "Success"); else request->send(404, "text/plain", "Webdav: Invalid delete request"); - } else { + } + else + { request->send(404, "text/plain", "Webdav: File to delete not found"); } - } else { + } + else + { request->send(404, "text/plain", "Webdav: Invalid request"); } } - virtual bool isRequestHandlerTrivial() override final {return false;} + virtual bool isRequestHandlerTrivial() override final { return false; } private: String prefix_; String folder_; }; -