2011-02-23 21:23:37 +00:00
|
|
|
/*
|
|
|
|
* SMTP class written using python's (2.7) smtplib.py as a base
|
|
|
|
*/
|
2014-04-07 06:22:08 +00:00
|
|
|
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 SMTPResponse = require('./response');
|
|
|
|
var SMTPError = require('./error');
|
|
|
|
|
|
|
|
var SMTP_PORT = 25;
|
2011-02-23 21:23:37 +00:00
|
|
|
var SMTP_SSL_PORT = 465;
|
2011-02-24 23:02:24 +00:00
|
|
|
var SMTP_TLS_PORT = 587;
|
2014-04-07 06:22:08 +00:00
|
|
|
var CRLF = "\r\n";
|
|
|
|
var AUTH_METHODS = {
|
|
|
|
PLAIN: 'PLAIN',
|
|
|
|
CRAM_MD5: 'CRAM-MD5',
|
|
|
|
LOGIN: 'LOGIN',
|
|
|
|
XOAUTH2: 'XOAUTH2'
|
2011-02-23 21:23:37 +00:00
|
|
|
};
|
2014-04-07 06:22:08 +00:00
|
|
|
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.
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
return data.replace(/(?:\r\n|\n|\r(?!\n))/g, CRLF).replace(/^\./gm, '..');
|
2011-02-23 21:23:37 +00:00
|
|
|
};
|
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
var caller = function(callback) {
|
|
|
|
if (typeof(callback) == 'function') {
|
|
|
|
var args = Array.prototype.slice.call(arguments);
|
|
|
|
args.shift();
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
callback.apply(null, args);
|
|
|
|
}
|
2011-02-23 21:23:37 +00:00
|
|
|
};
|
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
var SMTPState = {
|
|
|
|
NOTCONNECTED: 0,
|
|
|
|
CONNECTING: 1,
|
|
|
|
CONNECTED: 2
|
2011-02-23 21:23:37 +00:00
|
|
|
};
|
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
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;
|
2015-04-26 00:27:45 +00:00
|
|
|
this.authentication = options.authentication || [AUTH_METHODS.CRAM_MD5, AUTH_METHODS.LOGIN, AUTH_METHODS.PLAIN, AUTH_METHODS.XOAUTH2];
|
2014-04-07 06:22:08 +00:00
|
|
|
|
|
|
|
// keep these strings hidden when quicky debugging/logging
|
|
|
|
this.user = function() {
|
|
|
|
return options.user;
|
|
|
|
};
|
|
|
|
this.password = function() {
|
|
|
|
return options.password;
|
|
|
|
};
|
2011-02-23 21:23:37 +00:00
|
|
|
};
|
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
SMTP.prototype = {
|
|
|
|
debug: function(level) {
|
|
|
|
DEBUG = level;
|
|
|
|
},
|
|
|
|
|
|
|
|
state: function() {
|
|
|
|
return this._state;
|
|
|
|
},
|
2011-09-25 21:51:29 +00:00
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
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;
|
|
|
|
|
2014-07-06 06:18:48 +00:00
|
|
|
if (self._state != SMTPState.NOTCONNECTED) {
|
|
|
|
self.quit(function() {
|
|
|
|
self.connect(callback, port, host, options);
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
|
|
|
|
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));
|
2011-09-25 21:51:29 +00:00
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
var response = function(err, msg) {
|
|
|
|
if (err) {
|
2014-12-02 09:05:08 +00:00
|
|
|
if (self._state === SMTPState.NOTCONNECTED && !self.sock) {
|
|
|
|
return;
|
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
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);
|
2014-07-06 06:18:48 +00:00
|
|
|
self.quit(function() {
|
|
|
|
caller(callback, SMTPError("bad response on connection", SMTPError.BADRESPONSE, err, msg.data));
|
|
|
|
});
|
2011-09-25 21:51:29 +00:00
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
2014-07-06 01:33:03 +00:00
|
|
|
self.sock.once('error', response); // the socket could reset or throw, so let's handle it and let the user know
|
2014-04-07 06:22:08 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
2011-09-25 21:51:29 +00:00
|
|
|
|
|
|
|
self.sock.once('response', response);
|
2014-04-07 06:22:08 +00:00
|
|
|
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 {
|
2016-08-03 16:01:36 +00:00
|
|
|
if (codes.indexOf(Number(msg.code)) != -1) {
|
|
|
|
caller(callback, err, msg.data, msg.message);
|
|
|
|
} else {
|
|
|
|
var errorMessage = "bad response on command '" + cmd.split(' ')[0] + "'";
|
|
|
|
if (msg.message) {
|
|
|
|
errorMessage += ': ' + msg.message;
|
|
|
|
}
|
|
|
|
caller(callback, SMTPError(errorMessage, SMTPError.BADRESPONSE, null, msg.data));
|
|
|
|
}
|
2011-09-25 21:51:29 +00:00
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2011-09-25 21:51:29 +00:00
|
|
|
};
|
2014-04-07 06:22:08 +00:00
|
|
|
|
|
|
|
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 {
|
2016-09-05 05:55:38 +00:00
|
|
|
var secured_socket = new tls.TLSSocket(self.sock, {
|
|
|
|
secureContext: tls.createSecureContext ? tls.createSecureContext(self.tls) : crypto.createCredentials(self.tls)
|
|
|
|
});
|
2014-10-05 01:02:29 +00:00
|
|
|
|
|
|
|
secured_socket.on('error', function(err) {
|
|
|
|
self.close(true);
|
|
|
|
caller(callback, err);
|
|
|
|
});
|
2016-09-05 05:55:38 +00:00
|
|
|
|
|
|
|
self._secure = true;
|
|
|
|
self.sock = secured_socket;
|
|
|
|
|
|
|
|
SMTPResponse.monitor(self.sock, self.timeout, function() {
|
|
|
|
self.close(true);
|
|
|
|
});
|
|
|
|
caller(callback, msg.data);
|
2014-04-07 06:22:08 +00:00
|
|
|
}
|
2011-09-25 21:51:29 +00:00
|
|
|
};
|
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
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;
|
|
|
|
},
|
2011-09-25 21:51:29 +00:00
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
ehlo: function(callback, domain) {
|
|
|
|
var self = this,
|
2011-09-25 21:51:29 +00:00
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
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);
|
2011-09-25 21:51:29 +00:00
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
}
|
2011-09-25 21:51:29 +00:00
|
|
|
};
|
2014-04-07 06:22:08 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
2014-07-30 15:54:34 +00:00
|
|
|
var attempt = function(err, data) {
|
2014-04-07 06:22:08 +00:00
|
|
|
if (err) self.helo(response, domain);
|
2014-07-30 15:54:34 +00:00
|
|
|
else caller(callback, err, data);
|
2014-04-07 06:22:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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() : ''
|
|
|
|
},
|
|
|
|
|
2011-09-25 21:51:29 +00:00
|
|
|
domain = options && options.domain ? options.domain : this.domain,
|
2014-04-07 06:22:08 +00:00
|
|
|
|
|
|
|
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) {
|
2011-09-25 21:51:29 +00:00
|
|
|
challenge = (new Buffer(challenge, "base64")).toString("ascii");
|
|
|
|
|
2012-01-17 18:57:46 +00:00
|
|
|
var hmac = crypto.createHmac('md5', login.password());
|
2011-09-25 21:51:29 +00:00
|
|
|
hmac.update(challenge);
|
|
|
|
|
2012-01-17 18:57:46 +00:00
|
|
|
return (new Buffer(login.user() + " " + hmac.digest('hex')).toString("base64"));
|
2014-04-07 06:22:08 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
encode_plain = function() {
|
2014-10-04 21:00:55 +00:00
|
|
|
return (new Buffer("\u0000" + login.user() + "\u0000" + login.password())).toString("base64");
|
2014-04-07 06:22:08 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
encode_xoauth2 = function() {
|
2013-07-23 08:20:42 +00:00
|
|
|
// console.log("user=" + login.user() + "\1auth=Bearer " + login.password()+"\1\1");
|
|
|
|
// see: https://developers.google.com/gmail/xoauth2_protocol
|
2014-10-04 21:00:55 +00:00
|
|
|
return (new Buffer("user=" + login.user() + "\u0001auth=Bearer " + login.password() + "\u0001\u0001")).toString("base64");
|
2014-04-07 06:22:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// List of authentication methods we support: from preferred to
|
|
|
|
// less preferred methods.
|
|
|
|
if (!method) {
|
2015-04-26 00:27:45 +00:00
|
|
|
var preferred = self.authentication;
|
2014-04-07 06:22:08 +00:00
|
|
|
|
|
|
|
for (var i = 0; i < preferred.length; i++) {
|
|
|
|
if ((self.features.auth || "").indexOf(preferred[i]) != -1) {
|
|
|
|
method = preferred[i];
|
|
|
|
break;
|
2011-09-25 21:51:29 +00:00
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handle bad responses from command differently
|
|
|
|
var failed = function(err, data) {
|
|
|
|
self.loggedin = false;
|
2014-07-06 00:01:42 +00:00
|
|
|
self.close(); // if auth is bad, close the connection, it won't get better by itself
|
2014-04-07 06:22:08 +00:00
|
|
|
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]);
|
2011-09-25 21:51:29 +00:00
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
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]);
|
2011-09-25 21:51:29 +00:00
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
}
|
|
|
|
};
|
2012-05-28 09:59:31 +00:00
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
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));
|
2011-09-25 21:51:29 +00:00
|
|
|
};
|
2011-12-09 10:25:22 +00:00
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
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();
|
2011-12-09 10:25:22 +00:00
|
|
|
}
|
2014-04-07 06:22:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2011-09-25 21:51:29 +00:00
|
|
|
};
|
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
this.command("quit", response, [221, 250]);
|
|
|
|
}
|
2011-02-23 21:23:37 +00:00
|
|
|
};
|
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
for (var each in events.EventEmitter.prototype) {
|
|
|
|
SMTP.prototype[each] = events.EventEmitter.prototype[each];
|
2011-02-23 21:23:37 +00:00
|
|
|
}
|
|
|
|
|
2014-04-07 06:22:08 +00:00
|
|
|
exports.SMTP = SMTP;
|
|
|
|
exports.state = SMTPState;
|
2015-04-26 00:27:45 +00:00
|
|
|
exports.authentication = AUTH_METHODS;
|