ep_email_notifications/node_modules/emailjs/smtp/address.js
2013-01-29 17:35:40 +00:00

416 lines
8.3 KiB
JavaScript

/*
* Email address parsing code.
* rewritten with python's (2.7) email/_parseaddr.py as the starting point
*/
var SPACE = ' ';
var EMPTYSTRING = '';
var COMMASPACE = ', ';
var quote = function(str)
{
// Add quotes around a string.
return str.replace(/\\\\/g, '\\\\').replace(/"/g, '\\"');
};
/*
* To understand what this class does, it helps to have a copy of RFC 2822 in
* front of you.
*/
var Address = function(field)
{
/*
* Initialize a new instance.
* `field' is an unparsed address header field, containing
* one or more addresses.
*/
this.specials = '()<>@,:;.\"[]';
this.pos = 0;
this.LWS = ' \t';
this.CR = '\r\n';
this.FWS = this.LWS + this.CR;
this.atomends = this.specials + this.LWS + this.CR;
// Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it
// is obsolete syntax. RFC 2822 requires that we recognize obsolete
// syntax, so allow dots in phrases.
this.phraseends = this.atomends.replace(/\./g, '');
this.field = field || "";
this.commentlist = [];
};
Address.prototype =
{
gotonext: function()
{
//Parse up to the start of the next address.
while(this.pos < this.field.length)
{
if((this.LWS + '\n\r').indexOf(this.field[this.pos]) != -1)
this.pos++;
else if(this.field[this.pos] == '(')
this.commentlist.push(this.getcomment());
else
break;
}
},
getlist: function()
{
// Parse all addresses. Returns a list containing all of the addresses
var result = [], ad;
while(this.pos < this.field.length)
{
ad = this.get();
if(ad)
result.push(ad);
else
result.push({label:'', address:''});
}
return result;
},
get: function()
{
// Parse the next address
this.commentlist = [];
this.gotonext();
var oldpos = this.pos, oldcl = this.commentlist, plist = this.getphraselist(), returnlist = [],
addrspec, fieldlen, routeaddr;
this.gotonext();
if(this.pos >= this.field.length)
{
// Bad email address, no domain
if(plist.length)
returnlist = [{label:this.commentlist.join(SPACE), address:plist[0]}];
}
else if('.@'.indexOf(this.field[this.pos]) != -1)
{
// email address is just an addrspec
// this isn't very efficient since we start over
this.pos = oldpos;
this.commentlist = oldcl;
addrspec = this.getspec();
returnlist = {label:this.commentlist.join(SPACE), address:addrspec};
}
else if(this.field[this.pos] == ':')
{
// address is a group
returnlist = [];
fieldlen = this.field.length;
this.pos++;
while(this.pos < this.field.length)
{
this.gotonext();
if(this.pos < fieldlen && this.field[this.pos] == ';')
{
this.pos += 1;
break;
}
returnlist = returnlist.push(this.get());
}
}
else if(this.field[this.pos] == '<')
{
// Address is a prhase then a route addr
routeaddr = this.getroute();
if(this.commentlist.length)
returnlist = {label:plist.join(SPACE) + ' (' + this.commentlist.join(SPACE) + ')', address:routeaddr};
else
returnlist = {label:plist.join(SPACE), address:routeaddr};
}
else
{
if(plist.length)
returnlist = {label:this.commentlist.join(SPACE), address:plist[0]};
else if(this.specials.indexOf(this.field[this.pos]) != -1)
this.pos++;
}
this.gotonext();
if(this.pos < this.field.length && this.field[this.pos] == ',')
this.pos++;
return returnlist;
},
getroute: function()
{
// Parse a route address. this method skips all route stuff and returns addrspec
if(this.field[this.pos] != '<')
return '';
var expectroute = false, adlist = '';
this.pos++;
this.gotonext();
while(this.pos < this.field.length)
{
if(expectroute)
{
this.getdomain();
expectroute = false;
}
else if(this.field[this.pos] == '>')
{
this.pos += 1;
break;
}
else if(this.field[this.pos] == '@')
{
this.pos += 1;
expectroute = true;
}
else if(this.field[this.pos] == ':')
{
this.pos++;
}
else
{
adlist = this.getspec();
this.pos++;
break;
}
this.gotonext();
}
return adlist;
},
getspec: function()
{
//parse an RFC 2822 addr-spec
var aslist = [];
this.gotonext();
while(this.pos < this.field.length)
{
if(this.field[this.pos] == '.')
{
aslist.push('.');
this.pos++;
}
else if(this.field[this.pos] == '"')
aslist.push('"' + this.getquote() + '"');
else if(this.atomends.indexOf(this.field[this.pos]) != -1)
break;
else
aslist.push(this.getatom());
this.gotonext();
}
if(this.pos >= this.field.length || this.field[this.pos] != '@')
return aslist.join(EMPTYSTRING);
aslist.push('@');
this.pos++;
this.gotonext();
return aslist.join(EMPTYSTRING) + this.getdomain();
},
getdomain: function()
{
// get the complete domain name from an address
var sdlist = [];
while(this.pos < this.field.length)
{
if(this.LWS.indexOf(this.field[this.pos]) != -1)
this.pos++;
else if(this.field[this.pos] == '(')
this.commentlist.push(this.getcomment());
else if(this.field[this.pos] == '[')
sdlist.push(this.getdomainliteral());
else if(this.field[this.pos] == '.')
{
this.pos++;
sdlist.push('.');
}
else if(this.atomends.indexOf(this.field[this.pos]) != -1)
break;
else
sdlist.push(this.getatom());
}
return sdlist.join(EMPTYSTRING);
},
getdelimited: function(beginchar, endchars, allowcomments)
{
/*
* Parse a header fragment delimited by special characters.
*
* `beginchar' is the start character for the fragment.
* If self is not looking at an instance of `beginchar' then
* getdelimited returns the empty string.
*
* `endchars' is a sequence of allowable end-delimiting characters.
* Parsing stops when one of these is encountered.
*
* If `allowcomments' is non-zero, embedded RFC 2822 comments are allowed
* within the parsed fragment.
*/
if(this.field[this.pos] != beginchar)
return '';
allowcomments = (allowcomments === false) ? false : true;
var slist = [''], quote = false;
this.pos++;
while(this.pos < this.field.length)
{
if(quote)
{
slist.push(this.field[this.pos]);
quote = false;
}
else if(endchars.indexOf(this.field[this.pos]) != -1)
{
this.pos++;
break;
}
else if(allowcomments && this.field[this.pos] == '(')
{
slist.push(this.getcomment());
continue;
}
else if(this.field[this.pos] == '\\')
quote = true;
else
slist.push(this.field[this.pos]);
this.pos++;
}
return slist.join(EMPTYSTRING);
},
getquote: function()
{
// get a quote-delimited fragment from self's field
return this.getdelimited('"', '"\r', false);
},
getcomment: function()
{
// Get a parenthesis-delimited fragment from self's field.
return this.getdelimited('(', ')\r', true);
},
getdomainliteral: function()
{
// parse an rfc 2822 domain literal
return '[' + this.getdelimited('[', ']\r', false) + ']';
},
getatom: function(atomends)
{
/*
* Parse an RFC 2822 atom.
*
* Optional atomends specifies a different set of end token delimiters
* (the default is to use this.atomends). This is used e.g. in
* getphraselist() since phrase endings must not include the `.' (which
* is legal in phrases).
*/
var atomlist = [''];
if(atomends === undefined)
atomends = this.atomends;
while(this.pos < this.field.length)
{
if(atomends.indexOf(this.field[this.pos]) != -1)
break;
else
atomlist.push(this.field[this.pos]);
this.pos++;
}
return atomlist.join(EMPTYSTRING);
},
getphraselist: function()
{
/*
* Parse a sequence of RFC 2822 phrases.
*
* A phrase is a sequence of words, which are in turn either RFC 2822
* atoms or quoted-strings. Phrases are canonicalized by squeezing all
* runs of continuous whitespace into one space.
*/
var plist = [];
while(this.pos < this.field.length)
{
if(this.FWS.indexOf(this.field[this.pos]) != -1)
this.pos++;
else if(this.field[this.pos] == '"')
plist.push(this.getquote());
else if(this.field[this.pos] == '(')
this.commentlist.push(this.getcomment());
else if(this.phraseends.indexOf(this.field[this.pos]) != -1)
break;
else
plist.push(this.getatom(this.phraseends));
}
return plist;
}
};
exports.Address = Address;
exports.parse = function(field)
{
var addresses = (new Address(field)).getlist();
return addresses.length ? addresses : [];
};