13#if DETWS_ENABLE_WEBDAV
17WebDavMethod webdav_method(
const char *m)
20 return WebDavMethod::DAV_M_UNSUPPORTED;
21 if (!strcmp(m,
"OPTIONS"))
22 return WebDavMethod::DAV_M_OPTIONS;
23 if (!strcmp(m,
"GET"))
24 return WebDavMethod::DAV_M_GET;
25 if (!strcmp(m,
"HEAD"))
26 return WebDavMethod::DAV_M_HEAD;
27 if (!strcmp(m,
"PUT"))
28 return WebDavMethod::DAV_M_PUT;
29 if (!strcmp(m,
"DELETE"))
30 return WebDavMethod::DAV_M_DELETE;
31 if (!strcmp(m,
"PROPFIND"))
32 return WebDavMethod::DAV_M_PROPFIND;
33 if (!strcmp(m,
"PROPPATCH"))
34 return WebDavMethod::DAV_M_PROPPATCH;
35 if (!strcmp(m,
"MKCOL"))
36 return WebDavMethod::DAV_M_MKCOL;
37 if (!strcmp(m,
"COPY"))
38 return WebDavMethod::DAV_M_COPY;
39 if (!strcmp(m,
"MOVE"))
40 return WebDavMethod::DAV_M_MOVE;
41 if (!strcmp(m,
"LOCK"))
42 return WebDavMethod::DAV_M_LOCK;
43 if (!strcmp(m,
"UNLOCK"))
44 return WebDavMethod::DAV_M_UNLOCK;
45 return WebDavMethod::DAV_M_UNSUPPORTED;
48int webdav_depth(
const char *depth_hdr,
int dflt)
50 if (!depth_hdr || !depth_hdr[0])
52 if (!strcmp(depth_hdr,
"0"))
54 if (!strcmp(depth_hdr,
"1"))
56 if (!strcmp(depth_hdr,
"infinity"))
57 return DAV_DEPTH_INFINITY;
63static bool app(
char *buf,
size_t cap,
size_t *len,
const char *s)
65 size_t n = strnlen(s, cap + 1);
66 if (*len + n + 1 > cap)
68 memcpy(buf + *len, s, n);
74size_t webdav_xml_escape(
char *dst,
size_t cap,
const char *src)
79 for (
const char *p = src; *p; p++)
81 const char *rep =
nullptr;
104 size_t rn = strnlen(rep, cap + 1);
105 if (o + rn + 1 > cap)
107 memcpy(dst + o, rep, rn);
121bool webdav_dest_path(
const char *destination,
char *out,
size_t cap)
123 if (!destination || !out || cap == 0)
128 const char *p = destination;
129 const char *scheme = strstr(destination,
"://");
133 while (*p && *p !=
'/')
153 if (hi < 0 || lo < 0)
155 c = (char)((hi << 4) | lo);
167size_t webdav_ms_begin(
char *buf,
size_t cap,
size_t len)
169 app(buf, cap, &len,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<D:multistatus xmlns:D=\"DAV:\">\n");
173size_t webdav_ms_entry(
char *buf,
size_t cap,
size_t len,
const char *href,
bool is_collection, uint32_t size,
174 const char *rfc1123_mtime,
const char *content_type)
182 webdav_xml_escape(esc,
sizeof(esc), href);
186 if (!app(tmp,
sizeof(tmp), &t,
" <D:response>\n <D:href>") || !app(tmp,
sizeof(tmp), &t, esc) ||
187 !app(tmp,
sizeof(tmp), &t,
"</D:href>\n <D:propstat>\n <D:prop>\n <D:resourcetype>"))
192 if (!app(tmp,
sizeof(tmp), &t,
"<D:collection/>"))
195 if (!app(tmp,
sizeof(tmp), &t,
"</D:resourcetype>\n"))
201 unsigned long s = (
unsigned long)size;
207 rev[rn++] = (char)(
'0' + (
int)(s % 10));
209 }
while (s && rn < (
int)
sizeof(rev));
212 num[ni++] = rev[--rn];
214 if (!app(tmp,
sizeof(tmp), &t,
" <D:getcontentlength>") || !app(tmp,
sizeof(tmp), &t, num) ||
215 !app(tmp,
sizeof(tmp), &t,
"</D:getcontentlength>\n"))
217 if (content_type && content_type[0])
219 if (!app(tmp,
sizeof(tmp), &t,
" <D:getcontenttype>") || !app(tmp,
sizeof(tmp), &t, content_type) ||
220 !app(tmp,
sizeof(tmp), &t,
"</D:getcontenttype>\n"))
225 if (rfc1123_mtime && rfc1123_mtime[0])
227 if (!app(tmp,
sizeof(tmp), &t,
" <D:getlastmodified>") || !app(tmp,
sizeof(tmp), &t, rfc1123_mtime) ||
228 !app(tmp,
sizeof(tmp), &t,
"</D:getlastmodified>\n"))
232 if (!app(tmp,
sizeof(tmp), &t,
233 " </D:prop>\n <D:status>HTTP/1.1 200 OK</D:status>\n"
234 " </D:propstat>\n </D:response>\n"))
239 app(buf, cap, &len, tmp);
243size_t webdav_ms_end(
char *buf,
size_t cap,
size_t len)
245 app(buf, cap, &len,
"</D:multistatus>\n");
250static bool name_end_char(
char c)
252 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'/' || c ==
'>';
255size_t webdav_proppatch_ms(
char *buf,
size_t cap,
const char *href,
const char *body,
size_t body_len)
261 webdav_xml_escape(esc,
sizeof(esc), href);
262 if (!app(buf, cap, &len,
263 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<D:multistatus xmlns:D=\"DAV:\">\n"
264 " <D:response>\n <D:href>") ||
265 !app(buf, cap, &len, esc) || !app(buf, cap, &len,
"</D:href>\n <D:propstat>\n <D:prop>\n"))
273 bool in_prop =
false;
282 size_t start = i + 1;
283 if (start < body_len && (body[start] ==
'?' || body[start] ==
'!'))
285 while (i < body_len && body[i] !=
'>')
290 bool closing = (start < body_len && body[start] ==
'/');
294 while (end < body_len && body[end] !=
'>')
298 bool self_closed = (end > start && body[end - 1] ==
'/');
299 size_t name_end = start;
300 while (name_end < end && !name_end_char(body[name_end]))
302 size_t local = start;
303 for (
size_t k = start; k < name_end; k++)
306 bool is_prop = (name_end - local) == 4 && !strncmp(&body[local],
"prop", 4);
327 size_t copy_end = self_closed ? end - 1 : end;
328 while (copy_end > start && (body[copy_end - 1] ==
' ' || body[copy_end - 1] ==
'\t' ||
329 body[copy_end - 1] ==
'\r' || body[copy_end - 1] ==
'\n'))
331 bool ok = copy_end > start;
332 for (
size_t k = start; k < copy_end && ok; k++)
336 size_t tl = copy_end - start;
337 if (ok && tl <
sizeof(tag))
339 memcpy(tag, &body[start], tl);
341 if (app(buf, cap, &len,
" <") && app(buf, cap, &len, tag) && app(buf, cap, &len,
"/>\n"))
348 while (j + 1 < body_len && !(body[j] ==
'<' && body[j + 1] ==
'/'))
350 while (j < body_len && body[j] !=
'>')
352 i = (j < body_len) ? j + 1 : body_len;
361 if (!app(buf, cap, &len,
362 " </D:prop>\n <D:status>HTTP/1.1 403 Forbidden</D:status>\n"
363 " </D:propstat>\n </D:response>\n</D:multistatus>\n"))
#define DETWS_WEBDAV_MAX_PROPS
Maximum properties echoed in a WebDAV PROPPATCH 207 response (bounds the response).
Tiny no-stdlib hex encode / decode / nibble helpers (one shared copy).
int det_hex_val(char c)
Hex digit -> 0..15, or -1 if c is not a hex digit (either case).
WebDAV server core (RFC 4918): method classification, header parsing, and the 207 Multi-Status XML bu...