Added the ability to specify a byte range when fetching a message's (raw or part) body. Fixed a bug that was causing flags to not be added or removed at all. Lastly, a list of available permanent flags for the current mailbox is now available under the permFlags property of the mailbox object.

fork
Brian 14 years ago
parent 0c5ed3df53
commit 0bc8f47e97

@ -55,6 +55,7 @@ node-imap exposes one object: **ImapConnection**.
* _Box_ is an Object representing the currently open mailbox, and has the following properties:
* **name** - A String containing the name of this mailbox.
* **permFlags** - An Array containing the flags that can be permanently added/removed to/from messages in this mailbox.
* **messages** - An Object containing properties about message counts for this mailbox.
* **total** - An Integer representing total number of messages in this mailbox.
* **new** - An Integer representing the number of new (unread) messages in this mailbox.
@ -153,6 +154,15 @@ The structure of a message with only one part will simply look something like th
]
Therefore, an easy way to check for a multipart message is to check if the structure length is >1.
Lastly, here are the system flags defined by the IMAP spec (that may be added/removed to/from messages):
* \Seen - Message has been read
* \Answered - Message has been answered
* \Flagged - Message is "flagged" for urgent/special attention
* \Deleted - Message is "deleted" for removal
* \Draft - Message has not completed composition (marked as a draft).
It should be noted however that the IMAP server can limit which flags can be permanently modified for any given message. If in doubt, check the mailbox's **permFlags** Array first.
Additional custom flags may be provided by the server. If available, these will also be listed in the mailbox's **permFlags** Array.
ImapConnection Events
---------------------
@ -195,7 +205,7 @@ ImapConnection Functions
* 'NEW' - Messages that have the \Recent flag set but not the \Seen flag.
* 'SEEN' - Messages that have the \Seen flag set.
* 'RECENT' - Messages that have the \Recent flag set.
* 'OLD' - Messages that do not have the \Recent flag set. This is functionally equivalent to "!RECENT" (as opposed to "!NEW").
* 'OLD' - Messages that do not have the \Recent flag set. This is functionally equivalent to a criteria of "!RECENT" (as opposed to "!NEW").
* 'UNANSWERED' - Messages that do not have the \Answered flag set.
* 'UNDELETED' - Messages that do not have the \Deleted flag set.
* 'UNDRAFT' - Messages that do not have the \Draft flag set.
@ -226,13 +236,13 @@ ImapConnection Functions
* **request** - An Object indicating what to fetch (at least **headers** OR **body** must be set to false -- in other words, you can only fetch one aspect of the message at a time):
* **struct** - A Boolean indicating whether to fetch the structure of the message. **Default:** true
* **headers** - A Boolean/Array value. A value of true fetches all message headers. An Array containing specific message headers to retrieve can also be specified. **Default:** true
* **body** - A Boolean/String value. A value of true fetches the entire raw message body. A String value containing a valid partID (see _FetchResult_'s structure property) fetches the body/content of that particular part. **Default:** false
* **body** - A Boolean/String/Array value. A Boolean value of true fetches the entire raw message body. A String value containing a valid partID (see _FetchResult_'s structure property) fetches the entire body/content of that particular part. An Array value of length 2 can be specified if you wish to request a byte range of the content, where the first item is a Boolean/String as previously described and the second item is a String indicating the byte range, for example, to fetch the first 500 bytes: '0-500'. **Default:** false
* **removeDeleted**(Function) - _(void)_ - Permanently removes all messages flagged as \Deleted. The Function parameter is the callback with two parameters: the error (null if none), the _Box_ object of the currently open mailbox.
* **addFlags**(Integer, String/Array, Function) - _(void)_ - Adds the specified flag(s) to the message identified by the Integer parameter. 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 two parameters: the error (null if none), the _Box_ object of the currently open mailbox.
* **addFlags**(Integer, String/Array, Function) - _(void)_ - Adds the specified flag(s) to the message identified by the Integer parameter. 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 two parameters: the error (null if none), the _Box_ object of the currently open mailbox.
* **delFlags**(Integer, String/Array, Function) - _(void)_ - Removes the specified flag(s) from the message identified by the Integer parameter. 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 two parameters: the error (null if none), the _Box_ object of the currently open mailbox.
* **delFlags**(Integer, String/Array, Function) - _(void)_ - Removes the specified flag(s) from the message identified by the Integer parameter. 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 two parameters: the error (null if none), the _Box_ object of the currently open mailbox.
TODO
@ -244,7 +254,7 @@ A bunch of things not yet implemented in no particular order:
* Support AUTH=CRAM-MD5/AUTH=CRAM_MD5 authentication
* OR searching ability with () grouping
* HEADER.FIELDS.NOT capability during FETCH using "!" prefix
* Allow FETCHing of byte ranges of body TEXTs instead of always the entire body (useful for previews of large messages, etc)
* Support IMAP keywords (with a workaround for gmail's lack of support for IMAP keywords)
* Support additional IMAP commands/extensions:
* APPEND (is this really useful?)
* GETQUOTA (via QUOTA extension -- http://tools.ietf.org/html/rfc2087)

@ -22,7 +22,7 @@ function ImapConnection (options) {
tmoKeepalive: 10000,
curData: '',
fetchData: { flags: [], date: null, headers: null, body: null, structure: null, _total: 0 },
box: { _uidnext: 0, _uidvalidity: 0, _flags: [], _permflags: [], name: null, messages: { total: 0, new: 0 }}
box: { _uidnext: 0, _uidvalidity: 0, _flags: [], permFlags: [], name: null, messages: { total: 0, new: 0 }}
};
this._capabilities = [];
@ -68,7 +68,7 @@ ImapConnection.prototype.connect = function(loginCb) {
fnInit();
});
this._state.conn.on('data', function(data) {
var literalData;
var literalData = '';
debug('RECEIVED: ' + data);
if (data.indexOf(CRLF) === -1) {
@ -148,7 +148,7 @@ ImapConnection.prototype.connect = function(loginCb) {
else if ((result = /^\[UIDNEXT (\d+)\]$/i.exec(data[2])) !== null)
self._state.box._uidnext = result[1];
else if ((result = /^\[PERMANENTFLAGS \((.*)\)\]$/i.exec(data[2])) !== null)
self._state.box._permflags = result[1].split(' ');
self._state.box.permFlags = result[1].split(' ');
}
break;
case 'SEARCH':
@ -184,7 +184,7 @@ ImapConnection.prototype.connect = function(loginCb) {
self._state.fetchData.date = result[2];
self._state.fetchData.flags = result[3].split(' ').filter(isNotEmpty);
if (literalData.length > 0) {
result = /BODY\[(.*)\] \{[\d]+\}$/.exec(data[2]);
result = /BODY\[(.*)\](?:\<[\d]+\>)? \{[\d]+\}$/.exec(data[2]);
if (result[1].indexOf('HEADER') === 0) { // either full or selective headers
var headers = literalData.split(/\r\n(?=[\w])/), header;
self._state.fetchData.headers = {};
@ -406,26 +406,40 @@ ImapConnection.prototype.fetch = function(uid, options, cb) {
headers: true, // \_______ at most one of these can be used for any given fetch request
body: false // /
}
}, toFetch;
}, toFetch, bodyRange = '';
cb = arguments[arguments.length-1];
if (typeof options !== 'object')
options = {};
options = extend(true, defaults, options);
if (!Array.isArray(options.request.headers)) {
if (Array.isArray(options.request.body)) {
var rangeInfo;
if (options.request.body.length !== 2)
throw new Error("Expected Array of length 2 for body property for byte range");
else if (typeof options.request.body[1] !== 'string'
|| !(rangeInfo = /^([\d]+)\-([\d]+)$/.exec(options.request.body[1]))
|| parseInt(rangeInfo[1]) >= parseInt(rangeInfo[2]))
throw new Error("Invalid body byte range format");
bodyRange = '<' + parseInt(rangeInfo[1]) + '.' + parseInt(rangeInfo[2]) + '>';
options.request.body = options.request.body[0];
}
if (typeof options.request.headers === 'boolean' && options.request.headers === true)
toFetch = 'HEADER'; // fetches headers only
else if (typeof options.request.body === 'boolean' && options.request.body === true)
toFetch = 'TEXT'; // fetches the whole entire message text (minus the headers), including all message parts
else if (typeof options.request.body === 'string')
else if (typeof options.request.body === 'string') {
if (!/^([\d]+[\.]{0,1})*[\d]+$/.test(options.request.body))
throw new Error("Invalid body partID format");
toFetch = options.request.body; // specific message part identifier, e.g. '1', '2', '1.1', '1.2', etc
}
} else
toFetch = 'HEADER.FIELDS (' + options.request.headers.join(' ').toUpperCase() + ')'; // fetch specific headers only
this._resetFetch();
this._send('UID FETCH ' + uid + ' (FLAGS INTERNALDATE'
+ (options.request.struct ? ' BODYSTRUCTURE' : '')
+ (toFetch ? ' BODY' + (!options.markSeen ? '.PEEK' : '') + '[' + toFetch + ']' : '') + ')', cb);
+ (toFetch ? ' BODY' + (!options.markSeen ? '.PEEK' : '') + '[' + toFetch + ']' + bodyRange : '') + ')', cb);
};
ImapConnection.prototype.removeDeleted = function(cb) {
@ -465,9 +479,13 @@ ImapConnection.prototype._storeFlag = function(uid, flags, isAdding, cb) {
throw new Error('Flags argument must be a string or a non-empty Array');
if (!Array.isArray(flags))
flags = [flags];
for (var i=0; i<flags.length; i++) {
if (this._state.box.permFlags.indexOf(flags[i]) === -1 || flags[i] === '\*')
throw new Error('The flag "' + flags[i] + '" is not allowed by the server for this mailbox');
}
cb = arguments[arguments.length-1];
this._send('STORE ' + (isAdding ? '+' : '-') + 'FLAGS.SILENT (' + flags.join(' ') + ')', cb);
this._send('UID STORE ' + uid + ' ' + (isAdding ? '+' : '-') + 'FLAGS.SILENT (' + flags.join(' ') + ')', cb);
};
ImapConnection.prototype._login = function(cb) {
@ -511,8 +529,8 @@ ImapConnection.prototype._resetBox = function() {
this._state.box._uidnext = 0;
this._state.box._uidvalidity = 0;
this._state.box._flags = [];
this._state.box._permflags = [];
this._state.box._lastSearch = null;
this._state.box.permFlags = [];
this._state.box.name = null;
this._state.box.messages.total = 0;
this._state.box.messages.new = 0;

Loading…
Cancel
Save