draft-ietf-jmap-mail-05.txt   draft-ietf-jmap-mail-06.txt 
JMAP N. Jenkins JMAP N. Jenkins
Internet-Draft FastMail Internet-Draft FastMail
Updates: 5788 (if approved) May 9, 2018 Updates: 5788 (if approved) July 2, 2018
Intended status: Standards Track Intended status: Standards Track
Expires: November 10, 2018 Expires: January 3, 2019
JMAP for Mail JMAP for Mail
draft-ietf-jmap-mail-05 draft-ietf-jmap-mail-06
Abstract Abstract
This document specifies a data model for synchronising email data This document specifies a data model for synchronising email data
with a server using JMAP. with a server using JMAP.
Status of This Memo Status of This Memo
This Internet-Draft is submitted in full conformance with the This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79. provisions of BCP 78 and BCP 79.
skipping to change at page 1, line 32 skipping to change at page 1, line 32
Internet-Drafts are working documents of the Internet Engineering Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet- working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/. Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress." material or to cite them other than as "work in progress."
This Internet-Draft will expire on November 10, 2018. This Internet-Draft will expire on January 3, 2019.
Copyright Notice Copyright Notice
Copyright (c) 2018 IETF Trust and the persons identified as the Copyright (c) 2018 IETF Trust and the persons identified as the
document authors. All rights reserved. document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents Provisions Relating to IETF Documents
(https://trustee.ietf.org/license-info) in effect on the date of (https://trustee.ietf.org/license-info) in effect on the date of
publication of this document. Please review these documents publication of this document. Please review these documents
carefully, as they describe your rights and restrictions with respect carefully, as they describe your rights and restrictions with respect
to this document. Code Components extracted from this document must to this document. Code Components extracted from this document must
include Simplified BSD License text as described in Section 4.e of include Simplified BSD License text as described in Section 4.e of
the Trust Legal Provisions and are provided without warranty as the Trust Legal Provisions and are provided without warranty as
described in the Simplified BSD License. described in the Simplified BSD License.
Table of Contents Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1. Notational Conventions . . . . . . . . . . . . . . . . . 3 1.1. Notational conventions . . . . . . . . . . . . . . . . . 3
1.2. The Date datatypes . . . . . . . . . . . . . . . . . . . 4 1.2. The Date data types . . . . . . . . . . . . . . . . . . . 4
1.3. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4 1.3. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4
1.4. Addition to the capabilities object . . . . . . . . . . . 4 1.4. Addition to the capabilities object . . . . . . . . . . . 4
1.5. Data profile name . . . . . . . . . . . . . . . . . . . . 6 1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 9 2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 9
2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 9 2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 9
2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 9 2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 10
2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 10 2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 10
2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 10 2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 10
3. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.6. Example . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.1. Thread/get . . . . . . . . . . . . . . . . . . . . . . . 11 3. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 11 3.1. Thread/get . . . . . . . . . . . . . . . . . . . . . . . 17
3.2. Thread/changes . . . . . . . . . . . . . . . . . . . . . 12 3.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 17
4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.2. Thread/changes . . . . . . . . . . . . . . . . . . . . . 17
4.1. Properties of the Email object . . . . . . . . . . . . . 12 4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 12 4.1. Properties of the Email object . . . . . . . . . . . . . 17
4.1.2. Header fields . . . . . . . . . . . . . . . . . . . . 14 4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 19
4.1.3. Body parts . . . . . . . . . . . . . . . . . . . . . 19 4.1.2. Header fields . . . . . . . . . . . . . . . . . . . . 20
4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 26 4.1.3. Body parts . . . . . . . . . . . . . . . . . . . . . 26
4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 28 4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 32
4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 29 4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 34
4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 30 4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 35
4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 30 4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 36
4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 32 4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 36
4.4.3. Thread collapsing . . . . . . . . . . . . . . . . . . 34 4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 38
4.4.4. Response . . . . . . . . . . . . . . . . . . . . . . 34 4.4.3. Thread collapsing . . . . . . . . . . . . . . . . . . 40
4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 34 4.4.4. Response . . . . . . . . . . . . . . . . . . . . . . 40
4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 34 4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 40
4.7. Email/import . . . . . . . . . . . . . . . . . . . . . . 37 4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 40
4.8. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 38 4.7. Email/import . . . . . . . . . . . . . . . . . . . . . . 43
4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 40 4.8. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 44
5. Email submission . . . . . . . . . . . . . . . . . . . . . . 42 4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 46
5.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 46 5. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 46 5.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 49
5.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 46 5.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 49
5.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 47 5.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 49
5.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 47 5.4. Example . . . . . . . . . . . . . . . . . . . . . . . . . 49
6. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 49 6. Email submission . . . . . . . . . . . . . . . . . . . . . . 50
6.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 50 6.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 55
6.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 50 6.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 55
6.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 50 6.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 55
7. Search snippets . . . . . . . . . . . . . . . . . . . . . . . 51 6.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 56
7.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 51 6.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 56
6.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 58
8. Vacation response . . . . . . . . . . . . . . . . . . . . . . 52 7. Search snippets . . . . . . . . . . . . . . . . . . . . . . . 59
8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 53 7.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 60
8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 53 7.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 61
9. Security considerations . . . . . . . . . . . . . . . . . . . 53 8. Vacation response . . . . . . . . . . . . . . . . . . . . . . 62
9.1. EmailBodyPart value . . . . . . . . . . . . . . . . . . . 53 8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 63
9.2. HTML email display . . . . . . . . . . . . . . . . . . . 54 8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 63
9.3. Email submission . . . . . . . . . . . . . . . . . . . . 56 9. Security considerations . . . . . . . . . . . . . . . . . . . 63
10. References . . . . . . . . . . . . . . . . . . . . . . . . . 57 9.1. EmailBodyPart value . . . . . . . . . . . . . . . . . . . 63
10.1. Normative References . . . . . . . . . . . . . . . . . . 57 9.2. HTML email display . . . . . . . . . . . . . . . . . . . 64
10.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 60 9.3. Email submission . . . . . . . . . . . . . . . . . . . . 66
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 60 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 67
10.1. JMAP Capability Registration for "mail" . . . . . . . . 67
10.2. IMAP and JMAP Keywords Registry . . . . . . . . . . . . 67
10.2.1. Registration of JMAP keyword '$draft' . . . . . . . 68
10.2.2. Registration of JMAP keyword '$seen' . . . . . . . . 69
10.2.3. Registration of JMAP keyword '$flagged' . . . . . . 69
10.2.4. Registration of JMAP keyword '$answered' . . . . . . 70
10.2.5. Registration of '$recent' Keyword . . . . . . . . . 71
11. References . . . . . . . . . . . . . . . . . . . . . . . . . 72
11.1. Normative References . . . . . . . . . . . . . . . . . . 72
11.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 75
1. Introduction 1. Introduction
JMAP <https://tools.ietf.org/html/draft-ietf-jmap-core-03> is a JMAP <https://tools.ietf.org/html/draft-ietf-jmap-core-05> is a
generic protocol for synchronising data, such as mail, calendars or generic protocol for synchronising data, such as mail, calendars or
contacts, between a client and a server. It is optimised for mobile contacts, between a client and a server. It is optimised for mobile
and web environments, and aims to provide a consistent interface to and web environments, and aims to provide a consistent interface to
different data types. different data types.
This specification defines a data model for synchronising mail This specification defines a data model for synchronising mail
between a client and a server using JMAP. between a client and a server using JMAP.
1.1. Notational Conventions 1.1. Notational conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in [RFC2119]. document are to be interpreted as described in [RFC2119].
The underlying format used for this specification is I-JSON Type signatures, examples and property descriptions in this document
([RFC7493]). Consequently, the terms "object" and "array" as well as follow the conventions established in Section 1.1 of
the four primitive types (strings, numbers, booleans, and null) are <https://tools.ietf.org/html/draft-ietf-jmap-core-05>.
to be interpreted as described in Section 1 of [RFC7159]. Unless
otherwise noted, all the property names and values are case
sensitive.
Some examples in this document contain "partial" JSON documents used
for illustrative purposes. In these examples, three periods "..."
are used to indicate a portion of the document that has been removed
for compactness.
Types signatures are given for all JSON objects in this document.
The following conventions are used:
o "Boolean|String" - The value is either a JSON "Boolean" value, or
a JSON "String" value.
o "Foo" - Any name that is not a native JSON type means an object
for which the properties (and their types) are defined elsewhere
within this document.
o "Foo[]" - An array of objects of type "Foo".
o "String[Foo]" - A JSON "Object" being used as a map (associative
array), where all the values are of type "Foo".
Object properties may also have a set of attributes defined along Object properties may also have a set of attributes defined along
with the type signature. These have the following meanings: with the type signature. These have the following meanings:
o *sever-set*: Only the server can set the value for this property. o *sever-set*: Only the server can set the value for this property.
The client MUST NOT send this property when creating a new object The client MUST NOT send this property when creating a new object
of this type. of this type.
o *immutable*: The value MUST NOT change after the object is o *immutable*: The value MUST NOT change after the object is
created. created.
o *default*: (This is followed by a JSON value). The value that o *default*: (This is followed by a JSON value). The value that
will be used for this property if it is omitted in an argument, or will be used for this property if it is omitted in an argument, or
when creating a new object of this type. when creating a new object of this type.
1.2. The Date datatypes 1.2. The Date data types
Where "Date" is given as a type, it means a string in [RFC3339] Where "Date" is given as a type, it means a string in [RFC3339]
_date-time_ format. To ensure a normalised form, the _time-secfrac_ _date-time_ format. To ensure a normalised form, the _time-secfrac_
MUST always be omitted and any letters in the string (e.g. "T" and MUST always be omitted and any letters in the string (e.g. "T" and
"Z") MUST be upper-case. For example, ""2014-10-30T14:12:00+08:00"". "Z") MUST be upper-case. For example, ""2014-10-30T14:12:00+08:00"".
Where "UTCDate" is given as a type, it means a "Date" where the Where "UTCDate" is given as a type, it means a "Date" where the
_time-offset_ component MUST be "Z" (i.e. it must be in UTC time). _time-offset_ component MUST be "Z" (i.e. it must be in UTC time).
For example, ""2014-10-30T06:12:00Z"". For example, ""2014-10-30T06:12:00Z"".
skipping to change at page 5, line 15 skipping to change at page 4, line 50
o *maxMailboxesPerEmail*: "Number|null" The maximum number of o *maxMailboxesPerEmail*: "Number|null" The maximum number of
mailboxes that can be can assigned to a single email. This MUST mailboxes that can be can assigned to a single email. This MUST
be an integer >= 1, or "null" for no limit (or rather, the limit be an integer >= 1, or "null" for no limit (or rather, the limit
is always the number of mailboxes in the account). is always the number of mailboxes in the account).
o *maxSizeAttachmentsPerEmail*: "Number" The maximum total size of o *maxSizeAttachmentsPerEmail*: "Number" The maximum total size of
attachments, in octets, allowed for a single email. A server MAY attachments, in octets, allowed for a single email. A server MAY
still reject emails with a lower attachment size total (for still reject emails with a lower attachment size total (for
example, if the body includes several megabytes of text, causing example, if the body includes several megabytes of text, causing
the size of the encoded MIME structure to be over some server- the size of the encoded MIME structure to be over some server-
defined limit). defined limit). Note, this limit is for the sum of unencoded
attachment sizes. Users are generally not knowledgeable about
encoding overhead etc., nor should they need to be, so services
marketing and help materials normally tells them the "max size
attachments". This is the unencoded size they see on their hard
drive, and so this capability matches that and allows the client
to consistently enforce what the user understands as the limit.
The server may separately have a limit for the total size of the
RFC5322 message, which will have attachments Base64 encoded and
message headers and bodies too. For example, suppose the server
advertises "maxSizeAttachmentsPerEmail: 50000000" (50 MB). The
enforced server limit may be for an RFC5322 size of 70000000
octets (70 MB). Even with Base64 encoding and a 2 MB HTML body,
50 MB attachments would fit under this limit.
o *maxDelayedSend*: "Number" The number in seconds of the maximum o *maxDelayedSend*: "Number" The number in seconds of the maximum
delay the server supports in sending (see the EmailSubmission delay the server supports in sending (see the EmailSubmission
object). This is "0" if the server does not support delayed send. object). This is "0" if the server does not support delayed send.
o *emailsListSortOptions*: "String[]" A list of all the email o *emailsListSortOptions*: "String[]" A list of all the email
properties the server supports for sorting by. This MAY include properties the server supports for sorting by. This MAY include
properties the client does not recognise (for example custom properties the client does not recognise (for example custom
properties specified in a vendor extension). Clients MUST ignore properties specified in a vendor extension). Clients MUST ignore
any unknown properties in the list. any unknown properties in the list.
skipping to change at page 6, line 7 skipping to change at page 6, line 7
* MT-PRIORITY ([RFC6710]) * MT-PRIORITY ([RFC6710])
A JMAP server MAY advertise an extension and implement the A JMAP server MAY advertise an extension and implement the
semantics of that extension locally on the JMAP server even if a semantics of that extension locally on the JMAP server even if a
submission server used by JMAP doesn't implement it. The full submission server used by JMAP doesn't implement it. The full
IANA registry of submission extensions can be found at IANA registry of submission extensions can be found at
<https://www.iana.org/assignments/mail-parameters/mail- <https://www.iana.org/assignments/mail-parameters/mail-
parameters.xhtml#mail-parameters-2> parameters.xhtml#mail-parameters-2>
1.5. Data profile name The server MUST also include the string "urn:ietf:params:jmap:mail"
in the _hasDataFor_ property of any account in which the user may use
the data types contained in this specification.
The data profile name for the set of types defined in this 1.5. Push
specification is "mail".
The JMAP Session object has an _accounts_ property with the set of Servers MUST support the standard JMAP push mechanisms to receive
accounts to which the user has access. Any account that contains notifications when the state changes for any of the types defined in
data of the types defined in this specification MUST include the this specification.
string ""mail"" in the _hasDataFor_ property of the account object.
In addition, servers MUST support a psuedo-type called
"EmailDelivery" in the push mechanisms. The state string for this
MUST change whenever a new Email is added to the store, but SHOULD
NOT change upon any other change to the Email objects.
Clients in battery constrained environments may wish to delay
fetching changes initiated by the user, but fetch new messages
immediately so they can notify the user.
2. Mailboxes 2. Mailboxes
A mailbox represents a named set of emails. This is the primary A mailbox represents a named set of emails. This is the primary
mechanism for organising emails within an account. It is analogous mechanism for organising emails within an account. It is analogous
to a folder or a label in other systems. A mailbox may perform a to a folder or a label in other systems. A mailbox may perform a
certain role in the system; see below for more details. certain role in the system; see below for more details.
For compatibility with IMAP, an email MUST belong to one or more For compatibility with IMAP, an email MUST belong to one or more
mailboxes. The email id does not change if the email changes mailboxes. The email id does not change if the email changes
mailboxes. mailboxes.
A *Mailbox* object has the following properties: A *Mailbox* object has the following properties:
o *id*: "String" (immutable; server-set) The id of the mailbox. o *id*: "String" (immutable; server-set) The id of the mailbox.
o *name*: "String" User-visible name for the mailbox, e.g. "Inbox". o *name*: "String" User-visible name for the mailbox, e.g. "Inbox".
This may be any Net-Unicode string ([RFC5198]) of at least 1 This may be any Net-Unicode string ([RFC5198]) of at least 1
character in length and maximum 255 octets in size. Servers character in length and maximum 255 octets in size. Servers MUST
SHOULD forbid sibling Mailboxes with the same name. Servers MAY forbid sibling Mailboxes with the same name. Servers MAY reject
reject names that violate server policy (e.g., names containing names that violate server policy (e.g., names containing slash (/)
slash (/) or control characters). or control characters).
o *parentId*: "String|null" (default: "null") The mailbox id for the o *parentId*: "String|null" (default: "null") The mailbox id for the
parent of this mailbox, or "null" if this mailbox is at the top parent of this mailbox, or "null" if this mailbox is at the top
level. Mailboxes form acyclic graphs (forests) directed by the level. Mailboxes form acyclic graphs (forests) directed by the
child-to-parent relationship. There MUST NOT be a loop. child-to-parent relationship. There MUST NOT be a loop.
o *role*: "String|null" (default: "null") Identifies mailboxes that o *role*: "String|null" (default: "null") Identifies mailboxes that
have a particular common purpose (e.g. the "inbox"), regardless of have a particular common purpose (e.g. the "inbox"), regardless of
the _name_ (which may be localised). This value is shared with the _name_ (which may be localised). This value is shared with
IMAP (exposed in IMAP via the [RFC6154] SPECIAL-USE extension). IMAP (exposed in IMAP via the [RFC6154] SPECIAL-USE extension).
However, unlike in IMAP, a mailbox may only have a single role, However, unlike in IMAP, a mailbox may only have a single role,
and no two mailboxes in the same account may have the same role. and no two mailboxes in the same account may have the same role.
The value MUST be one of the mailbox attribute names listed in the The value MUST be one of the mailbox attribute names listed in the
IANA Mailbox Name Attributes Registry [1], as established in IANA Mailbox Name Attributes Registry [1], as established in
[TODO:being established in EXTRA], converted to lower-case. New
[TODO], converted to lower-case. New roles may be established roles may be established here in the future. An account is not
here in the future. An account is not required to have mailboxes required to have mailboxes with any particular roles.
with any particular roles.
o *sortOrder*: "Number" (default: "0") Defines the sort order of o *sortOrder*: "Number" (default: "0") Defines the sort order of
mailboxes when presented in the client's UI, so it is consistent mailboxes when presented in the client's UI, so it is consistent
between devices. The number MUST be an integer in the range 0 <= between devices. The number MUST be an integer in the range 0 <=
sortOrder < 2^31. A mailbox with a lower order should be sortOrder < 2^31. A mailbox with a lower order should be
displayed before a mailbox with a higher order (that has the same displayed before a mailbox with a higher order (that has the same
parent) in any mailbox listing in the client's UI. Mailboxes with parent) in any mailbox listing in the client's UI. Mailboxes with
equal order SHOULD be sorted in alphabetical order by name. The equal order SHOULD be sorted in alphabetical order by name. The
sorting SHOULD take into account locale-specific character order sorting SHOULD take into account locale-specific character order
convention.. convention.
o *totalEmails*: "Number" (server-set) The number of emails in this o *totalEmails*: "Number" (server-set) The number of emails in this
mailbox. mailbox.
o *unreadEmails*: "Number" (server-set) The number of emails in this o *unreadEmails*: "Number" (server-set) The number of emails in this
mailbox that have neither the "$seen" keyword nor the "$draft" mailbox that have neither the "$seen" keyword nor the "$draft"
keyword. keyword.
o *totalThreads*: "Number" (server-set) The number of threads where o *totalThreads*: "Number" (server-set) The number of threads where
at least one email in the thread is in this mailbox. at least one email in the thread is in this mailbox.
skipping to change at page 8, line 28 skipping to change at page 8, line 36
* *mayRename*: "Boolean" The user may rename the mailbox or make * *mayRename*: "Boolean" The user may rename the mailbox or make
it a child of another mailbox. Corresponds to IMAP ACL "x". it a child of another mailbox. Corresponds to IMAP ACL "x".
* *mayDelete*: "Boolean" The user may delete the mailbox itself. * *mayDelete*: "Boolean" The user may delete the mailbox itself.
Corresponds to IMAP ACL "x". Corresponds to IMAP ACL "x".
* *maySubmit*: "Boolean" Messages may be submitted directly to * *maySubmit*: "Boolean" Messages may be submitted directly to
this mailbox. Corresponds to IMAP ACL "p". this mailbox. Corresponds to IMAP ACL "p".
o *isSubscribed*: "Boolean" Has the user indicated they wish to see
this mailbox in their client? This SHOULD default to "false" for
mailboxes in shared accounts the user has access to, and "true"
for any new mailboxes created by the user themself. This MUST be
stored separately per-user where multiple users have access to a
shared mailbox. A user may have permission to access a large
number of shared accounts, or a shared account with a very large
set of mailboxes, but only be interested in the contents of a few
of these. Clients may choose only to display mailboxes to the
user that have the "isSubscribed" property set to "true", and
offer a separate UI to allow the user to see and subscribe/
unsubscribe from the full set of mailboxes. However, clients MAY
choose to ignore this property, either entirely, for ease of
implementation, or just for the primary account (which is normally
the user's own, rather than a shared account).
The Trash mailbox (that is a mailbox with "role == "trash"") MUST be The Trash mailbox (that is a mailbox with "role == "trash"") MUST be
treated specially for the purpose of unread counts: treated specially for the purpose of unread counts:
1. Emails that are *only* in the Trash (and no other mailbox) are 1. Emails that are *only* in the Trash (and no other mailbox) are
ignored when calculating the "unreadThreads" count of other ignored when calculating the "unreadThreads" count of other
mailboxes. mailboxes.
2. Emails that are *not* in the Trash are ignored when calculating 2. Emails that are *not* in the Trash are ignored when calculating
the "unreadThreads" count for the Trash mailbox. the "unreadThreads" count for the Trash mailbox.
skipping to change at page 9, line 29 skipping to change at page 10, line 6
this will be the list of properties that may have changed, i.e. this will be the list of properties that may have changed, i.e.
"["totalEmails", "unreadEmails", "totalThreads", "["totalEmails", "unreadEmails", "totalThreads",
"unreadThreads"]". If the server is unable to tell if only counts "unreadThreads"]". If the server is unable to tell if only counts
have changed, it MUST just be "null". have changed, it MUST just be "null".
Since counts frequently change but the rest of the mailboxes state Since counts frequently change but the rest of the mailboxes state
for most use cases changes rarely, the server can help the client for most use cases changes rarely, the server can help the client
optimise data transfer by keeping track of changes to email/thread optimise data transfer by keeping track of changes to email/thread
counts separately to other state changes. The _changedProperties_ counts separately to other state changes. The _changedProperties_
array may be used directly via a result reference in a subsequent array may be used directly via a result reference in a subsequent
Mailboxe/get call in a single request. Mailbox/get call in a single request.
2.3. Mailbox/query 2.3. Mailbox/query
Standard _/query_ method. Standard _/query_ method.
A *FilterCondition* object has the following properties, any of which A *FilterCondition* object has the following properties, any of which
may be omitted: may be omitted:
o *parentId*: "String|null" The Mailbox _parentId_ property must o *parentId*: "String|null" The Mailbox _parentId_ property must
match the given value exactly. match the given value exactly.
o *hasRole*: "Boolean" If this is "true", a Mailbox matches if it o *hasRole*: "Boolean" If this is "true", a Mailbox matches if it
has a non-"null" value for its _role_ property. If "false", it has a non-"null" value for its _role_ property. If "false", it
must has a "null" _role_ value to match. must has a "null" _role_ value to match.
o *isSubscribed*: "Boolean" The "isSubscribed" property of the
mailbox must be identical to the value given to match the
condition.
A Mailbox object matches the filter if and only if all of the given A Mailbox object matches the filter if and only if all of the given
conditions given match. If zero properties are specified, it is conditions given match. If zero properties are specified, it is
automatically "true" for all objects. automatically "true" for all objects.
The following properties MUST be supported for sorting: The following properties MUST be supported for sorting:
o "sortOrder" o "sortOrder"
o "name" o "name"
o "parent/name": This is a pseudo-property, just for sorting, with o "parent/name": This is a pseudo-property, just for sorting, with
the following semantics: if two mailboxes have a common parent, the following semantics: if two mailboxes have a common parent,
sort them by name. Otherwise, find the nearest ancestors of each sort them by name. Otherwise, find the nearest ancestors of each
that share a common parent and sort by their names instead. (i.e. that share a common parent and sort by their names instead. (i.e.
This sorts the mailbox list in tree order). This sorts the mailbox list in tree order).
2.4. Mailbox/queryChanges 2.4. Mailbox/queryChanges
skipping to change at page 10, line 38 skipping to change at page 11, line 20
For *destroy*: For *destroy*:
o "mailboxHasChild": The mailbox still has at least one child o "mailboxHasChild": The mailbox still has at least one child
mailbox. The client MUST remove these before it can delete the mailbox. The client MUST remove these before it can delete the
parent mailbox. parent mailbox.
o "mailboxHasEmail": The mailbox has at least one message assigned o "mailboxHasEmail": The mailbox has at least one message assigned
to it and the _onDestroyRemoveMessages_ argument was "false". to it and the _onDestroyRemoveMessages_ argument was "false".
2.6. Example
Fetching all mailboxes in an account:
[
"Mailbox/get",
{
"accountId": "u33084183",
"ids": null
},
"0"
]
And response:
[ "Mailbox/get",
{
"accountId": "u33084183",
"state": "78540",
"list": [
{
"id": "23cfa8094c0f41e6",
"name": "Inbox",
"parentId": null,
"role": "inbox",
"sortOrder": 10,
"totalEmails": 16307,
"unreadEmails": 13905,
"totalThreads": 5833,
"unreadThreads": 5128,
"myRights": {
"mayAddItems": true,
"mayRename": false,
"maySubmit": true,
"mayDelete": false,
"maySetKeywords": true,
"mayRemoveItems": true,
"mayCreateChild": true,
"maySetSeen": true,
"mayReadItems": true
},
"isSubscribed": true
},
{
"id": "674cc24095db49ce",
"name": "Important mail",
...
}
...
],
"notFound": []
},
"0"
]
Now suppose a message is marked read and we get a push update that
the Mailbox state has changed. You might fetch the updates like
this:
[
"Mailbox/changes",
{
"accountId": "u33084183",
"sinceState": "78540"
},
"0"
],
[
"Mailbox/get",
{
"accountId": "u33084183",
"#ids": {
"resultOf": "0",
"name": "Mailbox/changes",
"path": "/created"
}
},
"1"
],
[
"Mailbox/get",
{
"accountId": "u33084183",
"#ids": {
"resultOf": "0",
"name": "Mailbox/changes",
"path": "/updated"
},
"#properties": {
"resultOf": "0",
"name": "Mailbox/changes",
"path": "/changedProperties"
}
},
"2"
]
This fetches the list of ids for created/updated/destroyed mailboxes,
then using back references fetches the data for just the created/
updated mailboxes in the same request. The response may look
something like this:
[
"Mailbox/changes",
{
"accountId": "u33084183",
"oldState": "78541",
"newState": "78542",
"hasMoreChanges": false,
"changedProperties": [
"totalEmails", "unreadEmails",
"totalThreads", "unreadThreads"
],
"created": [],
"updated": ["23cfa8094c0f41e6"],
"destroyed": []
},
"0"
],
["Mailbox/get", {
"accountId": "u33084183",
"state": "78542",
"list": [],
"notFound": []
}, "1"],
["Mailbox/get", {
"accountId": "u33084183",
"state": "78542",
"list": [{
"id": "23cfa8094c0f41e6",
"totalEmails": 16307,
"unreadEmails": 13903,
"totalThreads": 5833,
"unreadThreads": 5127
}],
"notFound": []
}, "2"],
Here's an example where we try to rename one mailbox and destroy
another:
[
"Mailbox/set",
{
"accountId": "u33084183",
"ifInState": "78542",
"update": {
"674cc24095db49ce": {
"name": "Maybe important mail"
}
},
"destroy": [ "23cfa8094c0f41e6" ]
},
"0"
]
Suppose the rename succeeds, but we don't have permission to destroy
the mailbox we tried to destroy, we might get back:
[
"Mailbox/set",
{
"accountId": "u33084183",
"oldState": "78542",
"newState": "78549",
"created": null,
"notCreated": null,
"updated": {
"674cc24095db49ce": null
},
"notUpdated": null,
"destroyed": null,
"notDestroyed": {
"23cfa8094c0f41e6": {
"type": "forbidden"
}
}
},
"0"
]
3. Threads 3. Threads
Replies are grouped together with the original message to form a Replies are grouped together with the original message to form a
thread. In JMAP, a thread is simply a flat list of emails, ordered thread. In JMAP, a thread is simply a flat list of emails, ordered
by date. Every email MUST belong to a thread, even if it is the only by date. Every email MUST belong to a thread, even if it is the only
email in the thread. email in the thread.
The JMAP spec does not require the server to use any particular The exact algorithm for determining whether two emails belong to the
algorithm for determining whether two emails belong to the same same thread is not mandated in this spec to allow for compatibility
thread, however there is a recommended algorithm in the with different existing systems. For new implementations, it is
implementation guide [2]. suggested that two messages belong in the same thread if both of the
following conditions apply:
1. An identical RFC5322 message id appears in both messages in any
of the Message-Id, In-Reply-To and References headers.
2. After stripping automatically added prefixes such as "Fwd:",
"Re:", "[List-Tag]" etc. and ignoring whitespace, the subjects
are the same. This avoids the situation where a person replies
to an old message as a convenient way of finding the right
recipient to send to, but changes the subject and starts a new
conversation.
If emails are delivered out of order for some reason, a user may If emails are delivered out of order for some reason, a user may
receive two emails in the same thread but without headers that receive two emails in the same thread but without headers that
associate them with each other. The arrival of a third email in the associate them with each other. The arrival of a third email in the
thread may provide the missing references to join them all together thread may provide the missing references to join them all together
into a single thread. Since the _threadId_ of an email is immutable, into a single thread. Since the _threadId_ of an email is immutable,
if the server wishes to merge the threads, it MUST handle this by if the server wishes to merge the threads, it MUST handle this by
deleting and reinserting (with a new email id) the emails that change deleting and reinserting (with a new email id) the emails that change
threadId. threadId.
skipping to change at page 12, line 18 skipping to change at page 17, line 32
"list": [ "list": [
{ {
"id": "f123u4", "id": "f123u4",
"emailIds": [ "eaa623", "f782cbb"] "emailIds": [ "eaa623", "f782cbb"]
}, },
{ {
"id": "f41u44", "id": "f41u44",
"emailIds": [ "82cf7bb" ] "emailIds": [ "82cf7bb" ]
} }
], ],
"notFound": null "notFound": []
}, "#1" ] }, "#1" ]
3.2. Thread/changes 3.2. Thread/changes
Standard _/changes_ method. Standard _/changes_ method.
4. Emails 4. Emails
The *Email* object is a representation of an [RFC5322] message, which The *Email* object is a representation of an [RFC5322] message, which
allows clients to avoid the complexities of MIME parsing, transport allows clients to avoid the complexities of MIME parsing, transport
encoding and character encoding. encoding and character encoding.
4.1. Properties of the Email object 4.1. Properties of the Email object
Broadly, a message consists of two parts: a list of header fields, Broadly, a message consists of two parts: a list of header fields,
then a body. The body is normally a MIME-encoded set of documents in then a body. The JMAP Email object provides a way to access the full
a tree structure. The JMAP Email object provides a way to access the structure, or to use simplified properties and avoid some complexity
full structure, or to use simplified properties and avoid some if this is sufficient for the client application.
complexity if this is sufficient for the client application.
While raw headers can be fetched and set, the vast majority of
clients should use an appropriate parsed form for each of the headers
it wants to process, as this allows it to avoid the complexities of
various encodings that are required in a valid RFC5322 message.
The body of a message is normally a MIME-encoded set of documents in
a tree structure. This may be arbitrarily nested, but the majority
of email clients present a flat model of an email body (normally
plain text or HTML), with a set of attachments. Flattening the MIME
structure to form this model can be difficult, and causes
inconsistency between clients. Therefore in addition to the
_bodyStructure_ property, which gives the full tree, the Email object
contains 3 alternate properties with flat lists of body parts:
o _textBody_/_htmlBody_: These provide a list of parts that should
be rendered sequentially as the "body" of the message. This is a
list rather than a single part as messages may have headers and/or
footers appended/prepended as separate parts as they are
transmitted, and some clients send text and images, or even videos
and sound clips, intended to be displayed inline in the body as
multiple parts rather than a single HTML part with referenced
images.
Because MIME allows for multiple representations of the same data
(using "multipart/alternative"), there is a textBody property (which
prefers a plain text representation) and an htmlBody property (which
prefers an HTML representation) to accommodate the two most common
client requirements. The same part may appear in both lists where
there is no alternative between the two.
o _attachments_: This provides a list of parts that should be
presented as "attachments" to the message. Some images may be
solely there for embedding within an HTML body part; clients may
wish to not present these as attachments in the user interface if
they are displaying the HTML with the embedded images directly.
Some parts may also be in htmlBody/textBody; again, clients may
wish to not present these as attachments in the user interface if
rendered as part of the body.
The _bodyValues_ property allows for clients to fetch the value of
text parts directly without having to do a second request for the
blob, and have the server handle decoding the charset into unicode.
This data is in a separate property rather than on the EmailBodyPart
object to avoid duplication of large amounts of data, as the same
part may be included twice if the client fetches more than one of
bodyStructure, textBody and htmlBody.
Due to the number of properties involved, the set of _Email_ Due to the number of properties involved, the set of _Email_
properties is specified over the following three sub-sections. properties is specified over the following three sub-sections.
4.1.1. Metadata 4.1.1. Metadata
These properties represent metadata about the [RFC5322] message, and These properties represent metadata about the [RFC5322] message, and
are not derived from parsing the message itself. are not derived from parsing the message itself.
o *id*: "String" (immutable; server-set) The id of the Email object. o *id*: "String" (immutable; server-set) The id of the Email object.
skipping to change at page 13, line 42 skipping to change at page 20, line 8
The IMAP "\Recent" keyword is not exposed via JMAP. The IMAP The IMAP "\Recent" keyword is not exposed via JMAP. The IMAP
"\Deleted" keyword is also not present: IMAP uses a delete+expunge "\Deleted" keyword is also not present: IMAP uses a delete+expunge
model, which JMAP does not. Any message with the "\Deleted" model, which JMAP does not. Any message with the "\Deleted"
keyword MUST NOT be visible via JMAP. Users may add arbitrary keyword MUST NOT be visible via JMAP. Users may add arbitrary
keywords to an email. For compatibility with IMAP, a keyword is a keywords to an email. For compatibility with IMAP, a keyword is a
case-insensitive string of 1-255 characters in the ASCII subset case-insensitive string of 1-255 characters in the ASCII subset
%x21-%x7e (excludes control chars and space), and MUST NOT include %x21-%x7e (excludes control chars and space), and MUST NOT include
any of these characters: "( ) { ] % * " \" Because JSON is case- any of these characters: "( ) { ] % * " \" Because JSON is case-
sensitive, servers MUST return keywords in lower-case. The IANA sensitive, servers MUST return keywords in lower-case. The IANA
Keyword Registry [3] as established in [RFC5788] assigns semantic Keyword Registry [2] as established in [RFC5788] assigns semantic
meaning to some other keywords in common use. New keywords may be meaning to some other keywords in common use. New keywords may be
established here in the future. In particular, note: established here in the future. In particular, note:
* "$forwarded": The email has been forwarded. * "$forwarded": The email has been forwarded.
* "$phishing": The email is highly likely to be phishing. * "$phishing": The email is highly likely to be phishing.
Clients SHOULD warn users to take care when viewing this email Clients SHOULD warn users to take care when viewing this email
and disable links and attachments. and disable links and attachments.
* "$junk": The email is definitely spam. Clients SHOULD set this * "$junk": The email is definitely spam. Clients SHOULD set this
flag when users report spam to help train automated spam- flag when users report spam to help train automated spam-
detection systems. detection systems.
* "$notjunk": The email is definitely not spam. Clients SHOULD * "$notjunk": The email is definitely not spam. Clients SHOULD
set this flag when users indicate an email is legitimate, to set this flag when users indicate an email is legitimate, to
help train automated spam-detection systems. help train automated spam-detection systems.
o *size*: "Number" (immutable; server-set) The size, in octets, of o *size*: "Number" (immutable; server-set) The size, in octets, of
the raw data for the RFC5322 message (as referenced by the the raw data for the [RFC5322] message (as referenced by the
_blobId_, i.e. the number of octets in the file the user would _blobId_, i.e. the number of octets in the file the user would
download). download).
o *receivedAt*: "UTCDate" (immutable; default: time of creation on o *receivedAt*: "UTCDate" (immutable; default: time of creation on
server) The date the email was received by the message store. server) The date the email was received by the message store.
This is the _internal date_ in IMAP. This is the _internal date_ in IMAP.
4.1.2. Header fields 4.1.2. Header fields
These properties are derived from the [RFC5322] and [RFC6532] message These properties are derived from the [RFC5322] and [RFC6532] message
skipping to change at page 15, line 42 skipping to change at page 22, line 8
1. The surrounding DQUOTE characters are removed. 1. The surrounding DQUOTE characters are removed.
2. Any _quoted-pair_ is decoded. 2. Any _quoted-pair_ is decoded.
3. White-space is unfolded, and then any leading or trailing 3. White-space is unfolded, and then any leading or trailing
white-space is removed. white-space is removed.
* *email*: "String|null" The _addr-spec_ of the [RFC5322] * *email*: "String|null" The _addr-spec_ of the [RFC5322]
_mailbox_, or "null" if a _group_. _mailbox_, or "null" if a _group_.
Any syntactically correct [RFC2047] encoded sections with a known Any syntactically correct [RFC2047] encoded sections with a known
encoding MUST be decoded, following the same rules as for the _Text_ encoding MUST be decoded, following the same rules as for the
form. Any [RFC6532] UTF-8 values MUST be decoded. _Text_ form. Any [RFC6532] UTF-8 values MUST be decoded. Parsing
SHOULD be best-effort in the face of invalid structure to
Parsing SHOULD be best-effort in the face of invalid structure to accommodate invalid messages and semi-complete drafts.
accommodate invalid messages and semi-complete drafts. EmailAddress EmailAddress objects MAY have an _email_ property that does not
objects MAY have an _email_ property that does not conform to the conform to the _addr-spec_ form (for example, may not contain an @
_addr-spec_ form (for example, may not contain an @ symbol). symbol). To prevent obviously nonsense behaviour, which can lead
to interoperability issues, this form may only be fetched or set
To prevent obviously nonsense behaviour, which can lead to for the following header fields:
interoperability issues, this form may only be fetched or set for the
following header fields:
o From * From
o Sender * Sender
o Reply-To * Reply-To
o To * To
o Cc * Cc
o Bcc * Bcc
o Resent-From * Resent-From
o Resent-Sender * Resent-Sender
o Resent-Reply-To * Resent-Reply-To
o Resent-To * Resent-To
o Resent-Cc * Resent-Cc
o Resent-Bcc * Resent-Bcc
o Any header not defined in [RFC5322] or [RFC2369] * Any header not defined in [RFC5322] or [RFC2369]
o *MessageIds* ("String[]|null") The header is parsed as a list of o *MessageIds* ("String[]|null") The header is parsed as a list of
"msg-id" values, as specified in [RFC5322] section 3.6.4, into the "msg-id" values, as specified in [RFC5322] section 3.6.4, into the
"String[]" type. CFWS and surrounding angle brackets ("<>") are "String[]" type. CFWS and surrounding angle brackets ("<>") are
removed. If parsing fails, the value is "null". removed. If parsing fails, the value is "null". To prevent
obviously nonsense behaviour, which can lead to interoperability
issues, this form may only be fetched or set for the following
header fields:
To prevent obviously nonsense behaviour, which can lead to * Message-ID
interoperability issues, this form may only be fetched or set for the
following header fields:
o Message-ID * In-Reply-To
o In-Reply-To * References
o References * Resent-Message-ID
o Resent-Message-ID * Any header not defined in [RFC5322] or [RFC2369]
o Any header not defined in [RFC5322] or [RFC2369]
o *Date* ("Date|null") The header is parsed as a "date-time" value, o *Date* ("Date|null") The header is parsed as a "date-time" value,
as specified in [RFC5322] section 3.3, into the "Date" type. If as specified in [RFC5322] section 3.3, into the "Date" type. If
parsing fails, the value is "null". parsing fails, the value is "null". To prevent obviously nonsense
behaviour, which can lead to interoperability issues, this form
To prevent obviously nonsense behaviour, which can lead to may only be fetched or set for the following header fields:
interoperability issues, this form may only be fetched or set for the
following header fields:
o Date * Date
o Resent-Date * Resent-Date
o Any header not defined in [RFC5322] or [RFC2369] * Any header not defined in [RFC5322] or [RFC2369]
o *URLs* ("String[]|null") The header is parsed as a list of URLs, o *URLs* ("String[]|null") The header is parsed as a list of URLs,
as described in [RFC2369], into the "String[]" type. Values do as described in [RFC2369], into the "String[]" type. Values do
not include the surrounding angle brackets or any comments in the not include the surrounding angle brackets or any comments in the
header with the URLs. If parsing fails, the value is "null". header with the URLs. If parsing fails, the value is "null". To
prevent obviously nonsense behaviour, which can lead to
To prevent obviously nonsense behaviour, which can lead to interoperability issues, this form may only be fetched or set for
interoperability issues, this form may only be fetched or set for the the following header fields:
following header fields:
o List-Help * List-Help
o List-Unsubscribe * List-Unsubscribe
o List-Subscribe * List-Subscribe
o List-Post * List-Post
o List-Owner * List-Owner
o List-Archive * List-Archive
o Any header not defined in [RFC5322] or [RFC2369] * Any header not defined in [RFC5322] or [RFC2369]
The following low-level *Email* property is specified for complete The following low-level *Email* property is specified for complete
access to the header data of the message: access to the header data of the message:
o *headers*: "EmailHeader[]" (immutable) This is a list of all o *headers*: "EmailHeader[]" (immutable) This is a list of all
[RFC5322] header fields, in the same order they appear in the [RFC5322] header fields, in the same order they appear in the
message. An *EmailHeader* object has the following properties: message. An *EmailHeader* object has the following properties:
* *name*: "String" The header _field name_ as defined in RFC5322, * *name*: "String" The header _field name_ as defined in
with the same capitalization that it has in the message. [RFC5322], with the same capitalization that it has in the
message.
* *value*: "String" The header _field value_ as defined in * *value*: "String" The header _field value_ as defined in
RFC5322, in _Raw_ form. [RFC5322], in _Raw_ form.
In addition, the client may request/send properties representing In addition, the client may request/send properties representing
individual header fields of the form: individual header fields of the form:
header:{header-field-name} header:{header-field-name}
Where "{header-field-name}" means any series of one or more printable Where "{header-field-name}" means any series of one or more printable
ASCII characters (i.e. characters that have values between 33 and ASCII characters (i.e. characters that have values between 33 and
126, inclusive), except colon. The property may also have the 126, inclusive), except colon. The property may also have the
following suffixes: following suffixes:
skipping to change at page 20, line 35 skipping to change at page 26, line 45
[RFC2047] decoded _name_ parameter of the _Content-Type_ header [RFC2047] decoded _name_ parameter of the _Content-Type_ header
field. field.
o *type*: "String" The value of the _Content-Type_ header field of o *type*: "String" The value of the _Content-Type_ header field of
the part, if present, otherwise the implicit type as per the MIME the part, if present, otherwise the implicit type as per the MIME
standard ("text/plain", or "message/rfc822" if inside a standard ("text/plain", or "message/rfc822" if inside a
"multipart/digest"). CFWS is removed and any parameters are "multipart/digest"). CFWS is removed and any parameters are
stripped. stripped.
o *charset*: "String|null" The value of the charset parameter of the o *charset*: "String|null" The value of the charset parameter of the
_Content-Type_ header field, if present. _Content-Type_ header field, if present, or "null" if the header
field is present but has no charset parameter. If there is no
_Content-Type_ header field, this is the implicit charset as per
the MIME standard ("us-ascii").
o *disposition*: "String|null" The value of the _Content- o *disposition*: "String|null" The value of the _Content-
Disposition_ header field of the part, if present, otherwise Disposition_ header field of the part, if present, otherwise
"null". CFWS is removed and any parameters are stripped. "null". CFWS is removed and any parameters are stripped.
o *cid*: "String|null" The value of the _Content-Id_ header field of o *cid*: "String|null" The value of the _Content-Id_ header field of
the part, if present, otherwise "null". CFWS and surrounding the part, if present, otherwise "null". CFWS and surrounding
angle brackets ("<>") are removed. This may be used to reference angle brackets ("<>") are removed. This may be used to reference
the content from within an html body part using the "cid:" the content from within an html body part using the "cid:"
protocol. protocol.
skipping to change at page 21, line 19 skipping to change at page 27, line 29
this contains the body parts of each child. this contains the body parts of each child.
In addition, the client may request/send EmailBodyPart properties In addition, the client may request/send EmailBodyPart properties
representing individual header fields, following the same syntax and representing individual header fields, following the same syntax and
semantics as for the Email object, e.g. "header:Content-Type". semantics as for the Email object, e.g. "header:Content-Type".
The following *Email* properties are specified for access to the body The following *Email* properties are specified for access to the body
data of the message: data of the message:
o *bodyStructure*: "EmailBodyPart" (immutable) This is the full MIME o *bodyStructure*: "EmailBodyPart" (immutable) This is the full MIME
structure of the message body, including sub parts but not structure of the message body, represented as an array of the
recursing into "message/rfc822" or "message/global" parts. message's top-level MIME parts, without recursing into "message/
rfc822" or "message/global" parts. Note that EmailBodyParts may
have subParts if they are of type "multipart/*".
o *bodyValues*: "String[BodyValue]" (immutable) This is a map of o *bodyValues*: "String[EmailBodyValue]" (immutable) This is a map
_partId_ to an *EmailBodyValue* object for none, some or all of _partId_ to an *EmailBodyValue* object for none, some or all
"text/*" parts. Which parts are included and whether the value is "text/*" parts. Which parts are included and whether the value is
truncated is determined by various arguments to _Email/get_ and truncated is determined by various arguments to _Email/get_ and
_Email/parse_. An *EmailBodyValue* object has the following _Email/parse_. An *EmailBodyValue* object has the following
properties: properties:
* *value*: "String" The value of the body part after decoding * *value*: "String" The value of the body part after decoding
_Content-Transport-Encoding_ and decoding the _Content-Type_ _Content-Transport-Encoding_ and decoding the _Content-Type_
charset, if known to the server, and with any CRLF replaced charset, if known to the server, and with any CRLF replaced
with a single LF. The server MAY use heuristics to determine with a single LF. The server MAY use heuristics to determine
the charset to use for decoding if the charset is unknown, or the charset to use for decoding if the charset is unknown, or
if no charset is given, or if it believes the charset given is if no charset is given, or if it believes the charset given is
incorrect. Decoding is best-effort and SHOULD insert the incorrect. Decoding is best-effort and SHOULD insert the
unicode replacement character (U+FFFD) and continue when a unicode replacement character (U+FFFD) and continue when a
malformed section is encountered. malformed section is encountered. Note that due to the charset
decoding and line ending normalisation, the length of this
string will probably not be exactly the same as the _size_
property on the corresponding EmailBodyPart.
* *isEncodingProblem*: "Boolean" (default: "false") This is * *isEncodingProblem*: "Boolean" (default: "false") This is
"true" if malformed sections were found while decoding the "true" if malformed sections were found while decoding the
charset, or the charset was unknown. charset, or the charset was unknown.
* *isTruncated*: "Boolean" (default: "false") This is "true" if * *isTruncated*: "Boolean" (default: "false") This is "true" if
the _value_ has been truncated. the _value_ has been truncated.
See the security considerations section for issues related to See the security considerations section for issues related to
truncation and heuristic determination of content-type and truncation and heuristic determination of content-type and
skipping to change at page 22, line 12 skipping to change at page 28, line 26
o *textBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", o *textBody*: "EmailBodyPart[]" (immutable) A list of "text/plain",
"text/html", "image/*", "audio/*" and/or "video/*" parts to "text/html", "image/*", "audio/*" and/or "video/*" parts to
display (sequentially) as the message body, with a preference for display (sequentially) as the message body, with a preference for
"text/plain" when alternative versions are available. "text/plain" when alternative versions are available.
o *htmlBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", o *htmlBody*: "EmailBodyPart[]" (immutable) A list of "text/plain",
"text/html", "image/*", "audio/*" and/or "video/*" parts to "text/html", "image/*", "audio/*" and/or "video/*" parts to
display (sequentially) as the message body, with a preference for display (sequentially) as the message body, with a preference for
"text/html" when alternative versions are available. "text/html" when alternative versions are available.
o *attachedEmails*: "EmailBodyPart[]" (immutable) A list of all o *attachments*: "EmailBodyPart[]" (immutable) A list of all parts
parts of type "message/rfc822" or "message/global". Note, this
*does not* recurse, so the parts within these are not included.
The attached message may be fetched using the Email/parse method
and the blobId.
o *attachedFiles*: "EmailBodyPart[]" (immutable) A list of all parts
in _bodyStructure_, traversing depth-first, which satisfy either in _bodyStructure_, traversing depth-first, which satisfy either
of the following conditions: of the following conditions:
* not of type "multipart/*" and not included in _attachedEmails_, * not of type "multipart/*" and not included in _textBody_ or
_textBody_ or _htmlBody_ _htmlBody_
* of type "image/*", "audio/*" or "video/*" and not in both * of type "image/*", "audio/*" or "video/*" and not in both
_textBody_ and _htmlBody_ _textBody_ and _htmlBody_
Note, an HTML body part may reference image parts in attachedFiles None of these parts include subParts, including "message/*" types.
using "cid:" links to reference the _Content-Id_ or by referencing Attached messages may be fetched using the Email/parse method and
the _Content-Location_. the blobId. Note, an HTML body part may reference image parts in
attachments using "cid:" links to reference the _Content-Id_ or by
referencing the _Content-Location_.
o *hasAttachment*: "Boolean" (immutable; server-set) This is "true" o *hasAttachment*: "Boolean" (immutable; server-set) This is "true"
if there are one or more parts in the message that a client UI if there are one or more parts in the message that a client UI
should offer as downloadable. A server SHOULD set hasAttachment should offer as downloadable. A server SHOULD set hasAttachment
if either: if either:
* The _attachedEmails_ list contains at least one item. * The _attachments_ list contains at least one item that does not
have "Content-Disposition: inline". The server MAY ignore
* The _attachedFiles_ list contains at least one item that does
not have "Content-Disposition: inline". The server MAY ignore
parts in this list that are processed automatically in some parts in this list that are processed automatically in some
way, or are referenced as embedded images in one of the "text/ way, or are referenced as embedded images in one of the "text/
html" parts of the message. html" parts of the message.
The server MAY set hasAttachment based on implementation-defined The server MAY set hasAttachment based on implementation-defined
or site configurable heuristics. or site configurable heuristics.
o *preview*: "String" (immutable; server-set) Up to 255 octets of o *preview*: "String" (immutable; server-set) Up to 255 octets of
plain text, summarising the message body. This is intended to be plain text, summarising the message body. This is intended to be
shown as a preview line on a mailbox listing, and may be truncated shown as a preview line on a mailbox listing, and may be truncated
when shown. The server may choose which part of the message to when shown. The server may choose which part of the message to
include in the preview, for example skipping quoted sections and include in the preview, for example skipping quoted sections and
salutations and collapsing white-space can result in a more useful salutations and collapsing white-space can result in a more useful
preview. preview.
MIME structures are arbitrary nested trees of documents, but the
majority of email clients present a model of an email body (normally
plain text or HTML), with a set of attachments. Interpreting the
MIME structure to form this flat model represents considerable
difficulty and causes inconsistency between clients. Therefore in
addition to the _bodyStructure_ property, which gives the full tree,
the Email object contains 4 alternate properties with flat lists of
body parts:
o _textBody_/_htmlBody_: These provide a list of parts that should
be rendered as the "body" of the message. This is a list rather
than a single part as messages may have headers and/or footers
appended/prepended as separate parts as they are transmitted, and
some clients send text and images, or even videos and sound clips,
intended to be displayed inline in the body as multiple parts
rather than a single HTML part with referenced images.
Because MIME allows for multiple representations of the same data
(using "multipart/alternative"), there is a textBody property (which
prefers a plain text representation) and an htmlBody property (which
prefers an HTML representation) to accommodate the two most common
client requirements. The same part may appear in both lists where
there is no alternative between the two.
o _attachedEmails_/_attachedFiles_: These provide a list of parts
that should be presented as "attachments" to the message. Emails
are presented in a separate list so their contents may be easily
fetched via a back-reference with the "Email/parse" method in the
same request, if the client wishes to. Some images in
attachedFiles may be solely there for embedding within an HTML
body part; clients may wish to not present these as attachments in
the user interface if they are displaying the HTML with the
embedded images directly. Some parts may also be in htmlBody/
textBody; again, clients may wish to not present these as
attachments in the user interface if rendered as part of the body.
The _bodyValues_ property allows for clients to fetch the value of
text parts directly without having to do a second request for the
blob, and have the server handle decoding the charset into unicode.
This data is in a separate property rather than on the EmailBodyPart
object to avoid duplication of large amounts of data, as the same
part may be included twice if the client fetches more than one of
bodyStructure, textBody and htmlBody.
The exact algorithm for decomposing bodyStructure into textBody, The exact algorithm for decomposing bodyStructure into textBody,
htmlBody, attachedEmails and attachedFiles part lists is not htmlBody and attachments part lists is not mandated, as this is a
mandated, as this is a quality-of-service implementation issue and quality-of-service implementation issue and likely to require
likely to require workarounds for malformed content discovered over workarounds for malformed content discovered over time. However, the
time. However, the following algorithm (expressed here in following algorithm (expressed here in JavaScript) is suggested as a
JavaScript) is suggested as a starting point, based on real-world starting point, based on real-world experience:
experience:
function isInlineMediaType ( type ) { function isInlineMediaType ( type ) {
return type.startsWith( 'image/' ) || return type.startsWith( 'image/' ) ||
type.startsWith( 'audio/' ) || type.startsWith( 'audio/' ) ||
type.startsWith( 'video/' ); type.startsWith( 'video/' );
} }
function parseStructure ( parts, multipartType, inAlternative, function parseStructure ( parts, multipartType, inAlternative,
htmlBody, textBody, attachedEmails, attachedFiles ) { htmlBody, textBody, attachments ) {
// For multipartType == alternative // For multipartType == alternative
let textLength = textBody ? textBody.length : -1; let textLength = textBody ? textBody.length : -1;
let htmlLength = htmlBody ? htmlBody.length : -1; let htmlLength = htmlBody ? htmlBody.length : -1;
for ( let i = 0; i < parts.length; i += 1 ) { for ( let i = 0; i < parts.length; i += 1 ) {
let part = parts[i]; let part = parts[i];
let isMultipart = part.type.startsWith( 'multipart/' ); let isMultipart = part.type.startsWith( 'multipart/' );
// Is this a body part rather than an attachment // Is this a body part rather than an attachment
let isInline = part.disposition != "attachment" && let isInline = part.disposition != "attachment" &&
// Must be one of the allowed body types // Must be one of the allowed body types
( part.type == "text/plain" || ( part.type == "text/plain" ||
part.type == "text/html" || part.type == "text/html" ||
isInlineMediaType( part.type ) && isInlineMediaType( part.type ) ) &&
// If multipart/related, only the first part can be inline // If multipart/related, only the first part can be inline
// If a text part with a filename, and not the first item in the // If a text part with a filename, and not the first item in the
// multipart, assume it is an attachment // multipart, assume it is an attachment
( i === 0 || ( i === 0 ||
( multipartType != "related" && ( multipartType != "related" &&
( isInlineMediaType( part.type ) || !part.name ) ) ); ( isInlineMediaType( part.type ) || !part.name ) ) );
if ( isMultipart ) { if ( isMultipart ) {
let subMultiType = part.type.split( '/' )[1]; let subMultiType = part.type.split( '/' )[1];
parseStructure( part.subParts, subMultiType, parseStructure( part.subParts, subMultiType,
inAlternative || ( subMultiType == 'alternative' ), inAlternative || ( subMultiType == 'alternative' ),
htmlBody, textBody, attachedEmails, attachedFiles ); htmlBody, textBody, attachments );
} else if ( isInline ) { } else if ( isInline ) {
if ( multipartType == 'alternative' ) { if ( multipartType == 'alternative' ) {
switch ( part.type ) { switch ( part.type ) {
case 'text/plain': case 'text/plain':
textBody.push( part ); textBody.push( part );
break; break;
case 'text/html': case 'text/html':
htmlBody.push( part ); htmlBody.push( part );
break; break;
default: default:
attachedFiles.push( part ); attachments.push( part );
break; break;
} }
continue; continue;
} else if ( inAlternative ) { } else if ( inAlternative ) {
if ( part.type == 'text/plain' ) { if ( part.type == 'text/plain' ) {
htmlBody = null; htmlBody = null;
} }
if ( part.type == 'text/html' ) { if ( part.type == 'text/html' ) {
textBody = null; textBody = null;
} }
} }
if ( textBody ) { if ( textBody ) {
textBody.push( part ); textBody.push( part );
} }
if ( htmlBody ) { if ( htmlBody ) {
htmlBody.push( part ); htmlBody.push( part );
} }
if ( ( !textBody || !htmlBody ) && if ( ( !textBody || !htmlBody ) &&
isInlineMediaType( part.type ) { isInlineMediaType( part.type ) ) {
attachedFiles.push( part ); attachments.push( part );
} }
} else if ( part.type == 'message/rfc822' ||
part.type == 'message/global' ) {
attachedEmails.push( part );
} else { } else {
attachedFiles.push( part ); attachments.push( part );
} }
}
if ( multipartType == 'alternative' ) { if ( multipartType == 'alternative' && textBody && htmlBody ) {
// Found HTML part only // Found HTML part only
if ( textBody && textLength == textBody.length && if ( textLength == textBody.length &&
htmlLength != htmlBody.length ) { htmlLength != htmlBody.length ) {
for ( let i = htmlLength; i < htmlBody.length; i += 1 ) { for ( let i = htmlLength; i < htmlBody.length; i += 1 ) {
textBody.push( htmlBody[i] ); textBody.push( htmlBody[i] );
}
}
// Found plain text part only
if ( htmlBody && htmlLength == htmlBody.length &&
textLength != textBody.length ) {
for ( let i = textLength; i < textBody.length; i += 1 ) {
htmlBody.push( textBody[i] );
}
} }
} }
// Found plain text part only
if ( htmlLength == htmlBody.length &&
textLength != textBody.length ) {
for ( let i = textLength; i < textBody.length; i += 1 ) {
htmlBody.push( textBody[i] );
}
}
} }
} }
// Usage: // Usage:
let htmlBody = []; let htmlBody = [];
let textBody = []; let textBody = [];
let attachedEmails = []; let attachments = [];
let attachedFiles = [];
parseStructure( [ bodyStructure ], 'mixed', false, parseStructure( [ bodyStructure ], 'mixed', false,
htmlBody, textBody, attachedEmails, attachedFiles ); htmlBody, textBody, attachments );
For instance, consider a message with both text and html versions For instance, consider a message with both text and html versions
that's then gone through a list software manager that attaches a that's then gone through a list software manager that attaches a
header/footer. It might have a MIME structure something like: header/footer. It might have a MIME structure something like:
multipart/mixed multipart/mixed
text/plain, content-disposition=inline - A text/plain, content-disposition=inline - A
multipart/mixed multipart/mixed
multipart/alternative multipart/alternative
multipart/mixed multipart/mixed
skipping to change at page 26, line 40 skipping to change at page 31, line 46
multipart/related multipart/related
text/html - E text/html - E
image/jpeg - F image/jpeg - F
image/jpeg, content-disposition=attachment - G image/jpeg, content-disposition=attachment - G
application/x-excel - H application/x-excel - H
message/rfc822 - J message/rfc822 - J
text/plain, content-disposition=inline - K text/plain, content-disposition=inline - K
In this case, the above algorithm would decompose this to: In this case, the above algorithm would decompose this to:
textBody => [ A, B, C, D, K ] textBody => [ A, B, C, D, K ]
htmlBody => [ A, E, K ] htmlBody => [ A, E, K ]
attachedEmails: [ J ] attachments => [ C, F, G, H, J ]
attachedFiles => [ C, F, G, H ]
4.2. Email/get 4.2. Email/get
Standard _/get_ method, with the following additional arguments: Standard _/get_ method, with the following additional arguments:
o *bodyProperties*: "String[]" (optional) A list of properties to o *bodyProperties*: "String[]" (optional) A list of properties to
fetch for each EmailBodyPart returned. If omitted, this defaults fetch for each EmailBodyPart returned. If omitted, this defaults
to: [ "partId", "blobId", "size", "name", "type", "charset", to: [ "partId", "blobId", "size", "name", "type", "charset",
"disposition", cid", "language", "location" ] "disposition", cid", "language", "location" ]
skipping to change at page 27, line 34 skipping to change at page 32, line 43
in valid UTF-8 and does not occur mid-codepoint. If the part is in valid UTF-8 and does not occur mid-codepoint. If the part is
of type "text/html", the server SHOULD NOT truncate inside an HTML of type "text/html", the server SHOULD NOT truncate inside an HTML
tag e.g. in the middle of "<a href="https://example.com">". There tag e.g. in the middle of "<a href="https://example.com">". There
is no requirement for the truncated form to be a balanced tree or is no requirement for the truncated form to be a balanced tree or
valid HTML (indeed, the original source may well be neither of valid HTML (indeed, the original source may well be neither of
these things). these things).
If the standard _properties_ argument is omitted or "null", the If the standard _properties_ argument is omitted or "null", the
following default MUST be used instead of "all" properties: following default MUST be used instead of "all" properties:
[ "id", "blobId", "threadId", "mailboxIds", "keywords", "size", "receivedAt", "messageId", "inReplyTo", "references", "sender", "from", "to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment", "preview", "bodyValues", "textBody", "htmlBody", "attachedFiles", "attachedEmails" ] [ "id", "blobId", "threadId", "mailboxIds", "keywords", "size",
"receivedAt", "messageId", "inReplyTo", "references", "sender", "from",
"to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment",
"preview", "bodyValues", "textBody", "htmlBody", "attachments" ]
The following properties are expected to be fast to fetch in a The following properties are expected to be fast to fetch in a
quality implementation: quality implementation:
o id o id
o blobId o blobId
o threadId o threadId
o mailboxIds o mailboxIds
o keywords o keywords
o size o size
skipping to change at page 29, line 4 skipping to change at page 34, line 16
Request: Request:
["Email/get", { ["Email/get", {
"ids": [ "f123u456", "f123u457" ], "ids": [ "f123u456", "f123u457" ],
"properties": [ "threadId", "mailboxIds", "from", "subject", "receivedAt", "header:List-POST:asURLs" "htmlBody", "bodyValues" ], "properties": [ "threadId", "mailboxIds", "from", "subject", "receivedAt", "header:List-POST:asURLs" "htmlBody", "bodyValues" ],
"bodyProperties": [ "partId", "blobId", "size", "type" ], "bodyProperties": [ "partId", "blobId", "size", "type" ],
"fetchHTMLBodyValues": true, "fetchHTMLBodyValues": true,
"maxBodyValueBytes": 256 "maxBodyValueBytes": 256
}, "#1"] }, "#1"]
and response: and response:
["Email/get", { ["Email/get", {
"accountId": "abc", "accountId": "abc",
"state": "41234123231", "state": "41234123231",
"list": [ "list": [
{ {
"id": "f123u457", "id": "f123u457",
"threadId": "ef1314a", "threadId": "ef1314a",
"mailboxIds": { "f123": true }, "mailboxIds": { "f123": true },
"from": [{name: "Joe Bloggs", email: "joe@bloggs.com"}], "from": [{name: "Joe Bloggs", email: "joe@bloggs.com"}],
"subject": "Dinner on Thursday?", "subject": "Dinner on Thursday?",
"receivedAt": "2013-10-13T14:12:00Z", "receivedAt": "2013-10-13T14:12:00Z",
"header:List-POST:asURLs": "mailto:partytime@lists.example.com", "header:List-POST:asURLs": [ "mailto:partytime@lists.example.com" ],
"htmlBody": [{ "htmlBody": [{
"partId": "1", "partId": "1",
"blobId": "841623871", "blobId": "841623871",
"size": 283331, "size": 283331,
"type": "text/html" "type": "text/html"
}, { }, {
"partId": "2", "partId": "2",
"blobId": "319437193", "blobId": "319437193",
"size": 10343, "size": 10343,
"type": "text/plain" "type": "text/plain"
}], }],
"bodyValues": { "bodyValues": {
"1": { "1": {
"isEncodingProblem": false, "isEncodingProblem": false,
"isTruncated": true, "isTruncated": true,
"value": "<html><body><p>Hello ..." "value": "<html><body><p>Hello ..."
}, },
"2": { "2": {
"isEncodingProblem": false, "isEncodingProblem": false,
"isTruncated": false, "isTruncated": false,
"value": "-- \nSent by your friendly mailing list ..." "value": "-- \nSent by your friendly mailing list ..."
}
} }
} }
], }
notFound: [ "f123u456" ] ],
}, "#1"] notFound: [ "f123u456" ]
}, "#1"]
4.3. Email/changes 4.3. Email/changes
Standard _/changes_ method. Standard _/changes_ method.
4.4. Email/query 4.4. Email/query
Standard _/query_ method, but with the following additional Standard _/query_ method, but with the following additional
arguments: arguments:
skipping to change at page 31, line 17 skipping to change at page 37, line 17
o *notKeyword*: "String" This email must not have the given keyword o *notKeyword*: "String" This email must not have the given keyword
to match the condition. to match the condition.
o *hasAttachment*: "Boolean" The "hasAttachment" property of the o *hasAttachment*: "Boolean" The "hasAttachment" property of the
email must be identical to the value given to match the condition. email must be identical to the value given to match the condition.
o *text*: "String" Looks for the text in emails. The server SHOULD o *text*: "String" Looks for the text in emails. The server SHOULD
look up text in the _from_, _to_, _cc_, _bcc_, _subject_ header look up text in the _from_, _to_, _cc_, _bcc_, _subject_ header
fields of the message, and inside any "text/*" or other body parts fields of the message, and inside any "text/*" or other body parts
that may converted to text by the server. The server MAY extend that may be converted to text by the server. The server MAY
the search to any additional textual property. extend the search to any additional textual property.
o *from*: "String" Looks for the text in the _From_ header field of o *from*: "String" Looks for the text in the _From_ header field of
the message. the message.
o *to*: "String" Looks for the text in the _To_ header field of the o *to*: "String" Looks for the text in the _To_ header field of the
message. message.
o *cc*: "String" Looks for the text in the _Cc_ header field of the o *cc*: "String" Looks for the text in the _Cc_ header field of the
message. message.
o *bcc*: "String" Looks for the text in the _Bcc_ header field of o *bcc*: "String" Looks for the text in the _Bcc_ header field of
the message. the message.
o *subject*: "String" Looks for the text in the _subject_ property o *subject*: "String" Looks for the text in the _subject_ property
of the email. of the email.
o *body*: "String" Looks for the text in one of the "text/*" body o *body*: "String" Looks for the text in one of the "text/*" body
parts of the email. parts of the email.
o *attachments*: "String" Looks for the text in the attachments of o *attachments*: "String" Looks for the text in the attachments of
the email. Server MAY handle text extraction when possible for the email. Servers MAY handle text extraction when possible for
the different kinds of media. the different kinds of media.
o *header*: "String[]" The array MUST contain either one or two o *header*: "String[]" The array MUST contain either one or two
elements. The first element is the name of the header field to elements. The first element is the name of the header field to
match against. The second (optional) element is the text to look match against. The second (optional) element is the text to look
for in the header field value. If not supplied, the message for in the header field value. If not supplied, the message
matches simply if it _has_ a header field of the given name. matches simply if it _has_ a header field of the given name.
If zero properties are specified on the FilterCondition, the If zero properties are specified on the FilterCondition, the
condition MUST always evaluate to "true". If multiple properties are condition MUST always evaluate to "true". If multiple properties are
skipping to change at page 35, line 20 skipping to change at page 41, line 20
field (e.g. "header:from" and "from") within the Email or field (e.g. "header:from" and "from") within the Email or
particular EmailBodyPart. particular EmailBodyPart.
o Header fields MUST NOT be specified in parsed forms that are o Header fields MUST NOT be specified in parsed forms that are
forbidden for that particular field. forbidden for that particular field.
o Header fields beginning "Content-" MUST NOT be specified on the o Header fields beginning "Content-" MUST NOT be specified on the
Email object, only on EmailBodyPart objects. Email object, only on EmailBodyPart objects.
o If a bodyStructure property is given, there MUST NOT be textBody, o If a bodyStructure property is given, there MUST NOT be textBody,
htmlBody, attachedFiles or attachedEmails properties. htmlBody or attachments properties.
o If given, the bodyStructure EmailBodyPart MUST NOT contain a o If given, the bodyStructure EmailBodyPart MUST NOT contain a
property representing a header field that is already defined on property representing a header field that is already defined on
the top-level Email object. the top-level Email object.
o If given, textBody MUST contain exactly one body part, of type o If given, textBody MUST contain exactly one body part, of type
"text/plain". "text/plain".
o If given, htmlBody MUST contain exactly one body part, of type o If given, htmlBody MUST contain exactly one body part, of type
"text/html". "text/html".
skipping to change at page 37, line 12 skipping to change at page 43, line 12
o "tooManyMailboxes": The change to the email's mailboxes would o "tooManyMailboxes": The change to the email's mailboxes would
exceed a server-defined maximum. exceed a server-defined maximum.
4.7. Email/import 4.7. Email/import
The _Email/import_ method adds [RFC5322] messages to a user's set of The _Email/import_ method adds [RFC5322] messages to a user's set of
emails. The messages must first be uploaded as a file using the emails. The messages must first be uploaded as a file using the
standard upload mechanism. It takes the following arguments: standard upload mechanism. It takes the following arguments:
o *accountId*: "String|null" The id of the account to use for this o *accountId*: "String|null" The id of the account to use for this
call. If "null", defaults to the primary account. call. If "null", defaults to the "urn:ietf:params:jmap:mail"
primary account.
o *emails*: "String[EmailImport]" A map of creation id (client o *emails*: "String[EmailImport]" A map of creation id (client
specified) to EmailImport objects specified) to EmailImport objects
An *EmailImport* object has the following properties: An *EmailImport* object has the following properties:
o *blobId*: "String" The id of the blob containing the raw [RFC5322] o *blobId*: "String" The id of the blob containing the raw [RFC5322]
message. message.
o *mailboxIds* "String[Boolean]" The ids of the mailbox(es) to o *mailboxIds* "String[Boolean]" The ids of the mailboxes to assign
assign this email to. At least one mailbox MUST be given. this email to. At least one mailbox MUST be given.
o *keywords*: "String[Boolean]" (default: "{}") The keywords to o *keywords*: "String[Boolean]" (default: "{}") The keywords to
apply to the email. apply to the email.
o *receivedAt*: "UTCDate" (default: time of import on server) The o *receivedAt*: "UTCDate" (default: time of import on server) The
_receivedAt_ date to set on the email. _receivedAt_ date to set on the email.
Each email to import is considered an atomic unit which may succeed Each email to import is considered an atomic unit which may succeed
or fail individually. Importing successfully creates a new email or fail individually. Importing successfully creates a new email
object from the data reference by the blobId and applies the given object from the data reference by the blobId and applies the given
skipping to change at page 38, line 34 skipping to change at page 44, line 37
copy them using the _Email/copy_ method, then once the copy has copy them using the _Email/copy_ method, then once the copy has
succeeded, delete the original. The _onSuccessDestroyOriginal_ succeeded, delete the original. The _onSuccessDestroyOriginal_
argument allows you to try to do this in one method call, however argument allows you to try to do this in one method call, however
note that the two different actions are not atomic, and so it is note that the two different actions are not atomic, and so it is
possible for the copy to succeed but the original not to be destroyed possible for the copy to succeed but the original not to be destroyed
for some reason. for some reason.
The _Email/copy_ method takes the following arguments: The _Email/copy_ method takes the following arguments:
o *fromAccountId*: "String|null" The id of the account to copy o *fromAccountId*: "String|null" The id of the account to copy
emails from. If "null", defaults to the primary account. emails from. If "null", defaults to the
"urn:ietf:params:jmap:mail" primary account.
o *toAccountId*: "String|null" The id of the account to copy emails o *toAccountId*: "String|null" The id of the account to copy emails
to. If "null", defaults to the primary account. to. If "null", defaults to the "urn:ietf:params:jmap:mail"
primary account.
o *create*: "String[EmailCopy]" A map of _creation id_ to an o *create*: "String[EmailCopy]" A map of _creation id_ to an
EmailCopy object. EmailCopy object.
o *onSuccessDestroyOriginal*: "Boolean" (default: "false") If o *onSuccessDestroyOriginal*: "Boolean" (default: "false") If
"true", an attempt will be made to destroy the emails that were "true", an attempt will be made to destroy the emails that were
successfully copied: after emitting the _Email/copy_ response, but successfully copied: after emitting the _Email/copy_ response, but
before processing the next method, the server MUST make a single before processing the next method, the server MUST make a single
call to _Email/set_ to destroy the original of each successfully call to _Email/set_ to destroy the original of each successfully
copied message; the output of this is added to the responses as copied message; the output of this is added to the responses as
skipping to change at page 40, line 9 skipping to change at page 46, line 14
"alreadyExists": The server forbids duplicates and the email already "alreadyExists": The server forbids duplicates and the email already
exists in the target account. An _emailId_ property of type "String" exists in the target account. An _emailId_ property of type "String"
MUST be included on the error object with the id of the existing MUST be included on the error object with the id of the existing
email. email.
The following additional errors may be returned instead of the The following additional errors may be returned instead of the
_Email/copy_ response: _Email/copy_ response:
"fromAccountNotFound": A _fromAccountId_ was explicitly included with "fromAccountNotFound": A _fromAccountId_ was explicitly included with
the request, but it does not correspond to a valid account. the request, but it does not correspond to a valid account; or,
_fromAccountId_ was null but there is no primary account for
"urn:ietf:params:jmap:mail".
"toAccountNotFound": A _toAccountId_ was explicitly included with the "toAccountNotFound": A _toAccountId_ was explicitly included with the
request, but it does not correspond to a valid account. request, but it does not correspond to a valid account; or,
_toAccountId_ was null but there is no primary account for
"urn:ietf:params:jmap:mail".
"fromAccountNotSupportedByMethod": The _fromAccountId_ given "fromAccountNotSupportedByMethod": The _fromAccountId_ given
corresponds to a valid account, but does not contain any mail data. corresponds to a valid account, but does not contain any mail data.
"toAccountNotSupportedByMethod": The _toAccountId_ given corresponds "toAccountNotSupportedByMethod": The _toAccountId_ given corresponds
to a valid account, but does not contain any mail data. to a valid account, but does not contain any mail data.
4.9. Email/parse 4.9. Email/parse
This method allows you to parse blobs as [RFC5322] messages to get This method allows you to parse blobs as [RFC5322] messages to get
skipping to change at page 40, line 50 skipping to change at page 47, line 10
o *accountId*: "String|null" The id of the Account to use. If o *accountId*: "String|null" The id of the Account to use. If
"null", the primary account is used. "null", the primary account is used.
o *blobIds*: "String[]" The ids of the blobs to parse. o *blobIds*: "String[]" The ids of the blobs to parse.
o *properties*: "String[]" If supplied, only the properties listed o *properties*: "String[]" If supplied, only the properties listed
in the array are returned for each Email object. If omitted, in the array are returned for each Email object. If omitted,
defaults to: [ "messageId", "inReplyTo", "references", "sender", defaults to: [ "messageId", "inReplyTo", "references", "sender",
"from", "to", "cc", "bcc", "replyTo", "subject", "sentAt", "from", "to", "cc", "bcc", "replyTo", "subject", "sentAt",
"hasAttachment", "preview", "bodyValues", "textBody", "htmlBody", "hasAttachment", "preview", "bodyValues", "textBody", "htmlBody",
"attachedFiles", "attachedEmails" ] "attachments" ]
o *bodyProperties*: "String[]" (optional) A list of properties to o *bodyProperties*: "String[]" (optional) A list of properties to
fetch for each EmailBodyPart returned. If omitted, defaults to fetch for each EmailBodyPart returned. If omitted, defaults to
the same value as the Email/get "bodyProperties" default argument. the same value as the Email/get "bodyProperties" default argument.
o *fetchTextBodyValues*: "Boolean" (default: "false") If "true", the o *fetchTextBodyValues*: "Boolean" (default: "false") If "true", the
_bodyValues_ property includes any "text/*" part in the "textBody" _bodyValues_ property includes any "text/*" part in the "textBody"
property. property.
o *fetchHTMLBodyValues*: "Boolean" (default: "false") If "true", the o *fetchHTMLBodyValues*: "Boolean" (default: "false") If "true", the
skipping to change at page 42, line 9 skipping to change at page 48, line 14
As specified above, parsed forms of headers may only be used on As specified above, parsed forms of headers may only be used on
appropriate header fields. Attempting to fetch a form that is appropriate header fields. Attempting to fetch a form that is
forbidden (e.g. "header:From:asDate") MUST result in the method call forbidden (e.g. "header:From:asDate") MUST result in the method call
being rejected with an "invalidArguments" error. being rejected with an "invalidArguments" error.
Where a specific header is requested as a property, the Where a specific header is requested as a property, the
capitalization of the property name in the response MUST be identical capitalization of the property name in the response MUST be identical
to that used in the request. to that used in the request.
5. Email submission 5. Identities
An *Identity* object stores information about an email address (or
domain) the user may send from. It has the following properties:
o *id*: "String" (immutable; server-set) The id of the identity.
o *name*: "String" (default: """") The "From" _name_ the client
SHOULD use when creating a new message from this identity.
o *email*: "String" (immutable) The "From" email address the client
MUST use when creating a new message from this identity. The
value MAY alternatively be of the form "*@example.com", in which
case the client may use any valid email address ending in
"@example.com".
o *replyTo*: "EmailAddress[]|null" (default: "null") The Reply-To
value the client SHOULD set when creating a new message from this
identity.
o *bcc*: "EmailAddress[]|null" (default: "null") The Bcc value the
client SHOULD set when creating a new message from this identity.
o *textSignature*: "String" (default: """") Signature the client
SHOULD insert into new plain-text messages that will be sent from
this identity. Clients MAY ignore this and/or combine this with a
client-specific signature preference.
o *htmlSignature*: "String" (default: """") Signature the client
SHOULD insert into new HTML messages that will be sent from this
identity. This text MUST be an HTML snippet to be inserted into
the "<body></body>" section of the new email. Clients MAY ignore
this and/or combine this with a client-specific signature
preference.
o *mayDelete*: "Boolean" (server-set) Is the user allowed to delete
this identity? Servers may wish to set this to "false" for the
user's username or other default address.
See the "Addresses" header form description in the Email object for
the definition of _EmailAddress_.
Multiple identities with the same email address MAY exist, to allow
for different settings the user wants to pick between (for example
with different names/signatures).
The following JMAP methods are supported:
5.1. Identity/get
Standard _/get_ method. The _ids_ argument may be "null" to fetch
all at once.
5.2. Identity/changes
Standard _/changes_ method.
5.3. Identity/set
Standard _/set_ method. The following extra _SetError_ types are
defined:
For *create*:
o "maxQuotaReached": The user has reached a server-defined limit on
the number of identities.
o "emailNotPermitted": The user is not allowed to send from the
address given as the _email_ property of the identity.
For *destroy*:
o "forbidden": Returned if the identity's _mayDelete_ value is
"false".
5.4. Example
Request:
[ "Identity/get", {}, "0" ]
with response:
[ "Identity/get", {
"accountId": "acme",
"state": "99401312ae-11-333",
"list": [
{
"id": "3301-222-11_22AAz",
"name": "Joe Bloggs",
"email": "joe@example.com",
"replyTo": null,
"bcc": [{
"name": null,
"email": "joe+archive@example.com"
}],
"textSignature": "-- \nJoe Bloggs\nMaster of Email",
"htmlSignature": "<div><b>Joe Bloggs</b></div><div>Master of Email</div>",
"mayDelete": false,
},
{
"id": "9911312-11_22AAz",
"name": "Joe B",
"email": "joebloggs@example.com",
"replyTo": null,
"bcc": null,
"textSignature": "",
"htmlSignature": "",
"mayDelete": true
}
],
"notFound": []
}, "0" ]
6. Email submission
An *EmailSubmission* object represents the submission of an email for An *EmailSubmission* object represents the submission of an email for
delivery to one or more recipients. It has the following properties: delivery to one or more recipients. It has the following properties:
o *id*: "String" (immutable; server-set) The id of the email o *id*: "String" (immutable; server-set) The id of the email
submission. submission.
o *identityId*: "String" (immutable) The id of the identity to o *identityId*: "String" (immutable) The id of the identity to
associate with this submission. associate with this submission.
skipping to change at page 42, line 48 skipping to change at page 51, line 29
Servers that do this MAY replace a client-provided value for Servers that do this MAY replace a client-provided value for
ENVID with a server-provided value. ENVID with a server-provided value.
* *rcptTo*: "Address[]" The email addresses to send the message * *rcptTo*: "Address[]" The email addresses to send the message
to, and any RCPT TO parameters to pass with the recipient. to, and any RCPT TO parameters to pass with the recipient.
An *Address* object has the following properties: An *Address* object has the following properties:
* *email*: "String" The email address being represented by the * *email*: "String" The email address being represented by the
object. This as a "Mailbox" as used in the Reverse-path or object. This as a "Mailbox" as used in the Reverse-path or
Foward-path of the MAIL FROM or RCPT TO command in [RFC5321]. Forward-path of the MAIL FROM or RCPT TO command in [RFC5321].
* *parameters*: "Object|null" Any parameters to send with the * *parameters*: "Object|null" Any parameters to send with the
email (either mail-parameter or rcpt-parameter as appropriate, email (either mail-parameter or rcpt-parameter as appropriate,
as specified in [RFC5321]). If supplied, each key in the as specified in [RFC5321]). If supplied, each key in the
object is a parameter name, and the value either the parameter object is a parameter name, and the value either the parameter
value (type "String") or if the parameter does not take a value value (type "String") or if the parameter does not take a value
then "null". For both name and value, any xtext or unitext then "null". For both name and value, any xtext or unitext
encodings are removed ([RFC3461], [RFC6533]) and JSON string encodings are removed ([RFC3461], [RFC6533]) and JSON string
encoding applied. encoding applied.
skipping to change at page 46, line 36 skipping to change at page 55, line 13
object. object.
For efficiency, a server MAY destroy EmailSubmission objects a For efficiency, a server MAY destroy EmailSubmission objects a
certain amount of time after the email is successfully sent or it has certain amount of time after the email is successfully sent or it has
finished retrying sending the email. For very basic SMTP proxies, finished retrying sending the email. For very basic SMTP proxies,
this MAY be immediately after creation, as it has no way to assign a this MAY be immediately after creation, as it has no way to assign a
real id and return the information again if fetched later. real id and return the information again if fetched later.
The following JMAP methods are supported: The following JMAP methods are supported:
5.1. EmailSubmission/get 6.1. EmailSubmission/get
Standard _/get_ method. Standard _/get_ method.
5.2. EmailSubmission/changes 6.2. EmailSubmission/changes
Standard _/changes_ method. Standard _/changes_ method.
5.3. EmailSubmission/query 6.3. EmailSubmission/query
Standard _/query_ method. Standard _/query_ method.
A *FilterCondition* object has the following properties, any of which A *FilterCondition* object has the following properties, any of which
may be omitted: may be omitted:
o *emailIds*: "String[]" The EmailSubmission _emailId_ property must o *emailIds*: "String[]" The EmailSubmission _emailId_ property must
be in this list to match the condition. be in this list to match the condition.
o *threadIds*: "String[]" The EmailSubmission _threadId_ property o *threadIds*: "String[]" The EmailSubmission _threadId_ property
skipping to change at page 47, line 26 skipping to change at page 56, line 4
A EmailSubmission object matches the filter if and only if all of the A EmailSubmission object matches the filter if and only if all of the
given conditions given match. If zero properties are specified, it given conditions given match. If zero properties are specified, it
is automatically "true" for all objects. is automatically "true" for all objects.
The following properties MUST be supported for sorting: The following properties MUST be supported for sorting:
o "emailId" o "emailId"
o "threadId" o "threadId"
o "sentAt" o "sentAt"
5.4. EmailSubmission/queryChanges 6.4. EmailSubmission/queryChanges
Standard _/queryChanges_ method. Standard _/queryChanges_ method.
5.5. EmailSubmission/set 6.5. EmailSubmission/set
Standard _/set_ method, with the following two extra arguments: Standard _/set_ method, with the following two extra arguments:
o *onSuccessUpdateEmail*: "String[Email]|null" A map of o *onSuccessUpdateEmail*: "String[Email]|null" A map of
_EmailSubmission id_ to an object containing properties to update _EmailSubmission id_ to an object containing properties to update
on the Email object referenced by the EmailSubmission if the on the Email object referenced by the EmailSubmission if the
create/update/destroy succeeds. (For references to create/update/destroy succeeds. (For references to
EmailSubmission creations, this is equivalent to a back reference EmailSubmission creations, this is equivalent to a back reference
so the id will be the creation id prefixed with a "#".) so the id will be the creation id prefixed with a "#".)
skipping to change at page 48, line 50 skipping to change at page 57, line 28
o "noRecipients" - The envelope (supplied or generated) does not o "noRecipients" - The envelope (supplied or generated) does not
have any rcptTo emails. have any rcptTo emails.
o "invalidRecipients" - The _rcptTo_ property of the envelope o "invalidRecipients" - The _rcptTo_ property of the envelope
(supplied or generated) contains at least one rcptTo value which (supplied or generated) contains at least one rcptTo value which
is not a valid email for sending to. An _invalidRecipients_ is not a valid email for sending to. An _invalidRecipients_
"String[]" property MUST be present on the SetError, which is a "String[]" property MUST be present on the SetError, which is a
list of the invalid addresses. list of the invalid addresses.
o "forbiddenMailFrom" - The server does not permit the user to send
an email with the [RFC5321] envelope From.
o "forbiddenFrom" - The server does not permit the user to send an o "forbiddenFrom" - The server does not permit the user to send an
email with the From header of the email to be sent. email with the [RFC5322] From header of the email to be sent.
o "forbiddenToSend" - The user does not have permission to send at o "forbiddenToSend" - The user does not have permission to send at
all right now for some reason. A _description_ "String" property all right now for some reason. A _description_ "String" property
MAY be present on the SetError object to display to the user why MAY be present on the SetError object to display to the user why
they are not permitted. The server MAY choose to localise this they are not permitted. The server MAY choose to localise this
string into the user's preferred language, if known. string into the user's preferred language, if known.
o "emailNotFound" - The _emailId_ is not a valid id for an email in o "emailNotFound" - The _emailId_ is not a valid id for an email in
the account. the account.
skipping to change at page 49, line 25 skipping to change at page 58, line 5
SetError SHOULD contain a property called _properties_ of type SetError SHOULD contain a property called _properties_ of type
"String[]" that lists *all* the properties of the email that were "String[]" that lists *all* the properties of the email that were
invalid. invalid.
For *update*: For *update*:
o "cannotUnsend": The client attempted to update the _undoStatus_ of o "cannotUnsend": The client attempted to update the _undoStatus_ of
a valid EmailSubmission object from "pending" to "canceled", but a valid EmailSubmission object from "pending" to "canceled", but
the email cannot be unsent. the email cannot be unsent.
6. Identities 6.5.1. Example
An *Identity* object stores information about an email address (or
domain) the user may send from. It has the following properties:
o *id*: "String" (immutable; server-set) The id of the identity.
o *name*: "String" (default: """") The "From" _name_ the client
SHOULD use when creating a new message from this identity.
o *email*: "String" (immutable) The "From" email address the client
MUST use when creating a new message from this identity. The
value MAY alternatively be of the form "*@example.com", in which
case the client may use any valid email address ending in
"@example.com".
o *replyTo*: "EmailAddress[]|null" (default: "null") The Reply-To
value the client SHOULD set when creating a new message from this
identity.
o *bcc*: "EmailAddress[]|null" (default: "null") The Bcc value the
client SHOULD set when creating a new message from this identity.
o *textSignature*: "String" (default: """") Signature the client
SHOULD insert into new plain-text messages that will be sent from
this identity. Clients MAY ignore this and/or combine this with a
client-specific signature preference.
o *htmlSignature*: "String" (default: """") Signature the client
SHOULD insert into new HTML messages that will be sent from this
identity. This text MUST be an HTML snippet to be inserted into
the "<body></body>" section of the new email. Clients MAY ignore
this and/or combine this with a client-specific signature
preference.
o *mayDelete*: "Boolean" (server-set) Is the user allowed to delete
this identity? Servers may wish to set this to "false" for the
user's username or other default address.
See the "Addresses" header form description in the Email object for
the definition of _EmailAddress_.
Multiple identities with the same email address MAY exist, to allow
for different settings the user wants to pick between (for example
with different names/signatures).
The following JMAP methods are supported:
6.1. Identity/get
Standard _/get_ method. The _ids_ argument may be "null" to fetch
all at once.
6.2. Identity/changes
Standard _/changes_ method.
6.3. Identity/set
Standard _/set_ method. The following extra _SetError_ types are
defined:
For *create*:
o "maxQuotaReached": The user has reached a server-defined limit on The following example presumes a draft of the message to be sent has
the number of identities. already been saved, and its Email id is "M7f6ed5bcfd7e2604d1753f6c".
This call then sends the email immediately, and if successful removes
the draft flag and moves it from the Drafts folder (which has Mailbox
id "7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e") to the Sent folder (which
we presume has Mailbox id "73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6").
o "emailNotPermitted": The user is not allowed to send from the [
address given as the _email_ property of the identity. "EmailSubmission/set",
{
"accountId": "ue411d190",
"create": {
"k1490": {
"identityId": "64588216",
"emailId": "M7f6ed5bcfd7e2604d1753f6c",
"envelope": {
"mailFrom": {
"email": "john@example.com",
"parameters": null
},
"rcptTo": [
{
"email": "jane@example.com",
"parameters": null
}
]
}
}
},
"onSuccessUpdateEmail": {
"#k1490": {
"mailboxIds/7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e": null,
"mailboxIds/73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6": true,
"keywords/$draft": null
}
}
},
"0"
]
For *destroy*: A successful response might look like this. Note there are two
responses due to the implicit Email/set call, but both have the same
tag as they are due to the same call in the request:
o "forbidden": Returned if the identity's _mayDelete_ value is [
"false". "EmailSubmission/set",
{
"accountId": "ue411d190",
"oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21,
"newState": "355421f6-8aed-4cf4-a0c4-7377e951af36",
"created": {
"k1490": {
"id": "3bab7f9a-623e-4acf-99a5-2e67facb02a0"
}
},
"notCreated": null,
"updated": null,
"notUpdated": null,
"destroyed": null,
"notDestroyed": null
},
"0"
],
[
"Email/set",
{
"accountId": "neilj@fastmail.fm",
"oldState": "778193",
"newState": "778197",
"created": null,
"notCreated": null,
"updated": {
"M7f6ed5bcfd7e2604d1753f6c": null
},
"notUpdated": null,
"destroyed": null,
"notDestroyed": null
},
"0"
]
7. Search snippets 7. Search snippets
When doing a search on a "String" property, the client may wish to 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 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 preview instead of the beginning of the message, and to highlight any
matching terms in both this and the subject of the email. Search matching terms in both this and the subject of the email. Search
snippets represent this data. snippets represent this data.
A *SearchSnippet* object has the following properties: A *SearchSnippet* object has the following properties:
skipping to change at page 51, line 51 skipping to change at page 60, line 39
property called "id". property called "id".
The following JMAP method is supported: The following JMAP method is supported:
7.1. SearchSnippet/get 7.1. SearchSnippet/get
To fetch search snippets, make a call to "SearchSnippet/get". It To fetch search snippets, make a call to "SearchSnippet/get". It
takes the following arguments: takes the following arguments:
o *accountId*: "String|null" The id of the account to use for this o *accountId*: "String|null" The id of the account to use for this
call. If "null", defaults to the primary account. call. If "null", defaults to the "urn:ietf:params:jmap:mail"
primary account.
o *emailIds*: "String[]" The list of ids of emails to fetch the
snippets for.
o *filter*: "FilterOperator|FilterCondition|null" The same filter as o *filter*: "FilterOperator|FilterCondition|null" The same filter as
passed to Email/query; see the description of this method for passed to Email/query; see the description of this method for
details. details.
o *emailIds*: "String[]" The list of ids of emails to fetch the
snippets for.
The response has the following arguments: The response has the following arguments:
o *accountId*: "String" The id of the account used for the call. o *accountId*: "String" The id of the account used for the call.
o *filter*: "FilterOperator|FilterCondition|null" Echoed back from o *filter*: "FilterOperator|FilterCondition|null" Echoed back from
the call. the call.
o *list*: "SearchSnippet[]" An array of SearchSnippet objects for o *list*: "SearchSnippet[]" An array of SearchSnippet objects for
the requested email ids. This may not be in the same order as the the requested email ids. This may not be in the same order as the
ids that were in the request. ids that were in the request.
skipping to change at page 52, line 39 skipping to change at page 61, line 28
The following additional errors may be returned instead of the The following additional errors may be returned instead of the
_searchSnippets_ response: _searchSnippets_ response:
"requestTooLarge": Returned if the number of _emailIds_ requested by "requestTooLarge": Returned if the number of _emailIds_ requested by
the client exceeds the maximum number the server is willing to the client exceeds the maximum number the server is willing to
process in a single method call. process in a single method call.
"unsupportedFilter": Returned if the server is unable to process the "unsupportedFilter": Returned if the server is unable to process the
given _filter_ for any reason. given _filter_ for any reason.
7.2. Example
Here we did an Email/query to search for any email in the account
containing the word "foo", now we are fetching the search snippets
for some of the ids that were returned in the results:
[
"SearchSnippet/get",
{
"accountId": "ue150411c",
"filter": {
"text": "foo"
},
"emailIds": [
"M44200ec123de277c0c1ce69c",
"M7bcbcb0b58d7729686e83d99",
"M28d12783a0969584b6deaac0",
...
]
},
"tag-0"
]
Example response:
[
"SearchSnippet/get", {
"accountId": "ue150411c",
"filter": {
"text": "foo"
},
"list": [{
"emailId": "M44200ec123de277c0c1ce69c"
"subject": null,
"preview": null
}, {
"emailId": "M7bcbcb0b58d7729686e83d99",
"subject": "The <mark>Foo</mark>sball competition",
"preview": "...year the <mark>foo</mark>sball competition will be held in the Stadium de ..."
}, {
"emailId": "M28d12783a0969584b6deaac0",
"subject": null,
"preview": "...mail <mark>Foo</mark>/changes results often return current-state-minus-1 rather than new..."
},
...
],
"notFound": null
},
"0"
]
8. Vacation response 8. Vacation response
The *VacationResponse* object represents the state of vacation- The *VacationResponse* object represents the state of vacation-
response related settings for an account. It has the following response related settings for an account. It has the following
properties: properties:
o *id*: "String" (immutable) The id of the object. There is only o *id*: "String" (immutable) The id of the object. There is only
ever one vacation response object, and its id is ""singleton"". ever one vacation response object, and its id is ""singleton"".
o *isEnabled* "Boolean" Should a vacation response be sent if an o *isEnabled* "Boolean" Should a vacation response be sent if an
skipping to change at page 55, line 17 skipping to change at page 65, line 12
allow an email to draw content outside of its normal bounds, allow an email to draw content outside of its normal bounds,
potentially clickjacking a real interface element. potentially clickjacking a real interface element.
o If in a webmail context and not inside a separate frame, any o If in a webmail context and not inside a separate frame, any
styles defined in CSS rules will apply to interface elements as styles defined in CSS rules will apply to interface elements as
well if the selector matches, allowing the interface to be well if the selector matches, allowing the interface to be
modified. Similarly, any interface styles that match elements in modified. Similarly, any interface styles that match elements in
the email will alter their appearance, potentially breaking the the email will alter their appearance, potentially breaking the
layout of the email. layout of the email.
o The link text in HTML has no neccessary correlation with the o The link text in HTML has no necessary correlation with the actual
actual target of the link, which can be used to make phishing target of the link, which can be used to make phishing attacks
attacks more convincing. more convincing.
o Links opened from an email or embedded external content may leak o Links opened from an email or embedded external content may leak
private info in the "Referer" header sent by default in most private info in the "Referer" header sent by default in most
systems. systems.
o Forms can be used to mimic login boxes, providing a potent o Forms can be used to mimic login boxes, providing a potent
phishing vector if allowed to submit directly from the email phishing vector if allowed to submit directly from the email
display. display.
There are a number of ways clients can mitigate these issues, and a There are a number of ways clients can mitigate these issues, and a
defence-in-depth approach that uses a combination of techniques will defence-in-depth approach that uses a combination of techniques will
provide the strongest security. provide the strongest security.
o HTML can be filtered before rendering, stripping potentially o HTML can be filtered before rendering, stripping potentially
malicious content. Sanitizing HTML correctly is tricky, and malicious content. Sanitizing HTML correctly is tricky, and
implementors are strongly recommended to use a well-tested library implementers are strongly recommended to use a well-tested library
with a carefully vetted whitelist-only approach. New features with a carefully vetted whitelist-only approach. New features
with unexpected security characteristics may be added to HTML with unexpected security characteristics may be added to HTML
rendering engines in the future; a blacklist approach is likely to rendering engines in the future; a blacklist approach is likely to
result in security issues. result in security issues.
Subtle differences in parsing of HTML can introduce security flaws: Subtle differences in parsing of HTML can introduce security flaws:
to filter with 100% accurately you need to use the same parser when to filter with 100% accurately you need to use the same parser when
sanitizing that the HTML rendering engine will use. sanitizing that the HTML rendering engine will use.
o Encapsulating the message in an "<iframe sandbox>" can help o Encapsulating the message in an "<iframe sandbox>" can help
skipping to change at page 56, line 11 skipping to change at page 66, line 5
* Disable JavaScript. * Disable JavaScript.
* Disable form submission. * Disable form submission.
* Prevent drawing outside of its bounds, or conflict with * Prevent drawing outside of its bounds, or conflict with
interface CSS. interface CSS.
* Establish a unique anonymous origin, separate to the containing * Establish a unique anonymous origin, separate to the containing
origin. origin.
o A strong Content Security Policy [4] can, among other things, o A strong Content Security Policy [3] can, among other things,
block JavaScript and loading of external content should it manage block JavaScript and loading of external content should it manage
to evade the filter. to evade the filter.
o The leakage of information in the Referer header can be mitigated o The leakage of information in the Referer header can be mitigated
with the use of a referrer policy [5]. with the use of a referrer policy [4].
o A "crossorigin=anonymous" attribute on tags that load remote o A "crossorigin=anonymous" attribute on tags that load remote
content can prevent cookies from being sent. content can prevent cookies from being sent.
o If adding "target=_blank" to open links in new tabs, also add o If adding "target=_blank" to open links in new tabs, also add
"rel=noopener" to ensure the page that opens cannot change the URL "rel=noopener" to ensure the page that opens cannot change the URL
in the original tab to redirect the user to a phishing site. in the original tab to redirect the user to a phishing site.
As highly complex software components, HTML rendering engines As highly complex software components, HTML rendering engines
increase the attack surface of a client considerably, especially when increase the attack surface of a client considerably, especially when
skipping to change at page 57, line 7 skipping to change at page 66, line 48
this, but use of an authenticated channel is recommended to limit use this, but use of an authenticated channel is recommended to limit use
of that extension to explicitly authorized proxies. JMAP servers of that extension to explicitly authorized proxies. JMAP servers
that proxy to an SMTP Submission server SHOULD allow use of the that proxy to an SMTP Submission server SHOULD allow use of the
_submissions_ port [RFC8314] and SHOULD implement SASL PLAIN over TLS _submissions_ port [RFC8314] and SHOULD implement SASL PLAIN over TLS
[RFC4616] and/or TLS client certificate authentication with SASL [RFC4616] and/or TLS client certificate authentication with SASL
EXTERNAL [RFC4422] appendix A. Implementation of a mechanism similar EXTERNAL [RFC4422] appendix A. Implementation of a mechanism similar
to SMTP XCLIENT is strongly encouraged. to SMTP XCLIENT is strongly encouraged.
In the event the JMAP server directly relays mail to SMTP servers in In the event the JMAP server directly relays mail to SMTP servers in
other administrative domains, then implementation of the de-facto other administrative domains, then implementation of the de-facto
milter protocol is strongly encouranged to integrate with third-party milter protocol is strongly encouraged to integrate with third-party
products that address security issues including anti-virus/anti-spam, products that address security issues including anti-virus/anti-spam,
reputation protection, compliance archiving, and data loss reputation protection, compliance archiving, and data loss
prevention. Proxying to a local SMTP Submission server may be a prevention. Proxying to a local SMTP Submission server may be a
simpler way to provide such security services. simpler way to provide such security services.
10. References 10. IANA Considerations
10.1. Normative References 10.1. JMAP Capability Registration for "mail"
IANA will register the "mail" JMAP Capability as follows:
Capability Name: "urn:ietf:params:jmap:mail"
Specification document: this document
Intended use: common
Change Controller: IETF
Security and privacy considerations: this document, section 9
10.2. IMAP and JMAP Keywords Registry
This document makes two changes to the IMAP keywords registry as
defined in [RFC5788].
First, the name of the registry is changed to the "IMAP and JMAP
keywords Registry".
Second, a scope column is added to the template and registry
indicating whether a keyword applies to IMAP-only, JMAP-only, both,
or reserved. All keywords presently in the IMAP keyword registry
will be marked with a scope of both. The "reserved" status can be
used to prevent future registration of a name that would be confusing
if registered. Registration of keywords with scope 'reserved' omit
most fields in the registration template (see example for "$recent"
subsection of this section); such registrations are intended to be
infrequent.
IMAP clients MAY silently ignore any keywords marked JMAP-only or
reserved in the event they appear in protocol. JMAP clients MAY
silently ignore any keywords marked IMAP-only or reserved in the
event they appear in protocol.
New JMAP-only keywords are registered in the following sub-sections.
These keywords correspond to IMAP system keywords and are thus not
appropriate for use in IMAP. These keywords can not be subsequently
registered for use in IMAP except via standards action.
10.2.1. Registration of JMAP keyword '$draft'
This registers the JMAP-only keyword '$draft' in the "IMAP and JMAP
keywords Registry".
Keyword name: "$draft"
Scope: JMAP-only
Purpose (description): This is set when the user wants to treat the
message as a draft the user is composing. This is the JMAP
equivalent of the IMAP \Draft flag.
Private or Shared on a server: BOTH
Is it an advisory keyword or may it cause an automatic action:
Automatic. If the account has a mailbox marked with the \Drafts
special use [RFC6154], setting this flag MAY cause the message to
appear in that mailbox automatically. Certain JMAP computed values
such as _unreadEmails_ will change as a result of changing this flag.
In addition, mail clients typically will present draft messages in a
composer window rather than a viewer window.
When/by whom the keyword is set/cleared: This is typically set by a
JMAP client when referring to a draft message. One model for draft
emails would result in clearing this flag in an EmailSubmission/set
operation with an onSuccessUpdateEmail attribute. In a mailstore
shared by JMAP and IMAP, this is also set and cleared as necessary so
it matches the IMAP \Draft flag.
Related keywords: None
Related IMAP/JMAP Capabilities: SPECIAL-USE [RFC6154]
Security Considerations: A server implementing this keyword as a
shared keyword may disclose that a user considers the message a draft
message. This information would be exposed to other users with read
permission for the mailbox keywords.
Published specification (recommended): this document
Person & email address to contact for further information: (editor-
contact-goes-here)
Intended usage: COMMON
Owner/Change controller: IESG
10.2.2. Registration of JMAP keyword '$seen'
This registers the JMAP-only keyword '$seen' in the "IMAP and JMAP
keywords Registry".
Keyword name: "$seen"
Scope: JMAP-only
Purpose (description): This is set when the user wants to treat the
message as read. This is the JMAP equivalent of the IMAP \Seen flag.
Private or Shared on a server: BOTH
Is it an advisory keyword or may it cause an automatic action:
Advisory. However, certain JMAP computed values such as
_unreadEmails_ will change as a result of changing this flag.
When/by whom the keyword is set/cleared: This is set by a JMAP client
when it presents the message content to the user; clients often offer
an option to clear this flag. In a mailstore shared by JMAP and
IMAP, this is also set and cleared as necessary so it matches the
IMAP \Seen flag.
Related keywords: None
Related IMAP/JMAP Capabilities: None
Security Considerations: A server implementing this keyword as a
shared keyword may disclose that a user considers the message to have
been read. This information would be exposed to other users with
read permission for the mailbox keywords.
Published specification (recommended): this document
Person & email address to contact for further information: (editor-
contact-goes-here)
Intended usage: COMMON
Owner/Change controller: IESG
10.2.3. Registration of JMAP keyword '$flagged'
This registers the JMAP-only keyword '$flagged' in the "IMAP and JMAP
keywords Registry".
Keyword name: "$flagged"
Scope: JMAP-only
Purpose (description): This is set when the user wants to treat the
message as flagged for urgent/special attention. This is the JMAP
equivalent of the IMAP \Flagged flag.
Private or Shared on a server: BOTH
Is it an advisory keyword or may it cause an automatic action:
Automatic. If the account has a mailbox marked with the \Flagged
special use [RFC6154], setting this flag MAY cause the message to
appear in that mailbox automatically.
When/by whom the keyword is set/cleared: JMAP clients typically allow
a user to set/clear this flag as desired. In a mailstore shared by
JMAP and IMAP, this is also set and cleared as necessary so it
matches the IMAP \Flagged flag.
Related keywords: None
Related IMAP/JMAP Capabilities: SPECIAL-USE [RFC6154]
Security Considerations: A server implementing this keyword as a
shared keyword may disclose that a user considers the message as
flagged for urgent/special attention. This information would be
exposed to other users with read permission for the mailbox keywords.
Published specification (recommended): this document
Person & email address to contact for further information: (editor-
contact-goes-here)
Intended usage: COMMON
Owner/Change controller: IESG
10.2.4. Registration of JMAP keyword '$answered'
This registers the JMAP-only keyword '$answered' in the "IMAP and
JMAP keywords Registry".
Keyword name: "$answered"
Scope: JMAP-only
Purpose (description): This is set when the message has been
answered.
Private or Shared on a server: BOTH
Is it an advisory keyword or may it cause an automatic action:
Advisory.
When/by whom the keyword is set/cleared: JMAP clients typically set
this when submitting a reply or answer to the message. It may be set
by the EmailSubmission/set operation with an onSuccessUpdateEmail
attribute. In a mailstore shared by JMAP and IMAP, this is also set
and cleared as necessary so it matches the IMAP \Answered flag.
Related keywords: None
Related IMAP/JMAP Capabilities: None
Security Considerations: A server implementing this keyword as a
shared keyword may disclose that a user considers the message as
flagged for urgent/special attention. This information would be
exposed to other users with read permission for the mailbox keywords.
Published specification (recommended): this document
Person & email address to contact for further information: (editor-
contact-goes-here)
Intended usage: COMMON
Owner/Change controller: IESG
10.2.5. Registration of '$recent' Keyword
This registers the keyword '$recent' in the "IMAP and JMAP keywords
Registry".
Keyword name: "$recent"
Scope: reserved
Purpose (description): This keyword is not used to avoid confusion
with the IMAP \Recent system flag.
Published specification (recommended): this document
Person & email address to contact for further information: (editor-
contact-goes-here)
Owner/Change controller: IESG
11. References
11.1. Normative References
[RFC1870] Klensin, J., Freed, N., and K. Moore, "SMTP Service [RFC1870] Klensin, J., Freed, N., and K. Moore, "SMTP Service
Extension for Message Size Declaration", STD 10, RFC 1870, Extension for Message Size Declaration", STD 10, RFC 1870,
DOI 10.17487/RFC1870, November 1995, DOI 10.17487/RFC1870, November 1995,
<https://www.rfc-editor.org/info/rfc1870>. <https://www.rfc-editor.org/info/rfc1870>.
[RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
Extensions (MIME) Part One: Format of Internet Message Extensions (MIME) Part One: Format of Internet Message
Bodies", RFC 2045, DOI 10.17487/RFC2045, November 1996, Bodies", RFC 2045, DOI 10.17487/RFC2045, November 1996,
<https://www.rfc-editor.org/info/rfc2045>. <https://www.rfc-editor.org/info/rfc2045>.
skipping to change at page 59, line 49 skipping to change at page 74, line 44
[RFC6533] Hansen, T., Ed., Newman, C., and A. Melnikov, [RFC6533] Hansen, T., Ed., Newman, C., and A. Melnikov,
"Internationalized Delivery Status and Disposition "Internationalized Delivery Status and Disposition
Notifications", RFC 6533, DOI 10.17487/RFC6533, February Notifications", RFC 6533, DOI 10.17487/RFC6533, February
2012, <https://www.rfc-editor.org/info/rfc6533>. 2012, <https://www.rfc-editor.org/info/rfc6533>.
[RFC6710] Melnikov, A. and K. Carlberg, "Simple Mail Transfer [RFC6710] Melnikov, A. and K. Carlberg, "Simple Mail Transfer
Protocol Extension for Message Transfer Priorities", Protocol Extension for Message Transfer Priorities",
RFC 6710, DOI 10.17487/RFC6710, August 2012, RFC 6710, DOI 10.17487/RFC6710, August 2012,
<https://www.rfc-editor.org/info/rfc6710>. <https://www.rfc-editor.org/info/rfc6710>.
[RFC7159] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
Interchange Format", RFC 7159, DOI 10.17487/RFC7159, March
2014, <https://www.rfc-editor.org/info/rfc7159>.
[RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493,
DOI 10.17487/RFC7493, March 2015,
<https://www.rfc-editor.org/info/rfc7493>.
[RFC8314] Moore, K. and C. Newman, "Cleartext Considered Obsolete: [RFC8314] Moore, K. and C. Newman, "Cleartext Considered Obsolete:
Use of Transport Layer Security (TLS) for Email Submission Use of Transport Layer Security (TLS) for Email Submission
and Access", RFC 8314, DOI 10.17487/RFC8314, January 2018, and Access", RFC 8314, DOI 10.17487/RFC8314, January 2018,
<https://www.rfc-editor.org/info/rfc8314>. <https://www.rfc-editor.org/info/rfc8314>.
10.2. URIs 11.2. URIs
[1] TODO [1] TODO
[2] server.html [2] https://www.iana.org/assignments/imap-keywords/imap-
[3] https://www.iana.org/assignments/imap-keywords/imap-
keywords.xhtml keywords.xhtml
[4] https://www.w3.org/TR/CSP3/ [3] https://www.w3.org/TR/CSP3/
[5] https://www.w3.org/TR/referrer-policy/ [4] https://www.w3.org/TR/referrer-policy/
Author's Address Author's Address
Neil Jenkins Neil Jenkins
FastMail FastMail
Level 2, 114 William St PO Box 234, Collins St West
Melbourne VIC 3000 Melbourne VIC 8007
Australia Australia
Email: neilj@fastmailteam.com Email: neilj@fastmailteam.com
URI: https://www.fastmail.com URI: https://www.fastmail.com
 End of changes. 134 change blocks. 
432 lines changed or deleted 1059 lines changed or added

This html diff was produced by rfcdiff 1.47. The latest version is available from http://tools.ietf.org/tools/rfcdiff/