mirror of
https://github.com/atlanticbiomedical/biomedjs.git
synced 2025-07-02 00:47:26 -04:00
Added node-modules
This commit is contained in:
416
node_modules/emailjs/smtp/address.js
generated
vendored
Normal file
416
node_modules/emailjs/smtp/address.js
generated
vendored
Normal file
@ -0,0 +1,416 @@
|
||||
/*
|
||||
* 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 : [];
|
||||
};
|
209
node_modules/emailjs/smtp/client.js
generated
vendored
Normal file
209
node_modules/emailjs/smtp/client.js
generated
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
var smtp = require('./smtp');
|
||||
var smtpError = require('./error');
|
||||
var message = require('./message');
|
||||
var address = require('./address');
|
||||
|
||||
var Client = function(server)
|
||||
{
|
||||
this.smtp = new smtp.SMTP(server);
|
||||
//this.smtp.debug(1);
|
||||
|
||||
this.queue = [];
|
||||
this.timer = null;
|
||||
this.sending = false;
|
||||
this.ready = false;
|
||||
};
|
||||
|
||||
Client.prototype =
|
||||
{
|
||||
_poll: function()
|
||||
{
|
||||
var self = this;
|
||||
|
||||
clearTimeout(self.timer);
|
||||
|
||||
if(self.queue.length)
|
||||
{
|
||||
if(self.smtp.state() == smtp.state.NOTCONNECTED)
|
||||
self._connect(self.queue[0]);
|
||||
|
||||
else if(self.smtp.state() == smtp.state.CONNECTED && !self.sending && self.ready)
|
||||
self._sendmail(self.queue.shift());
|
||||
}
|
||||
// wait around 1 seconds in case something does come in, otherwise close out SMTP connection if still open
|
||||
else if(self.smtp.state() == smtp.state.CONNECTED)
|
||||
self.timer = setTimeout(function() { self.smtp.quit(); }, 1000);
|
||||
},
|
||||
|
||||
_connect: function(stack)
|
||||
{
|
||||
var self = this,
|
||||
|
||||
connect = function(err)
|
||||
{
|
||||
if(!err)
|
||||
{
|
||||
var begin = function(err)
|
||||
{
|
||||
if(!err)
|
||||
{
|
||||
self.ready = true;
|
||||
self._poll();
|
||||
}
|
||||
else {
|
||||
stack.callback(err, stack.message);
|
||||
|
||||
// clear out the queue so all callbacks can be called with the same error message
|
||||
self.queue.shift();
|
||||
self._poll();
|
||||
}
|
||||
};
|
||||
|
||||
if(!self.smtp.authorized())
|
||||
self.smtp.login(begin);
|
||||
|
||||
else
|
||||
self.smtp.ehlo_or_helo_if_needed(begin);
|
||||
}
|
||||
else {
|
||||
stack.callback(err, stack.message);
|
||||
|
||||
// clear out the queue so all callbacks can be called with the same error message
|
||||
self.queue.shift();
|
||||
self._poll();
|
||||
}
|
||||
};
|
||||
|
||||
self.ready = false;
|
||||
self.smtp.connect(connect);
|
||||
},
|
||||
|
||||
send: function(msg, callback)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
if(!(msg instanceof message.Message)
|
||||
&& msg.from
|
||||
&& (msg.to || msg.cc || msg.bcc)
|
||||
&& (msg.text || this._containsInlinedHtml(msg.attachment)))
|
||||
msg = message.create(msg);
|
||||
|
||||
if(msg instanceof message.Message)
|
||||
{
|
||||
msg.valid(function(valid, why)
|
||||
{
|
||||
if(valid)
|
||||
{
|
||||
var stack =
|
||||
{
|
||||
message: msg,
|
||||
to: address.parse(msg.header.to),
|
||||
from: address.parse(msg.header.from)[0].address,
|
||||
callback: callback || function() {}
|
||||
};
|
||||
|
||||
if(msg.header.cc)
|
||||
stack.to = stack.to.concat(address.parse(msg.header.cc));
|
||||
|
||||
if(msg.header.bcc)
|
||||
stack.to = stack.to.concat(address.parse(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;
|
||||
|
||||
self.queue.push(stack);
|
||||
self._poll();
|
||||
}
|
||||
else
|
||||
callback(new Error(why), msg);
|
||||
});
|
||||
}
|
||||
else
|
||||
callback(new Error("message is not a valid Message instance"), msg);
|
||||
},
|
||||
|
||||
_containsInlinedHtml: function(attachment) {
|
||||
if (Array.isArray(attachment)) {
|
||||
return attachment.some((function(ctx) {
|
||||
return function(att) {
|
||||
return ctx._isAttachmentInlinedHtml(att);
|
||||
};
|
||||
})(this));
|
||||
} else {
|
||||
return this._isAttachmentInlinedHtml(attachment);
|
||||
}
|
||||
},
|
||||
|
||||
_isAttachmentInlinedHtml: function(attachment) {
|
||||
return attachment &&
|
||||
(attachment.data || attachment.path) &&
|
||||
attachment.alternative === true;
|
||||
},
|
||||
|
||||
_sendsmtp: function(stack, next)
|
||||
{
|
||||
var self = this;
|
||||
var check= function(err)
|
||||
{
|
||||
if(!err && next)
|
||||
{
|
||||
next.apply(self, [stack]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we snag on SMTP commands, call done, passing the error
|
||||
// but first reset SMTP state so queue can continue polling
|
||||
self.smtp.rset(function() { self._senddone(err, stack); });
|
||||
}
|
||||
};
|
||||
|
||||
return check;
|
||||
},
|
||||
|
||||
_sendmail: function(stack)
|
||||
{
|
||||
var self = this;
|
||||
var from = stack.returnPath || stack.from;
|
||||
self.sending = true;
|
||||
self.smtp.mail(self._sendsmtp(stack, self._sendrcpt), '<' + from + '>');
|
||||
},
|
||||
|
||||
_sendrcpt: function(stack)
|
||||
{
|
||||
var self = this, to = stack.to.shift().address;
|
||||
self.smtp.rcpt(self._sendsmtp(stack, stack.to.length ? self._sendrcpt : self._senddata), '<'+ to +'>');
|
||||
},
|
||||
|
||||
_senddata: function(stack)
|
||||
{
|
||||
var self = this;
|
||||
self.smtp.data(self._sendsmtp(stack, self._sendmessage));
|
||||
},
|
||||
|
||||
_sendmessage: function(stack)
|
||||
{
|
||||
var self = this, stream = stack.message.stream();
|
||||
|
||||
stream.on('data', function(data) { self.smtp.message(data); });
|
||||
stream.on('end', function() { self.smtp.data_end(self._sendsmtp(stack, function() { self._senddone(null, stack) })); });
|
||||
|
||||
// there is no way to cancel a message while in the DATA portion, so we have to close the socket to prevent
|
||||
// a bad email from going out
|
||||
stream.on('error', function(err) { self.smtp.close(); self._senddone(err, stack); });
|
||||
},
|
||||
|
||||
_senddone: function(err, stack)
|
||||
{
|
||||
var self = this;
|
||||
self.sending = false;
|
||||
stack.callback(err, stack.message);
|
||||
self._poll();
|
||||
}
|
||||
};
|
||||
|
||||
exports.Client = Client;
|
||||
|
||||
exports.connect = function(server)
|
||||
{
|
||||
return new Client(server);
|
||||
};
|
21
node_modules/emailjs/smtp/error.js
generated
vendored
Normal file
21
node_modules/emailjs/smtp/error.js
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
module.exports = function(message, code, error, smtp)
|
||||
{
|
||||
var err = new Error(message);
|
||||
err.code = code;
|
||||
if(error)
|
||||
err.previous = error;
|
||||
err.smtp = smtp;
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
module.exports.COULDNOTCONNECT = 1;
|
||||
module.exports.BADRESPONSE = 2;
|
||||
module.exports.AUTHFAILED = 3;
|
||||
module.exports.TIMEDOUT = 4;
|
||||
module.exports.ERROR = 5;
|
||||
module.exports.NOCONNECTION = 6;
|
||||
module.exports.AUTHNOTSUPPORTED = 7;
|
||||
module.exports.CONNECTIONCLOSED = 8;
|
||||
module.exports.CONNECTIONENDED = 9;
|
||||
module.exports.CONNECTIONAUTH = 10;
|
622
node_modules/emailjs/smtp/message.js
generated
vendored
Normal file
622
node_modules/emailjs/smtp/message.js
generated
vendored
Normal file
@ -0,0 +1,622 @@
|
||||
var stream = require('stream');
|
||||
var util = require('util');
|
||||
var fs = require('fs');
|
||||
var os = require('os');
|
||||
var path = require('path');
|
||||
var moment = require('moment');
|
||||
var mimelib = require('mimelib');
|
||||
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
|
||||
var MIME64CHUNK= MIMECHUNK * 6; // meets both base64 and mime divisibility
|
||||
var BUFFERSIZE = MIMECHUNK*24*7; // size of the message stream buffer
|
||||
var counter = 0;
|
||||
|
||||
// support for nodejs without Buffer.concat native function
|
||||
if(!Buffer.concat)
|
||||
{
|
||||
require("bufferjs/concat");
|
||||
}
|
||||
|
||||
var generate_boundary = function()
|
||||
{
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+_,-./:=?";
|
||||
|
||||
for(var i=0; i < 69; i++)
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
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') {
|
||||
return l.replace(/([^<]+[^\s])(\s*)(<[^>]+>)/g, function(full, name, space, email) { return mimelib.encodeMimeWord(name, 'Q', 'utf-8') + space + email; });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var fix_header_name_case = function(header_name) {
|
||||
return header_name.toLowerCase().replace(/^(.)|-(.)/g, function(match) {
|
||||
return match.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
var Message = function(headers)
|
||||
{
|
||||
this.attachments = [];
|
||||
this.alternative = null;
|
||||
var now = new Date();
|
||||
this.header = {
|
||||
"message-id":"<" + now.getTime() + "." + (counter++) + "." + process.pid + "@" + os.hostname() +">",
|
||||
"date":moment().format("ddd, DD MMM YYYY HH:mm:ss ") + moment().format("Z").replace(/:/, '')
|
||||
};
|
||||
this.content = "text/plain; charset=utf-8";
|
||||
|
||||
for(var header in headers)
|
||||
{
|
||||
// allow user to override default content-type to override charset or send a single non-text message
|
||||
if(/^content-type$/i.test(header))
|
||||
{
|
||||
this.content = headers[header];
|
||||
}
|
||||
else if(header == 'text')
|
||||
{
|
||||
this.text = headers[header];
|
||||
}
|
||||
else if(header == "attachment" && typeof (headers[header]) == "object")
|
||||
{
|
||||
if(Array.isArray(headers[header])) {
|
||||
var that = this;
|
||||
|
||||
for (var i = 0, l = headers[header].length; i < l; i++) {
|
||||
this.attach(headers[header][i]);
|
||||
}
|
||||
} else {
|
||||
this.attach(headers[header]);
|
||||
}
|
||||
}
|
||||
else if(header == 'subject')
|
||||
{
|
||||
this.header.subject = mimelib.encodeMimeWord(headers.subject, 'Q', 'utf-8');
|
||||
}
|
||||
else if(/cc|bcc|to|from/i.test(header))
|
||||
{
|
||||
this.header[header.toLowerCase()] = person2address(headers[header]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// allow any headers the user wants to set??
|
||||
// if(/cc|bcc|to|from|reply-to|sender|subject|date|message-id/i.test(header))
|
||||
this.header[header.toLowerCase()] = headers[header];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Message.prototype =
|
||||
{
|
||||
attach: function(options)
|
||||
{
|
||||
/*
|
||||
legacy support, will remove eventually...
|
||||
arguments -> (path, type, name, headers)
|
||||
*/
|
||||
if (arguments.length > 1)
|
||||
options = {path:options, type:arguments[1], name:arguments[2]};
|
||||
|
||||
// sender can specify an attachment as an alternative
|
||||
if(options.alternative)
|
||||
{
|
||||
this.alternative = options;
|
||||
this.alternative.charset = options.charset || "utf-8";
|
||||
this.alternative.type = options.type || "text/html";
|
||||
this.alternative.inline = true;
|
||||
}
|
||||
else
|
||||
this.attachments.push(options);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/*
|
||||
legacy support, will remove eventually...
|
||||
should use Message.attach() instead
|
||||
*/
|
||||
attach_alternative: function(html, charset)
|
||||
{
|
||||
this.alternative =
|
||||
{
|
||||
data: html,
|
||||
charset: charset || "utf-8",
|
||||
type: "text/html",
|
||||
inline: true
|
||||
};
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
valid: function(callback)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
if(!self.header.from)
|
||||
{
|
||||
callback(false, "message does not have a valid sender");
|
||||
}
|
||||
if(!(self.header.to || self.header.cc || self.header.bcc))
|
||||
{
|
||||
callback(false, "message does not have a valid recipient");
|
||||
}
|
||||
else if(self.attachments.length === 0)
|
||||
{
|
||||
callback(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var check = [];
|
||||
var failed = [];
|
||||
|
||||
self.attachments.forEach(function(attachment, index)
|
||||
{
|
||||
if(attachment.path)
|
||||
{
|
||||
// migrating path->fs for existsSync)
|
||||
if(!(fs.existsSync || path.existsSync)(attachment.path))
|
||||
failed.push(attachment.path + " does not exist");
|
||||
}
|
||||
else if(attachment.stream)
|
||||
{
|
||||
if(!attachment.stream.readable)
|
||||
failed.push("attachment stream is not readable");
|
||||
}
|
||||
else if(!attachment.data)
|
||||
{
|
||||
failed.push("attachment has no data associated with it");
|
||||
}
|
||||
});
|
||||
|
||||
callback(failed.length === 0, failed.join(", "));
|
||||
}
|
||||
},
|
||||
|
||||
stream: function()
|
||||
{
|
||||
return new MessageStream(this);
|
||||
},
|
||||
|
||||
read: function(callback)
|
||||
{
|
||||
var buffer = "";
|
||||
|
||||
var capture = function(data)
|
||||
{
|
||||
buffer += data;
|
||||
};
|
||||
|
||||
var output = function(err)
|
||||
{
|
||||
callback(err, buffer);
|
||||
};
|
||||
|
||||
var str = this.stream();
|
||||
|
||||
str.on('data', capture);
|
||||
str.on('end', output);
|
||||
str.on('error', output);
|
||||
}
|
||||
};
|
||||
|
||||
var MessageStream = function(message)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
stream.Stream.call(self);
|
||||
|
||||
self.message = message;
|
||||
self.readable = true;
|
||||
self.paused = false;
|
||||
self.buffer = new Buffer(MIMECHUNK*24*7);
|
||||
self.bufferIndex = 0;
|
||||
|
||||
var output_process = function(next, args)
|
||||
{
|
||||
if(self.paused)
|
||||
{
|
||||
self.resumed = function() { next.apply(null, args); };
|
||||
}
|
||||
else
|
||||
{
|
||||
next.apply(null, args);
|
||||
}
|
||||
|
||||
next.apply(null, args);
|
||||
};
|
||||
|
||||
var output_mixed = function()
|
||||
{
|
||||
var boundary = generate_boundary();
|
||||
var data = ["Content-Type: multipart/mixed; boundary=\"", boundary, "\"", CRLF, CRLF, "--", boundary, CRLF];
|
||||
|
||||
output(data.join(''));
|
||||
|
||||
if(!self.message.alternative)
|
||||
{
|
||||
output_text(self.message);
|
||||
output_message(boundary, self.message.attachments, 0, close);
|
||||
}
|
||||
else
|
||||
{
|
||||
output_alternative(self.message, function() { output_message(boundary, self.message.attachments, 0, close); });
|
||||
}
|
||||
};
|
||||
|
||||
var output_message = function(boundary, list, index, callback)
|
||||
{
|
||||
if(index < list.length)
|
||||
{
|
||||
output(["--", boundary, CRLF].join(''));
|
||||
|
||||
if(list[index].related)
|
||||
{
|
||||
output_related(list[index], function() { output_message(boundary, list, index + 1, callback); });
|
||||
}
|
||||
else
|
||||
{
|
||||
output_attachment(list[index], function() { output_message(boundary, list, index + 1, callback); });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output([CRLF, "--", boundary, "--", CRLF, CRLF].join(''));
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
var output_attachment_headers = function(attachment)
|
||||
{
|
||||
var data = [],
|
||||
header,
|
||||
headers =
|
||||
{
|
||||
'content-type': attachment.type + (attachment.charset ? "; charset=" + attachment.charset : ""),
|
||||
'content-transfer-encoding': 'base64',
|
||||
'content-disposition': attachment.inline ? 'inline' : 'attachment; filename="' + mimelib.encodeMimeWord(attachment.name, 'Q', 'utf-8') + '"'
|
||||
};
|
||||
|
||||
for(header in (attachment.headers || {}))
|
||||
{
|
||||
// allow sender to override default headers
|
||||
headers[header.toLowerCase()] = attachment.headers[header];
|
||||
}
|
||||
|
||||
for(header in headers)
|
||||
{
|
||||
data = data.concat([fix_header_name_case(header), ': ', headers[header], CRLF]);
|
||||
}
|
||||
|
||||
output(data.concat([CRLF]).join(''));
|
||||
};
|
||||
|
||||
var output_attachment = function(attachment, callback)
|
||||
{
|
||||
var build = attachment.path ? output_file : attachment.stream ? output_stream : output_data;
|
||||
output_attachment_headers(attachment);
|
||||
build(attachment, callback);
|
||||
};
|
||||
|
||||
var output_data = function(attachment, callback)
|
||||
{
|
||||
output_base64(attachment.encoded ? attachment.data : new Buffer(attachment.data).toString("base64"), callback);
|
||||
};
|
||||
|
||||
var output_file = function(attachment, next)
|
||||
{
|
||||
var chunk = MIME64CHUNK*16;
|
||||
var buffer = new Buffer(chunk);
|
||||
var closed = function(fd) { fs.close(fd); };
|
||||
var opened = function(err, fd)
|
||||
{
|
||||
if(!err)
|
||||
{
|
||||
var read = function(err, bytes)
|
||||
{
|
||||
if(!err && self.readable)
|
||||
{
|
||||
// guaranteed to be encoded without padding unless it is our last read
|
||||
output_base64(buffer.toString("base64", 0, bytes), function()
|
||||
{
|
||||
if(bytes == chunk) // we read a full chunk, there might be more
|
||||
{
|
||||
fs.read(fd, buffer, 0, chunk, null, read);
|
||||
}
|
||||
else // that was the last chunk, we are done reading the file
|
||||
{
|
||||
self.removeListener("error", closed);
|
||||
fs.close(fd, next);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
self.emit('error', err || {message:"message stream was interrupted somehow!"});
|
||||
}
|
||||
};
|
||||
|
||||
fs.read(fd, buffer, 0, chunk, null, read);
|
||||
self.once("error", closed);
|
||||
}
|
||||
else
|
||||
self.emit('error', err);
|
||||
};
|
||||
|
||||
fs.open(attachment.path, 'r', opened);
|
||||
};
|
||||
|
||||
var output_stream = function(attachment, callback)
|
||||
{
|
||||
if(attachment.stream.readable)
|
||||
{
|
||||
var previous = null;
|
||||
|
||||
attachment.stream.resume();
|
||||
attachment.stream.on('end', function()
|
||||
{
|
||||
output_base64((previous || new Buffer(0)).toString("base64"), callback);
|
||||
self.removeListener('pause', attachment.stream.pause);
|
||||
self.removeListener('resume', attachment.stream.resume);
|
||||
self.removeListener('error', attachment.stream.resume);
|
||||
});
|
||||
|
||||
attachment.stream.on('data', function(buffer)
|
||||
{
|
||||
// do we have bytes from a previous stream data event?
|
||||
if(previous)
|
||||
{
|
||||
var buffer2 = Buffer.concat([previous, buffer]);
|
||||
previous = null; // free up the buffer
|
||||
buffer = null; // free up the buffer
|
||||
buffer = buffer2;
|
||||
}
|
||||
|
||||
var padded = buffer.length % (MIME64CHUNK);
|
||||
|
||||
// encode as much of the buffer to base64 without empty bytes
|
||||
if(padded)
|
||||
{
|
||||
previous = new Buffer(padded);
|
||||
// copy dangling bytes into previous buffer
|
||||
buffer.copy(previous, 0, buffer.length - padded);
|
||||
}
|
||||
|
||||
output_base64(buffer.toString("base64", 0, buffer.length - padded));
|
||||
});
|
||||
|
||||
self.on('pause', attachment.stream.pause);
|
||||
self.on('resume', attachment.stream.resume);
|
||||
self.on('error', attachment.stream.resume);
|
||||
}
|
||||
else
|
||||
self.emit('error', {message:"stream not readable"});
|
||||
};
|
||||
|
||||
var output_base64 = function(data, callback)
|
||||
{
|
||||
var loops = Math.ceil(data.length / MIMECHUNK);
|
||||
var loop = 0;
|
||||
|
||||
while(loop < loops)
|
||||
{
|
||||
output(data.substring(MIMECHUNK * loop, MIMECHUNK * (loop + 1)) + CRLF);
|
||||
loop++;
|
||||
}
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
};
|
||||
|
||||
var output_text = function(message)
|
||||
{
|
||||
var data = [];
|
||||
|
||||
data = data.concat(["Content-Type:", message.content, CRLF, "Content-Transfer-Encoding: 7bit", CRLF]);
|
||||
data = data.concat(["Content-Disposition: inline", CRLF, CRLF]);
|
||||
data = data.concat([message.text || "", CRLF, CRLF]);
|
||||
|
||||
output(data.join(''));
|
||||
};
|
||||
|
||||
var output_alternative = function(message, callback)
|
||||
{
|
||||
var data = [], boundary = generate_boundary();
|
||||
|
||||
data = data.concat(["Content-Type: multipart/alternative; boundary=\"", boundary, "\"", CRLF, CRLF]);
|
||||
data = data.concat(["--", boundary, CRLF]);
|
||||
|
||||
output(data.join(''));
|
||||
output_text(message);
|
||||
output(["--", boundary, CRLF].join(''));
|
||||
|
||||
var finish = function()
|
||||
{
|
||||
output([CRLF, "--", boundary, "--", CRLF, CRLF].join(''));
|
||||
callback();
|
||||
};
|
||||
|
||||
if(message.alternative.related)
|
||||
{
|
||||
output_related(message.alternative, finish);
|
||||
}
|
||||
else
|
||||
{
|
||||
output_attachment(message.alternative, finish);
|
||||
}
|
||||
};
|
||||
|
||||
var output_related = function(message, callback)
|
||||
{
|
||||
var data = [], boundary = generate_boundary();
|
||||
|
||||
data = data.concat(["Content-Type: multipart/related; boundary=\"", boundary, "\"", CRLF, CRLF]);
|
||||
data = data.concat(["--", boundary, CRLF]);
|
||||
|
||||
output(data.join(''));
|
||||
|
||||
output_attachment(message, function()
|
||||
{
|
||||
output_message(boundary, message.related, 0, function()
|
||||
{
|
||||
output([CRLF, "--", boundary, "--", CRLF, CRLF].join(''));
|
||||
callback();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var output_header_data = function()
|
||||
{
|
||||
if(self.message.attachments.length || self.message.alternative)
|
||||
{
|
||||
output("MIME-Version: 1.0" + CRLF);
|
||||
output_mixed();
|
||||
}
|
||||
else // you only have a text message!
|
||||
{
|
||||
output_text(self.message);
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
var output_header = function()
|
||||
{
|
||||
var data = [];
|
||||
|
||||
for(var header in self.message.header)
|
||||
{
|
||||
// do not output BCC in the headers...
|
||||
if(!(/bcc/i.test(header)))
|
||||
data = data.concat([fix_header_name_case(header), ": ", self.message.header[header], CRLF]);
|
||||
}
|
||||
|
||||
output(data.join(''));
|
||||
output_header_data();
|
||||
};
|
||||
|
||||
var output = function(data, callback, args)
|
||||
{
|
||||
var bytes = Buffer.byteLength(data);
|
||||
|
||||
// can we buffer the data?
|
||||
if(bytes + self.bufferIndex < self.buffer.length)
|
||||
{
|
||||
self.buffer.write(data, self.bufferIndex);
|
||||
self.bufferIndex += bytes;
|
||||
|
||||
if(callback)
|
||||
callback.apply(null, args);
|
||||
}
|
||||
// we can't buffer the data, so ship it out!
|
||||
else if(bytes > self.buffer.length)
|
||||
{
|
||||
if(self.bufferIndex)
|
||||
{
|
||||
self.emit('data', self.buffer.toString("utf-8", 0, self.bufferIndex));
|
||||
self.bufferIndex = 0;
|
||||
}
|
||||
|
||||
var loops = Math.ceil(data.length / self.buffer.length);
|
||||
var loop = 0;
|
||||
|
||||
while(loop < loops)
|
||||
{
|
||||
self.emit('data', data.substring(self.buffer.length*loop, self.buffer.length*(loop + 1)));
|
||||
loop++;
|
||||
}
|
||||
}
|
||||
else // we need to clean out the buffer, it is getting full
|
||||
{
|
||||
if(!self.paused)
|
||||
{
|
||||
self.emit('data', self.buffer.toString("utf-8", 0, self.bufferIndex));
|
||||
self.buffer.write(data, 0);
|
||||
self.bufferIndex = bytes;
|
||||
|
||||
// we could get paused after emitting data...
|
||||
if(self.paused)
|
||||
{
|
||||
self.once("resume", function() { callback.apply(null, args); });
|
||||
}
|
||||
else if(callback)
|
||||
{
|
||||
callback.apply(null, args);
|
||||
}
|
||||
}
|
||||
else // we can't empty out the buffer, so let's wait till we resume before adding to it
|
||||
{
|
||||
self.once("resume", function() { output(data, callback, args); });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var close = function(err)
|
||||
{
|
||||
if(err)
|
||||
{
|
||||
self.emit("error", err);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.emit('data', self.buffer.toString("utf-8", 0, self.bufferIndex));
|
||||
self.emit('end');
|
||||
}
|
||||
|
||||
self.buffer = null;
|
||||
self.bufferIndex = 0;
|
||||
self.readable = false;
|
||||
self.removeAllListeners("resume");
|
||||
self.removeAllListeners("pause");
|
||||
self.removeAllListeners("error");
|
||||
self.removeAllListeners("data");
|
||||
self.removeAllListeners("end");
|
||||
};
|
||||
|
||||
self.once("destroy", close);
|
||||
process.nextTick(output_header);
|
||||
};
|
||||
|
||||
util.inherits(MessageStream, stream.Stream);
|
||||
|
||||
MessageStream.prototype.pause = function()
|
||||
{
|
||||
this.paused = true;
|
||||
this.emit('pause');
|
||||
};
|
||||
|
||||
MessageStream.prototype.resume = function()
|
||||
{
|
||||
this.paused = false;
|
||||
this.emit('resume');
|
||||
};
|
||||
|
||||
MessageStream.prototype.destroy = function()
|
||||
{
|
||||
this.emit("destroy", self.bufferIndex > 0 ? {message:"message stream destroyed"} : null);
|
||||
};
|
||||
|
||||
MessageStream.prototype.destroySoon = function()
|
||||
{
|
||||
this.emit("destroy");
|
||||
};
|
||||
|
||||
exports.Message = Message;
|
||||
exports.BUFFERSIZE = BUFFERSIZE;
|
||||
exports.create = function(headers)
|
||||
{
|
||||
return new Message(headers);
|
||||
};
|
75
node_modules/emailjs/smtp/response.js
generated
vendored
Normal file
75
node_modules/emailjs/smtp/response.js
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
var SMTPError = require('./error');
|
||||
|
||||
var SMTPResponse = function(stream, timeout, onerror)
|
||||
{
|
||||
var buffer = '',
|
||||
|
||||
notify = function()
|
||||
{
|
||||
if(buffer.length)
|
||||
{
|
||||
// parse buffer for response codes
|
||||
var line = buffer.replace("\r", '');
|
||||
var match = line ? line.match(/(\d+)\s?(.*)/) : null;
|
||||
|
||||
stream.emit('response', null, match ? {code:match[1], message:match[2], data:line} : {code:-1, data:line});
|
||||
buffer = '';
|
||||
}
|
||||
},
|
||||
|
||||
error = function(err)
|
||||
{
|
||||
stream.emit('response', SMTPError('connection encountered an error', SMTPError.ERROR, err));
|
||||
},
|
||||
|
||||
timedout = function(err)
|
||||
{
|
||||
stream.emit('response', SMTPError('timedout while connecting to smtp server', SMTPError.TIMEDOUT, err));
|
||||
},
|
||||
|
||||
watch = function()
|
||||
{
|
||||
var data = stream.read();
|
||||
|
||||
if (data !== null) {
|
||||
var decoded = data.toString();
|
||||
var emit = false;
|
||||
var code = 0;
|
||||
|
||||
buffer += decoded;
|
||||
notify();
|
||||
}
|
||||
},
|
||||
|
||||
close = function(err)
|
||||
{
|
||||
stream.emit('response', SMTPError('connection has closed', SMTPError.CONNECTIONCLOSED, err));
|
||||
},
|
||||
|
||||
end = function(err)
|
||||
{
|
||||
stream.emit('response', SMTPError('connection has ended', SMTPError.CONNECTIONENDED, err));
|
||||
};
|
||||
|
||||
this.stop = function(err) {
|
||||
stream.removeAllListeners('response');
|
||||
stream.removeListener('readable', watch);
|
||||
stream.removeListener('end', end);
|
||||
stream.removeListener('close', close);
|
||||
stream.removeListener('error', error);
|
||||
|
||||
if(err && typeof(onerror) == "function")
|
||||
onerror(err);
|
||||
};
|
||||
|
||||
stream.on('readable', watch);
|
||||
stream.on('end', end);
|
||||
stream.on('close', close);
|
||||
stream.on('error', error);
|
||||
stream.setTimeout(timeout, timedout);
|
||||
};
|
||||
|
||||
exports.monitor = function(stream, timeout, onerror)
|
||||
{
|
||||
return new SMTPResponse(stream, timeout, onerror);
|
||||
};
|
537
node_modules/emailjs/smtp/smtp.js
generated
vendored
Normal file
537
node_modules/emailjs/smtp/smtp.js
generated
vendored
Normal file
@ -0,0 +1,537 @@
|
||||
/*
|
||||
* SMTP class written using python's (2.7) smtplib.py as a base
|
||||
*/
|
||||
var net = require('net');
|
||||
var crypto = require('crypto');
|
||||
var os = require('os');
|
||||
var tls = require('tls');
|
||||
var util = require('util');
|
||||
var events = require('events');
|
||||
var starttls = require('./tls');
|
||||
|
||||
var SMTPResponse = require('./response');
|
||||
var SMTPError = require('./error');
|
||||
|
||||
var SMTP_PORT = 25;
|
||||
var SMTP_SSL_PORT = 465;
|
||||
var SMTP_TLS_PORT = 587;
|
||||
var CRLF = "\r\n";
|
||||
var AUTH_METHODS = {
|
||||
PLAIN: 'PLAIN',
|
||||
CRAM_MD5: 'CRAM-MD5',
|
||||
LOGIN: 'LOGIN',
|
||||
XOAUTH2: 'XOAUTH2'
|
||||
};
|
||||
var TIMEOUT = 5000;
|
||||
var DEBUG = 0;
|
||||
|
||||
var log = function() {
|
||||
if (DEBUG) {
|
||||
Array.prototype.slice.call(arguments).forEach(function(d) {
|
||||
console.log(d);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var quotedata = function(data) {
|
||||
// Quote data for email.
|
||||
// Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
|
||||
// Internet CRLF end-of-line.
|
||||
|
||||
return data.replace(/(?:\r\n|\n|\r(?!\n))/g, CRLF).replace(/^\./gm, '..');
|
||||
};
|
||||
|
||||
var caller = function(callback) {
|
||||
if (typeof(callback) == 'function') {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.shift();
|
||||
|
||||
callback.apply(null, args);
|
||||
}
|
||||
};
|
||||
|
||||
var SMTPState = {
|
||||
NOTCONNECTED: 0,
|
||||
CONNECTING: 1,
|
||||
CONNECTED: 2
|
||||
};
|
||||
|
||||
var SMTP = function(options) {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
options = options || {};
|
||||
|
||||
this.sock = null;
|
||||
this.timeout = options.timeout || TIMEOUT;
|
||||
this.features = null;
|
||||
this._state = SMTPState.NOTCONNECTED;
|
||||
this._secure = false;
|
||||
this.loggedin = (options.user && options.password) ? false : true;
|
||||
this.domain = options.domain || os.hostname();
|
||||
this.host = options.host || 'localhost';
|
||||
this.port = options.port || (options.ssl ? SMTP_SSL_PORT : options.tls ? SMTP_TLS_PORT : SMTP_PORT);
|
||||
this.ssl = options.ssl || false;
|
||||
this.tls = options.tls || false;
|
||||
this.monitor = null;
|
||||
|
||||
// keep these strings hidden when quicky debugging/logging
|
||||
this.user = function() {
|
||||
return options.user;
|
||||
};
|
||||
this.password = function() {
|
||||
return options.password;
|
||||
};
|
||||
};
|
||||
|
||||
SMTP.prototype = {
|
||||
debug: function(level) {
|
||||
DEBUG = level;
|
||||
},
|
||||
|
||||
state: function() {
|
||||
return this._state;
|
||||
},
|
||||
|
||||
authorized: function() {
|
||||
return this.loggedin;
|
||||
},
|
||||
|
||||
connect: function(callback, port, host, options) {
|
||||
options = options || {};
|
||||
|
||||
var self = this;
|
||||
|
||||
self.host = host || self.host;
|
||||
self.port = port || self.port;
|
||||
self.ssl = options.ssl || self.ssl;
|
||||
|
||||
if (self._state != SMTPState.NOTCONNECTED) {
|
||||
self.quit(function() {
|
||||
self.connect(callback, port, host, options);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var connected = function(err) {
|
||||
if (!err) {
|
||||
log("connected: " + self.host + ":" + self.port);
|
||||
|
||||
if (self.ssl && !self.tls) {
|
||||
// if key/ca/cert was passed in, check if connection is authorized
|
||||
if (typeof(self.ssl) != 'boolean' && !self.sock.authorized) {
|
||||
self.close(true);
|
||||
caller(callback, SMTPError('could not establish an ssl connection', SMTPError.CONNECTIONAUTH, err));
|
||||
} else self._secure = true;
|
||||
}
|
||||
} else {
|
||||
self.close(true);
|
||||
caller(callback, SMTPError("could not connect", SMTPError.COULDNOTCONNECT, err));
|
||||
}
|
||||
};
|
||||
|
||||
var response = function(err, msg) {
|
||||
if (err) {
|
||||
self.close(true);
|
||||
caller(callback, err);
|
||||
} else if (msg.code == '220') {
|
||||
log(msg.data);
|
||||
|
||||
// might happen first, so no need to wait on connected()
|
||||
self._state = SMTPState.CONNECTED;
|
||||
caller(callback, null, msg.data);
|
||||
} else {
|
||||
log("response (data): " + msg.data);
|
||||
self.quit(function() {
|
||||
caller(callback, SMTPError("bad response on connection", SMTPError.BADRESPONSE, err, msg.data));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
self._state = SMTPState.CONNECTING;
|
||||
log("connecting: " + self.host + ":" + self.port);
|
||||
|
||||
if (self.ssl) {
|
||||
self.sock = tls.connect(self.port, self.host, self.ssl, connected);
|
||||
} else {
|
||||
self.sock = new net.Socket();
|
||||
self.sock.connect(self.port, self.host, connected);
|
||||
}
|
||||
|
||||
self.monitor = SMTPResponse.monitor(self.sock, self.timeout, function() {
|
||||
self.close(true);
|
||||
});
|
||||
self.sock.once('response', response);
|
||||
self.sock.once('error', response); // the socket could reset or throw, so let's handle it and let the user know
|
||||
},
|
||||
|
||||
send: function(str, callback) {
|
||||
var self = this;
|
||||
|
||||
if (self.sock && self._state == SMTPState.CONNECTED) {
|
||||
log(str);
|
||||
|
||||
var response = function(err, msg) {
|
||||
if (err) {
|
||||
caller(callback, err);
|
||||
} else {
|
||||
log(msg.data);
|
||||
caller(callback, null, msg);
|
||||
}
|
||||
};
|
||||
|
||||
self.sock.once('response', response);
|
||||
self.sock.write(str);
|
||||
} else {
|
||||
self.close(true);
|
||||
caller(callback, SMTPError('no connection has been established', SMTPError.NOCONNECTION));
|
||||
}
|
||||
},
|
||||
|
||||
command: function(cmd, callback, codes, failed) {
|
||||
codes = Array.isArray(codes) ? codes : typeof(codes) == 'number' ? [codes] : [250];
|
||||
|
||||
var response = function(err, msg) {
|
||||
if (err) {
|
||||
caller(callback, err);
|
||||
} else {
|
||||
if (codes.indexOf(Number(msg.code)) != -1) caller(callback, err, msg.data, msg.message);
|
||||
|
||||
else caller(callback, SMTPError("bad response on command '" + cmd.split(' ')[0] + "'", SMTPError.BADRESPONSE, null, msg.data));
|
||||
}
|
||||
};
|
||||
|
||||
this.send(cmd + CRLF, response);
|
||||
},
|
||||
|
||||
helo: function(callback, domain) {
|
||||
/*
|
||||
* SMTP 'helo' command.
|
||||
* Hostname to send for self command defaults to the FQDN of the local
|
||||
* host.
|
||||
*/
|
||||
|
||||
var self = this,
|
||||
|
||||
response = function(err, data) {
|
||||
if (err) {
|
||||
caller(callback, err);
|
||||
} else {
|
||||
self.parse_smtp_features(data);
|
||||
caller(callback, err, data);
|
||||
}
|
||||
};
|
||||
|
||||
this.command("helo " + (domain || this.domain), response);
|
||||
},
|
||||
|
||||
starttls: function(callback) {
|
||||
var self = this,
|
||||
|
||||
response = function(err, msg) {
|
||||
if (err) {
|
||||
err.message += " while establishing a starttls session";
|
||||
caller(callback, err);
|
||||
} else {
|
||||
var secured_socket = null;
|
||||
var secured = function() {
|
||||
self._secure = true;
|
||||
self.sock = secured_socket;
|
||||
|
||||
var error = function(err) {
|
||||
self.close(true);
|
||||
caller(callback, err);
|
||||
};
|
||||
|
||||
SMTPResponse.monitor(self.sock, self.timeout, function() {
|
||||
self.close(true);
|
||||
});
|
||||
caller(callback, msg.data);
|
||||
};
|
||||
|
||||
secured_socket = starttls.secure(self.sock, self.tls, secured);
|
||||
}
|
||||
};
|
||||
|
||||
this.command("starttls", response, [220]);
|
||||
},
|
||||
|
||||
parse_smtp_features: function(data) {
|
||||
var self = this;
|
||||
|
||||
// According to RFC1869 some (badly written)
|
||||
// MTA's will disconnect on an ehlo. Toss an exception if
|
||||
// that happens -ddm
|
||||
|
||||
data.split("\n").forEach(function(ext) {
|
||||
var parse = ext.match(/^(?:\d+[\-=]?)\s*?([^\s]+)(?:\s+(.*)\s*?)?$/);
|
||||
|
||||
// To be able to communicate with as many SMTP servers as possible,
|
||||
// we have to take the old-style auth advertisement into account,
|
||||
// because:
|
||||
// 1) Else our SMTP feature parser gets confused.
|
||||
// 2) There are some servers that only advertise the auth methods we
|
||||
// support using the old style.
|
||||
|
||||
if (parse) {
|
||||
// RFC 1869 requires a space between ehlo keyword and parameters.
|
||||
// It's actually stricter, in that only spaces are allowed between
|
||||
// parameters, but were not going to check for that here. Note
|
||||
// that the space isn't present if there are no parameters.
|
||||
self.features[parse[1].toLowerCase()] = parse[2] || true;
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
ehlo: function(callback, domain) {
|
||||
var self = this,
|
||||
|
||||
response = function(err, data) {
|
||||
if (err) {
|
||||
caller(callback, err);
|
||||
} else {
|
||||
self.parse_smtp_features(data);
|
||||
|
||||
if (self.tls && !self._secure) {
|
||||
self.starttls(function() {
|
||||
self.ehlo(callback, domain);
|
||||
});
|
||||
} else {
|
||||
caller(callback, err, data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.features = {};
|
||||
this.command("ehlo " + (domain || this.domain), response);
|
||||
},
|
||||
|
||||
has_extn: function(opt) {
|
||||
return this.features[opt.toLowerCase()] === undefined;
|
||||
},
|
||||
|
||||
help: function(callback, args) {
|
||||
// SMTP 'help' command, returns text from the server
|
||||
this.command(args ? "help " + args : "help", callback, [211, 214]);
|
||||
},
|
||||
|
||||
rset: function(callback) {
|
||||
this.command("rset", callback);
|
||||
},
|
||||
|
||||
noop: function(callback) {
|
||||
this.send("noop", callback);
|
||||
},
|
||||
|
||||
mail: function(callback, from) {
|
||||
this.command("mail FROM:" + from, callback);
|
||||
},
|
||||
|
||||
rcpt: function(callback, to) {
|
||||
this.command("RCPT TO:" + to, callback, [250, 251]);
|
||||
},
|
||||
|
||||
data: function(callback) {
|
||||
this.command("data", callback, [354]);
|
||||
},
|
||||
|
||||
data_end: function(callback) {
|
||||
this.command(CRLF + ".", callback);
|
||||
},
|
||||
|
||||
message: function(data) {
|
||||
log(data);
|
||||
this.sock.write(data);
|
||||
},
|
||||
|
||||
verify: function(address, callback) {
|
||||
// SMTP 'verify' command -- checks for address validity."""
|
||||
this.command("vrfy " + address, callback, [250, 251, 252]);
|
||||
},
|
||||
|
||||
expn: function(address, callback) {
|
||||
// SMTP 'expn' command -- expands a mailing list.
|
||||
this.command("expn " + address, callback);
|
||||
},
|
||||
|
||||
ehlo_or_helo_if_needed: function(callback, domain) {
|
||||
// Call self.ehlo() and/or self.helo() if needed.
|
||||
// If there has been no previous EHLO or HELO command self session, self
|
||||
// method tries ESMTP EHLO first.
|
||||
var self = this;
|
||||
|
||||
if (!this.features) {
|
||||
var response = function(err, data) {
|
||||
caller(callback, err, data);
|
||||
};
|
||||
|
||||
var attempt = function(err, data) {
|
||||
if (err) self.helo(response, domain);
|
||||
else caller(callback, err, data);
|
||||
};
|
||||
|
||||
self.ehlo(attempt, domain);
|
||||
}
|
||||
},
|
||||
|
||||
login: function(callback, user, password, options) {
|
||||
var self = this,
|
||||
|
||||
login = {
|
||||
user: user ? function() {
|
||||
return user;
|
||||
} : self.user,
|
||||
password: password ? function() {
|
||||
return password;
|
||||
} : self.password,
|
||||
method: options && options.method ? options.method.toUpperCase() : ''
|
||||
},
|
||||
|
||||
domain = options && options.domain ? options.domain : this.domain,
|
||||
|
||||
initiate = function(err, data) {
|
||||
if (err) {
|
||||
caller(callback, err);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Log in on an SMTP server that requires authentication.
|
||||
*
|
||||
* The arguments are:
|
||||
* - user: The user name to authenticate with.
|
||||
* - password: The password for the authentication.
|
||||
*
|
||||
* If there has been no previous EHLO or HELO command self session, self
|
||||
* method tries ESMTP EHLO first.
|
||||
*
|
||||
* This method will return normally if the authentication was successful.
|
||||
*/
|
||||
|
||||
var method = null,
|
||||
|
||||
encode_cram_md5 = function(challenge) {
|
||||
challenge = (new Buffer(challenge, "base64")).toString("ascii");
|
||||
|
||||
var hmac = crypto.createHmac('md5', login.password());
|
||||
hmac.update(challenge);
|
||||
|
||||
return (new Buffer(login.user() + " " + hmac.digest('hex')).toString("base64"));
|
||||
},
|
||||
|
||||
encode_plain = function() {
|
||||
return (new Buffer("\0" + login.user() + "\0" + login.password())).toString("base64");
|
||||
},
|
||||
|
||||
encode_xoauth2 = function() {
|
||||
// console.log("user=" + login.user() + "\1auth=Bearer " + login.password()+"\1\1");
|
||||
// see: https://developers.google.com/gmail/xoauth2_protocol
|
||||
return (new Buffer("user=" + login.user() + "\1auth=Bearer " + login.password() + "\1\1")).toString("base64");
|
||||
};
|
||||
|
||||
// List of authentication methods we support: from preferred to
|
||||
// less preferred methods.
|
||||
if (!method) {
|
||||
var preferred = [AUTH_METHODS.CRAM_MD5, AUTH_METHODS.LOGIN, AUTH_METHODS.PLAIN, AUTH_METHODS.XOAUTH2];
|
||||
|
||||
for (var i = 0; i < preferred.length; i++) {
|
||||
if ((self.features.auth || "").indexOf(preferred[i]) != -1) {
|
||||
method = preferred[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle bad responses from command differently
|
||||
var failed = function(err, data) {
|
||||
self.loggedin = false;
|
||||
self.close(); // if auth is bad, close the connection, it won't get better by itself
|
||||
caller(callback, SMTPError('authorization.failed', SMTPError.AUTHFAILED, err, data));
|
||||
};
|
||||
|
||||
var response = function(err, data) {
|
||||
if (err) {
|
||||
failed(err, data);
|
||||
} else {
|
||||
self.loggedin = true;
|
||||
caller(callback, err, data);
|
||||
}
|
||||
};
|
||||
|
||||
var attempt = function(err, data, msg) {
|
||||
if (err) {
|
||||
failed(err, data);
|
||||
} else {
|
||||
if (method == AUTH_METHODS.CRAM_MD5) {
|
||||
self.command(encode_cram_md5(msg), response, [235, 503]);
|
||||
} else if (method == AUTH_METHODS.LOGIN) {
|
||||
self.command((new Buffer(login.password())).toString("base64"), response, [235, 503]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var attempt_user = function(err, data, msg) {
|
||||
if (err) {
|
||||
failed(err, data);
|
||||
} else {
|
||||
if (method == AUTH_METHODS.LOGIN) {
|
||||
self.command((new Buffer(login.user())).toString("base64"), attempt, [334]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (method == AUTH_METHODS.CRAM_MD5) self.command("AUTH " + AUTH_METHODS.CRAM_MD5, attempt, [334]);
|
||||
|
||||
else if (method == AUTH_METHODS.LOGIN) self.command("AUTH " + AUTH_METHODS.LOGIN, attempt_user, [334]);
|
||||
|
||||
else if (method == AUTH_METHODS.PLAIN) self.command("AUTH " + AUTH_METHODS.PLAIN + " " + encode_plain(login.user(), login.password()), response, [235, 503]);
|
||||
|
||||
else if (method == AUTH_METHODS.XOAUTH2) self.command("AUTH " + AUTH_METHODS.XOAUTH2 + " " + encode_xoauth2(login.user(), login.password()), response, [235, 503]);
|
||||
|
||||
else if (!method) caller(callback, SMTPError('no form of authorization supported', SMTPError.AUTHNOTSUPPORTED, null, data));
|
||||
};
|
||||
|
||||
self.ehlo_or_helo_if_needed(initiate, domain);
|
||||
},
|
||||
|
||||
close: function(force) {
|
||||
if (this.sock) {
|
||||
if (force) {
|
||||
log("smtp connection destroyed!");
|
||||
this.sock.destroy();
|
||||
} else {
|
||||
log("smtp connection closed.");
|
||||
this.sock.end();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.monitor) {
|
||||
this.monitor.stop();
|
||||
this.monitor = null;
|
||||
}
|
||||
|
||||
this._state = SMTPState.NOTCONNECTED;
|
||||
this._secure = false;
|
||||
this.sock = null;
|
||||
this.features = null;
|
||||
this.loggedin = !(this.user() && this.password());
|
||||
},
|
||||
|
||||
quit: function(callback) {
|
||||
var self = this,
|
||||
response = function(err, data) {
|
||||
caller(callback, err, data);
|
||||
self.close();
|
||||
};
|
||||
|
||||
this.command("quit", response, [221, 250]);
|
||||
}
|
||||
};
|
||||
|
||||
for (var each in events.EventEmitter.prototype) {
|
||||
SMTP.prototype[each] = events.EventEmitter.prototype[each];
|
||||
}
|
||||
|
||||
exports.SMTP = SMTP;
|
||||
exports.state = SMTPState;
|
64
node_modules/emailjs/smtp/tls.js
generated
vendored
Normal file
64
node_modules/emailjs/smtp/tls.js
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
var crypto = require('crypto');
|
||||
var tls = require('tls');
|
||||
|
||||
var secure = function(socket, options, cb)
|
||||
{
|
||||
var sslcontext = crypto.createCredentials(options);
|
||||
//sslcontext.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA');
|
||||
|
||||
var pair = tls.createSecurePair(sslcontext, false);
|
||||
|
||||
var cleartext = pipe(pair, socket);
|
||||
|
||||
pair.on('secure', function()
|
||||
{
|
||||
var verifyError = (pair.ssl || pair._ssl).verifyError();
|
||||
|
||||
if(verifyError)
|
||||
{
|
||||
cleartext.authorized = false;
|
||||
cleartext.authorizationError = verifyError;
|
||||
}
|
||||
else
|
||||
{
|
||||
cleartext.authorized = true;
|
||||
}
|
||||
|
||||
if (cb) cb();
|
||||
});
|
||||
|
||||
cleartext._controlReleased = true;
|
||||
return cleartext;
|
||||
};
|
||||
|
||||
var pipe = function(pair, socket)
|
||||
{
|
||||
pair.encrypted.pipe(socket);
|
||||
socket.pipe(pair.encrypted);
|
||||
|
||||
var cleartext = pair.cleartext;
|
||||
cleartext.socket = socket;
|
||||
cleartext.encrypted = pair.encrypted;
|
||||
cleartext.authorized = false;
|
||||
|
||||
function onerror(e)
|
||||
{
|
||||
if (cleartext._controlReleased)
|
||||
{
|
||||
cleartext.emit('error', e);
|
||||
}
|
||||
}
|
||||
|
||||
function onclose()
|
||||
{
|
||||
socket.removeListener('error', onerror);
|
||||
socket.removeListener('close', onclose);
|
||||
}
|
||||
|
||||
socket.on('error', onerror);
|
||||
socket.on('close', onclose);
|
||||
|
||||
return cleartext;
|
||||
};
|
||||
|
||||
exports.secure = secure;
|
Reference in New Issue
Block a user