CalDAV: HTML-Entities in Terminfeldern dekodieren
Manche Kalender speichern SUMMARY/LOCATION/DESCRIPTION mit wörtlichen HTML-Entities (z. B. "Vorstellungsgespräch – dot.haus"). Diese wurden roh angezeigt. Beim Parsen werden sie jetzt in echte Zeichen dekodiert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+18
-3
@@ -122,6 +122,21 @@ function unescapeText(s) {
|
|||||||
.replace(/\\\\/g, '\\');
|
.replace(/\\\\/g, '\\');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some clients store SUMMARY/LOCATION/DESCRIPTION with HTML entities baked in
|
||||||
|
// (e.g. "Vorstellungsgespräch – dot.haus"). iCal text is plain, so
|
||||||
|
// decode them back to real characters before we hand the value on.
|
||||||
|
function decodeHtmlEntities(s) {
|
||||||
|
return String(s == null ? '' : s)
|
||||||
|
.replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(parseInt(n, 10)))
|
||||||
|
.replace(/&#x([0-9a-fA-F]+);/g, (_, n) => String.fromCodePoint(parseInt(n, 16)))
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, "'")
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/ /g, ' ')
|
||||||
|
.replace(/&/g, '&');
|
||||||
|
}
|
||||||
|
|
||||||
// Fold a content line to <=75 octets per RFC 5545 (CRLF + single space).
|
// Fold a content line to <=75 octets per RFC 5545 (CRLF + single space).
|
||||||
function fold(line) {
|
function fold(line) {
|
||||||
if (Buffer.byteLength(line, 'utf8') <= 75) return line;
|
if (Buffer.byteLength(line, 'utf8') <= 75) return line;
|
||||||
@@ -204,9 +219,9 @@ function parseVEvent(ics) {
|
|||||||
paramParts.forEach((p) => { const [k, v] = p.split('='); if (k) params[k.toUpperCase()] = v; });
|
paramParts.forEach((p) => { const [k, v] = p.split('='); if (k) params[k.toUpperCase()] = v; });
|
||||||
const key = name.toUpperCase();
|
const key = name.toUpperCase();
|
||||||
if (key === 'UID') ev.uid = value.trim();
|
if (key === 'UID') ev.uid = value.trim();
|
||||||
else if (key === 'SUMMARY') ev.summary = unescapeText(value);
|
else if (key === 'SUMMARY') ev.summary = decodeHtmlEntities(unescapeText(value));
|
||||||
else if (key === 'LOCATION') ev.location = unescapeText(value);
|
else if (key === 'LOCATION') ev.location = decodeHtmlEntities(unescapeText(value));
|
||||||
else if (key === 'DESCRIPTION') ev.description = unescapeText(value);
|
else if (key === 'DESCRIPTION') ev.description = decodeHtmlEntities(unescapeText(value));
|
||||||
else if (key === 'DTSTART') { const r = icsToDate(value, params); ev.start = r.date; ev.allDay = r.allDay; }
|
else if (key === 'DTSTART') { const r = icsToDate(value, params); ev.start = r.date; ev.allDay = r.allDay; }
|
||||||
else if (key === 'DTEND') { const r = icsToDate(value, params); ev.end = r.date; }
|
else if (key === 'DTEND') { const r = icsToDate(value, params); ev.end = r.date; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user