You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
730 lines
19 KiB
730 lines
19 KiB
/*
|
|
|
|
Node.js POP3 client library
|
|
|
|
Copyright (C) 2011-2013 by Ditesh Shashikant Gathani <ditesh@gathani.org>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
var net = require('net'),
|
|
tls = require('tls'),
|
|
util = require('util'),
|
|
crypto = require('crypto'),
|
|
events = require('events');
|
|
|
|
// Constructor
|
|
function POP3Client(port, host, options) {
|
|
if (options === undefined) options = {};
|
|
|
|
// Optional constructor arguments
|
|
var enabletls = options.enabletls !== undefined ? options.enabletls : false;
|
|
var ignoretlserrs = options.ignoretlserrs !== undefined ? options.ignoretlserrs : false;
|
|
var debug = options.debug || false;
|
|
|
|
var tlsDirectOpts = options.tlsopts !== undefined ? options.tlsopts : {};
|
|
|
|
// Private variables follow
|
|
var self = this;
|
|
var response = null;
|
|
var checkResp = true;
|
|
var bufferedData = '';
|
|
var state = 0;
|
|
var locked = false;
|
|
var multiline = false;
|
|
var socket = null;
|
|
var tlssock = null;
|
|
var callback = function (resp, data) {
|
|
if (resp === false) {
|
|
locked = false;
|
|
callback = function () {};
|
|
self.emit('connect', false, data);
|
|
} else {
|
|
// Checking for APOP support
|
|
var banner = data.trim();
|
|
var bannerComponents = banner.split(' ');
|
|
|
|
for (var i = 0; i < bannerComponents.length; i++) {
|
|
if (bannerComponents[i].indexOf('@') > 0) {
|
|
self.data['apop'] = true;
|
|
self.data['apop-timestamp'] = bannerComponents[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
state = 1;
|
|
self.data['banner'] = banner;
|
|
self.emit('connect', true, data);
|
|
}
|
|
};
|
|
|
|
// Public variables follow
|
|
this.data = {
|
|
host: host,
|
|
port: port,
|
|
banner: '',
|
|
stls: false,
|
|
apop: false,
|
|
username: '',
|
|
tls: enabletls,
|
|
ignoretlserrs: ignoretlserrs,
|
|
};
|
|
|
|
// Privileged methods follow
|
|
this.setCallback = function (cb) {
|
|
callback = cb;
|
|
};
|
|
this.getCallback = function () {
|
|
return callback;
|
|
};
|
|
this.setState = function (val) {
|
|
state = val;
|
|
};
|
|
this.getState = function () {
|
|
return state;
|
|
};
|
|
this.setLocked = function (val) {
|
|
locked = val;
|
|
};
|
|
this.getLocked = function () {
|
|
return locked;
|
|
};
|
|
this.setMultiline = function (val) {
|
|
multiline = val;
|
|
};
|
|
this.getMultiline = function () {
|
|
return multiline;
|
|
};
|
|
|
|
// Writes to remote server socket
|
|
this.write = function (command, argument) {
|
|
var text = command;
|
|
|
|
if (argument !== undefined) text = text + ' ' + argument + '\r\n';
|
|
else text = text + '\r\n';
|
|
|
|
if (debug) console.log('Client: ' + util.inspect(text));
|
|
|
|
socket.write(text);
|
|
};
|
|
|
|
// Kills the socket connection
|
|
this.end = function () {
|
|
socket.end();
|
|
};
|
|
|
|
// Upgrades a standard unencrypted TCP connection to use TLS
|
|
// Liberally copied and modified from https://gist.github.com/848444
|
|
// starttls() should be a private function, but I can't figure out
|
|
// how to get a public prototypal method (stls) to talk to private method (starttls)
|
|
// which references private variables without going through a privileged method
|
|
this.starttls = function (options) {
|
|
var s = socket;
|
|
s.removeAllListeners('end');
|
|
s.removeAllListeners('data');
|
|
s.removeAllListeners('error');
|
|
socket = null;
|
|
|
|
var sslcontext = require('crypto').createCredentials(options);
|
|
var pair = tls.createSecurePair(sslcontext, false);
|
|
var cleartext = pipe(pair);
|
|
|
|
pair.on('secure', function () {
|
|
var verifyError = pair.ssl.verifyError();
|
|
cleartext.authorized = true;
|
|
|
|
if (verifyError) {
|
|
cleartext.authorized = false;
|
|
cleartext.authorizationError = verifyError;
|
|
}
|
|
|
|
cleartext.on('data', onData);
|
|
cleartext.on('error', onError);
|
|
cleartext.on('end', onEnd);
|
|
socket = cleartext;
|
|
self.getCallback()(cleartext.authorized, cleartext.authorizationError);
|
|
});
|
|
|
|
cleartext._controlReleased = true;
|
|
|
|
function pipe(pair) {
|
|
pair.encrypted.pipe(s);
|
|
s.pipe(pair.encrypted);
|
|
|
|
pair.fd = s.fd;
|
|
var cleartext = pair.cleartext;
|
|
cleartext.socket = s;
|
|
cleartext.encrypted = pair.encrypted;
|
|
cleartext.authorized = false;
|
|
|
|
function onerror(e) {
|
|
if (cleartext._controlReleased) cleartext.emit('error', e);
|
|
}
|
|
|
|
function onclose() {
|
|
s.removeListener('error', onerror);
|
|
s.removeListener('close', onclose);
|
|
}
|
|
|
|
s.on('error', onerror);
|
|
s.on('close', onclose);
|
|
return cleartext;
|
|
}
|
|
};
|
|
|
|
// Private methods follow
|
|
// Event handlers follow
|
|
function onData(data) {
|
|
data = data.toString('ascii');
|
|
bufferedData += data;
|
|
|
|
if (debug) console.log('Server: ' + util.inspect(data));
|
|
|
|
if (checkResp === true) {
|
|
if (bufferedData.substr(0, 3) === '+OK') {
|
|
checkResp = false;
|
|
response = true;
|
|
} else if (bufferedData.substr(0, 4) === '-ERR') {
|
|
checkResp = false;
|
|
response = false;
|
|
|
|
// The following is only used for SASL
|
|
} else if (multiline === false) {
|
|
checkResp = false;
|
|
response = true;
|
|
}
|
|
}
|
|
|
|
if (checkResp === false) {
|
|
if (multiline === true && (response === false || bufferedData.substr(bufferedData.length - 5) === '\r\n.\r\n')) {
|
|
// Make a copy to avoid race conditions
|
|
var responseCopy = response;
|
|
var bufferedDataCopy = bufferedData;
|
|
|
|
response = null;
|
|
checkResp = true;
|
|
multiline = false;
|
|
bufferedData = '';
|
|
|
|
callback(responseCopy, bufferedDataCopy);
|
|
} else if (multiline === false) {
|
|
// Make a copy to avoid race conditions
|
|
var responseCopy = response;
|
|
var bufferedDataCopy = bufferedData;
|
|
|
|
response = null;
|
|
checkResp = true;
|
|
multiline = false;
|
|
bufferedData = '';
|
|
|
|
callback(responseCopy, bufferedDataCopy);
|
|
}
|
|
}
|
|
}
|
|
|
|
function onError(err) {
|
|
if (err.errno === 111) self.emit('connect', false, err);
|
|
else self.emit('error', err);
|
|
}
|
|
|
|
function onEnd(data) {
|
|
self.setState(0);
|
|
socket = null;
|
|
}
|
|
|
|
function onClose() {
|
|
self.emit('close');
|
|
}
|
|
|
|
// Constructor code follows
|
|
// Set up EventEmitter constructor function
|
|
events.EventEmitter.call(this);
|
|
|
|
// Remote end socket
|
|
if (enabletls === true) {
|
|
tlssock = tls.connect(
|
|
{
|
|
host: host,
|
|
port: port,
|
|
rejectUnauthorized: !self.data.ignoretlserrs,
|
|
},
|
|
function () {
|
|
if (tlssock.authorized === false && self.data['ignoretlserrs'] === false) self.emit('tls-error', tlssock.authorizationError);
|
|
},
|
|
);
|
|
|
|
socket = tlssock;
|
|
} else socket = new net.createConnection(port, host);
|
|
|
|
// Set up event handlers
|
|
socket.on('data', onData);
|
|
socket.on('error', onError);
|
|
socket.on('end', onEnd);
|
|
socket.on('close', onClose);
|
|
}
|
|
|
|
util.inherits(POP3Client, events.EventEmitter);
|
|
|
|
POP3Client.prototype.login = function (username, password) {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 1) self.emit('invalid-state', 'login');
|
|
else if (self.getLocked() === true) self.emit('locked', 'login');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
if (resp === false) {
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
self.emit('login', false, data);
|
|
} else {
|
|
self.setCallback(function (resp, data) {
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
if (resp !== false) self.setState(2);
|
|
self.emit('login', resp, data);
|
|
});
|
|
|
|
self.setMultiline(false);
|
|
self.write('PASS', password);
|
|
}
|
|
});
|
|
|
|
self.setMultiline(false);
|
|
self.write('USER', username);
|
|
}
|
|
};
|
|
|
|
// SASL AUTH implementation
|
|
// Currently supports SASL PLAIN and CRAM-MD5
|
|
POP3Client.prototype.auth = function (type, username, password) {
|
|
type = type.toUpperCase();
|
|
var self = this;
|
|
var types = { 'PLAIN': 1, 'CRAM-MD5': 1 };
|
|
var initialresp = '';
|
|
|
|
if (self.getState() !== 1) self.emit('invalid-state', 'auth');
|
|
else if (self.getLocked() === true) self.emit('locked', 'auth');
|
|
|
|
if (type in types === false) {
|
|
self.emit('auth', false, 'Invalid auth type', null);
|
|
return;
|
|
}
|
|
|
|
function tlsok() {
|
|
if (type === 'PLAIN') {
|
|
initialresp = ' ' + new Buffer(username + '\u0000' + username + '\u0000' + password).toString('base64') + '=';
|
|
self.setCallback(function (resp, data) {
|
|
if (resp !== false) self.setState(2);
|
|
self.emit('auth', resp, data, data);
|
|
});
|
|
} else if (type === 'CRAM-MD5') {
|
|
self.setCallback(function (resp, data) {
|
|
if (resp === false) self.emit('auth', resp, 'Server responded -ERR to AUTH CRAM-MD5', data);
|
|
else {
|
|
var challenge = new Buffer(data.trim().substr(2), 'base64').toString();
|
|
var hmac = crypto.createHmac('md5', password);
|
|
var response = new Buffer(username + ' ' + hmac.update(challenge).digest('hex')).toString('base64');
|
|
|
|
self.setCallback(function (resp, data) {
|
|
var errmsg = null;
|
|
|
|
if (resp !== false) self.setState(2);
|
|
else errmsg = 'Server responded -ERR to response';
|
|
|
|
self.emit('auth', resp, null, data);
|
|
});
|
|
|
|
self.write(response);
|
|
}
|
|
});
|
|
}
|
|
|
|
self.write('AUTH ' + type + initialresp);
|
|
}
|
|
|
|
if (self.data['tls'] === false && self.data['stls'] === false) {
|
|
// Remove all existing STLS listeners
|
|
self.removeAllListeners('stls');
|
|
|
|
self.on('stls', function (resp, rawdata) {
|
|
if (resp === false) {
|
|
// We (optionally) ignore self signed cert errors,
|
|
// in blatant violation of RFC 2595, Section 2.4
|
|
if (self.data['ignoretlserrs'] === true && rawdata === 'DEPTH_ZERO_SELF_SIGNED_CERT') tlsok();
|
|
else self.emit('auth', false, 'Unable to upgrade connection to STLS', rawdata);
|
|
} else tlsok();
|
|
});
|
|
|
|
self.stls();
|
|
} else tlsok();
|
|
};
|
|
|
|
POP3Client.prototype.apop = function (username, password) {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 1) self.emit('invalid-state', 'apop');
|
|
else if (self.getLocked() === true) self.emit('locked', 'apop');
|
|
else if (self.data['apop'] === false) self.emit('apop', false, 'APOP support not detected on remote server');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
if (resp === true) self.setState(2);
|
|
self.emit('apop', resp, data);
|
|
});
|
|
|
|
self.setMultiline(false);
|
|
self.write(
|
|
'APOP',
|
|
username +
|
|
' ' +
|
|
crypto
|
|
.createHash('md5')
|
|
.update(self.data['apop-timestamp'] + password)
|
|
.digest('hex'),
|
|
);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.stls = function () {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 1) self.emit('invalid-state', 'stls');
|
|
else if (self.getLocked() === true) self.emit('locked', 'stls');
|
|
else if (self.data['tls'] === true) self.emit('stls', false, 'Unable to execute STLS as TLS connection already established');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
if (resp === true) {
|
|
self.setCallback(function (resp, data) {
|
|
if (resp === false && self.data['ignoretlserrs'] === true && data === 'DEPTH_ZERO_SELF_SIGNED_CERT') resp = true;
|
|
|
|
self.data['stls'] = true;
|
|
self.emit('stls', resp, data);
|
|
});
|
|
|
|
self.starttls();
|
|
} else self.emit('stls', false, data);
|
|
});
|
|
|
|
self.setMultiline(false);
|
|
self.write('STLS');
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.top = function (msgnumber, lines) {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 2) self.emit('invalid-state', 'top');
|
|
else if (self.getLocked() === true) self.emit('locked', 'top');
|
|
else {
|
|
self.setCallback(function (resp, data) {
|
|
var returnValue = null;
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
if (resp !== false) {
|
|
returnValue = '';
|
|
var startOffset = data.indexOf('\r\n', 0) + 2;
|
|
var endOffset = data.indexOf('\r\n.\r\n', 0) + 2;
|
|
|
|
if (endOffset > startOffset) returnValue = data.substr(startOffset, endOffset - startOffset);
|
|
}
|
|
|
|
self.emit('top', resp, msgnumber, returnValue, data);
|
|
});
|
|
|
|
self.setMultiline(true);
|
|
self.write('TOP', msgnumber + ' ' + lines);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.list = function (msgnumber) {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 2) self.emit('invalid-state', 'list');
|
|
else if (self.getLocked() === true) self.emit('locked', 'list');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
var returnValue = null;
|
|
var msgcount = 0;
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
if (resp !== false) {
|
|
returnValue = [];
|
|
|
|
if (msgnumber !== undefined) {
|
|
msgcount = 1;
|
|
listitem = data.split(' ');
|
|
returnValue[listitem[1]] = listitem[2];
|
|
} else {
|
|
var offset = 0;
|
|
var listitem = '';
|
|
var newoffset = 0;
|
|
var returnValue = [];
|
|
var startOffset = data.indexOf('\r\n', 0) + 2;
|
|
var endOffset = data.indexOf('\r\n.\r\n', 0) + 2;
|
|
|
|
if (endOffset > startOffset) {
|
|
data = data.substr(startOffset, endOffset - startOffset);
|
|
|
|
while (true) {
|
|
if (offset > endOffset) break;
|
|
|
|
newoffset = data.indexOf('\r\n', offset);
|
|
|
|
if (newoffset < 0) break;
|
|
|
|
msgcount++;
|
|
listitem = data.substr(offset, newoffset - offset);
|
|
listitem = listitem.split(' ');
|
|
returnValue[listitem[0]] = listitem[1];
|
|
offset = newoffset + 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.emit('list', resp, msgcount, msgnumber, returnValue, data);
|
|
});
|
|
|
|
if (msgnumber !== undefined) self.setMultiline(false);
|
|
else self.setMultiline(true);
|
|
|
|
self.write('LIST', msgnumber);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.stat = function () {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 2) self.emit('invalid-state', 'stat');
|
|
else if (self.getLocked() === true) self.emit('locked', 'stat');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
var returnValue = null;
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
if (resp !== false) {
|
|
listitem = data.split(' ');
|
|
returnValue = {
|
|
count: listitem[1].trim(),
|
|
octets: listitem[2].trim(),
|
|
};
|
|
}
|
|
|
|
self.emit('stat', resp, returnValue, data);
|
|
});
|
|
|
|
self.setMultiline(false);
|
|
self.write('STAT', undefined);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.uidl = function (msgnumber) {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 2) self.emit('invalid-state', 'uidl');
|
|
else if (self.getLocked() === true) self.emit('locked', 'uidl');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
var returnValue = null;
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
if (resp !== false) {
|
|
returnValue = [];
|
|
|
|
if (msgnumber !== undefined) {
|
|
listitem = data.split(' ');
|
|
returnValue[listitem[1]] = listitem[2].trim();
|
|
} else {
|
|
var offset = 0;
|
|
var listitem = '';
|
|
var newoffset = 0;
|
|
var returnValue = [];
|
|
var startOffset = data.indexOf('\r\n', 0) + 2;
|
|
var endOffset = data.indexOf('\r\n.\r\n', 0) + 2;
|
|
|
|
if (endOffset > startOffset) {
|
|
data = data.substr(startOffset, endOffset - startOffset);
|
|
endOffset -= startOffset;
|
|
|
|
while (offset < endOffset) {
|
|
newoffset = data.indexOf('\r\n', offset);
|
|
listitem = data.substr(offset, newoffset - offset);
|
|
listitem = listitem.split(' ');
|
|
returnValue[listitem[0]] = listitem[1];
|
|
offset = newoffset + 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.emit('uidl', resp, msgnumber, returnValue, data);
|
|
});
|
|
|
|
if (msgnumber !== undefined) self.setMultiline(false);
|
|
else self.setMultiline(true);
|
|
|
|
self.write('UIDL', msgnumber);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.retr = function (msgnumber) {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 2) self.emit('invalid-state', 'retr');
|
|
else if (self.getLocked() === true) self.emit('locked', 'retr');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
var returnValue = null;
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
if (resp !== false) {
|
|
var startOffset = data.indexOf('\r\n', 0) + 2;
|
|
var endOffset = data.indexOf('\r\n.\r\n', 0);
|
|
returnValue = data.substr(startOffset, endOffset - startOffset);
|
|
}
|
|
|
|
self.emit('retr', resp, msgnumber, returnValue, data);
|
|
});
|
|
|
|
self.setMultiline(true);
|
|
self.write('RETR', msgnumber);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.dele = function (msgnumber) {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 2) self.emit('invalid-state', 'dele');
|
|
else if (self.getLocked() === true) self.emit('locked', 'dele');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
self.emit('dele', resp, msgnumber, data);
|
|
});
|
|
|
|
self.setMultiline(false);
|
|
self.write('DELE', msgnumber);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.noop = function () {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 2) self.emit('invalid-state', 'noop');
|
|
else if (self.getLocked() === true) self.emit('locked', 'noop');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
self.emit('noop', resp, data);
|
|
});
|
|
|
|
self.setMultiline(false);
|
|
self.write('NOOP', undefined);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.rset = function () {
|
|
var self = this;
|
|
|
|
if (self.getState() !== 2) self.emit('invalid-state', 'rset');
|
|
else if (self.getLocked() === true) self.emit('locked', 'rset');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
self.emit('rset', resp, data);
|
|
});
|
|
|
|
self.setMultiline(false);
|
|
self.write('RSET', undefined);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.capa = function () {
|
|
var self = this;
|
|
|
|
if (self.getState() === 0) self.emit('invalid-state', 'quit');
|
|
else if (self.getLocked() === true) self.emit('locked', 'capa');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
var returnValue = null;
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
if (resp === true) {
|
|
var startOffset = data.indexOf('\r\n', 0) + 2;
|
|
var endOffset = data.indexOf('\r\n.\r\n', 0);
|
|
returnValue = data.substr(startOffset, endOffset - startOffset);
|
|
returnValue = returnValue.split('\r\n');
|
|
}
|
|
|
|
self.emit('capa', resp, returnValue, data);
|
|
});
|
|
|
|
self.setMultiline(true);
|
|
self.write('CAPA', undefined);
|
|
}
|
|
};
|
|
|
|
POP3Client.prototype.quit = function () {
|
|
var self = this;
|
|
|
|
if (self.getState() === 0) self.emit('invalid-state', 'quit');
|
|
else if (self.getLocked() === true) self.emit('locked', 'quit');
|
|
else {
|
|
self.setLocked(true);
|
|
self.setCallback(function (resp, data) {
|
|
self.setLocked(false);
|
|
self.setCallback(function () {});
|
|
|
|
self.end();
|
|
self.emit('quit', resp, data);
|
|
});
|
|
|
|
self.setMultiline(false);
|
|
self.write('QUIT', undefined);
|
|
}
|
|
};
|
|
|
|
module.exports = POP3Client;
|
|
|