DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
webdav.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file webdav.h
6 * @brief WebDAV server core (RFC 4918): method classification, header parsing,
7 * and the 207 Multi-Status XML builder.
8 *
9 * Mirrors the CoAP/SNMP split: this header declares the pure, host-testable core
10 * (no sockets, no filesystem - unit-tested in env:native_webdav). The
11 * filesystem-backed request handling (PROPFIND directory walk, PUT/MKCOL/DELETE/
12 * COPY/MOVE, GET via the file-serving path) lives in DetWebServer::serve_dav_*
13 * and runs only on a build with a real Arduino FS.
14 *
15 * Scope: class 1 (PROPFIND Depth 0/1, PROPPATCH, PUT, DELETE, MKCOL, COPY, MOVE)
16 * plus OPTIONS and advisory LOCK/UNLOCK (a synthetic token is issued but not
17 * enforced). PROPPATCH is answered 207 with every requested property refused 403
18 * (read-only live properties, no dead-property store). The filesystem-backed
19 * handler streams a PUT body straight to the file (DetWebServer's stream-body
20 * hook), so uploads are not bounded by BODY_BUF_SIZE.
21 */
22
23#ifndef DETERMINISTICESPASYNCWEBSERVER_WEBDAV_H
24#define DETERMINISTICESPASYNCWEBSERVER_WEBDAV_H
25
26#include "ServerConfig.h"
27#include <stddef.h>
28#include <stdint.h>
29
30#if DETWS_ENABLE_WEBDAV
31
32/** @brief WebDAV request methods recognized by the server. */
33enum class WebDavMethod : uint8_t
34{
35 DAV_M_OPTIONS,
36 DAV_M_GET,
37 DAV_M_HEAD,
38 DAV_M_PUT,
39 DAV_M_DELETE,
40 DAV_M_PROPFIND,
41 DAV_M_PROPPATCH,
42 DAV_M_MKCOL,
43 DAV_M_COPY,
44 DAV_M_MOVE,
45 DAV_M_LOCK,
46 DAV_M_UNLOCK,
47 DAV_M_UNSUPPORTED ///< Anything else - answered 405 Method Not Allowed.
48};
49
50/** @brief "infinity" Depth value (RFC 4918 §10.2). */
51/** @brief Depth: infinity sentinel (a lone constant). */
52static constexpr int32_t DAV_DEPTH_INFINITY = 0x7fffffff;
53
54/** @brief Classify an HTTP method token (e.g. "PROPFIND") into a WebDavMethod. */
55WebDavMethod webdav_method(const char *m);
56
57/**
58 * @brief Parse a Depth header value ("0", "1", or "infinity").
59 * @return 0, 1, or DAV_DEPTH_INFINITY; @p dflt when @p depth_hdr is null/empty.
60 */
61int webdav_depth(const char *depth_hdr, int dflt);
62
63/**
64 * @brief XML-escape @p src into @p dst (`&`, `<`, `>`, `"`, `'`).
65 * @return length written (NUL-terminated; truncated to fit @p cap).
66 */
67size_t webdav_xml_escape(char *dst, size_t cap, const char *src);
68
69/**
70 * @brief Extract and percent-decode the path of a Destination header.
71 *
72 * Accepts an absolute URI ("http://host/p/q") or an absolute path ("/p/q"); the
73 * scheme + authority are skipped and `%xx` escapes are decoded into @p out.
74 * @return false on overflow or a malformed value.
75 */
76bool webdav_dest_path(const char *destination, char *out, size_t cap);
77
78// 207 Multi-Status incremental builder. Each call appends to a buffer already
79// holding @p len bytes and returns the new length, never exceeding @p cap. A
80// return value equal to @p len means the fragment did not fit (the caller should
81// stop adding entries and close the document).
82
83/** @brief Write the XML prolog and the open <multistatus> element. */
84size_t webdav_ms_begin(char *buf, size_t cap, size_t len);
85
86/**
87 * @brief Append one <response> describing a resource.
88 *
89 * @param href the resource's URL path (XML-escaped here).
90 * @param is_collection true for a directory (emits <collection/>).
91 * @param size content length (files only).
92 * @param rfc1123_mtime Last-Modified string, or "" to omit.
93 * @param content_type MIME type (files only), or "" to omit.
94 */
95size_t webdav_ms_entry(char *buf, size_t cap, size_t len, const char *href, bool is_collection, uint32_t size,
96 const char *rfc1123_mtime, const char *content_type);
97
98/** @brief Close the <multistatus> element. */
99size_t webdav_ms_end(char *buf, size_t cap, size_t len);
100
101/**
102 * @brief Build a complete 207 Multi-Status body answering a PROPPATCH.
103 *
104 * The server has no dead-property store and its live properties are read-only, so
105 * every requested property is refused with 403 Forbidden. The property elements
106 * are echoed verbatim (self-closed) from the request @p body so the client sees
107 * exactly which properties it asked for, namespace declarations intact; a property
108 * whose tag would contain a stray '<' is skipped (no XML injection). Up to
109 * DETWS_WEBDAV_MAX_PROPS properties are echoed.
110 *
111 * @param buf destination buffer (whole document, NUL-terminated).
112 * @param cap buffer capacity.
113 * @param href the resource path (XML-escaped here).
114 * @param body the PROPPATCH request body (not required to be NUL-terminated).
115 * @param body_len length of @p body.
116 * @return bytes written, or 0 if the document did not fit @p cap.
117 */
118size_t webdav_proppatch_ms(char *buf, size_t cap, const char *href, const char *body, size_t body_len);
119
120#endif // DETWS_ENABLE_WEBDAV
121
122#endif // DETERMINISTICESPASYNCWEBSERVER_WEBDAV_H
User-facing configuration for DeterministicESPAsyncWebServer.