Added: james/project/trunk/server/protocols/jmap/doc/spec/mailbox.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/doc/spec/mailbox.mdwn?rev=1726757&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/doc/spec/mailbox.mdwn (added)
+++ james/project/trunk/server/protocols/jmap/doc/spec/mailbox.mdwn Tue Jan 26 
10:04:59 2016
@@ -0,0 +1,270 @@
+## Mailboxes
+
+A mailbox represents a named set of emails. This is the primary mechanism for 
organising messages within an account. It is analogous to a folder in IMAP or a 
label in other systems. A mailbox may perform a certain role in the system.
+
+A **Mailbox** object has the following properties:
+
+- **id**: `String`
+  The id of the mailbox. This property is immutable.
+- **name**: `String`
+  User-visible name for the mailbox, e.g. "Inbox". This may be any UTF-8 
string of at least 1 character in length and maximum 256 bytes in size. 
Mailboxes MAY have the same name as a sibling Mailbox.
+- **parentId**: `String|null`
+  The mailbox id for the parent of this mailbox, or `null` if this mailbox is 
at the top level. Mailboxes form acyclic graphs (forests) directed by the 
child-to-parent relationship. There MUST NOT be a loop.
+- **role**: `String|null`
+  Identifies system mailboxes. This property is immutable.
+
+  The following values SHOULD be used for the relevant mailboxes:
+
+  - `inbox` – the mailbox to which new mail is delivered by default, unless 
diverted by a rule or spam filter etc.
+  - `archive` – messages the user does not need right now, but does not 
wish to delete.
+  - `drafts` – messages the user is currently writing and are not yet sent.
+  - `outbox` – messages the user has finished writing and wishes to send 
(see the `setMessages` method description for more information). A mailbox with 
this role MUST be present if the user is allowed to send mail through an 
account. If not present, the user MAY NOT send mail with that account.
+  - `sent` – messages the user has sent.
+  - `trash` – messages the user has deleted.
+  - `spam` – messages considered spam by the server.
+  - `templates` – drafts which should be used as templates (i.e. used as 
the basis for creating new drafts).
+
+  No two mailboxes may have the same role. Mailboxes without a known purpose 
MUST have a role of `null`.
+
+  An account is not required to have mailboxes with any of the above roles. A 
client MAY create new mailboxes with a role property to help them keep track of 
a use-case not covered by the above list. To avoid potential conflict with any 
special behaviour a server might apply to mailboxes with certain roles in the 
future, any roles not in the above list created by the client must begin with 
`"x-"`. The client MAY attempt to create mailboxes with the standard roles if 
not already present, but the server MAY reject these.
+- **sortOrder**: `Number`
+  Defines the sort order of mailboxes when presented in the UI, so it is
+  consistent between devices. The number MUST be an integer in the range
+  0 <= sortOrder < 2^31.
+
+  A mailbox with a lower order should be displayed before a mailbox with
+  a higher order (but the same parent) in any mailbox listing in the
+  client's UI. Mailboxes with equal order should be sorted in
+  alphabetical order by name. The sorting should take into locale-specific
+  character order convention.
+- **mustBeOnlyMailbox**: `Boolean`
+  If true, messages in this mailbox may not also be in any other mailbox.
+- **mayReadItems**: `Boolean`
+  If true, may use this mailbox as part of a filter in a *getMessageList* call.
+  If a submailbox is shared but not the parent mailbox, this may be `false`.
+- **mayAddItems**: `Boolean`
+  The user may add messages to this mailbox (by either creating a new message 
or modifying an existing one).
+- **mayRemoveItems**: `Boolean`
+  The user may remove messages from this mailbox (by either changing the 
mailboxes of a message or deleting it).
+- **mayCreateChild**: `Boolean`
+  The user may create a mailbox with this mailbox as its parent.
+- **mayRename**: `Boolean`
+  The user may rename the mailbox or make it a child of another mailbox.
+- **mayDelete**: `Boolean`
+  The user may delete the mailbox itself.
+- **totalMessages**: `Number`
+  The number of messages in this mailbox.
+- **unreadMessages**: `Number`
+  The number of messages in this mailbox where the *isUnread* property of the 
message is `true` and the *isDraft* property is `false`.
+- **totalThreads**: `Number`
+  The number of threads where at least one message in the thread is in this 
mailbox (but see below for special case handling of Trash).
+- **unreadThreads**: `Number`
+  The number of threads where at least one message in the thread has `isUnread 
== true` and `isDraft == false` AND at least one message in the thread is in 
this mailbox (but see below for special case handling of Trash). Note, the 
unread message does not need to be the one in this mailbox.
+
+The Trash mailbox (that is a mailbox with `role == "trash"`) MUST be treated 
specially:
+
+* Messages in the Trash are ignored when calculating the `unreadThreads` and 
`totalThreads` count of other mailboxes.
+* Messages not in the Trash are ignored when calculating the `unreadThreads` 
and `totalThreads` count for the Trash folder.
+
+The result of this is that messages in the Trash are treated as though they 
are in a separate thread for the purposes of mailbox counts. It is expected 
that clients will hide messages in the Trash when viewing a thread in another 
mailbox and vice versa. This allows you to delete a single message to the Trash 
out of a thread.
+
+For example, suppose you have an account where the entire contents is a single 
conversation with 2 messages: an unread message in the Trash and a read message 
in the Inbox. The `unreadThreads` count would be `1` for the Trash and `0` for 
the Inbox.
+
+### getMailboxes
+
+Mailboxes can either be fetched explicitly by id, or all of them at once. To 
fetch mailboxes, make a call to `getMailboxes`. It takes the following 
arguments:
+
+- **accountId**: `String|null`
+  The Account to fetch the mailboxes for. If `null`, the primary account is 
used.
+- **ids**: `String[]|null`
+  The ids of the mailboxes to fetch. If `null`, all mailboxes in the account 
are returned.
+- **properties**: `String[]|null`
+  The properties of each mailbox to fetch. If `null`, all properties are 
returned. The id of the mailbox will **always** be returned, even if not 
explicitly requested.
+
+The response to *getMailboxes* is called *mailboxes*. It has the following 
arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **state**: `String`
+  A string representing the state on the server for **all** mailboxes. If the 
state of a mailbox changes, or a new mailbox is created, or a mailbox is 
destroyed, this string will change. It is used to get delta updates.
+- **list**: `Mailbox[]`
+  An array of the Mailbox objects requested. This will be the **empty array** 
if the *ids* argument was the empty array, or contained only ids for mailboxes 
that could not be found.
+- **notFound**: `String[]|null`
+  This array contains the ids passed to the method for mailboxes that do not 
exist, or `null` if all requested ids were found. It will always be `null` if 
the *ids* argument in the call was `null`.
+
+The following errors may be returned instead of the *mailboxes* response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`invalidArguments`: Returned if the request does not include one of the 
required arguments, or one of the arguments is of the wrong type, or otherwise 
invalid. A `description` property MAY be present on the response object to help 
debug with an explanation of what the problem was.
+
+### getMailboxUpdates
+
+The *getMailboxUpdates* call allows a client to efficiently update the state 
of its cached mailboxes to match the new state on the server. It takes the 
following arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If `null`, the primary account 
will be used.
+- **sinceState**: `String`
+  The current state of the client. This is the string that was returned as the 
*state* argument in the *mailboxes* response. The server will return the 
changes made since this state.
+- **fetchRecords**: `Boolean|null`
+  If `true`, after outputting a *mailboxUpdates* response, an implicit call 
will be made to *getMailboxes* with the *changed* property of the response as 
the *ids* argument, and the *fetchRecordProperties* argument as the 
*properties* argument. If `false` or `null`, no implicit call will be made.
+- **fetchRecordProperties**: `String[]|null`
+  If `null`, all Mailbox properties will be fetched unless *onlyCountsChanged* 
in the *mailboxUpdates* response is `true`, in which case only the 4 counts 
properties will be returned (*totalMessages*, *unreadMessages*, *totalThreads* 
and *unreadThreads*). If not `null`, this value will be passed through to the 
*getMailboxes* call regardless of the *onlyCountsChanged* value in the 
*mailboxUpdates* response.
+
+The response to *getMailboxUpdates* is called *mailboxUpdates*. It has the 
following arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **oldState**: `String`
+  This is the *sinceState* argument echoed back; the state from which the 
server is returning changes.
+- **newState**: `String`
+  This is the state the client will be in after applying the set of changes to 
the old state.
+- **changed**: `String[]`
+  An array of Mailbox ids where a property of the mailbox has changed between 
the old state and the new state, or the mailbox has been created, and the 
mailbox has not been destroyed.
+- **removed**: `String[]`
+  An array of Mailbox ids for mailboxes which have been destroyed since the 
old state.
+- **onlyCountsChanged**: `Boolean`
+  Indicates that only the folder counts (unread/total messages/threads) have 
changed since the old state. The client can then use this to optimise its data 
transfer and only fetch the counts. If the server is unable to tell if only 
counts have changed, it should just always return `false`.
+
+If a mailbox has been modified AND deleted since the oldState, the server 
should just return the id in the *removed* array, but MAY return it in the 
*changed* array as well. If a mailbox has been created AND deleted since the 
oldState, the server SHOULD remove the mailbox id from the response entirely, 
but MAY include it in the *removed* array, and optionally the *changed* array 
as well.
+
+The following errors may be returned instead of the `mailboxUpdates` response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`invalidArguments`: Returned if the request does not include one of the 
required arguments, or one of the arguments is of the wrong type, or otherwise 
invalid. A `description` property MAY be present on the response object to help 
debug with an explanation of what the problem was.
+
+`cannotCalculateChanges`: Returned if the server cannot calculate the changes 
from the state string given by the client. Usually due to the client's state 
being too old. The client MUST invalidate its Mailbox cache. The error object 
MUST also include a `newState: String` property with the current state for the 
type.
+
+### setMailboxes
+
+Mailboxes can be created, updated and destroyed using the *setMailboxes* 
method. The method takes the following arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If `null`, defaults to the 
primary account.
+- **ifInState**: `String|null`
+  This is a state string as returned by the *getMailboxes* method. If 
supplied, the string must match the current state, otherwise the method will be 
aborted and a `stateMismatch` error returned.
+- **create**: `String[Mailbox]|null`
+  A map of *creation id* (an arbitrary string set by the client) to Mailbox 
objects. If `null`, no objects will be created.
+- **update**: `String[Mailbox]|null`
+  A map of id to a an object containing the properties to update for that 
Mailbox. If `null`, no objects will be updated.
+- **destroy**: `String[]|null`
+  A list of ids for Mailboxes to permanently delete. If `null`, no objects 
will be deleted.
+
+Each create, update or destroy is considered an atomic unit. The server MAY 
commit some of the changes but not others, however MAY NOT only commit part of 
an update to a single record (e.g. update the *name* field but not the 
*parentId* field, if both are supplied in the update object).
+
+If a create, update or destroy is rejected, the appropriate error should be 
added to the notCreated/notUpdated/notDestroyed property of the response and 
the server MUST continue to the next create/update/destroy. It does not 
terminate the method.
+
+####  Creating mailboxes
+
+The properties of the Mailbox object submitted for creation MUST conform to 
the following conditions:
+
+- The *id* property MUST NOT be present.
+- The *parentId* property MUST be either `null` or be a valid id for a mailbox 
for which the `mayCreateChild` property is `true`.
+- The *role* property MUST be either `null`, a valid role as listed in the 
Mailbox object specification, or prefixed by `"x-"`.
+- The *mustBeOnlyMailbox* property MUST NOT be present. This is server 
dependent and will be set by the server.
+- The *mayXXX* properties MUST NOT be present. Restrictions may only be set by 
the server for system mailboxes, or when sharing mailboxes with other users 
(setting sharing is not defined yet in this spec).
+- The *totalMessages*, *unreadMessages*, *totalThreads* and *unreadThreads* 
properties MUST NOT be present.
+
+If any of the properties are invalid, the server MUST reject the create with 
an `invalidProperties` error. The Error object SHOULD contain a property called 
*properties* of type `String[]` that lists **all** the properties that were 
invalid. The object MAY also contain a *description* property of type `String` 
with a user-friendly description of the problems.
+
+There may be a maximum number of mailboxes allowed on the server. If this is 
reached, any attempt at creation will be rejected with a `maxQuotaReached` 
error.
+
+####  Updating mailboxes
+
+If the *id* given does not correspond to a Mailbox in the given account, the 
update MUST be rejected with a `notFound` error.
+
+All properties being updated must be of the correct type, not immutable or 
server-set-only, and the new value must obey all conditions of the property. In 
particular, note the following conditions:
+
+- The *name* property MUST be valid UTF-8, between 1 and 256 bytes in size.
+- The *parentId* property MUST be either `null` or be a valid id for *another* 
mailbox that is **not a descendant** of this mailbox, and for which the 
`mayCreateChild` property is `true`.
+- These properties are immutable or may only be set by the server:
+  - id
+  - role
+  - mustBeOnlyMailbox
+  - mayReadMessageList
+  - mayAddMessages
+  - mayRemoveMessages
+  - mayCreateChild
+  - mayRenameMailbox
+  - mayDeleteMailbox
+  - totalMessages
+  - unreadMessages
+  - totalThreads
+  - unreadThreads
+
+If any of the properties are invalid, the server MUST reject the update with 
an `invalidProperties` error. The Error object SHOULD contain a property called 
*properties* of type `String[]` that lists **all** the properties that were 
invalid. The object MAY also contain a *description* property of type `String` 
with a user-friendly description of the problems.
+
+#### Destroying mailboxes
+
+If the *id* given does not correspond to a Mailbox in the given account, the 
destruction MUST be rejected with a `notFound` error.
+
+If the mailbox has `mayDeleteMailbox == false`, the destruction MUST be 
rejected with a `forbidden` error.
+
+A mailbox MAY NOT be destroyed if it still has any child mailboxes. Attempting 
to do so will result in the destruction being rejected with a `mailboxHasChild` 
error.
+
+Destroying a mailbox MUST NOT delete any messages still contained within it. 
It only removes them from the mailbox. Since messages MUST always be in at 
least one mailbox, if the last mailbox they are in is deleted the messages must 
be added to the mailbox with `role == "inbox"`. If no Inbox exists, the 
messages must be moved to any other mailbox; this is server dependent.
+
+There MUST always be **at least one** mailbox. It is expected that the server 
will enforce this by setting `mayDeleteMailbox == false` on at least the Inbox, 
if not all system mailboxes. However, if this is not the case, an attempt to 
destroy the last mailbox MUST still be rejected with a `mailboxRequired` error.
+
+#### Ordering of changes
+
+Each individual create, update or destroy MUST take the server from one valid 
state to another valid state, so the changes can be processed linearly without 
need to undo or backtrack. However the order of the changes may affect the 
validity of the change. The server MUST process the changes in a manner which 
is indistinguishable from an order following these rules:
+
+1. Creates comes before any other changes.
+2. Creates with a parentId that is not a *creation id* reference come before 
those that reference another newly created mailbox.
+3. Creation of a mailbox X comes before creation of a mailbox Y if Y will be a 
descendent of X.
+4. Updates come before destroying mailboxes.
+5. Update/Destroy X comes before update/destroy Y if X is a descendent of Y.
+
+#### Response
+
+The response to *setMailboxes* is called *mailboxesSet*. It has the following 
arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **oldState**: `String|null`
+  The state string that would have been returned by `getMailboxes` before 
making the requested changes, or `null` if the server doesn't know what the 
previous state string was.
+- **newState**: `String`
+  The state string that will now be returned by `getMailboxes`.
+- **created**: `String[Mailbox]`
+  A map of the creation id to an object containing the **id** and 
**mustBeOnlyMailbox** properties for each successfully created Mailbox.
+- **updated**: `String[]`
+  A list of ids for Mailboxes that were successfully updated.
+- **destroyed**: `String[]`
+  A list of ids for Mailboxes that were successfully destroyed.
+- **notCreated**: `String[SetError]`
+  A map of creation id to a SetError object for each Mailbox that failed to be 
created. The possible errors are defined above.
+- **notUpdated**: `String[SetError]`
+  A map of Mailbox id to a SetError object for each Mailbox that failed to be 
updated. The possible errors are defined above.
+- **notDestroyed**: `String[SetError]`
+  A map of Mailbox id to a SetError object for each Mailbox that failed to be 
destroyed. The possible errors are defined above.
+
+The following errors may be returned instead of the *mailboxesSet* response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`accountReadOnly`: Returned if the account has `isReadOnly == true`.
+
+`invalidArguments`: Returned if one of the arguments is of the wrong type, or 
otherwise invalid. A *description* property MAY be present on the response 
object to help debug with an explanation of what the problem was.
+
+`stateMismatch`: Returned if an `ifInState` argument was supplied and it does 
not match the current state.
+
+Example request:
+
+    [ "setMailboxes", {
+      "ifInState": "ms4123",
+      "update": {
+        "f3": {
+          "name": "The new name"
+        }
+      },
+      "destroy": [ "f5" ]
+    }, "#0" ]
+

Added: james/project/trunk/server/protocols/jmap/doc/spec/message.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/doc/spec/message.mdwn?rev=1726757&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/doc/spec/message.mdwn (added)
+++ james/project/trunk/server/protocols/jmap/doc/spec/message.mdwn Tue Jan 26 
10:04:59 2016
@@ -0,0 +1,494 @@
+## Messages
+
+Just like in IMAP, a message is **immutable** except for the boolean `isXXX` 
status properties and the set of mailboxes it is in. This allows for more 
efficient caching of messages, and gives easier backwards compatibility for 
servers implementing an IMAP interface to the same data.
+
+JMAP completely hides the complexities of MIME. All special encodings of 
either headers or the body, such as 
[base64](https://tools.ietf.org/html/rfc4648), or 
[RFC2047](http://tools.ietf.org/html/rfc2047) encoding of non-ASCII characters, 
MUST be fully decoded into standard UTF-8.
+
+A **Message** object has the following properties:
+
+- **id**: `String`
+  The id of the message.
+- **blobId**: `String`
+  The id representing the raw RFC2822 message. This may be used to download
+  the original message or to attach it directly to another message etc.
+- **threadId**: `String`
+  The id of the thread to which this message belongs.
+- **mailboxIds**: `String[]` (Mutable)
+  The ids of the mailboxes the message is in. A message MUST belong to one or 
more mailboxes at all times (until it is deleted).
+- **inReplyToMessageId**: `String|null`
+  The id of the Message this message is a reply to. This is primarily for 
drafts, but the server MAY support this for received messages as well by 
looking up the RFC2822 Message-Id referenced in the `In-Reply-To` header and 
searching for this message in the user's mail.
+- **isUnread**: `Boolean` (Mutable)
+  Has the message not yet been read? This corresponds to the **opposite** of 
the `\Seen` system flag in IMAP.
+- **isFlagged**: `Boolean` (Mutable)
+  Is the message flagged? This corresponds to the `\Flagged` system flag in 
IMAP.
+- **isAnswered**: `Boolean` (Mutable)
+  Has the message been replied to? This corresponds to the `\Answered` system 
flag in IMAP.
+- **isDraft**: `Boolean` (Mutable by the server only)
+  Is the message a draft? This corresponds to the `\Draft` system flag in IMAP.
+- **hasAttachment**: `Boolean`
+  Does the message have any attachments?
+- **headers**: `String[String]`
+  A map of header name to (decoded) header value for all headers in the 
message. For headers that occur multiple times (e.g. `Received`), the values 
are concatenated with a single new line (`\n`) character in between each one.
+- **from**: `Emailer|null`
+  An Emailer object (see below) containing the name/email from the parsed 
`From` header of the email. If the email doesn't have a `From` header, this is 
`null`.
+- **to**:  `Emailer[]|null`
+  An array of name/email objects (see below) representing the parsed `To` 
header of the email, in the same order as they appear in the header. If the 
email doesn't have a `To` header, this is `null`. If the header exists but does 
not have any content, the response is an array of zero length.
+- **cc**:  `Emailer[]|null`
+  An array of name/email objects (see below) representing the parsed `Cc` 
header of the email, in the same order as they appear in the header. If the 
email doesn't have a `Cc` header, this is `null`. If the header exists but does 
not have any content, the response is an array of zero length.
+- **bcc**:  `Emailer[]|null`
+  An array of name/email objects (see below) representing the parsed `Bcc` 
header of the email. If the email doesn't have a `Bcc` header (which will be 
true for most emails outside of the Sent mailbox), this is `null`. If the 
header exists but does not have any content, the response is an array of zero 
length.
+- **replyTo**: `Emailer|null`
+  An Emailer object (see below) containing the name/email from the parsed 
`Reply-To` header of the email. If the email doesn't have a `Reply-To` header, 
this is `null`.
+- **subject**: `String`
+  The subject of the message.
+- **date**: `Date`
+  The date the message was sent (or saved, if the message is a draft).
+- **size**: `Number`
+  The size in bytes of the whole message as counted by the server towards the 
user's quota.
+- **preview**: `String`
+  Up to 256 characters of the beginning of a plain text version of the message 
body. This is intended to be shown as a preview line on a mailbox listing, and 
the server may choose to skip quoted sections or salutations to return a more 
useful preview.
+- **textBody**: `String|null`
+  The plain text body part for the message. If there is only an HTML version 
of the body, a plain text version will be generated from this.
+- **htmlBody**: `String|null`
+  The HTML body part for the message if present. If there is only a plain text 
version of the body, an HTML version will be generated from this. Any scripting 
content, or references to external plugins, MUST be stripped from the HTML by 
the server.
+- **attachments**: `Attachment[]|null`
+  An array of attachment objects (see below) detailing all the attachments to 
the message.
+- **attachedMessages**: `String[Message]|null`
+  An object mapping attachment id (as found in the `attachments` property) to 
a **Message** object with the following properties, for each RFC2822 message 
attached to this one:
+  - headers
+  - from
+  - to
+  - cc
+  - bcc
+  - replyTo
+  - subject
+  - date
+  - textBody
+  - htmlBody
+  - attachments
+  - attachedMessages
+
+An **Emailer** object has the following properties:
+
+- **name**: `String`
+  The name of the sender/recipient. If a name cannot be extracted for an 
email, this property should be the empty string.
+- **email**: `String`
+  The email address of the sender/recipient. This MUST be of the form 
`"<mailbox>@<host>"` If a `host` or even `mailbox` cannot be extracted for an 
email, the empty string should be used for this part (so the result will always 
still contain an `"@"`).
+
+Example Emailer object:
+
+    [
+        {name:"Joe Bloggs", email:"j...@example.com"},
+        {name:"", email:"j...@example.com"},
+        {name:"John Smith", email: "john@"}
+    ]
+
+An **Attachment** object has the following properties:
+
+- **blobId**: `String`
+  The id of the binary data.
+- **type**: `String`
+  The content-type of the attachment.
+- **name**: `String`
+  The full file name, e.g. "myworddocument.doc"
+- **size**: `Number`
+  The size, in bytes, of the attachment when fully decoded (i.e. the number of 
bytes in the file the user would download).
+- **cid**: `String|null`
+  The id used within the message body to reference this attachment. This is 
only unique when paired with the message id, and has no meaning without 
reference to that.
+- **isInline**: `Boolean`
+  True if the attachment is referenced by a `cid:` link from within the HTML 
body of the message.
+- **width**: `Number|null`
+  The width (in px) of the image, if the attachment is an image.
+- **height**: `Number|null`
+  The height (in px) of the image, if the attachment is an image.
+
+### getMessages
+
+Messages can only be fetched explicitly by id. To fetch messages, make a call 
to `getMessages`. It takes the following arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If not given, defaults to the 
primary account.
+- **ids**: `String[]`
+  An array of ids for the messages to fetch.
+- **properties**: `String[]|null`
+  A list of properties to fetch for each message. If `null`, all properties 
will be fetched.
+
+The `id` property is always returned, regardless of whether it is in the list 
of requested properties. The possible values for `properties` can be found 
above in the description of the Message object. In addition to this, the client 
may request the following special values:
+
+- **body**: If `"body"` is included in the list of requested properties, it 
will be interpreted by the server as a request for `"htmlBody"` if the message 
has an HTML part, or `"textBody"` otherwise.
+- **headers.property**: Instead of requesting all the headers (by requesting 
the `"headers"` property, the client may specify the particular headers it 
wants using the `headers.property-name` syntax, e.g. `"headers.X-Spam-Score", 
"headers.X-Spam-Hits"`). The server will return a *headers* property but with 
just the requested headers in the object rather than all headers. If 
`"headers"` is requested, the server MUST ignore the individual header requests 
and just return all headers. If a requested header is not present in the 
message, it MUST not be present in the *headers* object. Header names are 
case-insensitive.
+
+The response to *getMessages* is called *messages*. It has the following 
arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **state**: `String`
+  A string encoding the current state on the server. This string will change
+  if any messages change (that is, a new message arrives, a change is made to 
one of the mutable properties, or a message is deleted). It can be passed to 
*getMessageUpdates* to efficiently get the list of changes from the previous 
state.
+- **list**: `Message[]`
+  An array of Message objects for the requested message ids. This may not be 
in the same order as the ids were in the request.
+- **notFound**: `String[]|null`
+  An array of message ids requested which could not be found, or `null` if all
+  ids were found.
+
+The following errors may be returned instead of the *messages* response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`invalidArguments`: Returned if the request does not include one of the 
required arguments, or one of the arguments is of the wrong type, or otherwise 
invalid. A *description* property MAY be present on the response object to help 
debug with an explanation of what the problem was.
+
+Example request:
+
+    ["getMessages", {
+      "ids": [ "f123u456", "f123u457" ],
+      "properties": [ "threadId", "mailboxIds", "from", "subject", "date" ]
+    }, "#1"]
+
+and response:
+
+    ["messages", {
+      "state": "41234123231",
+      "list": [
+        {
+          messageId: "f123u457",
+          threadId: "ef1314a",
+          mailboxIds: [ "f123" ],
+          from: [{name: "Joe Bloggs", email: "j...@bloggs.com"}],
+          subject: "Dinner on Thursday?",
+          date: "2013-10-13T14:12:00Z"
+        }
+      ],
+      notFound: [ "f123u456" ]
+    }, "#1"]
+
+
+### getMessageUpdates
+
+If a call to *getMessages* returns with a different *state* string in the 
response to a previous call, the state of the messages has changed on the 
server. For example, a new message may have been delivered, or an existing 
message may have changed mailboxes.
+
+The *getMessageUpdates* call allows a client to efficiently update the state 
of any cached messages to match the new state on the server. It takes the 
following arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If not given, defaults to the 
primary account.
+- **sinceState**: `String`
+  The current state of the client. This is the string that was returned as the 
*state* argument in the *messages* response. The server will return the changes 
made since this state.
+- **maxChanges**: `Number|null`
+  The maximum number of changed messages to return in the response. The server 
MAY choose to clamp this value to a particular maximum or set a maximum if none 
is given by the client. If supplied by the client, the value MUST be a positive 
integer greater than 0. If a value outside of this range is given, the server 
MUST reject the call with an `invalidArguments` error.
+- **fetchRecords**: `Boolean|null`
+  If true, after outputting a *messageUpdates* response, an implicit call will 
be made to *getMessages* with a list of all message ids in the *changed* 
argument of the response as the *ids* argument, and the *fetchRecordProperties* 
argument as the *properties* argument.
+- **fetchRecordProperties**: `String[]|null`
+  The list of properties to fetch on any fetched messages. See *getMessages* 
for a full description.
+
+The response to *getMessageUpdates* is called *messageUpdates*. It has the 
following arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **oldState**: `String`
+  This is the *sinceState* argument echoed back; the state from which the 
server is returning changes.
+- **newState**: `String`
+  This is the state the client will be in after applying the set of changes to 
the old state.
+- **hasMoreUpdates**: `Boolean`
+  If `true`, the client may call *getMessageUpdates* again with the *newState* 
returned to get further updates. If `false`, *newState* is the current server 
state.
+- **changed**: `String[]`
+  An array of message ids for messages that have either been created or had 
their state change, and are not currently deleted.
+- **removed**: `String[]`
+  An array of message ids for messages that have been deleted since the 
oldState.
+
+If a *maxChanges* is supplied, or set automatically by the server, the server 
must try to limit the number of ids across *changed* and *removed* to the 
number given. If there are more changes than this between the client's state 
and the current server state, the update returned MUST take the client to an 
intermediate state, from which the client can continue to call 
*getMessageUpdates* until it is fully up to date. The server MAY return more 
ids than the *maxChanges* total if this is required for it to be able to 
produce an update to an intermediate state, but it SHOULD try to keep it close 
to the maximum requested.
+
+If a message has been modified AND deleted since the oldState, the server 
should just return the id in the *removed* response, but MAY return it in the 
changed response as well. If a message has been created AND deleted since the 
oldState, the server should remove the message id from the response entirely, 
but MAY include it in the *removed* response, and (if in the *removed* 
response) MAY included it in the *changed* response as well.
+
+The following errors may be returned instead of the *messageUpdates* response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`invalidArguments`: Returned if the request does not include one of the 
required arguments, or one of the arguments is of the wrong type, or otherwise 
invalid. A *description* property MAY be present on the response object to help 
debug with an explanation of what the problem was.
+
+`cannotCalculateChanges`: Returned if the server cannot calculate the changes 
from the state string given by the client. Usually due to the client's state 
being too old, or the server being unable to produce an update to an 
intermediate state when there are too many updates. The client MUST invalidate 
its Message cache. The error object MUST also include a `newState: String` 
property with the current state for the type.
+
+### setMessages
+
+The *setMessages* method encompasses:
+
+- Creating a draft message
+- Sending a message
+- Changing the flags of a message (unread/flagged status)
+- Adding/removing a message to/from mailboxes (moving a message)
+- Deleting messages
+
+It takes the following arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If not given, defaults to the 
primary account.
+- **ifInState**: `String|null`
+  This is a state string as returned by the *getMessages* method. If supplied, 
the string must match the current state, otherwise the method will be aborted 
and a `stateMismatch` error returned.
+- **create**: `String[Message]|null`
+  A map of *creation id* (an arbitrary string set by the client) to Message 
objects (see below for a detailed description).
+- **update**: `String[Message]|null`
+  A map of id to a an object containing the properties to update for that 
Message.
+- **destroy**: `String[]|null`
+  A list of ids for Message objects to permanently delete.
+
+Each create, update or destroy is considered an atomic unit. It is permissible 
for the server to commit some of the changes but not others, however it is not 
permissible to only commit part of an update to a single record (e.g. update 
the *isFlagged* field but not the *mailboxIds* field, if both are supplied in 
the update object for a message).
+
+If a create, update or destroy is rejected, the appropriate error should be 
added to the notCreated/notUpdated/notDestroyed property of the response and 
the server MUST continue to the next create/update/destroy. It does not 
terminate the method.
+
+If an id given cannot be found, the update or destroy MUST be rejected with a 
`notFound` set error.
+
+#### Saving a draft
+
+Creating messages via the *setMessages* method is only for creating draft 
messages and sending them. For delivering/importing a complete RFC2822 message, 
use the `importMessages` method.
+
+The properties of the Message object submitted for creation MUST conform to 
the following conditions:
+
+- **id**: This property MUST NOT be included. It is set by the server upon 
creation.
+- **blobId**: This property MUST NOT be included. It is set by the server upon 
creation.
+- **threadId**: This property MUST NOT be included. It is set by the server 
upon creation.
+- **mailboxIds**: This property MUST be included. The value MUST include the 
id of either the mailbox with `role == "drafts"` (to save a draft) or the 
mailbox with `role == "outbox"` (to send the message). If this mailbox does not 
have `mustBeOnlyMailbox == true`, others may be included too.
+- **inReplyToMessageId**: Optional. If included, the server will look up this 
message and if found set appropriate `References` and `In-Reply-To` headers. 
These will override any such headers supplied in the *headers* property. If not 
found, the creation MUST be rejected with an `inReplyToNotFound` error.
+- **isUnread**: Optional, defaults to `false`. If included this MUST be 
`false`.
+- **isFlagged**: Optional, defaults to `false`.
+- **isAnswered**: Optional, defaults to `false`. If included this MUST be 
`false`.
+- **isDraft**: Optional, defaults to `true`. If included this MUST be `true`.
+- **hasAttachment**: This property MUST NOT be included. It is set by the 
server upon creation based on the attachments property.
+- **headers**: Optional. The keys MUST only contain the characters A-Z, a-z, 
0-9 and hyphens.
+- **from**: Optional. Overrides a "From" in the *headers*.
+- **to**: Optional. Overrides a "To" in the *headers*.
+- **cc**: Optional. Overrides a "Cc" in the *headers*.
+- **bcc**:  Optional. Overrides a "Bcc" in the *headers*.
+- **replyTo**: Optional. Overrides a "Reply-To" in the *headers*.
+- **subject**: Optional. Defaults to the empty string (`""`).
+- **date**: Optional. If included, the server SHOULD wait until this time to 
send the message (once moved to the outbox folder). Until it is sent, the send 
may be cancelled by moving the message back out of the outbox folder. If the 
date is in the past, the message must be sent immediately. A client may find 
out if the server supports delayed sending by querying the capabilities 
property of the Account object.
+- **size**: This MUST NOT be included. It is set by the server upon creation.
+- **preview**: This MUST NOT be included. It is set by the server upon 
creation.
+- **textBody**: Optional. If not supplied and an htmlBody is, the server 
SHOULD generate a text version for the message from this.
+- **htmlBody**: Optional. If this contains internal links (cid:) the cid value 
should be the attachment id.
+- **attachments**: Optional. An array of Attachment objects detailing all the 
attachments to the message. To add an attachment, the file must first be 
uploaded using the standard upload mechanism; this will give the client a URL 
that may be used to identify the file. The `id` property may be assigned by the 
client, and is solely used for matching up with `cid:<id>` links inside the 
`htmlBody`. The server MAY (and probably will) change the ids upon sending.
+
+  If one of the attachments is not found, the creation MUST be rejected with 
an `invalidProperties` error. An extra property SHOULD be included in the error 
object called `attachmentsNotFound`, of type `String[]`, which should be an 
array of the ids of any attachments that could not be found on the server.
+- **attachedMessages**: This MUST NOT be included.
+
+All optional properties default to `null` unless otherwise stated. Where 
included, properties MUST conform to the type given in the Message object 
definition.
+
+If any of the properties are invalid, the server MUST reject the create with 
an `invalidProperties` error. The Error object SHOULD contain a property called 
*properties* of type `String[]` that lists **all** the properties that were 
invalid. The object MAY also contain a *description* property of type `String` 
with a user-friendly description of the problems.
+
+Other than making sure it conforms to the correct type, the server MUST NOT 
attempt to validate from/to/cc/bcc when saved as a draft. This is to ensure 
messages can be saved at any point. Validation occurs when the user tries to 
send a message.
+
+If a draft cannot be saved due to the user reaching their maximum mail storage 
quota, the creation MUST be rejected with a `maxQuotaReached` error.
+
+#### Updating messages
+
+Messages are mainly immutable, so to update a draft the client must create a 
new message and delete the old one. This ensures that if the draft is also 
being edited elsewhere, the two will split into two different drafts to avoid 
data loss.
+
+Only the following properties may be modified:
+
+- **mailboxIds**: The server MUST reject any attempt to add a message with 
`isDraft == false` to the outbox. The server MAY reject attempts to add a draft 
message to a mailbox that does not have a role of `drafts`, `outbox` or 
`templates`.
+- **isFlagged**
+- **isUnread**
+- **isAnswered**
+
+Note, a mailbox id may be a *creation id* (see `setFoos` for a description of 
how this works).
+
+If any of the properties in the update are invalid (immutable and different to 
the current server value, wrong type, invalid value for the property – like 
a mailbox id for non-existent mailbox), the server MUST reject the update with 
an `invalidProperties` error. The Error object SHOULD contain a property called 
*properties* of type `String[]` that lists **all** the properties that were 
invalid. The object MAY also contain a *description* property of type `String` 
with a user-friendly description of the problems.
+
+If the *id* given does not correspond to a Message in the given account, 
reject the update with a `notFound` error.
+
+To **delete a message** to trash, simply change the `mailboxIds` property so 
it is now in the mailbox with `role == "trash"`. If the mailbox has the 
property `mustBeOnlyMailbox == true`, it must be removed from all other 
mailboxes. Otherwise, leave it in those mailboxes so that it will be restored 
to its previous state if undeleted.
+
+#### Sending messages
+
+To send a message, either create a new message directly into the mailbox with 
`role == "outbox"` or move an existing draft into this mailbox. At this point 
the server will check that it has everything it needs for a valid message. In 
particular, that it has a valid "From" address, it has at least one address to 
send to, and all addresses in To/Cc/Bcc are valid email addresses. If it cannot 
send, it will reject the creation/update with an `invalidProperties` error. The 
Error object SHOULD contain a property called *properties* of type `String[]` 
that lists **all** the properties that were invalid. The object SHOULD also 
contain a *description* property of type `String` with a user-friendly 
description of the problems to present to the user.
+
+If the message is accepted, the server should **asynchronously** schedule the 
message to be sent **after** this method call is complete (note, this MAY occur 
before the next method in the same API request or after the whole API request 
is complete). This means that the `newState` string in the response represents 
a state where the message is still in the outbox. When the message is sent, the 
server MUST delete the message from the **outbox** and SHOULD create a **new** 
copy of the sent message (with a new id) in the **sent** mailbox, unless the 
user has indicated another preference. If `inReplyToMessageId` was set, the 
server SHOULD mark this message as `isAnswered: true` at this point, if found.
+
+#### Cancelling a send
+
+A message may be moved out of the **outbox** and back to the **drafts** 
mailbox using the standard update message mechanism, if it has not yet been 
sent at the time the method is called. This MUST cancel the queued send. If the 
message has already been sent then it will have been deleted from the outbox, 
so the update will fail with a standard `notFound` error.
+
+#### Destroying messages
+
+If the *id* given does not correspond to a Message in the given account, the 
server MUST reject the destruction with a `notFound` error.
+
+Destroying a message removes it from all mailboxes to which it belonged.
+
+#### Response
+
+The response to *setMessages* is called *messagesSet*. It has the following 
arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **oldState**: `String|null`
+  The state string that would have been returned by *getMessages* before 
making the requested changes, or `null` if the server doesn't know what the 
previous state string was.
+- **newState**: `String`
+  The state string that will now be returned by *getMessages*.
+- **created**: `String[Message]`
+  A map of the creation id to an object containing the *id*, *blobId*, 
*threadId*, and *size* properties for each successfully created Message.
+- **updated**: `String[]`
+  A list of Message ids for Messages that were successfully updated.
+- **destroyed**: `String[]`
+  A list of Message ids for Messages that were successfully destroyed.
+- **notCreated**: `String[SetError]`
+  A map of creation id to a SetError object for each Message that failed to be 
created. The possible errors are defined above.
+- **notUpdated**: `String[SetError]`
+  A map of Message id to a SetError object for each Message that failed to be 
updated. The possible errors are defined above.
+- **notDestroyed**: `String[SetError]`
+  A map of Message id to a SetError object for each Message that failed to be 
destroyed. The possible errors are defined above.
+
+The following errors may be returned instead of the *messagesSet* response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`accountReadOnly`: Returned if the account has `isReadOnly == true`.
+
+`invalidArguments`: Returned if one of the arguments is of the wrong type, or 
otherwise invalid. A *description* property MAY be present on the response 
object to help debug with an explanation of what the problem was.
+
+`stateMismatch`: Returned if an *ifInState* argument was supplied and it does 
not match the current state.
+
+### importMessages
+
+The *importMessages* method adds RFC2822 messages to a user's set of messages. 
The messages must first be uploaded as a file using the standard upload 
mechanism. It takes the following arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If `null`, defaults to the 
primary account.
+- **messages**: `String[MessageImport]`
+  A map of creation id (client specified) to MessageImport objects
+
+An **ImportMessage** object has the following properties:
+
+- **file**: `String`
+  The URL of the uploaded file (see the file upload section).
+- **mailboxIds** `String[]`
+  The ids of the mailbox(es) to assign this message to.
+- **isUnread**: `Boolean`
+- **isFlagged**: `Boolean`
+- **isAnswered**: `Boolean`
+- **isDraft**: `Boolean`
+
+If `isDraft == true`, the mailboxes MUST include the drafts or outbox mailbox. 
Adding to the outbox will send the message, as described in the *setMessages* 
section (it will NOT automatically mark any other message as *isAnswered*).
+
+The response to *importMessages* is called *messagesImported*. It has the 
following arguments:
+
+- **accountId**: `String`
+  The id of the account used for this call.
+- **created**: `String[Message]`
+  A map of the creation id to an object containing the *id*, *blobId*, 
*threadId* and *size* properties for each successfully imported Message.
+- **notCreated**: `String[SetError]`
+  A map of creation id to a SetError object for each Message that failed to be 
created. The possible errors are defined above.
+
+The following errors may be returned instead of the *messageImported* response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`accountReadOnly`: Returned if the account has `isReadOnly == true`.
+
+`invalidArguments`: Returned if one of the arguments is of the wrong type, or 
otherwise invalid. A `description` property MAY be present on the response 
object to help debug with an explanation of what the problem was.
+
+`notFound`: Returned if the URL given in the `file` argument does not 
correspond to an internal file.
+
+`invalidMailboxes`: Returned if one of the mailbox ids cannot be found, or an 
invalid combination of mailbox ids is specified.
+
+`maxQuotaReached`: Returned if the user has reached their mail quota so the 
message cannot be imported.
+
+### copyMessages
+
+The only way to move messages **between** two different accounts is to copy 
them using the *copyMessages* method, then once the copy has succeeded, delete 
the original. It takes the following arguments:
+
+- **fromAccountId**: `String|null`
+  The id of the account to copy messages from. If `null`, defaults to the 
primary account.
+- **toAccountId**: `String|null`
+  The id of the account to copy messages to. If `null`, defaults to the 
primary account.
+- **messages**: `String[MessageCopy]`
+  A map of *creation id* to a MessageCopy object.
+
+A **MessageCopy** object has the following properties:
+
+- **messageId**: `String`
+  The id of the message to be copied in the "from" account.
+- **mailboxIds**: `String[]`
+  The ids of the mailboxes (in the "to" account) to add the copied message to.
+- **isUnread**: `Boolean`
+  The *isUnread* property for the copy.
+- **isFlagged**: `Boolean`
+  The *isFlagged* property for the copy.
+- **isAnswered**: `Boolean`
+  The *isAnswered* property for the copy.
+- **isDraft**: `Boolean`
+  The *isDraft* property for the copy.
+
+The "from" account may be the same as the "to" account to copy messages within 
an account.
+
+The response to *copyMessages* is called *messagesCopied*. It has the 
following arguments:
+
+- **fromAccountId**: `String`
+  The id of the account messages were copied from.
+- **toAccountId**: `String`
+  The id of the account messages were copied to.
+- **created**: `String[Message]|null`
+  A map of the creation id to an object containing the *id*, *blobId*, 
*threadId* and *size* properties for each successfully copied Message.
+- **notCreated**: `String[SetError]|null`
+  A map of creation id to a SetError object for each Message that failed to be 
copied, `null` if none.
+
+The **SetError** may be one of the following types:
+
+`notFound`: Returned if the messageId given can't be found.
+
+`invalidMailboxes`: Returned if one of the mailbox ids cannot be found, or an 
invalid combination of mailbox ids is specified.
+
+`maxQuotaReached`: Returned if the user has reached their mail quota so the 
message cannot be copied.
+
+The following errors may be returned instead of the *messagesCopied* response:
+
+`fromAccountNotFound`: Returned if a *fromAccountId* was explicitly included 
with the request, but it does not correspond to a valid account.
+
+`toAccountNotFound`: Returned if a *toAccountId* was explicitly included with 
the request, but it does not correspond to a valid account.
+
+`fromAccountNoMail`: Returned if the *fromAccountId* given corresponds to a 
valid account, but does not contain any mail data.
+
+`toAccountNoMail`: Returned if the *toAccountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`accountReadOnly`: Returned if the "to" account has `isReadOnly == true`.
+
+`invalidArguments`: Returned if one of the arguments is of the wrong type, or 
otherwise invalid. A `description` property MAY be present on the response 
object to help debug with an explanation of what the problem was.
+
+### reportMessages
+
+Messages can be reported as spam or non-spam to help train the user's spam 
filter. This MUST NOT affect the state of the Message objects (it DOES NOT move 
a message into or out of the Spam mailbox).
+
+To report messages, make a call to *reportMessages*. It takes the following 
arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If not given, defaults to the 
primary account.
+- **messageIds**: `String[]`
+  The list of ids of messages to report.
+- **asSpam**: `Boolean`
+  If `true`, learn these messages as spam. If `false`, learn as non-spam.
+
+
+The response to *reportMessages* is called *messagesReported*. It has the 
following arguments:
+
+- **accountId**: `String`
+  The id of the account used for this call.
+- **asSpam**: `Boolean`
+  Echoed back from the call
+- **reported**: `String[]`
+  The ids of each message successfully reported.
+- **notFound**: `String`
+  The ids of each message not found.
+
+The following errors may be returned instead of the *messagesReported* 
response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`accountReadOnly`: Returned if the account has `isReadOnly == true`.
+
+`invalidArguments`: Returned if one of the arguments is of the wrong type, or 
otherwise invalid. A `description` property MAY be present on the response 
object to help debug with an explanation of what the problem was.

Added: james/project/trunk/server/protocols/jmap/doc/spec/messagelist.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/doc/spec/messagelist.mdwn?rev=1726757&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/doc/spec/messagelist.mdwn (added)
+++ james/project/trunk/server/protocols/jmap/doc/spec/messagelist.mdwn Tue Jan 
26 10:04:59 2016
@@ -0,0 +1,272 @@
+## MessageLists
+
+A **MessageList** is a sorted query on the set of messages in a user's 
account. Since it can be very long, the client must specify what section of the 
list to return. The client can optionally also fetch the threads and/or 
messages for this part of the list.
+
+The same message may appear in multiple messages lists. For example, it may 
belong to multiple mailboxes, and of course it can appear in searches. Since 
messages have an immutable id, a client can easily tell if it already has a 
message cached and only fetch the ones it needs.
+
+When the state changes on the server, a delta update can be requested to 
efficiently update the client's cache of this list to the new state. If the 
server doesn't support this, the client still only needs to fetch the message 
list again, not the messages themselves.
+
+### getMessageList
+
+To fetch a message list, make a call to *getMessageList*. It takes the 
following arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If `null`, the primary account 
will be used.
+- **filter**: `FilterCondition|FilterOperator|null`
+  Determines the set of messages returned in the results. See the "Filtering" 
section below for allowed values and semantics.
+- **sort**: `String[]|null`
+  A list of Message property names to sort by. See the "Sorting" section below 
for allowed values and semantics.
+- **collapseThreads**: `Boolean|null`
+  If true, each thread will only be returned once in the resulting list, at 
the position of the first message in the list (given the filter and sort order) 
belonging to the thread. If `false` or `null`, threads may be returned multiple 
times.
+- **position**: `Number|null`
+  The 0-based index of the first result in the list to return. If a negative 
value is given, the call MUST be rejected with an `invalidArguments` error. If 
`null`, 0 is used.
+- **anchor**: `String|null`
+  A Message id. The index of this message id will be used in combination with 
the `anchorOffset` argument to determine the index of the first result to 
return (see the "Windowing" section below for more details).
+- **anchorOffset**: `Number|null`
+  The index of the anchor message relative to the index of the first result to 
return. This MAY be negative. For example, `-1` means the first message after 
the anchor message should be the first result in the results returned (see the 
"Windowing" section below for more details).
+- **limit**: `Number|null`
+  The maximum number of results to return. If `null`, no limit is presumed. 
The server MAY choose to enforce a maximum `limit` argument. In this case, if a 
greater value is given, the limit should be clamped to the maximum; since the 
total number of results in the list is returned, the client should not be 
relying on how many results are returned to determine if it has reached the end 
of the list. If a negative value is given, the call MUST be rejected with an 
`invalidArguments` error.
+- **fetchThreads**: `Boolean|null`
+  If `true`, after outputting a *messageList* response, an implicit call will 
be made to *getThreads* with the *threadIds* array in the response as the *ids* 
argument, and the *fetchMessages* and *fetchMessageProperties* arguments passed 
straight through from the call to *getMessageList*. If `false` or `null`, no 
implicit call will be made.
+- **fetchMessages**: `Boolean|null`
+  If `true` and `fetchThreads == false`, then after outputting a *messageList* 
response, an implicit call will be made to *getMessages* with the `messageIds` 
array in the response as the *ids* argument, and the *fetchMessageProperties* 
argument as the *properties* argument. If `false` or `null`, no implicit call 
will be made.
+- **fetchMessageProperties**: `String[]|null`
+  The list of properties to fetch on any fetched messages. See *getMessages* 
for a full description.
+- **fetchSearchSnippets**: `Boolean|null`
+  If `true`, then after outputting a *messageList* and making any other 
implicit calls, an implicit call will be made to *getSearchSnippets*. The 
*messageIds* array from the response will be used as the *messageIds* argument, 
and the *filter* argument will be passed straight through. If `false` or 
`null`, no implicit call will be made.
+
+#### Filtering
+
+A **FilterOperator** object has the following properties:
+
+- **operator**: `String`
+  This MUST be one of the following strings: "AND"/"OR"/"NOT":
+  - **AND**: all of the conditions must match for the filter to match.
+  - **OR**: at least one of the conditions must match for the filter to match.
+  - **NOT**: none of the conditions must match for the filter to match.
+- **conditions**: `(FilterCondition|FilterOperator)[]`
+  The conditions to evaluate against each message.
+
+A **FilterCondition** object has the following properties:
+
+- **inMailboxes**: `String[]|null`
+  A list of mailbox ids. A message must be in ALL of these mailboxes to match 
the condition.
+- **notInMailboxes**: `String[]|null`
+  A list of mailbox ids. A message must NOT be in ANY of these mailboxes to 
match the condition.
+- **before**: `Date|null`
+  The date of the message (as returned on the Message object) must be before 
this date to match the condition.
+- **after**: `Date|null`
+  The date of the message (as returned on the Message object) must be on or 
after this date to match the condition.
+- **minSize**: `Number|null`
+  The size of the message in bytes (as returned on the Message object) must be 
equal to or greater than this number to match the condition.
+- **maxSize**: `Number|null`
+  The size of the message in bytes (as returned on the Message object) must be 
less than this number to match the condition.
+- **threadIsFlagged**: `Boolean|null`
+  If `true`, the condition is matched if the `isFlagged` property of *any* 
message in the same thread as the message being examined is `true`. If `false`, 
the `isFlagged` property of *every* message in the same thread as the message 
being examined must be `false` to match the condition.
+- **threadIsUnread**: `Boolean|null`
+  If `true`, the condition is matched if the `isUnread` property of *any* 
message in the same thread as the message being examined is `true`. If `false`, 
the `isUnread` property of *every* message in the same thread as the message 
being examined must be `false` to match the condition.
+- **isFlagged**: `Boolean|null`
+  The `isFlagged` property of the message must be identical to the value given 
to match the condition.
+- **isUnread**: `Boolean|null`
+  The `isUnread` property of the message must be identical to the value given 
to match the condition.
+- **isAnswered**: `Boolean|null`
+  The `isAnswered` property of the message must be identical to the value 
given to match the condition.
+- **isDraft**: `Boolean|null`
+  The `isDraft` property of the message must be identical to the value given 
to match the condition.
+- **hasAttachment**: `Boolean|null`
+  The `hasAttachment` property of the message must be identical to the value 
given to match the condition.
+- **text**: `String|null`
+  Looks for the text in the *from*, *to*, *cc*, *bcc*, *subject*, *textBody* 
or *htmlBody* properties of the message.
+- **from**: `String|null`
+  Looks for the text in the *from* property of the message.
+- **to**: `String|null`
+  Looks for the text in the *to* property of the message.
+- **cc**: `String|null`
+  Looks for the text in the *cc* property of the message.
+- **bcc**: `String|null`
+  Looks for the text in the *bcc* property of the message.
+- **subject**: `String|null`
+  Looks for the text in the *subject* property of the message.
+- **body**: `String|null`
+  Looks for the text in the *textBody* or *htmlBody* property of the message.
+- **header**: `String[]|null`
+  The array MUST contain either one or two elements. The first element is the 
name of the header to match against. The second (optional) element is the text 
to look for in the header. If not supplied, the message matches simply if it 
*has* a header of the given name.
+
+If zero properties are specified on the FilterCondition, the condition MUST 
always evaluate to `true`. If multiple properties are specified, ALL must apply 
for the condition to be `true` (it is equivalent to splitting the object into 
one-property conditions and making them all the child of an AND filter 
operator).
+
+The exact semantics for matching `String` fields is **deliberately not 
defined** to allow for flexibility in indexing implementation, subject to the 
following:
+
+- Text SHOULD be matched in a case-insensitive manner.
+- Text contained in either (but matched) single or double quotes SHOULD be 
treated as a **phrase search**, that is a match is required for that exact 
sequence of words, excluding the surrounding quotation marks. Use `\"`, `\'` 
and `\\` to match a literal `"`, `'` and `\` respectively in a phrase.
+- Outside of a phrase, white-space SHOULD be treated as dividing separate 
tokens that may be searched for separately in the message, but MUST all be 
present for the message to match the filter.
+- Tokens MAY be matched on a whole-word basis using stemming (so for example a 
text search for `bus` would match "buses" but not "business").
+- When searching inside the *htmlBody* property, HTML tags and attributes 
SHOULD be ignored.
+
+#### Sorting
+
+The `sort` argument lists the properties to compare between two messages to 
determine which comes first in the sort. If two messages have an identical 
value for the first property, the next property will be considered and so on. 
If all properties are the same (this includes the case where an empty array or 
`null` is given as the argument), the sort order is server-dependent, but MUST 
be stable between calls to `getMessageList`.
+
+Optionally, following the property name there can be a space and then either 
the string `asc` or `desc` to specify ascending or descending sort for that 
property. If not specified, it MUST default to **descending**.
+
+The following properties MUST be supported for sorting:
+
+- **id** - The id as returned in the Message object.
+- **date** - The date as returned in the Message object.
+
+The following properties SHOULD be supported for sorting:
+
+- **size** - The size as returned in the Message object.
+- **from** – This is taken to be either the "name" part of the Emailer 
object, or if none then the "email" part of the Emailer object (see the 
definition of the from property in the Message object). If still none, consider 
the value to be the empty string.
+- **to** - This is taken to be either the "name" part of the **first** Emailer 
object, or if none then the "email" part of the **first** Emailer object (see 
the definition of the to property in the Message object). If still none, 
consider the value to be the empty string.
+- **subject** - This is taken to be the subject of the Message with any 
ignoring any leading "Fwd:"s or "Re:"s (case-insensitive match).
+- **threadIsFlagged** - This value MUST be considered `true` for the message 
if **any** of the messages in the same thread (regardless of mailbox) have 
`isFlagged: true`.
+- **threadIsUnread** - This value MUST be considered `true` for the message if 
**any** of the messages in the same thread (regardless of mailbox) have 
`isUnread: true`.
+- **isFlagged** - The `isFlagged` state of the message (only).
+- **isUnread** - The `isUnread` state of the message (only).
+
+The server MAY support sorting based on other properties as well. A client can 
discover which properties are supported by inspecting the *capabilities* 
property on the Account object.
+
+The method of comparison depends on the type of the property:
+
+- `String`: Comparison function is server-dependent. It SHOULD be 
case-insensitive and SHOULD take into account locale-specific conventions if 
known for the user. However, the server MAY choose to just sort based on 
unicode code point, after best-effort translation to lower-case.
+- `Date`: If sorting in ascending order, the earlier date MUST come first.
+- `Boolean`: If sorting in ascending order, a `false` value MUST come before a 
`true` value.
+
+#### Thread collapsing
+
+When `collapseThreads == true`, then after filtering and sorting the message 
list, the list is further winnowed by removing any messages for a thread id 
that has already been seen (when passing through the list sequentially). A 
thread will therefore only appear **once** in the `threadIds` list of the 
result, at the position of the first message in the list that belongs to the 
thread.
+
+#### Windowing
+
+If a *position* offset is supplied, then this is the 0-based index of the 
first result to return in the list of messages after filtering, sorting and 
collapsing threads. If the index is greater than or equal to the total number 
of messages in the list, then there are no results to return, but this DOES NOT 
generate an error. If *position* is `null` (or, equivalently, omitted) this 
MUST be interpreted as `position: 0`.
+
+Alternatively, a message id, called the **anchor** may be given. In this case, 
after filtering, sorting and collapsing threads, the anchor is searched for in 
the message list. If found, the **anchor offset** is then subtracted from this 
index. If the resulting index is now negative, it is clamped to 0. This index 
is now used exactly as though it were supplied as the `position` argument. If 
the anchor is not found, the call is rejected with an `anchorNotFound` error.
+
+If an *anchor* is specified, any position argument supplied by the client MUST 
be ignored. If *anchorOffset* is `null`, it defaults to `0`. If no *anchor* is 
supplied, any anchor offset argument MUST be ignored.
+
+#### Response
+
+The response to a call to *getMessageList* is called *messageList*. It has the 
following arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **filter**: `FilterCondition|FilterOperator|null`
+  The filter of the message list. Echoed back from the call.
+- **sort**: `String[]`
+  A list of Message property names used to sort by. Echoed back from the call.
+- **collapseThreads**: `Boolean`
+  Echoed back from the call.
+- **state**: `String`
+  A string encoding the current state on the server. This string will change 
if the results of the message list MAY have changed (for example, there has 
been a change to the state of the set of Messages; it does not guarantee that 
anything in the list has changed). It may be passed to *getMessageListUpdates* 
to efficiently get the set of changes from the previous state.
+
+  Should a client receive back a response with a different state string to a 
previous call, it MUST either throw away the currently cached list and fetch it 
again (note, this does not require fetching the messages again, just the list 
of ids) or, if the server supports it, call *getMessageListUpdates* to get the 
delta difference.
+- **canCalculateUpdates**: `Boolean`
+  This is `true` if the server supports calling `getMessageListUpdates` with 
these `filter`/`sort`/`collapseThreads` parameters. Note, this does not 
guarantee that the getMessageListUpdates call will succeed, as it may only be 
possible for a limited time afterwards due to server internal implementation 
details.
+- **position**: `Number`
+  The 0-based index of the first result in the `threadIds` array within the 
complete list.
+- **total**: `Number`
+  The total number of messages in the message list (given the *filter* and 
*collapseThreads* arguments).
+- **threadIds**: `String[]`
+  The list of Thread ids for each message in the list after filtering, sorting 
and collapsing threads, starting at the index given by the *position* argument 
of this response, and continuing until it hits the end of the list or reaches 
the `limit` number of ids.
+- **messageIds**: `String[]`
+  The list of Message ids for each message in the list after filtering, 
sorting and collapsing threads, starting at the index given by the *position* 
argument of this response, and continuing until it hits the end of the list or 
reaches the `limit` number of ids.
+
+The following errors may be returned instead of the `messageList` response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`unsupportedSort`: Returned if the *sort* includes a property the server does 
not support sorting on.
+
+`invalidArguments`: Returned if the request does not include one of the 
required arguments, or one of the arguments is of the wrong type, or otherwise 
invalid. A `description` property MAY be present on the response object to help 
debug with an explanation of what the problem was.
+
+`anchorNotFound`: Returned if an anchor argument was supplied, but it cannot 
be found in the message list.
+
+### getMessageListUpdates
+
+The `getMessageListUpdates` call allows a client to efficiently update the 
state of any cached message list to match the new state on the server. It takes 
the following arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If `null`, the primary account 
will be used.
+- **filter**: `FilterCondition|FilterOperator|null`
+  The filter argument that was used with *getMessageList*.
+- **sort**: `String[]|null`
+  The sort argument that was used with *getMessageList*.
+- **collapseThreads**: `Boolean|null`
+  The *collapseThreads* argument that was used with *getMessageList*.
+- **sinceState**: `String`
+  The current state of the client. This is the string that was returned as the 
*state* argument in the *messageList* response. The server will return the 
changes made since this state.
+- **uptoMessageId**: `String|null`
+  The message id of the last message in the list that the client knows about. 
In the common case of the client only having the first X ids cached, this 
allows the server to ignore changes further down the list the client doesn't 
care about.
+- **maxChanges**: `Number|null`
+  The maximum number of changes to return in the response. See below for a 
more detailed description.
+
+The response to *getMessageListUpdates* is called *messageListUpdates* It has 
the following arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **filter**: `FilterCondition|FilterOperator|null`
+  The filter of the message list. Echoed back from the call.
+- **sort**: `String[]|null`
+  A list of Message property names used to sort by. Echoed back from the call.
+- **collapseThreads**: `Boolean`
+  Echoed back from the call.
+- **oldState**: `String`
+  This is the `sinceState` argument echoed back; the state from which the 
server is returning changes.
+- **newState**: `String`
+  This is the state the client will be in after applying the set of changes to 
the old state.
+- **uptoMessageId**: `String|null`
+  Echoed back from the call.
+- **total**: `Number`
+  The total number of messages in the message list (given the filter and 
collapseThreads arguments).
+- **removed**: `RemovedItem[]`
+  The *messageId* and *threadId* for every message that was in the list in the 
old state and is not in the list in the new state. If the server cannot 
calculate this exactly, the server MAY return extra messages in addition that 
MAY have been in the old list but are not in the new list.
+
+  If an *uptoMessageId* was given AND this id was found in the list, only 
messages positioned before this message that were removed need be returned.
+
+  In addition, if the sort includes the property *isUnread* or *isFlagged*, 
the server MUST include all messages in the current list for which this 
property MAY have changed. If `collapseThreads == true`, then the server MUST 
include all messages in the current list for which this property MAY have 
changed **on any of the messages in the thread**.
+
+- **added**: `AddedItem[]`
+  The messageId and threadId and index in the list (in the new state) for 
every message that has been added to the list since the old state AND every 
message in the current list that was included in the *removed* array (due to a 
filter or sort based upon a mutable property). The array MUST be sorted in 
order of index, lowest index first.
+
+  If an *uptoMessageId* was given AND this id was found in the list, only 
messages positioned before this message that have been added need be returned.
+
+A **RemovedItem** object has the following properties:
+
+- **messageId**: `String`
+- **threadId**: `String`
+
+An **AddedItem** object has the following properties:
+
+- **messageId**: `String`
+- **threadId**: `String`
+- **index**: `Number`
+
+The result of this should be that if the client has a cached sparse array of 
message ids in the list in the old state:
+
+    messageIds = [ "id1", "id2", null, null, "id3", "id4", null, null, null ]
+
+then if it **splices out** all messages in the removed array:
+
+    removed = [{ messageId: "id2", … }];
+    messageIds => [ "id1", null, null, "id3", "id4", null, null, null ]
+
+and **splices in** (in order) all of the messages in the added array:
+
+    added = [{ messageId: "id5", index: 0, … }];
+    messageIds => [ "id5", "id1", null, null, "id3", "id4", null, null, null ]
+
+then the message list will now be in the new state.
+
+The following errors may be returned instead of the `messageListUpdates` 
response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoMail`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any mail data.
+
+`invalidArguments`: Returned if the request does not include one of the 
required arguments, or one of the arguments is of the wrong type, or otherwise 
invalid. A *description* property MAY be present on the response object to help 
debug with an explanation of what the problem was.
+
+`tooManyChanges`: Returned if there are more changes the the client's 
*maxChanges* argument. Each item in the removed or added array is considered as 
one change. The client may retry with a higher max changes or invalidate its 
cache of the message list.
+
+`cannotCalculateChanges`: Returned if the server cannot calculate the changes 
from the state string given by the client. Usually due to the client's state 
being too old. The client MUST invalidate its cache of the message list.

Added: james/project/trunk/server/protocols/jmap/doc/spec/push.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/doc/spec/push.mdwn?rev=1726757&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/doc/spec/push.mdwn (added)
+++ james/project/trunk/server/protocols/jmap/doc/spec/push.mdwn Tue Jan 26 
10:04:59 2016
@@ -0,0 +1,62 @@
+## Push
+
+Any modern email client should be able to update instantly whenever the data 
on the server is changed by another client or message delivery. Push 
notifications in JMAP occur out-of-band (i.e. not over the same connection as 
API exchanges) so that they can make use of efficient native push mechanisms on 
different platforms.
+
+The general model for push is simple and does not send any sensitive data over 
the push channel, making it suitable for use with less trusted 3rd party 
intermediaries. The format allows multiple changes to be coalesced into a 
single push update, and the frequency of pushes to be rate limited by the 
server. It doesn't matter if some push events are dropped before they reach the 
client; it will still get all changes next time it syncs.
+
+When something changes on the server, the server pushes a small JSON object to 
the client with the following property:
+
+- **changed**: `String[ChangedStates]`
+  A map of *account id* to an object encoding the state of data types which 
have changed for that account since the last push event, for each of the 
accounts to which the user has access and for which something has changed.
+
+A **ChangedStates** object is a map of the type name (e.g. "Mailbox" or 
"Message") to the current state token for that type (i.e. the "state" property 
that would currently be returned by a call to "getMailboxes" or "getMessages", 
as appropriate). The types in JMAP are "Mailbox", "Thread", "Message", 
"ContactGroup", "Contact", "Calendar", "CalendarEvent".
+
+Upon receiving this data, the client can compare the new state strings with 
its current values to see whether it has the current data for these types. The 
actual changes can then be efficiently fetched in a single standard API request 
(using the *getFooUpdates* type methods).
+
+### Event Source
+
+There are two mechanisms by which the client can receive the push events. The 
first is directly via a `text/event-stream` resource, as described in
+<http://www.w3.org/TR/eventsource/>. This is essentially a long running HTTP 
request down which the server can push data. When a change occurs, the server 
MUST push an event called **state** to any connected clients.
+
+The server MAY also set a new `Last-Event-Id` that encodes the entire server 
state visible to the user. When a new connection is made to the event-source 
endpoint, the server can then work out whether the client has missed some 
changes which it should send immediately.
+
+The server MUST also send an event called **ping** with an empty object as the 
data if a maximum of 5 minutes has elapsed since the previous event. This MUST 
NOT set a new `Last-Event-Id`. A client may detect the absence of these to 
determine that the HTTP connection has been dropped somewhere along the route 
and so it needs to re-establish the connection.
+
+Refer to the Authentication section of this spec for details on how to get the 
URL for the event-source endpoint. The request must be authenticated using an 
`Authorization` header like any HTTP request.
+
+A client MAY hold open multiple connections to the event-source, although it 
SHOULD try to use a single connection for efficiency.
+
+### setPushCallback
+
+The second push mechanism is to register a callback URL to which the JMAP 
server will make an HTTPS POST request whenever the event occurs. The request 
MUST have a content type of `application/json` and contain the same UTF-8 JSON 
encoded object as described above as the body.
+
+The JMAP server MUST also set the following headers in the POST request:
+- `X-JMAP-EventType: state`
+- `X-JMAP-User: ${username}` where `${username}` is the username of the 
authenticated user for which the push event occurred.
+
+The JMAP server MUST follow any redirects. If the final response code from the 
server is `2xx`, the callback is considered a success. If the response code is 
`503` (Service Unavailable), the JMAP server MAY try again later (but may also 
just drop the event). If the response code is `429` (Too Many Requests) the 
JMAP server SHOULD attempt to reduce the frequency of pushes to that URL. Any 
other response code SHOULD be considered a **permanent failure** and the 
callback should be deregistered (not tried again even for future events unless 
explicitly re-registered by the client).
+
+The URL set by the client MUST use the HTTPS protocol and SHOULD encode within 
it a unique token that can be verified by the server to know that the request 
comes from the JMAP server the authenticated client connected to.
+
+The callback is tied to the access token used to create it. Should the access 
token expire or be revoked, the callback MUST be removed by the JMAP server. 
The client MUST re-register the callback after reauthenticating to resume 
callbacks.
+
+Each session may only have a single callback URL registered. To set it, make a 
call to *setPushCallback*. It takes the following argument:
+
+- **callback**: `String|null`
+  The (HTTPS) URL the JMAP server should POST events to. This will replace any 
previously set URL. Set to `null` to just remove any previously set callback 
URL.
+
+The response to *setPushCallback* is called *pushCallbackSet*. It has the 
following argument:
+
+- **callback**: `String|null`
+  Echoed back from the call.
+
+The following error may be returned instead of the *mailboxesSet* response:
+
+`invalidUrl`: Returned if the URL does not begin with `https://`, or is 
otherwise syntactically invalid or does not resolve.
+
+### getPushCallback
+
+To check the currently set callback URL (if any), make a call to 
*getPushCallback*. It does not take any arguments. The response to 
*getPushCallback* is called `pushCallback`. It has a single argument:
+
+- **callback**: `String|null`
+  The URL the JMAP server is currently posting push events to, or `null` if 
none.

Added: james/project/trunk/server/protocols/jmap/doc/spec/searchsnippet.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/doc/spec/searchsnippet.mdwn?rev=1726757&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/doc/spec/searchsnippet.mdwn 
(added)
+++ james/project/trunk/server/protocols/jmap/doc/spec/searchsnippet.mdwn Tue 
Jan 26 10:04:59 2016
@@ -0,0 +1,41 @@
+## SearchSnippets
+
+When doing a search on a `String` property, the client may wish to show the 
relevant section of the body that matches the search as a preview instead of 
the beginning of the message, and to highlight any matching terms in both this 
and the subject of the message. Search snippets represent this data.
+
+A **SearchSnippet** object has the following properties:
+
+- **messageId**: `String`
+  The message id the snippet applies to.
+- **subject**: `String|null`
+  If text from the filter matches the subject, this is the subject of the 
message HTML-escaped, with matching words/phrases wrapped in `<mark></mark>` 
tags. If it does not match, this is `null`.
+- **preview**: `String|null`
+  If text from the filter matches the plain-text or HTML body, this is the 
relevant section of the body (converted to plain text if originally HTML), 
HTML-escaped, with matching words/phrases wrapped in `<mark></mark>` tags, up 
to 256 characters long. If it does not match, this is `null`.
+
+It is server-defined what is a relevant section of the body for preview. If 
the server is unable to determine search snippets, it MUST just return `null` 
for both the *subject* and *preview* properties.
+
+Note, unlike most data types, a SearchSnippet DOES NOT have a property called 
`id`.
+
+### getSearchSnippets
+
+To fetch search snippets, make a call to `getSearchSnippets`. It takes the 
following arguments:
+
+- **accountId**: `String|null`
+  The id of the account to use for this call. If `null`, defaults to the 
primary account.
+- **messageIds**: `String[]`
+  The list of ids of messages to fetch the snippets for.
+- **filter**: `FilterCondition|FilterOperator|null`
+  The same filter as passed to getMessageList; see the description of this 
method for details.
+
+The response to `getSearchSnippets` is called `searchSnippets`. It has the 
following arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **filter**: `FilterOperator`
+  Echoed back from the call.
+- **list**: `SearchSnippet[]`
+  An array of SearchSnippets objects for the requested message ids. This may 
not be in the same order as the ids that were in the request.
+- **notFound**: `String[]|null`
+  An array of message ids requested which could not be found, or `null` if all
+  ids were found.
+
+Since snippets are only based on immutable properties, there is no state 
string or update mechanism needed.



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org
For additional commands, e-mail: server-dev-h...@james.apache.org

Reply via email to