mailer
Advanced tools
Comparing version 0.4.52 to 0.5.6
@@ -12,3 +12,3 @@ var email = require("./lib/node_mailer"); | ||
subject : "node_mailer test email", | ||
template : "../templates/sample.txt", // path to template name | ||
template : "./templates/sample.txt", // path to template name | ||
data : { | ||
@@ -25,3 +25,2 @@ "username": "Billy Bob", | ||
authentication : "login", // auth login is supported; anything else is no auth | ||
username : "dXNlcm5hbWU=", // Base64 encoded username | ||
@@ -28,0 +27,0 @@ password : "cGFzc3dvcmQ=" // Base64 encoded password |
18
demo.js
var email = require("./lib/node_mailer"); | ||
for(var i = 0; i < 10; i++){ | ||
email.send({ | ||
host : "localhost", // smtp server hostname | ||
port : "25", // smtp server port | ||
domain : "localhost", // domain used by client to identify itself to server | ||
to : "marak.squires@gmail.com", | ||
from : "obama@whitehouse.gov", | ||
ssl: true, | ||
host : "smtp.gmail.com", // smtp server hostname | ||
port : 465, // smtp server port | ||
domain : "[127.0.0.1]", // domain used by client to identify itself to server | ||
to : "bradley.meck@gmail.com", | ||
from : "bradley.meck@gmail.com", | ||
subject : "node_mailer test email", | ||
body: "Hello! This is a test of the node_mailer.", | ||
authentication : "login", // auth login is supported; anything else is no auth | ||
username : "dXNlcm5hbWU=", // Base64 encoded username | ||
password : "cGFzc3dvcmQ=" // Base64 encoded password | ||
username : 'username', // Base64 encoded username | ||
password : 'password' // Base64 encoded password | ||
}, | ||
@@ -20,2 +20,2 @@ function(err, result){ | ||
}); | ||
} | ||
} |
@@ -25,331 +25,119 @@ /* Copyright (c) 2009-2010 Marak Squires, Elijah Insua, Fedor Indutny - http://github.com/marak/node_mailer | ||
var SMTPClient = require("nodemailer").SMTPClient; | ||
var EmailMessage = require("nodemailer").EmailMessage; | ||
var fs = require('fs'); | ||
var mustache = require('../vendor/mustache'); | ||
var _templateCache = {}; | ||
var tcp = require('net'), | ||
fs = require('fs'), | ||
carrier = require('carrier'), | ||
colors = require('colors'), | ||
mustache = require('../vendor/mustache'), | ||
defaultOptions = { | ||
to : "marak.squires@gmail.com", | ||
from : "obama@whitehouse.gov", | ||
data : {}, | ||
subject : "node_mailer test email", | ||
body : "hello this is a test email from the node_mailer", | ||
host : "localhost", | ||
domain : "localhost", | ||
'content-type': 'text/plain', | ||
port : 25 | ||
}, | ||
keys = Object.keys(defaultOptions), | ||
connections = {}, | ||
templateCache = {}; | ||
function SMTPClientPool() { | ||
this.servers = {}; | ||
} | ||
SMTPClientPool.prototype.addClient = function(port,host,options) { | ||
if(this.servers[host] && this.servers[host][options.user]) return; | ||
var hostClients = this.servers[host] || (this.servers[host] = {}); | ||
var pool = this; | ||
var client = hostClients[options.user] = new SMTPClient(host,port,options); | ||
client.on("close",function() { | ||
if(client == hostClients[options.user]) { | ||
//only because this could be crazy long lived and dynamic | ||
delete hostClients[options.user]; | ||
if(Object.keys(hostClients).length == 0) { | ||
delete pool.servers[host] | ||
} | ||
} | ||
}) | ||
client.on("empty",function(){ | ||
delete hostClients[options.user]; | ||
client.close();}) | ||
} | ||
SMTPClientPool.prototype.send = function send(message, callback) { | ||
this.servers[message.SERVER.host][message.SERVER.user].sendMail(message,callback); | ||
} | ||
exports.connections = connections; | ||
var pool = new SMTPClientPool(); | ||
var Connection = function(options, callback) { | ||
this.options = options; | ||
this.callback = callback || function(){}; | ||
}; | ||
Connection.prototype = { | ||
options : {}, | ||
_stream : null, | ||
_connecting : false, | ||
_queue : [], | ||
_processing : false, | ||
_process : function() { | ||
var self = this; | ||
if (!self._stream || | ||
['open', 'writeOnly'].indexOf(self._stream.readyState) === -1) | ||
{ | ||
return; | ||
} | ||
exports.send = function node_mail(message, callback) { | ||
if (!self._processing) { | ||
var dequeue = function() { | ||
if (!self._stream || | ||
['open', 'writeOnly'].indexOf(self._stream.readyState) === -1) | ||
{ | ||
self.connect(function(err) { | ||
if (err) { | ||
console.log(err.red.bold); | ||
return; | ||
} | ||
doProcess(); | ||
}); | ||
return; | ||
} else doProcess(); | ||
var server = { | ||
host: message.host, | ||
hostname: message.domain, | ||
port: + message.port, | ||
use_authentication: message.authentication, | ||
ssl: message.ssl, | ||
user: new Buffer(message.username || '', 'base64').toString('utf8'), | ||
pass: new Buffer(message.password || '', 'base64').toString('utf8'), | ||
debug: true | ||
}; | ||
function doProcess() { | ||
var email = self._queue.shift(); | ||
if(message.username || message.password) { | ||
pool.addClient(server.port, server.host, server); | ||
} | ||
if (!email) return; | ||
var from = email.options.from, | ||
msg; | ||
function _cb_tpl(fn) { | ||
return function(err) { | ||
if (err) { | ||
try { | ||
self.disconnect(); | ||
} catch(e) { | ||
} | ||
return; | ||
} | ||
fn(); | ||
}; | ||
} | ||
writeFrom(); | ||
function writeFrom() { | ||
self._stream.write( | ||
'mail from: ' + (/<.+>/.test(from) ? from : '<' + from + '>') + | ||
'\r\n' | ||
); | ||
self._stream.promise.wait('accepted', _cb_tpl(writeTo)); | ||
} | ||
function writeTo() { | ||
self._stream.write('rcpt to: ' + email.options.to + '\r\n'); | ||
self._stream.promise.wait('accepted', _cb_tpl(writeDataStart)); | ||
} | ||
function writeDataStart() { | ||
self._stream.write('data\r\n'); | ||
self._stream.promise.wait('continue', _cb_tpl(writeData)); | ||
} | ||
function writeData() { | ||
self._stream.write([ | ||
'From: ' + email.options.from, | ||
'To: ' + email.options.to, | ||
'Subject: ' + email.options.subject, | ||
'Content-Type: ' + email.options['content-type'], | ||
'', | ||
email.options.body, | ||
'.', | ||
'RSET', | ||
'' | ||
].join('\r\n')); | ||
onEnd(); | ||
} | ||
function onEnd() { | ||
if (self._queue.length > 0) { | ||
process.nextTick(dequeue); | ||
} else { | ||
self.disconnect(); | ||
} | ||
} | ||
} | ||
}; | ||
self._processing = true; | ||
process.nextTick(dequeue); | ||
// If a template was passed in as part of the message | ||
if (message.template) { | ||
// If the template path is in the cache | ||
if (_templateCache[message.template]) { | ||
// If the template is already fully loaded in the cahe | ||
if (_templateCache[message.template].loaded) { | ||
// Use the cached template and send the email | ||
message.body = mustache.to_html(_templateCache[message.template].template, message.data); | ||
pool.send(new EmailMessage({ | ||
to: message.to, | ||
sender: message.from, | ||
subject: message.subject, | ||
body: message.body, | ||
server: server, | ||
debug: message.debug | ||
}), callback); | ||
} | ||
else { | ||
// We've started to load the template, but it's not loaded yet. queue up this message to be sent later | ||
_templateCache[message.template].queue.push(message); | ||
} | ||
} | ||
}, | ||
connect : function(fn) { | ||
if (!this._stream && !this._connecting) { | ||
this._connecting = true; | ||
var self = this, | ||
stream = tcp.createConnection(this.options.port, this.options.host), | ||
stream_promise = new process.EventEmitter; | ||
stream.promise = stream_promise; | ||
// Parse reply lines | ||
carrier.carry(stream, function(line) { | ||
// If we got successfull auth | ||
if (/^235\s/.test(line)) { | ||
stream_promise.emit('auth'); | ||
} else | ||
// If server has accepted something | ||
if (/^250\s/.test(line)) { | ||
stream_promise.emit('accepted'); | ||
} else | ||
// If server says Continue | ||
if (/^354\s/.test(line)) { | ||
stream_promise.emit('continue'); | ||
} | ||
}); | ||
// Wait for event with timeout | ||
stream_promise.wait = function(event, callback, timeout) { | ||
stream_promise.on(event, onEvent); | ||
function onEvent() { | ||
stream_promise.removeListener(event, onEvent); | ||
clearTimeout(timeout); | ||
callback(null); | ||
} | ||
var timeout = setTimeout(function() { | ||
stream_promise.removeListener(event, onEvent); | ||
callback('timeout'); | ||
}, timeout || 5000); | ||
}; | ||
stream.setEncoding("utf8"); | ||
stream.on("connect", function() { | ||
self._stream = stream; | ||
self._connecting = false; | ||
stream.write("helo " + self.options.domain + "\r\n"); | ||
stream.promise.wait('accepted', function() { | ||
if(self.options.authentication === "login") { | ||
stream.write("auth login\r\n"); | ||
stream.write(self.options.username + "\r\n"); | ||
stream.write(self.options.password + "\r\n"); | ||
if (typeof fn === 'function') { | ||
// Set auth callback | ||
stream_promise.wait('auth', function(err) { | ||
if (err) { | ||
try { | ||
self.disconnect(); | ||
} catch (e) { | ||
} | ||
} | ||
fn(err, self._stream); | ||
}, self.options.auth_timeout); | ||
} | ||
} else { | ||
if (typeof fn === 'function') { | ||
fn(null, self._stream); | ||
} | ||
} | ||
}); | ||
}); | ||
else { | ||
// The template path wasn't found in the cache, start to load the template | ||
_templateCache[message.template] = {}; | ||
_templateCache[message.template].loaded = false; | ||
_templateCache[message.template].template = ''; | ||
_templateCache[message.template].queue = []; | ||
stream.on("error", function() { | ||
self.callback(new Error("could not connect")); | ||
console.log(arguments); | ||
stream.destroy(); | ||
self._connecting = false; | ||
stream = null; | ||
}); | ||
stream.on("end", function() { | ||
self.callback(null); | ||
self._connecting = false; | ||
self._stream = null; | ||
}); | ||
} else if (typeof fn === 'function') { | ||
process.nextTick(function() { | ||
fn(this._stream); | ||
}); | ||
} | ||
}, | ||
queue : function(email) { | ||
var self = this; | ||
self._queue.push(email); | ||
process.nextTick(function() { | ||
self.connect(function(err) { | ||
fs.readFile(message.template, function(err, result){ | ||
if (err) { | ||
// Probably process error | ||
console.log(err.red.bold); | ||
console.log(err); | ||
return; | ||
} | ||
process.nextTick(function() { | ||
self._process() | ||
}); | ||
}); | ||
}); | ||
}, | ||
disconnect : function() { | ||
this._processing = false; | ||
if (this._stream) { | ||
this._stream.end('quit\r\n'); | ||
this._stream = null; | ||
} | ||
} | ||
}; | ||
var Email = function(options, callback) { | ||
this.options = options; | ||
if (options.lineWrap === undefined && /html/i.test(options['content-type'])) { | ||
options.lineWrap = false; | ||
} | ||
if (options.lineWrap !== false) { | ||
this.options.body = this.lineWrap(this.options.body); | ||
} | ||
_templateCache[message.template].template = result.toString(); | ||
_templateCache[message.template].loaded = true; | ||
// create a new connection if needed | ||
var connectionKey = [options.host, options.port].join(':'); | ||
if (!connections[connectionKey]) { | ||
connections[connectionKey] = new Connection(options, callback); | ||
} | ||
// "Drain" the queue | ||
_templateCache[message.template].queue.forEach(function(msg, i){ | ||
msg.body = mustache.to_html(_templateCache[message.template].template, msg.data); | ||
pool.send(new EmailMessage({ | ||
to: msg.to, | ||
sender: msg.from, | ||
subject: msg.subject, | ||
body: msg.body, | ||
server: server, | ||
debug: msg.debug | ||
}), callback); | ||
}); | ||
// queue this email in the appropriate connection | ||
connections[connectionKey].queue(this); | ||
}; | ||
// Clear the queue out | ||
_templateCache[message.template].queue = []; | ||
Email.prototype = { | ||
options : {}, | ||
lineWrap : function(str) { | ||
// Split by lines | ||
return str.split(/\r|\n|\r\n/g).reduce(function(prev, piece) { | ||
var parts = []; | ||
// Wrap line | ||
while (piece.length) { | ||
var match = piece.match(/^.{1,80}(?:\s|$)/), | ||
matchlen; | ||
if (!match) { | ||
match = [piece.substr(0, 80)]; | ||
} | ||
if (matchlen = match[0].length) { | ||
parts.push(match[0]); | ||
piece = piece.substr(matchlen); | ||
} | ||
}; | ||
return prev.concat(parts); | ||
}, []).join('\r\n'); | ||
} | ||
}; | ||
exports.send = function(options, callback) { | ||
var i, key; | ||
options = options || {}; | ||
// fill in the gaps with defaultOptions | ||
for (i=0; i<keys.length; i++) { | ||
key = keys[i]; | ||
if (typeof options[key] === "undefined") { | ||
options[key] = defaultOptions[key]; | ||
} | ||
} | ||
// determine if we need to load a template before processing email for sending | ||
// there is currently a bit of a dog pile effect on the template caching, but in theory | ||
// it should eventually cache before we run out of File Descriptors. templateCache should be refactored | ||
if(options.template){ | ||
if(typeof templateCache[options.template] != 'undefined'){ | ||
options.body = mustache.to_html(templateCache[options.template], options.data); | ||
return new Email(options, callback); | ||
} | ||
else{ | ||
fs.readFile(options.template, function (err, data) { | ||
if (err) throw err; | ||
templateCache[options.template] = data.toString(); | ||
options.body = mustache.to_html(templateCache[options.template], options.data); | ||
return new Email(options, callback); | ||
}); | ||
} | ||
} | ||
else{ | ||
return new Email(options, callback); | ||
else { // No template being used | ||
pool.send(new EmailMessage({ | ||
to: message.to, | ||
sender: message.from, | ||
subject: message.subject, | ||
body: message.body, | ||
server: server, | ||
debug: message.debug | ||
}), callback); | ||
} | ||
}; |
{ | ||
"name": "mailer", | ||
"description": "send emails from node.js to a smtp server, simple as cake", | ||
"version": "0.4.52", | ||
"version": "0.5.6", | ||
"author": "Marak Squires", | ||
"contributors" : [ | ||
"Elijah Insua <tmpvar@gmail.com> (http://tmvpar.com/)", | ||
"Fedor Indutny <fedor.indutny@gmail.com> (http://indutny.com/)" | ||
"Fedor Indutny <fedor.indutny@gmail.com> (http://indutny.com/)", | ||
"Bradley Meck <bradley.meck@gmail.com> (http://bradleymeck.com)" | ||
], | ||
@@ -15,7 +16,7 @@ "repository": { | ||
"dependencies": { | ||
"carrier": ">= 0.0.4", | ||
"nodemailer": ">= 0.1.10", | ||
"colors": ">= 0.3.0" | ||
}, | ||
"engine": [ "node >=0.1.90" ], | ||
"engine": [ "node >=0.3.0" ], | ||
"main": "lib/node_mailer" | ||
} |
@@ -30,2 +30,3 @@ #v0.4.51 | ||
port : "25", // smtp server port | ||
ssl: true, // for SSL support - REQUIRES NODE v0.3.x OR HIGHER | ||
domain : "localhost", // domain used by client to identify itself to server | ||
@@ -45,2 +46,7 @@ to : "marak.squires@gmail.com", | ||
# USING SSL | ||
node_mailer SSL connections (not TLS / STARTTLS, just SSL from the beginning of the connection). To use it, | ||
add an option ssl: true. See usage above. | ||
# USING TEMPLATES | ||
@@ -50,3 +56,3 @@ | ||
/templates/sample.text | ||
/templates/sample.txt | ||
@@ -97,5 +103,7 @@ Hello {{username}}, | ||
## Authors | ||
Marak Squires, Elijah Insua, Fedor Indutny | ||
Marak Squires, Elijah Insua, Fedor Indutny, Bradley Meck | ||
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
106
0
36393
10
473
+ Addednodemailer@>= 0.1.10
+ Addednodemailer@6.9.13(transitive)
- Removedcarrier@>= 0.0.4
- Removedcarrier@0.3.0(transitive)