Skip to content

Commit

Permalink
Merge pull request #238 from nextcloud/feature/11-12/inbox_outbox
Browse files Browse the repository at this point in the history
Add support for RFC6638 and RFC7953
  • Loading branch information
georgehrke authored Dec 13, 2019
2 parents 964c26c + 2def788 commit 1e4d81b
Show file tree
Hide file tree
Showing 9 changed files with 339 additions and 11 deletions.
2 changes: 1 addition & 1 deletion dist/dist.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/dist.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/models/calendarHome.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class CalendarHome extends DavCollection {
* @returns {Promise<Calendar[]>}
*/
async findAllCalendars() {
return super.findAllByFilter((elm) => elm instanceof Calendar);
return super.findAllByFilter((elm) => elm instanceof Calendar && !(elm instanceof ScheduleInbox));
}

/**
Expand Down
29 changes: 26 additions & 3 deletions src/models/scheduleInbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,31 @@
*
*/

import { DavCollection } from './davCollection.js';
import { Calendar } from './calendar.js';
import * as NS from '../utility/namespaceUtility.js';
import scheduleInboxPropSet from '../propset/scheduleInboxPropSet.js';

export default class ScheduleInbox extends Calendar {

/**
* @inheritDoc
*/
constructor(...args) {
super(...args);

super._registerPropSetFactory(scheduleInboxPropSet);

// https://tools.ietf.org/html/rfc7953#section-7.2.4
super._exposeProperty('availability', NS.IETF_CALDAV, 'calendar-availability', true);
}

/**
* @inheritDoc
*/
static getPropFindList() {
return super.getPropFindList().concat([
[NS.IETF_CALDAV, 'calendar-availability']
]);
}

export default class ScheduleInbox extends DavCollection {
// TODO implement me
}
39 changes: 38 additions & 1 deletion src/models/scheduleOutbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,44 @@
*/

import { DavCollection } from './davCollection.js';
import * as NS from '../utility/namespaceUtility.js';

export default class ScheduleOutbox extends DavCollection {
// TODO implement me

/**
* Sends a free-busy-request for the scheduling outbox
* The data is required to be a valid iTIP data.
* For an example, see https://tools.ietf.org/html/rfc6638#appendix-B.5
*
* @param {String} data iTIP with VFREEBUSY component and METHOD:REQUEST
* @returns {Promise<String[]>}
*/
async freeBusyRequest(data) {
const result = {};
const response = await this._request.post(this.url, {
'Content-Type': 'text/calendar; charset="utf-8"'
}, data);

const domParser = new DOMParser();
const document = domParser.parseFromString(response.body, 'application/xml');

const responses = document.evaluate('/cl:schedule-response/cl:response', document, NS.resolve, XPathResult.ANY_TYPE, null);
let responseNode;

while ((responseNode = responses.iterateNext()) !== null) {
const recipient = document.evaluate('string(cl:recipient/d:href)', responseNode, NS.resolve, XPathResult.ANY_TYPE, null).stringValue;
const status = document.evaluate('string(cl:request-status)', responseNode, NS.resolve, XPathResult.ANY_TYPE, null).stringValue;
const calendarData = document.evaluate('string(cl:calendar-data)', responseNode, NS.resolve, XPathResult.ANY_TYPE, null).stringValue;
const success = /^2.\d(;.+)?$/.test(status);

result[recipient] = {
calendarData,
status,
success
};
}

return result;
}

}
48 changes: 48 additions & 0 deletions src/propset/scheduleInboxPropSet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* CDAV Library
*
* This library is part of the Nextcloud project
*
* @author Georg Ehrke
* @copyright 2019 Georg Ehrke <oc.list@georgehrke.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/

import * as NS from '../utility/namespaceUtility.js';

/**
* This function is capable of creating the propset xml structure for:
* - {urn:ietf:params:xml:ns:caldav}calendar-availability
*
* @param {Object} props
* @return {Object}
*/
export default function calendarPropSet(props) {
const xmlified = [];

Object.entries(props).forEach(([key, value]) => {
switch (key) {
case '{urn:ietf:params:xml:ns:caldav}calendar-availability':
xmlified.push({
name: [NS.IETF_CALDAV, 'calendar-availability'],
value: value.toString()
});
break;
}
});

return xmlified;
}
31 changes: 29 additions & 2 deletions test/unit/models/scheduleInboxTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,35 @@
*
*/

import ScheduleInbox from "../../../src/models/scheduleInbox.js";
import { Calendar } from "../../../src/models/calendar.js";

describe('Schedule inbox model', () => {
it('should ...', () => {
pending();

it('should inherit from Calendar', () => {
const parent = jasmine.createSpyObj('DavCollection', ['findAll', 'findAllByFilter', 'find',
'createCollection', 'createObject', 'update', 'delete', 'isReadable', 'isWriteable']);
const request = jasmine.createSpyObj('Request', ['propFind', 'put', 'delete']);
const url = '/foo/bar/folder';
const props = {
'{urn:ietf:params:xml:ns:caldav}calendar-availability': 'VAVAILABILITY123'
}

const scheduleInbox = new ScheduleInbox(parent, request, url, props);
expect(scheduleInbox).toEqual(jasmine.any(Calendar))
});

it('should inherit expose the property calendar-availability', () => {
const parent = jasmine.createSpyObj('DavCollection', ['findAll', 'findAllByFilter', 'find',
'createCollection', 'createObject', 'update', 'delete', 'isReadable', 'isWriteable']);
const request = jasmine.createSpyObj('Request', ['propFind', 'put', 'delete']);
const url = '/foo/bar/folder';
const props = {
'{urn:ietf:params:xml:ns:caldav}calendar-availability': 'VAVAILABILITY123'
}

const scheduleInbox = new ScheduleInbox(parent, request, url, props);
expect(scheduleInbox.availability).toEqual('VAVAILABILITY123');
});

});
153 changes: 151 additions & 2 deletions test/unit/models/scheduleOutboxTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,157 @@
*
*/

import ScheduleOutbox from "../../../src/models/scheduleOutbox.js";
import { DavCollection } from "../../../src/models/davCollection.js";

describe('Schedule outbox model', () => {
it('should ...', () => {
pending();

it('should inherit from DavCollection', () => {
const parent = jasmine.createSpyObj('DavCollection', ['findAll', 'findAllByFilter', 'find',
'createCollection', 'createObject', 'update', 'delete', 'isReadable', 'isWriteable']);
const request = jasmine.createSpyObj('Request', ['propFind', 'put', 'delete']);
const url = '/foo/bar/folder';
const props = {}

const scheduleOutbox = new ScheduleOutbox(parent, request, url, props)
expect(scheduleOutbox).toEqual(jasmine.any(DavCollection))
});

it('should provide a method to gather free/busy data', () => {
const parent = jasmine.createSpyObj('DavCollection', ['findAll', 'findAllByFilter', 'find',
'createCollection', 'createObject', 'update', 'delete', 'isReadable', 'isWriteable']);
const request = jasmine.createSpyObj('Request', ['propFind', 'put', 'delete', 'post']);
const url = '/foo/bar/folder';
const props = {}

const scheduleOutbox = new ScheduleOutbox(parent, request, url, props)

const requestData = `BEGIN:VCALENDAR
...
END:VCALENDAR`;

// Example response taken from https://tools.ietf.org/html/rfc6638#appendix-B.5
const response = `<?xml version="1.0" encoding="utf-8" ?>
<C:schedule-response xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<C:response>
<C:recipient>
<D:href>mailto:wilfredo@example.com</D:href>
</C:recipient>
<C:request-status>2.0;Success</C:request-status>
<C:calendar-data>BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Server//EN
METHOD:REPLY
BEGIN:VFREEBUSY
UID:4FD3AD926350
DTSTAMP:20090602T200733Z
DTSTART:20090602T000000Z
DTEND:20090604T000000Z
ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com
ATTENDEE;CN="Wilfredo Sanchez Vega":mailto:wilfredo@example.com
FREEBUSY;FBTYPE=BUSY:20090602T110000Z/20090602T120000Z
FREEBUSY;FBTYPE=BUSY:20090603T170000Z/20090603T180000Z
END:VFREEBUSY
END:VCALENDAR
</C:calendar-data>
</C:response>
<C:response>
<C:recipient>
<D:href>mailto:bernard@example.net</D:href>
</C:recipient>
<C:request-status>2.0;Success</C:request-status>
<C:calendar-data>BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Server//EN
METHOD:REPLY
BEGIN:VFREEBUSY
UID:4FD3AD926350
DTSTAMP:20090602T200733Z
DTSTART:20090602T000000Z
DTEND:20090604T000000Z
ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com
ATTENDEE;CN="Bernard Desruisseaux":mailto:bernard@example.net
FREEBUSY;FBTYPE=BUSY:20090602T150000Z/20090602T160000Z
FREEBUSY;FBTYPE=BUSY:20090603T090000Z/20090603T100000Z
FREEBUSY;FBTYPE=BUSY:20090603T180000Z/20090603T190000Z
END:VFREEBUSY
END:VCALENDAR
</C:calendar-data>
</C:response>
<C:response>
<C:recipient>
<D:href>mailto:mike@example.org</D:href>
</C:recipient>
<C:request-status>3.7;Invalid calendar user</C:request-status>
</C:response>
</C:schedule-response>`

request.post.and.callFake(() => {
return Promise.resolve({
status: 207,
body: response,
xhr: null
})
});

return scheduleOutbox.freeBusyRequest(requestData).then((freeBusyData) => {
expect(freeBusyData).toEqual({
'mailto:wilfredo@example.com': {
calendarData: `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Server//EN
METHOD:REPLY
BEGIN:VFREEBUSY
UID:4FD3AD926350
DTSTAMP:20090602T200733Z
DTSTART:20090602T000000Z
DTEND:20090604T000000Z
ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com
ATTENDEE;CN="Wilfredo Sanchez Vega":mailto:wilfredo@example.com
FREEBUSY;FBTYPE=BUSY:20090602T110000Z/20090602T120000Z
FREEBUSY;FBTYPE=BUSY:20090603T170000Z/20090603T180000Z
END:VFREEBUSY
END:VCALENDAR
`,
status: '2.0;Success',
success: true
},
'mailto:bernard@example.net': {
calendarData: `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Server//EN
METHOD:REPLY
BEGIN:VFREEBUSY
UID:4FD3AD926350
DTSTAMP:20090602T200733Z
DTSTART:20090602T000000Z
DTEND:20090604T000000Z
ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com
ATTENDEE;CN="Bernard Desruisseaux":mailto:bernard@example.net
FREEBUSY;FBTYPE=BUSY:20090602T150000Z/20090602T160000Z
FREEBUSY;FBTYPE=BUSY:20090603T090000Z/20090603T100000Z
FREEBUSY;FBTYPE=BUSY:20090603T180000Z/20090603T190000Z
END:VFREEBUSY
END:VCALENDAR
`,
status: '2.0;Success',
success: true
},
'mailto:mike@example.org': {
calendarData: '',
status: '3.7;Invalid calendar user',
success: false
}
});

expect(request.post).toHaveBeenCalledTimes(1)
expect(request.post).toHaveBeenCalledWith('/foo/bar/folder/', {
'Content-Type': 'text/calendar; charset="utf-8"'
}, requestData);
}).catch(() => {
fail('Calendar findAllVObjects was not supposed to fail');
});

})

});
44 changes: 44 additions & 0 deletions test/unit/propset/scheduleInboxPropSetTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* CDAV Library
*
* This library is part of the Nextcloud project
*
* @author Georg Ehrke
* @copyright 2018 Georg Ehrke <oc.list@georgehrke.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/

import scheduleInboxPropSet from "../../../src/propset/scheduleInboxPropSet.js";

describe('Schedule Inbox collection prop-set', () => {
it('should ignore unknown properties', () => {
expect(scheduleInboxPropSet({
'{Foo:}bar': 123
})).toEqual([]);
});

it('should serialize {DAV:}displayname correctly', () => {
expect(scheduleInboxPropSet({
'{Foo:}bar': 123,
'{urn:ietf:params:xml:ns:caldav}calendar-availability': 'NEW:VAVAILABILITY'
})).toEqual([
{
name: ['urn:ietf:params:xml:ns:caldav', 'calendar-availability'],
value: 'NEW:VAVAILABILITY'
}
]);
});
});

0 comments on commit 1e4d81b

Please sign in to comment.