caldav-adapter 8.0.3 → 8.1.0
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/common/parse-body.js +6 -4
- package/common/tags.js +45 -31
- package/index.js +2 -5
- package/package.json +1 -1
- package/routes/calendar/calendar/proppatch.js +62 -1
- package/routes/calendar/calendar/put.js +6 -0
- package/routes/calendar/calendar.js +2 -2
- package/routes/principal/mkcalendar.js +12 -0
package/common/parse-body.js
CHANGED
|
@@ -9,16 +9,18 @@ module.exports = async function (ctx) {
|
|
|
9
9
|
});
|
|
10
10
|
} catch (err) {
|
|
11
11
|
// <https://github.com/stream-utils/raw-body/blob/f62e660e7c50891844f5615de075ab145c1f6129/README.md?plain=1#L82-L116>
|
|
12
|
-
if (ctx
|
|
13
|
-
else
|
|
12
|
+
if (ctx.logger) ctx.logger.warn(err);
|
|
13
|
+
else if (ctx?.app?.emit) ctx.app.emit('error', err, ctx);
|
|
14
|
+
else console.warn(err);
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
if (ctx.request.type.includes('xml')) {
|
|
17
18
|
try {
|
|
18
19
|
ctx.request.xml = new DOMParser().parseFromString(ctx.request.body);
|
|
19
20
|
} catch (err) {
|
|
20
|
-
if (ctx
|
|
21
|
-
else
|
|
21
|
+
if (ctx.logger) ctx.logger.warn(err);
|
|
22
|
+
else if (ctx?.app?.emit) ctx.app.emit('error', err, ctx);
|
|
23
|
+
else console.warn(err);
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
};
|
package/common/tags.js
CHANGED
|
@@ -57,11 +57,17 @@ module.exports = function (options) {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
if (resource === 'calendar')
|
|
60
|
+
if (resource === 'calendar')
|
|
61
61
|
return {
|
|
62
62
|
[buildTag(dav, 'displayname')]: calendar.name
|
|
63
63
|
};
|
|
64
|
-
|
|
64
|
+
|
|
65
|
+
if (resource === 'calendarProppatch')
|
|
66
|
+
return response(ctx.url, status[200], [
|
|
67
|
+
{
|
|
68
|
+
[buildTag(dav, 'displayname')]: calendar.name
|
|
69
|
+
}
|
|
70
|
+
]);
|
|
65
71
|
}
|
|
66
72
|
},
|
|
67
73
|
getcontenttype: {
|
|
@@ -230,37 +236,33 @@ module.exports = function (options) {
|
|
|
230
236
|
'calendar-description': {
|
|
231
237
|
doc: 'https://tools.ietf.org/html/rfc4791#section-5.2.1',
|
|
232
238
|
async resp({ resource, ctx, calendar }) {
|
|
233
|
-
if (resource === 'calendar')
|
|
239
|
+
if (resource === 'calendar')
|
|
234
240
|
return {
|
|
235
|
-
[buildTag(
|
|
241
|
+
[buildTag(cal, 'calendar-description')]: calendar.description
|
|
236
242
|
};
|
|
237
|
-
}
|
|
238
243
|
|
|
239
|
-
if (resource === 'calendarProppatch')
|
|
240
|
-
return response(ctx.url, status[
|
|
244
|
+
if (resource === 'calendarProppatch')
|
|
245
|
+
return response(ctx.url, status[200], [
|
|
241
246
|
{
|
|
242
|
-
[buildTag(cal, 'calendar-description')]:
|
|
247
|
+
[buildTag(cal, 'calendar-description')]: calendar.description
|
|
243
248
|
}
|
|
244
249
|
]);
|
|
245
|
-
}
|
|
246
250
|
}
|
|
247
251
|
},
|
|
248
252
|
'calendar-timezone': {
|
|
249
253
|
doc: 'https://tools.ietf.org/html/rfc4791#section-5.2.2',
|
|
250
254
|
async resp({ resource, ctx, calendar }) {
|
|
251
|
-
if (resource === 'calendar')
|
|
255
|
+
if (resource === 'calendar')
|
|
252
256
|
return {
|
|
253
|
-
[buildTag(
|
|
257
|
+
[buildTag(cal, 'calendar-timezone')]: calendar.timezone
|
|
254
258
|
};
|
|
255
|
-
}
|
|
256
259
|
|
|
257
|
-
if (resource === 'calendarProppatch')
|
|
258
|
-
return response(ctx.url, status[
|
|
260
|
+
if (resource === 'calendarProppatch')
|
|
261
|
+
return response(ctx.url, status[200], [
|
|
259
262
|
{
|
|
260
|
-
[buildTag(cal, 'calendar-timezone')]:
|
|
263
|
+
[buildTag(cal, 'calendar-timezone')]: calendar.timezone
|
|
261
264
|
}
|
|
262
265
|
]);
|
|
263
|
-
}
|
|
264
266
|
}
|
|
265
267
|
},
|
|
266
268
|
'calendar-user-address-set': {
|
|
@@ -351,7 +353,19 @@ module.exports = function (options) {
|
|
|
351
353
|
},
|
|
352
354
|
'checksum-versions': {},
|
|
353
355
|
'dropbox-home-URL': {},
|
|
354
|
-
'email-address-set': {
|
|
356
|
+
'email-address-set': {
|
|
357
|
+
async resp({ resource, ctx }) {
|
|
358
|
+
if (
|
|
359
|
+
resource === 'calendar' &&
|
|
360
|
+
(ctx?.state?.user?.email || ctx?.state?.user?.username)
|
|
361
|
+
) {
|
|
362
|
+
return {
|
|
363
|
+
[buildTag(cs, 'email-address-set')]:
|
|
364
|
+
ctx?.state?.user?.email || ctx?.state?.user?.username
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
},
|
|
355
369
|
getctag: {
|
|
356
370
|
// DEPRECATED
|
|
357
371
|
doc: 'https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-ctag.txt',
|
|
@@ -370,37 +384,37 @@ module.exports = function (options) {
|
|
|
370
384
|
[ical]: {
|
|
371
385
|
'calendar-color': {
|
|
372
386
|
async resp({ resource, ctx, calendar }) {
|
|
373
|
-
if (resource === 'calendar')
|
|
387
|
+
if (resource === 'calendar')
|
|
374
388
|
return {
|
|
375
389
|
[buildTag(ical, 'calendar-color')]: calendar.color
|
|
376
390
|
};
|
|
377
|
-
}
|
|
378
391
|
|
|
379
|
-
if (resource === 'calendarProppatch')
|
|
380
|
-
return response(ctx.url, status[
|
|
392
|
+
if (resource === 'calendarProppatch')
|
|
393
|
+
return response(ctx.url, status[200], [
|
|
381
394
|
{
|
|
382
|
-
[buildTag(ical, 'calendar-color')]:
|
|
395
|
+
[buildTag(ical, 'calendar-color')]: calendar.color
|
|
383
396
|
}
|
|
384
397
|
]);
|
|
385
|
-
}
|
|
386
398
|
}
|
|
387
399
|
},
|
|
388
400
|
'calendar-order': {
|
|
389
|
-
async resp({ resource, ctx }) {
|
|
390
|
-
if (
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
401
|
+
async resp({ resource, ctx, calendar }) {
|
|
402
|
+
if (resource === 'calendar')
|
|
403
|
+
return {
|
|
404
|
+
[buildTag(ical, 'calendar-order')]: calendar.order
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
if (resource === 'calendarProppatch')
|
|
408
|
+
return response(ctx.url, status[200], [
|
|
395
409
|
{
|
|
396
|
-
[buildTag(ical, 'calendar-order')]:
|
|
410
|
+
[buildTag(ical, 'calendar-order')]: calendar.order
|
|
397
411
|
}
|
|
398
412
|
]);
|
|
399
|
-
}
|
|
400
413
|
}
|
|
401
414
|
}
|
|
402
415
|
}
|
|
403
416
|
};
|
|
417
|
+
|
|
404
418
|
const getResponse = async ({ resource, child, ctx, calendar, event }) => {
|
|
405
419
|
if (!child.namespaceURI) {
|
|
406
420
|
return null;
|
package/index.js
CHANGED
|
@@ -134,11 +134,8 @@ module.exports = function (options) {
|
|
|
134
134
|
if (
|
|
135
135
|
ctx.url.toLowerCase() === '/.well-known/caldav' &&
|
|
136
136
|
!options.disableWellKnown
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
ctx.status = 404;
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
137
|
+
)
|
|
138
|
+
return ctx.redirect(rootRoute); // TODO: should be 302?
|
|
142
139
|
|
|
143
140
|
if (!rootRegexp.test(ctx.url)) {
|
|
144
141
|
await next();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "caldav-adapter",
|
|
3
3
|
"description": "CalDAV server for Node.js and Koa. Modernized and maintained for Forward Email.",
|
|
4
|
-
"version": "8.0
|
|
4
|
+
"version": "8.1.0",
|
|
5
5
|
"author": "Sanders DeNardi and Forward Email LLC",
|
|
6
6
|
"contributors": [
|
|
7
7
|
"Sanders DeNardi <sedenardi@gmail.com> (http://www.sandersdenardi.com/)",
|
|
@@ -18,12 +18,73 @@ module.exports = function (options) {
|
|
|
18
18
|
ctx.request.xml
|
|
19
19
|
);
|
|
20
20
|
|
|
21
|
+
const updates = {};
|
|
22
|
+
for (const child of children) {
|
|
23
|
+
if (!child.localName || !child.textContent) continue;
|
|
24
|
+
switch (child.localName) {
|
|
25
|
+
case 'displayname': {
|
|
26
|
+
updates.name = child.textContent;
|
|
27
|
+
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
case 'calendar-description': {
|
|
32
|
+
updates.description = child.textContent;
|
|
33
|
+
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
case 'calendar-timezone': {
|
|
38
|
+
updates.timezone = child.textContent;
|
|
39
|
+
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case 'calendar-color': {
|
|
44
|
+
updates.color = child.textContent;
|
|
45
|
+
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
case 'calendar-order': {
|
|
50
|
+
updates.order = Number.parseInt(child.textContent, 10);
|
|
51
|
+
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// TODO: finish me
|
|
56
|
+
|
|
57
|
+
// No default
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//
|
|
62
|
+
// if updates was empty then we should log so we can alert admins
|
|
63
|
+
// as to what other properties clients are attempting to update
|
|
64
|
+
// (this code is an anti-pattern but temporary so we can improve)
|
|
65
|
+
//
|
|
66
|
+
if (_.isEmpty(updates)) {
|
|
67
|
+
const err = new TypeError('CalDAV PROPPATCH missing fields');
|
|
68
|
+
err.isCodeBug = true; // Specific to Forward Email (can be removed later)
|
|
69
|
+
err.str = ctx.request.body; // Sensitive and should be removed later
|
|
70
|
+
err.xml = ctx.request.xml;
|
|
71
|
+
console.error(err);
|
|
72
|
+
if (ctx.logger) ctx.logger.error(err);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const updatedCalendar = await options.data.updateCalendar(ctx, {
|
|
76
|
+
principalId: ctx.state.params.principalId,
|
|
77
|
+
calendarId: ctx.state.params.calendarId,
|
|
78
|
+
user: ctx.state.user,
|
|
79
|
+
updates
|
|
80
|
+
});
|
|
81
|
+
|
|
21
82
|
const actions = _.map(children, async (child) => {
|
|
22
83
|
return tags.getResponse({
|
|
23
84
|
resource: 'calendarProppatch',
|
|
24
85
|
child,
|
|
25
86
|
ctx,
|
|
26
|
-
calendar
|
|
87
|
+
calendar: updatedCalendar
|
|
27
88
|
});
|
|
28
89
|
});
|
|
29
90
|
const res = await Promise.all(actions);
|
|
@@ -13,6 +13,12 @@ module.exports = function (options) {
|
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// TODO: support XML update (?)
|
|
17
|
+
// const { children } = xml.getWithChildren(
|
|
18
|
+
// '/D:propertyupdate/D:set/D:prop',
|
|
19
|
+
// ctx.request.xml
|
|
20
|
+
// );
|
|
21
|
+
|
|
16
22
|
if (
|
|
17
23
|
ctx.request.type !== 'text/calendar' ||
|
|
18
24
|
typeof ctx.request.body !== 'string'
|
|
@@ -11,7 +11,7 @@ const routerUserPropfind = require('./user/propfind');
|
|
|
11
11
|
const routerCalPropfind = require('./calendar/propfind');
|
|
12
12
|
const routerCalReport = require('./calendar/report');
|
|
13
13
|
const routerCalGet = require('./calendar/get');
|
|
14
|
-
|
|
14
|
+
const routerCalProppatch = require('./calendar/proppatch');
|
|
15
15
|
const routerCalPut = require('./calendar/put');
|
|
16
16
|
const routerCalDelete = require('./calendar/delete');
|
|
17
17
|
|
|
@@ -25,7 +25,7 @@ module.exports = function (options) {
|
|
|
25
25
|
propfind: routerCalPropfind(options),
|
|
26
26
|
report: routerCalReport(options),
|
|
27
27
|
get: routerCalGet(options),
|
|
28
|
-
|
|
28
|
+
proppatch: routerCalProppatch(options),
|
|
29
29
|
put: routerCalPut(options),
|
|
30
30
|
delete: routerCalDelete(options),
|
|
31
31
|
mkcalendar: routeMkCalendar(options)
|
|
@@ -31,6 +31,18 @@ module.exports = function (options) {
|
|
|
31
31
|
|
|
32
32
|
break;
|
|
33
33
|
}
|
|
34
|
+
|
|
35
|
+
case 'calendar-color': {
|
|
36
|
+
calendar.color = child.textContent;
|
|
37
|
+
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
case 'calendar-order': {
|
|
42
|
+
calendar.order = Number.parseInt(child.textContent, 10);
|
|
43
|
+
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
34
46
|
// No default
|
|
35
47
|
}
|
|
36
48
|
}
|