1
0
mirror of https://github.com/eleith/emailjs.git synced 2024-07-05 20:10:37 +00:00

move address parsing to a maintained module

This commit is contained in:
eleith 2015-04-18 20:14:06 -07:00
parent aba99c406e
commit 3a89b55392
4 changed files with 42 additions and 465 deletions

View File

@ -1,36 +1,39 @@
{
"name": "emailjs",
"description": "send text/html emails and attachments (files, streams and strings) from node.js to any smtp server",
"version": "0.3.14",
"author": "eleith",
"contributors":["izuzak", "Hiverness", "mscdex", "jimmybergman"],
"repository":
{
"type": "git",
"url": "http://github.com/eleith/emailjs.git"
},
"dependencies":
{
"moment" : "= 1.7.0",
"name": "emailjs",
"description": "send text/html emails and attachments (files, streams and strings) from node.js to any smtp server",
"version": "0.3.15",
"author": "eleith",
"contributors": [
"izuzak",
"Hiverness",
"mscdex",
"jimmybergman"
],
"repository": {
"type": "git",
"url": "http://github.com/eleith/emailjs.git"
},
"dependencies": {
"addressparser": "^0.3.2",
"mimelib": "0.2.14",
"moment": "= 1.7.0",
"starttls": "0.2.1"
},
"optionalDependencies":
{
"bufferjs": "=1.1.0"
},
"devDependencies":
{
},
"optionalDependencies": {
"bufferjs": "=1.1.0"
},
"devDependencies": {
"mocha": "= 1.7.4",
"chai": "= 1.1.0",
"simplesmtp": "0.3.32",
"mailparser": "0.4.1",
"iconv": "2.1.6"
},
"engine": ["node >= 0.10"],
"main": "email",
"scripts":
{
"test": "mocha -R spec -t 5000"
}
},
"engine": [
"node >= 0.10"
],
"main": "email",
"scripts": {
"test": "mocha -R spec -t 5000"
}
}

View File

@ -1,416 +0,0 @@
/*
* 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 : [];
};

View File

@ -1,7 +1,7 @@
var smtp = require('./smtp');
var smtpError = require('./error');
var message = require('./message');
var address = require('./address');
var addressparser= require('addressparser');
var Client = function(server)
{
@ -97,19 +97,19 @@ Client.prototype =
var stack =
{
message: msg,
to: address.parse(msg.header.to),
from: address.parse(msg.header.from)[0].address,
to: addressparser(msg.header.to),
from: addressparser(msg.header.from)[0].address,
callback: callback || function() {}
};
if(msg.header.cc)
stack.to = stack.to.concat(address.parse(msg.header.cc));
stack.to = stack.to.concat(addressparser(msg.header.cc));
if(msg.header.bcc)
stack.to = stack.to.concat(address.parse(msg.header.bcc));
stack.to = stack.to.concat(addressparser(msg.header.bcc));
if(msg.header['return-path'] && address.parse(msg.header['return-path']).length)
stack.returnPath = address.parse(msg.header['return-path'])[0].address;
if(msg.header['return-path'] && addressparser(msg.header['return-path']).length)
stack.returnPath = addressparser(msg.header['return-path'])[0].address;
self.queue.push(stack);
self._poll();

View File

@ -5,7 +5,7 @@ var os = require('os');
var path = require('path');
var moment = require('moment');
var mimelib = require('mimelib');
var address = require('./address');
var addressparser = require('addressparser');
var CRLF = "\r\n";
var MIMECHUNK = 76; // MIME standard wants 76 char chunks when sending out.
var BASE64CHUNK= 24; // BASE64 bits needed before padding is used
@ -32,20 +32,10 @@ var generate_boundary = function()
function person2address(l)
{
// an array of emails or name+emails
if (Array.isArray(l)) {
l = l.join(', ');
}
// a string of comma separated emails or comma separated name+<emails>
if(typeof l == 'string') {
var addresses = address.parse(l);
return addresses.map(function(addr) {
return addr.label !== '' ? mimelib.encodeMimeWord(addr.label, 'Q', 'utf-8') + ' ' + '<' + addr.address + '>' : addr.address;
}).join(', ');
}
return null;
var addresses = addressparser(l);
return addresses.map(function(addr) {
return addr.name ? mimelib.encodeMimeWord(addr.name, 'Q', 'utf-8').replace(/,/g, '=2C') + ' ' + '<' + addr.address + '>' : addr.address;
}).join(', ');
}
var fix_header_name_case = function(header_name) {