Fails to parse COMMA-separated list of calendar addresses in quoted-strings (e.g. `MEMBER` parameter)
st3iny opened this issue · 7 comments
st3iny commented
RFC: https://www.rfc-editor.org/rfc/rfc5545#section-3.2.11
The builtin ics parser does not handle parsing COMMA-separated list of calendar addresses in quoted-strings correctly.
Note: The ics example data (attendee property) is valid and was copied as is from the RFC.
Example
const ics = 'ATTENDEE;MEMBER="mailto:projectA@example.com","mailto:projectB@example.com":mailto:janedoe@example.com'
const prop = ICAL.Property.fromString(ics)
Expected
[
"attendee",
{
"member": [
"mailto:projectA@example.com",
"mailto:projectB@example.com"
]
},
"cal-address",
"mailto:janedoe@example.com"
]
Received
[
"attendee",
{
"member": [
"mailto:projectA@example.com",
"mailto:projectB@example.com"
]
},
"cal-address",
"projectA@example.com\",\"mailto:projectB@example.com\":mailto:janedoe@example.com"
]
The parser stops at the colon inside the first quoted-string MEMBER
parameter. But for some reason both member parameters are still parsed correctly. Only the property value is not parsed correctly.
onny commented
@st3iny I'm unable to reproduce the issue. Here is my script:
const ICAL = require('ical.js');
var iCalendarData = [
'BEGIN:VCALENDAR',
'CALSCALE:GREGORIAN',
'PRODID:-//Example Inc.//Example Calendar//EN',
'VERSION:2.0',
'BEGIN:VEVENT',
'DTSTAMP:20080205T191224Z',
'DTSTART:20081006',
'SUMMARY:Planning meeting',
'ATTENDEE;CN=MyGroup;CUTYPE=GROUP;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANTRSVP=TRUE;SCHEDULE-STATUS1.1:mailto:mygroup@localhost',
'ATTENDEE;MEMBER="mailto:mygroup@localhost","mailto:mygroup2@localhost","mailto:mygroup3@localhost";CN=user1;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;LANGUAGE=en;SCHEDULE-STATUS=1.1:mailto:user1@localhost',
'ATTENDEE;MEMBER="mailto:mygroup@localhost","mailto:mygroup2@localhost","mailto:mygroup3@localhost";CN=user2;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;LANGUAGE=en;SCHEDULE-STATUS=1.1:mailto:user2@localhost',
'ORGANIZER;CN=admin:mailto:admin@localhost',
'UID:4088E990AD89CB3DBB484909',
'END:VEVENT',
'END:VCALENDAR'
].join("\r\n");
var jcalData = ICAL.parse(iCalendarData);
var vcalendar = new ICAL.Component(jcalData);
var vevent = vcalendar.getFirstSubcomponent('vevent');
var attendees = vevent.getAllProperties('attendee');
attendees.forEach(function(attendee) {
console.log('Attendee:', attendee);
});
Here is the result of one attendee:
Attendee: <ref *1> e {
_parent: t {
_hydratedPropertyCount: 3,
_hydratedComponentCount: 0,
_timezoneCache: null,
jCal: [ 'vevent', [Array], [] ],
parent: t {
_hydratedPropertyCount: 0,
_hydratedComponentCount: 1,
_timezoneCache: Map(0) {},
jCal: [Array],
parent: null,
_components: [Array]
},
_properties: [ <3 empty items>, [e], [e], [Circular *1] ]
},
jCal: [
'attendee',
{
member: [Array],
cn: 'user2',
cutype: 'INDIVIDUAL',
partstat: 'NEEDS-ACTION',
role: 'REQ-PARTICIPANT',
rsvp: 'TRUE',
language: 'en',
'schedule-status': '1.1'
},
'cal-address',
'mailto:user2@localhost'
],
isDecorated: false,
isMultiValue: false,
isStructuredValue: false
}
st3iny commented
Thanks for investigating further.
It seems to break only if the MEMBER
parameter is the last one before the actual value of the ATTENDEE
property.
Take a look at the following example:
const ICAL = require('ical.js');
var iCalendarData = [
'ATTENDEE;MEMBER="mailto:mygroup@localhost","mailto:mygroup2@localhost","mailto:mygroup3@localhost";CN=user1;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;LANGUAGE=en;SCHEDULE-STATUS=1.1:mailto:user1@localhost',
'ATTENDEE;MEMBER="mailto:mygroup@localhost","mailto:mygroup2@localhost","mailto:mygroup3@localhost":mailto:user2@localhost',
]
for (const attendeeIcs of iCalendarData) {
const attendee = ICAL.Property.fromString(attendeeIcs);
console.log('expected:', attendeeIcs)
console.log('actual: ', attendee.toICALString())
console.log('equal? ', attendee.toICALString() === attendeeIcs)
console.log('--------------------------------------------------')
}
It will yield:
expected: ATTENDEE;MEMBER="mailto:mygroup@localhost","mailto:mygroup2@localhost","mailto:mygroup3@localhost";CN=user1;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;LANGUAGE=en;SCHEDULE-STATUS=1.1:mailto:user1@localhost
actual: ATTENDEE;MEMBER="mailto:mygroup@localhost","mailto:mygroup2@localhost","mailto:mygroup3@localhost";CN=user1;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;LANGUAGE=en;SCHEDULE-STATUS=1.1:mailto:user1@localhost
equal? true
--------------------------------------------------
expected: ATTENDEE;MEMBER="mailto:mygroup@localhost","mailto:mygroup2@localhost","mailto:mygroup3@localhost":mailto:user2@localhost
actual: ATTENDEE;MEMBER="mailto:mygroup@localhost","mailto:mygroup2@localhost","mailto:mygroup3@localhost":mygroup@localhost","mailto:mygroup2@localhost","mailto:mygroup3@localhost":mailto:user2@localhost
equal? false
--------------------------------------------------