[d565449] | 1 | import { pctEncChar, pctDecChars, unescapeComponent } from "../uri";
|
---|
| 2 | import punycode from "punycode";
|
---|
| 3 | import { merge, subexp, toUpperCase, toArray } from "../util";
|
---|
| 4 | const O = {};
|
---|
| 5 | const isIRI = true;
|
---|
| 6 | //RFC 3986
|
---|
| 7 | const UNRESERVED$$ = "[A-Za-z0-9\\-\\.\\_\\~" + (isIRI ? "\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF" : "") + "]";
|
---|
| 8 | const HEXDIG$$ = "[0-9A-Fa-f]"; //case-insensitive
|
---|
| 9 | const PCT_ENCODED$ = subexp(subexp("%[EFef]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%[89A-Fa-f]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%" + HEXDIG$$ + HEXDIG$$)); //expanded
|
---|
| 10 | //RFC 5322, except these symbols as per RFC 6068: @ : / ? # [ ] & ; =
|
---|
| 11 | //const ATEXT$$ = "[A-Za-z0-9\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\_\\`\\{\\|\\}\\~]";
|
---|
| 12 | //const WSP$$ = "[\\x20\\x09]";
|
---|
| 13 | //const OBS_QTEXT$$ = "[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]"; //(%d1-8 / %d11-12 / %d14-31 / %d127)
|
---|
| 14 | //const QTEXT$$ = merge("[\\x21\\x23-\\x5B\\x5D-\\x7E]", OBS_QTEXT$$); //%d33 / %d35-91 / %d93-126 / obs-qtext
|
---|
| 15 | //const VCHAR$$ = "[\\x21-\\x7E]";
|
---|
| 16 | //const WSP$$ = "[\\x20\\x09]";
|
---|
| 17 | //const OBS_QP$ = subexp("\\\\" + merge("[\\x00\\x0D\\x0A]", OBS_QTEXT$$)); //%d0 / CR / LF / obs-qtext
|
---|
| 18 | //const FWS$ = subexp(subexp(WSP$$ + "*" + "\\x0D\\x0A") + "?" + WSP$$ + "+");
|
---|
| 19 | //const QUOTED_PAIR$ = subexp(subexp("\\\\" + subexp(VCHAR$$ + "|" + WSP$$)) + "|" + OBS_QP$);
|
---|
| 20 | //const QUOTED_STRING$ = subexp('\\"' + subexp(FWS$ + "?" + QCONTENT$) + "*" + FWS$ + "?" + '\\"');
|
---|
| 21 | const ATEXT$$ = "[A-Za-z0-9\\!\\$\\%\\'\\*\\+\\-\\^\\_\\`\\{\\|\\}\\~]";
|
---|
| 22 | const QTEXT$$ = "[\\!\\$\\%\\'\\(\\)\\*\\+\\,\\-\\.0-9\\<\\>A-Z\\x5E-\\x7E]";
|
---|
| 23 | const VCHAR$$ = merge(QTEXT$$, "[\\\"\\\\]");
|
---|
| 24 | const DOT_ATOM_TEXT$ = subexp(ATEXT$$ + "+" + subexp("\\." + ATEXT$$ + "+") + "*");
|
---|
| 25 | const QUOTED_PAIR$ = subexp("\\\\" + VCHAR$$);
|
---|
| 26 | const QCONTENT$ = subexp(QTEXT$$ + "|" + QUOTED_PAIR$);
|
---|
| 27 | const QUOTED_STRING$ = subexp('\\"' + QCONTENT$ + "*" + '\\"');
|
---|
| 28 | //RFC 6068
|
---|
| 29 | const DTEXT_NO_OBS$$ = "[\\x21-\\x5A\\x5E-\\x7E]"; //%d33-90 / %d94-126
|
---|
| 30 | const SOME_DELIMS$$ = "[\\!\\$\\'\\(\\)\\*\\+\\,\\;\\:\\@]";
|
---|
| 31 | const QCHAR$ = subexp(UNRESERVED$$ + "|" + PCT_ENCODED$ + "|" + SOME_DELIMS$$);
|
---|
| 32 | const DOMAIN$ = subexp(DOT_ATOM_TEXT$ + "|" + "\\[" + DTEXT_NO_OBS$$ + "*" + "\\]");
|
---|
| 33 | const LOCAL_PART$ = subexp(DOT_ATOM_TEXT$ + "|" + QUOTED_STRING$);
|
---|
| 34 | const ADDR_SPEC$ = subexp(LOCAL_PART$ + "\\@" + DOMAIN$);
|
---|
| 35 | const TO$ = subexp(ADDR_SPEC$ + subexp("\\," + ADDR_SPEC$) + "*");
|
---|
| 36 | const HFNAME$ = subexp(QCHAR$ + "*");
|
---|
| 37 | const HFVALUE$ = HFNAME$;
|
---|
| 38 | const HFIELD$ = subexp(HFNAME$ + "\\=" + HFVALUE$);
|
---|
| 39 | const HFIELDS2$ = subexp(HFIELD$ + subexp("\\&" + HFIELD$) + "*");
|
---|
| 40 | const HFIELDS$ = subexp("\\?" + HFIELDS2$);
|
---|
| 41 | const MAILTO_URI = new RegExp("^mailto\\:" + TO$ + "?" + HFIELDS$ + "?$");
|
---|
| 42 | const UNRESERVED = new RegExp(UNRESERVED$$, "g");
|
---|
| 43 | const PCT_ENCODED = new RegExp(PCT_ENCODED$, "g");
|
---|
| 44 | const NOT_LOCAL_PART = new RegExp(merge("[^]", ATEXT$$, "[\\.]", '[\\"]', VCHAR$$), "g");
|
---|
| 45 | const NOT_DOMAIN = new RegExp(merge("[^]", ATEXT$$, "[\\.]", "[\\[]", DTEXT_NO_OBS$$, "[\\]]"), "g");
|
---|
| 46 | const NOT_HFNAME = new RegExp(merge("[^]", UNRESERVED$$, SOME_DELIMS$$), "g");
|
---|
| 47 | const NOT_HFVALUE = NOT_HFNAME;
|
---|
| 48 | const TO = new RegExp("^" + TO$ + "$");
|
---|
| 49 | const HFIELDS = new RegExp("^" + HFIELDS2$ + "$");
|
---|
| 50 | function decodeUnreserved(str) {
|
---|
| 51 | const decStr = pctDecChars(str);
|
---|
| 52 | return (!decStr.match(UNRESERVED) ? str : decStr);
|
---|
| 53 | }
|
---|
| 54 | const handler = {
|
---|
| 55 | scheme: "mailto",
|
---|
| 56 | parse: function (components, options) {
|
---|
| 57 | const mailtoComponents = components;
|
---|
| 58 | const to = mailtoComponents.to = (mailtoComponents.path ? mailtoComponents.path.split(",") : []);
|
---|
| 59 | mailtoComponents.path = undefined;
|
---|
| 60 | if (mailtoComponents.query) {
|
---|
| 61 | let unknownHeaders = false;
|
---|
| 62 | const headers = {};
|
---|
| 63 | const hfields = mailtoComponents.query.split("&");
|
---|
| 64 | for (let x = 0, xl = hfields.length; x < xl; ++x) {
|
---|
| 65 | const hfield = hfields[x].split("=");
|
---|
| 66 | switch (hfield[0]) {
|
---|
| 67 | case "to":
|
---|
| 68 | const toAddrs = hfield[1].split(",");
|
---|
| 69 | for (let x = 0, xl = toAddrs.length; x < xl; ++x) {
|
---|
| 70 | to.push(toAddrs[x]);
|
---|
| 71 | }
|
---|
| 72 | break;
|
---|
| 73 | case "subject":
|
---|
| 74 | mailtoComponents.subject = unescapeComponent(hfield[1], options);
|
---|
| 75 | break;
|
---|
| 76 | case "body":
|
---|
| 77 | mailtoComponents.body = unescapeComponent(hfield[1], options);
|
---|
| 78 | break;
|
---|
| 79 | default:
|
---|
| 80 | unknownHeaders = true;
|
---|
| 81 | headers[unescapeComponent(hfield[0], options)] = unescapeComponent(hfield[1], options);
|
---|
| 82 | break;
|
---|
| 83 | }
|
---|
| 84 | }
|
---|
| 85 | if (unknownHeaders)
|
---|
| 86 | mailtoComponents.headers = headers;
|
---|
| 87 | }
|
---|
| 88 | mailtoComponents.query = undefined;
|
---|
| 89 | for (let x = 0, xl = to.length; x < xl; ++x) {
|
---|
| 90 | const addr = to[x].split("@");
|
---|
| 91 | addr[0] = unescapeComponent(addr[0]);
|
---|
| 92 | if (!options.unicodeSupport) {
|
---|
| 93 | //convert Unicode IDN -> ASCII IDN
|
---|
| 94 | try {
|
---|
| 95 | addr[1] = punycode.toASCII(unescapeComponent(addr[1], options).toLowerCase());
|
---|
| 96 | }
|
---|
| 97 | catch (e) {
|
---|
| 98 | mailtoComponents.error = mailtoComponents.error || "Email address's domain name can not be converted to ASCII via punycode: " + e;
|
---|
| 99 | }
|
---|
| 100 | }
|
---|
| 101 | else {
|
---|
| 102 | addr[1] = unescapeComponent(addr[1], options).toLowerCase();
|
---|
| 103 | }
|
---|
| 104 | to[x] = addr.join("@");
|
---|
| 105 | }
|
---|
| 106 | return mailtoComponents;
|
---|
| 107 | },
|
---|
| 108 | serialize: function (mailtoComponents, options) {
|
---|
| 109 | const components = mailtoComponents;
|
---|
| 110 | const to = toArray(mailtoComponents.to);
|
---|
| 111 | if (to) {
|
---|
| 112 | for (let x = 0, xl = to.length; x < xl; ++x) {
|
---|
| 113 | const toAddr = String(to[x]);
|
---|
| 114 | const atIdx = toAddr.lastIndexOf("@");
|
---|
| 115 | const localPart = (toAddr.slice(0, atIdx)).replace(PCT_ENCODED, decodeUnreserved).replace(PCT_ENCODED, toUpperCase).replace(NOT_LOCAL_PART, pctEncChar);
|
---|
| 116 | let domain = toAddr.slice(atIdx + 1);
|
---|
| 117 | //convert IDN via punycode
|
---|
| 118 | try {
|
---|
| 119 | domain = (!options.iri ? punycode.toASCII(unescapeComponent(domain, options).toLowerCase()) : punycode.toUnicode(domain));
|
---|
| 120 | }
|
---|
| 121 | catch (e) {
|
---|
| 122 | components.error = components.error || "Email address's domain name can not be converted to " + (!options.iri ? "ASCII" : "Unicode") + " via punycode: " + e;
|
---|
| 123 | }
|
---|
| 124 | to[x] = localPart + "@" + domain;
|
---|
| 125 | }
|
---|
| 126 | components.path = to.join(",");
|
---|
| 127 | }
|
---|
| 128 | const headers = mailtoComponents.headers = mailtoComponents.headers || {};
|
---|
| 129 | if (mailtoComponents.subject)
|
---|
| 130 | headers["subject"] = mailtoComponents.subject;
|
---|
| 131 | if (mailtoComponents.body)
|
---|
| 132 | headers["body"] = mailtoComponents.body;
|
---|
| 133 | const fields = [];
|
---|
| 134 | for (const name in headers) {
|
---|
| 135 | if (headers[name] !== O[name]) {
|
---|
| 136 | fields.push(name.replace(PCT_ENCODED, decodeUnreserved).replace(PCT_ENCODED, toUpperCase).replace(NOT_HFNAME, pctEncChar) +
|
---|
| 137 | "=" +
|
---|
| 138 | headers[name].replace(PCT_ENCODED, decodeUnreserved).replace(PCT_ENCODED, toUpperCase).replace(NOT_HFVALUE, pctEncChar));
|
---|
| 139 | }
|
---|
| 140 | }
|
---|
| 141 | if (fields.length) {
|
---|
| 142 | components.query = fields.join("&");
|
---|
| 143 | }
|
---|
| 144 | return components;
|
---|
| 145 | }
|
---|
| 146 | };
|
---|
| 147 | export default handler;
|
---|
| 148 | //# sourceMappingURL=mailto.js.map |
---|