mirror of https://github.com/eleith/emailjs.git
support starttls, update docs
This commit is contained in:
parent
e41bca2d92
commit
3679d2b0f3
119
Readme.md
119
Readme.md
|
@ -1,60 +1,123 @@
|
|||
#v0.1
|
||||
|
||||
###send emails, html and attachments from node.js to any smtp server
|
||||
### emailjs
|
||||
|
||||
send emails, html and attachments from node.js to any smtp server
|
||||
|
||||
### Installing
|
||||
|
||||
npm install emailjs
|
||||
npm install emailjs
|
||||
|
||||
# FEATURES
|
||||
- works with SSL smtp servers (ex: gmail)
|
||||
- works with smtp server authentication (PLAIN, LOGIN, CRAMMD5)
|
||||
- works with SSL and TLS smtp servers (ex: gmail)
|
||||
- supports smtp authentication (PLAIN, LOGIN, CRAMMD5)
|
||||
- emails are queued and the queue is sent asynchronously
|
||||
- supports sending html emails and emails with multiple attachments
|
||||
- supports sending html emails and emails with multiple attachments (MIME)
|
||||
- works with nodejs 3.8 and above
|
||||
|
||||
# REQUIRES
|
||||
- access to an SMTP Server (ex: gmail)
|
||||
|
||||
# USAGE - text only emails
|
||||
# API
|
||||
|
||||
## email.server.connect(options)
|
||||
|
||||
// options is an object with the following keys
|
||||
options =
|
||||
{
|
||||
username // username for logging into smtp
|
||||
password // password for logging into smtp
|
||||
host // smtp host
|
||||
port // smtp port (if null a standard port number will be used)
|
||||
ssl // boolean or object {key, ca, cert} (if exists, ssl connection will be made)
|
||||
tls // boolean (if true, starttls will be initiated)
|
||||
timeout // max number of milliseconds to wait for smtp responses (defaults to 5000)
|
||||
domain // domain to greet smtp with (defaults to os.hostname)
|
||||
}
|
||||
|
||||
## email.server.send(message, callback)
|
||||
|
||||
// message can be a smtp.Message (as returned by email.message.create)
|
||||
// or an object identical to the first argument accepted by email.message.create
|
||||
|
||||
// callback will be executed with (err, message)
|
||||
// either when message is sent or an error has occurred
|
||||
|
||||
## email.message.create(headers)
|
||||
|
||||
// headers is an object with the following keys ('from' and 'to' are required)
|
||||
// returns a Message object
|
||||
|
||||
headers =
|
||||
{
|
||||
text // text of the email
|
||||
from // sender of the format (address or name <address> or "name" <address>)
|
||||
to // recipients (same format as above), multiple recipients are separated by a comma
|
||||
cc // carbon copied recipients (same format as above)
|
||||
bcc // blind carbon copied recipients (same format as above)
|
||||
subject // string subject of the email
|
||||
}
|
||||
|
||||
## Message.attach_alternative(html)
|
||||
|
||||
// should only be called once
|
||||
|
||||
html // string representing the html version of the email message
|
||||
|
||||
## Message.attach(path, mime_type, name)
|
||||
|
||||
// can be called multiple times, each creating a new
|
||||
// attachment on the email itself
|
||||
|
||||
path // string to where the file is located
|
||||
mime_type // string of the file mime type
|
||||
name // name to give the file as perceived by the recipient
|
||||
|
||||
# EXAMPLE USAGE - text only emails
|
||||
|
||||
var email = require("./path/to/emailjs/email");
|
||||
var server = email.server.connect({
|
||||
user:yourUSER,
|
||||
password:yourPASS,
|
||||
host:"smtp.gmail.com",
|
||||
port:465,
|
||||
domain:yourDOMAIN,
|
||||
secure:true});
|
||||
user: "username",
|
||||
password:"password",
|
||||
host: "smtp.gmail.com",
|
||||
ssl: true
|
||||
});
|
||||
|
||||
// send the message and get a callback with an error or details of the message that was sent
|
||||
server.send({
|
||||
text:"i hope this works",
|
||||
from:yourUSER + "@gmail.com",
|
||||
to:yourFRIEND,
|
||||
subject:"testing emailjs"},
|
||||
function(err, message) {
|
||||
console.log(err || message);
|
||||
});
|
||||
text: "i hope this works",
|
||||
from: "you <username@gmail.com>",
|
||||
to: "someone <someone@gmail.com>, another <another@gmail.com>",
|
||||
cc: "else <else@gmail.com>",
|
||||
subject: "testing emailjs"
|
||||
}, function(err, message) { console.log(err || message); });
|
||||
|
||||
# USAGE - html emails and attachments
|
||||
# EXAMPLE USAGE - html emails and attachments
|
||||
|
||||
var email = require("./path/to/emailjs/email");
|
||||
var server = email.server.connect({
|
||||
user:yourUSER,
|
||||
password:yourPASS,
|
||||
host:"smtp.gmail.com",
|
||||
port:465,
|
||||
domain:yourDOMAIN,
|
||||
secure:true});
|
||||
user: "username",
|
||||
password:"password",
|
||||
host: "smtp.gmail.com",
|
||||
ssl: true
|
||||
});
|
||||
|
||||
var message = email.message.create("i hope this works", {from:yourUSER + "@gmail.com", to:yourFRIEND, subject:"testing emailjs"});
|
||||
var headers = {
|
||||
text: "i hope this works",
|
||||
from: "you <username@gmail.com>",
|
||||
to: "someone <someone@gmail.com>, another <another@gmail.com>",
|
||||
cc: "else <else@gmail.com>",
|
||||
subject: "testing emailjs"
|
||||
};
|
||||
|
||||
// create the message
|
||||
var message = email.message.create(headers);
|
||||
|
||||
// attach an alternative html email for those with advanced email clients
|
||||
message.attach_alternative("i <i>hope</i> this works!");
|
||||
|
||||
// attach attachments because you can!
|
||||
message..attach("path/to/file.zip", "application/zip", "renamed.zip");
|
||||
message.attach("path/to/file.zip", "application/zip", "renamed.zip");
|
||||
|
||||
// send the message and get a callback with an error or details of the message that was sent
|
||||
server.send(message, function(err, message) { console.log(err || message); });
|
||||
|
|
26
demo.js
26
demo.js
|
@ -4,18 +4,19 @@ var os = require('os');
|
|||
|
||||
SMTP =
|
||||
{
|
||||
USER: '',
|
||||
PASS: '',
|
||||
USER: 'username',
|
||||
PASS: 'password',
|
||||
HOST: "smtp.gmail.com",
|
||||
SECURE: true,
|
||||
PORT: 465
|
||||
PORT: null, // emailjs will use default SMTP port standards (587, 465, 25) where appropriate
|
||||
SSL: false, // use ssl from begin to end on smtp connection, accepts object of (key, ca, certs) as well...
|
||||
TLS: true // use STARTTLS encrypting stream after initial smtp connection
|
||||
};
|
||||
|
||||
MESSAGE =
|
||||
{
|
||||
DOMAIN: os.hostname(),
|
||||
FROM: '',
|
||||
TO: '',
|
||||
FROM: 'username@gmail.com',
|
||||
TO: 'person1 <person1@example.com>, person2 <person2@example.com>, person3 <person2@example.com>',
|
||||
SUBJECT: 'testing emailjs',
|
||||
TEXT: 'i hope this works',
|
||||
HTML: 'i <i>hope</i> <b>this</b> works',
|
||||
|
@ -28,9 +29,16 @@ MESSAGE =
|
|||
};
|
||||
|
||||
|
||||
var server = email.server.connect({user:SMTP.USER, password:SMTP.PASS, host:SMTP.HOST, port:SMTP.PORT, domain:MESSAGE.DOMAIN, secure:SMTP.SECURE});
|
||||
var msg = email.message.create(MESSAGE.TEXT, {from:MESSAGE.FROM, to:MESSAGE.TO, subject:MESSAGE.SUBJECT});
|
||||
var server = email.server.connect({
|
||||
user: SMTP.USER,
|
||||
password: SMTP.PASS,
|
||||
host: SMTP.HOST,
|
||||
port: SMTP.PORT,
|
||||
tls: SMTP.TLS,
|
||||
ssl: SMTP.SSL,
|
||||
domain: MESSAGE.DOMAIN});
|
||||
|
||||
var msg = email.message.create({text:MESSAGE.TEXT, from:MESSAGE.FROM, to:MESSAGE.TO, subject:MESSAGE.SUBJECT});
|
||||
msg.attach_alternative(MESSAGE.HTML).attach(ATTACH.PATH, ATTACH.TYPE, ATTACH.NAME);
|
||||
|
||||
server.send(msg, function(err, message) { console.log(message); });
|
||||
server.send(msg, function(err, message) { console.log(err || message); });
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "emailjs",
|
||||
"description": "send emails, html and attachments from node.js to any smtp server",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.2",
|
||||
"author": "eleith",
|
||||
"contributors" : [
|
||||
],
|
||||
|
|
|
@ -82,6 +82,12 @@ Client.prototype =
|
|||
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"]));
|
||||
|
||||
self.queue.push(stack);
|
||||
self._poll();
|
||||
}
|
||||
|
@ -114,7 +120,7 @@ Client.prototype =
|
|||
_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 + '>');
|
||||
self.smtp.rcpt(self._sendsmtp(stack, stack.to.length ? self._sendrcpt : self._senddata), '<'+ to +'>');
|
||||
},
|
||||
|
||||
_senddata: function(stack)
|
||||
|
|
|
@ -15,13 +15,15 @@ var generate_boundary = function()
|
|||
return text;
|
||||
};
|
||||
|
||||
var Message = function(text, headers)
|
||||
var Message = function(headers)
|
||||
{
|
||||
this.attachments = [];
|
||||
this.text = text;
|
||||
this.text = headers.text;
|
||||
this.html = null;
|
||||
this.header = {"message-id":"<" + (new Date()).getTime() + "." + process.pid + "@" + os.hostname() +">"};
|
||||
|
||||
delete headers.text;
|
||||
|
||||
for(var header in headers)
|
||||
{
|
||||
// allow any headers the user wants to set??
|
||||
|
@ -239,7 +241,11 @@ var MessageStream = function(message)
|
|||
var data = [];
|
||||
|
||||
for(var header in self.message.header)
|
||||
data = data.concat([header, ": ", self.message.header[header], CRLF]);
|
||||
{
|
||||
// do not output BCC in the headers...
|
||||
if(!/bcc/i.test(header))
|
||||
data = data.concat([header, ": ", self.message.header[header], CRLF]);
|
||||
}
|
||||
|
||||
self.emit('data', data.join(''));
|
||||
output_process(output_data);
|
||||
|
|
132
smtp/smtp.js
132
smtp/smtp.js
|
@ -7,12 +7,14 @@ 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'};
|
||||
var TIMEOUT = 5000;
|
||||
|
@ -68,14 +70,15 @@ var SMTP = function(options)
|
|||
|
||||
this.sock = null;
|
||||
this.timeout = options.timeout || TIMEOUT;
|
||||
this.secure = options.secure || false;
|
||||
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.secure ? SMTP_SSL_PORT : SMTP_PORT;
|
||||
this.ssl = options.ssl;
|
||||
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;
|
||||
|
||||
// keep private
|
||||
SMTP_USER = options.user;
|
||||
|
@ -103,32 +106,35 @@ SMTP.prototype =
|
|||
{
|
||||
options = options || {};
|
||||
|
||||
var self = this;
|
||||
var self = this, connect_timeout = null;
|
||||
|
||||
self.host = host || self.host;
|
||||
self.port = port || self.port;
|
||||
self.secure = options.secure || self.secure;
|
||||
self.ssl = options.ssl || self.ssl;
|
||||
|
||||
if(self._state != SMTPState.NOTCONNECTED)
|
||||
self.quit();
|
||||
|
||||
|
||||
var connected = function(err)
|
||||
{
|
||||
clearTimeout(connect_timeout);
|
||||
|
||||
if(!err)
|
||||
{
|
||||
if(self.secure)
|
||||
log("connected: " + self.host + ":" + self.port);
|
||||
|
||||
if(self.ssl && !self.tls)
|
||||
{
|
||||
// if key/ca/cert was passed and ssl is used, check if authorized is false
|
||||
if(self.ssl && !self.sock.authorize)
|
||||
// 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, {code:SMTPError.CONNECTIONAUTH, message:"could not establish an ssl connection", error:err});
|
||||
return;
|
||||
}
|
||||
else
|
||||
self._secure = true;
|
||||
}
|
||||
|
||||
log("connected: " + self.host + ":" + self.port);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -143,7 +149,7 @@ SMTP.prototype =
|
|||
|
||||
if(!err && msg.code == '220')
|
||||
{
|
||||
log("response: " + data);
|
||||
log(data);
|
||||
|
||||
// might happen first, so no need to wait on connected()
|
||||
self._state = SMTPState.CONNECTED;
|
||||
|
@ -156,38 +162,42 @@ SMTP.prototype =
|
|||
log("response (error): " + err);
|
||||
self.close(true);
|
||||
|
||||
caller(callback, {code:err.code, error:err.error});
|
||||
caller(callback, {code:err.code, error:err.error, message:err.message});
|
||||
}
|
||||
else
|
||||
{
|
||||
log("response (data): " + data);
|
||||
self.quit();
|
||||
|
||||
caller(callback, {code:SMTPError.BadResponse, message:"bad response on connection", smtp:data, error:err});
|
||||
caller(callback, {code:SMTPError.BADRESPONSE, message:"bad response on connection", smtp:data, error:err});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var timedout = function()
|
||||
{
|
||||
if(self._state != SMTPState.CONNECTED)
|
||||
{
|
||||
self.close(true);
|
||||
caller(callback, {code:SMTPError.TIMEDOUT, message:"timedout while connecting to smtp server"});
|
||||
}
|
||||
};
|
||||
|
||||
self._state = SMTPState.CONNECTING;
|
||||
|
||||
if(self.secure)
|
||||
if(self.ssl)
|
||||
{
|
||||
// object, may contain 'key' and/or 'ca' and/or 'cert'
|
||||
if(self.ssl)
|
||||
self.sock = tls.connect(self.port, self.host, self.ssl, connected);
|
||||
|
||||
else
|
||||
self.sock = tls.connect(self.port, self.host, connected);
|
||||
self.sock = tls.connect(self.port, self.host, self.ssl, connected);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.sock = net.Socket();
|
||||
self.sock.connect(self.port, self.host, connected);
|
||||
}
|
||||
|
||||
|
||||
connect_timeout = setTimeout(timedout, self.timeout);
|
||||
SMTPResponse.watch(self.sock);
|
||||
|
||||
|
||||
self.sock.setTimeout(self.timeout);
|
||||
self.sock.once('response', response);
|
||||
},
|
||||
|
@ -198,11 +208,11 @@ SMTP.prototype =
|
|||
|
||||
if(self.sock && self._state == SMTPState.CONNECTED)
|
||||
{
|
||||
log("send: " + str);
|
||||
log(str);
|
||||
|
||||
var response = function(err, data)
|
||||
{
|
||||
log("response: " + (data || err));
|
||||
log((data || err));
|
||||
|
||||
if(err)
|
||||
self.close(true);
|
||||
|
@ -253,15 +263,49 @@ SMTP.prototype =
|
|||
this.command("helo " + (domain || this.domain), callback);
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
// STARTTLS is not supported since node net api doesn't support upgrading a socket to a secure socket
|
||||
// use ssl instead of tls. the benefit is that the entire communication will be encrypted from the beginning
|
||||
starttls: function(callback, domain)
|
||||
// */
|
||||
starttls: function(callback)
|
||||
{
|
||||
this.command("starttls", callback);
|
||||
var self = this,
|
||||
|
||||
response = function(err, data)
|
||||
{
|
||||
if(!err)
|
||||
{
|
||||
var secured_socket = null;
|
||||
|
||||
var secured_timer = null;
|
||||
|
||||
var secured = function()
|
||||
{
|
||||
clearTimeout(secured_timer);
|
||||
|
||||
self._secure = true;
|
||||
self.sock = secured_socket;
|
||||
|
||||
SMTPResponse.watch(self.sock);
|
||||
caller(callback, err);
|
||||
};
|
||||
|
||||
var timeout = function()
|
||||
{
|
||||
caller(callback, {code:SMTPError.TIMEDOUT, message:"connection timedout during STARTTLS handshake"});
|
||||
};
|
||||
|
||||
secured_timer = setTimeout(timeout, self.timeout);
|
||||
secured_socket = starttls.secure(self.sock, self.ssl, secured);
|
||||
}
|
||||
else
|
||||
{
|
||||
caller(callback, err);
|
||||
}
|
||||
};
|
||||
|
||||
this.command("starttls", response, [220]);
|
||||
},
|
||||
*/
|
||||
|
||||
ehlo: function(callback, domain)
|
||||
{
|
||||
|
@ -295,8 +339,22 @@ SMTP.prototype =
|
|||
self.features[parse[1].toLowerCase()] = parse[2] || true;
|
||||
}
|
||||
});
|
||||
|
||||
caller(callback, null, data);
|
||||
|
||||
if(self.tls && !self._secure)
|
||||
{
|
||||
var secured = function(err, data)
|
||||
{
|
||||
if(!err)
|
||||
self.ehlo(callback, domain);
|
||||
|
||||
else
|
||||
caller(callback, err, data);
|
||||
};
|
||||
|
||||
self.starttls(secured);
|
||||
}
|
||||
else
|
||||
caller(callback, null, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -336,8 +394,7 @@ SMTP.prototype =
|
|||
|
||||
rcpt: function(callback, to)
|
||||
{
|
||||
// SMTP 'rcpt' command -- indicates 1 recipient for self mail
|
||||
this.command("rcpt TO:" + to, callback, [250, 251]);
|
||||
this.command("RCPT TO:" + to, callback, [250, 251]);
|
||||
},
|
||||
|
||||
data: function(callback)
|
||||
|
@ -347,11 +404,12 @@ SMTP.prototype =
|
|||
|
||||
data_end: function(callback)
|
||||
{
|
||||
this.command(CRLF + "." + CRLF, callback);
|
||||
this.command(CRLF + ".", callback);
|
||||
},
|
||||
|
||||
message: function(data)
|
||||
{
|
||||
log(data);
|
||||
this.sock.write(data);
|
||||
},
|
||||
|
||||
|
@ -448,7 +506,7 @@ SMTP.prototype =
|
|||
|
||||
for(var i = 0; i < preferred.length; i++)
|
||||
{
|
||||
if((self.features["auth"]).indexOf(preferred[i]) != -1)
|
||||
if((self.features["auth"] || "").indexOf(preferred[i]) != -1)
|
||||
{
|
||||
method = preferred[i];
|
||||
break;
|
||||
|
@ -497,7 +555,7 @@ SMTP.prototype =
|
|||
self.command("AUTH " + AUTH_METHODS.PLAIN + " " + encode_plain(login.user, login.password), response, [235, 503]);
|
||||
|
||||
else if(!method)
|
||||
caller(callback, {code:SMTPError.AUTHNOTSUPPORTED, message:"authorization no supported", smtp:data});
|
||||
caller(callback, {code:SMTPError.AUTHNOTSUPPORTED, message:"no form of authorization supported", smtp:data});
|
||||
};
|
||||
|
||||
self.ehlo_or_helo_if_needed(initiate, domain);
|
||||
|
@ -515,9 +573,9 @@ SMTP.prototype =
|
|||
}
|
||||
|
||||
this._state = SMTPState.NOTCONNECTED;
|
||||
this._secure = false;
|
||||
this.sock = null;
|
||||
this.features = null;
|
||||
this.secure = false;
|
||||
this.loggedin = false;
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in New Issue