mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
173 lines
5.2 KiB
JavaScript
173 lines
5.2 KiB
JavaScript
var Future = Npm.require('fibers/future');
|
|
var urlModule = Npm.require('url');
|
|
var MailComposer = Npm.require('mailcomposer').MailComposer;
|
|
|
|
Email = {};
|
|
EmailTest = {};
|
|
|
|
var makePool = function (mailUrlString) {
|
|
var mailUrl = urlModule.parse(mailUrlString);
|
|
if (mailUrl.protocol !== 'smtp:')
|
|
throw new Error("Email protocol in $MAIL_URL (" +
|
|
mailUrlString + ") must be 'smtp'");
|
|
|
|
var port = +(mailUrl.port);
|
|
var auth = false;
|
|
if (mailUrl.auth) {
|
|
var parts = mailUrl.auth.split(':', 2);
|
|
auth = {user: parts[0] && decodeURIComponent(parts[0]),
|
|
pass: parts[1] && decodeURIComponent(parts[1])};
|
|
}
|
|
|
|
var simplesmtp = Npm.require('simplesmtp');
|
|
var pool = simplesmtp.createClientPool(
|
|
port, // Defaults to 25
|
|
mailUrl.hostname, // Defaults to "localhost"
|
|
{ secureConnection: (port === 465),
|
|
// XXX allow maxConnections to be configured?
|
|
auth: auth });
|
|
|
|
pool._future_wrapped_sendMail = _.bind(Future.wrap(pool.sendMail), pool);
|
|
return pool;
|
|
};
|
|
|
|
// We construct smtpPool at the first call to Email.send, so that
|
|
// Meteor.startup code can set $MAIL_URL.
|
|
var smtpPoolFuture = new Future;;
|
|
var configured = false;
|
|
|
|
var getPool = function () {
|
|
// We check MAIL_URL in case someone else set it in Meteor.startup code.
|
|
if (!configured) {
|
|
configured = true;
|
|
AppConfig.configurePackage('email', function (config) {
|
|
// XXX allow reconfiguration when the app config changes
|
|
if (smtpPoolFuture.isResolved())
|
|
return;
|
|
var url = config.url || process.env.MAIL_URL;
|
|
var pool = null;
|
|
if (url)
|
|
pool = makePool(url);
|
|
smtpPoolFuture.return(pool);
|
|
});
|
|
}
|
|
|
|
return smtpPoolFuture.wait();
|
|
};
|
|
|
|
var next_devmode_mail_id = 0;
|
|
var output_stream = process.stdout;
|
|
|
|
// Testing hooks
|
|
EmailTest.overrideOutputStream = function (stream) {
|
|
next_devmode_mail_id = 0;
|
|
output_stream = stream;
|
|
};
|
|
|
|
EmailTest.restoreOutputStream = function () {
|
|
output_stream = process.stdout;
|
|
};
|
|
|
|
var devModeSend = function (mc) {
|
|
var devmode_mail_id = next_devmode_mail_id++;
|
|
|
|
var stream = output_stream;
|
|
|
|
// This approach does not prevent other writers to stdout from interleaving.
|
|
stream.write("====== BEGIN MAIL #" + devmode_mail_id + " ======\n");
|
|
stream.write("(Mail not sent; to enable sending, set the MAIL_URL " +
|
|
"environment variable.)\n");
|
|
mc.streamMessage();
|
|
mc.pipe(stream, {end: false});
|
|
var future = new Future;
|
|
mc.on('end', function () {
|
|
stream.write("====== END MAIL #" + devmode_mail_id + " ======\n");
|
|
future['return']();
|
|
});
|
|
future.wait();
|
|
};
|
|
|
|
var smtpSend = function (pool, mc) {
|
|
pool._future_wrapped_sendMail(mc).wait();
|
|
};
|
|
|
|
/**
|
|
* Mock out email sending (eg, during a test.) This is private for now.
|
|
*
|
|
* f receives the arguments to Email.send and should return true to go
|
|
* ahead and send the email (or at least, try subsequent hooks), or
|
|
* false to skip sending.
|
|
*/
|
|
var sendHooks = [];
|
|
EmailTest.hookSend = function (f) {
|
|
sendHooks.push(f);
|
|
};
|
|
|
|
// Old comment below
|
|
/**
|
|
* Send an email.
|
|
*
|
|
* Connects to the mail server configured via the MAIL_URL environment
|
|
* variable. If unset, prints formatted message to stdout. The "from" option
|
|
* is required, and at least one of "to", "cc", and "bcc" must be provided;
|
|
* all other options are optional.
|
|
*
|
|
* @param options
|
|
* @param options.from {String} RFC5322 "From:" address
|
|
* @param options.to {String|String[]} RFC5322 "To:" address[es]
|
|
* @param options.cc {String|String[]} RFC5322 "Cc:" address[es]
|
|
* @param options.bcc {String|String[]} RFC5322 "Bcc:" address[es]
|
|
* @param options.replyTo {String|String[]} RFC5322 "Reply-To:" address[es]
|
|
* @param options.subject {String} RFC5322 "Subject:" line
|
|
* @param options.text {String} RFC5322 mail body (plain text)
|
|
* @param options.html {String} RFC5322 mail body (HTML)
|
|
* @param options.headers {Object} custom RFC5322 headers (dictionary)
|
|
*/
|
|
|
|
// New API doc comment below
|
|
/**
|
|
* @summary Send an email. Throws an `Error` on failure to contact mail server
|
|
* or if mail server returns an error. All fields should match
|
|
* [RFC5322](http://tools.ietf.org/html/rfc5322) specification.
|
|
* @locus Server
|
|
* @param {Object} options
|
|
* @param {String} options.from "From:" address (required)
|
|
* @param {String|String[]} options.to,cc,bcc,replyTo
|
|
* "To:", "Cc:", "Bcc:", and "Reply-To:" addresses
|
|
* @param {String} [options.subject] "Subject:" line
|
|
* @param {String} [options.text|html] Mail body (in plain text or HTML)
|
|
* @param {Object} [options.headers] Dictionary of custom headers
|
|
*/
|
|
Email.send = function (options) {
|
|
for (var i = 0; i < sendHooks.length; i++)
|
|
if (! sendHooks[i](options))
|
|
return;
|
|
|
|
var mc = new MailComposer();
|
|
|
|
// setup message data
|
|
// XXX support attachments (once we have a client/server-compatible binary
|
|
// Buffer class)
|
|
mc.setMessageOption({
|
|
from: options.from,
|
|
to: options.to,
|
|
cc: options.cc,
|
|
bcc: options.bcc,
|
|
replyTo: options.replyTo,
|
|
subject: options.subject,
|
|
text: options.text,
|
|
html: options.html
|
|
});
|
|
|
|
_.each(options.headers, function (value, name) {
|
|
mc.addHeader(name, value);
|
|
});
|
|
|
|
var pool = getPool();
|
|
if (pool) {
|
|
smtpSend(pool, mc);
|
|
} else {
|
|
devModeSend(mc);
|
|
}
|
|
};
|