caldav-adapter 5.0.1 → 6.0.1
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 +0 -3
- package/common/tags.js +4 -4
- package/index.js +1 -1
- package/package.json +2 -2
- package/routes/calendar/calendar/calendar-multiget.js +3 -3
- package/routes/calendar/calendar/calendar-query.js +4 -4
- package/routes/calendar/calendar/delete.js +2 -2
- package/routes/calendar/calendar/get.js +2 -2
- package/routes/calendar/calendar/propfind.js +3 -3
- package/routes/calendar/calendar/put.js +34 -15
- package/routes/calendar/calendar/sync-collection.js +2 -2
- package/routes/calendar/calendar.js +1 -1
- package/routes/calendar/user/propfind.js +1 -1
- package/routes/principal/mkcalendar.js +2 -2
package/common/parse-body.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const ical = require('node-ical');
|
|
2
1
|
const raw = require('raw-body');
|
|
3
2
|
const { DOMParser } = require('@xmldom/xmldom');
|
|
4
3
|
|
|
@@ -10,7 +9,5 @@ module.exports = async function (ctx) {
|
|
|
10
9
|
|
|
11
10
|
if (ctx.request.type.includes('xml')) {
|
|
12
11
|
ctx.request.xml = new DOMParser().parseFromString(ctx.request.body);
|
|
13
|
-
} else if (ctx.request.type === 'text/calendar') {
|
|
14
|
-
ctx.request.ical = await ical.async.parseICS(ctx.request.body);
|
|
15
12
|
}
|
|
16
13
|
};
|
package/common/tags.js
CHANGED
|
@@ -83,10 +83,10 @@ module.exports = function (options) {
|
|
|
83
83
|
},
|
|
84
84
|
getetag: {
|
|
85
85
|
doc: 'https://tools.ietf.org/html/rfc4791#section-5.3.4',
|
|
86
|
-
async resp({ resource, event }) {
|
|
86
|
+
async resp({ resource, ctx, event }) {
|
|
87
87
|
if (resource === 'event') {
|
|
88
88
|
return {
|
|
89
|
-
[buildTag(dav, 'getetag')]: options.data.getETag(event)
|
|
89
|
+
[buildTag(dav, 'getetag')]: options.data.getETag(ctx, event)
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
}
|
|
@@ -199,8 +199,8 @@ module.exports = function (options) {
|
|
|
199
199
|
[cal]: {
|
|
200
200
|
'calendar-data': {
|
|
201
201
|
doc: 'https://tools.ietf.org/html/rfc4791#section-9.6',
|
|
202
|
-
async resp({ event, calendar }) {
|
|
203
|
-
const ics = await options.data.buildICS(event, calendar);
|
|
202
|
+
async resp({ event, ctx, calendar }) {
|
|
203
|
+
const ics = await options.data.buildICS(ctx, event, calendar);
|
|
204
204
|
return {
|
|
205
205
|
[buildTag(cal, 'calendar-data')]: ics
|
|
206
206
|
};
|
package/index.js
CHANGED
|
@@ -85,7 +85,7 @@ module.exports = function (options) {
|
|
|
85
85
|
return false;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
ctx.state.user = await options.authenticate({
|
|
88
|
+
ctx.state.user = await options.authenticate(ctx, {
|
|
89
89
|
username: creds.name,
|
|
90
90
|
password: creds.pass,
|
|
91
91
|
principalId: ctx.state.params.principalId
|
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": "
|
|
4
|
+
"version": "6.0.1",
|
|
5
5
|
"author": "Sanders DeNardi and Forward Email LLC",
|
|
6
6
|
"contributors": [
|
|
7
7
|
"Sanders DeNardi <sedenardi@gmail.com> (http://www.sandersdenardi.com/)",
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@xmldom/xmldom": "^0.8.10",
|
|
12
12
|
"basic-auth": "^2.0.1",
|
|
13
|
+
"ical.js": "^1.5.0",
|
|
13
14
|
"lodash": "^4.17.21",
|
|
14
15
|
"moment": "^2.30.1",
|
|
15
|
-
"node-ical": "^0.17.1",
|
|
16
16
|
"path-to-regexp": "^6.2.1",
|
|
17
17
|
"raw-body": "^2.5.2",
|
|
18
18
|
"winston": "^3.11.0",
|
|
@@ -19,7 +19,7 @@ module.exports = function (options) {
|
|
|
19
19
|
|
|
20
20
|
const hrefParts = href.split('/');
|
|
21
21
|
const eventId = hrefParts.at(-1).slice(0, -4);
|
|
22
|
-
const event = await options.data.getEvent({
|
|
22
|
+
const event = await options.data.getEvent(ctx, {
|
|
23
23
|
eventId,
|
|
24
24
|
principalId: ctx.state.params.principalId,
|
|
25
25
|
calendarId: ctx.state.params.calendarId,
|
|
@@ -31,10 +31,10 @@ module.exports = function (options) {
|
|
|
31
31
|
return response(href, status[404]);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const ics = await options.data.buildICS(event, calendar);
|
|
34
|
+
const ics = await options.data.buildICS(ctx, event, calendar);
|
|
35
35
|
return response(href, status[200], [
|
|
36
36
|
{
|
|
37
|
-
'D:getetag': options.data.getETag(event)
|
|
37
|
+
'D:getetag': options.data.getETag(ctx, event)
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
'CAL:calendar-data': ics
|
|
@@ -21,9 +21,9 @@ module.exports = function (options) {
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
if (!filters?.[0]) {
|
|
24
|
-
const events = await options.data.getEventsForCalendar({
|
|
24
|
+
const events = await options.data.getEventsForCalendar(ctx, {
|
|
25
25
|
principalId: ctx.state.params.principalId,
|
|
26
|
-
calendarId: options.data.getCalendarId(calendar),
|
|
26
|
+
calendarId: options.data.getCalendarId(ctx, calendar),
|
|
27
27
|
user: ctx.state.user,
|
|
28
28
|
fullData
|
|
29
29
|
});
|
|
@@ -67,9 +67,9 @@ module.exports = function (options) {
|
|
|
67
67
|
if (endAttr && endAttr.nodeValue && moment(endAttr.nodeValue).isValid())
|
|
68
68
|
end = moment(endAttr.nodeValue).toDate();
|
|
69
69
|
|
|
70
|
-
const events = await options.data.getEventsByDate({
|
|
70
|
+
const events = await options.data.getEventsByDate(ctx, {
|
|
71
71
|
principalId: ctx.state.params.principalId,
|
|
72
|
-
calendarId: options.data.getCalendarId(calendar),
|
|
72
|
+
calendarId: options.data.getCalendarId(ctx, calendar),
|
|
73
73
|
start,
|
|
74
74
|
end,
|
|
75
75
|
user: ctx.state.user,
|
|
@@ -17,7 +17,7 @@ module.exports = function (options) {
|
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const existing = await options.data.getEvent({
|
|
20
|
+
const existing = await options.data.getEvent(ctx, {
|
|
21
21
|
eventId: ctx.state.params.eventId,
|
|
22
22
|
principalId: ctx.state.params.principalId,
|
|
23
23
|
calendarId: ctx.state.params.calendarId,
|
|
@@ -26,7 +26,7 @@ module.exports = function (options) {
|
|
|
26
26
|
});
|
|
27
27
|
log.debug(`existing event${existing ? '' : ' not'} found`);
|
|
28
28
|
|
|
29
|
-
await options.data.deleteEvent({
|
|
29
|
+
await options.data.deleteEvent(ctx, {
|
|
30
30
|
eventId: ctx.state.params.eventId,
|
|
31
31
|
principalId: ctx.state.params.principalId,
|
|
32
32
|
calendarId: ctx.state.params.calendarId,
|
|
@@ -5,7 +5,7 @@ module.exports = function (options) {
|
|
|
5
5
|
const log = winston({ ...options, label: 'calendar/get' });
|
|
6
6
|
|
|
7
7
|
const exec = async function (ctx, calendar) {
|
|
8
|
-
const event = await options.data.getEvent({
|
|
8
|
+
const event = await options.data.getEvent(ctx, {
|
|
9
9
|
eventId: ctx.state.params.eventId,
|
|
10
10
|
principalId: ctx.state.params.principalId,
|
|
11
11
|
calendarId: ctx.state.params.calendarId,
|
|
@@ -18,7 +18,7 @@ module.exports = function (options) {
|
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
return options.data.buildICS(event, calendar);
|
|
21
|
+
return options.data.buildICS(ctx, event, calendar);
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
return {
|
|
@@ -31,7 +31,7 @@ module.exports = function (options) {
|
|
|
31
31
|
|
|
32
32
|
const calendarUrl = path.join(
|
|
33
33
|
ctx.state.calendarHomeUrl,
|
|
34
|
-
options.data.getCalendarId(calendar),
|
|
34
|
+
options.data.getCalendarId(ctx, calendar),
|
|
35
35
|
'/'
|
|
36
36
|
);
|
|
37
37
|
const props = _.compact(res);
|
|
@@ -53,9 +53,9 @@ module.exports = function (options) {
|
|
|
53
53
|
const fullData = _.some(children, (child) => {
|
|
54
54
|
return child.localName === 'calendar-data';
|
|
55
55
|
});
|
|
56
|
-
const events = await options.data.getEventsForCalendar({
|
|
56
|
+
const events = await options.data.getEventsForCalendar(ctx, {
|
|
57
57
|
principalId: ctx.state.params.principalId,
|
|
58
|
-
calendarId: options.data.getCalendarId(calendar),
|
|
58
|
+
calendarId: options.data.getCalendarId(ctx, calendar),
|
|
59
59
|
user: ctx.state.user,
|
|
60
60
|
fullData
|
|
61
61
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const ICAL = require('ical.js');
|
|
2
2
|
const { notFound, preconditionFail } = require('../../../common/x-build');
|
|
3
3
|
const { setMissingMethod } = require('../../../common/response');
|
|
4
4
|
const winston = require('../../../common/winston');
|
|
@@ -13,20 +13,41 @@ module.exports = function (options) {
|
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
if (
|
|
17
|
-
|
|
16
|
+
if (
|
|
17
|
+
ctx.request.type !== 'text/calendar' ||
|
|
18
|
+
typeof ctx.request.body !== 'string'
|
|
19
|
+
) {
|
|
20
|
+
log.warn('incoming VEVENT not present');
|
|
18
21
|
ctx.body = notFound(ctx.url); // Make more meaningful
|
|
19
22
|
return;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
if (!ctx.state.params.eventId) {
|
|
26
|
+
try {
|
|
27
|
+
const parsed = ICAL.parse(ctx.request.body);
|
|
28
|
+
if (!parsed || parsed.length === 0) {
|
|
29
|
+
const err = new TypeError('ICAL.parse was not successful');
|
|
30
|
+
err.parsed = parsed;
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const comp = new ICAL.Component(parsed);
|
|
35
|
+
if (!comp) throw new TypeError('ICAL.Component was not successful');
|
|
36
|
+
const vevent = comp.getFirstSubcomponent('vevent');
|
|
37
|
+
if (!vevent)
|
|
38
|
+
throw new TypeError('comp.getFirstSubcomponent was not successful');
|
|
39
|
+
const uid = vevent.getFirstPropertyValue('uid');
|
|
40
|
+
if (!uid || typeof uid !== 'string')
|
|
41
|
+
throw new TypeError('VEVENT missing UID');
|
|
42
|
+
ctx.state.params.eventId = uid;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
log.warn(err);
|
|
45
|
+
ctx.body = notFound(ctx.url); // Make more meaningful
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
27
48
|
}
|
|
28
49
|
|
|
29
|
-
const existing = await options.data.getEvent({
|
|
50
|
+
const existing = await options.data.getEvent(ctx, {
|
|
30
51
|
eventId: ctx.state.params.eventId,
|
|
31
52
|
principalId: ctx.state.params.principalId,
|
|
32
53
|
calendarId: ctx.state.params.calendarId,
|
|
@@ -43,30 +64,28 @@ module.exports = function (options) {
|
|
|
43
64
|
return;
|
|
44
65
|
}
|
|
45
66
|
|
|
46
|
-
const updateObject = await options.data.updateEvent({
|
|
67
|
+
const updateObject = await options.data.updateEvent(ctx, {
|
|
47
68
|
eventId: ctx.state.params.eventId,
|
|
48
69
|
principalId: ctx.state.params.principalId,
|
|
49
70
|
calendarId: ctx.state.params.calendarId,
|
|
50
|
-
event: incoming,
|
|
51
71
|
user: ctx.state.user
|
|
52
72
|
});
|
|
53
73
|
log.debug('event updated');
|
|
54
74
|
|
|
55
75
|
/* https://tools.ietf.org/html/rfc4791#section-5.3.2 */
|
|
56
76
|
ctx.status = 201;
|
|
57
|
-
ctx.set('ETag', options.data.getETag(updateObject));
|
|
77
|
+
ctx.set('ETag', options.data.getETag(ctx, updateObject));
|
|
58
78
|
} else {
|
|
59
|
-
const newObject = await options.data.createEvent({
|
|
79
|
+
const newObject = await options.data.createEvent(ctx, {
|
|
60
80
|
eventId: ctx.state.params.eventId,
|
|
61
81
|
principalId: ctx.state.params.principalId,
|
|
62
82
|
calendarId: ctx.state.params.calendarId,
|
|
63
|
-
event: incoming,
|
|
64
83
|
user: ctx.state.user
|
|
65
84
|
});
|
|
66
85
|
log.debug('new event created');
|
|
67
86
|
/* https://tools.ietf.org/html/rfc4791#section-5.3.2 */
|
|
68
87
|
ctx.status = 201;
|
|
69
|
-
ctx.set('ETag', options.data.getETag(newObject));
|
|
88
|
+
ctx.set('ETag', options.data.getETag(ctx, newObject));
|
|
70
89
|
}
|
|
71
90
|
};
|
|
72
91
|
|
|
@@ -19,9 +19,9 @@ module.exports = function (options) {
|
|
|
19
19
|
const fullData = _.some(children, (child) => {
|
|
20
20
|
return child.localName === 'calendar-data';
|
|
21
21
|
});
|
|
22
|
-
const events = await options.data.getEventsForCalendar({
|
|
22
|
+
const events = await options.data.getEventsForCalendar(ctx, {
|
|
23
23
|
principalId: ctx.state.params.principalId,
|
|
24
|
-
calendarId: options.data.getCalendarId(calendar),
|
|
24
|
+
calendarId: options.data.getCalendarId(ctx, calendar),
|
|
25
25
|
user: ctx.state.user,
|
|
26
26
|
fullData
|
|
27
27
|
});
|
|
@@ -32,7 +32,7 @@ module.exports = function (options) {
|
|
|
32
32
|
|
|
33
33
|
if (calendarId) {
|
|
34
34
|
// Check calendar exists & user has access
|
|
35
|
-
const calendar = await options.data.getCalendar({
|
|
35
|
+
const calendar = await options.data.getCalendar(ctx, {
|
|
36
36
|
principalId: ctx.state.params.principalId,
|
|
37
37
|
calendarId,
|
|
38
38
|
user: ctx.state.user
|
|
@@ -36,7 +36,7 @@ module.exports = function (options) {
|
|
|
36
36
|
response(ctx.url, props.length > 0 ? status[200] : status[404], props)
|
|
37
37
|
];
|
|
38
38
|
|
|
39
|
-
const calendars = await options.data.getCalendarsForPrincipal({
|
|
39
|
+
const calendars = await options.data.getCalendarsForPrincipal(ctx, {
|
|
40
40
|
principalId: ctx.state.params.principalId,
|
|
41
41
|
user: ctx.state.user
|
|
42
42
|
});
|
|
@@ -62,8 +62,8 @@ module.exports = function (options) {
|
|
|
62
62
|
// </d:set>
|
|
63
63
|
// </c:mkcalendar>
|
|
64
64
|
|
|
65
|
-
const calendarObject = await options.data.createCalendar(calendar);
|
|
65
|
+
const calendarObject = await options.data.createCalendar(ctx, calendar);
|
|
66
66
|
ctx.status = 201;
|
|
67
|
-
ctx.set('ETag', options.data.getETag(calendarObject));
|
|
67
|
+
ctx.set('ETag', options.data.getETag(ctx, calendarObject));
|
|
68
68
|
};
|
|
69
69
|
};
|