diff --git a/README.md b/README.md index 21df832..f82d920 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ If you are in need of this kind of extra functionality, check out andris9's [mim Requirements ============ -* [node.js](http://nodejs.org/) -- tested with v0.2.4 +* [node.js](http://nodejs.org/) -- v0.4.0 or newer * An IMAP server -- tested with gmail @@ -19,7 +19,7 @@ Example This example fetches the 'date', 'from', 'to', 'subject' message headers and the message structure of all unread messages in the Inbox since May 20, 2010: - var ImapConnection = require('./imap').ImapConnection, sys = require('sys'), + var ImapConnection = require('./imap').ImapConnection, util = require('util'), imap = new ImapConnection({ username: 'mygmailname@gmail.com', password: 'mygmailpassword', @@ -46,12 +46,12 @@ This example fetches the 'date', 'from', 'to', 'subject' message headers and the function(results) { var fetch = imap.fetch(results, { request: { headers: ['from', 'to', 'subject', 'date'] } }); fetch.on('message', function(msg) { - console.log('Got message: ' + sys.inspect(msg, false, 5)); + console.log('Got message: ' + util.inspect(msg, false, 5)); msg.on('data', function(chunk) { console.log('Got message chunk of size ' + chunk.length); }); msg.on('end', function() { - console.log('Finished message: ' + sys.inspect(msg, false, 5)); + console.log('Finished message: ' + util.inspect(msg, false, 5)); }); }); fetch.on('end', function() { diff --git a/imap.js b/imap.js index 6b4fa5c..d410bba 100644 --- a/imap.js +++ b/imap.js @@ -1,5 +1,13 @@ -var sys = require('sys'), net = require('net'), EventEmitter = require('events').EventEmitter; -var emptyFn = function() {}, CRLF = "\r\n", debug=emptyFn, STATES = { NOCONNECT: 0, NOAUTH: 1, AUTH: 2, BOXSELECTING: 3, BOXSELECTED: 4 }, BOX_ATTRIBS = ['NOINFERIORS', 'NOSELECT', 'MARKED', 'UNMARKED']; +var util = require('util'), net = require('net'), + tls = require('tls'), EventEmitter = require('events').EventEmitter; +var emptyFn = function() {}, CRLF = "\r\n", debug=emptyFn, + STATES = { + NOCONNECT: 0, + NOAUTH: 1, + AUTH: 2, + BOXSELECTING: 3, + BOXSELECTED: 4 + }, BOX_ATTRIBS = ['NOINFERIORS', 'NOSELECT', 'MARKED', 'UNMARKED']; function ImapConnection (options) { if (!(this instanceof ImapConnection)) @@ -30,7 +38,16 @@ function ImapConnection (options) { curExpected: 0, curXferred: 0, capabilities: [], - box: { _uidnext: 0, _flags: [], _newKeywords: false, validity: 0, keywords: [], permFlags: [], name: null, messages: { total: 0, new: 0 }} + box: { + _uidnext: 0, + _flags: [], + _newKeywords: false, + validity: 0, + keywords: [], + permFlags: [], + name: null, + messages: { total: 0, new: 0 } + } }; this._options = extend(true, this._options, options); @@ -39,7 +56,7 @@ function ImapConnection (options) { this.delim = null; this.namespaces = { personal: [], other: [], shared: [] }; }; -sys.inherits(ImapConnection, EventEmitter); +util.inherits(ImapConnection, EventEmitter); exports.ImapConnection = ImapConnection; ImapConnection.prototype.connect = function(loginCb) { @@ -70,30 +87,33 @@ ImapConnection.prototype.connect = function(loginCb) { this._state.conn = net.createConnection(this._options.port, this._options.host); + this._state.tmrConn = setTimeout(this._fnTmrConn, this._options.connTimeout, loginCb); + this._state.conn.setKeepAlive(true); + if (this._options.secure) { - this._state.conn.setSecure(); + // TODO: support STARTTLS + this._state.conn.cleartext = this._state.conn.setSecure(); this._state.conn.on('secure', function() { debug('Secure connection made.'); }); + this._state.conn.cleartext.setEncoding('utf8'); + } else { + this._state.conn.setEncoding('utf8'); + this._state.conn.cleartext = this._state.conn; } - this._state.tmrConn = setTimeout(this._fnTmrConn, this._options.connTimeout, loginCb); - - this._state.conn.setKeepAlive(true); - this._state.conn.setEncoding('utf8'); - this._state.conn.on('connect', function() { clearTimeout(self._state.tmrConn); debug('Connected to host.'); - self._state.conn.write(''); + self._state.conn.cleartext.write(''); self._state.status = STATES.NOAUTH; }); this._state.conn.on('ready', function() { fnInit(); }); - this._state.conn.on('data', function(data) { + this._state.conn.cleartext.on('data', function(data) { var trailingCRLF = false, literalInfo, bypass = false; - debug('<>: ' + sys.inspect(data)); + debug('<>: ' + util.inspect(data)); if (self._state.curExpected === 0) { if (data.indexOf(CRLF) === -1) { @@ -152,7 +172,7 @@ ImapConnection.prototype.connect = function(loginCb) { if (data[0] === '*') { // found additional responses, so don't try splitting the proceeding // response(s) for better performance in case they have literals too - process.nextTick(function() { self._state.conn.emit('data', data); }); + process.nextTick(function() { self._state.conn.cleartext.emit('data', data); }); return; } } else if (self._state.curExpected === 0 @@ -168,7 +188,7 @@ ImapConnection.prototype.connect = function(loginCb) { curReq._msg = msg; curReq._fetcher.emit('message', msg); curReq._msgtype = (type.indexOf('HEADER') === 0 ? 'headers' : 'body'); - self._state.conn.emit('data', data.substr(data.indexOf(CRLF)+2)); + self._state.conn.cleartext.emit('data', data.substr(data.indexOf(CRLF)+2)); //} return; } @@ -181,7 +201,7 @@ ImapConnection.prototype.connect = function(loginCb) { if (data.length > 1) { data.slice(1).forEach(function(line) { process.nextTick(function() { - self._state.conn.emit('data', line + CRLF); + self._state.conn.cleartext.emit('data', line + CRLF); }); }); } @@ -697,15 +717,15 @@ ImapConnection.prototype._send = function(cmdstr, cb, bypass) { clearTimeout(this._state.tmrKeepalive); this._state.isIdle = false; var cmd = (bypass ? cmdstr : this._state.requests[0].command); - this._state.conn.write('A' + ++this._state.curId + ' ' + cmd + CRLF); + this._state.conn.cleartext.write('A' + ++this._state.curId + ' ' + cmd + CRLF); debug('<>: A' + this._state.curId + ' ' + cmd); } }; function ImapMessage() {} -sys.inherits(ImapMessage, EventEmitter); +util.inherits(ImapMessage, EventEmitter); function ImapFetch() {} -sys.inherits(ImapFetch, EventEmitter); +util.inherits(ImapFetch, EventEmitter); /****** Utility Functions ******/ @@ -1354,4 +1374,41 @@ function extend() { // Return the modified object return target; -}; \ No newline at end of file +}; + +net.Stream.prototype.setSecure = function() { + var pair = tls.createSecurePair(); + var cleartext = pipe(pair, this); + + pair.on('secure', function() { + process.nextTick(function() { cleartext.socket.emit('secure'); }); + }); + + cleartext._controlReleased = true; + return cleartext; +}; + +function pipe(pair, socket) { + pair.encrypted.pipe(socket); + socket.pipe(pair.encrypted); + + pair.fd = socket.fd; + var cleartext = pair.cleartext; + cleartext.socket = socket; + cleartext.encrypted = pair.encrypted; + + function onerror(e) { + if (cleartext._controlReleased) + cleartext.socket.emit('error', e); + } + + function onclose() { + socket.removeListener('error', onerror); + socket.removeListener('close', onclose); + } + + socket.on('error', onerror); + socket.on('close', onclose); + + return cleartext; +} \ No newline at end of file