17WebDavMethod pc_webdav_method(
const char *m)
21 return WebDavMethod::DAV_M_UNSUPPORTED;
23 if (!strcmp(m,
"OPTIONS"))
25 return WebDavMethod::DAV_M_OPTIONS;
27 if (!strcmp(m,
"GET"))
29 return WebDavMethod::DAV_M_GET;
31 if (!strcmp(m,
"HEAD"))
33 return WebDavMethod::DAV_M_HEAD;
35 if (!strcmp(m,
"PUT"))
37 return WebDavMethod::DAV_M_PUT;
39 if (!strcmp(m,
"DELETE"))
41 return WebDavMethod::DAV_M_DELETE;
43 if (!strcmp(m,
"PROPFIND"))
45 return WebDavMethod::DAV_M_PROPFIND;
47 if (!strcmp(m,
"PROPPATCH"))
49 return WebDavMethod::DAV_M_PROPPATCH;
51 if (!strcmp(m,
"MKCOL"))
53 return WebDavMethod::DAV_M_MKCOL;
55 if (!strcmp(m,
"COPY"))
57 return WebDavMethod::DAV_M_COPY;
59 if (!strcmp(m,
"MOVE"))
61 return WebDavMethod::DAV_M_MOVE;
63 if (!strcmp(m,
"LOCK"))
65 return WebDavMethod::DAV_M_LOCK;
67 if (!strcmp(m,
"UNLOCK"))
69 return WebDavMethod::DAV_M_UNLOCK;
71 return WebDavMethod::DAV_M_UNSUPPORTED;
74int pc_webdav_depth(
const char *depth_hdr,
int dflt)
76 if (!depth_hdr || !depth_hdr[0])
80 if (!strcmp(depth_hdr,
"0"))
84 if (!strcmp(depth_hdr,
"1"))
88 if (!strcmp(depth_hdr,
"infinity"))
90 return PC_DAV_DEPTH_INFINITY;
97static bool app(
char *buf,
size_t cap,
size_t *len,
const char *s)
99 size_t n = strnlen(s, cap + 1);
100 if (*len + n + 1 > cap)
104 memcpy(buf + *len, s, n);
110size_t pc_webdav_xml_escape(
char *dst,
size_t cap,
const char *src)
117 for (
const char *p = src; *p; p++)
119 const char *rep =
nullptr;
142 size_t rn = strnlen(rep, cap + 1);
143 if (o + rn + 1 > cap)
147 memcpy(dst + o, rep, rn);
163bool pc_webdav_dest_path(
const char *destination,
char *out,
size_t cap)
165 if (!destination || !out || cap == 0)
172 const char *p = destination;
173 const char *scheme = strstr(destination,
"://");
177 while (*p && *p !=
'/')
201 if (hi < 0 || lo < 0)
205 c = (char)((hi << 4) | lo);
219size_t pc_webdav_ms_begin(
char *buf,
size_t cap,
size_t len)
221 app(buf, cap, &len,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<D:multistatus xmlns:D=\"DAV:\">\n");
225size_t pc_webdav_ms_entry(
char *buf,
size_t cap,
size_t len,
const char *href,
bool is_collection, uint32_t size,
226 const char *rfc1123_mtime,
const char *content_type)
234 pc_webdav_xml_escape(esc,
sizeof(esc), href);
244 if (!app(tmp,
sizeof(tmp), &t,
" <D:response>\n <D:href>") || !app(tmp,
sizeof(tmp), &t, esc) ||
245 !app(tmp,
sizeof(tmp), &t,
"</D:href>\n <D:propstat>\n <D:prop>\n <D:resourcetype>"))
251 if (is_collection && !app(tmp,
sizeof(tmp), &t,
"<D:collection/>"))
255 if (!app(tmp,
sizeof(tmp), &t,
"</D:resourcetype>\n"))
263 unsigned long s = (
unsigned long)size;
269 rev[rn++] = (char)(
'0' + (
int)(s % 10));
271 }
while (s && rn < (
int)
sizeof(rev));
276 num[ni++] = rev[--rn];
284 if (!app(tmp,
sizeof(tmp), &t,
" <D:getcontentlength>") ||
285 !app(tmp,
sizeof(tmp), &t, num) ||
286 !app(tmp,
sizeof(tmp), &t,
"</D:getcontentlength>\n"))
294 if (content_type && content_type[0] &&
295 (!app(tmp,
sizeof(tmp), &t,
" <D:getcontenttype>") || !app(tmp,
sizeof(tmp), &t, content_type) ||
296 !app(tmp,
sizeof(tmp), &t,
"</D:getcontenttype>\n")))
303 if (rfc1123_mtime && rfc1123_mtime[0])
305 if (!app(tmp,
sizeof(tmp), &t,
" <D:getlastmodified>") || !app(tmp,
sizeof(tmp), &t, rfc1123_mtime) ||
306 !app(tmp,
sizeof(tmp), &t,
"</D:getlastmodified>\n"))
312 if (!app(tmp,
sizeof(tmp), &t,
313 " </D:prop>\n <D:status>HTTP/1.1 200 OK</D:status>\n"
314 " </D:propstat>\n </D:response>\n"))
321 app(buf, cap, &len, tmp);
325size_t pc_webdav_ms_end(
char *buf,
size_t cap,
size_t len)
327 app(buf, cap, &len,
"</D:multistatus>\n");
332static bool name_end_char(
char c)
337 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'/' || c ==
'>';
340size_t pc_webdav_proppatch_ms(
char *buf,
size_t cap,
const char *href,
const char *body,
size_t body_len)
348 pc_webdav_xml_escape(esc,
sizeof(esc), href);
349 if (!app(buf, cap, &len,
350 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<D:multistatus xmlns:D=\"DAV:\">\n"
351 " <D:response>\n <D:href>") ||
352 !app(buf, cap, &len, esc) || !app(buf, cap, &len,
"</D:href>\n <D:propstat>\n <D:prop>\n"))
362 bool in_prop =
false;
371 size_t start = i + 1;
372 if (start < body_len && (body[start] ==
'?' || body[start] ==
'!'))
374 while (i < body_len && body[i] !=
'>')
381 bool closing = (start < body_len && body[start] ==
'/');
387 while (end < body_len && body[end] !=
'>')
395 bool self_closed = (end > start && body[end - 1] ==
'/');
396 size_t name_end = start;
397 while (name_end < end && !name_end_char(body[name_end]))
401 size_t local = start;
402 for (
size_t k = start; k < name_end; k++)
409 bool is_prop = (name_end - local) == 4 && !strncmp(&body[local],
"prop", 4);
434 size_t copy_end = self_closed ? end - 1 : end;
435 while (copy_end > start && (body[copy_end - 1] ==
' ' || body[copy_end - 1] ==
'\t' ||
436 body[copy_end - 1] ==
'\r' || body[copy_end - 1] ==
'\n'))
440 bool ok = copy_end > start;
441 for (
size_t k = start; k < copy_end && ok; k++)
449 size_t tl = copy_end - start;
450 if (ok && tl <
sizeof(tag))
452 memcpy(tag, &body[start], tl);
454 if (app(buf, cap, &len,
" <") && app(buf, cap, &len, tag) && app(buf, cap, &len,
"/>\n"))
463 while (j + 1 < body_len && !(body[j] ==
'<' && body[j + 1] ==
'/'))
467 while (j < body_len && body[j] !=
'>')
471 i = (j < body_len) ? j + 1 : body_len;
482 if (!app(buf, cap, &len,
483 " </D:prop>\n <D:status>HTTP/1.1 403 Forbidden</D:status>\n"
484 " </D:propstat>\n </D:response>\n</D:multistatus>\n"))
494static bool dav_lock_copy(
char *dst,
size_t cap,
const char *src)
496 size_t n = strnlen(src, cap);
501 memcpy(dst, src, n + 1);
506static bool dav_lock_norm(
char *dst,
size_t cap,
const char *path)
508 size_t n = strnlen(path, cap);
509 while (n > 1 && path[n - 1] ==
'/')
517 memcpy(dst, path, n);
523static bool dav_lock_same_or_under(
const char *parent,
const char *child)
525 size_t pn = strnlen(parent, PC_DAV_LOCK_PATH_MAX);
526 if (strncmp(parent, child, pn) != 0)
534 if (pn == 1 && parent[0] ==
'/')
538 return child[pn] ==
'/';
542static bool dav_lock_overlap(
const char *pa,
bool ia,
const char *pb,
bool ib)
544 if (strcmp(pa, pb) == 0)
548 if (ia && dav_lock_same_or_under(pa, pb))
552 if (ib && dav_lock_same_or_under(pb, pa))
560static bool dav_lock_covers(
const DavLock *l,
const char *np)
562 if (strcmp(l->path, np) == 0)
566 return l->depth_infinity && dav_lock_same_or_under(l->path, np);
569void pc_dav_lock_init(DavLockTable *t)
575 for (
size_t i = 0; i < PC_DAV_LOCK_MAX; i++)
577 t->locks[i].active =
false;
581const DavLock *pc_dav_lock_acquire(DavLockTable *t,
const char *path,
const char *token,
bool exclusive,
582 bool depth_infinity, uint32_t expiry_s)
584 if (!t || !path || !token)
588 char np[PC_DAV_LOCK_PATH_MAX];
589 if (!dav_lock_norm(np,
sizeof(np), path))
593 if (strnlen(token, PC_DAV_LOCK_TOKEN_MAX) + 1 > PC_DAV_LOCK_TOKEN_MAX)
600 for (
size_t i = 0; i < PC_DAV_LOCK_MAX; i++)
602 const DavLock *l = &t->locks[i];
603 if (l->active && dav_lock_overlap(l->path, l->depth_infinity, np, depth_infinity) &&
604 (exclusive || l->exclusive))
610 for (
size_t i = 0; i < PC_DAV_LOCK_MAX; i++)
612 DavLock *l = &t->locks[i];
617 (void)dav_lock_copy(l->path,
sizeof(l->path), np);
618 (void)dav_lock_copy(l->token,
sizeof(l->token), token);
619 l->exclusive = exclusive;
620 l->depth_infinity = depth_infinity;
621 l->expiry_s = expiry_s;
628size_t pc_dav_lock_sweep(DavLockTable *t, uint32_t now_s)
635 for (
size_t i = 0; i < PC_DAV_LOCK_MAX; i++)
637 DavLock *l = &t->locks[i];
638 if (l->active && l->expiry_s != 0 && l->expiry_s <= now_s)
647const DavLock *pc_dav_lock_refresh(DavLockTable *t,
const char *token, uint32_t new_expiry_s)
653 for (
size_t i = 0; i < PC_DAV_LOCK_MAX; i++)
655 DavLock *l = &t->locks[i];
656 if (l->active && strcmp(l->token, token) == 0)
658 l->expiry_s = new_expiry_s;
665const DavLock *pc_dav_lock_find(
const DavLockTable *t,
const char *path)
671 char np[PC_DAV_LOCK_PATH_MAX];
672 if (!dav_lock_norm(np,
sizeof(np), path))
676 for (
size_t i = 0; i < PC_DAV_LOCK_MAX; i++)
678 if (t->locks[i].active && dav_lock_covers(&t->locks[i], np))
686bool pc_dav_lock_release(DavLockTable *t,
const char *token)
692 for (
size_t i = 0; i < PC_DAV_LOCK_MAX; i++)
694 if (t->locks[i].active && strcmp(t->locks[i].token, token) == 0)
696 t->locks[i].active =
false;
703bool pc_dav_lock_can_write(
const DavLockTable *t,
const char *path,
const char *presented_token)
713 char np[PC_DAV_LOCK_PATH_MAX];
714 if (!dav_lock_norm(np,
sizeof(np), path))
718 bool covered =
false;
719 for (
size_t i = 0; i < PC_DAV_LOCK_MAX; i++)
721 const DavLock *l = &t->locks[i];
722 if (!l->active || !dav_lock_covers(l, np))
727 if (presented_token && strcmp(l->token, presented_token) == 0)
735bool pc_dav_if_token(
const char *if_header,
char *out,
size_t cap)
737 if (!if_header || !out || cap == 0)
743 const char *lp = strchr(if_header,
'(');
748 const char *lt = strchr(lp,
'<');
753 const char *gt = strchr(lt,
'>');
758 size_t n = (size_t)(gt - lt - 1);
763 memcpy(out, lt + 1, n);
Base-16 conversion between raw bytes and their ASCII digits.
int8_t pc_hex_val(char c)
Hex character c as 0..15, or -1 when c is not a hex digit of either case.
#define PC_WEBDAV_MAX_PROPS
Maximum properties echoed in a WebDAV PROPPATCH 207 response (bounds the response).
WebDAV server core (RFC 4918): method classification, header parsing, and the 207 Multi-Status XML bu...