diff options
Diffstat (limited to 'content/includes/tools.js')
-rw-r--r-- | content/includes/tools.js | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/content/includes/tools.js b/content/includes/tools.js new file mode 100644 index 0000000..df51d79 --- /dev/null +++ b/content/includes/tools.js @@ -0,0 +1,198 @@ +/* + * This file is part of DAV-4-TbSync. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +"use strict"; + +var tools = { + + //* * * * * * * * * * * * * + //* UTILS + //* * * * * * * * * * * * * + + /** + * Removes XML-invalid characters from a string. + * @param {string} string - a string potentially containing XML-invalid characters, such as non-UTF8 characters, STX, EOX and so on. + * @param {boolean} removeDiscouragedChars - a string potentially containing XML-invalid characters, such as non-UTF8 characters, STX, EOX and so on. + * @returns : a sanitized string without all the XML-invalid characters. + * + * Source: https://www.ryadel.com/en/javascript-remove-xml-invalid-chars-characters-string-utf8-unicode-regex/ + */ + removeXMLInvalidChars: function (string, removeDiscouragedChars = true) + { + // remove everything forbidden by XML 1.0 specifications, plus the unicode replacement character U+FFFD + var regex = /((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))/g; + string = string.replace(regex, ""); + + if (removeDiscouragedChars) { + // remove everything not suggested by XML 1.0 specifications + regex = new RegExp( + "([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|(?:\\uD83F[\\uDFFE\\uDFFF])|(?:\\uD87F[\\uDF"+ + "FE\\uDFFF])|(?:\\uD8BF[\\uDFFE\\uDFFF])|(?:\\uD8FF[\\uDFFE\\uDFFF])|(?:\\uD93F[\\uDFFE\\uD"+ + "FFF])|(?:\\uD97F[\\uDFFE\\uDFFF])|(?:\\uD9BF[\\uDFFE\\uDFFF])|(?:\\uD9FF[\\uDFFE\\uDFFF])"+ + "|(?:\\uDA3F[\\uDFFE\\uDFFF])|(?:\\uDA7F[\\uDFFE\\uDFFF])|(?:\\uDABF[\\uDFFE\\uDFFF])|(?:\\"+ + "uDAFF[\\uDFFE\\uDFFF])|(?:\\uDB3F[\\uDFFE\\uDFFF])|(?:\\uDB7F[\\uDFFE\\uDFFF])|(?:\\uDBBF"+ + "[\\uDFFE\\uDFFF])|(?:\\uDBFF[\\uDFFE\\uDFFF])(?:[\\0-\\t\\x0B\\f\\x0E-\\u2027\\u202A-\\uD7FF\\"+ + "uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|"+ + "(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))", "g"); + string = string.replace(regex, ""); + } + + return string; + }, + + xmlns: function (ns) { + let _xmlns = []; + for (let i=0; i < ns.length; i++) { + _xmlns.push('xmlns:'+ns[i]+'="'+dav.sync.ns[ns[i]]+'"'); + } + return _xmlns.join(" "); + }, + + parseUri: function (aUri) { + let uri; + try { + // Test if the entered uri can be parsed. + uri = Services.io.newURI(aUri, null, null); + } catch (ex) { + throw new Error("invalid-calendar-url"); + } + return uri; + }, + + parseVcardDateTime: function ( newServerValue, metadata ) { + if (!newServerValue) { + return false; + } + + /* + ** This accepts RFC2426 BDAY values (with/without hyphens), + ** though TB doesn't handle the time part of date-times, so we discard it. + */ + let bday = newServerValue.match( /^(\d{4})-?(\d{2})-?(\d{2})/ ); + if (!bday) { + return false; + } + + /* + ** Apple Contacts shoehorns date with missing year into vcard3 thus: BDAY;X-APPLE-OMIT-YEAR=1604:1604-03-15 + ** Later in vcard4, it will be represented as BDAY:--0315 + */ + if (metadata + && metadata['x-apple-omit-year'] + && metadata['x-apple-omit-year'] == bday[1]) { + bday[1] = ''; + } + return bday; + }, + + //* * * * * * * * * * * * * * + //* EVALUATE XML RESPONSES * + //* * * * * * * * * * * * * * + + convertToXML: function(text) { + //try to convert response body to xml + let xml = null; + let oParser = new DOMParser(); + try { + xml = oParser.parseFromString(dav.tools.removeXMLInvalidChars(text), "application/xml"); + } catch (e) { + //however, domparser does not throw an error, it returns an error document + //https://developer.mozilla.org/de/docs/Web/API/DOMParser + xml = null; + } + //check if xml is error document + if (xml && xml.documentElement.nodeName == "parsererror") { + xml = null; + } + + return xml; + }, + + evaluateNode: function (_node, path) { + let node = _node; + let valid = false; + + for (let i=0; i < path.length; i++) { + + let children = node.children; + valid = false; + + for (let c=0; c < children.length; c++) { + if (children[c].localName == path[i][1] && children[c].namespaceURI == dav.sync.ns[path[i][0]]) { + node = children[c]; + valid = true; + break; + } + } + + if (!valid) { + //none of the children matched the path abort + return null; + } + } + + if (valid) return node; + return null; + }, + + hrefMatch:function (_requestHref, _responseHref) { + if (_requestHref === null) + return true; + + let requestHref = _requestHref; + let responseHref = _responseHref; + while (requestHref.endsWith("/")) { requestHref = requestHref.slice(0,-1); } + while (responseHref.endsWith("/")) { responseHref = responseHref.slice(0,-1); } + if (requestHref.endsWith(responseHref) || decodeURIComponent(requestHref).endsWith(responseHref) || requestHref.endsWith(decodeURIComponent(responseHref))) + return true; + + return false; + }, + + getNodeTextContentFromMultiResponse: function (response, path, href = null, status = ["200"]) { + for (let i=0; i < response.multi.length; i++) { + let node = dav.tools.evaluateNode(response.multi[i].node, path); + if (node !== null && dav.tools.hrefMatch(href, response.multi[i].href) && status.includes(response.multi[i].status)) { + return node.textContent; + } + } + return null; + }, + + getNodesTextContentFromMultiResponse: function (response, path, href = null, status = "200") { + //remove last element from path + let lastPathElement = path.pop(); + let rv = []; + + for (let i=0; i < response.multi.length; i++) { + let node = dav.tools.evaluateNode(response.multi[i].node, path); + if (node !== null && dav.tools.hrefMatch(href, response.multi[i].href) && response.multi[i].status == status) { + + //get all children + let children = node.getElementsByTagNameNS(dav.sync.ns[lastPathElement[0]], lastPathElement[1]); + for (let c=0; c < children.length; c++) { + if (children[c].textContent) rv.push(children[c].textContent); + } + } + } + return rv; + }, + + getMultiGetRequest: function(hrefs) { + let request = "<card:addressbook-multiget "+dav.tools.xmlns(["d", "card"])+"><d:prop><d:getetag /><card:address-data /></d:prop>"; + let counts = 0; + for (let i=0; i < hrefs.length; i++) { + request += "<d:href>"+hrefs[i]+"</d:href>"; + counts++; + } + request += "</card:addressbook-multiget>"; + + if (counts > 0) return request; + else return null; + }, +} |