diff --git a/README.md b/README.md index 7beaf5e..3332c62 100644 --- a/README.md +++ b/README.md @@ -370,6 +370,12 @@ ImapConnection Functions * **move**(Integer/String/Array, String, Function) - _(void)_ - Moves the message(s) with the message ID(s) identified by the first parameter, in the currently open mailbox, to the mailbox specified by the second parameter. The first parameter can either be an Integer for a single message ID, a String for a message ID range (e.g. '2504:2507' or '*' or '2504:*'), or an Array containing any number of the aforementioned Integers and/or Strings. The Function parameter is the callback with one parameter: the error (null if none). **Note:** The message in the destination mailbox will have a new message ID. +* **append**(Buffer/String, Object, Function) - _(void)_ - Appends a message to selected mailbox. The first parameter is either a string or Buffer containing a RFC-822 compatible MIME message. The second parameter is a configuration object. Valid options are: + * **mailbox** - (optional) The name of the mailbox to append the message to. If not specified, the currently connected mailbox is assumed. + * **flags** - (optional) Either a string or an Array of flags to append to the message, eg. `['Seen', 'Flagged']` + * **date** - (optional) A Date object that denotes when the message was received. +The Function parameter is the callback with one parameter: the error (null if none). + * **addFlags**(Integer/String/Array, String/Array, Function) - _(void)_ - Adds the specified flag(s) to the message(s) identified by the first parameter. The first parameter can either be an Integer for a single message ID, a String for a message ID range (e.g. '2504:2507' or '*' or '2504:*'), or an Array containing any number of the aforementioned Integers and/or Strings. The second parameter can either be a String containing a single flag or can be an Array of flags. The Function parameter is the callback with one parameter: the error (null if none). * **delFlags**(Integer/String/Array, String/Array, Function) - _(void)_ - Removes the specified flag(s) from the message(s) identified by the first parameter. The first parameter can either be an Integer for a single message ID, a String for a message ID range (e.g. '2504:2507' or '*' or '2504:*'), or an Array containing any number of the aforementioned Integers and/or Strings. The second parameter can either be a String containing a single flag or can be an Array of flags. The Function parameter is the callback with one parameter: the error (null if none). diff --git a/imap.js b/imap.js index 8afbd77..65dbb2a 100644 --- a/imap.js +++ b/imap.js @@ -9,6 +9,8 @@ var emptyFn = function() {}, CRLF = '\r\n', debug=emptyFn, BOXSELECTING: 3, BOXSELECTED: 4 }, BOX_ATTRIBS = ['NOINFERIORS', 'NOSELECT', 'MARKED', 'UNMARKED'], + MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec'], reFetch = /^\* (\d+) FETCH .+? \{(\d+)\}\r\n/; function ImapConnection (options) { @@ -412,7 +414,8 @@ ImapConnection.prototype.connect = function(loginCb) { } } } - } else if (data[0][0] === 'A') { // Tagged server response + } else if (data[0][0] === 'A' || + (data[0] === '+' && self._state.requests.length && !self._state.isIdle)) { // Tagged server response or continutation response var sendBox = false; clearTimeout(self._state.tmrKeepalive); @@ -425,7 +428,6 @@ ImapConnection.prototype.connect = function(loginCb) { self._resetBox(); } } - if (self._state.requests[0].command.indexOf('RENAME') > -1) { self._state.box.name = self._state.box._newName; delete self._state.box._newName; @@ -436,7 +438,14 @@ ImapConnection.prototype.connect = function(loginCb) { var err = null; var args = self._state.requests[0].args, cmd = self._state.requests[0].command; - if (data[1] !== 'OK') { + if (data[0] === '+') { + if (cmd.indexOf('APPEND') !== 0) { + err = new Error('Unexpected continuation'); + err.type = 'continuation'; + err.request = cmd; + } else + return self._state.requests[0].callback(); + } else if (data[1] !== 'OK') { err = new Error('Error while executing request: ' + data[2]); err.type = data[1]; err.request = cmd; @@ -613,6 +622,43 @@ ImapConnection.prototype._search = function(which, options, cb) { + buildSearchQuery(options, this.capabilities), cb); }; +ImapConnection.prototype.append = function(data, options, cb) { + options = options || {}; + if (!('mailbox' in options)) { + if (this._state.status !== STATES.BOXSELECTED) + throw new Error('No mailbox specified or currently selected'); + else + options.mailbox = this._state.box.name + } + cmd = 'APPEND "'+escape(options.mailbox)+'"'; + if ('flags' in options) { + if (!Array.isArray(options.flags)) + options.flags = Array(options.flags); + cmd += " (\\"+options.flags.join(' \\')+")"; + } + if ('date' in options) { + if (!(options.date instanceof Date)) + throw new Error('Expected null or Date object for date'); + cmd += ' "'+options.date.getDate()+'-'+MONTHS[options.date.getMonth()]+'-'+options.date.getFullYear(); + cmd += ' '+('0'+options.date.getHours()).slice(-2)+':'+('0'+options.date.getMinutes()).slice(-2)+':'+('0'+options.date.getSeconds()).slice(-2); + cmd += ((options.date.getTimezoneOffset() > 0) ? ' -' : ' +' ); + cmd += ('0'+(-options.date.getTimezoneOffset() / 60)).slice(-2); + cmd += ('0'+(-options.date.getTimezoneOffset() % 60)).slice(-2); + cmd += '"'; + } + cmd += ' {'; + cmd += (Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data)); + cmd += '}'; + var self = this, step = 1; + this._send(cmd, function(err) { + if (err || step++ === 2) + return cb(err); + self._state.conn.cleartext.write(data); + self._state.conn.cleartext.write(CRLF); + debug('\n<>: ' + util.inspect(data.toString()) + '\n'); + }); +} + ImapConnection.prototype.fetch = function(uids, options) { return this._fetch('UID ', uids, options); }; @@ -957,7 +1003,9 @@ ImapConnection.prototype._send = function(cmdstr, cb, bypass) { } if (cmd !== 'IDLE' && cmd !== 'DONE') prefix = 'A' + ++this._state.curId + ' '; - this._state.conn.cleartext.write(prefix + cmd + CRLF); + this._state.conn.cleartext.write(prefix); + this._state.conn.cleartext.write(cmd); + this._state.conn.cleartext.write(CRLF); debug('\n<>: ' + prefix + cmd + '\n'); } }; @@ -970,9 +1018,7 @@ util.inherits(ImapFetch, EventEmitter); /****** Utility Functions ******/ function buildSearchQuery(options, extensions, isOrChild) { - var searchargs = '', - months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; + var searchargs = ''; for (var i=0,len=options.length; i