swimtracker-firmware/firmware/lib/esphttp/WebDAV.cpp

140 lines
4.8 KiB
C++
Raw Normal View History

#include "FilesystemAbstraction.h"
#include "WebDAV.h"
namespace webdav_constants
{
constexpr const char *MULTISTATUS_START = "<?xml version=\"1.0\" ?><D:multistatus xmlns:D=\"DAV:\">";
constexpr const char *MULTISTATUS_END = "</D:multistatus>";
constexpr const char *RESPONSE_START = "<D:response>";
constexpr const char *RESPONSE_END = "</D:response>\n";
constexpr const char *HREF_START = "<D:href>";
constexpr const char *HREF_END = "</D:href>";
constexpr const char *PROPSTAT_START = "<D:propstat>";
constexpr const char *PROPSTAT_END = "</D:propstat>";
constexpr const char *PROP_START = "<D:prop>";
constexpr const char *PROP_END = "</D:prop>";
constexpr const char *RESOURCETYPE_START = "<D:resourcetype>";
constexpr const char *RESOURCETYPE_END = "</D:resourcetype>";
constexpr const char *RESOURCE_COLLECTION = "<D:collection/>";
constexpr const char *HTTP_204_NO_CONTENT = "HTTP/1.1 204 No Content";
constexpr const char *CONTENTLEN_START = "<D:getcontentlength>";
constexpr const char *CONTENTLEN_END = "</D:getcontentlength>";
constexpr const char *CREATEDATE_START = "<D:creationdate>";
constexpr const char *CREATEDATE_END = "</D:creationdate>";
constexpr const char *MODDATE_START = "<D:getlastmodified>";
constexpr const char *MODDATE_END = "</D:getlastmodified>";
constexpr const char *STATUS_OK = "<D:status>HTTP/1.1 200 OK</D:status>";
constexpr const char *USED_BYTES_START = "<D:quota-used-bytes>";
constexpr const char *USED_BYTES_END = "</D:quota-used-bytes>";
} // namespace webdav_constants
size_t webdavFileListingSpiffs(char *buffer, size_t maxLength,
const char *spiffsPath)
{
using namespace webdav_constants;
size_t bytesWritten = 0;
auto toBuffer = [&buffer, &bytesWritten](const char *text) {
auto len = strlen(text);
memcpy(buffer, text, len);
buffer += len;
bytesWritten += len;
};
// crude upper bound on how much space a file entry + footer needs
constexpr size_t sizePerFile = 512;
assert(maxLength >= sizePerFile);
int fileIdx = 0;
auto dir = portablefs::openDir(spiffsPath);
toBuffer(MULTISTATUS_START);
bool incomplete = false;
while (dir.next())
{
const auto freeSpace = maxLength - bytesWritten;
if (freeSpace < sizePerFile)
{
incomplete = true;
break;
}
fileIdx += 1;
toBuffer(RESPONSE_START);
toBuffer(HREF_START);
const auto fileName = dir.fileName();
const auto fileNameWithoutDir = fileName.substring(fileName.lastIndexOf("/") + 1);
toBuffer((fileNameWithoutDir + ".st").c_str());
toBuffer(HREF_END);
toBuffer(PROPSTAT_START);
toBuffer(PROP_START);
if (dir.isDirectory())
{
toBuffer(RESOURCETYPE_START);
toBuffer(RESOURCE_COLLECTION);
toBuffer(RESOURCETYPE_END);
}
else
{
toBuffer(CONTENTLEN_START);
String fileSizeStr(dir.fileSize());
toBuffer(fileSizeStr.c_str());
toBuffer(CONTENTLEN_END);
}
toBuffer(PROP_END);
toBuffer(STATUS_OK);
toBuffer(PROPSTAT_END);
toBuffer(webdav_constants::RESPONSE_END);
}
toBuffer(MULTISTATUS_END);
if (incomplete)
Serial.println("WebDAV listing response is incomplete, because buffer was too small");
return bytesWritten;
}
String uriToFileName(const String &uriStr, const char *spiffsFolder)
{
String filename;
if (uriStr.endsWith(".st"))
filename = uriStr.substring(0, uriStr.length() - strlen(".st"));
filename = spiffsFolder + String("/") + filename;
return filename;
}
std::function<void(httpd_req_t *)> webdavHandler(const char *uriPrefix,
const char *spiffsFolder)
{
return [=](httpd_req_t *req) {
String uri = String(req->uri).substring(strlen(uriPrefix));
constexpr size_t WEBDAV_BUFF_LEN = 1024 * 256;
static char *webdavBuffer = (char *)heap_caps_malloc(WEBDAV_BUFF_LEN, MALLOC_CAP_SPIRAM);
switch (req->method)
{
case HTTP_GET:
case HTTP_PROPFIND:
{
size_t bytesWritten = webdavFileListingSpiffs(webdavBuffer, WEBDAV_BUFF_LEN, spiffsFolder);
httpd_resp_send(req, webdavBuffer, bytesWritten);
break;
}
case HTTP_DELETE:
{
httpd_resp_set_hdr(req, "Content-Type", "text/plain");
if (portablefs::remove(uriToFileName(uri, spiffsFolder).c_str()))
httpd_resp_send(req, "Deleted file", -1);
else
httpd_resp_send_404(req);
break;
}
default:
httpd_resp_send_404(req);
}
};
}