fix: decode escaped colons in imported vcard notes

This commit is contained in:
Linus Rath
2026-10-29 19:52:28 +01:00
parent cd6724f754
commit 9d547b9d16
2 changed files with 31 additions and 5 deletions
+26
View File
@@ -74,6 +74,32 @@ describe('parseVCard', () => {
expect(note).toBe('line one\nline two, still going'); expect(note).toBe('line one\nline two, still going');
}); });
it('decodes the escaped colons Google Contacts writes (#1438)', () => {
const vcf = [
'BEGIN:VCARD',
'VERSION:3.0',
'FN:Test Example',
'NOTE:Reference\\: fictional value\\nSecond line\\, with comma\\; and semicolon',
'END:VCARD',
].join('\r\n');
const [card] = parseVCard(vcf);
expect(Object.values(card.notes ?? {})[0]?.note).toBe(
'Reference: fictional value\nSecond line, with comma; and semicolon',
);
});
it('keeps an escaped backslash literal when an escapable character follows', () => {
const vcf = [
'BEGIN:VCARD',
'VERSION:3.0',
'FN:Paths',
'NOTE:C:\\\\new\\\\:dir\\\\\\:x\\\\\\\\',
'END:VCARD',
].join('\r\n');
const [card] = parseVCard(vcf);
expect(Object.values(card.notes ?? {})[0]?.note).toBe('C:\\new\\:dir\\:x\\\\');
});
it('parses CATEGORIES into keywords', () => { it('parses CATEGORIES into keywords', () => {
const vcf = 'BEGIN:VCARD\r\nVERSION:3.0\r\nFN:Tagged\r\nEMAIL:t@x.com\r\nCATEGORIES:Friends,VIP\r\nEND:VCARD'; const vcf = 'BEGIN:VCARD\r\nVERSION:3.0\r\nFN:Tagged\r\nEMAIL:t@x.com\r\nCATEGORIES:Friends,VIP\r\nEND:VCARD';
const [card] = parseVCard(vcf); const [card] = parseVCard(vcf);
+5 -5
View File
@@ -252,12 +252,12 @@ function decodeQuotedPrintable(input: string, charset?: string): string {
return decodeBytes(bytes, charset); return decodeBytes(bytes, charset);
} }
// One pass, so an escaped backslash can't pair up with the next character.
// \: isn't a vCard escape, but Google Contacts writes it.
function decodeValue(raw: string): string { function decodeValue(raw: string): string {
return raw return raw.replace(/\\([\\,;:nN])/g, (_, c: string) =>
.replace(/\\n/gi, '\n') c === 'n' || c === 'N' ? '\n' : c,
.replace(/\\,/g, ',') );
.replace(/\\;/g, ';')
.replace(/\\\\/g, '\\');
} }
function encodeValue(val: string): string { function encodeValue(val: string): string {