|
|
|
@ -5,8 +5,8 @@ var util = require('util'),
|
|
|
|
|
var parsers = require('./imap.parsers'),
|
|
|
|
|
utils = require('./imap.utilities');
|
|
|
|
|
|
|
|
|
|
var emptyFn = function() {},
|
|
|
|
|
CRLF = '\r\n',
|
|
|
|
|
// main constants
|
|
|
|
|
var CRLF = '\r\n',
|
|
|
|
|
STATES = {
|
|
|
|
|
NOCONNECT: 0,
|
|
|
|
|
NOAUTH: 1,
|
|
|
|
@ -16,6 +16,7 @@ var emptyFn = function() {},
|
|
|
|
|
}, BOX_ATTRIBS = ['NOINFERIORS', 'NOSELECT', 'MARKED', 'UNMARKED'],
|
|
|
|
|
reFetch = /^\* (\d+) FETCH .+? \{(\d+)\}\r\n/;
|
|
|
|
|
|
|
|
|
|
// extension constants
|
|
|
|
|
var IDLE_NONE = 1,
|
|
|
|
|
IDLE_WAIT = 2,
|
|
|
|
|
IDLE_READY = 3;
|
|
|
|
@ -72,7 +73,7 @@ function ImapConnection (options) {
|
|
|
|
|
if (typeof this._options.debug === 'function')
|
|
|
|
|
this.debug = this._options.debug;
|
|
|
|
|
else
|
|
|
|
|
this.debug = emptyFn;
|
|
|
|
|
this.debug = false;
|
|
|
|
|
this.delim = null;
|
|
|
|
|
this.namespaces = { personal: [], other: [], shared: [] };
|
|
|
|
|
this.capabilities = [];
|
|
|
|
@ -108,8 +109,6 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
loginCb = loginCb || emptyFn;
|
|
|
|
|
|
|
|
|
|
this._reset();
|
|
|
|
|
|
|
|
|
|
this._state.conn = new Socket();
|
|
|
|
@ -119,21 +118,21 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
// TODO: support STARTTLS
|
|
|
|
|
this._state.conn.cleartext = utils.setSecure(this._state.conn);
|
|
|
|
|
this._state.conn.on('secure', function() {
|
|
|
|
|
self.debug('Secure connection made.');
|
|
|
|
|
self.debug&&self.debug('Secure connection made.');
|
|
|
|
|
});
|
|
|
|
|
} else
|
|
|
|
|
this._state.conn.cleartext = this._state.conn;
|
|
|
|
|
|
|
|
|
|
this._state.conn.on('connect', function() {
|
|
|
|
|
clearTimeout(self._state.tmrConn);
|
|
|
|
|
self.debug('Connected to host.');
|
|
|
|
|
self.debug&&self.debug('Connected to host.');
|
|
|
|
|
self._state.conn.cleartext.write('');
|
|
|
|
|
self._state.status = STATES.NOAUTH;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this._state.conn.on('end', function() {
|
|
|
|
|
self._reset();
|
|
|
|
|
self.debug('FIN packet received. Disconnecting...');
|
|
|
|
|
self.debug&&self.debug('FIN packet received. Disconnecting...');
|
|
|
|
|
self.emit('end');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
@ -142,16 +141,14 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
if (self._state.status === STATES.NOCONNECT)
|
|
|
|
|
loginCb(new Error('Unable to connect. Reason: ' + err));
|
|
|
|
|
self.emit('error', err);
|
|
|
|
|
self.debug('Error occurred: ' + err);
|
|
|
|
|
self.debug&&self.debug('Error occurred: ' + err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//this._state.conn.on('error', errorHandler);
|
|
|
|
|
|
|
|
|
|
this._state.conn.cleartext.on('error', errorHandler);
|
|
|
|
|
|
|
|
|
|
this._state.conn.on('close', function(had_error) {
|
|
|
|
|
self._reset();
|
|
|
|
|
self.debug('Connection forcefully closed.');
|
|
|
|
|
self.debug&&self.debug('Connection forcefully closed.');
|
|
|
|
|
self.emit('close', had_error);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
@ -160,7 +157,7 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
this._state.conn.cleartext.on('data', function(data) {
|
|
|
|
|
if (data.length === 0) return;
|
|
|
|
|
var trailingCRLF = false, literalInfo;
|
|
|
|
|
self.debug('\n<<RECEIVED>>: ' + util.inspect(data.toString()) + '\n');
|
|
|
|
|
self.debug&&self.debug('\nSERVER: ' + util.inspect(data.toString()) + '\n');
|
|
|
|
|
|
|
|
|
|
if (self._state.curExpected === 0) {
|
|
|
|
|
if (utils.bufferIndexOf(data, CRLF) === -1) {
|
|
|
|
@ -569,10 +566,7 @@ ImapConnection.prototype.openBox = function(name, readOnly, cb) {
|
|
|
|
|
if (this._state.status === STATES.BOXSELECTED)
|
|
|
|
|
this._resetBox();
|
|
|
|
|
if (cb === undefined) {
|
|
|
|
|
if (readOnly === undefined)
|
|
|
|
|
cb = emptyFn;
|
|
|
|
|
else
|
|
|
|
|
cb = readOnly;
|
|
|
|
|
cb = readOnly;
|
|
|
|
|
readOnly = false;
|
|
|
|
|
}
|
|
|
|
|
this._state.status = STATES.BOXSELECTING;
|
|
|
|
@ -599,13 +593,13 @@ ImapConnection.prototype.closeBox = function(cb) {
|
|
|
|
|
ImapConnection.prototype.removeDeleted = function(cb) {
|
|
|
|
|
if (this._state.status !== STATES.BOXSELECTED)
|
|
|
|
|
throw new Error('No mailbox is currently selected');
|
|
|
|
|
cb = arguments[arguments.length-1];
|
|
|
|
|
cb = arguments[arguments.length - 1];
|
|
|
|
|
|
|
|
|
|
this._send('EXPUNGE', cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.getBoxes = function(namespace, cb) {
|
|
|
|
|
cb = arguments[arguments.length-1];
|
|
|
|
|
cb = arguments[arguments.length - 1];
|
|
|
|
|
if (arguments.length !== 2)
|
|
|
|
|
namespace = '';
|
|
|
|
|
this._send(((this.capabilities.indexOf('XLIST') == -1) ? 'LIST' : 'XLIST')
|
|
|
|
@ -613,7 +607,7 @@ ImapConnection.prototype.getBoxes = function(namespace, cb) {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.addBox = function(name, cb) {
|
|
|
|
|
cb = arguments[arguments.length-1];
|
|
|
|
|
cb = arguments[arguments.length - 1];
|
|
|
|
|
if (typeof name !== 'string' || name.length === 0)
|
|
|
|
|
throw new Error('Mailbox name must be a string describing the full path'
|
|
|
|
|
+ ' of a new mailbox to be created');
|
|
|
|
@ -621,7 +615,7 @@ ImapConnection.prototype.addBox = function(name, cb) {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.delBox = function(name, cb) {
|
|
|
|
|
cb = arguments[arguments.length-1];
|
|
|
|
|
cb = arguments[arguments.length - 1];
|
|
|
|
|
if (typeof name !== 'string' || name.length === 0)
|
|
|
|
|
throw new Error('Mailbox name must be a string describing the full path'
|
|
|
|
|
+ ' of an existing mailbox to be deleted');
|
|
|
|
@ -629,7 +623,7 @@ ImapConnection.prototype.delBox = function(name, cb) {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.renameBox = function(oldname, newname, cb) {
|
|
|
|
|
cb = arguments[arguments.length-1];
|
|
|
|
|
cb = arguments[arguments.length - 1];
|
|
|
|
|
if (typeof oldname !== 'string' || oldname.length === 0)
|
|
|
|
|
throw new Error('Old mailbox name must be a string describing the full path'
|
|
|
|
|
+ ' of an existing mailbox to be renamed');
|
|
|
|
@ -644,19 +638,6 @@ ImapConnection.prototype.renameBox = function(oldname, newname, cb) {
|
|
|
|
|
+ '"', cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.search = function(options, cb) {
|
|
|
|
|
this._search('UID ', options, cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype._search = function(which, options, cb) {
|
|
|
|
|
if (this._state.status !== STATES.BOXSELECTED)
|
|
|
|
|
throw new Error('No mailbox is currently selected');
|
|
|
|
|
if (!Array.isArray(options))
|
|
|
|
|
throw new Error('Expected array for search options');
|
|
|
|
|
this._send(which + 'SEARCH'
|
|
|
|
|
+ utils.buildSearchQuery(options, this.capabilities), cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.append = function(data, options, cb) {
|
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
|
cb = options;
|
|
|
|
@ -698,10 +679,23 @@ ImapConnection.prototype.append = function(data, options, cb) {
|
|
|
|
|
return cb(err);
|
|
|
|
|
self._state.conn.cleartext.write(data);
|
|
|
|
|
self._state.conn.cleartext.write(CRLF);
|
|
|
|
|
self.debug('\n<<SENT>>: ' + util.inspect(data.toString()) + '\n');
|
|
|
|
|
self.debug&&self.debug('\nCLIENT: ' + util.inspect(data.toString()) + '\n');
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.search = function(options, cb) {
|
|
|
|
|
this._search('UID ', options, cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype._search = function(which, options, cb) {
|
|
|
|
|
if (this._state.status !== STATES.BOXSELECTED)
|
|
|
|
|
throw new Error('No mailbox is currently selected');
|
|
|
|
|
if (!Array.isArray(options))
|
|
|
|
|
throw new Error('Expected array for search options');
|
|
|
|
|
this._send(which + 'SEARCH'
|
|
|
|
|
+ utils.buildSearchQuery(options, this.capabilities), cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.fetch = function(uids, options) {
|
|
|
|
|
return this._fetch('UID ', uids, options);
|
|
|
|
|
};
|
|
|
|
@ -770,6 +764,7 @@ ImapConnection.prototype._fetch = function(which, uids, options) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var extensions = '';
|
|
|
|
|
// always fetch GMail-specific bits of information when on GMail
|
|
|
|
|
if (this.capabilities.indexOf('X-GM-EXT-1') > -1)
|
|
|
|
|
extensions = 'X-GM-THRID X-GM-MSGID X-GM-LABELS ';
|
|
|
|
|
|
|
|
|
@ -1024,9 +1019,9 @@ ImapConnection.prototype._reset = function() {
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype._resetBox = function() {
|
|
|
|
|
this._state.box._uidnext = 0;
|
|
|
|
|
this._state.box.validity = 0;
|
|
|
|
|
this._state.box._flags = [];
|
|
|
|
|
this._state.box._newKeywords = false;
|
|
|
|
|
this._state.box.validity = 0;
|
|
|
|
|
this._state.box.permFlags = [];
|
|
|
|
|
this._state.box.keywords = [];
|
|
|
|
|
this._state.box.name = null;
|
|
|
|
@ -1061,7 +1056,7 @@ ImapConnection.prototype._send = function(cmdstr, cb, bypass) {
|
|
|
|
|
this._state.conn.cleartext.write(prefix);
|
|
|
|
|
this._state.conn.cleartext.write(cmd);
|
|
|
|
|
this._state.conn.cleartext.write(CRLF);
|
|
|
|
|
this.debug('\n<<SENT>>: ' + prefix + cmd + '\n');
|
|
|
|
|
this.debug&&this.debug('\nCLIENT: ' + prefix + cmd + '\n');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|