@sockethub/platform-caldav 1.0.0-beta.3 → 1.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -10
- package/dist/dav.d.ts +6 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.js +296 -63
- package/dist/index.js.map +6 -6
- package/dist/schema.d.ts +612 -0
- package/dist/types.d.ts +9 -0
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -66075,9 +66075,10 @@ function xmlText(value) {
|
|
|
66075
66075
|
return predefined[entity.toLowerCase()] ?? reference;
|
|
66076
66076
|
});
|
|
66077
66077
|
}
|
|
66078
|
-
function parseDavXml(xml) {
|
|
66078
|
+
function parseDavXml(xml, options = {}) {
|
|
66079
66079
|
return new XMLParser({
|
|
66080
66080
|
ignoreAttributes: false,
|
|
66081
|
+
parseTagValue: options.parseTagValue ?? true,
|
|
66081
66082
|
removeNSPrefix: true,
|
|
66082
66083
|
processEntities: false,
|
|
66083
66084
|
tagValueProcessor: (_tagName, value) => xmlText(value),
|
|
@@ -66106,6 +66107,39 @@ function propertyHref(value) {
|
|
|
66106
66107
|
return davHref(value.href);
|
|
66107
66108
|
return davHref(value);
|
|
66108
66109
|
}
|
|
66110
|
+
function safePathname(pathname) {
|
|
66111
|
+
if (pathname.includes("\\") || /%(?:2f|5c)/i.test(pathname))
|
|
66112
|
+
return false;
|
|
66113
|
+
for (const segment of pathname.split("/")) {
|
|
66114
|
+
let decoded = segment;
|
|
66115
|
+
try {
|
|
66116
|
+
for (let pass = 0;pass < 3; pass += 1) {
|
|
66117
|
+
const next = decodeURIComponent(decoded);
|
|
66118
|
+
if (next === decoded)
|
|
66119
|
+
break;
|
|
66120
|
+
decoded = next;
|
|
66121
|
+
}
|
|
66122
|
+
} catch {
|
|
66123
|
+
return false;
|
|
66124
|
+
}
|
|
66125
|
+
if (decoded === "." || decoded === ".." || /[\\/]/.test(decoded))
|
|
66126
|
+
return false;
|
|
66127
|
+
}
|
|
66128
|
+
return true;
|
|
66129
|
+
}
|
|
66130
|
+
function isDavCollectionChild(collectionId, resourceId) {
|
|
66131
|
+
try {
|
|
66132
|
+
const collection = new URL(collectionId);
|
|
66133
|
+
const resource = new URL(resourceId);
|
|
66134
|
+
if (collection.search || collection.hash || resource.search || resource.hash || !safePathname(collection.pathname) || !safePathname(resource.pathname))
|
|
66135
|
+
return false;
|
|
66136
|
+
const prefix = collection.pathname.endsWith("/") ? collection.pathname : `${collection.pathname}/`;
|
|
66137
|
+
return resource.origin === collection.origin && resource.pathname.startsWith(prefix) && resource.pathname !== prefix;
|
|
66138
|
+
} catch {
|
|
66139
|
+
return false;
|
|
66140
|
+
}
|
|
66141
|
+
}
|
|
66142
|
+
|
|
66109
66143
|
class DiscoveryCache {
|
|
66110
66144
|
maxEntries;
|
|
66111
66145
|
secret = randomBytes(32);
|
|
@@ -66223,6 +66257,16 @@ function digestChallenge(header) {
|
|
|
66223
66257
|
return;
|
|
66224
66258
|
}
|
|
66225
66259
|
var quote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', "\\\"")}"`;
|
|
66260
|
+
var isStatus = (value, code) => new RegExp(`\\s${code}(?:\\s|$)`).test(String(value ?? ""));
|
|
66261
|
+
function davPreconditions(xml) {
|
|
66262
|
+
try {
|
|
66263
|
+
const error = parseDavXml(xml).error;
|
|
66264
|
+
return error && typeof error === "object" ? Object.keys(error) : [];
|
|
66265
|
+
} catch {
|
|
66266
|
+
return [];
|
|
66267
|
+
}
|
|
66268
|
+
}
|
|
66269
|
+
var xmlEscape = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
66226
66270
|
|
|
66227
66271
|
class DavClient {
|
|
66228
66272
|
authentication;
|
|
@@ -66267,9 +66311,10 @@ class DavClient {
|
|
|
66267
66311
|
this.assertAllowed(url);
|
|
66268
66312
|
let response;
|
|
66269
66313
|
try {
|
|
66314
|
+
const { passForbidden: _, ...fetchInit } = init;
|
|
66270
66315
|
const authorization = this.authorization(url, init);
|
|
66271
66316
|
response = await this.fetchImpl(url, {
|
|
66272
|
-
...
|
|
66317
|
+
...fetchInit,
|
|
66273
66318
|
redirect: "manual",
|
|
66274
66319
|
signal,
|
|
66275
66320
|
headers: {
|
|
@@ -66313,7 +66358,7 @@ class DavClient {
|
|
|
66313
66358
|
}
|
|
66314
66359
|
throw new DavFailure(`${this.errorPrefix}:${authAttempts > 0 ? "authentication-failed" : "unsupported-authentication"}`);
|
|
66315
66360
|
}
|
|
66316
|
-
if (response.status === 401 || response.status === 403) {
|
|
66361
|
+
if (response.status === 401 || response.status === 403 && !init.passForbidden) {
|
|
66317
66362
|
await response.body?.cancel().catch(() => {});
|
|
66318
66363
|
throw new DavFailure(`${this.errorPrefix}:authentication-failed`);
|
|
66319
66364
|
}
|
|
@@ -66334,6 +66379,146 @@ class DavClient {
|
|
|
66334
66379
|
await response.body?.cancel().catch(() => {});
|
|
66335
66380
|
throw new DavFailure(`${this.errorPrefix}:invalid-response`);
|
|
66336
66381
|
}
|
|
66382
|
+
return this.readMultistatus(response);
|
|
66383
|
+
}
|
|
66384
|
+
async syncCollection(collection, prop, syncToken, namespaces = "") {
|
|
66385
|
+
const response = await this.request(collection, {
|
|
66386
|
+
method: "REPORT",
|
|
66387
|
+
passForbidden: true,
|
|
66388
|
+
headers: {
|
|
66389
|
+
depth: "0",
|
|
66390
|
+
"content-type": "application/xml; charset=utf-8"
|
|
66391
|
+
},
|
|
66392
|
+
body: `<?xml version="1.0" encoding="utf-8"?>
|
|
66393
|
+
<d:sync-collection xmlns:d="DAV:"${namespaces ? ` ${namespaces}` : ""}>
|
|
66394
|
+
${syncToken ? `<d:sync-token>${xmlEscape(syncToken)}</d:sync-token>` : "<d:sync-token/>"}
|
|
66395
|
+
<d:sync-level>1</d:sync-level>
|
|
66396
|
+
<d:prop>${prop}</d:prop>
|
|
66397
|
+
</d:sync-collection>`
|
|
66398
|
+
});
|
|
66399
|
+
if (response.status !== 207) {
|
|
66400
|
+
let body = "";
|
|
66401
|
+
try {
|
|
66402
|
+
body = await response.text();
|
|
66403
|
+
} catch {}
|
|
66404
|
+
const preconditions = davPreconditions(body);
|
|
66405
|
+
const prefix = this.errorPrefix;
|
|
66406
|
+
if (preconditions.includes("valid-sync-token"))
|
|
66407
|
+
throw new DavFailure(`${prefix}:sync-token-invalid`);
|
|
66408
|
+
if (preconditions.includes("supported-report"))
|
|
66409
|
+
throw new DavFailure(`${prefix}:sync-unsupported`);
|
|
66410
|
+
if (response.status === 404)
|
|
66411
|
+
throw new DavFailure(`${prefix}:not-found`);
|
|
66412
|
+
if ([400, 409, 412].includes(response.status) && syncToken)
|
|
66413
|
+
throw new DavFailure(`${prefix}:sync-token-invalid`);
|
|
66414
|
+
if (response.status === 403)
|
|
66415
|
+
throw new DavFailure(`${prefix}:authentication-failed`);
|
|
66416
|
+
if ([400, 405, 415, 501].includes(response.status))
|
|
66417
|
+
throw new DavFailure(`${prefix}:sync-unsupported`);
|
|
66418
|
+
throw new DavFailure(`${prefix}:sync-failed`);
|
|
66419
|
+
}
|
|
66420
|
+
let multistatus;
|
|
66421
|
+
try {
|
|
66422
|
+
const parsed = parseDavXml(await response.text(), {
|
|
66423
|
+
parseTagValue: false
|
|
66424
|
+
}).multistatus;
|
|
66425
|
+
if (!parsed || typeof parsed !== "object")
|
|
66426
|
+
throw new Error;
|
|
66427
|
+
multistatus = parsed;
|
|
66428
|
+
} catch {
|
|
66429
|
+
throw new DavFailure(`${this.errorPrefix}:invalid-response`);
|
|
66430
|
+
}
|
|
66431
|
+
const token = multistatus["sync-token"];
|
|
66432
|
+
if (typeof token !== "string" || token.trim() === "")
|
|
66433
|
+
throw new DavFailure(`${this.errorPrefix}:invalid-response`);
|
|
66434
|
+
const result = {
|
|
66435
|
+
changed: [],
|
|
66436
|
+
removed: [],
|
|
66437
|
+
syncToken: token.trim(),
|
|
66438
|
+
truncated: false
|
|
66439
|
+
};
|
|
66440
|
+
for (const item of asArray(multistatus.response)) {
|
|
66441
|
+
const href = davHref(item.href);
|
|
66442
|
+
if (!href)
|
|
66443
|
+
throw new DavFailure(`${this.errorPrefix}:invalid-response`);
|
|
66444
|
+
const id = new URL(href, collection).href;
|
|
66445
|
+
const status = item.status;
|
|
66446
|
+
if (id === collection.href || `${id}/` === collection.href) {
|
|
66447
|
+
if (isStatus(status, 507))
|
|
66448
|
+
result.truncated = true;
|
|
66449
|
+
continue;
|
|
66450
|
+
}
|
|
66451
|
+
if (!isDavCollectionChild(collection.href, id))
|
|
66452
|
+
throw new DavFailure(`${this.errorPrefix}:invalid-response`);
|
|
66453
|
+
if (isStatus(status, 404))
|
|
66454
|
+
result.removed.push(id);
|
|
66455
|
+
else
|
|
66456
|
+
result.changed.push({ id, props: successfulProps(item) });
|
|
66457
|
+
}
|
|
66458
|
+
return result;
|
|
66459
|
+
}
|
|
66460
|
+
async syncResources(collection, options, syncToken) {
|
|
66461
|
+
const property = options.data.slice(options.data.indexOf(":") + 1);
|
|
66462
|
+
const sync = await this.syncCollection(collection, `<d:getetag/><${options.data}/>`, syncToken, options.namespaces);
|
|
66463
|
+
const resources = [];
|
|
66464
|
+
const missing = new Map;
|
|
66465
|
+
const add = (id, value, etag) => {
|
|
66466
|
+
const data = value && typeof value === "object" ? value["#text"] : value;
|
|
66467
|
+
if (typeof data !== "string")
|
|
66468
|
+
return false;
|
|
66469
|
+
resources.push({
|
|
66470
|
+
id,
|
|
66471
|
+
data,
|
|
66472
|
+
...typeof etag === "string" ? { etag } : {}
|
|
66473
|
+
});
|
|
66474
|
+
return true;
|
|
66475
|
+
};
|
|
66476
|
+
for (const member of sync.changed)
|
|
66477
|
+
if (!add(member.id, member.props[property], member.props.getetag))
|
|
66478
|
+
missing.set(member.id, member.props.getetag);
|
|
66479
|
+
if (missing.size) {
|
|
66480
|
+
const hrefs = [...missing.keys()].map((id) => `<d:href>${xmlEscape(new URL(id).pathname)}</d:href>`).join("");
|
|
66481
|
+
const response = await this.request(collection, {
|
|
66482
|
+
method: "REPORT",
|
|
66483
|
+
headers: {
|
|
66484
|
+
depth: "1",
|
|
66485
|
+
"content-type": "application/xml; charset=utf-8"
|
|
66486
|
+
},
|
|
66487
|
+
body: `<?xml version="1.0" encoding="utf-8"?>
|
|
66488
|
+
<${options.multiget} xmlns:d="DAV:" ${options.namespaces}>
|
|
66489
|
+
<d:prop><d:getetag/><${options.data}/></d:prop>
|
|
66490
|
+
${hrefs}
|
|
66491
|
+
</${options.multiget}>`
|
|
66492
|
+
});
|
|
66493
|
+
if (response.status !== 207)
|
|
66494
|
+
return rejectDavResponse(response, `${this.errorPrefix}:sync-failed`);
|
|
66495
|
+
for (const item of await this.readMultistatus(response)) {
|
|
66496
|
+
const href = davHref(item.href);
|
|
66497
|
+
if (!href)
|
|
66498
|
+
continue;
|
|
66499
|
+
const id = new URL(href, collection).href;
|
|
66500
|
+
if (!missing.has(id))
|
|
66501
|
+
continue;
|
|
66502
|
+
if (isStatus(item.status, 404)) {
|
|
66503
|
+
sync.removed.push(id);
|
|
66504
|
+
missing.delete(id);
|
|
66505
|
+
continue;
|
|
66506
|
+
}
|
|
66507
|
+
const props = successfulProps(item);
|
|
66508
|
+
if (add(id, props[property], props.getetag ?? missing.get(id)))
|
|
66509
|
+
missing.delete(id);
|
|
66510
|
+
}
|
|
66511
|
+
if (missing.size)
|
|
66512
|
+
throw new DavFailure(`${this.errorPrefix}:invalid-response`);
|
|
66513
|
+
}
|
|
66514
|
+
return {
|
|
66515
|
+
resources,
|
|
66516
|
+
removed: sync.removed,
|
|
66517
|
+
syncToken: sync.syncToken,
|
|
66518
|
+
truncated: sync.truncated
|
|
66519
|
+
};
|
|
66520
|
+
}
|
|
66521
|
+
async readMultistatus(response) {
|
|
66337
66522
|
try {
|
|
66338
66523
|
const parsed = parseDavXml(await response.text());
|
|
66339
66524
|
return asArray(parsed.multistatus?.response);
|
|
@@ -66634,7 +66819,7 @@ var ActivityStreamSchema = {
|
|
|
66634
66819
|
var package_default = {
|
|
66635
66820
|
name: "@sockethub/schemas",
|
|
66636
66821
|
description: "JSON schema files for validating Sockethub Activity Streams",
|
|
66637
|
-
version: "3.0.0-beta.
|
|
66822
|
+
version: "3.0.0-beta.5",
|
|
66638
66823
|
type: "module",
|
|
66639
66824
|
private: false,
|
|
66640
66825
|
author: "Nick Jennings <nick@silverbucket.net>",
|
|
@@ -66704,7 +66889,7 @@ var package_default = {
|
|
|
66704
66889
|
"ajv-formats": "^2.1.1",
|
|
66705
66890
|
"ajv-formats-draft2019": "^1.6.1"
|
|
66706
66891
|
},
|
|
66707
|
-
gitHead: "
|
|
66892
|
+
gitHead: "cc4ffdd9714ebbd8372a88c4f2d795974e04029e",
|
|
66708
66893
|
types: "./dist/index.d.ts"
|
|
66709
66894
|
};
|
|
66710
66895
|
|
|
@@ -68124,7 +68309,15 @@ var queryBody = (query) => `<?xml version="1.0" encoding="utf-8"?>
|
|
|
68124
68309
|
<d:prop><d:getetag/><c:calendar-data/></d:prop>
|
|
68125
68310
|
<c:filter><c:comp-filter name="VCALENDAR"><c:comp-filter name="${query.type === "task" ? "VTODO" : "VEVENT"}">${query.startTime || query.endTime ? `<c:time-range${query.startTime ? ` start="${calDavTime(query.startTime)}"` : ""}${query.endTime ? ` end="${calDavTime(query.endTime)}"` : ""}/>` : ""}</c:comp-filter></c:comp-filter></c:filter>
|
|
68126
68311
|
</c:calendar-query>`;
|
|
68312
|
+
var HAS_EVENT_OR_TASK = /^BEGIN:(?:VEVENT|VTODO)\r?$/m;
|
|
68127
68313
|
var CalDavFailure = DavFailure;
|
|
68314
|
+
function calendarItem(data, id, etag) {
|
|
68315
|
+
try {
|
|
68316
|
+
return parseICalendar(data, id, typeof etag === "string" ? etag : undefined);
|
|
68317
|
+
} catch {
|
|
68318
|
+
throw new CalDavFailure("caldav:invalid-response");
|
|
68319
|
+
}
|
|
68320
|
+
}
|
|
68128
68321
|
|
|
68129
68322
|
class CalDavClient extends DavClient {
|
|
68130
68323
|
constructor(url, authentication, timeoutMs = 15000, networkOptions = {}) {
|
|
@@ -68232,28 +68425,28 @@ class CalDavClient extends DavClient {
|
|
|
68232
68425
|
await response.body?.cancel().catch(() => {});
|
|
68233
68426
|
throw new CalDavFailure("caldav:query-failed");
|
|
68234
68427
|
}
|
|
68235
|
-
|
|
68236
|
-
try {
|
|
68237
|
-
parsed = parseDavXml(await response.text());
|
|
68238
|
-
} catch {
|
|
68239
|
-
throw new CalDavFailure("caldav:invalid-response");
|
|
68240
|
-
}
|
|
68241
|
-
const responses = asArray(parsed.multistatus?.response);
|
|
68242
|
-
for (const item of responses) {
|
|
68428
|
+
for (const item of await this.readMultistatus(response)) {
|
|
68243
68429
|
const props = successfulProps(item);
|
|
68244
68430
|
const itemHref = davHref(item.href);
|
|
68245
68431
|
const data = props["calendar-data"];
|
|
68246
68432
|
if (!itemHref || typeof data !== "string")
|
|
68247
68433
|
continue;
|
|
68248
|
-
|
|
68249
|
-
items.push(parseICalendar(data, new URL(itemHref, calendar.id).href, typeof props.getetag === "string" ? props.getetag : undefined));
|
|
68250
|
-
} catch {
|
|
68251
|
-
throw new CalDavFailure("caldav:invalid-response");
|
|
68252
|
-
}
|
|
68434
|
+
items.push(calendarItem(data, new URL(itemHref, calendar.id).href, props.getetag));
|
|
68253
68435
|
}
|
|
68254
68436
|
}
|
|
68255
68437
|
return items;
|
|
68256
68438
|
}
|
|
68439
|
+
async sync(calendar, syncToken) {
|
|
68440
|
+
const { resources, ...result } = await this.syncResources(new URL(calendar.id), {
|
|
68441
|
+
namespaces: 'xmlns:c="urn:ietf:params:xml:ns:caldav"',
|
|
68442
|
+
data: "c:calendar-data",
|
|
68443
|
+
multiget: "c:calendar-multiget"
|
|
68444
|
+
}, syncToken);
|
|
68445
|
+
return {
|
|
68446
|
+
...result,
|
|
68447
|
+
items: resources.filter((resource) => HAS_EVENT_OR_TASK.test(resource.data)).map((resource) => calendarItem(resource.data, resource.id, resource.etag))
|
|
68448
|
+
};
|
|
68449
|
+
}
|
|
68257
68450
|
async update(id, etag, body) {
|
|
68258
68451
|
const resource = new URL(id);
|
|
68259
68452
|
this.assertAllowed(resource);
|
|
@@ -68489,7 +68682,7 @@ END:VCALENDAR\r
|
|
|
68489
68682
|
var package_default2 = {
|
|
68490
68683
|
name: "@sockethub/platform-caldav",
|
|
68491
68684
|
description: "A Sockethub platform for synchronizing CalDAV calendars, events, and tasks",
|
|
68492
|
-
version: "1.0.0-beta.
|
|
68685
|
+
version: "1.0.0-beta.5",
|
|
68493
68686
|
type: "module",
|
|
68494
68687
|
private: false,
|
|
68495
68688
|
license: "LGPL-3.0+",
|
|
@@ -68523,16 +68716,16 @@ var package_default2 = {
|
|
|
68523
68716
|
test: "bun test src/"
|
|
68524
68717
|
},
|
|
68525
68718
|
dependencies: {
|
|
68526
|
-
"@sockethub/dav": "^1.0.0-beta.
|
|
68527
|
-
"@sockethub/schemas": "^3.0.0-beta.
|
|
68528
|
-
"@sockethub/util": "^1.0.0-beta.
|
|
68719
|
+
"@sockethub/dav": "^1.0.0-beta.5",
|
|
68720
|
+
"@sockethub/schemas": "^3.0.0-beta.5",
|
|
68721
|
+
"@sockethub/util": "^1.0.0-beta.5",
|
|
68529
68722
|
undici: "^7.30.0"
|
|
68530
68723
|
},
|
|
68531
68724
|
devDependencies: {
|
|
68532
68725
|
"@types/bun": "latest"
|
|
68533
68726
|
},
|
|
68534
68727
|
peerDependencies: {
|
|
68535
|
-
"@sockethub/server": "^5.0.0-beta.
|
|
68728
|
+
"@sockethub/server": "^5.0.0-beta.5"
|
|
68536
68729
|
},
|
|
68537
68730
|
peerDependenciesMeta: {
|
|
68538
68731
|
"@sockethub/server": {
|
|
@@ -68540,7 +68733,7 @@ var package_default2 = {
|
|
|
68540
68733
|
}
|
|
68541
68734
|
},
|
|
68542
68735
|
types: "./dist/index.d.ts",
|
|
68543
|
-
gitHead: "
|
|
68736
|
+
gitHead: "cc4ffdd9714ebbd8372a88c4f2d795974e04029e"
|
|
68544
68737
|
};
|
|
68545
68738
|
|
|
68546
68739
|
// src/schema.ts
|
|
@@ -68737,7 +68930,13 @@ var queryObject = {
|
|
|
68737
68930
|
endTime: { type: "string", format: "date-time" }
|
|
68738
68931
|
}
|
|
68739
68932
|
};
|
|
68740
|
-
var
|
|
68933
|
+
var syncToken = { type: "string", minLength: 1, maxLength: 4096 };
|
|
68934
|
+
var syncObject = {
|
|
68935
|
+
type: "object",
|
|
68936
|
+
additionalProperties: false,
|
|
68937
|
+
properties: { syncToken }
|
|
68938
|
+
};
|
|
68939
|
+
var calendarItem2 = { oneOf: [eventObject, taskObject] };
|
|
68741
68940
|
var mutationObject = {
|
|
68742
68941
|
type: "object",
|
|
68743
68942
|
required: ["id", "type"],
|
|
@@ -68760,12 +68959,26 @@ var PlatformCalDavSchema = {
|
|
|
68760
68959
|
properties: {
|
|
68761
68960
|
type: {
|
|
68762
68961
|
type: "string",
|
|
68763
|
-
enum: [
|
|
68962
|
+
enum: [
|
|
68963
|
+
"fetch",
|
|
68964
|
+
"read",
|
|
68965
|
+
"query",
|
|
68966
|
+
"sync",
|
|
68967
|
+
"create",
|
|
68968
|
+
"update",
|
|
68969
|
+
"delete"
|
|
68970
|
+
]
|
|
68764
68971
|
},
|
|
68765
68972
|
actor,
|
|
68766
68973
|
target: { oneOf: [target, feedTarget] },
|
|
68767
68974
|
object: {
|
|
68768
|
-
oneOf: [
|
|
68975
|
+
oneOf: [
|
|
68976
|
+
eventObject,
|
|
68977
|
+
taskObject,
|
|
68978
|
+
deleteObject,
|
|
68979
|
+
queryObject,
|
|
68980
|
+
{ ...syncObject, required: ["syncToken"] }
|
|
68981
|
+
]
|
|
68769
68982
|
}
|
|
68770
68983
|
}
|
|
68771
68984
|
},
|
|
@@ -68794,11 +69007,19 @@ var PlatformCalDavSchema = {
|
|
|
68794
69007
|
},
|
|
68795
69008
|
required: ["type", "actor", "target"]
|
|
68796
69009
|
},
|
|
69010
|
+
{
|
|
69011
|
+
properties: {
|
|
69012
|
+
type: { const: "sync" },
|
|
69013
|
+
target,
|
|
69014
|
+
object: syncObject
|
|
69015
|
+
},
|
|
69016
|
+
required: ["type", "actor", "target"]
|
|
69017
|
+
},
|
|
68797
69018
|
{
|
|
68798
69019
|
properties: {
|
|
68799
69020
|
type: { const: "create" },
|
|
68800
69021
|
target,
|
|
68801
|
-
object:
|
|
69022
|
+
object: calendarItem2
|
|
68802
69023
|
},
|
|
68803
69024
|
required: ["type", "actor", "target", "object"]
|
|
68804
69025
|
},
|
|
@@ -68909,7 +69130,7 @@ var PlatformCalDavSchema = {
|
|
|
68909
69130
|
type: "array",
|
|
68910
69131
|
items: {
|
|
68911
69132
|
oneOf: [
|
|
68912
|
-
|
|
69133
|
+
calendarItem2,
|
|
68913
69134
|
{
|
|
68914
69135
|
type: "object",
|
|
68915
69136
|
required: [
|
|
@@ -68939,6 +69160,30 @@ var PlatformCalDavSchema = {
|
|
|
68939
69160
|
}
|
|
68940
69161
|
}
|
|
68941
69162
|
},
|
|
69163
|
+
{
|
|
69164
|
+
type: "object",
|
|
69165
|
+
required: [
|
|
69166
|
+
"@context",
|
|
69167
|
+
"type",
|
|
69168
|
+
"totalItems",
|
|
69169
|
+
"items",
|
|
69170
|
+
"removed",
|
|
69171
|
+
"syncToken",
|
|
69172
|
+
"truncated"
|
|
69173
|
+
],
|
|
69174
|
+
additionalProperties: false,
|
|
69175
|
+
properties: {
|
|
69176
|
+
"@context": { type: "array", items: { type: "string" } },
|
|
69177
|
+
id: { type: ["string", "null"] },
|
|
69178
|
+
type: { const: "collection" },
|
|
69179
|
+
summary: { type: "string" },
|
|
69180
|
+
totalItems: { type: "integer", minimum: 0 },
|
|
69181
|
+
items: { type: "array", items: calendarItem2 },
|
|
69182
|
+
removed: { type: "array", items: shared.id },
|
|
69183
|
+
syncToken,
|
|
69184
|
+
truncated: { type: "boolean" }
|
|
69185
|
+
}
|
|
69186
|
+
},
|
|
68942
69187
|
{
|
|
68943
69188
|
type: "object",
|
|
68944
69189
|
required: ["@context", "type", "actor", "target", "object"],
|
|
@@ -68960,39 +69205,8 @@ var PlatformCalDavSchema = {
|
|
|
68960
69205
|
var CONTEXT = buildCanonicalContext(PlatformCalDavSchema.contextUrl);
|
|
68961
69206
|
var MAX_FEED_BYTES = 10 * 1024 * 1024;
|
|
68962
69207
|
var MAX_FEED_REDIRECTS = 5;
|
|
68963
|
-
function safePathname(pathname) {
|
|
68964
|
-
if (pathname.includes("\\") || /%(?:2f|5c)/i.test(pathname))
|
|
68965
|
-
return false;
|
|
68966
|
-
for (const segment of pathname.split("/")) {
|
|
68967
|
-
let decoded = segment;
|
|
68968
|
-
try {
|
|
68969
|
-
for (let pass = 0;pass < 3; pass += 1) {
|
|
68970
|
-
const next = decodeURIComponent(decoded);
|
|
68971
|
-
if (next === decoded)
|
|
68972
|
-
break;
|
|
68973
|
-
decoded = next;
|
|
68974
|
-
}
|
|
68975
|
-
} catch {
|
|
68976
|
-
return false;
|
|
68977
|
-
}
|
|
68978
|
-
if (decoded === "." || decoded === ".." || /[\\/]/.test(decoded))
|
|
68979
|
-
return false;
|
|
68980
|
-
}
|
|
68981
|
-
return true;
|
|
68982
|
-
}
|
|
68983
69208
|
function assertCalendarResource(calendarId, resourceId) {
|
|
68984
|
-
|
|
68985
|
-
let resource;
|
|
68986
|
-
try {
|
|
68987
|
-
calendar = new URL(calendarId);
|
|
68988
|
-
resource = new URL(resourceId);
|
|
68989
|
-
} catch {
|
|
68990
|
-
throw new CalDavFailure("caldav:invalid-resource");
|
|
68991
|
-
}
|
|
68992
|
-
if (calendar.search || calendar.hash || resource.search || resource.hash || !safePathname(calendar.pathname) || !safePathname(resource.pathname))
|
|
68993
|
-
throw new CalDavFailure("caldav:invalid-resource");
|
|
68994
|
-
const prefix = calendar.pathname.endsWith("/") ? calendar.pathname : `${calendar.pathname}/`;
|
|
68995
|
-
if (resource.origin !== calendar.origin || !resource.pathname.startsWith(prefix) || resource.pathname === prefix)
|
|
69209
|
+
if (!isDavCollectionChild(calendarId, resourceId))
|
|
68996
69210
|
throw new CalDavFailure("caldav:invalid-resource");
|
|
68997
69211
|
}
|
|
68998
69212
|
|
|
@@ -69003,7 +69217,14 @@ class CalDav {
|
|
|
69003
69217
|
discovery = new DiscoveryCache;
|
|
69004
69218
|
config = {
|
|
69005
69219
|
persist: false,
|
|
69006
|
-
requireCredentials: [
|
|
69220
|
+
requireCredentials: [
|
|
69221
|
+
"fetch",
|
|
69222
|
+
"query",
|
|
69223
|
+
"sync",
|
|
69224
|
+
"create",
|
|
69225
|
+
"update",
|
|
69226
|
+
"delete"
|
|
69227
|
+
],
|
|
69007
69228
|
connectTimeoutMs: 15000,
|
|
69008
69229
|
allowPrivateAddresses: false,
|
|
69009
69230
|
allowInsecureHttp: false,
|
|
@@ -69093,6 +69314,18 @@ class CalDav {
|
|
|
69093
69314
|
items
|
|
69094
69315
|
})).catch((error) => this.fail(job, error, done, credentials)).finally(() => client.close().catch(() => {}));
|
|
69095
69316
|
}
|
|
69317
|
+
sync(job, credentials, done) {
|
|
69318
|
+
const client = this.client(credentials);
|
|
69319
|
+
const input = job.object ?? {};
|
|
69320
|
+
this.calendar(client, credentials, job.target?.id ?? "").then((calendar) => client.sync(calendar, input.syncToken)).then((result) => done(null, {
|
|
69321
|
+
"@context": CONTEXT,
|
|
69322
|
+
...job.id ? { id: job.id } : {},
|
|
69323
|
+
type: "collection",
|
|
69324
|
+
summary: "CalDAV changes",
|
|
69325
|
+
totalItems: result.items.length,
|
|
69326
|
+
...result
|
|
69327
|
+
})).catch((error) => this.fail(job, error, done, credentials)).finally(() => client.close().catch(() => {}));
|
|
69328
|
+
}
|
|
69096
69329
|
update(job, credentials, done) {
|
|
69097
69330
|
const client = this.client(credentials);
|
|
69098
69331
|
const input = job.object;
|
|
@@ -69228,4 +69461,4 @@ export {
|
|
|
69228
69461
|
foldLine
|
|
69229
69462
|
};
|
|
69230
69463
|
|
|
69231
|
-
//# debugId=
|
|
69464
|
+
//# debugId=16184EE35BE0342464756E2164756E21
|