draft-ietf-jmap-mail-09.txt   draft-ietf-jmap-mail-10.txt 
JMAP N. Jenkins JMAP N. Jenkins
Internet-Draft FastMail Internet-Draft FastMail
Updates: 5788 (if approved) C. Newman Updates: 5788 (if approved) C. Newman
Intended status: Standards Track Oracle Intended status: Standards Track Oracle
Expires: April 26, 2019 October 23, 2018 Expires: May 10, 2019 November 6, 2018
JMAP for Mail JMAP for Mail
draft-ietf-jmap-mail-09 draft-ietf-jmap-mail-10
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 April 26, 2019. This Internet-Draft will expire on May 10, 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
skipping to change at page 2, line 11 skipping to change at page 2, line 11
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 . . . . . . . . . . . . . . . . . . . . . . . . 4 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1. Notational conventions . . . . . . . . . . . . . . . . . 4 1.1. Notational conventions . . . . . . . . . . . . . . . . . 4
1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4 1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4
1.3. Additions to the capabilities object . . . . . . . . . . 4 1.3. Additions to the capabilities object . . . . . . . . . . 4
1.3.1. urn:ietf:params:jmap:mail . . . . . . . . . . . . . . 5 1.3.1. urn:ietf:params:jmap:mail . . . . . . . . . . . . . . 4
1.3.2. urn:ietf:params:jmap:submission . . . . . . . . . . . 6 1.3.2. urn:ietf:params:jmap:submission . . . . . . . . . . . 5
1.3.3. urn:ietf:params:jmap:vacationresponse . . . . . . . . 6 1.3.3. urn:ietf:params:jmap:vacationresponse . . . . . . . . 6
1.4. Data type support in different accounts . . . . . . . . . 7 1.4. Data type support in different accounts . . . . . . . . . 6
1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 11 2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 10
2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 11 2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 10
2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 11 2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 11
2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 12 2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 11
2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 12 2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 11
2.6. Example . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.6. Example . . . . . . . . . . . . . . . . . . . . . . . . . 12
3. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.1. Thread/get . . . . . . . . . . . . . . . . . . . . . . . 17 3.1. Thread/get . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 17 3.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 17
3.2. Thread/changes . . . . . . . . . . . . . . . . . . . . . 18 3.2. Thread/changes . . . . . . . . . . . . . . . . . . . . . 17
4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.1. Properties of the Email object . . . . . . . . . . . . . 18 4.1. Properties of the Email object . . . . . . . . . . . . . 18
4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 19 4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 19
4.1.2. Header fields parsed forms . . . . . . . . . . . . . 21 4.1.2. Header fields parsed forms . . . . . . . . . . . . . 21
4.1.3. Header fields properties . . . . . . . . . . . . . . 26 4.1.3. Header fields properties . . . . . . . . . . . . . . 26
4.1.4. Body parts . . . . . . . . . . . . . . . . . . . . . 28 4.1.4. Body parts . . . . . . . . . . . . . . . . . . . . . 28
4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 34 4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 36 4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 36
4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 37 4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 37
4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 38 4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 38
4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 38 4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 38
4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 40 4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 40
4.4.3. Thread collapsing . . . . . . . . . . . . . . . . . . 42 4.4.3. Thread collapsing . . . . . . . . . . . . . . . . . . 42
4.4.4. Response . . . . . . . . . . . . . . . . . . . . . . 42
4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 42 4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 42
4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 42 4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 42
4.7. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 45 4.7. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 45
4.8. Email/import . . . . . . . . . . . . . . . . . . . . . . 45 4.8. Email/import . . . . . . . . . . . . . . . . . . . . . . 45
4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 47 4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 47
4.10. Examples . . . . . . . . . . . . . . . . . . . . . . . . 49 4.10. Examples . . . . . . . . . . . . . . . . . . . . . . . . 49
5. Search snippets . . . . . . . . . . . . . . . . . . . . . . . 57 5. Search snippets . . . . . . . . . . . . . . . . . . . . . . . 57
5.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 58 5.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 58
5.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 59 5.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 59
6. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 60 6. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 59
6.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 61 6.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 60
6.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 61 6.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 61
6.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 61 6.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 61
6.4. Example . . . . . . . . . . . . . . . . . . . . . . . . . 61 6.4. Example . . . . . . . . . . . . . . . . . . . . . . . . . 61
7. Email submission . . . . . . . . . . . . . . . . . . . . . . 62 7. Email submission . . . . . . . . . . . . . . . . . . . . . . 62
7.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 67 7.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 67
7.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 67 7.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 67
7.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 67 7.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 67
7.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 68 7.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 68
7.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 68 7.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 68
7.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 70 7.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 70
8. Vacation response . . . . . . . . . . . . . . . . . . . . . . 72 8. Vacation response . . . . . . . . . . . . . . . . . . . . . . 71
8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 73 8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 72
8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 73 8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 72
9. Security considerations . . . . . . . . . . . . . . . . . . . 73 9. Security considerations . . . . . . . . . . . . . . . . . . . 73
9.1. EmailBodyPart value . . . . . . . . . . . . . . . . . . . 73 9.1. EmailBodyPart value . . . . . . . . . . . . . . . . . . . 73
9.2. HTML email display . . . . . . . . . . . . . . . . . . . 73 9.2. HTML email display . . . . . . . . . . . . . . . . . . . 73
9.3. Email submission . . . . . . . . . . . . . . . . . . . . 76 9.3. Email submission . . . . . . . . . . . . . . . . . . . . 75
10. IANA considerations . . . . . . . . . . . . . . . . . . . . . 76 10. IANA considerations . . . . . . . . . . . . . . . . . . . . . 76
10.1. JMAP capability registration for "mail" . . . . . . . . 76 10.1. JMAP capability registration for "mail" . . . . . . . . 76
10.2. JMAP capability registration for "submission" . . . . . 77 10.2. JMAP capability registration for "submission" . . . . . 76
10.3. JMAP capability registration for "vacationresponse" . . 77 10.3. JMAP capability registration for "vacationresponse" . . 77
10.4. IMAP and JMAP keywords registry . . . . . . . . . . . . 77 10.4. IMAP and JMAP keywords registry . . . . . . . . . . . . 77
10.4.1. Registration of JMAP keyword '$draft' . . . . . . . 78 10.4.1. Registration of JMAP keyword '$draft' . . . . . . . 78
10.4.2. Registration of JMAP keyword '$seen' . . . . . . . . 79 10.4.2. Registration of JMAP keyword '$seen' . . . . . . . . 79
10.4.3. Registration of JMAP keyword '$flagged' . . . . . . 80 10.4.3. Registration of JMAP keyword '$flagged' . . . . . . 79
10.4.4. Registration of JMAP keyword '$answered' . . . . . . 80 10.4.4. Registration of JMAP keyword '$answered' . . . . . . 80
10.4.5. Registration of '$recent' keyword . . . . . . . . . 81 10.4.5. Registration of '$recent' keyword . . . . . . . . . 81
10.5. Registration of "inbox" role in . . . . . . . . . . . . 82 10.5. Registration of "inbox" role in . . . . . . . . . . . . 82
10.6. JMAP Error Codes registry . . . . . . . . . . . . . . . 82 10.6. JMAP Error Codes registry . . . . . . . . . . . . . . . 82
10.6.1. mailboxHasChild . . . . . . . . . . . . . . . . . . 82 10.6.1. mailboxHasChild . . . . . . . . . . . . . . . . . . 82
10.6.2. mailboxHasEmail . . . . . . . . . . . . . . . . . . 82 10.6.2. mailboxHasEmail . . . . . . . . . . . . . . . . . . 82
10.6.3. blobNotFound . . . . . . . . . . . . . . . . . . . . 83 10.6.3. blobNotFound . . . . . . . . . . . . . . . . . . . . 82
10.6.4. tooManyKeywords . . . . . . . . . . . . . . . . . . 83 10.6.4. tooManyKeywords . . . . . . . . . . . . . . . . . . 83
10.6.5. tooManyMailboxes . . . . . . . . . . . . . . . . . . 83 10.6.5. tooManyMailboxes . . . . . . . . . . . . . . . . . . 83
10.6.6. emailNotFound . . . . . . . . . . . . . . . . . . . 83 10.6.6. invalidEmail . . . . . . . . . . . . . . . . . . . . 83
10.6.7. emailTooLarge . . . . . . . . . . . . . . . . . . . 83 10.6.7. tooManyRecipients . . . . . . . . . . . . . . . . . 83
10.6.8. invalidEmail . . . . . . . . . . . . . . . . . . . . 84 10.6.8. noRecipients . . . . . . . . . . . . . . . . . . . . 83
10.6.9. tooManyRecipients . . . . . . . . . . . . . . . . . 84 10.6.9. invalidRecipients . . . . . . . . . . . . . . . . . 84
10.6.10. noRecipients . . . . . . . . . . . . . . . . . . . . 84 10.6.10. forbiddenMailFrom . . . . . . . . . . . . . . . . . 84
10.6.11. invalidRecipients . . . . . . . . . . . . . . . . . 84 10.6.11. forbiddenFrom . . . . . . . . . . . . . . . . . . . 84
10.6.12. forbiddenMailFrom . . . . . . . . . . . . . . . . . 84 10.6.12. forbiddenToSend . . . . . . . . . . . . . . . . . . 84
10.6.13. forbiddenFrom . . . . . . . . . . . . . . . . . . . 85 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 84
10.6.14. forbiddenToSend . . . . . . . . . . . . . . . . . . 85
11. References . . . . . . . . . . . . . . . . . . . . . . . . . 85
11.1. Normative References . . . . . . . . . . . . . . . . . . 85 11.1. Normative References . . . . . . . . . . . . . . . . . . 85
11.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 88 11.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 88 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 88
1. Introduction 1. Introduction
JMAP <https://tools.ietf.org/html/draft-ietf-jmap-core-05> is a JMAP (RFC XXXX) is a generic protocol for synchronising data, such as
generic protocol for synchronising data, such as mail, calendars or mail, calendars or contacts, between a client and a server. It is
contacts, between a client and a server. It is optimised for mobile optimised for mobile and web environments, and aims to provide a
and web environments, and aims to provide a consistent interface to 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 mail over 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].
Type signatures, examples and property descriptions in this document Type signatures, examples and property descriptions in this document
follow the conventions established in Section 1.1 of follow the conventions established in Section 1.1 of RFC XXXX. Data
<https://tools.ietf.org/html/draft-ietf-jmap-core-05>. types defined in the core specification are also used in this
Object properties may also have a set of attributes defined along
with the type signature. These have the following meanings:
o *server-set*: Only the server can set the value for this property.
The client MUST NOT send this property when creating a new object
of this type.
o *immutable*: The value MUST NOT change after the object is
created.
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
when creating a new object of this type.
Data types defined in the core specification are used in this
document. document.
1.2. Terminology 1.2. Terminology
The same terminology is used in this document as in the core JMAP The same terminology is used in this document as in the core JMAP
specification. specification.
1.3. Additions to the capabilities object 1.3. Additions to the capabilities object
The capabilities object is returned as part of the standard JMAP The capabilities object is returned as part of the standard JMAP
Session object; see the JMAP Core specification. Session object; see RFC XXXX, section 2.
This document defines three additional capability objects. This document defines three additional capability URIs.
1.3.1. urn:ietf:params:jmap:mail 1.3.1. urn:ietf:params:jmap:mail
This represents support for the Mailbox, Thread, Email, and This represents support for the Mailbox, Thread, Email, and
SearchSnippet data types and associated API methods. The value of SearchSnippet data types and associated API methods. The value of
this property is an object which MUST contain the following this property is an object which MUST contain the following
information on server capabilities: information on server capabilities:
o *maxMailboxesPerEmail*: "PositiveInt|null" The maximum number of o *maxMailboxesPerEmail*: "PositiveInt|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
skipping to change at page 5, line 34 skipping to change at page 5, line 16
octets, allowed for the name of a mailbox. This MUST be >= 255. octets, allowed for the name of a mailbox. This MUST be >= 255.
o *maxSizeAttachmentsPerEmail*: "PositiveInt" The maximum total size o *maxSizeAttachmentsPerEmail*: "PositiveInt" The maximum total size
of attachments, in octets, allowed for a single email. A server of attachments, in octets, allowed for a single email. A server
MAY still reject import or creation of emails with a lower MAY still reject import or creation of emails with a lower
attachment size total (for example, if the body includes several attachment size total (for example, if the body includes several
megabytes of text, causing the size of the encoded MIME structure megabytes of text, causing the size of the encoded MIME structure
to be over some server-defined limit). Note, this limit is for to be over some server-defined limit). Note, this limit is for
the sum of unencoded attachment sizes. Users are generally not the sum of unencoded attachment sizes. Users are generally not
knowledgeable about encoding overhead etc., nor should they need knowledgeable about encoding overhead etc., nor should they need
to be, so services marketing and help materials normally tells to be, so marketing and help materials normally tell them the "max
them the "max size attachments". This is the unencoded size they size attachments". This is the unencoded size they see on their
see on their hard drive, and so this capability matches that and hard drive, and so this capability matches that and allows the
allows the client to consistently enforce what the user client to consistently enforce what the user understands as the
understands as the limit. The server may separately have a limit limit. The server may separately have a limit for the total size
for the total size of the RFC5322 message, which will have of the RFC5322 message, which will have attachments Base64 encoded
attachments Base64 encoded and message headers and bodies too. and message headers and bodies too. For example, suppose the
For example, suppose the server advertises server advertises "maxSizeAttachmentsPerEmail: 50000000" (50 MB).
"maxSizeAttachmentsPerEmail: 50000000" (50 MB). The enforced The enforced server limit may be for an RFC5322 size of 70000000
server limit may be for an RFC5322 size of 70000000 octets (70 octets (70 MB). Even with Base64 encoding and a 2 MB HTML body,
MB). Even with Base64 encoding and a 2 MB HTML body, 50 MB 50 MB attachments would fit under this limit.
attachments would fit under this limit.
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 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.
1.3.2. urn:ietf:params:jmap:submission 1.3.2. urn:ietf:params:jmap:submission
This represents support for the Identity and MessageSubmission data This represents support for the Identity and MessageSubmission data
types and associated API methods. The value of this property is an types and associated API methods. The value of this property is an
object which MUST contain the following information on server object which MUST contain the following information on server
capabilities: capabilities:
skipping to change at page 6, line 44 skipping to change at page 6, line 24
* DELIVERYBY ([RFC2852]) * DELIVERYBY ([RFC2852])
* 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.3.3. urn:ietf:params:jmap:vacationresponse 1.3.3. urn:ietf:params:jmap:vacationresponse
This represents support for the VacationResponse data type and This represents support for the VacationResponse data type and
associated API methods. The value of this property is an empty associated API methods. The value of this property is an empty
object. object.
1.4. Data type support in different accounts 1.4. Data type support in different accounts
The server MUST include the appropriate capability strings in the The server MUST include the appropriate capability strings in the
_hasDataFor_ property of any account in which the user may use the _hasDataFor_ property of any account with which the user may use the
data types represented by that URN. Supported data types may differ data types represented by that URI. Supported data types may differ
between accounts the user has access to. For example, in the user's between accounts the user has access to. For example, in the user's
personal account they may have access to all three sets of data, but personal account they may have access to all three sets of data, but
in a shared account they may only have data for in a shared account they may only have data for
"urn:ietf:params:jmap:mail". This means they can access "urn:ietf:params:jmap:mail". This means they can access
Mailbox/Thread/Email data in the shared account but are not allowed Mailbox/Thread/Email data in the shared account but are not allowed
to send as that account (and so do not have access to Identity/ to send as that account (and so do not have access to Identity/
MessageSubmission objects) or view/set its vacation response. MessageSubmission objects) or view/set its vacation response.
1.5. Push 1.5. Push
skipping to change at page 9, line 4 skipping to change at page 8, line 33
where at least one email in the thread is in this mailbox. where at least one email in the thread is in this mailbox.
o *unreadThreads*: "PositiveInt" (server-set) An indication of the o *unreadThreads*: "PositiveInt" (server-set) An indication of the
number of "unread" threads in the mailbox. This may be presented number of "unread" threads in the mailbox. This may be presented
by the client as a badge or marker associated with the mailbox. by the client as a badge or marker associated with the mailbox.
For compatibility with existing implementations, the way "unread For compatibility with existing implementations, the way "unread
threads" is determined is not mandated in this document. The threads" is determined is not mandated in this document. The
simplest solution to implement is simply the number of threads simplest solution to implement is simply the number of threads
where at least one email in the thread is both in this mailbox and where at least one email in the thread is both in this mailbox and
has neither the "$seen" nor "$draft" keywords. However, a quality has neither the "$seen" nor "$draft" keywords. However, a quality
implementation will make return the number of unread items the implementation will return the number of unread items the user
user would see if they opened that mailbox. A thread is shown as would see if they opened that mailbox. A thread is shown as
unread if it contains any unread messages that will be displayed unread if it contains any unread messages that will be displayed
when the thread is opened. Therefore "unreadThreads" should be when the thread is opened. Therefore "unreadThreads" should be
the number of threads where at least one email in the thread has the number of threads where at least one email in the thread has
neither the "$seen" nor the "$draft" keyword AND at least one neither the "$seen" nor the "$draft" keyword AND at least one
email in the thread is in this mailbox. Note, the unread email email in the thread is in this mailbox. Note, the unread email
does not need to be the one in this mailbox. In addition, the does not need to be the one in this mailbox. In addition, the
Trash mailbox (that is a mailbox whose "role" is "trash") is Trash mailbox (that is a mailbox whose "role" is "trash") is
treated specially: treated specially:
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
skipping to change at page 10, line 37 skipping to change at page 10, line 17
mailboxes in shared accounts the user has access to, and "true" mailboxes in shared accounts the user has access to, and "true"
for any new mailboxes created by the user themself. This MUST be for any new mailboxes created by the user themself. This MUST be
stored separately per-user where multiple users have access to a stored separately per-user where multiple users have access to a
shared mailbox. A user may have permission to access a large shared mailbox. A user may have permission to access a large
number of shared accounts, or a shared account with a very 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 set of mailboxes, but only be interested in the contents of a few
of these. Clients may choose only to display mailboxes to the of these. Clients may choose only to display mailboxes to the
user that have the "isSubscribed" property set to "true", and user that have the "isSubscribed" property set to "true", and
offer a separate UI to allow the user to see and subscribe/ offer a separate UI to allow the user to see and subscribe/
unsubscribe from the full set of mailboxes. However, clients MAY unsubscribe from the full set of mailboxes. However, clients MAY
choose to ignore this property, either entirely, for ease of choose to ignore this property, either entirely for ease of
implementation, or just for the primary account (which is normally implementation, or just for the primary account (which is normally
the user's own, rather than a shared account). the user's own, rather than a shared account).
For IMAP compatibility, an email in both the Trash and another For IMAP compatibility, an email in both the Trash and another
mailbox SHOULD be treated by the client as existing in both places mailbox SHOULD be treated by the client as existing in both places
(i.e. when emptying the trash, the client SHOULD just remove the (i.e. when emptying the trash, the client SHOULD just remove the
Trash mailbox and leave it in the other mailbox). Trash mailbox and leave it in the other mailbox).
The following JMAP methods are supported: The following JMAP methods are supported:
skipping to change at page 11, line 22 skipping to change at page 10, line 45
Standard "/changes" method, but with one extra argument to the Standard "/changes" method, but with one extra argument to the
response: response:
o *updatedProperties*: "String[]|null" If only the mailbox counts o *updatedProperties*: "String[]|null" If only the mailbox counts
(unread/total emails/threads) have changed since the old state, (unread/total emails/threads) have changed since the old state,
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 other properties are generally
for most use cases changes rarely, the server can help the client only changed rarely, the server can help the client optimise data
optimise data transfer by keeping track of changes to email/thread transfer by keeping track of changes to email/thread counts
counts separately to other state changes. The _updatedProperties_ separately to other state changes. The _updatedProperties_ array may
array may be used directly via a result reference in a subsequent be used directly via a back-reference in a subsequent Mailbox/get
Mailbox/get call in a single request. call in the same single request so only these properties are returned
if nothing else has changed.
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.
skipping to change at page 12, line 19 skipping to change at page 11, line 42
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 alphabetical tree order).
2.4. Mailbox/queryChanges 2.4. Mailbox/queryChanges
Standard "/queryChanges" method. Standard "/queryChanges" method.
2.5. Mailbox/set 2.5. Mailbox/set
Standard "/set" method, but with the following additional argument: Standard "/set" method, but with the following additional argument:
o *onDestroyRemoveMessages*: "Boolean" (default: false) If "false", o *onDestroyRemoveMessages*: "Boolean" (default: false) If "false",
attempts to destroy a mailbox that still has any messages in it any attempt to destroy a mailbox that still has messages in it
will be rejected with a "mailboxHasEmail" SetError. If "true", will be rejected with a "mailboxHasEmail" SetError. If "true",
any messages that were in the mailbox will be removed from it, and any messages that were in the mailbox will be removed from it, and
if in no other mailboxes will be destroyed when the mailbox is if in no other mailboxes will be destroyed when the mailbox is
destroyed. destroyed.
The following extra _SetError_ types are defined: The following extra _SetError_ types are defined:
For *destroy*: For *destroy*:
o "mailboxHasChild": The mailbox still has at least one child o "mailboxHasChild": The mailbox still has at least one child
skipping to change at page 13, line 15 skipping to change at page 13, line 8
[[ "Mailbox/get", { [[ "Mailbox/get", {
"accountId": "u33084183", "accountId": "u33084183",
"ids": null "ids": null
}, "0" ]] }, "0" ]]
And response: And response:
[[ "Mailbox/get", { [[ "Mailbox/get", {
"accountId": "u33084183", "accountId": "u33084183",
"state": "78540", "state": "78540",
"list": [ "list": [{
{
"id": "23cfa8094c0f41e6", "id": "23cfa8094c0f41e6",
"name": "Inbox", "name": "Inbox",
"parentId": null, "parentId": null,
"role": "inbox", "role": "inbox",
"sortOrder": 10, "sortOrder": 10,
"totalEmails": 16307, "totalEmails": 16307,
"unreadEmails": 13905, "unreadEmails": 13905,
"totalThreads": 5833, "totalThreads": 5833,
"unreadThreads": 5128, "unreadThreads": 5128,
"myRights": { "myRights": {
skipping to change at page 13, line 38 skipping to change at page 13, line 30
"mayRename": false, "mayRename": false,
"maySubmit": true, "maySubmit": true,
"mayDelete": false, "mayDelete": false,
"maySetKeywords": true, "maySetKeywords": true,
"mayRemoveItems": true, "mayRemoveItems": true,
"mayCreateChild": true, "mayCreateChild": true,
"maySetSeen": true, "maySetSeen": true,
"mayReadItems": true "mayReadItems": true
}, },
"isSubscribed": true "isSubscribed": true
}, }, {
{
"id": "674cc24095db49ce", "id": "674cc24095db49ce",
"name": "Important mail", "name": "Important mail",
... ...
} }, ... ],
...
],
"notFound": [] "notFound": []
}, "0" ]] }, "0" ]]
Now suppose a message is marked read and we get a push update that 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 the Mailbox state has changed. You might fetch the updates like
this: this:
[[ "Mailbox/changes", { [[ "Mailbox/changes", {
"accountId": "u33084183", "accountId": "u33084183",
"sinceState": "78540" "sinceState": "78540"
skipping to change at page 14, line 32 skipping to change at page 14, line 32
"path": "/updated" "path": "/updated"
}, },
"#properties": { "#properties": {
"resultOf": "0", "resultOf": "0",
"name": "Mailbox/changes", "name": "Mailbox/changes",
"path": "/updatedProperties" "path": "/updatedProperties"
} }
}, "2" ]] }, "2" ]]
This fetches the list of ids for created/updated/destroyed mailboxes, This fetches the list of ids for created/updated/destroyed mailboxes,
then using back references fetches the data for just the created/ then using back-references fetches the data for just the created/
updated mailboxes in the same request. The response may look updated mailboxes in the same request. The response may look
something like this: something like this:
[[ "Mailbox/changes", { [[ "Mailbox/changes", {
"accountId": "u33084183", "accountId": "u33084183",
"oldState": "78541", "oldState": "78541",
"newState": "78542", "newState": "78542",
"hasMoreChanges": false, "hasMoreChanges": false,
"updatedProperties": [ "updatedProperties": [
"totalEmails", "unreadEmails", "totalEmails", "unreadEmails",
skipping to change at page 16, line 9 skipping to change at page 16, line 9
"destroy": [ "23cfa8094c0f41e6" ] "destroy": [ "23cfa8094c0f41e6" ]
}, "0" ]] }, "0" ]]
Suppose the rename succeeds, but we don't have permission to destroy Suppose the rename succeeds, but we don't have permission to destroy
the mailbox we tried to destroy, we might get back: the mailbox we tried to destroy, we might get back:
[[ "Mailbox/set", { [[ "Mailbox/set", {
"accountId": "u33084183", "accountId": "u33084183",
"oldState": "78542", "oldState": "78542",
"newState": "78549", "newState": "78549",
"created": null,
"notCreated": null,
"updated": { "updated": {
"674cc24095db49ce": null "674cc24095db49ce": null
}, },
"notUpdated": null,
"destroyed": null,
"notDestroyed": { "notDestroyed": {
"23cfa8094c0f41e6": { "23cfa8094c0f41e6": {
"type": "forbidden" "type": "forbidden"
} }
} }
}, "0" ]] }, "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
skipping to change at page 17, line 9 skipping to change at page 17, line 5
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.
A *Thread* object has the following properties: A *Thread* object has the following properties:
o *id*: "String" (immutable) The id of the thread. o *id*: "String" (immutable; server-set) The id of the thread.
o *emailIds*: "String[]" The ids of the emails in the thread, sorted o *emailIds*: "String[]" (server-set) The ids of the emails in the
by the _receivedAt_ date of the email, oldest first. If two thread, sorted by the _receivedAt_ date of the email, oldest
emails have an identical date, the sort is server-dependent but first. If two emails have an identical date, the sort is server-
MUST be stable (sorting by id is recommended). dependent but MUST be stable (sorting by id is recommended).
The following JMAP methods are supported: The following JMAP methods are supported:
3.1. Thread/get 3.1. Thread/get
Standard "/get" method. Standard "/get" method.
3.1.1. Example 3.1.1. Example
Request: Request:
[[ "Thread/get", { [[ "Thread/get", {
"accountId": "acme", "accountId": "acme",
"ids": ["f123u4", "f41u44"], "ids": ["f123u4", "f41u44"]
}, "#1" ]] }, "#1" ]]
with response: with response:
[[ "Thread/get", { [[ "Thread/get", {
"accountId": "acme", "accountId": "acme",
"state": "f6a7e214", "state": "f6a7e214",
"list": [ "list": [
{ {
"id": "f123u4", "id": "f123u4",
"emailIds": [ "eaa623", "f782cbb"] "emailIds": [ "eaa623", "f782cbb"]
skipping to change at page 18, line 40 skipping to change at page 18, line 36
plain text or HTML), with a set of attachments. Flattening the MIME plain text or HTML), with a set of attachments. Flattening the MIME
structure to form this model can be difficult, and causes structure to form this model can be difficult, and causes
inconsistency between clients. Therefore in addition to the inconsistency between clients. Therefore in addition to the
_bodyStructure_ property, which gives the full tree, the Email object _bodyStructure_ property, which gives the full tree, the Email object
contains 3 alternate properties with flat lists of body parts: contains 3 alternate properties with flat lists of body parts:
o _textBody_/_htmlBody_: These provide a list of parts that should o _textBody_/_htmlBody_: These provide a list of parts that should
be rendered sequentially as the "body" of the message. This is a 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 list rather than a single part as messages may have headers and/or
footers appended/prepended as separate parts as they are footers appended/prepended as separate parts as they are
transmitted, and some clients send text and images, or even videos transmitted, and some clients send text and images intended to be
and sound clips, intended to be displayed inline in the body as displayed inline in the body (or even videos and sound clips) as
multiple parts rather than a single HTML part with referenced multiple parts rather than a single HTML part with referenced
images. images.
Because MIME allows for multiple representations of the same data Because MIME allows for multiple representations of the same data
(using "multipart/alternative"), there is a textBody property (which (using "multipart/alternative"), there is a textBody property (which
prefers a plain text representation) and an htmlBody property (which prefers a plain text representation) and an htmlBody property (which
prefers an HTML representation) to accommodate the two most common prefers an HTML representation) to accommodate the two most common
client requirements. The same part may appear in both lists where client requirements. The same part may appear in both lists where
there is no alternative between the two. there is no alternative between the two.
skipping to change at page 19, line 42 skipping to change at page 19, line 37
header field value. header field value.
o *blobId*: "String" (immutable; server-set) The id representing the o *blobId*: "String" (immutable; server-set) The id representing the
raw octets of the [RFC5322] message. This may be used to download raw octets of the [RFC5322] message. This may be used to download
the raw original message, or to attach it directly to another the raw original message, or to attach it directly to another
Email etc. Email etc.
o *threadId*: "String" (immutable; server-set) The id of the Thread o *threadId*: "String" (immutable; server-set) The id of the Thread
to which this Email belongs. to which this Email belongs.
o *mailboxIds*: "String[Boolean]" The set of mailbox ids this email o *mailboxIds*: "String[Boolean]" The set of Mailbox ids this email
belongs to. An email MUST belong to one or more mailboxes at all belongs to. An email MUST belong to one or more mailboxes at all
times (until it is deleted). The set is represented as an object, times (until it is deleted). The set is represented as an object,
with each key being a _Mailbox id_. The value for each key in the with each key being a _Mailbox id_. The value for each key in the
object MUST be "true". object MUST be "true".
o *keywords*: "String[Boolean]" (default: ) A set of keywords that o *keywords*: "String[Boolean]" (default: ) A set of keywords that
apply to the email. The set is represented as an object, with the apply to the email. The set is represented as an object, with the
keys being the _keywords_. The value for each key in the object keys being the _keywords_. The value for each key in the object
MUST be "true". Keywords are shared with IMAP. The six system MUST be "true". Keywords are shared with IMAP. The six system
keywords from IMAP are treated specially. The following four keywords from IMAP are treated specially. The following four
skipping to change at page 20, line 19 skipping to change at page 20, line 15
* "$seen": The email has been read. * "$seen": The email has been read.
* "$flagged": The email has been flagged for urgent/special * "$flagged": The email has been flagged for urgent/special
attention. attention.
* "$answered": The email has been replied to. * "$answered": The email has been replied to.
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 (including as part of any
keywords to an email. For compatibility with IMAP, a keyword is a mailbox counts). Users may add arbitrary keywords to an email.
case-insensitive string of 1-255 characters in the ASCII subset For compatibility with IMAP, a keyword is a case-insensitive
%x21-%x7e (excludes control chars and space), and MUST NOT include string of 1-255 characters in the ASCII subset %x21-%x7e (excludes
any of these characters: "( ) { ] % * " \" Because JSON is case- control chars and space), and MUST NOT include any of these
sensitive, servers MUST return keywords in lower-case. The IANA characters: "( ) { ] % * " \" Because JSON is case-sensitive,
Keyword Registry [2] as established in [RFC5788] assigns semantic servers MUST return keywords in lower-case. The IANA Keyword
meaning to some other keywords in common use. New keywords may be Registry [2] as established in [RFC5788] assigns semantic 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-
skipping to change at page 21, line 33 skipping to change at page 21, line 33
or octet run with the high bit set that violates UTF-8 syntax with or octet run with the high bit set that violates UTF-8 syntax with
the unicode replacement character (U+FFFD). Any NUL octet MUST be the unicode replacement character (U+FFFD). Any NUL octet MUST be
dropped. dropped.
4.1.2.2. Text 4.1.2.2. Text
Type: "String" Type: "String"
The header field value with: The header field value with:
1. White space unfolded (as defined in [RFC5322] section 2.2.3) 1. White space unfolded (as defined in [RFC5322] section 2.2.3).
2. The terminating CRLF at the end of the value removed 2. The terminating CRLF at the end of the value removed.
3. Any SP characters at the beginning of the value removed 3. Any SP characters at the beginning of the value removed.
4. Any syntactically correct [RFC2047] encoded sections with a known 4. Any syntactically correct [RFC2047] encoded sections with a known
character set decoded. Any [RFC2047] encoded NUL octets or character set decoded. Any [RFC2047] encoded NUL octets or
control characters are dropped from the decoded value. Any text control characters are dropped from the decoded value. Any text
that looks like [RFC2047] syntax but violates [RFC2047] placement that looks like [RFC2047] syntax but violates [RFC2047] placement
or whitespace rules MUST NOT be decoded. or whitespace rules MUST NOT be decoded.
5. Any [RFC6532] UTF-8 values decoded. 5. Any [RFC6532] UTF-8 values decoded.
6. The resulting unicode converted to NFC form. 6. The resulting unicode converted to NFC form.
skipping to change at page 23, line 9 skipping to change at page 23, line 9
_addr-spec_ form (for example, may not contain an @ symbol). _addr-spec_ form (for example, may not contain an @ symbol).
For example, the following "address-list" string: For example, the following "address-list" string:
" James Smythe" <james@example.com>, Friends: jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= <john@example.com>; " James Smythe" <james@example.com>, Friends: jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= <john@example.com>;
would be parsed as: would be parsed as:
[ [
{ "name": "James Smythe", "email": "james@example.com" }, { "name": "James Smythe", "email": "james@example.com" },
{ "name": null, "email": "jane@example.com" }, { "name": null, "email": "jane@example.com" },
{ "name": "John Smith", "email": "john@example.com" }, { "name": "John Smith", "email": "john@example.com" }
] ]
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 the interoperability issues, this form may only be fetched or set for the
following header fields: following header fields:
o From o From
o Sender o Sender
skipping to change at page 28, line 37 skipping to change at page 28, line 37
o *partId*: "String|null" Identifies this part uniquely within the o *partId*: "String|null" Identifies this part uniquely within the
Email. This is scoped to the _emailId_ and has no meaning outside Email. This is scoped to the _emailId_ and has no meaning outside
of the JMAP Email object representation. This is "null" if, and of the JMAP Email object representation. This is "null" if, and
only if, the part is of type "multipart/*". only if, the part is of type "multipart/*".
o *blobId*: "String|null" The id representing the raw octets of the o *blobId*: "String|null" The id representing the raw octets of the
contents of the part after decoding any _Content-Transfer- contents of the part after decoding any _Content-Transfer-
Encoding_ (as defined in [RFC2045]), or "null" if, and only if, Encoding_ (as defined in [RFC2045]), or "null" if, and only if,
the part is of type "multipart/*". Note, two parts may be the part is of type "multipart/*". Note, two parts may be
transfer-encoded differently but have same the same blob id if transfer-encoded differently but have the same blob id if their
their decoded octets are identical and the server is using a decoded octets are identical and the server is using a secure hash
secure hash of the data for the blob id. of the data for the blob id.
o *size*: "PositiveInt" The size, in octets, of the raw data after o *size*: "PositiveInt" The size, in octets, of the raw data after
content transfer decoding (as referenced by the _blobId_, i.e. the content transfer decoding (as referenced by the _blobId_, i.e. the
number of octets in the file the user would download). number of octets in the file the user would download).
o *headers*: "EmailHeader[]" This is a list of all header fields in o *headers*: "EmailHeader[]" This is a list of all header fields in
the part, in the order they appear. The values are in _Raw_ form. the part, in the order they appear in the message. The values are
in _Raw_ form.
o *name*: "String|null" This is the [RFC2231] decoded _filename_ o *name*: "String|null" This is the [RFC2231] decoded _filename_
parameter of the _Content-Disposition_ header field, or (for parameter of the _Content-Disposition_ header field, or (for
compatibility with existing systems) if not present then the compatibility with existing systems) if not present then the
[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
skipping to change at page 31, line 14 skipping to change at page 31, line 17
None of these parts include subParts, including "message/*" types. None of these parts include subParts, including "message/*" types.
Attached messages may be fetched using the Email/parse method and Attached messages may be fetched using the Email/parse method and
the blobId. Note, an HTML body part may reference image parts in the blobId. Note, an HTML body part may reference image parts in
attachments using "cid:" links to reference the _Content-Id_ or by attachments using "cid:" links to reference the _Content-Id_ or by
referencing the _Content-Location_. 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: to "true" if the _attachments_ list contains at least one item
that does not have "Content-Disposition: inline". The server MAY
* The _attachments_ list contains at least one item that does not ignore parts in this list that are processed automatically in some
have "Content-Disposition: inline". The server MAY ignore way, or are referenced as embedded images in one of the "text/
parts in this list that are processed automatically in some html" parts of the message. The server MAY set hasAttachment
way, or are referenced as embedded images in one of the "text/ based on implementation-defined or site configurable heuristics.
html" parts of the message.
The server MAY set hasAttachment based on implementation-defined
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; skipping quoted sections and salutations
salutations and collapsing white-space can result in a more useful and collapsing white-space can result in a more useful preview.
preview.
The exact algorithm for decomposing bodyStructure into textBody, The exact algorithm for decomposing bodyStructure into textBody,
htmlBody and attachments part lists is not mandated, as this is a htmlBody and attachments part lists is not mandated, as this is a
quality-of-service implementation issue and likely to require quality-of-service implementation issue and likely to require
workarounds for malformed content discovered over time. However, the workarounds for malformed content discovered over time. However, the
following algorithm (expressed here in JavaScript) is suggested as a following algorithm (expressed here in JavaScript) is suggested as a
starting point, based on real-world experience: starting point, based on real-world experience:
function isInlineMediaType ( type ) { function isInlineMediaType ( type ) {
return type.startsWith( 'image/' ) || return type.startsWith( 'image/' ) ||
skipping to change at page 35, line 7 skipping to change at page 35, line 7
_bodyValues_ property includes any "text/*" part in the _bodyValues_ property includes any "text/*" part in the
"bodyStructure" property. "bodyStructure" property.
o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than
zero, the _value_ property of any EmailBodyValue object returned zero, the _value_ property of any EmailBodyValue object returned
in _bodyValues_ MUST be truncated if necessary so it does not in _bodyValues_ MUST be truncated if necessary so it does not
exceed this number of octets in size. If "0" (the default), no exceed this number of octets in size. If "0" (the default), no
truncation occurs. The server MUST ensure the truncation results truncation occurs. The server MUST ensure the truncation results
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">".
is no requirement for the truncated form to be a balanced tree or There is no requirement for the truncated form to be a balanced
valid HTML (indeed, the original source may well be neither of tree or valid HTML (indeed, the original source may well be
these things). neither of 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", [ "id", "blobId", "threadId", "mailboxIds", "keywords", "size",
"receivedAt", "messageId", "inReplyTo", "references", "sender", "from", "receivedAt", "messageId", "inReplyTo", "references", "sender", "from",
"to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment", "to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment",
"preview", "bodyValues", "textBody", "htmlBody", "attachments" ] "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
skipping to change at page 36, line 29 skipping to change at page 36, line 29
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.
4.2.1. Example 4.2.1. Example
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@example.com"}], "from": [{ "name": "Joe Bloggs", "email": "joe@example.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",
skipping to change at page 37, line 37 skipping to change at page 37, line 37
}], }],
"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": "-- Sent by your friendly mailing list ..."
} }
} }
} }
], ],
notFound: [ "f123u456" ] "notFound": [ "f123u456" ]
}, "#1" ] }, "#1" ]]
4.3. Email/changes 4.3. Email/changes
Standard "/changes" method. If generating intermediate states for a Standard "/changes" method. If generating intermediate states for a
large set of changes, it is recommended that newer changes are large set of changes, it is recommended that newer changes are
returned first, as these are generally of more interest to users. returned first, as these are generally of more interest to users.
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:
o *collapseThreads*: "Boolean" (default: false) If "true", emails in o *collapseThreads*: "Boolean" (default: false) If "true", emails in
the same thread as a previous email in the list (given the filter the same thread as a previous email in the list (given the filter
and sort order) will be removed from the list. This means at most and sort order) will be removed from the list. This means only
only one email will be included in the list for any given thread. one email at most will be included in the list for any given
thread.
In quality implementations, the query "total" property is expected to In quality implementations, the query "total" property is expected to
be fast to calculate when the filter consists solely of a single be fast to calculate when the filter consists solely of a single
"inMailbox" property, as it is the same as the totalEmails or "inMailbox" property, as it is the same as the totalEmails or
totalThreads properties (depending on whether collapseThreads is totalThreads properties (depending on whether collapseThreads is
true) of the associated Mailbox object. true) of the associated Mailbox object.
4.4.1. Filtering 4.4.1. Filtering
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 *inMailbox*: "String" A mailbox id. An email must be in this o *inMailbox*: "String" A mailbox id. An email must be in this
mailbox to match the condition. mailbox to match the condition.
o *inMailboxOtherThan*: "String[]" A list of mailbox ids. An email o *inMailboxOtherThan*: "String[]" A list of mailbox ids. An email
must be in at least one mailbox not in this list to match the must be in at least one mailbox not in this list to match the
condition. This is to allow messages solely in trash/spam to be condition. This is to allow messages solely in trash/spam to be
easily excluded from a search. easily excluded from a search.
o *before*: "UTCDate" The _receivedAt_ date of the email must be o *before*: "UTCDate" The _receivedAt_ date-time of the email must
before this date to match the condition. be before this date-time to match the condition.
o *after*: "UTCDate" The _receivedAt_ date of the email must be on o *after*: "UTCDate" The _receivedAt_ date-time of the email must be
or after this date to match the condition. the same or after this date-time to match the condition.
o *minSize*: "PositiveInt" The _size_ of the email in octets must be o *minSize*: "PositiveInt" The _size_ of the email in octets must be
equal to or greater than this number to match the condition. equal to or greater than this number to match the condition.
o *maxSize*: "PositiveInt" The size of the email in octets must be o *maxSize*: "PositiveInt" The _size_ of the email in octets must be
less than this number to match the condition. less than this number to match the condition.
o *allInThreadHaveKeyword*: "String" All emails (including this one) o *allInThreadHaveKeyword*: "String" All emails (including this one)
in the same thread as this email must have the given keyword to in the same thread as this email must have the given keyword to
match the condition. match the condition.
o *someInThreadHaveKeyword*: "String" At least one email (possibly o *someInThreadHaveKeyword*: "String" At least one email (possibly
this one) in the same thread as this email must have the given this one) in the same thread as this email must have the given
keyword to match the condition. keyword to match the condition.
skipping to change at page 39, line 46 skipping to change at page 39, line 50
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 body parts of o *body*: "String" Looks for the text in one of the body parts of
the email. The server MAY exclude MIME body parts with content the email. The server MAY exclude MIME body parts with content
media types other than "text/_" and "message/_" from consideration media types other than "text/_" and "message/_" from consideration
in search matching. Care should be taken to match based on the in search matching. Care should be taken to match based on the
text content actually presented to an end-user by viewers for that text content actually presented to an end-user by viewers for that
media type, or otherwise identified as appropriate for search media type, or otherwise identified as appropriate for search
indexing. Matching document metadata uninteresting to an end-user indexing. Matching document metadata uninteresting to an end-user
(e.g., markup tag and attribute names), is undesirable. (e.g., markup tag and attribute names) is undesirable.
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
specified, ALL must apply for the condition to be "true" (it is specified, ALL must apply for the condition to be "true" (it is
skipping to change at page 40, line 50 skipping to change at page 41, line 7
4.4.2. Sorting 4.4.2. Sorting
The following properties MUST be supported for sorting: The following properties MUST be supported for sorting:
o *receivedAt* - The _receivedAt_ date as returned in the Email o *receivedAt* - The _receivedAt_ date as returned in the Email
object. object.
The following properties SHOULD be supported for sorting: The following properties SHOULD be supported for sorting:
o *size* - The size as returned in the Email object. o *size* - The _size_ as returned in the Email object.
o *from* - This is taken to be either the "name" part, or if o *from* - This is taken to be either the "name" part, or if
"null"/empty then the "email" part, of the *first* EmailAddress "null"/empty then the "email" part, of the *first* EmailAddress
object in the _from_ property. If still none, consider the value object in the _from_ property. If still none, consider the value
to be the empty string. to be the empty string.
o *to* - This is taken to be either the "name" part, or if o *to* - This is taken to be either the "name" part, or if
"null"/empty then the "email" part, of the *first* EmailAddress "null"/empty then the "email" part, of the *first* EmailAddress
object in the _to_ property. If still none, consider the value to object in the _to_ property. If still none, consider the value to
be the empty string. be the empty string.
skipping to change at page 41, line 36 skipping to change at page 41, line 40
(regardless of mailbox) have the keyword given as an additional (regardless of mailbox) have the keyword given as an additional
_keyword_ property on the _Comparator_ object. _keyword_ property on the _Comparator_ object.
o *someInThreadHaveKeyword* - This value MUST be considered "true" o *someInThreadHaveKeyword* - This value MUST be considered "true"
for the email if *any* of the emails in the same thread for the email if *any* of the emails in the same thread
(regardless of mailbox) have the keyword given as an additional (regardless of mailbox) have the keyword given as an additional
_keyword_ property on the _Comparator_ object. _keyword_ property on the _Comparator_ object.
The server MAY support sorting based on other properties as well. A The server MAY support sorting based on other properties as well. A
client can discover which properties are supported by inspecting the client can discover which properties are supported by inspecting the
server's _capabilities_ object (see section 1). server's _capabilities_ object (see section 1.3).
Example sort: Example sort:
[{ [{
"property": "someInThreadHaveKeyword", "property": "someInThreadHaveKeyword",
"keyword": "$flagged", "keyword": "$flagged",
"isAscending": false, "isAscending": false
}, { }, {
"property": "subject", "property": "subject",
"collation": "i;ascii-casemap" "collation": "i;ascii-casemap"
}, { }, {
"property": "receivedAt", "property": "receivedAt",
"isAscending": false, "isAscending": false
}] }]
This would sort emails in flagged threads first (the thread is This would sort emails in flagged threads first (the thread is
considered flagged if any email within it is flagged), and then in considered flagged if any email within it is flagged), and then in
subject order, then newest first for messages with the same subject. subject order, then newest first for messages with the same subject.
If two emails have both identical flagged status, subject and date, If two emails have both identical flagged status, subject and date,
the order is server-dependent but must be stable. the order is server-dependent but must be stable.
4.4.3. Thread collapsing 4.4.3. Thread collapsing
When _collapseThreads_ is "true", then after filtering and sorting When _collapseThreads_ is "true", then after filtering and sorting
the email list, the list is further winnowed by removing any emails the email list, the list is further winnowed by removing any emails
for a thread id that has already been seen (when passing through the for a thread id that has already been seen (when passing through the
list sequentially). A thread will therefore only appear *once* in list sequentially). A thread will therefore only appear *once* in
the result, at the position of the first email in the list that the result, at the position of the first email in the list that
belongs to the thread (given the current sort/filter). belongs to the thread (given the current sort/filter).
4.4.4. Response
The response has the following additional argument:
o *collapseThreads*: "Boolean" The _collapseThreads_ value that was
used when calculating the email list for this call.
4.5. Email/queryChanges 4.5. Email/queryChanges
Standard "/queryChanges" method, with the following additional Standard "/queryChanges" method, with the following additional
arguments: arguments:
o *collapseThreads*: "Boolean" (default: false) The o *collapseThreads*: "Boolean" (default: false) The
_collapseThreads_ argument that was used with _Email/query_. _collapseThreads_ argument that was used with _Email/query_.
The response has the following additional argument:
o *collapseThreads*: "Boolean" The _collapseThreads_ value that was
used when calculating the email list for this call.
4.6. Email/set 4.6. Email/set
Standard "/set" method. The _Email/set_ method encompasses: Standard "/set" method. The _Email/set_ method encompasses:
o Creating a draft o Creating a draft
o Changing the keywords of an email (e.g. unread/flagged status) o Changing the keywords of an email (e.g. unread/flagged status)
o Adding/removing an email to/from mailboxes (moving a message) o Adding/removing an email to/from mailboxes (moving a message)
skipping to change at page 44, line 26 skipping to change at page 44, line 26
"invalidProperties" error, however a server MAY choose to modify the "invalidProperties" error, however a server MAY choose to modify the
Email (e.g. choose between conflicting headers, use a different Email (e.g. choose between conflicting headers, use a different
content-encoding etc.) to comply with its requirements instead. content-encoding etc.) to comply with its requirements instead.
The server MAY also choose to set additional headers. If not The server MAY also choose to set additional headers. If not
included, the server MUST generate and set a "Message-ID" header included, the server MUST generate and set a "Message-ID" header
field in conformance with [RFC5322] section 3.6.4, and a "Date" field in conformance with [RFC5322] section 3.6.4, and a "Date"
header field in conformance with section 3.6.1. header field in conformance with section 3.6.1.
The final RFC5322 email generated may be invalid. For example, if it The final RFC5322 email generated may be invalid. For example, if it
is a half-finished draft, the "To" field may data that does not is a half-finished draft, the "To" field may have a value that does
currently conform to the required syntax for this header field. The not conform to the required syntax for this header field. The
message will be checked for strict conformance when submitted for message will be checked for strict conformance when submitted for
sending (see the EmailSubmission object description). sending (see the EmailSubmission object description).
Destroying an email removes it from all mailboxes to which it Destroying an email removes it from all mailboxes to which it
belonged. To just delete an email to trash, simply change the belonged. To just delete an email to trash, simply change the
"mailboxIds" property so it is now in the mailbox with "role == "mailboxIds" property so it is now in the mailbox with "role ==
"trash"", and remove all other mailbox ids. "trash"", and remove all other mailbox ids.
When emptying the trash, clients SHOULD NOT destroy emails which are When emptying the trash, clients SHOULD NOT destroy emails which are
also in a mailbox other than trash. For those emails, they SHOULD also in a mailbox other than trash. For those emails, they SHOULD
skipping to change at page 45, line 30 skipping to change at page 45, line 30
content, or even just with the same [RFC5322] Message-ID, to coexist content, or even just with the same [RFC5322] Message-ID, to coexist
within an account; if the target account already has the email the within an account; if the target account already has the email the
copy will be rejected with a standard "alreadyExists" error. copy will be rejected with a standard "alreadyExists" error.
For successfully copied Email objects, the _created_ response For successfully copied Email objects, the _created_ response
contains the _id_, _blobId_, _threadId_ and _size_ properties of the contains the _id_, _blobId_, _threadId_ and _size_ properties of the
new object. new object.
4.8. Email/import 4.8. Email/import
The _Email/import_ method adds [RFC5322] messages to a user's set of The _Email/import_ method adds [RFC5322] messages to the set of
emails. The messages must first be uploaded as a file using the emails in an account. The messages must first be uploaded as files
standard upload mechanism. It takes the following arguments: using the standard upload mechanism. It takes the following
arguments:
o *accountId*: "String" The id of the account to use. o *accountId*: "String" The id of the account to use.
o *ifInState*: "String|null" This is a state string as returned by o *ifInState*: "String|null" This is a state string as returned by
the _Email/get_ method. If supplied, the string must match the the _Email/get_ method. If supplied, the string must match the
current state of the account referenced by the accountId, current state of the account referenced by the accountId,
otherwise the method will be aborted and a "stateMismatch" error otherwise the method will be aborted and a "stateMismatch" error
returned. If "null", any changes will be applied to the current returned. If "null", any changes will be applied to the current
state. state.
skipping to change at page 46, line 8 skipping to change at page 46, line 11
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 mailboxes to assign o *mailboxIds*: "String[Boolean]" The ids of the mailboxes to 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 apply to o *keywords*: "String[Boolean]" (default: ) The keywords to apply to
the email. the email.
o *receivedAt*: "UTCDate" (default: time of import on server) The o *receivedAt*: "UTCDate" (default: time of most recent Received
_receivedAt_ date to set on the email. header, or time of import on server if none) The _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 referenced by the blobId and applies the given object from the data referenced by the blobId and applies the given
mailboxes, keywords and receivedAt date. mailboxes, keywords and receivedAt date.
The server MAY forbid two email objects with the same exact [RFC5322] The server MAY forbid two email objects with the same exact [RFC5322]
content, or even just with the same [RFC5322] Message-ID, to coexist content, or even just with the same [RFC5322] Message-ID, to coexist
within an account. In this case, it MUST reject attempts to import within an account. In this case, it MUST reject attempts to import
an email considered a duplicate with an "alreadyExists" SetError. An an email considered a duplicate with an "alreadyExists" SetError. An
skipping to change at page 47, line 17 skipping to change at page 47, line 20
for each successfully imported Email. for each successfully imported Email.
o *notCreated*: "String[SetError]" A map of creation id to a o *notCreated*: "String[SetError]" A map of creation id to a
SetError object for each Email that failed to be created. The SetError object for each Email that failed to be created. The
possible errors are defined above. possible errors are defined above.
The following additional errors may be returned instead of the _Foo/ The following additional errors may be returned instead of the _Foo/
copy_ response: copy_ response:
"stateMismatch": An "ifInState" argument was supplied and it does not "stateMismatch": An "ifInState" argument was supplied and it does not
match the current state match the current state.
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
Email objects. This can be used to parse and display attached emails Email objects. This can be used to parse and display attached emails
without having to import them as top-level email objects in the mail without having to import them as top-level email objects in the mail
store in their own right. store in their own right.
The following metadata properties on the Email objects will be "null" The following metadata properties on the Email objects will be "null"
if requested: if requested:
skipping to change at page 48, line 28 skipping to change at page 48, line 31
_bodyValues_ property includes any "text/*" part in the _bodyValues_ property includes any "text/*" part in the
"bodyStructure" property. "bodyStructure" property.
o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than
zero, the _value_ property of any EmailBodyValue object returned zero, the _value_ property of any EmailBodyValue object returned
in _bodyValues_ MUST be truncated if necessary so it does not in _bodyValues_ MUST be truncated if necessary so it does not
exceed this number of octets in size. If "0" (the default), no exceed this number of octets in size. If "0" (the default), no
truncation occurs. The server MUST ensure the truncation results truncation occurs. The server MUST ensure the truncation results
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">".
is no requirement for the truncated form to be a balanced tree or There is no requirement for the truncated form to be a balanced
valid HTML (indeed, the original source may well be neither of tree or valid HTML (indeed, the original source may well be
these things). neither of these things).
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 *parsed*: "String[Email]|null" A map of blob id to parsed Email o *parsed*: "String[Email]|null" A map of blob id to parsed Email
representation for each successfully parsed blob, or "null" if representation for each successfully parsed blob, or "null" if
none. none.
o *notParsable*: "String[]|null" A list of ids given that o *notParsable*: "String[]|null" A list of ids given that
skipping to change at page 50, line 24 skipping to change at page 50, line 29
"from", "from",
"subject", "subject",
"receivedAt", "receivedAt",
"size", "size",
"preview" "preview"
] ]
}, "3" ]] }, "3" ]]
Let's break down the 4 method calls to see what they're doing: Let's break down the 4 method calls to see what they're doing:
1. This asks the server for the ids of the first 30 Email objects in "0": This asks the server for the ids of the first 30 Email objects
the inbox, sorted newest first, ignoring messages from the same in the inbox, sorted newest first, ignoring messages from the same
thread as a newer message in the mailbox (i.e. it is the first 30 thread as a newer message in the mailbox (i.e. it is the first 30
unique threads). unique threads).
2. Now we use a backreference to fetch the thread ids for each of "1": Now we use a back-reference to fetch the thread ids for each of
these email ids. these email ids.
3. Another backreference fetches the Thread object for each of these "2": Another back-reference fetches the Thread object for each of
thread ids. these thread ids.
4. Finally, we fetch the information we need to display the mailbox "3": Finally, we fetch the information we need to display the mailbox
listing ( but no more!) for every message in each of these 30 listing (but no more!) for every message in each of these 30 threads.
threads. The client may aggregate this data for display, for The client may aggregate this data for display, for example showing
example showing the thread as "flagged" if any of the messages in the thread as "flagged" if any of the messages in it contain the
it contain the "$flagged" keyword. "$flagged" keyword.
The response from the server may look something like this: The response from the server may look something like this:
[[ "Email/query", { [[ "Email/query", {
"accountId": "ue150411c", "accountId": "ue150411c",
"filter": {
"inMailbox": "fb666a55"
},
"sort": [{
"property": "receivedAt",
"isAscending": false
}],
"collapseThreads": true,
"queryState": "09aa9a075588-780599:0", "queryState": "09aa9a075588-780599:0",
"canCalculateChanges": true, "canCalculateChanges": true,
"position": 0, "position": 0,
"total": 115, "total": 115,
"ids": [ "Ma783e5cdf5f2deffbc97930a", "M9bd17497e2a99cb345fc1d0a", ... ] "ids": [ "Ma783e5cdf5f2deffbc97930a", "M9bd17497e2a99cb345fc1d0a", ... ]
}, "0" ], }, "0" ],
[ "Email/get", { [ "Email/get", {
"accountId": "ue150411c", "accountId": "ue150411c",
"state": "780599", "state": "780599",
"list": [{ "list": [{
"id": "Ma783e5cdf5f2deffbc97930a", "id": "Ma783e5cdf5f2deffbc97930a",
"threadId": "T36703c2cfe9bd5ed" "threadId": "T36703c2cfe9bd5ed"
}, { }, {
"id": "M9bd17497e2a99cb345fc1d0a" "id": "M9bd17497e2a99cb345fc1d0a",
"threadId": "T0a22ad76e9c097a1", "threadId": "T0a22ad76e9c097a1"
}, ... ], }, ... ],
"notFound": [] "notFound": []
}, "1" ], }, "1" ],
[ "Thread/get", { [ "Thread/get", {
"accountId": "ue150411c", "accountId": "ue150411c",
"state": "22a8728b", "state": "22a8728b",
"list": [{ "list": [{
"id": "T36703c2cfe9bd5ed" "id": "T36703c2cfe9bd5ed",
"emailIds": [ "Ma783e5cdf5f2deffbc97930a" ], "emailIds": [ "Ma783e5cdf5f2deffbc97930a" ]
}, { }, {
"id": "T0a22ad76e9c097a1" "id": "T0a22ad76e9c097a1",
"emailIds": [ "M3b568670a63e5d100f518fa5", "M9bd17497e2a99cb345fc1d0a" ], "emailIds": [ "M3b568670a63e5d100f518fa5", "M9bd17497e2a99cb345fc1d0a" ]
}, ... ], }, ... ],
"notFound": [] "notFound": []
}, "2" ], }, "2" ],
[ "Email/get", { [ "Email/get", {
"accountId": "ue150411c", "accountId": "ue150411c",
"state": "780599", "state": "780599",
"list": [{ "list": [{
"id": "Ma783e5cdf5f2deffbc97930a" "id": "Ma783e5cdf5f2deffbc97930a",
"threadId": "T36703c2cfe9bd5ed", "threadId": "T36703c2cfe9bd5ed",
"mailboxIds": { "mailboxIds": {
"fb666a55": true "fb666a55": true
}, },
"keywords": { "keywords": {
"$seen": true, "$seen": true,
"$flagged": true "$flagged": true
}, },
"hasAttachment": true, "hasAttachment": true,
"from": [{ "from": [{
skipping to change at page 52, line 4 skipping to change at page 51, line 49
"fb666a55": true "fb666a55": true
}, },
"keywords": { "keywords": {
"$seen": true, "$seen": true,
"$flagged": true "$flagged": true
}, },
"hasAttachment": true, "hasAttachment": true,
"from": [{ "from": [{
"email": "jdoe@example.com", "email": "jdoe@example.com",
"name": "Jane Doe" "name": "Jane Doe"
}], }],
"subject": "The Big Reveal", "subject": "The Big Reveal",
"receivedAt": "2018-06-27T00:20:35Z", "receivedAt": "2018-06-27T00:20:35Z",
"size": 175047, "size": 175047,
"preview": "As you may be aware, we are required to prepare a presentation where we wow a panel of 5 random members of the public, on or before 30 June each year. We have drafted the ...", "preview": "As you may be aware, we are required to prepare a presentation where we wow a panel of 5 random members of the public, on or before 30 June each year. We have drafted the ..."
}, },
... ...
], ],
"notFound": [] "notFound": []
}, "3" ]] }, "3" ]]
Now, on another device the user marks the first message as unread, Now, on another device the user marks the first message as unread,
sending this API request: sending this API request:
[[ "Email/set", { [[ "Email/set", {
skipping to change at page 52, line 32 skipping to change at page 52, line 28
"Ma783e5cdf5f2deffbc97930a": { "Ma783e5cdf5f2deffbc97930a": {
"keywords/$seen": null "keywords/$seen": null
} }
} }
}, "0" ]] }, "0" ]]
The server applies this and sends the success response: The server applies this and sends the success response:
[[ "Email/set", { [[ "Email/set", {
"accountId": "ue150411c", "accountId": "ue150411c",
"oldState": "780605" "oldState": "780605",
"newState": "780606", "newState": "780606",
"updated": { "updated": {
"Ma783e5cdf5f2deffbc97930a": null "Ma783e5cdf5f2deffbc97930a": null
}, },
... ...
}, "0" ]] }, "0" ]]
The user also deletes a few messages, and then a new message arrives. The user also deletes a few messages, and then a new message arrives.
Back on our original machine, we receive a push update that the state Back on our original machine, we receive a push update that the state
skipping to change at page 54, line 5 skipping to change at page 53, line 28
}], }],
"collapseThreads": true, "collapseThreads": true,
"sinceQueryState": "09aa9a075588-780599:0", "sinceQueryState": "09aa9a075588-780599:0",
"upToId": "Mc2781d5e856a908d8a35a564", "upToId": "Mc2781d5e856a908d8a35a564",
"maxChanges": 25, "maxChanges": 25,
"calculateTotal": true "calculateTotal": true
}, "11" ]] }, "11" ]]
The response: The response:
[ "Email/changes", { [[ "Email/changes", {
"accountId": "ue150411c", "accountId": "ue150411c",
"oldState": "780605", "oldState": "780605",
"newState": "780800", "newState": "780800",
"hasMoreChanges": false, "hasMoreChanges": false,
"created": [ "Me8de6c9f6de198239b982ea2" ], "created": [ "Me8de6c9f6de198239b982ea2" ],
"updated": [ "Ma783e5cdf5f2deffbc97930a" ], "updated": [ "Ma783e5cdf5f2deffbc97930a" ],
"destroyed": [ "M9bd17497e2a99cb345fc1d0a", ... ], "destroyed": [ "M9bd17497e2a99cb345fc1d0a", ... ]
}, "3" ], }, "3" ],
[ "Email/queryChanges", { [ "Email/queryChanges", {
"accountId": "ue150411c", "accountId": "ue150411c",
"oldQueryState": "09aa9a075588-780599:0" "oldQueryState": "09aa9a075588-780599:0",
"newQueryState": "e35e9facf117-780615:0", "newQueryState": "e35e9facf117-780615:0",
"filter": { "added": [{
"inMailbox": "fb666a55" "id": "Me8de6c9f6de198239b982ea2",
}, "index": 0
"sort": [{ }],
"property": "receivedAt", "removed": [ "M9bd17497e2a99cb345fc1d0a" ],
"isAscending": false "total": 115
}], }, "11" ]]
"collapseThreads": true,
"upToId": "Mc2781d5e856a908d8a35a564",
"added": [{
"id": "Me8de6c9f6de198239b982ea2",
"index": 0
}],
"removed": [ "M9bd17497e2a99cb345fc1d0a" ],
"total": 115,
}, "11" ],
The client can update its local cache of the query results by The client can update its local cache of the query results by
removing "M9bd17497e2a99cb345fc1d0a" and then splicing in removing "M9bd17497e2a99cb345fc1d0a" and then splicing in
"Me8de6c9f6de198239b982ea2" at position 0. As it does not have the "Me8de6c9f6de198239b982ea2" at position 0. As it does not have the
data for this new email, it will then fetch it (it also could have data for this new email, it will then fetch it (it also could have
done this in the same request using backreferences). done this in the same request using back-references).
It knows something has changed about "Ma783e5cdf5f2deffbc97930a", so It knows something has changed about "Ma783e5cdf5f2deffbc97930a", so
it will refetch the mailboxes and keywords (the only mutable it will refetch the mailboxes and keywords (the only mutable
properties) for this email too. properties) for this email too.
The user composes a new message and saves a draft. The client sends: The user composes a new message and saves a draft. The client sends:
[[ "Email/set", { [[ "Email/set", {
"accountId": "ue150411c", "accountId": "ue150411c",
"create": { "create": {
skipping to change at page 57, line 22 skipping to change at page 57, line 22
"id": "M138f9954a5cd2423daeafa55", "id": "M138f9954a5cd2423daeafa55",
"blobId": "G6b9fb047cba722c48c611e79233d057c6b0b74e8", "blobId": "G6b9fb047cba722c48c611e79233d057c6b0b74e8",
"threadId": "T2f242ea424a4079a", "threadId": "T2f242ea424a4079a",
"size": 11721 "size": 11721
} }
}, },
"notCreated": null "notCreated": null
}, "0" ], }, "0" ],
[ "Email/set", { [ "Email/set", {
"accountId": "ue150411c", "accountId": "ue150411c",
"oldState": "780839" "oldState": "780839",
"newState": "780871", "newState": "780871",
"destroyed": [ "Ma783e5cdf5f2deffbc97930a" ], "destroyed": [ "Ma783e5cdf5f2deffbc97930a" ],
... ...
}, "0" ]] }, "0" ]]
5. Search snippets 5. 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
skipping to change at page 58, line 7 skipping to change at page 58, line 7
o *preview*: "String|null" If text from the filter matches the o *preview*: "String|null" If text from the filter matches the
plain-text or HTML body, this is the relevant section of the body plain-text or HTML body, this is the relevant section of the body
(converted to plain text if originally HTML), HTML-escaped, with (converted to plain text if originally HTML), HTML-escaped, with
matching words/phrases wrapped in "<mark></mark>" tags. It MUST matching words/phrases wrapped in "<mark></mark>" tags. It MUST
NOT be bigger than 255 octets in size. If it does not match, this NOT be bigger than 255 octets in size. If it does not match, this
is "null". is "null".
It is server-defined what is a relevant section of the body for It is server-defined what is a relevant section of the body for
preview. If the server is unable to determine search snippets, it preview. If the server is unable to determine search snippets, it
MUST return "null" for both the _subject_ and _preview_ roperties. MUST return "null" for both the _subject_ and _preview_ properties.
Note, unlike most data types, a SearchSnippet DOES NOT have a Note, unlike most data types, a SearchSnippet DOES NOT have a
property called "id". property called "id".
The following JMAP method is supported: The following JMAP method is supported:
5.1. SearchSnippet/get 5.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" The id of the account to use. o *accountId*: "String" The id of the account to use.
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 in
details. section 4.4 for details.
o *emailIds*: "String[]" The list of ids of emails to fetch the o *emailIds*: "String[]" The ids of the emails to fetch snippets
snippets for. 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
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.
o *notFound*: "String[]|null" An array of email ids requested which o *notFound*: "String[]|null" An array of email ids requested which
could not be found, or "null" if all ids were found. could not be found, or "null" if all ids were found.
Since snippets are only based on immutable properties, there is no Since snippets are only based on immutable properties, there is no
state string or update mechanism needed. state string or update mechanism needed.
skipping to change at page 59, line 25 skipping to change at page 59, line 22
"accountId": "ue150411c", "accountId": "ue150411c",
"filter": { "filter": {
"text": "foo" "text": "foo"
}, },
"emailIds": [ "emailIds": [
"M44200ec123de277c0c1ce69c", "M44200ec123de277c0c1ce69c",
"M7bcbcb0b58d7729686e83d99", "M7bcbcb0b58d7729686e83d99",
"M28d12783a0969584b6deaac0", "M28d12783a0969584b6deaac0",
... ...
] ]
}, "tag-0" ] }, "tag-0" ]]
Example response: Example response:
[[ "SearchSnippet/get", { [[ "SearchSnippet/get", {
"accountId": "ue150411c", "accountId": "ue150411c",
"filter": {
"text": "foo"
},
"list": [{ "list": [{
"emailId": "M44200ec123de277c0c1ce69c" "emailId": "M44200ec123de277c0c1ce69c",
"subject": null, "subject": null,
"preview": null "preview": null
}, { }, {
"emailId": "M7bcbcb0b58d7729686e83d99", "emailId": "M7bcbcb0b58d7729686e83d99",
"subject": "The <mark>Foo</mark>sball competition", "subject": "The <mark>Foo</mark>sball competition",
"preview": "...year the <mark>foo</mark>sball competition will be held in the Stadium de ..." "preview": "...year the <mark>foo</mark>sball competition will be held in the Stadium de ..."
}, { }, {
"emailId": "M28d12783a0969584b6deaac0", "emailId": "M28d12783a0969584b6deaac0",
"subject": null, "subject": null,
"preview": "...mail <mark>Foo</mark>/changes results often return current-state-minus-1 rather than new..." "preview": "...the <mark>Foo</mark>/bar method results often returns &lt;1 widget rather than the complete..."
}, },
... ...
], ],
"notFound": null "notFound": null
}, "tag-0" ]] }, "tag-0" ]]
6. Identities 6. Identities
An *Identity* object stores information about an email address (or An *Identity* object stores information about an email address (or
domain) the user may send from. It has the following properties: domain) the user may send from. It has the following properties:
o *id*: "String" (immutable; server-set) The id of the identity. o *id*: "String" (immutable; server-set) The id of the identity.
o *name*: "String" (default: "") The "From" _name_ the client SHOULD o *name*: "String" (default: "") The "From" _name_ the client SHOULD
use when creating a new message from this identity. use when creating a new message from this identity.
skipping to change at page 61, line 30 skipping to change at page 61, line 23
For *create*: For *create*:
o "forbiddenFrom": The user is not allowed to send from the address o "forbiddenFrom": The user is not allowed to send from the address
given as the _email_ property of the identity. given as the _email_ property of the identity.
6.4. Example 6.4. Example
Request: Request:
[ "Identity/get", { [ "Identity/get", {
"accountId": "acme", "accountId": "acme"
}, "0" ] }, "0" ]
with response: with response:
[ "Identity/get", { [ "Identity/get", {
"accountId": "acme", "accountId": "acme",
"state": "99401312ae-11-333", "state": "99401312ae-11-333",
"list": [ "list": [
{ {
"id": "3301-222-11_22AAz", "id": "3301-222-11_22AAz",
"name": "Joe Bloggs", "name": "Joe Bloggs",
"email": "joe@example.com", "email": "joe@example.com",
"replyTo": null, "replyTo": null,
"bcc": [{ "bcc": [{
"name": null, "name": null,
"email": "joe+archive@example.com" "email": "joe+archive@example.com"
}], }],
"textSignature": "-- \nJoe Bloggs\nMaster of Email", "textSignature": "-- \nJoe Bloggs\nMaster of Email",
"htmlSignature": "<div><b>Joe Bloggs</b></div><div>Master of Email</div>", "htmlSignature": "<div><b>Joe Bloggs</b></div><div>Master of Email</div>",
"mayDelete": false, "mayDelete": false
}, },
{ {
"id": "9911312-11_22AAz", "id": "9911312-11_22AAz",
"name": "Joe B", "name": "Joe B",
"email": "joebloggs@example.com", "email": "*@example.com",
"replyTo": null, "replyTo": null,
"bcc": null, "bcc": null,
"textSignature": "", "textSignature": "",
"htmlSignature": "", "htmlSignature": "",
"mayDelete": true "mayDelete": true
} }
], ],
"notFound": [] "notFound": []
}, "0" ] }, "0" ]
skipping to change at page 63, line 9 skipping to change at page 63, line 9
associate with this submission. associate with this submission.
o *emailId*: "String" (immutable) The id of the email to send. The o *emailId*: "String" (immutable) The id of the email to send. The
email being sent does not have to be a draft, for example when email being sent does not have to be a draft, for example when
"redirecting" an existing email to a different address. "redirecting" an existing email to a different address.
o *threadId*: "String" (immutable; server-set) The thread id of the o *threadId*: "String" (immutable; server-set) The thread id of the
email to send. This is set by the server to the _threadId_ email to send. This is set by the server to the _threadId_
property of the email referenced by the _emailId_. property of the email referenced by the _emailId_.
o *envelope*: "Envelope|null" (immutable; default: null) Information o *envelope*: "Envelope|null" (immutable) Information for use when
for use when sending via SMTP. An *Envelope* object has the sending via SMTP. An *Envelope* object has the following
following properties: properties:
* *mailFrom*: "Address" The email address to use as the return * *mailFrom*: "Address" The email address to use as the return
address in the SMTP submission, plus any parameters to pass address in the SMTP submission, plus any parameters to pass
with the MAIL FROM address. The JMAP server MAY allow the with the MAIL FROM address. The JMAP server MAY allow the
address to be the empty string. When a JMAP server performs an address to be the empty string. When a JMAP server performs an
SMTP message submission, it MAY use the same id string for the SMTP message submission, it MAY use the same id string for the
[RFC3461] ENVID parameter and the EmailSubmission object id. [RFC3461] ENVID parameter and the EmailSubmission object id.
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 is a "Mailbox" as used in the Reverse-path or
Forward-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 67, line 38 skipping to change at page 67, line 38
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
must be in this list to match the condition. must be in this list to match the condition.
o *undoStatus*: "String" The EmailSubmission _undoStatus_ property o *undoStatus*: "String" The EmailSubmission _undoStatus_ property
must be identical to the value given to match the condition. must be identical to the value given to match the condition.
o *before*: "UTCDate" The _sendAt_ property of the EmailSubmission o *before*: "UTCDate" The _sendAt_ property of the EmailSubmission
object must be before this date to match the condition. object must be before this date-time to match the condition.
o *after*: "UTCDate" The _sendAt_ property of the EmailSubmission o *after*: "UTCDate" The _sendAt_ property of the EmailSubmission
object must be after this date to match the condition. object must be the same as or after this date-time to match the
condition.
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"
skipping to change at page 68, line 18 skipping to change at page 68, line 18
Standard "/queryChanges" method. Standard "/queryChanges" method.
7.5. EmailSubmission/set 7.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 "#".)
o *onSuccessDestroyEmail*: "String[]|null" A list of o *onSuccessDestroyEmail*: "String[]|null" A list of
_EmailSubmission ids_ for which the email with the corresponding _EmailSubmission ids_ for which the email with the corresponding
emailId should be destroyed if the create/update/destroy succeeds. emailId should be destroyed if the create/update/destroy succeeds.
(For references to EmailSubmission creations, this is equivalent (For references to EmailSubmission creations, this is equivalent
to a back reference so the id will be the creation id prefixed to a back-reference so the id will be the creation id prefixed
with a "#".) with a "#".)
A single implicit _Email/set_ call MUST be made after all A single implicit _Email/set_ call MUST be made after all
EmailSubmission create/update/destroy requests have been processed to EmailSubmission create/update/destroy requests have been processed to
perform any changes requested in these two arguments. The response perform any changes requested in these two arguments. The response
to this MUST be returned after the _EmailSubmission/set_ response. to this MUST be returned after the _EmailSubmission/set_ response.
An email is sent by creating a EmailSubmission object. When An email is sent by creating an EmailSubmission object. When
processing each create, the server must check that the email is processing each create, the server must check that the email is
valid, and the user has sufficient authorization to send it. If the valid, and the user has sufficient authorization to send it. If the
creation succeeds, the email will be sent to the recipients given in creation succeeds, the email will be sent to the recipients given in
the envelope _rcptTo_ parameter. The server MUST remove any _Bcc_ the envelope _rcptTo_ parameter. The server MUST remove any _Bcc_
header present on the email during delivery. The server MAY add or header present on the email during delivery. The server MAY add or
remove other headers from the submitted email, or make further remove other headers from the submitted email, or make further
alterations in accordance with the server's policy during delivery. alterations in accordance with the server's policy during delivery.
If the referenced email is destroyed at any point after the If the referenced email is destroyed at any point after the
EmailSubmission object is created, this MUST NOT change the behaviour EmailSubmission object is created, this MUST NOT change the behaviour
skipping to change at page 71, line 13 skipping to change at page 71, line 13
tag as they are due to the same call in the request: tag as they are due to the same call in the request:
[[ "EmailSubmission/set", { [[ "EmailSubmission/set", {
"accountId": "ue411d190", "accountId": "ue411d190",
"oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21", "oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21",
"newState": "355421f6-8aed-4cf4-a0c4-7377e951af36", "newState": "355421f6-8aed-4cf4-a0c4-7377e951af36",
"created": { "created": {
"k1490": { "k1490": {
"id": "3bab7f9a-623e-4acf-99a5-2e67facb02a0" "id": "3bab7f9a-623e-4acf-99a5-2e67facb02a0"
} }
}, }
"notCreated": null,
"updated": null,
"notUpdated": null,
"destroyed": null,
"notDestroyed": null
}, "0" ], }, "0" ],
[ "Email/set", { [ "Email/set", {
"accountId": "ue411d190", "accountId": "ue411d190",
"oldState": "778193", "oldState": "778193",
"newState": "778197", "newState": "778197",
"created": null,
"notCreated": null,
"updated": { "updated": {
"M7f6ed5bcfd7e2604d1753f6c": null "M7f6ed5bcfd7e2604d1753f6c": null
}, }
"notUpdated": null,
"destroyed": null,
"notDestroyed": null
}, "0" ]] }, "0" ]]
If the email submission was not accepted on the other hand, the If the email submission was not accepted on the other hand, the
response may look like this: response may look like this:
[[ "EmailSubmission/set", { [[ "EmailSubmission/set", {
"accountId": "ue411d190", "accountId": "ue411d190",
"oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21", "oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21",
"newState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21", "newState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21",
"created": null,
"notCreated": { "notCreated": {
"k1490": { "k1490": {
"type": "tooManyRecipients", "type": "tooManyRecipients",
"maxRecipients": 10 "maxRecipients": 10
} }
}, }
"updated": null,
"notUpdated": null,
"destroyed": null,
"notDestroyed": null
}, "0" ]] }, "0" ]]
8. Vacation response 8. Vacation response
A vacation response automatically sends a reply to messages sent to a A vacation response automatically sends a reply to messages sent to a
particular account, to inform the original sender that their message particular account, to inform the original sender that their message
may not be processed for some time. Automated message sending can may not be read for some time. Automated message sending can produce
produce undesireable behaviour. To avoid this, implementors MUST undesirable behaviour. To avoid this, implementors MUST follow the
follow the recommendations set forth in [RFC3834]. recommendations set forth in [RFC3834].
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; server-set) The id of the object.
ever one vacation response object, and its id is ""singleton"". There is only 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
email arrives between the _fromDate_ and _toDate_? email arrives between the _fromDate_ and _toDate_?
o *fromDate*: "UTCDate|null" If _isEnabled_ is "true", the date/time o *fromDate*: "UTCDate|null" If _isEnabled_ is "true" emails that
in UTC after which emails that arrive should receive the user's arrive on or after this date-time should receive the user's
vacation response. If "null", the vacation response is effective vacation response. If "null", the vacation response is effective
immediately. immediately.
o *toDate*: "UTCDate|null" If _isEnabled_ is "true", the date/time o *toDate*: "UTCDate|null" If _isEnabled_ is "true", emails that
in UTC after which emails that arrive should no longer receive the arrive before this date-time should receive the user's vacation
user's vacation response. If "null", the vacation response is response. If "null", the vacation response is effective
effective indefinitely. indefinitely.
o *subject*: "String|null" The subject that will be used by the o *subject*: "String|null" The subject that will be used by the
message sent in response to emails when the vacation response is message sent in response to emails when the vacation response is
enabled. If null, an appropriate subject SHOULD be set by the enabled. If null, an appropriate subject SHOULD be set by the
server. server.
o *textBody*: "String|null" The plain text part of the message to o *textBody*: "String|null" The plain text body to send in response
send in response to emails when the vacation response is enabled. to emails when the vacation response is enabled. If this is
If this is "null", when the vacation message is sent a plain-text "null", when the vacation message is sent a plain-text body part
body part SHOULD be generated from the _htmlBody_ but the server SHOULD be generated from the _htmlBody_ but the server MAY choose
MAY choose to send the response as HTML only. to send the response as HTML only.
o *htmlBody*: "String|null" The HTML message to send in response to o *htmlBody*: "String|null" The HTML body to send in response to
emails when the vacation response is enabled. If this is "null", emails when the vacation response is enabled. If this is "null",
when the vacation message is sent an HTML body part MAY be when the vacation message is sent an HTML body part MAY be
generated from the _textBody_, or the server MAY choose to send generated from the _textBody_, or the server MAY choose to send
the response as plain-text only. the response as plain-text only.
The following JMAP methods are supported: The following JMAP methods are supported:
8.1. VacationResponse/get 8.1. VacationResponse/get
Standard "/get" method. Standard "/get" method.
skipping to change at page 75, line 18 skipping to change at page 75, line 6
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
implementers 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% accuracy 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
mitigate a number of risks. This will: mitigate a number of risks. This will:
* 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
skipping to change at page 76, line 18 skipping to change at page 76, line 6
9.3. Email submission 9.3. Email submission
SMTP submission servers [RFC6409] use a number of mechanisms to SMTP submission servers [RFC6409] use a number of mechanisms to
mitigate damage caused by compromised user accounts and end-user mitigate damage caused by compromised user accounts and end-user
systems including rate limiting, anti-virus/anti-spam milters and systems including rate limiting, anti-virus/anti-spam milters and
other technologies. The technologies work better when they have more other technologies. The technologies work better when they have more
information about the client connection. If JMAP email submission is information about the client connection. If JMAP email submission is
implemented as a proxy to an SMTP Submission server, it is useful to implemented as a proxy to an SMTP Submission server, it is useful to
communicate this information from the JMAP proxy to the submission communicate this information from the JMAP proxy to the submission
server. The de-facto XCLIENT extension to SMTP server. The de-facto XCLIENT extension to SMTP
(<http://www.postfix.org/XCLIENT_README.html>) can be used to do <http://www.postfix.org/XCLIENT_README.html> can be used to do this,
this, but use of an authenticated channel is recommended to limit use but use of an authenticated channel is recommended to limit use of
of that extension to explicitly authorized proxies. that extension to explicitly authorized proxies.
JMAP servers that proxy to an SMTP Submission server SHOULD allow use JMAP servers that proxy to an SMTP Submission server SHOULD allow use
of the _submissions_ port [RFC8314] and SHOULD implement SASL PLAIN of the _submissions_ port [RFC8314] and SHOULD implement SASL PLAIN
over TLS [RFC4616] and/or TLS client certificate authentication with over TLS [RFC4616] and/or TLS client certificate authentication with
SASL EXTERNAL [RFC4422] appendix A. Implementation of a mechanism SASL EXTERNAL [RFC4422] appendix A. Implementation of a mechanism
similar to SMTP XCLIENT is strongly encouraged. similar 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 encouraged to integrate with third-party milter protocol is strongly encouraged to integrate with third-party
skipping to change at page 77, line 47 skipping to change at page 77, line 34
First, the name of the registry is changed to the "IMAP and JMAP First, the name of the registry is changed to the "IMAP and JMAP
keywords Registry". keywords Registry".
Second, a scope column is added to the template and registry Second, a scope column is added to the template and registry
indicating whether a keyword applies to IMAP-only, JMAP-only, both, indicating whether a keyword applies to IMAP-only, JMAP-only, both,
or reserved. All keywords presently in the IMAP keyword registry or reserved. All keywords presently in the IMAP keyword registry
will be marked with a scope of both. The "reserved" status can be 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 used to prevent future registration of a name that would be confusing
if registered. Registration of keywords with scope 'reserved' omit if registered. Registration of keywords with scope 'reserved' omit
most fields in the registration template (see example for "$recent" most fields in the registration template (see registration of
subsection of this section); such registrations are intended to be "$recent" below for an example); such registrations are intended to
infrequent. be infrequent.
IMAP clients MAY silently ignore any keywords marked JMAP-only or IMAP clients MAY silently ignore any keywords marked JMAP-only or
reserved in the event they appear in protocol. JMAP clients MAY reserved in the event they appear in protocol. JMAP clients MAY
silently ignore any keywords marked IMAP-only or reserved in the silently ignore any keywords marked IMAP-only or reserved in the
event they appear in protocol. event they appear in protocol.
New JMAP-only keywords are registered in the following sub-sections. New JMAP-only keywords are registered in the following sub-sections.
These keywords correspond to IMAP system keywords and are thus not These keywords correspond to IMAP system keywords and are thus not
appropriate for use in IMAP. These keywords can not be subsequently appropriate for use in IMAP. These keywords can not be subsequently
registered for use in IMAP except via standards action. registered for use in IMAP except via standards action.
skipping to change at page 83, line 35 skipping to change at page 83, line 25
10.6.5. tooManyMailboxes 10.6.5. tooManyMailboxes
JMAP Error Code: tooManyMailboxes JMAP Error Code: tooManyMailboxes
Intended use: common Intended use: common
Change controller: IETF Change controller: IETF
Reference: This document, section 4.6 Reference: This document, section 4.6
10.6.6. emailNotFound 10.6.6. invalidEmail
JMAP Error Code: emailNotFound
Intended use: common
Change controller: IETF
Reference: This document, section 7.5
10.6.7. emailTooLarge
JMAP Error Code: emailTooLarge
Intended use: common
Change controller: IETF
Reference: This document, section 7.5
10.6.8. invalidEmail
JMAP Error Code: invalidEmail JMAP Error Code: invalidEmail
Intended use: common Intended use: common
Change controller: IETF Change controller: IETF
Reference: This document, section 7.5 Reference: This document, section 7.5
10.6.9. tooManyRecipients 10.6.7. tooManyRecipients
JMAP Error Code: tooManyRecipients JMAP Error Code: tooManyRecipients
Intended use: common Intended use: common
Change controller: IETF Change controller: IETF
Reference: This document, section 7.5 Reference: This document, section 7.5
10.6.10. noRecipients 10.6.8. noRecipients
JMAP Error Code: noRecipients JMAP Error Code: noRecipients
Intended use: common Intended use: common
Change controller: IETF Change controller: IETF
Reference: This document, section 7.5 Reference: This document, section 7.5
10.6.11. invalidRecipients 10.6.9. invalidRecipients
JMAP Error Code: invalidRecipients JMAP Error Code: invalidRecipients
Intended use: common Intended use: common
Change controller: IETF Change controller: IETF
Reference: This document, section 7.5 Reference: This document, section 7.5
10.6.12. forbiddenMailFrom 10.6.10. forbiddenMailFrom
JMAP Error Code: forbiddenMailFrom JMAP Error Code: forbiddenMailFrom
Intended use: common Intended use: common
Change controller: IETF Change controller: IETF
Reference: This document, section 7.5 Reference: This document, section 7.5
10.6.13. forbiddenFrom 10.6.11. forbiddenFrom
JMAP Error Code: forbiddenFrom JMAP Error Code: forbiddenFrom
Intended use: common Intended use: common
Change controller: IETF Change controller: IETF
Reference: This document, sections 6.3 and 7.5 Reference: This document, sections 6.3 and 7.5
10.6.14. forbiddenToSend 10.6.12. forbiddenToSend
JMAP Error Code: forbiddenToSend JMAP Error Code: forbiddenToSend
Intended use: common Intended use: common
Change controller: IETF Change controller: IETF
Reference: This document, section 7.5 Reference: This document, section 7.5
11. References 11. References
 End of changes. 131 change blocks. 
345 lines changed or deleted 250 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/