From 0b319c31a060134b4022fa0d7123479b679c2379 Mon Sep 17 00:00:00 2001 From: Andrew Jessup Date: Mon, 30 Jan 2012 00:47:46 +1100 Subject: [PATCH] Updates .append() and ._send() to support Buffers, .append() to use a configuration object, and some code tidying --- README.md | 6 ++++- imap.js | 75 +++++++++++++++++++++++++++++++++---------------------- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 60ea1ec..3332c62 100644 --- a/README.md +++ b/README.md @@ -370,7 +370,11 @@ 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**(String, Array, Date, Function) - _(void)_ - Appends a message to selected mailbox with the specified flags and date. The first parameter is a string the message to be appended, which should be a RFC-822 compatible MIME document including any headers and suitably encoded message attachments. The second parameter is should be an array of strings that denote the flags to be applied to the appended message (eg. ['\Seen', '\Flagged']) or null or an empty array if no flags should be set. The third parameter is a Date object that denotes the date the IMAP server should consider the message as being received, or it can be null (in which case the server will determine the received date). The Function parameter is the callback with one parameter: the error (null if none). **Note:** This method only serves to manipulate a connected mailbox, not to actually send mail. Some IMAP servers such as GMail will modify an existing message rather than create a new one if the specified 'Message-ID' header conflicts with a message already stored in that mailbox. +* **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). diff --git a/imap.js b/imap.js index 9ab33d3..19b6773 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) { @@ -613,30 +615,43 @@ ImapConnection.prototype._search = function(which, options, cb) { + buildSearchQuery(options, this.capabilities), cb); }; -ImapConnection.prototype.append = function(mimedata, flags, date, cb) { - if (this._state.status !== STATES.BOXSELECTED) { - throw new Error('No mailbox is currently selected'); +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(' \\')+")"; } - cmd = 'APPEND '+this._state.box.name+' '; - if(flags && flags.length) - if (!Array.isArray(flags)) - throw new Error('Expected null or array for flags'); - cmd += "("+flags.join(' ')+") "; - if(date){ - var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; - if (!(date instanceof Date)) + if ('date' in options) { + if (!(options.date instanceof Date)) throw new Error('Expected null or Date object for date'); - cmd += '"'+date.getDate()+'-'+months[date.getMonth()]+'-'+date.getFullYear(); - cmd += ' '+('0'+date.getHours().toString()).slice(-2)+':'+('0'+date.getMinutes().toString()).slice(-2)+':'+('0'+date.getSeconds().toString()).slice(-2); - cmd += ((date.getTimezoneOffset() > 0) ? ' -' : ' +' ); - cmd += ('0'+(-date.getTimezoneOffset() / 60).toString()).slice(-2); - cmd += ('0'+(-date.getTimezoneOffset() % 60).toString()).slice(-2); - cmd += '" '; + 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 += '"'; + } + if (data instanceof Buffer) { + cmd += ' {'+data.length+"}\r\n" + cmdbuf = new Buffer(Buffer.byteLength(cmd)+data.length); + cmdbuf.write(cmd); + data.copy(cmdbuf, Buffer.byteLength(cmd)); + this._send(cmdbuf, cb); + } else { + cmd += ' {'+Buffer.byteLength(data.toString())+"}\r\n" + cmd += data.toString(); // Send the command+data as a string + this._send(cmd, cb); } - cmd += '{'+mimedata.length+"}\r\n" + mimedata; - this._send(cmd, cb); } + ImapConnection.prototype.fetch = function(uids, options) { return this._fetch('UID ', uids, options); }; @@ -961,12 +976,12 @@ ImapConnection.prototype._noop = function() { if (this._state.status >= STATES.AUTH) this._send('NOOP'); }; -ImapConnection.prototype._send = function(cmdstr, cb, bypass) { - if (typeof cmdstr !== 'undefined' && !bypass) - this._state.requests.push({ command: cmdstr, callback: cb, args: [] }); - if ((typeof cmdstr === 'undefined' && this._state.requests.length) || +ImapConnection.prototype._send = function(cmdobj, cb, bypass) { + if (typeof cmdobj !== 'undefined' && !bypass) + this._state.requests.push({ command: cmdobj, callback: cb, args: [] }); + if ((typeof cmdobj === 'undefined' && this._state.requests.length) || this._state.requests.length === 1 || bypass) { - var prefix = '', cmd = (bypass ? cmdstr : this._state.requests[0].command); + var prefix = '', cmd = (bypass ? cmdobj : this._state.requests[0].command); clearTimeout(this._state.tmrKeepalive); if (this._state.ext.idle.sentIdle && cmd !== 'DONE') { this._send('DONE', undefined, true); @@ -981,7 +996,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'); } }; @@ -994,9 +1011,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