Added: james/project/trunk/spec/apimodel.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/spec/apimodel.mdwn?rev=1726745&view=auto
==============================================================================
--- james/project/trunk/spec/apimodel.mdwn (added)
+++ james/project/trunk/spec/apimodel.mdwn Tue Jan 26 07:45:37 2016
@@ -0,0 +1,259 @@
+# JMAP: The Spec
+
+<div id="last-update">Last updated 2016-01-15</div>
+
+This is a specification. It is meant to be readable but it also has to be 
comprehensive, so it can be dense in places. If you want to get a quick idea of 
how JMAP works, you should probably read the [guide for client 
developers](client.html) first. This has lots of example exchanges and should 
give you a good feel for what JMAP is all about. The spec is heavier going; it 
attempts to document exactly what each method should do, and what should happen 
in a myriad of edge cases.
+
+There are undoubtably edge cases that are not yet covered. If you find one, 
please email <edi...@jmap.io> or make a pull request on GitHub if you have a 
proposed fix.
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", 
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be 
interpreted as described in [RFC2119](https://tools.ietf.org/html/rfc2119).
+
+## The JSON API model
+
+JSON is a text-based data interchange format as specified in 
[RFC7159](https://tools.ietf.org/html/rfc7159). The I-JSON format defined in 
[RFC7493](https://tools.ietf.org/html/rfc7493) is a strict subset of this, 
adding restrictions to avoid potentially confusing scenarios (for example, it 
mandates that an object MUST NOT have two properties with the same key). All 
data sent from the client to the server or from the server to the client MUST 
be valid I-JSON according to the RFC, encoded in UTF-8.
+
+### The structure of an exchange
+
+The client initiates an API request by sending the server a JSON array. Each 
element in this array is another array representing a method invocation on the 
server. The server will process the method calls and return a response 
consisting of an array in the same format. Each method call always contains 
three elements:
+
+1. The **name** of the method to call, or the name of the response from the 
server. This is a `String`.
+2. An `Object` containing *named* **arguments** for that method or response.
+3. A **client id**: an arbitrary `String` to be echoed back with the responses 
emitted by that method call (as we'll see lower down, a method may return 1 or 
more responses, as some methods make implicit calls to other ones).
+
+Example query:
+
+    [
+      ["method1", {"arg1": "arg1data", "arg2": "arg2data"}, "#1"],
+      ["method2", {"arg1": "arg1data"}, "#2"],
+      ["method3", {}, "#3"]
+    ]
+
+The method calls MUST be processed sequentially, in order. Each API request
+(which, as shown, may contain multiple method calls) receives a JSON
+response in exactly the same format. The output of the methods MUST be added
+to the array in the same order as the methods are processed.
+
+Example response:
+
+    [
+      ["responseFromMethod1", {"arg1": 3, "arg2": "foo"}, "#1"],
+      ["responseFromMethod2", {"isBlah": true}, "#2"],
+      ["anotherResponseFromMethod2", {"data": 10, "yetmoredata": "Hello"}, 
"#2"],
+      ["aResponseFromMethod3", {}, "#3"]
+    ]
+
+### Errors
+
+If the data sent as an API request is not valid JSON or does not match the 
structure above, an error will be returned at the transport level. For example, 
when using JMAP over HTTP, a `400 Bad Request` error will be returned at the 
HTTP level.
+
+Possible errors for each method are specified in the method descriptions. If a 
method encounters an error, an `error` response must be inserted at the current 
point in the output array and, unless otherwise specified, no further 
processing must happen within that method.
+
+Any further method calls in the request MUST then be processed as normal.
+
+An `error` response looks like this:
+
+    ["error", {
+      type: "unknownMethod"
+    }, "client-id"]
+
+The response name is `error`, and it has a type property as specified in the 
method description. Other properties may be present with further information; 
these are detailed in the method descriptions where appropriate.
+
+If an unknown method is called, an `unknownMethod` error (this is the type 
shown in the example above) MUST be inserted and then the next method call MUST 
be processed as normal.
+
+If an unknown argument or invalid arguments (wrong type, or in violation of 
other specified constraints) are supplied to a method, an `invalidArguments` 
error MUST be inserted and then the next method call MUST be processed as 
normal.
+
+### Vendor-specific extensions
+
+Individual services will have custom features they wish to expose over JMAP. 
This may take the form of extra datatypes and/or methods not in the spec, or 
extra arguments to JMAP methods, or extra properties on existing data types 
(which may also appear in arguments to methods that take property names). To 
ensure compatibility with clients that don't know about a specific custom 
extension, and for compatibility with future versions of JMAP, the server MUST 
ONLY expose these extensions if the client explicitly opts in. Without opt-in, 
the server MUST just follow the spec and reject anything that does not conform 
to it as specified.
+
+Any vendor extensions supported by the server are advertised to the client in 
the capabilities property on the Account object. The client opt-in happens at 
the transport layer (see the next section).
+
+### JMAP over HTTPS
+
+To make an API request over HTTP (support for other protocols may be added in 
future extensions to the spec), the client makes an authenticated POST request 
to the API URL; see the Authentication section of the spec for how to discover 
this URL and how to authenticate requests.
+
+The request MUST have a content type of `application/json` and be encoded in 
utf-8.
+
+The request MAY include an `X-JMAP-Version` header, the value of which is a 
number for the spec version the client would like to use (the list of versions 
supported by each account can be discovered by the client by inspecting the 
capabilities property of the Account object). If omitted, the server MUST 
presume a value equal to the *lowest* supported version common to all accounts.
+
+The request MAY include an 'X-JMAP-Extensions' header, the value of which is a 
comma-separated list of 'vendor-extension-name:extension-version` the client 
would like to use. For example, 
"com.fastmail.message:1,com.fastmail.savedSearch:4". Any white-space should be 
ignored when evaluating this header.
+
+The server will respond with one of the following HTTP response codes:
+
+#### `200`: OK
+
+The API request was successful. The response will be of type 
`application/json` and consists of the response to the API calls, as described 
above.
+
+#### `400`: Bad Request
+
+The request was malformed. For example, it may have had the wrong content 
type, or have had a JSON object that did not conform to the API calling 
structure (see *The structure of an exchange* above). The client SHOULD NOT 
retry the same request.
+
+#### `401`: Unauthorized
+
+The `Authorization` header was missing or did not contain a valid token. 
Reauthenticate and then retry the request. There is no content in the response.
+
+#### `404`: Not Found
+
+The API endpoint has moved. See the Authentication section of the spec for how 
to rediscover the current URL to use. There is no content in the response.
+
+#### `412`: Precondition Failed
+
+This means either the JMAP version specified in an `X-JMAP-Version` header, or 
an extension/version specified in an `X-JMAP-Extensions` header is not 
supported by one of the accounts used in a method.
+
+After authentication, but before processing any methods, the server MUST scan 
the methods in the request and check the requested JMAP version and extensions 
are supported by all the accounts referenced by the methods (unknown account 
ids MUST just be skipped; these method calls will error out when processed). If 
there are any accounts that do not support the requested version and 
extensions, this HTTP error code is returned and the server MUST NOT process 
any of the methods.
+
+#### `500`: Internal Server Error
+
+Something has gone wrong internally, and the server is in a broken state. 
Don't automatically retry. There is no content in the response.
+
+#### `503`: Service Unavailable
+
+The server is currently down. Try again later with exponential backoff. There 
is no content in the response.
+
+### Security
+
+As always, the server must be strict about data received from the client. 
Arguments need to be checked for validity; a malicious user could attempt to 
find an exploit through the API. In case of invalid arguments 
(unknown/insufficient/wrong type for data etc.) the method should return an 
`invalidArguments` error and terminate.
+
+### Concurrency
+
+To ensure the client always sees a consistent view of the data, the state 
accessed by a method call MUST NOT change during the execution of the method, 
except due to actions by the method call itself. The state MAY change 
in-between method calls (even within a single API request).
+
+### The Number datatype
+
+The JSON datatypes are limited to those found in JavaScript. A `Number` in 
JavaScript is represented as a signed double (64-bit floating point). However, 
except where explicitly specified, all numbers used in this API are unsigned 
integers <= 2^53 (the maximum integer that may be reliably stored in a double). 
This implicitly limits the maximum length of message lists in queries and the 
like.
+
+### The Date datatypes
+
+Where the API specifies `Date` as a type, it means a string in 
[RFC3339](https://tools.ietf.org/html/rfc3339) *date-time* format, with the 
*time-offset* component always `Z` (i.e. the date-time MUST be in UTC time) and 
*time-secfrac* always omitted. The "T" and "Z" MUST always be upper-case. For 
example, `"2014-10-30T14:12:00Z"`.
+
+Where the API specifies `LocalDate` as a type, it means a string in the same 
format as `Date`, but with the `Z` omitted from the end. This only occurs in 
relation to calendar events. The interpretation in absolute time depends upon 
the time zone for the event, which MAY not be a fixed offset (for example when 
daylight saving time occurs).
+
+### Use of `null`
+
+Unless otherwise specified, a missing property in a request, response or 
object MUST be intepreted exactly the same as that property having the value 
`null`. If `null` is not a valid value for that property this would typically 
cause an error to occur. This rule does not apply to the [top-level 
datatypes](#data-model-overview), where a missing property usually indicates 
that the sender wants to leave the existing property value untouched (e.g. in a 
[*setFoos*](#setfoos) request or a [*getFooUpdates*](#getfooupdates) response).
+
+### CRUD methods
+
+JMAP defines various types of objects and provides a uniform interface for 
creating, retrieving, updating and deleting them. A **data type** is a 
collection of named, typed properties, just like the schema for a database 
table. Each row of the table is a **record**. For a `Foo` data type, records of 
that type would be fetched via a `getFoos` call and modified via a `setFoos` 
call. Delta updates may be fetched via a `getFooUpdates` call. These methods 
all follow a standard format as described below.
+
+### getFoos
+
+Objects of type **Foo** are fetched via a call to *getFoos*. Methods with a 
name starting with `get` MUST NOT alter state on the server.
+
+This method may take some or all of the following arguments. The getter for a 
particular data type may not implement all of the arguments (for example if the 
data type only has two properties, there is little point in being able to just 
return one of them etc.); see the docs of the type in question. However, if one 
of the following arguments is available, it will behave exactly as specified 
below.
+
+- **ids**: `String[]|null`
+  The ids of the Foo objects to return. If `null` then **all** records of the 
data type are returned.
+- **properties**: `String[]|null`
+  If supplied, only the properties listed in the array are returned for each 
Foo object. If `null`, all properties of the object are returned. The id of the 
object is **always** returned, even if not explicitly requested.
+- **sinceState**: `String|null`
+  The *state* argument from a *foos* response may be passed back to future 
*getFoos* calls as the *sinceState* argument. If the current state is the same, 
the server SHOULD skip fetching the records and return a result indicating 
there is no change (this is essentially like an ETag). Most types support the 
more sophisticated *getFooUpdates* call instead to allow for delta updates. 
However, for small collections of data that change infrequently, this might be 
used. If available, this argument is always optional.
+
+The response to `getFoos` is called `foos`. It has the following arguments:
+
+- **state**: `String`
+  A string representing the state on the server for **all** the data of this 
type. If the data changes, this string will change. It is used to get delta 
updates, if supported for the type.
+- **list**: `Foo[]|null`
+  An array of the Foo objects requested. This is the **empty array** if no 
objects were requested (and *ids* argument was passed with an empty array), or 
none were found (the user has no objects of this type, or none of the ids given 
were valid). If *sinceState* was supplied and it is identical to the current 
state, this property is `null` (the client already has up to date data so the 
server may skip returning it).
+- **notFound**: `String[]|null`
+  This array contains the ids passed to the method for records 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 error may be returned instead of the `foos` response:
+
+`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.
+
+### getFooUpdates
+
+When the state of the set of Foo records changes on the server (whether due to 
creation, updates or deletion), the *state* property of the *foos* response 
will change. The *getFooUpdates* call allows a client to efficiently update the 
state of any its Foo cache to match the new state on the server. It takes the 
following arguments:
+
+- **sinceState**: `String`
+  The current state of the client. This is the string that was returned as the 
*state* argument in the *foos* response. The server will return the changes 
made since this state.
+- **maxChanges**: `Number|null`
+  The maximum number of Foo ids 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 the *fooUpdates* response, the server will make 
an implicit call to *getFoos* with the *changed* property of the response as 
the *ids* argument. If `false` or `null`, no implicit call will be made.
+- **fetchRecordProperties**: `String[]|null`
+  If the *getFoos* method takes a *properties* argument, this argument is 
passed through on implicit calls (see the *fetchRecords* argument).
+
+The response to *getFooUpdates* is called *fooUpdates*. It has the following 
arguments:
+
+- **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 *getFooUpdates* again with the *newState* 
returned to get further updates. If `false`, *newState* is the current server 
state.
+- **changed**: `String[]`
+  An array of Foo ids for records which have been created or changed but not 
destroyed since the oldState.
+- **removed**: `String[]`
+  An array of Foo ids for records which have been destroyed since the old 
state.
+
+The *maxChanges* argument (and *hasMoreUpdates* response argument) is 
available for data types with potentially large amounts of data (i.e. those for 
which there is a *getFooList* method available for loading the data in pages).  
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 *getFooUpdates* 
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 Foo record 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 Foo record has been created AND deleted since 
the oldState, the server should remove the Foo id from the response entirely, 
but MAY include it in the *removed* response, and optionally the *changed* 
response as well.
+
+The following errors may be returned instead of the *fooUpdates* response:
+
+`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 Foo cache. The error object MUST also include a `newState: String` property 
with the current state for the type.
+
+### setFoos
+
+Modifying the state of Foo objects on the server is done via the *setFoos* 
method. This encompasses creating, updating and destroying Foo records. This 
has two benefits:
+
+1. It allows the server to sort out ordering and dependencies that may exist 
if doing multiple operations at once (for example to ensure there is always a 
minimum number of a certain record type).
+2. Only a single call is required to make all changes to a particular type, so 
the *ifInState* requirement will not be invalidated by a previous method call 
in the same request.
+
+The *setFoos* method takes the following arguments:
+
+- **ifInState**: `String|null`
+  This is a state string as returned by the *getFoos* method. If supplied, the 
string must match the current state, otherwise the method will be aborted and a 
`stateMismatch` error returned. If `null`, the change will be applied to the 
current state.
+- **create**: `String[Foo]|null`
+  A map of *creation id* (an arbitrary string set by the client) to Foo 
objects (containing all properties except the id, unless otherwise stated in 
the specific documentation of the data type). If `null`, no objects will be 
created.
+- **update**: `String[Foo]|null`
+  A map of id to a Foo object. The object may omit any property; only 
properties that have changed need be included. If `null`, no objects will be 
updated.
+- **destroy**: `String[]|null`
+  A list of ids for Foo objects to permanently delete. If `null`, no objects 
will be deleted.
+
+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 a 
*name* property but not a *count* property, 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.
+
+If an id given cannot be found, the update or destroy MUST be rejected with a 
`notFound` set error.
+
+Some record objects may hold references to others (foreign keys). When records 
are created or modified, they may reference other records being created *in the 
same API request* by using the creation id prefixed with a `#`. The order of 
the method calls in the request by the client MUST be such that the record 
being referenced is created in the same or an earlier call. The server thus 
never has to look ahead. Instead, while processing a request (a series of 
method calls), the server MUST keep a simple map for the duration of the 
request of creation id to record id for each newly created record, so it can 
substitute in the correct value if necessary in later method calls.
+
+The response to *setFoos* is called *foosSet*. It has the following arguments:
+
+- **oldState**: `String|null`
+  The state string that would have been returned by *getFoos* 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 *getFoos*.
+- **created**: `String[Foo]`
+  A map of the creation id to an object containing any **server-assigned** 
properties of the Foo object (including the id) for all successfully created 
records.
+- **updated**: `String[]`
+  A list of Foo ids for records that were successfully updated.
+- **destroyed**: `String[]`
+  A list of Foo ids for records that were successfully destroyed.
+- **notCreated**: `String[SetError]`
+  A map of creation id to a SetError object for each record that failed to be 
created. The possible errors are defined in the description of the method for 
specific data types.
+- **notUpdated**: `String[SetError]`
+  A map of Foo id to a SetError object for each record that failed to be 
updated. The possible errors are defined in the description of the method for 
specific data types.
+- **notDestroyed**: `String[SetError]`
+  A map of Foo id to a SetError object for each record that failed to be 
destroyed. The possible errors are defined in the description of the method for 
specific data types.
+
+A **SetError** object has the following properties:
+
+- **type**: `String`
+  The type of error.
+- **description**: `String|null`
+  A description of the error to display to the user.
+
+Other properties may also be present on the object, as described in the 
relevant methods.
+
+The following errors may be returned instead of the `foosSet` response:
+
+`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.

Added: james/project/trunk/spec/authentication.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/spec/authentication.mdwn?rev=1726745&view=auto
==============================================================================
--- james/project/trunk/spec/authentication.mdwn (added)
+++ james/project/trunk/spec/authentication.mdwn Tue Jan 26 07:45:37 2016
@@ -0,0 +1,171 @@
+## Authentication
+
+Before connecting to any JMAP service, the client must first gain an access 
token. It cannot just use a username/password directly. This allows the server 
to know (and show the user) which clients currently have access to the account, 
and to be able to revoke access individually.
+
+The server may support multiple different mechanisms for authenticating a user 
to gain the access token. It is expected that further types may be added in 
future extensions to the JMAP specification (for example 
[FIDO](https://fidoalliance.org/)).
+
+### Service autodiscovery
+
+There are three common autodiscovery methods in use for internet protocols:
+
+- **DNS srv**
+  See [RFC6186](https://tools.ietf.org/html/rfc6186) and 
[RFC6764](https://tools.ietf.org/html/rfc6764)
+- **.well-known/`servicename`**
+  See [RFC5785](https://tools.ietf.org/html/rfc5785)
+- **autoconfig.example.com**/**autodiscover.example.com**
+  Used by 
[Thunderbird](https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration/FileFormat/HowTo)
 and Outlook.
+
+A JMAP-supporting email host for the domain `example.com` SHOULD publish a SRV 
record `_jmaps._tcp.example.com` which gives a hostname and port (usually port 
`443`).
+
+The authentication URL is `https://hostname/.well-known/jmap` (following any 
redirects).
+
+Other autodiscovery options using `autoconfig.example.com` or 
`autodiscover.example.com` may be added to a future version of JMAP to support 
clients which can't use SRV lookup.
+
+### Getting an access token
+
+Authorization always starts with the client making a POST request to the 
authentication URL (found either via service autodiscovery or manual entry). 
The request MUST be of type `application/json` and specify an `Accept: 
application/json` header. The body of the request MUST be a single JSON object, 
encoded in UTF-8, with the following properties:
+
+- **username**: `String`
+  The username the client wishes to authenticate. This is normally the primary 
email address of the user.
+- **clientName**: `String`
+  The name of the client software. e.g. `Mozilla Thunderbird`.
+- **clientVersion**: `String`
+  Information to identify the version of the client.  This MUST change for any 
changed client code (e.g. a version control tag or counter for development 
software) and SHOULD sort lexically later for newer versions.
+- **deviceName**: `String`
+  A human-friendly string to identify the device making the request, e.g. "Joe 
Blogg's iPhone".
+
+The server may use the client/device information to help identify the login to 
the user in a login log or other security reporting. Although hopefully 
unnecessary, they may also be helpful for working around client bugs in the 
future.
+
+The server will respond with one of the following HTTP status codes:
+
+#### `200`: Success, but more authorization required.
+
+The response body will be a single JSON object with the following properties.
+
+- **continuationToken**: `String`
+  A token from the server to allow it to connect the next request with 
previous requests in the login process. This SHOULD be of limited time validity 
(e.g. 15 minutes from previous call).
+- **methods**: `String[]`
+  A list of the supported authentication methods to continue with 
authentication. This will often have only have one item in it. Allowed values 
are `"password"`, `"external"` and `"oauth"`. More options may be added in 
future extensions to JMAP.
+- **prompt**: `String|null`
+  A message to display in the client to the user.
+
+This is the standard response to an initial request. Note, a server may return 
this even if the username is not actually active, to prevent enumeration. The 
client should then pick one of the *methods* from the list in the response to 
continue with authentication (if no methods supported by the client are in the 
list, it will not be able to log in to this account):
+
+- `"password"`: the client should prompt the user for a password. If the 
`prompt` property is non-null, this message should be displayed to the user to 
explain what input is required. The prompt MUST be treated as plain text, but 
the client SHOULD automatically hyperlink any URLs it finds in the text if a 
system browser is available. If `prompt == null`, the client SHOULD just show a 
generic password prompt message.
+- `"external"`: the user must do something out-of-band to authorize the app. 
The server SHOULD return a prompt string to display to the user to tell them 
what they need to do. Again, the client MUST treat the prompt as plain text, 
but SHOULD automatically hyperlink any URLs it finds if a system browser is 
available. The client MUST also offer a continue button (or similar) for the 
user to indicate to the client when they have completed the out-of-band 
authentication.
+- `"oauth"`: OAuth based authentication. For OAuth integration, see the docs 
of the service in question, since every service implements it slightly 
differently and the client must register with the service beforehand to use it. 
If using this method, an access token is obtained entirely through the OAuth 
mechanism. See the "Refetching URL endpoints" section below for how to obtain 
the URL endpoints after successfully authenticating using OAuth.
+
+If using `"password"` or `"external"`, the user will at some point indicate to 
the client to continue authentication. At this point the client submits a POST 
request to the same URL as before, with the body being a single JSON object 
with the following properties:
+
+- **token**: `String`
+  The *continuationToken* the server sent from the previous request.
+- **method**: `String`
+  The method chosen to continue authentication.
+- **password**: `String` (optional)
+  The password entered by the user (if `method == password`). The client 
SHOULD NOT store the password the user entered beyond what is required to 
submit it to the server in this step.
+
+The server will then return one of the same set of responses as before, which 
should be handled the same (for example, if 2FA is required, a `200` response 
may be returned again and a second password prompted for, with a `prompt` text 
explaining to the user that a one-time password has been SMSed to them).
+
+#### `201`: Authentication is complete, access token created.
+
+The response body will be a single JSON object with the following properties.
+
+- **versions**: `Number[]`
+  The list of supported JMAP-spec versions. If the client does not opt in to a 
particular version, then the lowest/oldest version in the list will be used by 
the server.
+- **extensions**: `String[Number[]]`
+  As described in the API Model, specific vendors may need to add custom 
extensions to the JMAP protocol, including new datatypes, extra properties on 
existing datatypes and extra methods. To ensure compability, these extensions 
must be explicitly opted into by the client.
+
+  The *extensions* property is a map of a name that uniquely identifies the 
extension (including a unique string identifying the vendor) to a list of 
version numbers supported for that extension. e.g. `"com.fastmail.message": [ 1 
]`.
+
+  A client may opt in to an extension by sending 
`"extension-name:extension-version"` in the `X-JMAP-Extensions` header of the 
request (or as specified for any future non-HTTP transport). It is up to the 
vendor to document exactly what the extension provides.
+
+  It is expected that this mechanism is mainly used for custom clients by the 
vendor for their own server. Where features are added that are more universally 
useful, it is hoped they will quickly be standardised in a future version of 
this spec, to maintain open compatibility between vendors.
+- **accessToken**: `String`
+  The secret token to be used by the client to authenticate all future JMAP 
requests. The client should keep this secure, preferably in an OS keychain or 
the like. Since tokens should not be reused across devices or clients, the 
client SHOULD NOT reveal this token to the user.
+- **api**: `String`
+  The URL to use for JMAP API requests.
+- **eventSource**: `String`
+  The URL to connect to for push events (see the Push section of this spec).
+- **upload**: `String`
+  The URL endpoint to use when uploading files (see the Upload section of this 
spec).
+- **download**: `String`
+  The URL endpoint to use when downloading files, in [RFC6570 URI 
Template](https://tools.ietf.org/html/rfc6570) (level 1) format. The URL MUST 
contain a variable called `blobId`. The URL SHOULD contain a variable called 
`name`. The client may use this template in combination with a blobId to 
download any binary data (files) referenced by other objects. Since a blob is 
not associated with a particular name, the template SHOULD allow a name to be 
substituted in as well; the server will return this as the filename if it sets 
a `Content-Disposition` header. To download the data the client MUST make an 
authenticated GET request (see below for how to authenticate requests) to the 
expanded URL, and then follow any redirects.
+
+URLs are returned only after logging in. This allows different URLs to be used 
for users located in different geographic datacentres within the same service.
+
+Note, if authentication is done via IP or mobile subscriber ID or some similar 
mechanism, a `201` response MAY be returned in response to the initial request 
(with just the username and client info).
+
+#### `400`: Malformed request
+
+The request is of the wrong content type, or does not contain data in the 
expected format. The client MUST NOT retry the same request.
+
+#### `401`: Authentication step failed
+
+Returned in response to a continuation request which failed (e.g. the password 
entered was not correct, or the out-of-band step was not completed 
successfully). The response body will be a single JSON object with the same 
properties as the `200` response.
+
+#### `403`: Restart authentication
+
+This normally means the continuation token has expired, but it can be returned 
for any other reason. The client MUST restart authentication (go back to 
sending the username and client info to the server).
+
+#### `429`: Rate limited
+
+Returned if the server is temporarily blocking this IP/client from 
authenticating. This may be due to too many failed password attempts, or 
detected username enumeration attempts, or any other reason. (Legitimate) 
clients should wait a while then try again.
+
+### Refetching URL endpoints
+
+A server MAY (although SHOULD NOT) move end points for any services other than 
authentication at any time. If a request to the API/file upload/event source 
endpoint returns a `404`, the client MUST refetch the URL endpoints. To do 
this, it should make an authenticated GET request to the authentication URL 
(see below for how to authenticate requests).
+
+For OAuth logins, this is how the URLs may be fetched initially as well.
+
+The server MUST respond with one of the following status codes:
+
+#### `200`: OK
+
+The request was successful. The response will be of type `application/json` 
and consists of a single JSON object containing the following properties:
+
+- **versions**: `Number[]`
+  The list of JMAP-spec versions supported by the account.
+- **extensions**: `String[Number[]]`
+  Map of extension names to version number of that extension (see above).
+- **api**: `String`
+  The URL to use for JMAP API requests.
+- **eventSource**: `String`
+  The URL to connect to for push events (see the Push section of this spec).
+- **upload**: `String`
+  The URL endpoint to use when uploading files (see the Upload section of this 
spec).
+- **download**: `String`
+  The URL endpoint to use when downloading files (see above).
+
+#### `401`: Unauthorized
+
+The `Authorization` header was missing or did not contain a valid token. 
Reauthenticate and then retry the request. There is no content in the response.
+
+#### `404`: Not Found
+
+The JMAP server is no longer here. There is no content in the response.
+
+#### `500`: Internal Server Error
+
+Something has gone wrong internally, and the server is in a broken state. 
Don't automatically retry. There is no content in the response.
+
+#### `503`: Service Unavailable
+
+The server is currently down. Try again later with exponential backoff. There 
is no content in the response.
+
+### Revoking an access token
+
+The validity of an access token is determined by the server. It may be valid 
for a limited time only, or expire after a certain time of inactivity, or be 
valid indefinitely etc. If an access token expires, it MUST NOT be resurrected. 
The client MUST restart the authentication process to get a new access token.
+
+A client may revoke an access token at any time by making a DELETE HTTP 
request to the authentication URL (used to get the token in the first place), 
with the correct `Authorization` header (see below). The response from the 
server will be one of the following:
+
+`204`: Success (the access token has now been revoked).
+
+`401`: Failed due to due to no `Authorization` header, or `Authorization` 
header is not a valid access token.
+
+No content is returned in either case.
+
+For OAuth, see the provider's documentation on revoking access tokens.
+
+### Authenticating HTTP requests
+
+All HTTP requests other than to the authentication URL must be authenticated. 
To do this, the client MUST add an `Authorization` header to each request with 
the value being the access token. This applies to OAuth tokens too, but see the 
provider's documentation for the details of what value to use for the header.

Added: james/project/trunk/spec/calendar.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/spec/calendar.mdwn?rev=1726745&view=auto
==============================================================================
--- james/project/trunk/spec/calendar.mdwn (added)
+++ james/project/trunk/spec/calendar.mdwn Tue Jan 26 07:45:37 2016
@@ -0,0 +1,192 @@
+## Calendars
+
+A Calendar is a named collection of events. All events are associated with 
one, and only one, calendar.
+
+A **Calendar** object has the following properties:
+
+- **id**: `String`
+  The id of the calendar. This property is immutable.
+- **name**: `String`
+  The user-visible name of the calendar. This may be any UTF-8 string of at 
least 1 character in length and maximum 256 bytes in size.
+- **color**: `String`
+  Any valid CSS color value. The color to be used when displaying events 
associated with the calendar. The color SHOULD have sufficient contrast to be 
used as text on a white background.
+- **sortOrder**: `Number`
+  Defines the sort order of calendars 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 calendar with a lower order should be displayed before a calendar with
+  a higher order in any list of calendars in the client's UI. Calendars with
+  equal order should be sorted in alphabetical order by name. The sorting
+  should take into locale-specific character order convention.
+- **isVisible**: `Boolean`
+  Should the calendar's events be displayed to the user at the moment?
+- **mayReadFreeBusy**: `Boolean`
+  The user may read the free-busy information for this calendar. In JMAP
+  terms, this means the user may use this calendar as part of a filter in a
+  *getCalendarEventList* call, however unless `mayRead == true`, the events
+  returned for this calendar will only contain free-busy information, and be 
stripped of any other data.
+  This property MUST be `true` if *mayRead* is `true`.
+- **mayReadItems**: `Boolean`
+  The user may fetch the events in this calendar. In JMAP terms, this means
+  the user may use this calendar as part of a filter in a
+  *getCalendarEventList* call
+- **mayAddItems**: `Boolean`
+  The user may add events to this calendar. In JMAP terms, this means the
+  user may call *setCalendarEvents* to create new events in this calendar or
+  move existing events into this calendar from another calendar.
+  This property MUST be `false` if the account to which this calendar belongs
+  has the *isReadOnly* property set to `true`.
+- **mayModifyItems**: `Boolean`
+  The user may edit events in this calendar by calling *setCalendarEvents* with
+  the *update* argument referencing events in this collection.
+  This property MUST be `false` if the account to which this calendar belongs
+  has the *isReadOnly* property set to `true`.
+- **mayRemoveItems**: `Boolean`
+  The user may remove events from this calendar by calling *setCalendarEvents*
+  with the *destroy* argument referencing events in this collection, or by
+  updating their *calendarId* property to a different calendar.
+  This property MUST be `false` if the account to which this calendar belongs
+  has the *isReadOnly* property set to `true`.
+- **mayRename**: `Boolean`
+  The user may rename the calendar.
+  This property MUST be `false` if the account to which this calendar belongs
+  has the *isReadOnly* property set to `true`.
+- **mayDelete**: `Boolean`
+  The user may delete the calendar itself.
+  This property MUST be `false` if the account to which this calendar belongs
+  has the *isReadOnly* property set to `true`.
+
+
+### getCalendars
+
+Calendars can either be fetched explicitly by id, or all of them at once. To 
fetch calendars, make a call to `getCalendars`. It takes the following 
arguments:
+
+- **accountId**: `String|null`
+  The Account to fetch the calendars for. If `null`, the primary account is 
used.
+- **ids**: `String|null`
+  The ids of the calendars to fetch. If `null`, all calendars in the account 
are be fetched.
+
+The response to *getCalendars* is called *calendars*. 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** calendars. If a 
property of a calendar changes, or a new calendar is created, or a calendar is 
destroyed, this string will change. It is used to get delta updates.
+- **list**: `Calendar[]`
+  An array of the Calendar objects requested. This will be the **empty array** 
if the *ids* argument was the empty array, or contained only ids for calendars 
that could not be found.
+- **notFound**: `String[]|null`
+  This array contains the ids passed to the method for calendars 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 *calendars* response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any calendar data.
+
+`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.
+
+### getCalendarUpdates
+
+The *getCalendarUpdates* call allows a client to efficiently update the state 
of its cached calendars 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 *calendars* response. The server will return the 
changes made since this state.
+- **fetchRecords**: `Boolean|null`
+  If `true`, after outputting a *calendarUpdates* response, an implicit call 
will be made to *getCalendars* with the *changed* property of the response as 
the *ids* argument. If `false` or `null`, no implicit call will be made.
+
+The response to *getCalendarUpdates* is called *calendarUpdates*. 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 Calendar ids where a property of the calendar has changed 
between the old state and the new state, or the calendar has been created, and 
the calendar has not been destroyed.
+- **removed**: `String[]`
+  An array of Calendar ids for calendars which have been destroyed since the 
old state.
+
+If a calendar 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 calendar has been created AND deleted since the 
oldState, the server SHOULD remove the calendar 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 `calendarUpdates` response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any calendar 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 Calendar cache. The error object 
MUST also include a `newState: String` property with the current state for the 
type.
+
+### setCalendars
+
+Modifying the state of Calendar objects on the server is done via the 
*setCalendars* method. This encompasses creating, updating and destroying 
Calendar records.
+
+The *setCalendars* method 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.
+- **ifInState**: `String|null`
+  This is a state string as returned by the *getCalendars* method. If 
supplied, the string must match the current state, otherwise the method will be 
aborted and a `stateMismatch` error returned.
+- **create**: `String[Calendar]|null`
+  A map of *creation id* (an arbitrary string set by the client) to Calendar 
objects (containing all properties except the id).
+- **update**: `String[Calendar]|null`
+  A map of id to a Calendar object. The object may omit any property; only 
properties that have changed need be included.
+- **destroy**: `String[]|null`
+  A list of ids for Calendar 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 calendar (e.g. update 
the *name* property but not the *isVisible* property 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.
+
+If an id given cannot be found, the update or destroy MUST be rejected with a 
`notFound` set error.
+
+The *mayXXX* properties are optional when creating a calendar. All default to 
`true`. If present, they MUST all be set to `true`. These properties are 
*read-only* to the client and may not be modified in an update call. 
Restrictions may only be set by the server, or when sharing calendars with 
other accounts (setting up sharing is not yet defined in this spec).
+
+A calendar MAY be deleted that is currently associated with one or more 
events. In this case, the events belonging to this calendar MUST also be 
deleted. Conceptually, this MUST happen prior to the calendar itself being 
deleted, and MUST generate a **push** event that modifies the state of the 
*CalendarEvent* type for the account, and has a *clientId* of `null`, to 
indicate that a change has been made to the event data not explicitly requested 
by the client.
+
+The response to *setCalendars* is called *calendarsSet*. 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 *getCalendars* 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 *getCalendars*.
+- **created**: `String[Calendar]`
+  A map of the creation id to an object containing the **id** property for all 
successfully created calendars.
+- **updated**: `String[]`
+  A list of ids for groups that were successfully updated.
+- **destroyed**: `String[]`
+  A list of ids for calendars that were successfully destroyed.
+- **notCreated**: `String[SetError]`
+  A map of creation id to a SetError object for each calendar that failed to 
be created. The possible errors are defined in the description of the method 
for specific data types.
+- **notUpdated**: `String[SetError]`
+  A map of Calendar id to a SetError object for each calendar that failed to 
be updated. The possible errors are defined in the description of the method 
for specific data types.
+- **notDestroyed**: `String[SetError]`
+  A map of Calendar id to a SetError object for each calendar that failed to 
be destroyed. The possible errors are defined in the description of the method 
for specific data types.
+
+A **SetError** object has the following properties:
+
+- **type**: `String`
+  The type of error.
+- **description**: `String|null`
+  A description of the error to display to the user.
+
+If any of the properties in a create or update are invalid (immutable and 
different to the current server value, wrong type, invalid value for the 
property – like a zero-length *name*), the server MUST reject the 
create/update with a SetError of type `invalidProperties`. The SetError 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.
+
+The following errors may be returned instead of the *calendarEventsSet* 
response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any calendar 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.

Added: james/project/trunk/spec/calendarevent.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/spec/calendarevent.mdwn?rev=1726745&view=auto
==============================================================================
--- james/project/trunk/spec/calendarevent.mdwn (added)
+++ james/project/trunk/spec/calendarevent.mdwn Tue Jan 26 07:45:37 2016
@@ -0,0 +1,302 @@
+## Calendar Events
+
+A CalendarEvent contains information about an event, or recurring series of 
events, that takes place at a particular time. The object is designed to be 
easily convertible to/from iCalendar format ([RFC 
5545](https://tools.ietf.org/html/rfc5545)) for compatibility with existing 
calendaring systems.
+
+A **CalendarEvent** object has the following properties:
+
+- **id**: `String`
+  The id of the event. This property is immutable.
+- **calendarId**: `String`
+  The id of the calendar this event belongs to.
+- **summary**: `String`
+  A short summary of the event. This maps to the SUMMARY property in iCalendar.
+- **description**: `String`
+  A longer form description of the event. This is plain text, not HTML, but
+  a client *should* attempt to mark up URLs. This maps to the DESCRIPTION 
property in iCalendar.
+- **location**: `String`
+  Where the event is to take place. This maps to the LOCATION property in 
iCalendar.
+- **showAsFree**: `Boolean`
+  If true, the even should be ignored when calculating free/busy data for the
+  user. This maps to the TRANSP property in iCalendar (`false <=> OPAQUE`, 
`true <=> TRANSPARENT`).
+- **isAllDay**: `Boolean`
+  Is the event an all day event, such as a birthday or public holiday? This 
corresponds to the type (DATE or DATE-TIME) of the DTSTART property in 
iCalendar.
+- **start**: `LocalDate`
+  The date/time the event would start in the event's time zone. This 
corresponds to the DTSTART property in iCalendar.
+- **end**: `LocalDate`
+  The date/time the event would end in the event's time zone. This corresponds 
to the DTSTART/DURATION or DTEND property in iCalendar.
+- **startTimeZone**: `String|null`
+  The [Olsen Time Zone Database](http://www.iana.org/time-zones) name for the 
timezone the start of the event is actually in, or `null` for floating time. 
This corresponds to the TZID part of the DTSTART property; if the underlying 
iCalendar file does not use an Olsen name, the server SHOULD try to guess the 
correct time zone based on the VTIMEZONE information, or fallback to floating 
time.
+- **endTimeZone**: `String|null`
+  The [Olsen Time Zone Database](http://www.iana.org/time-zones) name for the 
timezone the end of the event is actually in, or `null` for floating time. This 
corresponds to the TZID part of the DTEND property (or DTSTART if not present); 
if the underlying iCalendar file does not use an Olsen name, the server SHOULD 
try to guess the correct time zone based on the VTIMEZONE information, or 
fallback to floating time.
+- **recurrence**: `Recurrence|null`
+  The recurrence rule for the event, or `null` if it does not recur. This 
corresponds to the RRULE property in iCalendar.
+- **inclusions**: `LocalDate[]|null`
+  List of extra **local** start times to recur on. The list MUST be sorted in 
date order (oldest first). This corresponds to the RDATE property in iCalendar.
+- **exceptions**: `LocalDate[null|CalendarEvent]|null`
+  An object mapping an occurrence start time (in **local time**, not UTC) to 
either:
+  - `null`: The occurrence has been deleted. This corresponds to the EXDATE 
property in iCalendar.
+  - `CalendarEvent`: A partial CalendarEvent object. Any properties in the 
object override the property of the same name for this occurrence. This 
corresponds to extra VEVENTs with RECURRENCE-IDs in iCalendar. Allowed 
properties are:
+    - summary
+    - description
+    - location
+    - showAsFree
+    - start
+    - end
+    - startTimeZone
+    - endTimeZone
+    - alerts
+    - organizer
+    - attendees
+- **alerts**: `Alert[]|null`
+  A list of alerts to display or send the user for this event. This maps to 
the VALARM property in iCalendar.
+- **organizer**: `Participant|null`
+  The organizer of the event. This maps to the ORGANIZER property in iCalendar
+- **attendees**: `Participant[]|null`
+  A list of attendees at the event. This maps to the ATTENDEE property in 
iCalendar.
+- **attachments**: `File[]|null`
+  A list of file attachments to the event. This maps to the ATTACH property in 
iCalendar.
+
+Conditions:
+
+- The *end* date MUST be equal to or after the *start* date when both are 
converted to UTC time (the event MUST NOT have a negative duration).
+- if *isAllDay* is `true`, the *start*/*end* properties MUST have a time 
component of `T00:00:00` and *startTimeZone*/*endTimeZone* properties MUST be 
`null`.
+- if *recurrence* is `null`, *inclusions* and *exceptions* MUST also be `null`.
+- either both organizer and attendees are `null`, or neither are.
+- any `null`able array/object property MUST be `null` rather than an empty 
array or object.
+
+A **Recurrence** object is a JSON object mapping of a RECUR value type in 
iCalendar. To make it easier to check if two recurrence rules are identical, 
optional properties MUST NOT be included if the value is the default. A 
Recurrence object has the following properties:
+
+- **frequency**: `String`
+  This MUST be one of the following values:
+  - `"yearly"`
+  - `"monthly"`
+  - `"weekly"`
+  - `"daily"`
+  - `"hourly"`
+  - `"minutely"`
+  - `"secondly"`
+  To convert from iCal, simply lower-case the FREQ part.
+- **interval**: `Number` (optional)
+  The INTERVAL part from iCal. Defaults to `1` if not present. This MUST NOT 
be included if the interval == 1, to ensure a canonical representation. If 
included, it MUST be an integer `x > 1`.
+- **firstDayOfWeek**: `Number` (optional)
+  The WKST part from iCal. Defaults to `1` (Monday) if not present. This MUST 
NOT be included if == 1 (Monday), to ensure a canonical representation. It MUST 
be an integer in the range (0,6), where SU => 0, TU => 2, WE => 3 etc.
+- **byDay**: `Number[]` (optional)
+  The BYDAY part from iCal. The array MUST have at least one entry if included 
and MUST be sorted in ascending order. To convert from the iCal string format 
(e.g. SU,+1MO):
+  1. Convert SU/MO/TU… <-> 0/1/2…
+  2. If it has a number attached (+1, -2 etc.), add on 7 * the attached number.
+  e.g. `+1MO => 1 + (7 * +1) => 8`, `-2TH => 4 + (7 * -2) => -10`
+- **byDate**: `Number[]` (optional)
+  The BYMONTHDAY part from iCal. The array MUST have at least one entry if 
included and MUST be sorted in ascending order.
+- **byMonth**: `Number[]` (optional)
+  The BYMONTH part from iCal, but with Jan == 0, Feb == 1 etc. (Jan == 1 in 
iCal). The array MUST have at least one entry if included and MUST be sorted in 
ascending order.
+- **byYearDay**: `Number[]` (optional)
+  The BYYEARDAY part from iCal. The array MUST have at least one entry if 
included and MUST be sorted in ascending order.
+- **byWeekNo**: `Number[]` (optional)
+  The BYWEEKNO part from iCal. The array MUST have at least one entry if 
included and MUST be sorted in ascending order.
+- **byHour**: `Number[]` (optional)
+  The BYHOUR part from iCal. The array MUST have at least one entry if 
included and MUST be sorted in ascending order.
+- **byMinute**: `Number[]` (optional)
+  The BYMINUTE part from iCal. The array MUST have at least one entry if 
included and MUST be sorted in ascending order.
+- **bySecond**: `Number[]` (optional)
+  The BYSECOND part from iCal. The array MUST have at least one entry if 
included and MUST be sorted in ascending order.
+- **bySetPosition**: `Number[]` (optional)
+  The BYSETPOS part from iCal. The array MUST have at least one entry if 
included and MUST be sorted in ascending order.
+- **count**: `Number` (optional)
+  The COUNT part from iCal. This MUST NOT be included if an *until* property 
is specified.
+- **until**: `LocalDate` (optional)
+  The UNTIL part from iCal. This MUST NOT be included if a *count* property is 
specified.
+
+An **Alert** Object has the following properties:
+
+- **minutesBefore**: `Number`
+  The number of minutes before the start time of the event to show the alert. 
The number MAY be negative for an alert after the event start. Note, if the 
event is in floating time (including all-day events), the server SHOULD use the 
user's default time zone when determining the start time.
+- **type**: `String`
+  The value MUST be one of the following:
+  - `"email"` – the server will send an email to the user at the specified 
time. The format of this email is service-specific.
+  - `"alert"` – a message should be shown to the user on any client 
connected to this account at the specified time.
+
+A **Participant** Object has the following properties:
+
+- **name**: `String`
+  The name of the participant.
+- **email**: `String`
+  The email address of the participant.
+- **isYou**: `Boolean`
+  This is `true` if the participant is the logged in user (determining this is 
not defined and is server dependent).
+- **rsvp**: `String`
+  The value MUST be one of:
+  - `""`: no RSVP made.
+  - `"yes"`: the participant is attending
+  - `"maybe"`: the participant may be attending
+  - `"no"`: the participant is not attending
+
+A **File** Object has the following properties:
+
+- **blobId**: `String`
+  The id of the binary data.
+- **type**: `String|null`
+  The content-type of the attachment, if known.
+- **name**: `String|null`
+  The full file name, if known. e.g. "myworddocument.doc"
+- **size**: `Number|null`
+  The size, in bytes, of the attachment when fully decoded (i.e. the number of 
bytes in the file the user would download), if known.
+
+### getCalendarEvents
+
+CalendarEvents can only be fetched explicitly by id. To fetch events, make a 
call to `getCalendarEvents`. 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 events to fetch.
+- **properties**: `String[]|null`
+  A list of properties to fetch for each event. 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 CalendarEvent object.
+
+The response to *getCalendarEvents* is called *calendarEvents*. 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 events change (that is, an event is created, updated or deleted). It 
can be passed to *getCalendarEventUpdates* to efficiently get the list of 
changes from the previous state.
+- **list**: `CalendarEvent[]`
+  An array of CalendarEvent objects for the requested event ids. This may not 
be in the same order as the ids were in the request.
+- **notFound**: `String[]|null`
+  An array of calendar event ids requested which could not be found, or `null` 
if all ids were found.
+
+The following errors may be returned instead of the *events* response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any calendar 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.
+
+### getCalendarEventUpdates
+
+The *getCalendarEventUpdates* call allows a client to efficiently update the 
state of its cached calendar events 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 *calendarEvents* response. The server will return the 
changes made since this state.
+- **maxChanges**: `Number|null`
+  The maximum number of CalendarEvent ids 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 *calendarEventUpdates* response, an implicit 
call will be made to *getCalendarEvents* 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`
+  Passed through as the *properties* argument to any implicit 
*getCalendarEvents* call.
+
+The response to *getCalendarEventUpdates* is called *calendarEventUpdates*. 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 *getCalendarEventUpdates* again with the 
*newState* returned to get further updates. If `false`, *newState* is the 
current server state.
+- **changed**: `String[]`
+  An array of CalendarEvent ids where a property of the event has changed 
between the old state and the new state, or the event has been created, and the 
event has not been destroyed.
+- **removed**: `String[]`
+  An array of CalendarEvent ids for events which have been destroyed since the 
old state.
+
+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 
*getCalendarEventUpdates* 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 an event 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 an event has been created AND deleted since the 
oldState, the server SHOULD remove the event 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 `calendarEventUpdates` 
response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any calendar 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 CalendarEvent cache. The error object MUST also include a `newState: 
String` property with the current state for the type.
+
+### setCalendarEvents
+
+Modifying the state of CalendarEvent objects on the server is done via the 
*setCalendarEvents* method. This encompasses creating, updating and destroying 
CalendarEvent records.
+
+The *setCalendarEvents* method 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.
+- **ifInState**: `String|null`
+  This is a state string as returned by the *getCalendarEvents* method. If 
supplied, the string must match the current state, otherwise the method will be 
aborted and a `stateMismatch` error returned. If `null`, the change will be 
applied to the current state.
+- **create**: `String[CalendarEvent]|null`
+  A map of *creation id* (an arbitrary string set by the client) to 
CalendarEvent objects (containing all properties except the id).
+- **update**: `String[CalendarEvent]|null`
+  A map of id to a CalendarEvent object. The object may omit any property; 
only properties that have changed need be included.
+- **destroy**: `String[]|null`
+  A list of ids for CalendarEvent 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 event (e.g. update the 
*start* property but not the *startTimeZone* property 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.
+
+If an id given cannot be found, the update or destroy MUST be rejected with a 
`notFound` set error.
+
+CalendarEvents reference a Calendar object. When events are created or 
modified, they may reference a calendar being created *in the same API request* 
by using the creation id prefixed with a `#`. The order of the method calls in 
the request by the client MUST be such that the calendar being referenced is 
created in an earlier call. The server thus never has to look ahead. Instead, 
while processing a request (a series of method calls), the server MUST keep a 
simple map for the duration of the request of creation id to Calendar id for 
each newly created calendar, so it can substitute in the correct value if 
necessary in later method calls.
+
+To add new attachments, the file must first be uploaded using the standard 
upload mechanism (see the File Uploads section of this spec). This will give 
the client a valid blobId/size/type to use.
+
+When an event is created, updated or destroyed, the server MUST also ensure 
the following:
+
+- Any alerts are scheduled/cancelled correctly..
+- If `organizer.isYou == true`:
+  - If an event is created with attendees, send a REQUEST iMIP email with the
+    event as an ICS attachment to all attendees with `isYou == false`.
+  - When an event is updated, email all attendees with `isYou == false` the
+    change with an appropriate iMIP email.
+  - When an event is destroyed, if it is in the future, then email all
+    attendees with `isYou == false` to inform them that the event has been 
cancelled. If it is in the past, the server SHOULD NOT send a message.
+- If `organizer.isYou != true` and one of the attendees of the event has 
`isYou == true`, then changing the RSVP status of this attendee MUST send the 
appropriate email to the organizer to inform them of the change in status, as 
per the iMIP standard.
+
+Note, for compatibility with CalDAV clients, when updating an attendee, the 
server SHOULD maintain any ROLE, CUTYPE, MEMBER, DELEGATED-TO, DELEGATED-FROM, 
SENT-BY etc. parameters currently in the iCalendar object for that attendee, 
even though this API does not currently care about it.
+
+The response to *setCalendarEvents* is called *calendarEventsSet*. 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 *getCalendarEvents* 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 *getCalendarEvents*.
+- **created**: `String[CalendarEvent]`
+  A map of the creation id to an object containing the **id** property for all 
successfully created events
+- **updated**: `String[]`
+  A list of ids for events that were successfully updated.
+- **destroyed**: `String[]`
+  A list of ids for events that were successfully destroyed.
+- **notCreated**: `String[SetError]`
+  A map of creation id to a SetError object for each event that failed to be 
created. The possible errors are defined in the description of the method for 
specific data types.
+- **notUpdated**: `String[SetError]`
+  A map of CalendarEvent id to a SetError object for each event that failed to 
be updated. The possible errors are defined in the description of the method 
for specific data types.
+- **notDestroyed**: `String[SetError]`
+  A map of CalendarEvent id to a SetError object for each event that failed to 
be destroyed. The possible errors are defined in the description of the method 
for specific data types.
+
+A **SetError** object has the following properties:
+
+- **type**: `String`
+  The type of error.
+- **description**: `String|null`
+  A description of the error to display to the user.
+
+If any of the properties in a create or update are invalid (immutable and 
different to the current server value, wrong type, invalid value for the 
property – like a *calendarId* for a non-existent calendar), the server MUST 
reject the create/update with a SetError of type `invalidProperties`. The 
SetError 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.
+
+The following errors may be returned instead of the *calendarEventsSet* 
response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any calendar 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.

Added: james/project/trunk/spec/calendareventlist.mdwn
URL: 
http://svn.apache.org/viewvc/james/project/trunk/spec/calendareventlist.mdwn?rev=1726745&view=auto
==============================================================================
--- james/project/trunk/spec/calendareventlist.mdwn (added)
+++ james/project/trunk/spec/calendareventlist.mdwn Tue Jan 26 07:45:37 2016
@@ -0,0 +1,94 @@
+## CalendarEventLists
+
+A **CalendarEventList** is a query on the set of events in a user's calendars. 
The client can optionally also fetch the events.
+
+### getCalendarEventList
+
+To fetch a calendar event list, make a call to *getCalendarEventList*. 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 events returned in the results. See the "Filtering" 
section below for allowed values and semantics.
+- **position**: `Number|null`
+  The 0-based index of the first result in the list to return, presumed `0` if 
`null`. If a negative value is given, the call MUST be rejected with an 
`invalidArguments` error.
+- **limit**: `Number|null`
+  The maximum number of results to return. If `null`, no limit presumed. The 
server MAY choose to enforce a maximum `limit` argument. In this case, if a 
greater value is given (or if it is `null`), the limit should be clamped to the 
maximum; since the total number of results in the list is returned, the client 
can determine if it has received all the results. If a negative value is given, 
the call MUST be rejected with an `invalidArguments` error.
+- **fetchCalendarEvents**: `Boolean|null`
+  If `true` then after outputting a *calendarEventList* response, an implicit 
call will be made to *getCalendarEvents* with the `calendarEventIds` array in 
the response as the *ids* argument. 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 event.
+
+A **FilterCondition** object has the following properties:
+
+- **inCalendars**: `String[]|null`
+  A list of calendar ids. An event must be in ANY of these calendars to match 
the condition.
+- **after**: `Date|null`
+  The end of the event, or any recurrence of the event, in UTC time must be 
after this date to match the condition.
+- **before**: `Date|null`
+  The start of the event, or any recurrence of the event, in UTC time must be 
before this date to match the condition.
+- **text**: `String|null`
+  Looks for the text in the *summary*, *description*, *location*, *organizer*, 
*attendees* properties of the event or any recurrence of the event (matching 
either name or email in the organizer/attendee case).
+- **summary**: `String|null`
+  Looks for the text in the *summary* property of the event, or the overridden 
*summary* property of a recurrence.
+- **description**: `String|null`
+  Looks for the text in the *description* property of the event, or the 
overridden *description* property of a recurrence.
+- **location**: `String|null`
+  Looks for the text in the *location* property of the event, or the 
overridden *location* property of a recurrence.
+- **organizer**: `String|null`
+  Looks for the text in the name or email fields of the *organizer* property 
of the event, or the overridden *organizer* property of a recurrence.
+- **attendee**: `String|null`
+  Looks for the text in the name or email of any item in the *attendees* 
property of the event, or the overridden *attendees* property of a recurrence.
+
+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 event, but MUST all be 
present for the event 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").
+
+#### Sorting
+
+Results MUST be sorted in a stable order so the client can load the full list 
in sections. The exact ordering to use is server dependent.
+
+#### Windowing
+
+To paginate the results the client MAY supply a *position* argument: this is 
the 0-based index of the first result to return in the list of events after 
filtering and sorting. If the index is greater than or equal to the total 
number of events in the list, then there are no results to return, but this 
DOES NOT generate an error. If `null`, this defaults to `0`.
+
+#### Response
+
+The response to a call to *getCalendarEventList* is called 
*calendarEventList*. It has the following arguments:
+
+- **accountId**: `String`
+  The id of the account used for the call.
+- **filter**: `FilterCondition|FilterOperator|null`
+  The filter of the event list. 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 event list MAY have changed (for example, there has 
been a change to the state of the set of CalendarEvents; it does not guarantee 
that anything in the list has changed).
+- **position**: `Number`
+  The 0-based index of the first result in the `calendarEventIds` array within 
the complete list.
+- **total**: `Number`
+  The total number of events in the list (given the *filter*).
+- **calendarEventIds**: `String[]`
+  The list of CalendarEvent ids for each event in the list after filtering and 
sorting, 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. Note, that recurrences are not returned separately in 
any way; the event only occurs once in the calendarEventIds array no matter how 
many times it recurs.
+
+The following errors may be returned instead of the `calendarEventList` 
response:
+
+`accountNotFound`: Returned if an *accountId* was explicitly included with the 
request, but it does not correspond to a valid account.
+
+`accountNoCalendars`: Returned if the *accountId* given corresponds to a valid 
account, but does not contain any calendar 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.



---------------------------------------------------------------------
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