| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090 |
- // email-addresses.js - RFC 5322 email address parser
- // v 3.1.0
- //
- // http://tools.ietf.org/html/rfc5322
- //
- // This library does not validate email addresses.
- // emailAddresses attempts to parse addresses using the (fairly liberal)
- // grammar specified in RFC 5322.
- //
- // email-addresses returns {
- // ast: <an abstract syntax tree based on rfc5322>,
- // addresses: [{
- // node: <node in ast for this address>,
- // name: <display-name>,
- // address: <addr-spec>,
- // local: <local-part>,
- // domain: <domain>
- // }, ...]
- // }
- //
- // emailAddresses.parseOneAddress and emailAddresses.parseAddressList
- // work as you might expect. Try it out.
- //
- // Many thanks to Dominic Sayers and his documentation on the is_email function,
- // http://code.google.com/p/isemail/ , which helped greatly in writing this parser.
- (function (global) {
- "use strict";
- function parse5322(opts) {
- // tokenizing functions
- function inStr() { return pos < len; }
- function curTok() { return parseString[pos]; }
- function getPos() { return pos; }
- function setPos(i) { pos = i; }
- function nextTok() { pos += 1; }
- function initialize() {
- pos = 0;
- len = parseString.length;
- }
- // parser helper functions
- function o(name, value) {
- return {
- name: name,
- tokens: value || "",
- semantic: value || "",
- children: []
- };
- }
- function wrap(name, ast) {
- var n;
- if (ast === null) { return null; }
- n = o(name);
- n.tokens = ast.tokens;
- n.semantic = ast.semantic;
- n.children.push(ast);
- return n;
- }
- function add(parent, child) {
- if (child !== null) {
- parent.tokens += child.tokens;
- parent.semantic += child.semantic;
- }
- parent.children.push(child);
- return parent;
- }
- function compareToken(fxnCompare) {
- var tok;
- if (!inStr()) { return null; }
- tok = curTok();
- if (fxnCompare(tok)) {
- nextTok();
- return o('token', tok);
- }
- return null;
- }
- function literal(lit) {
- return function literalFunc() {
- return wrap('literal', compareToken(function (tok) {
- return tok === lit;
- }));
- };
- }
- function and() {
- var args = arguments;
- return function andFunc() {
- var i, s, result, start;
- start = getPos();
- s = o('and');
- for (i = 0; i < args.length; i += 1) {
- result = args[i]();
- if (result === null) {
- setPos(start);
- return null;
- }
- add(s, result);
- }
- return s;
- };
- }
- function or() {
- var args = arguments;
- return function orFunc() {
- var i, result, start;
- start = getPos();
- for (i = 0; i < args.length; i += 1) {
- result = args[i]();
- if (result !== null) {
- return result;
- }
- setPos(start);
- }
- return null;
- };
- }
- function opt(prod) {
- return function optFunc() {
- var result, start;
- start = getPos();
- result = prod();
- if (result !== null) {
- return result;
- }
- else {
- setPos(start);
- return o('opt');
- }
- };
- }
- function invis(prod) {
- return function invisFunc() {
- var result = prod();
- if (result !== null) {
- result.semantic = "";
- }
- return result;
- };
- }
- function colwsp(prod) {
- return function collapseSemanticWhitespace() {
- var result = prod();
- if (result !== null && result.semantic.length > 0) {
- result.semantic = " ";
- }
- return result;
- };
- }
- function star(prod, minimum) {
- return function starFunc() {
- var s, result, count, start, min;
- start = getPos();
- s = o('star');
- count = 0;
- min = minimum === undefined ? 0 : minimum;
- while ((result = prod()) !== null) {
- count = count + 1;
- add(s, result);
- }
- if (count >= min) {
- return s;
- }
- else {
- setPos(start);
- return null;
- }
- };
- }
- // One expects names to get normalized like this:
- // " First Last " -> "First Last"
- // "First Last" -> "First Last"
- // "First Last" -> "First Last"
- function collapseWhitespace(s) {
- return s.replace(/([ \t]|\r\n)+/g, ' ').replace(/^\s*/, '').replace(/\s*$/, '');
- }
- // UTF-8 pseudo-production (RFC 6532)
- // RFC 6532 extends RFC 5322 productions to include UTF-8
- // using the following productions:
- // UTF8-non-ascii = UTF8-2 / UTF8-3 / UTF8-4
- // UTF8-2 = <Defined in Section 4 of RFC3629>
- // UTF8-3 = <Defined in Section 4 of RFC3629>
- // UTF8-4 = <Defined in Section 4 of RFC3629>
- //
- // For reference, the extended RFC 5322 productions are:
- // VCHAR =/ UTF8-non-ascii
- // ctext =/ UTF8-non-ascii
- // atext =/ UTF8-non-ascii
- // qtext =/ UTF8-non-ascii
- // dtext =/ UTF8-non-ascii
- function isUTF8NonAscii(tok) {
- // In JavaScript, we just deal directly with Unicode code points,
- // so we aren't checking individual bytes for UTF-8 encoding.
- // Just check that the character is non-ascii.
- return tok.charCodeAt(0) >= 128;
- }
- // common productions (RFC 5234)
- // http://tools.ietf.org/html/rfc5234
- // B.1. Core Rules
- // CR = %x0D
- // ; carriage return
- function cr() { return wrap('cr', literal('\r')()); }
- // CRLF = CR LF
- // ; Internet standard newline
- function crlf() { return wrap('crlf', and(cr, lf)()); }
- // DQUOTE = %x22
- // ; " (Double Quote)
- function dquote() { return wrap('dquote', literal('"')()); }
- // HTAB = %x09
- // ; horizontal tab
- function htab() { return wrap('htab', literal('\t')()); }
- // LF = %x0A
- // ; linefeed
- function lf() { return wrap('lf', literal('\n')()); }
- // SP = %x20
- function sp() { return wrap('sp', literal(' ')()); }
- // VCHAR = %x21-7E
- // ; visible (printing) characters
- function vchar() {
- return wrap('vchar', compareToken(function vcharFunc(tok) {
- var code = tok.charCodeAt(0);
- var accept = (0x21 <= code && code <= 0x7E);
- if (opts.rfc6532) {
- accept = accept || isUTF8NonAscii(tok);
- }
- return accept;
- }));
- }
- // WSP = SP / HTAB
- // ; white space
- function wsp() { return wrap('wsp', or(sp, htab)()); }
- // email productions (RFC 5322)
- // http://tools.ietf.org/html/rfc5322
- // 3.2.1. Quoted characters
- // quoted-pair = ("\" (VCHAR / WSP)) / obs-qp
- function quotedPair() {
- var qp = wrap('quoted-pair',
- or(
- and(literal('\\'), or(vchar, wsp)),
- obsQP
- )());
- if (qp === null) { return null; }
- // a quoted pair will be two characters, and the "\" character
- // should be semantically "invisible" (RFC 5322 3.2.1)
- qp.semantic = qp.semantic[1];
- return qp;
- }
- // 3.2.2. Folding White Space and Comments
- // FWS = ([*WSP CRLF] 1*WSP) / obs-FWS
- function fws() {
- return wrap('fws', or(
- obsFws,
- and(
- opt(and(
- star(wsp),
- invis(crlf)
- )),
- star(wsp, 1)
- )
- )());
- }
- // ctext = %d33-39 / ; Printable US-ASCII
- // %d42-91 / ; characters not including
- // %d93-126 / ; "(", ")", or "\"
- // obs-ctext
- function ctext() {
- return wrap('ctext', or(
- function ctextFunc1() {
- return compareToken(function ctextFunc2(tok) {
- var code = tok.charCodeAt(0);
- var accept =
- (33 <= code && code <= 39) ||
- (42 <= code && code <= 91) ||
- (93 <= code && code <= 126);
- if (opts.rfc6532) {
- accept = accept || isUTF8NonAscii(tok);
- }
- return accept;
- });
- },
- obsCtext
- )());
- }
- // ccontent = ctext / quoted-pair / comment
- function ccontent() {
- return wrap('ccontent', or(ctext, quotedPair, comment)());
- }
- // comment = "(" *([FWS] ccontent) [FWS] ")"
- function comment() {
- return wrap('comment', and(
- literal('('),
- star(and(opt(fws), ccontent)),
- opt(fws),
- literal(')')
- )());
- }
- // CFWS = (1*([FWS] comment) [FWS]) / FWS
- function cfws() {
- return wrap('cfws', or(
- and(
- star(
- and(opt(fws), comment),
- 1
- ),
- opt(fws)
- ),
- fws
- )());
- }
- // 3.2.3. Atom
- //atext = ALPHA / DIGIT / ; Printable US-ASCII
- // "!" / "#" / ; characters not including
- // "$" / "%" / ; specials. Used for atoms.
- // "&" / "'" /
- // "*" / "+" /
- // "-" / "/" /
- // "=" / "?" /
- // "^" / "_" /
- // "`" / "{" /
- // "|" / "}" /
- // "~"
- function atext() {
- return wrap('atext', compareToken(function atextFunc(tok) {
- var accept =
- ('a' <= tok && tok <= 'z') ||
- ('A' <= tok && tok <= 'Z') ||
- ('0' <= tok && tok <= '9') ||
- (['!', '#', '$', '%', '&', '\'', '*', '+', '-', '/',
- '=', '?', '^', '_', '`', '{', '|', '}', '~'].indexOf(tok) >= 0);
- if (opts.rfc6532) {
- accept = accept || isUTF8NonAscii(tok);
- }
- return accept;
- }));
- }
- // atom = [CFWS] 1*atext [CFWS]
- function atom() {
- return wrap('atom', and(colwsp(opt(cfws)), star(atext, 1), colwsp(opt(cfws)))());
- }
- // dot-atom-text = 1*atext *("." 1*atext)
- function dotAtomText() {
- var s, maybeText;
- s = wrap('dot-atom-text', star(atext, 1)());
- if (s === null) { return s; }
- maybeText = star(and(literal('.'), star(atext, 1)))();
- if (maybeText !== null) {
- add(s, maybeText);
- }
- return s;
- }
- // dot-atom = [CFWS] dot-atom-text [CFWS]
- function dotAtom() {
- return wrap('dot-atom', and(invis(opt(cfws)), dotAtomText, invis(opt(cfws)))());
- }
- // 3.2.4. Quoted Strings
- // qtext = %d33 / ; Printable US-ASCII
- // %d35-91 / ; characters not including
- // %d93-126 / ; "\" or the quote character
- // obs-qtext
- function qtext() {
- return wrap('qtext', or(
- function qtextFunc1() {
- return compareToken(function qtextFunc2(tok) {
- var code = tok.charCodeAt(0);
- var accept =
- (33 === code) ||
- (35 <= code && code <= 91) ||
- (93 <= code && code <= 126);
- if (opts.rfc6532) {
- accept = accept || isUTF8NonAscii(tok);
- }
- return accept;
- });
- },
- obsQtext
- )());
- }
- // qcontent = qtext / quoted-pair
- function qcontent() {
- return wrap('qcontent', or(qtext, quotedPair)());
- }
- // quoted-string = [CFWS]
- // DQUOTE *([FWS] qcontent) [FWS] DQUOTE
- // [CFWS]
- function quotedString() {
- return wrap('quoted-string', and(
- invis(opt(cfws)),
- invis(dquote), star(and(opt(colwsp(fws)), qcontent)), opt(invis(fws)), invis(dquote),
- invis(opt(cfws))
- )());
- }
- // 3.2.5 Miscellaneous Tokens
- // word = atom / quoted-string
- function word() {
- return wrap('word', or(atom, quotedString)());
- }
- // phrase = 1*word / obs-phrase
- function phrase() {
- return wrap('phrase', or(obsPhrase, star(word, 1))());
- }
- // 3.4. Address Specification
- // address = mailbox / group
- function address() {
- return wrap('address', or(mailbox, group)());
- }
- // mailbox = name-addr / addr-spec
- function mailbox() {
- return wrap('mailbox', or(nameAddr, addrSpec)());
- }
- // name-addr = [display-name] angle-addr
- function nameAddr() {
- return wrap('name-addr', and(opt(displayName), angleAddr)());
- }
- // angle-addr = [CFWS] "<" addr-spec ">" [CFWS] /
- // obs-angle-addr
- function angleAddr() {
- return wrap('angle-addr', or(
- and(
- invis(opt(cfws)),
- literal('<'),
- addrSpec,
- literal('>'),
- invis(opt(cfws))
- ),
- obsAngleAddr
- )());
- }
- // group = display-name ":" [group-list] ";" [CFWS]
- function group() {
- return wrap('group', and(
- displayName,
- literal(':'),
- opt(groupList),
- literal(';'),
- invis(opt(cfws))
- )());
- }
- // display-name = phrase
- function displayName() {
- return wrap('display-name', function phraseFixedSemantic() {
- var result = phrase();
- if (result !== null) {
- result.semantic = collapseWhitespace(result.semantic);
- }
- return result;
- }());
- }
- // mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list
- function mailboxList() {
- return wrap('mailbox-list', or(
- and(
- mailbox,
- star(and(literal(','), mailbox))
- ),
- obsMboxList
- )());
- }
- // address-list = (address *("," address)) / obs-addr-list
- function addressList() {
- return wrap('address-list', or(
- and(
- address,
- star(and(literal(','), address))
- ),
- obsAddrList
- )());
- }
- // group-list = mailbox-list / CFWS / obs-group-list
- function groupList() {
- return wrap('group-list', or(
- mailboxList,
- invis(cfws),
- obsGroupList
- )());
- }
- // 3.4.1 Addr-Spec Specification
- // local-part = dot-atom / quoted-string / obs-local-part
- function localPart() {
- // note: quoted-string, dotAtom are proper subsets of obs-local-part
- // so we really just have to look for obsLocalPart, if we don't care about the exact parse tree
- return wrap('local-part', or(obsLocalPart, dotAtom, quotedString)());
- }
- // dtext = %d33-90 / ; Printable US-ASCII
- // %d94-126 / ; characters not including
- // obs-dtext ; "[", "]", or "\"
- function dtext() {
- return wrap('dtext', or(
- function dtextFunc1() {
- return compareToken(function dtextFunc2(tok) {
- var code = tok.charCodeAt(0);
- var accept =
- (33 <= code && code <= 90) ||
- (94 <= code && code <= 126);
- if (opts.rfc6532) {
- accept = accept || isUTF8NonAscii(tok);
- }
- return accept;
- });
- },
- obsDtext
- )()
- );
- }
- // domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
- function domainLiteral() {
- return wrap('domain-literal', and(
- invis(opt(cfws)),
- literal('['),
- star(and(opt(fws), dtext)),
- opt(fws),
- literal(']'),
- invis(opt(cfws))
- )());
- }
- // domain = dot-atom / domain-literal / obs-domain
- function domain() {
- return wrap('domain', function domainCheckTLD() {
- var result = or(obsDomain, dotAtom, domainLiteral)();
- if (opts.rejectTLD) {
- if (result && result.semantic && result.semantic.indexOf('.') < 0) {
- return null;
- }
- }
- // strip all whitespace from domains
- if (result) {
- result.semantic = result.semantic.replace(/\s+/g, '');
- }
- return result;
- }());
- }
- // addr-spec = local-part "@" domain
- function addrSpec() {
- return wrap('addr-spec', and(
- localPart, literal('@'), domain
- )());
- }
- // 3.6.2 Originator Fields
- // Below we only parse the field body, not the name of the field
- // like "From:", "Sender:", or "Reply-To:". Other libraries that
- // parse email headers can parse those and defer to these productions
- // for the "RFC 5322" part.
- // RFC 6854 2.1. Replacement of RFC 5322, Section 3.6.2. Originator Fields
- // from = "From:" (mailbox-list / address-list) CRLF
- function fromSpec() {
- return wrap('from', or(
- mailboxList,
- addressList
- )());
- }
- // RFC 6854 2.1. Replacement of RFC 5322, Section 3.6.2. Originator Fields
- // sender = "Sender:" (mailbox / address) CRLF
- function senderSpec() {
- return wrap('sender', or(
- mailbox,
- address
- )());
- }
- // RFC 6854 2.1. Replacement of RFC 5322, Section 3.6.2. Originator Fields
- // reply-to = "Reply-To:" address-list CRLF
- function replyToSpec() {
- return wrap('reply-to', addressList());
- }
- // 4.1. Miscellaneous Obsolete Tokens
- // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
- // %d11 / ; characters that do not
- // %d12 / ; include the carriage
- // %d14-31 / ; return, line feed, and
- // %d127 ; white space characters
- function obsNoWsCtl() {
- return opts.strict ? null : wrap('obs-NO-WS-CTL', compareToken(function (tok) {
- var code = tok.charCodeAt(0);
- return ((1 <= code && code <= 8) ||
- (11 === code || 12 === code) ||
- (14 <= code && code <= 31) ||
- (127 === code));
- }));
- }
- // obs-ctext = obs-NO-WS-CTL
- function obsCtext() { return opts.strict ? null : wrap('obs-ctext', obsNoWsCtl()); }
- // obs-qtext = obs-NO-WS-CTL
- function obsQtext() { return opts.strict ? null : wrap('obs-qtext', obsNoWsCtl()); }
- // obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR)
- function obsQP() {
- return opts.strict ? null : wrap('obs-qp', and(
- literal('\\'),
- or(literal('\0'), obsNoWsCtl, lf, cr)
- )());
- }
- // obs-phrase = word *(word / "." / CFWS)
- function obsPhrase() {
- if (opts.strict ) return null;
- return opts.atInDisplayName ? wrap('obs-phrase', and(
- word,
- star(or(word, literal('.'), literal('@'), colwsp(cfws)))
- )()) :
- wrap('obs-phrase', and(
- word,
- star(or(word, literal('.'), colwsp(cfws)))
- )());
- }
- // 4.2. Obsolete Folding White Space
- // NOTE: read the errata http://www.rfc-editor.org/errata_search.php?rfc=5322&eid=1908
- // obs-FWS = 1*([CRLF] WSP)
- function obsFws() {
- return opts.strict ? null : wrap('obs-FWS', star(
- and(invis(opt(crlf)), wsp),
- 1
- )());
- }
- // 4.4. Obsolete Addressing
- // obs-angle-addr = [CFWS] "<" obs-route addr-spec ">" [CFWS]
- function obsAngleAddr() {
- return opts.strict ? null : wrap('obs-angle-addr', and(
- invis(opt(cfws)),
- literal('<'),
- obsRoute,
- addrSpec,
- literal('>'),
- invis(opt(cfws))
- )());
- }
- // obs-route = obs-domain-list ":"
- function obsRoute() {
- return opts.strict ? null : wrap('obs-route', and(
- obsDomainList,
- literal(':')
- )());
- }
- // obs-domain-list = *(CFWS / ",") "@" domain
- // *("," [CFWS] ["@" domain])
- function obsDomainList() {
- return opts.strict ? null : wrap('obs-domain-list', and(
- star(or(invis(cfws), literal(','))),
- literal('@'),
- domain,
- star(and(
- literal(','),
- invis(opt(cfws)),
- opt(and(literal('@'), domain))
- ))
- )());
- }
- // obs-mbox-list = *([CFWS] ",") mailbox *("," [mailbox / CFWS])
- function obsMboxList() {
- return opts.strict ? null : wrap('obs-mbox-list', and(
- star(and(
- invis(opt(cfws)),
- literal(',')
- )),
- mailbox,
- star(and(
- literal(','),
- opt(and(
- mailbox,
- invis(cfws)
- ))
- ))
- )());
- }
- // obs-addr-list = *([CFWS] ",") address *("," [address / CFWS])
- function obsAddrList() {
- return opts.strict ? null : wrap('obs-addr-list', and(
- star(and(
- invis(opt(cfws)),
- literal(',')
- )),
- address,
- star(and(
- literal(','),
- opt(and(
- address,
- invis(cfws)
- ))
- ))
- )());
- }
- // obs-group-list = 1*([CFWS] ",") [CFWS]
- function obsGroupList() {
- return opts.strict ? null : wrap('obs-group-list', and(
- star(and(
- invis(opt(cfws)),
- literal(',')
- ), 1),
- invis(opt(cfws))
- )());
- }
- // obs-local-part = word *("." word)
- function obsLocalPart() {
- return opts.strict ? null : wrap('obs-local-part', and(word, star(and(literal('.'), word)))());
- }
- // obs-domain = atom *("." atom)
- function obsDomain() {
- return opts.strict ? null : wrap('obs-domain', and(atom, star(and(literal('.'), atom)))());
- }
- // obs-dtext = obs-NO-WS-CTL / quoted-pair
- function obsDtext() {
- return opts.strict ? null : wrap('obs-dtext', or(obsNoWsCtl, quotedPair)());
- }
- /////////////////////////////////////////////////////
- // ast analysis
- function findNode(name, root) {
- var i, stack, node;
- if (root === null || root === undefined) { return null; }
- stack = [root];
- while (stack.length > 0) {
- node = stack.pop();
- if (node.name === name) {
- return node;
- }
- for (i = node.children.length - 1; i >= 0; i -= 1) {
- stack.push(node.children[i]);
- }
- }
- return null;
- }
- function findAllNodes(name, root) {
- var i, stack, node, result;
- if (root === null || root === undefined) { return null; }
- stack = [root];
- result = [];
- while (stack.length > 0) {
- node = stack.pop();
- if (node.name === name) {
- result.push(node);
- }
- for (i = node.children.length - 1; i >= 0; i -= 1) {
- stack.push(node.children[i]);
- }
- }
- return result;
- }
- function findAllNodesNoChildren(names, root) {
- var i, stack, node, result, namesLookup;
- if (root === null || root === undefined) { return null; }
- stack = [root];
- result = [];
- namesLookup = {};
- for (i = 0; i < names.length; i += 1) {
- namesLookup[names[i]] = true;
- }
- while (stack.length > 0) {
- node = stack.pop();
- if (node.name in namesLookup) {
- result.push(node);
- // don't look at children (hence findAllNodesNoChildren)
- } else {
- for (i = node.children.length - 1; i >= 0; i -= 1) {
- stack.push(node.children[i]);
- }
- }
- }
- return result;
- }
- function giveResult(ast) {
- var addresses, groupsAndMailboxes, i, groupOrMailbox, result;
- if (ast === null) {
- return null;
- }
- addresses = [];
- // An address is a 'group' (i.e. a list of mailboxes) or a 'mailbox'.
- groupsAndMailboxes = findAllNodesNoChildren(['group', 'mailbox'], ast);
- for (i = 0; i < groupsAndMailboxes.length; i += 1) {
- groupOrMailbox = groupsAndMailboxes[i];
- if (groupOrMailbox.name === 'group') {
- addresses.push(giveResultGroup(groupOrMailbox));
- } else if (groupOrMailbox.name === 'mailbox') {
- addresses.push(giveResultMailbox(groupOrMailbox));
- }
- }
- result = {
- ast: ast,
- addresses: addresses,
- };
- if (opts.simple) {
- result = simplifyResult(result);
- }
- if (opts.oneResult) {
- return oneResult(result);
- }
- if (opts.simple) {
- return result && result.addresses;
- } else {
- return result;
- }
- }
- function giveResultGroup(group) {
- var i;
- var groupName = findNode('display-name', group);
- var groupResultMailboxes = [];
- var mailboxes = findAllNodesNoChildren(['mailbox'], group);
- for (i = 0; i < mailboxes.length; i += 1) {
- groupResultMailboxes.push(giveResultMailbox(mailboxes[i]));
- }
- return {
- node: group,
- parts: {
- name: groupName,
- },
- type: group.name, // 'group'
- name: grabSemantic(groupName),
- addresses: groupResultMailboxes,
- };
- }
- function giveResultMailbox(mailbox) {
- var name = findNode('display-name', mailbox);
- var aspec = findNode('addr-spec', mailbox);
- var cfws = findAllNodes('cfws', mailbox);
- var comments = findAllNodesNoChildren(['comment'], mailbox);
- var local = findNode('local-part', aspec);
- var domain = findNode('domain', aspec);
- return {
- node: mailbox,
- parts: {
- name: name,
- address: aspec,
- local: local,
- domain: domain,
- comments: cfws
- },
- type: mailbox.name, // 'mailbox'
- name: grabSemantic(name),
- address: grabSemantic(aspec),
- local: grabSemantic(local),
- domain: grabSemantic(domain),
- comments: concatComments(comments),
- groupName: grabSemantic(mailbox.groupName),
- };
- }
- function grabSemantic(n) {
- return n !== null && n !== undefined ? n.semantic : null;
- }
- function simplifyResult(result) {
- var i;
- if (result && result.addresses) {
- for (i = 0; i < result.addresses.length; i += 1) {
- delete result.addresses[i].node;
- }
- }
- return result;
- }
- function concatComments(comments) {
- var result = '';
- if (comments) {
- for (var i = 0; i < comments.length; i += 1) {
- result += grabSemantic(comments[i]);
- }
- }
- return result;
- }
- function oneResult(result) {
- if (!result) { return null; }
- if (!opts.partial && result.addresses.length > 1) { return null; }
- return result.addresses && result.addresses[0];
- }
- /////////////////////////////////////////////////////
- var parseString, pos, len, parsed, startProduction;
- opts = handleOpts(opts, {});
- if (opts === null) { return null; }
- parseString = opts.input;
- startProduction = {
- 'address': address,
- 'address-list': addressList,
- 'angle-addr': angleAddr,
- 'from': fromSpec,
- 'group': group,
- 'mailbox': mailbox,
- 'mailbox-list': mailboxList,
- 'reply-to': replyToSpec,
- 'sender': senderSpec,
- }[opts.startAt] || addressList;
- if (!opts.strict) {
- initialize();
- opts.strict = true;
- parsed = startProduction(parseString);
- if (opts.partial || !inStr()) {
- return giveResult(parsed);
- }
- opts.strict = false;
- }
- initialize();
- parsed = startProduction(parseString);
- if (!opts.partial && inStr()) { return null; }
- return giveResult(parsed);
- }
- function parseOneAddressSimple(opts) {
- return parse5322(handleOpts(opts, {
- oneResult: true,
- rfc6532: true,
- simple: true,
- startAt: 'address-list',
- }));
- }
- function parseAddressListSimple(opts) {
- return parse5322(handleOpts(opts, {
- rfc6532: true,
- simple: true,
- startAt: 'address-list',
- }));
- }
- function parseFromSimple(opts) {
- return parse5322(handleOpts(opts, {
- rfc6532: true,
- simple: true,
- startAt: 'from',
- }));
- }
- function parseSenderSimple(opts) {
- return parse5322(handleOpts(opts, {
- oneResult: true,
- rfc6532: true,
- simple: true,
- startAt: 'sender',
- }));
- }
- function parseReplyToSimple(opts) {
- return parse5322(handleOpts(opts, {
- rfc6532: true,
- simple: true,
- startAt: 'reply-to',
- }));
- }
- function handleOpts(opts, defs) {
- function isString(str) {
- return Object.prototype.toString.call(str) === '[object String]';
- }
- function isObject(o) {
- return o === Object(o);
- }
- function isNullUndef(o) {
- return o === null || o === undefined;
- }
- var defaults, o;
- if (isString(opts)) {
- opts = { input: opts };
- } else if (!isObject(opts)) {
- return null;
- }
- if (!isString(opts.input)) { return null; }
- if (!defs) { return null; }
- defaults = {
- oneResult: false,
- partial: false,
- rejectTLD: false,
- rfc6532: false,
- simple: false,
- startAt: 'address-list',
- strict: false,
- atInDisplayName: false
- };
- for (o in defaults) {
- if (isNullUndef(opts[o])) {
- opts[o] = !isNullUndef(defs[o]) ? defs[o] : defaults[o];
- }
- }
- return opts;
- }
- parse5322.parseOneAddress = parseOneAddressSimple;
- parse5322.parseAddressList = parseAddressListSimple;
- parse5322.parseFrom = parseFromSimple;
- parse5322.parseSender = parseSenderSimple;
- parse5322.parseReplyTo = parseReplyToSimple;
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
- module.exports = parse5322;
- } else {
- global.emailAddresses = parse5322;
- }
- }(this));
|