#include #include #define FLASH_TEXT(name) const char *name namespace webdav_constants { FLASH_TEXT(MULTISTATUS_START) = ""; FLASH_TEXT(MULTISTATUS_END) = ""; FLASH_TEXT(RESPONSE_START) = ""; FLASH_TEXT(RESPONSE_END) = "\n"; FLASH_TEXT(HREF_START) = ""; FLASH_TEXT(HREF_END) = ""; FLASH_TEXT(PROPSTAT_START) = ""; FLASH_TEXT(PROPSTAT_END) = ""; FLASH_TEXT(PROP_START) = ""; FLASH_TEXT(PROP_END) = ""; FLASH_TEXT(RESOURCETYPE_START) = ""; FLASH_TEXT(RESOURCETYPE_END) = ""; FLASH_TEXT(RESOURCE_COLLECTION) = ""; FLASH_TEXT(HTTP_204_NO_CONTENT) = "HTTP/1.1 204 No Content"; FLASH_TEXT(CONTENTLEN_START) = ""; FLASH_TEXT(CONTENTLEN_END) = ""; FLASH_TEXT(CREATEDATE_START) = ""; FLASH_TEXT(CREATEDATE_END) = ""; FLASH_TEXT(MODDATE_START) = ""; FLASH_TEXT(MODDATE_END) = ""; FLASH_TEXT(STATUS_OK) = "HTTP/1.1 200 OK"; } // namespace webdav_constants 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()) { 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(dir->fileName()); response->print(HREF_END); response->print(PROPSTAT_START); response->print(PROP_START); if (dir->isDirectory()) { response->print(RESOURCETYPE_START); response->print(RESOURCE_COLLECTION); response->print(RESOURCETYPE_END); } else { response->print(CONTENTLEN_START); response->print(dir->fileSize(), DEC); response->print(CONTENTLEN_END); } response->print(PROP_END); 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) : prefix_(prefix), folder_(folder) { } virtual bool canHandle(AsyncWebServerRequest *request) override final { Serial.print("Can handle for url : "); Serial.println(request->url()); return request->url().startsWith(prefix_); } virtual void handleRequest(AsyncWebServerRequest *request) override final { if (request->url() == prefix_ + "/" && (request->method() == HTTP_GET || request->method() == HTTP_PROPFIND)) { Serial.println("Propfind start"); //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) { AsyncResponseStream * response = request->beginResponseStream("text/plain", 1460*10); Dir dir = SPIFFS.openDir(folder_); Serial.print("Opening folder "); Serial.println(folder_); while (dir.next()) { Serial.print(" File: "); Serial.println(dir.fileName()); response->println(dir.fileName()); } request->send(response); }*/ else if (request->method() == HTTP_GET) { auto path = folder_ + request->url().substring(prefix_.length()); if (SPIFFS.exists(path)) { request->send(SPIFFS, path, "application/x-msgpack"); } else { request->send(404, "text/plain", "Webdav: File not found"); } } else if (request->method() == HTTP_DELETE) { auto path = folder_ + request->url().substring(prefix_.length()); 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 { request->send(404, "text/plain", "Webdav: File to delete not found"); } } else { request->send(404, "text/plain", "Webdav: Invalid request"); } } virtual bool isRequestHandlerTrivial() override final { return false; } private: String prefix_; String folder_; };