mailto.js (7746B)
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