Skip to content
File

Blob: src/worker/dav/readers.ts

typescript84 lines
1import { davError, parseDepth } from "@/worker/dav/http";
2import { parseIfHeader, type ParsedIfHeader } from "@/worker/dav/if-header";
3import { maxXmlBodyBytes } from "@/worker/dav/limits";
4import type { AppContext } from "@/worker/types";
5import {
6 parsePropfindXml,
7 parseProppatchXml,
8 type PropfindRequest,
9 type ProppatchInstruction,
10} from "@/worker/xml/dav-request";
11 
12export type DavReadResult<T> = { ok: true; value: T } | { ok: false; response: Response };
13 
14export interface DavConditionalHeaders {
15 ifMatch: string | null;
16 ifNoneMatch: string | null;
17 ifHeader: ParsedIfHeader;
18}
19 
20export function acceptedDavOrigins(c: AppContext): string[] {
21 return [new URL(c.req.url).origin];
22}
23 
24export function readDavConditionalHeaders(c: AppContext): DavReadResult<DavConditionalHeaders> {
25 const parsedIf = parseIfHeader(c.req.header("if") ?? null);
26 if (!parsedIf.ok) return { ok: false, response: davError(400, parsedIf.message) };
27 return {
28 ok: true,
29 value: {
30 ifMatch: c.req.header("if-match") ?? null,
31 ifNoneMatch: c.req.header("if-none-match") ?? null,
32 ifHeader: parsedIf.header,
33 },
34 };
35}
36 
37export function readReportDepth(
38 c: AppContext,
39 fallback: "0" | "1" | "infinity",
40): DavReadResult<"0" | "1" | "infinity"> {
41 const depth = parseDepth(c.req.header("depth") ?? null, fallback);
42 if (!depth) return { ok: false, response: davError(400, "REPORT Depth must be 0, 1, or infinity") };
43 return { ok: true, value: depth };
44}
45 
46export async function readCollectionPropfind(
47 c: AppContext,
48 resourceKind: string,
49): Promise<DavReadResult<{ depth: "0" | "1"; request: PropfindRequest }>> {
50 const depth = parseDepth(c.req.header("depth") ?? null, resourceKind === "object" ? "0" : "1");
51 if (!depth) return { ok: false, response: davError(400, "Invalid Depth header") };
52 
53 try {
54 return {
55 ok: true,
56 value: {
57 // CalDAV/CardDAV collections are flat in dab. Treat valid Depth infinity
58 // as the same bounded child listing as Depth 1 for client probes.
59 depth: depth === "infinity" ? "1" : depth,
60 request: parsePropfindXml(await c.req.text(), maxXmlBodyBytes(c.env)),
61 },
62 };
63 } catch (cause) {
64 return {
65 ok: false,
66 response: davError(400, cause instanceof Error ? cause.message : "Invalid PROPFIND body"),
67 };
68 }
69}
70 
71export async function readCollectionProppatch(
72 c: AppContext,
73 _resourceKind?: unknown,
74): Promise<DavReadResult<ProppatchInstruction[]>> {
75 try {
76 return { ok: true, value: parseProppatchXml(await c.req.text(), maxXmlBodyBytes(c.env)) };
77 } catch (cause) {
78 return {
79 ok: false,
80 response: davError(400, cause instanceof Error ? cause.message : "Invalid PROPPATCH body"),
81 };
82 }
83}