Skip to content

Commit

Permalink
fix: Do not escape quotes when not required
Browse files Browse the repository at this point in the history
close #377
  • Loading branch information
sebbo2002 committed Apr 27, 2022
1 parent d73ed8b commit 08a4d62
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 34 deletions.
8 changes: 4 additions & 4 deletions src/alarm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,21 +597,21 @@ export default class ICalAlarm {

// ATTACH
if (this.data.type === 'audio' && this.data.attach && this.data.attach.mime) {
g += 'ATTACH;FMTTYPE=' + this.data.attach.mime + ':' + this.data.attach.uri + '\r\n';
g += 'ATTACH;FMTTYPE=' + escape(this.data.attach.mime, false) + ':' + escape(this.data.attach.uri, false) + '\r\n';
}
else if (this.data.type === 'audio' && this.data.attach) {
g += 'ATTACH;VALUE=URI:' + this.data.attach.uri + '\r\n';
g += 'ATTACH;VALUE=URI:' + escape(this.data.attach.uri, false) + '\r\n';
}
else if (this.data.type === 'audio') {
g += 'ATTACH;VALUE=URI:Basso\r\n';
}

// DESCRIPTION
if (this.data.type === 'display' && this.data.description) {
g += 'DESCRIPTION:' + escape(this.data.description) + '\r\n';
g += 'DESCRIPTION:' + escape(this.data.description, false) + '\r\n';
}
else if (this.data.type === 'display') {
g += 'DESCRIPTION:' + escape(this.event.summary()) + '\r\n';
g += 'DESCRIPTION:' + escape(this.event.summary(), false) + '\r\n';
}

// CUSTOM X ATTRIBUTES
Expand Down
8 changes: 4 additions & 4 deletions src/attendee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,22 +592,22 @@ export default class ICalAttendee {

// CN / Name
if (this.data.name) {
g += ';CN="' + escape(this.data.name) + '"';
g += ';CN="' + escape(this.data.name, true) + '"';
}

// EMAIL
if (this.data.email && this.data.mailto) {
g += ';EMAIL=' + escape(this.data.email);
g += ';EMAIL=' + escape(this.data.email, false);
}

// CUSTOM X ATTRIBUTES
if(this.data.x.length) {
g += ';' + this.data.x
.map(([key, value]) => key.toUpperCase() + '=' + escape(value))
.map(([key, value]) => key.toUpperCase() + '=' + escape(value, false))
.join(';');
}

g += ':MAILTO:' + escape(this.data.mailto || this.data.email) + '\r\n';
g += ':MAILTO:' + escape(this.data.mailto || this.data.email, false) + '\r\n';

return g;
}
Expand Down
2 changes: 1 addition & 1 deletion src/category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,6 @@ export default class ICalCategory {
throw new Error('No value for `name` in ICalCategory given!');
}

return escape(this.data.name);
return escape(this.data.name, false);
}
}
35 changes: 19 additions & 16 deletions src/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1545,55 +1545,58 @@ export default class ICalEvent {
}

// SUMMARY
g += 'SUMMARY:' + escape(this.data.summary) + '\r\n';
g += 'SUMMARY:' + escape(this.data.summary, false) + '\r\n';

// TRANSPARENCY
if (this.data.transparency) {
g += 'TRANSP:' + escape(this.data.transparency) + '\r\n';
g += 'TRANSP:' + escape(this.data.transparency, false) + '\r\n';
}

// LOCATION
if (this.data.location?.title) {
g += 'LOCATION:' + escape(
this.data.location.title +
(this.data.location.address ? '\n' + this.data.location.address : '')
(this.data.location.address ? '\n' + this.data.location.address : ''),
false
) + '\r\n';

if (this.data.location.radius && this.data.location.geo) {
g += 'X-APPLE-STRUCTURED-LOCATION;VALUE=URI;' +
(this.data.location.address ? 'X-ADDRESS=' + escape(this.data.location.address) + ';' : '') +
'X-APPLE-RADIUS=' + escape(this.data.location.radius) + ';' +
'X-TITLE=' + escape(this.data.location.title) +
':geo:' + escape(this.data.location.geo?.lat) + ',' + escape(this.data.location.geo?.lon) + '\r\n';
(this.data.location.address ? 'X-ADDRESS=' + escape(this.data.location.address, false) + ';' : '') +
'X-APPLE-RADIUS=' + escape(this.data.location.radius, false) + ';' +
'X-TITLE=' + escape(this.data.location.title, false) +
':geo:' + escape(this.data.location.geo?.lat, false) + ',' +
escape(this.data.location.geo?.lon, false) + '\r\n';
}

if (this.data.location.geo) {
g += 'GEO:' + escape(this.data.location.geo?.lat) + ';' + escape(this.data.location.geo?.lon) + '\r\n';
g += 'GEO:' + escape(this.data.location.geo?.lat, false) + ';' +
escape(this.data.location.geo?.lon, false) + '\r\n';
}
}

// DESCRIPTION
if (this.data.description) {
g += 'DESCRIPTION:' + escape(this.data.description.plain) + '\r\n';
g += 'DESCRIPTION:' + escape(this.data.description.plain, false) + '\r\n';

// HTML DESCRIPTION
if (this.data.description.html) {
g += 'X-ALT-DESC;FMTTYPE=text/html:' + escape(this.data.description.html) + '\r\n';
g += 'X-ALT-DESC;FMTTYPE=text/html:' + escape(this.data.description.html, false) + '\r\n';
}
}

// ORGANIZER
if (this.data.organizer) {
g += 'ORGANIZER;CN="' + escape(this.data.organizer.name) + '"';
g += 'ORGANIZER;CN="' + escape(this.data.organizer.name, true) + '"';

if (this.data.organizer.sentBy) {
g += ';SENT-BY="mailto:' + escape(this.data.organizer.sentBy) + '"';
g += ';SENT-BY="mailto:' + escape(this.data.organizer.sentBy, true) + '"';
}
if (this.data.organizer.email && this.data.organizer.mailto) {
g += ';EMAIL=' + escape(this.data.organizer.email);
g += ';EMAIL=' + escape(this.data.organizer.email, false);
}
if(this.data.organizer.email) {
g += ':mailto:' + escape(this.data.organizer.mailto || this.data.organizer.email);
g += ':mailto:' + escape(this.data.organizer.mailto || this.data.organizer.email, false);
}
g += '\r\n';
}
Expand All @@ -1617,13 +1620,13 @@ export default class ICalEvent {

// URL
if (this.data.url) {
g += 'URL;VALUE=URI:' + escape(this.data.url) + '\r\n';
g += 'URL;VALUE=URI:' + escape(this.data.url, false) + '\r\n';
}

// ATTACHMENT
if (this.data.attachments.length > 0) {
this.data.attachments.forEach(url => {
g += 'ATTACH:' + escape(url) + '\r\n';
g += 'ATTACH:' + escape(url, false) + '\r\n';
});
}

Expand Down
6 changes: 3 additions & 3 deletions src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ export function formatDateTZ (timezone: string | null, property: string, date: I
/**
* Escapes special characters in the given string
*/
export function escape (str: string | unknown): string {
return String(str).replace(/[\\;,"]/g, function (match) {
export function escape (str: string | unknown, inQuotes: boolean): string {
return String(str).replace(inQuotes ? /[\\;,"]/g : /[\\;,]/g, function (match) {
return '\\' + match;
}).replace(/(?:\r\n|\r|\n)/g, '\\n');
}
Expand Down Expand Up @@ -198,7 +198,7 @@ export function addOrGetCustomAttributes (data: {x: [string, string][]}, keyOrAr

export function generateCustomAttributes (data: {x: [string, string][]}): string {
const str = data.x
.map(([key, value]) => key.toUpperCase() + ':' + escape(value))
.map(([key, value]) => key.toUpperCase() + ':' + escape(value, false))
.join('\r\n');
return str.length ? str + '\r\n' : '';
}
Expand Down
29 changes: 29 additions & 0 deletions test/issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,33 @@ describe('Issues', function () {
].join('\r\n'));
});
});

describe('Issue #377', function () {
it('should not escape quotes in summary', function () {
const calendar = ical({
events: [
{
id: 'foo',
start: new Date('2020-08-13T00:00:00-05:00'),
stamp: new Date('2020-08-13T00:00:00-05:00'),
summary:'My "quoted" string'
}
]
});

assert.strictEqual(calendar.toString(), [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//sebbo.net//ical-generator//EN',
'BEGIN:VEVENT',
'UID:foo',
'SEQUENCE:0',
'DTSTAMP:20200813T050000Z',
'DTSTART:20200813T050000Z',
'SUMMARY:My "quoted" string',
'END:VEVENT',
'END:VCALENDAR'
].join('\r\n'));
});
});
});
24 changes: 18 additions & 6 deletions test/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,40 +162,52 @@ describe('ICalTools', function () {
describe('escape()', function () {
it('should escape \\', function () {
assert.strictEqual(
escape('Lorem \\ipsum'),
escape('Lorem \\ipsum', false),
'Lorem \\\\ipsum'
);
});
it('should escape ;', function () {
assert.strictEqual(
escape('Lorem ;ipsum'),
escape('Lorem ;ipsum', false),
'Lorem \\;ipsum'
);
});
it('should escape ,', function () {
assert.strictEqual(
escape('Lorem, ipsum'),
escape('Lorem, ipsum', false),
'Lorem\\, ipsum'
);
});
it('should escape \\r', function () {
assert.strictEqual(
escape('Lorem \ripsum'),
escape('Lorem \ripsum', false),
'Lorem \\nipsum'
);
});
it('should escape \\n', function () {
assert.strictEqual(
escape('Lorem \nipsum'),
escape('Lorem \nipsum', false),
'Lorem \\nipsum'
);
});
it('should escape \\r\\n', function () {
assert.strictEqual(
escape('Lorem \r\nipsum'),
escape('Lorem \r\nipsum', false),
'Lorem \\nipsum'
);
});
it('should escape " in text when inQuotes = true', function () {
assert.strictEqual(
escape('Lorem "ipsum', true),
'Lorem \\"ipsum'
);
});
it('should not escape " in text when inQuotes = false', function () {
assert.strictEqual(
escape('Lorem "ipsum', false),
'Lorem "ipsum'
);
});
});

describe('foldLines()', function () {
Expand Down

0 comments on commit 08a4d62

Please sign in to comment.