| < draft-ietf-jmap-mail-06.txt | draft-ietf-jmap-mail-07.txt > | |||
|---|---|---|---|---|
| JMAP N. Jenkins | JMAP N. Jenkins | |||
| Internet-Draft FastMail | Internet-Draft FastMail | |||
| Updates: 5788 (if approved) July 2, 2018 | Updates: 5788 (if approved) C. Newman | |||
| Intended status: Standards Track | Intended status: Standards Track Oracle | |||
| Expires: January 3, 2019 | Expires: February 8, 2019 August 7, 2018 | |||
| JMAP for Mail | JMAP for Mail | |||
| draft-ietf-jmap-mail-06 | draft-ietf-jmap-mail-07 | |||
| 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 January 3, 2019. | This Internet-Draft will expire on February 8, 2019. | |||
| Copyright Notice | Copyright Notice | |||
| Copyright (c) 2018 IETF Trust and the persons identified as the | Copyright (c) 2018 IETF Trust and the persons identified as the | |||
| document authors. All rights reserved. | document authors. All rights reserved. | |||
| This document is subject to BCP 78 and the IETF Trust's Legal | This document is subject to BCP 78 and the IETF Trust's Legal | |||
| Provisions Relating to IETF Documents | Provisions Relating to IETF Documents | |||
| (https://trustee.ietf.org/license-info) in effect on the date of | (https://trustee.ietf.org/license-info) in effect on the date of | |||
| publication of this document. Please review these documents | publication of this document. Please review these documents | |||
| carefully, as they describe your rights and restrictions with respect | carefully, as they describe your rights and restrictions with respect | |||
| to this document. Code Components extracted from this document must | to this document. Code Components extracted from this document must | |||
| include Simplified BSD License text as described in Section 4.e of | include Simplified BSD License text as described in Section 4.e of | |||
| the Trust Legal Provisions and are provided without warranty as | the Trust Legal Provisions and are provided without warranty as | |||
| described in the Simplified BSD License. | described in the Simplified BSD License. | |||
| Table of Contents | Table of Contents | |||
| 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4 | |||
| 1.1. Notational conventions . . . . . . . . . . . . . . . . . 3 | 1.1. Notational conventions . . . . . . . . . . . . . . . . . 4 | |||
| 1.2. The Date data types . . . . . . . . . . . . . . . . . . . 4 | 1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4 | |||
| 1.3. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4 | 1.3. Additions to the capabilities object . . . . . . . . . . 4 | |||
| 1.4. Addition to the capabilities object . . . . . . . . . . . 4 | 1.3.1. urn:ietf:params:jmap:mail . . . . . . . . . . . . . . 5 | |||
| 1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 6 | 1.3.2. urn:ietf:params:jmap:submission . . . . . . . . . . . 5 | |||
| 2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 6 | 1.3.3. urn:ietf:params:jmap:vacationresponse . . . . . . . . 6 | |||
| 2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 9 | 1.4. Data type support in different accounts . . . . . . . . . 6 | |||
| 2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 9 | 1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 7 | |||
| 2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 10 | 2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 7 | |||
| 2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 10 | 2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 10 | |||
| 2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 10 | 2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 10 | |||
| 2.6. Example . . . . . . . . . . . . . . . . . . . . . . . . . 11 | 2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 11 | |||
| 3. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 | 2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 11 | |||
| 2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 12 | ||||
| 2.6. Example . . . . . . . . . . . . . . . . . . . . . . . . . 12 | ||||
| 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 . . . . . . . . . . . . . . . . . . . . . 17 | 3.2. Thread/changes . . . . . . . . . . . . . . . . . . . . . 18 | |||
| 4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 | 4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 | |||
| 4.1. Properties of the Email object . . . . . . . . . . . . . 17 | 4.1. Properties of the Email object . . . . . . . . . . . . . 18 | |||
| 4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 19 | 4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 19 | |||
| 4.1.2. Header fields . . . . . . . . . . . . . . . . . . . . 20 | 4.1.2. Header fields parsed forms . . . . . . . . . . . . . 21 | |||
| 4.1.3. Body parts . . . . . . . . . . . . . . . . . . . . . 26 | 4.1.3. Header fields properties . . . . . . . . . . . . . . 26 | |||
| 4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 32 | 4.1.4. Body parts . . . . . . . . . . . . . . . . . . . . . 28 | |||
| 4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 34 | 4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 34 | |||
| 4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 35 | 4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 36 | |||
| 4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 36 | 4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 37 | |||
| 4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 36 | 4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 38 | |||
| 4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 38 | 4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 38 | |||
| 4.4.3. Thread collapsing . . . . . . . . . . . . . . . . . . 40 | 4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 40 | |||
| 4.4.4. Response . . . . . . . . . . . . . . . . . . . . . . 40 | 4.4.3. Thread collapsing . . . . . . . . . . . . . . . . . . 42 | |||
| 4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 40 | 4.4.4. Response . . . . . . . . . . . . . . . . . . . . . . 42 | |||
| 4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 40 | 4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 42 | |||
| 4.7. Email/import . . . . . . . . . . . . . . . . . . . . . . 43 | 4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 42 | |||
| 4.8. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 44 | 4.7. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 45 | |||
| 4.8. Email/import . . . . . . . . . . . . . . . . . . . . . . 45 | ||||
| 4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 46 | 4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 46 | |||
| 5. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 48 | 4.10. Examples . . . . . . . . . . . . . . . . . . . . . . . . 48 | |||
| 5.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 49 | 5. Search snippets . . . . . . . . . . . . . . . . . . . . . . . 56 | |||
| 5.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 49 | 5.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 57 | |||
| 5.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 49 | 5.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 58 | |||
| 5.4. Example . . . . . . . . . . . . . . . . . . . . . . . . . 49 | 6. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 59 | |||
| 6. Email submission . . . . . . . . . . . . . . . . . . . . . . 50 | 6.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 60 | |||
| 6.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 55 | 6.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 60 | |||
| 6.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 55 | 6.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 60 | |||
| 6.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 55 | 6.4. Example . . . . . . . . . . . . . . . . . . . . . . . . . 60 | |||
| 6.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 56 | 7. Email submission . . . . . . . . . . . . . . . . . . . . . . 61 | |||
| 6.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 56 | 7.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 66 | |||
| 6.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 58 | 7.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 66 | |||
| 7. Search snippets . . . . . . . . . . . . . . . . . . . . . . . 59 | 7.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 66 | |||
| 7.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 60 | 7.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 67 | |||
| 7.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 61 | 7.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 67 | |||
| 8. Vacation response . . . . . . . . . . . . . . . . . . . . . . 62 | 7.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 69 | |||
| 8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 63 | 8. Vacation response . . . . . . . . . . . . . . . . . . . . . . 71 | |||
| 8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 63 | 8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 72 | |||
| 9. Security considerations . . . . . . . . . . . . . . . . . . . 63 | 8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 72 | |||
| 9.1. EmailBodyPart value . . . . . . . . . . . . . . . . . . . 63 | 9. Security considerations . . . . . . . . . . . . . . . . . . . 72 | |||
| 9.2. HTML email display . . . . . . . . . . . . . . . . . . . 64 | 9.1. EmailBodyPart value . . . . . . . . . . . . . . . . . . . 72 | |||
| 9.3. Email submission . . . . . . . . . . . . . . . . . . . . 66 | 9.2. HTML email display . . . . . . . . . . . . . . . . . . . 72 | |||
| 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 67 | 9.3. Email submission . . . . . . . . . . . . . . . . . . . . 75 | |||
| 10.1. JMAP Capability Registration for "mail" . . . . . . . . 67 | 10. IANA considerations . . . . . . . . . . . . . . . . . . . . . 75 | |||
| 10.2. IMAP and JMAP Keywords Registry . . . . . . . . . . . . 67 | 10.1. JMAP capability registration for "mail" . . . . . . . . 75 | |||
| 10.2.1. Registration of JMAP keyword '$draft' . . . . . . . 68 | 10.2. JMAP capability registration for "submission" . . . . . 76 | |||
| 10.2.2. Registration of JMAP keyword '$seen' . . . . . . . . 69 | 10.3. JMAP capability registration for "vacationresponse" . . 76 | |||
| 10.2.3. Registration of JMAP keyword '$flagged' . . . . . . 69 | 10.4. IMAP and JMAP keywords registry . . . . . . . . . . . . 76 | |||
| 10.2.4. Registration of JMAP keyword '$answered' . . . . . . 70 | 10.4.1. Registration of JMAP keyword '$draft' . . . . . . . 77 | |||
| 10.2.5. Registration of '$recent' Keyword . . . . . . . . . 71 | 10.4.2. Registration of JMAP keyword '$seen' . . . . . . . . 78 | |||
| 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 72 | 10.4.3. Registration of JMAP keyword '$flagged' . . . . . . 79 | |||
| 11.1. Normative References . . . . . . . . . . . . . . . . . . 72 | 10.4.4. Registration of JMAP keyword '$answered' . . . . . . 79 | |||
| 11.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 75 | 10.4.5. Registration of '$recent' keyword . . . . . . . . . 80 | |||
| Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 75 | 10.5. JMAP Error Codes registry . . . . . . . . . . . . . . . 81 | |||
| 10.5.1. mailboxHasChild . . . . . . . . . . . . . . . . . . 81 | ||||
| 10.5.2. mailboxHasEmail . . . . . . . . . . . . . . . . . . 81 | ||||
| 10.5.3. blobNotFound . . . . . . . . . . . . . . . . . . . . 81 | ||||
| 10.5.4. tooManyKeywords . . . . . . . . . . . . . . . . . . 81 | ||||
| 10.5.5. tooManyMailboxes . . . . . . . . . . . . . . . . . . 82 | ||||
| 10.5.6. emailNotFound . . . . . . . . . . . . . . . . . . . 82 | ||||
| 10.5.7. emailTooLarge . . . . . . . . . . . . . . . . . . . 82 | ||||
| 10.5.8. invalidEmail . . . . . . . . . . . . . . . . . . . . 82 | ||||
| 10.5.9. tooManyRecipients . . . . . . . . . . . . . . . . . 83 | ||||
| 10.5.10. noRecipients . . . . . . . . . . . . . . . . . . . . 83 | ||||
| 10.5.11. invalidRecipients . . . . . . . . . . . . . . . . . 83 | ||||
| 10.5.12. forbiddenMailFrom . . . . . . . . . . . . . . . . . 83 | ||||
| 10.5.13. forbiddenFrom . . . . . . . . . . . . . . . . . . . 83 | ||||
| 10.5.14. forbiddenToSend . . . . . . . . . . . . . . . . . . 84 | ||||
| 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 84 | ||||
| 11.1. Normative References . . . . . . . . . . . . . . . . . . 84 | ||||
| 11.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 87 | ||||
| Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 87 | ||||
| 1. Introduction | 1. Introduction | |||
| JMAP <https://tools.ietf.org/html/draft-ietf-jmap-core-05> is a | JMAP <https://tools.ietf.org/html/draft-ietf-jmap-core-05> is a | |||
| generic protocol for synchronising data, such as mail, calendars or | generic protocol for synchronising data, such as mail, calendars or | |||
| contacts, between a client and a server. It is optimised for mobile | contacts, between a client and a server. It is optimised for mobile | |||
| and web environments, and aims to provide a consistent interface to | and web environments, and aims to provide a consistent interface to | |||
| different data types. | different data types. | |||
| This specification defines a data model for synchronising mail | This specification defines a data model for synchronising mail | |||
| skipping to change at page 4, line 5 ¶ | skipping to change at page 4, line 29 ¶ | |||
| "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 | |||
| <https://tools.ietf.org/html/draft-ietf-jmap-core-05>. | <https://tools.ietf.org/html/draft-ietf-jmap-core-05>. | |||
| Object properties may also have a set of attributes defined along | Object properties may also have a set of attributes defined along | |||
| with the type signature. These have the following meanings: | with the type signature. These have the following meanings: | |||
| o *sever-set*: Only the server can set the value for this property. | o *server-set*: Only the server can set the value for this property. | |||
| The client MUST NOT send this property when creating a new object | The client MUST NOT send this property when creating a new object | |||
| of this type. | of this type. | |||
| o *immutable*: The value MUST NOT change after the object is | o *immutable*: The value MUST NOT change after the object is | |||
| created. | created. | |||
| o *default*: (This is followed by a JSON value). The value that | o *default*: (This is followed by a JSON value). The value that | |||
| will be used for this property if it is omitted in an argument, or | will be used for this property if it is omitted in an argument, or | |||
| when creating a new object of this type. | when creating a new object of this type. | |||
| 1.2. The Date data types | Data types defined in the core specification are used in this | |||
| document. | ||||
| Where "Date" is given as a type, it means a string in [RFC3339] | ||||
| _date-time_ format. To ensure a normalised form, the _time-secfrac_ | ||||
| MUST always be omitted and any letters in the string (e.g. "T" and | ||||
| "Z") MUST be upper-case. For example, ""2014-10-30T14:12:00+08:00"". | ||||
| Where "UTCDate" is given as a type, it means a "Date" where the | ||||
| _time-offset_ component MUST be "Z" (i.e. it must be in UTC time). | ||||
| For example, ""2014-10-30T06:12:00Z"". | ||||
| 1.3. 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.4. Addition 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 spec. Servers supporting _this_ | Session object; see the JMAP Core specification. | |||
| specification MUST add a property called "urn:ietf:params:jmap:mail" | ||||
| to the capabilities object. The value of this property is an object | ||||
| which MUST contain the following information on server capabilities: | ||||
| o *maxMailboxesPerEmail*: "Number|null" The maximum number of | This document defines three additional capability objects. | |||
| 1.3.1. urn:ietf:params:jmap:mail | ||||
| This represents support for the Mailbox, Thread, Email, and | ||||
| SearchSnippet data types and associated API methods. The value of | ||||
| this property is an object which MUST contain the following | ||||
| information on server capabilities: | ||||
| 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 | |||
| be an integer >= 1, or "null" for no limit (or rather, the limit | be an integer >= 1, or "null" for no limit (or rather, the limit | |||
| is always the number of mailboxes in the account). | is always the number of mailboxes in the account). | |||
| o *maxSizeAttachmentsPerEmail*: "Number" The maximum total size of | o *maxSizeAttachmentsPerEmail*: "PositiveInt" The maximum total size | |||
| attachments, in octets, allowed for a single email. A server MAY | of attachments, in octets, allowed for a single email. A server | |||
| still reject emails with a lower attachment size total (for | MAY still reject import or creation of emails with a lower | |||
| example, if the body includes several megabytes of text, causing | attachment size total (for example, if the body includes several | |||
| the size of the encoded MIME structure to be over some server- | megabytes of text, causing the size of the encoded MIME structure | |||
| defined limit). Note, this limit is for the sum of unencoded | to be over some server-defined limit). Note, this limit is for | |||
| attachment sizes. Users are generally not knowledgeable about | the sum of unencoded attachment sizes. Users are generally not | |||
| encoding overhead etc., nor should they need to be, so services | knowledgeable about encoding overhead etc., nor should they need | |||
| marketing and help materials normally tells them the "max size | to be, so services marketing and help materials normally tells | |||
| attachments". This is the unencoded size they see on their hard | them the "max size attachments". This is the unencoded size they | |||
| drive, and so this capability matches that and allows the client | see on their hard drive, and so this capability matches that and | |||
| to consistently enforce what the user understands as the limit. | allows the client to consistently enforce what the user | |||
| The server may separately have a limit for the total size of the | understands as the limit. The server may separately have a limit | |||
| RFC5322 message, which will have attachments Base64 encoded and | for the total size of the RFC5322 message, which will have | |||
| message headers and bodies too. For example, suppose the server | attachments Base64 encoded and message headers and bodies too. | |||
| advertises "maxSizeAttachmentsPerEmail: 50000000" (50 MB). The | For example, suppose the server advertises | |||
| enforced server limit may be for an RFC5322 size of 70000000 | "maxSizeAttachmentsPerEmail: 50000000" (50 MB). The enforced | |||
| octets (70 MB). Even with Base64 encoding and a 2 MB HTML body, | server limit may be for an RFC5322 size of 70000000 octets (70 | |||
| 50 MB attachments would fit under this limit. | MB). Even with Base64 encoding and a 2 MB HTML body, 50 MB | |||
| attachments would fit under this limit. | ||||
| o *maxDelayedSend*: "Number" The number in seconds of the maximum | ||||
| delay the server supports in sending (see the EmailSubmission | ||||
| object). This is "0" if the server does not support delayed send. | ||||
| o *emailsListSortOptions*: "String[]" A list of all the email | o *emailsListSortOptions*: "String[]" A list of all the email | |||
| properties the server supports for sorting by. This MAY include | properties the server supports for sorting by. This MAY include | |||
| properties the client does not recognise (for example custom | properties the client does not recognise (for example custom | |||
| properties specified in a vendor extension). Clients MUST ignore | properties specified in a vendor extension). Clients MUST ignore | |||
| any unknown properties in the list. | any unknown properties in the list. | |||
| 1.3.2. urn:ietf:params:jmap:submission | ||||
| This represents support for the Identity and MessageSubmission data | ||||
| types and associated API methods. The value of this property is an | ||||
| object which MUST contain the following information on server | ||||
| capabilities: | ||||
| o *maxDelayedSend*: "PositiveInt" The number in seconds of the | ||||
| maximum delay the server supports in sending (see the | ||||
| EmailSubmission object description). This is "0" if the server | ||||
| does not support delayed send. | ||||
| o *submissionExtensions*: "String[String[]]" A JMAP implementation | o *submissionExtensions*: "String[String[]]" A JMAP implementation | |||
| that talks to a Submission [RFC6409] server SHOULD have a | that talks to a Submission [RFC6409] server SHOULD have a | |||
| configuration setting that allows an administrator to expose a new | configuration setting that allows an administrator to expose a new | |||
| submission EHLO capability in this field. This allows a JMAP | submission EHLO capability in this field. This allows a JMAP | |||
| server to gain access to a new submission extension without code | server to gain access to a new submission extension without code | |||
| changes. By default, the JMAP server should show only known safe- | changes. By default, the JMAP server should show only known safe- | |||
| to-expose EHLO capabilities in this field, and hide EHLO | to-expose EHLO capabilities in this field, and hide EHLO | |||
| capabilities that are only relevant to the JMAP server. Each key | capabilities that are only relevant to the JMAP server. Each key | |||
| in the object is the _ehlo-name_, and the value is a list of | in the object is the _ehlo-name_, and the value is a list of | |||
| _ehlo-args_. Examples of safe-to-expose Submission extensions | _ehlo-args_. Examples of safe-to-expose Submission extensions | |||
| skipping to change at page 6, line 7 ¶ | skipping to change at page 6, line 39 ¶ | |||
| * 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> | |||
| The server MUST also include the string "urn:ietf:params:jmap:mail" | 1.3.3. urn:ietf:params:jmap:vacationresponse | |||
| in the _hasDataFor_ property of any account in which the user may use | ||||
| the data types contained in this specification. | This represents support for the VacationResponse data type and | |||
| associated API methods. The value of this property is an empty | ||||
| object. | ||||
| 1.4. Data type support in different accounts | ||||
| The server MUST include the appropriate capability strings in the | ||||
| _hasDataFor_ property of any account in which the user may use the | ||||
| data types represented by that URN. Supported data types may differ | ||||
| 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 | ||||
| in a shared account they may only have data for | ||||
| "urn:ietf:params:jmap:mail". This means they can access | ||||
| 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/ | ||||
| MessageSubmission objects) or view/set its vacation response. | ||||
| 1.5. Push | 1.5. Push | |||
| Servers MUST support the standard JMAP push mechanisms to receive | Servers MUST support the standard JMAP push mechanisms to receive | |||
| notifications when the state changes for any of the types defined in | notifications when the state changes for any of the types defined in | |||
| this specification. | this specification. | |||
| In addition, servers MUST support a psuedo-type called | In addition, servers MUST support a psuedo-type called | |||
| "EmailDelivery" in the push mechanisms. The state string for this | "EmailDelivery" in the push mechanisms. The state string for this | |||
| MUST change whenever a new Email is added to the store, but SHOULD | MUST change whenever a new Email is added to the store, but SHOULD | |||
| skipping to change at page 6, line 48 ¶ | skipping to change at page 7, line 46 ¶ | |||
| o *id*: "String" (immutable; server-set) The id of the mailbox. | o *id*: "String" (immutable; server-set) The id of the mailbox. | |||
| o *name*: "String" User-visible name for the mailbox, e.g. "Inbox". | o *name*: "String" User-visible name for the mailbox, e.g. "Inbox". | |||
| This may be any Net-Unicode string ([RFC5198]) of at least 1 | This may be any Net-Unicode string ([RFC5198]) of at least 1 | |||
| character in length and maximum 255 octets in size. Servers MUST | character in length and maximum 255 octets in size. Servers MUST | |||
| forbid sibling Mailboxes with the same name. Servers MAY reject | forbid sibling Mailboxes with the same name. Servers MAY reject | |||
| names that violate server policy (e.g., names containing slash (/) | names that violate server policy (e.g., names containing slash (/) | |||
| or control characters). | or control characters). | |||
| o *parentId*: "String|null" (default: "null") The mailbox id for the | o *parentId*: "String|null" (default: null) The mailbox id for the | |||
| parent of this mailbox, or "null" if this mailbox is at the top | parent of this mailbox, or "null" if this mailbox is at the top | |||
| level. Mailboxes form acyclic graphs (forests) directed by the | level. Mailboxes form acyclic graphs (forests) directed by the | |||
| child-to-parent relationship. There MUST NOT be a loop. | child-to-parent relationship. There MUST NOT be a loop. | |||
| o *role*: "String|null" (default: "null") Identifies mailboxes that | o *role*: "String|null" (default: null) Identifies mailboxes that | |||
| have a particular common purpose (e.g. the "inbox"), regardless of | have a particular common purpose (e.g. the "inbox"), regardless of | |||
| the _name_ (which may be localised). This value is shared with | the _name_ (which may be localised). This value is shared with | |||
| IMAP (exposed in IMAP via the [RFC6154] SPECIAL-USE extension). | IMAP (exposed in IMAP via the [RFC6154] SPECIAL-USE extension). | |||
| However, unlike in IMAP, a mailbox may only have a single role, | However, unlike in IMAP, a mailbox may only have a single role, | |||
| and no two mailboxes in the same account may have the same role. | and no two mailboxes in the same account may have the same role. | |||
| The value MUST be one of the mailbox attribute names listed in the | The value MUST be one of the mailbox attribute names listed in the | |||
| IANA Mailbox Name Attributes Registry [1], as established in | IANA Mailbox Name Attributes Registry [1], as established in | |||
| [TODO:being established in EXTRA], converted to lower-case. New | [TODO:being established in EXTRA], converted to lower-case. New | |||
| roles may be established here in the future. An account is not | roles may be established here in the future. An account is not | |||
| required to have mailboxes with any particular roles. | required to have mailboxes with any particular roles. | |||
| o *sortOrder*: "Number" (default: "0") Defines the sort order of | o *sortOrder*: "PositiveInt" (default: 0) Defines the sort order of | |||
| mailboxes when presented in the client's UI, so it is consistent | mailboxes when presented in the client's UI, so it is consistent | |||
| between devices. The number MUST be an integer in the range 0 <= | between devices. The number MUST be an integer in the range 0 <= | |||
| sortOrder < 2^31. A mailbox with a lower order should be | sortOrder < 2^31. A mailbox with a lower order should be | |||
| displayed before a mailbox with a higher order (that has the same | displayed before a mailbox with a higher order (that has the same | |||
| parent) in any mailbox listing in the client's UI. Mailboxes with | parent) in any mailbox listing in the client's UI. Mailboxes with | |||
| equal order SHOULD be sorted in alphabetical order by name. The | equal order SHOULD be sorted in alphabetical order by name. The | |||
| sorting SHOULD take into account locale-specific character order | sorting SHOULD take into account locale-specific character order | |||
| convention. | convention. | |||
| o *totalEmails*: "Number" (server-set) The number of emails in this | o *totalEmails*: "PositiveInt" (server-set) The number of emails in | |||
| mailbox. | this mailbox. | |||
| o *unreadEmails*: "Number" (server-set) The number of emails in this | o *unreadEmails*: "PositiveInt" (server-set) The number of emails in | |||
| mailbox that have neither the "$seen" keyword nor the "$draft" | this mailbox that have neither the "$seen" keyword nor the | |||
| keyword. | "$draft" keyword. | |||
| o *totalThreads*: "Number" (server-set) The number of threads where | o *totalThreads*: "PositiveInt" (server-set) The number of threads | |||
| 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*: "Number" (server-set) The number of threads where | o *unreadThreads*: "PositiveInt" (server-set) The number of threads | |||
| at least one email in the thread has neither the "$seen" keyword | where at least one email in the thread has neither the "$seen" | |||
| nor the "$draft" keyword AND at least one email in the thread is | keyword nor the "$draft" keyword AND at least one email in the | |||
| in this mailbox (but see below for special case handling of | thread is in this mailbox (but see below for special case handling | |||
| Trash). Note, the unread email does not need to be the one in | of Trash). Note, the unread email does not need to be the one in | |||
| this mailbox. | this mailbox. | |||
| o *myRights*: "MailboxRights" (server-set) The set of rights (ACLs) | o *myRights*: "MailboxRights" (server-set) The set of rights (ACLs) | |||
| the user has in relation to this mailbox. A _MailboxRights_ | the user has in relation to this mailbox. A _MailboxRights_ | |||
| object has the following properties: | object has the following properties: | |||
| * *mayReadItems*: "Boolean" If true, the user may use this | * *mayReadItems*: "Boolean" If true, the user may use this | |||
| mailbox as part of a filter in a _Email/query_ call and the | mailbox as part of a filter in a _Email/query_ call and the | |||
| mailbox may be included in the _mailboxIds_ set of _Email_ | mailbox may be included in the _mailboxIds_ set of _Email_ | |||
| objects. If a sub-mailbox is shared but not the parent | objects. If a sub-mailbox is shared but not the parent | |||
| skipping to change at page 9, line 35 ¶ | skipping to change at page 10, line 35 ¶ | |||
| 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: | |||
| 2.1. Mailbox/get | 2.1. Mailbox/get | |||
| Standard _/get_ method. The _ids_ argument may be "null" to fetch | Standard "/get" method. The _ids_ argument may be "null" to fetch | |||
| all at once. | all at once. | |||
| 2.2. Mailbox/changes | 2.2. Mailbox/changes | |||
| Standard _/changes_ method, but with one extra argument to the | Standard "/changes" method, but with one extra argument to the | |||
| response: | response: | |||
| o *changedProperties*: "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 the rest of the mailboxes state | |||
| for most use cases changes rarely, the server can help the client | for most use cases changes rarely, the server can help the client | |||
| optimise data transfer by keeping track of changes to email/thread | optimise data transfer by keeping track of changes to email/thread | |||
| counts separately to other state changes. The _changedProperties_ | counts separately to other state changes. The _updatedProperties_ | |||
| array may be used directly via a result reference in a subsequent | array may be used directly via a result reference in a subsequent | |||
| Mailbox/get call in a single request. | Mailbox/get call in a single request. | |||
| 2.3. Mailbox/query | 2.3. Mailbox/query | |||
| Standard _/query_ method. | Standard "/query" method. | |||
| A *FilterCondition* object has the following properties, any of which | A *FilterCondition* object has the following properties, any of which | |||
| may be omitted: | may be omitted: | |||
| o *parentId*: "String|null" The Mailbox _parentId_ property must | o *parentId*: "String|null" The Mailbox _parentId_ property must | |||
| match the given value exactly. | match the given value exactly. | |||
| o *hasRole*: "Boolean" If this is "true", a Mailbox matches if it | o *name*: "String" The Mailbox _name_ property contains the given | |||
| has a non-"null" value for its _role_ property. If "false", it | string. | |||
| must has a "null" _role_ value to match. | ||||
| o *role*: "String|null" The Mailbox _role_ property must match the | ||||
| given value exactly. | ||||
| o *hasAnyRole*: "Boolean" If "true", a Mailbox matches if it has any | ||||
| non-"null" value for its _role_ property. | ||||
| o *isSubscribed*: "Boolean" The "isSubscribed" property of the | o *isSubscribed*: "Boolean" The "isSubscribed" property of the | |||
| mailbox must be identical to the value given to match the | mailbox must be identical to the value given to match the | |||
| condition. | condition. | |||
| A Mailbox object matches the filter if and only if all of the given | A Mailbox object matches the filter if and only if all of the given | |||
| conditions given match. If zero properties are specified, it is | conditions given match. If zero properties are specified, it is | |||
| automatically "true" for all objects. | automatically "true" for all objects. | |||
| The following properties MUST be supported for sorting: | The following properties MUST be supported for sorting: | |||
| skipping to change at page 10, line 44 ¶ | skipping to change at page 11, line 49 ¶ | |||
| o "name" | o "name" | |||
| o "parent/name": This is a pseudo-property, just for sorting, with | o "parent/name": This is a pseudo-property, just for sorting, with | |||
| the following semantics: if two mailboxes have a common parent, | the following semantics: if two mailboxes have a common parent, | |||
| sort them by name. Otherwise, find the nearest ancestors of each | sort them by name. Otherwise, find the nearest ancestors of each | |||
| that share a common parent and sort by their names instead. (i.e. | that share a common parent and sort by their names instead. (i.e. | |||
| This sorts the mailbox list in tree order). | This sorts the mailbox list in tree order). | |||
| 2.4. Mailbox/queryChanges | 2.4. Mailbox/queryChanges | |||
| 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 | o *onDestroyRemoveMessages*: "Boolean" (default: false) If "false", | |||
| "false", attempts to destroy a mailbox that still has any messages | attempts to destroy a mailbox that still has any messages in it | |||
| in it will be rejected with a "mailboxHasEmail" SetError. If | will be rejected with a "mailboxHasEmail" SetError. If "true", | |||
| "true", any messages that were in the mailbox will be removed from | any messages that were in the mailbox will be removed from it, and | |||
| it, and if in no other mailboxes will be destroyed when the | if in no other mailboxes will be destroyed when the mailbox is | |||
| 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 | |||
| mailbox. The client MUST remove these before it can delete the | mailbox. The client MUST remove these before it can delete the | |||
| parent mailbox. | parent mailbox. | |||
| o "mailboxHasEmail": The mailbox has at least one message assigned | o "mailboxHasEmail": The mailbox has at least one message assigned | |||
| to it and the _onDestroyRemoveMessages_ argument was "false". | to it and the _onDestroyRemoveMessages_ argument was "false". | |||
| 2.6. Example | 2.6. Example | |||
| Fetching all mailboxes in an account: | Fetching all mailboxes in an account: | |||
| [ | [[ "Mailbox/get", { | |||
| "Mailbox/get", | "accountId": "u33084183", | |||
| { | "ids": null | |||
| "accountId": "u33084183", | }, "0" ]] | |||
| "ids": null | ||||
| }, | ||||
| "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": { | "mayAddItems": true, | |||
| "mayAddItems": true, | "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", | |||
| { | "sinceState": "78540" | |||
| "accountId": "u33084183", | }, "0" ], | |||
| "sinceState": "78540" | [ "Mailbox/get", { | |||
| }, | "accountId": "u33084183", | |||
| "0" | "#ids": { | |||
| ], | "resultOf": "0", | |||
| [ | "name": "Mailbox/changes", | |||
| "Mailbox/get", | "path": "/created" | |||
| { | } | |||
| "accountId": "u33084183", | }, "1" ], | |||
| "#ids": { | [ "Mailbox/get", { | |||
| "resultOf": "0", | "accountId": "u33084183", | |||
| "name": "Mailbox/changes", | "#ids": { | |||
| "path": "/created" | "resultOf": "0", | |||
| } | "name": "Mailbox/changes", | |||
| }, | "path": "/updated" | |||
| "1" | }, | |||
| ], | "#properties": { | |||
| [ | "resultOf": "0", | |||
| "Mailbox/get", | "name": "Mailbox/changes", | |||
| { | "path": "/updatedProperties" | |||
| "accountId": "u33084183", | } | |||
| "#ids": { | }, "2" ]] | |||
| "resultOf": "0", | ||||
| "name": "Mailbox/changes", | ||||
| "path": "/updated" | ||||
| }, | ||||
| "#properties": { | ||||
| "resultOf": "0", | ||||
| "name": "Mailbox/changes", | ||||
| "path": "/changedProperties" | ||||
| } | ||||
| }, | ||||
| "2" | ||||
| ] | ||||
| This fetches the list of ids for created/updated/destroyed mailboxes, | 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", | |||
| { | "oldState": "78541", | |||
| "accountId": "u33084183", | "newState": "78542", | |||
| "oldState": "78541", | "hasMoreChanges": false, | |||
| "newState": "78542", | "updatedProperties": [ | |||
| "hasMoreChanges": false, | "totalEmails", "unreadEmails", | |||
| "changedProperties": [ | "totalThreads", "unreadThreads" | |||
| "totalEmails", "unreadEmails", | ], | |||
| "totalThreads", "unreadThreads" | "created": [], | |||
| ], | "updated": ["23cfa8094c0f41e6"], | |||
| "created": [], | "destroyed": [] | |||
| "updated": ["23cfa8094c0f41e6"], | }, "0" ], | |||
| "destroyed": [] | [ "Mailbox/get", { | |||
| }, | "accountId": "u33084183", | |||
| "0" | "state": "78542", | |||
| ], | "list": [], | |||
| ["Mailbox/get", { | "notFound": [] | |||
| "accountId": "u33084183", | }, "1" ], | |||
| "state": "78542", | [ "Mailbox/get", { | |||
| "list": [], | "accountId": "u33084183", | |||
| "notFound": [] | "state": "78542", | |||
| }, "1"], | "list": [{ | |||
| ["Mailbox/get", { | "id": "23cfa8094c0f41e6", | |||
| "accountId": "u33084183", | "totalEmails": 16307, | |||
| "state": "78542", | "unreadEmails": 13903, | |||
| "list": [{ | "totalThreads": 5833, | |||
| "id": "23cfa8094c0f41e6", | "unreadThreads": 5127 | |||
| "totalEmails": 16307, | }], | |||
| "unreadEmails": 13903, | "notFound": [] | |||
| "totalThreads": 5833, | }, "2" ]] | |||
| "unreadThreads": 5127 | ||||
| }], | ||||
| "notFound": [] | ||||
| }, "2"], | ||||
| Here's an example where we try to rename one mailbox and destroy | Here's an example where we try to rename one mailbox and destroy | |||
| another: | another: | |||
| [ | [[ "Mailbox/set", { | |||
| "Mailbox/set", | "accountId": "u33084183", | |||
| { | "ifInState": "78542", | |||
| "accountId": "u33084183", | "update": { | |||
| "ifInState": "78542", | "674cc24095db49ce": { | |||
| "update": { | "name": "Maybe important mail" | |||
| "674cc24095db49ce": { | } | |||
| "name": "Maybe important mail" | }, | |||
| } | "destroy": [ "23cfa8094c0f41e6" ] | |||
| }, | }, "0" ]] | |||
| "destroy": [ "23cfa8094c0f41e6" ] | ||||
| }, | ||||
| "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", | |||
| { | "oldState": "78542", | |||
| "accountId": "u33084183", | "newState": "78549", | |||
| "oldState": "78542", | "created": null, | |||
| "newState": "78549", | "notCreated": null, | |||
| "created": null, | "updated": { | |||
| "notCreated": null, | "674cc24095db49ce": null | |||
| "updated": { | }, | |||
| "674cc24095db49ce": null | "notUpdated": null, | |||
| }, | "destroyed": null, | |||
| "notUpdated": null, | "notDestroyed": { | |||
| "destroyed": null, | "23cfa8094c0f41e6": { | |||
| "notDestroyed": { | "type": "forbidden" | |||
| "23cfa8094c0f41e6": { | } | |||
| "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 | |||
| thread. In JMAP, a thread is simply a flat list of emails, ordered | thread. In JMAP, a thread is simply a flat list of emails, ordered | |||
| by date. Every email MUST belong to a thread, even if it is the only | by date. Every email MUST belong to a thread, even if it is the only | |||
| email in the thread. | email in the thread. | |||
| The exact algorithm for determining whether two emails belong to the | The exact algorithm for determining whether two emails belong to the | |||
| same thread is not mandated in this spec to allow for compatibility | same thread is not mandated in this spec to allow for compatibility | |||
| skipping to change at page 16, line 35 ¶ | skipping to change at page 17, line 12 ¶ | |||
| 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) The id of the thread. | |||
| o *emailIds*: "String[]" The ids of the emails in the thread, sorted | o *emailIds*: "String[]" The ids of the emails in the thread, sorted | |||
| such that: | by the _receivedAt_ date of the email, oldest first. If two | |||
| emails have an identical date, the sort is server-dependent but | ||||
| * Any email with the "$draft" keyword that has an "In-Reply-To" | MUST be stable (sorting by id is recommended). | |||
| header is sorted after the _first_ non-draft email in the | ||||
| thread with the corresponding "Message-Id" header, but before | ||||
| any subsequent non-draft emails. | ||||
| * Other than that, everything is sorted by the _receivedAt_ date | ||||
| of the email, oldest first. | ||||
| * If two emails are identical under the above two conditions, the | ||||
| sort is server-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", { | |||
| "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"] | |||
| }, | }, | |||
| { | { | |||
| "id": "f41u44", | "id": "f41u44", | |||
| "emailIds": [ "82cf7bb" ] | "emailIds": [ "82cf7bb" ] | |||
| } | } | |||
| ], | ], | |||
| "notFound": [] | "notFound": [] | |||
| }, "#1" ] | }, "#1" ]] | |||
| 3.2. Thread/changes | 3.2. Thread/changes | |||
| Standard _/changes_ method. | Standard "/changes" method. | |||
| 4. Emails | 4. Emails | |||
| The *Email* object is a representation of an [RFC5322] message, which | The *Email* object is a representation of an [RFC5322] message, which | |||
| allows clients to avoid the complexities of MIME parsing, transport | allows clients to avoid the complexities of MIME parsing, transport | |||
| encoding and character encoding. | encoding and character encoding. | |||
| 4.1. Properties of the Email object | 4.1. Properties of the Email object | |||
| Broadly, a message consists of two parts: a list of header fields, | Broadly, a message consists of two parts: a list of header fields, | |||
| skipping to change at page 19, line 31 ¶ | skipping to change at page 19, line 48 ¶ | |||
| 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 | o *keywords*: "String[Boolean]" (default: ) A set of keywords that | |||
| that apply to the email. The set is represented as an object, | apply to the email. The set is represented as an object, with the | |||
| with the keys being the _keywords_. The value for each key in the | keys being the _keywords_. The value for each key in the object | |||
| object MUST be "true". Keywords are shared with IMAP. The six | MUST be "true". Keywords are shared with IMAP. The six system | |||
| system keywords from IMAP are treated specially. The following | keywords from IMAP are treated specially. The following four | |||
| four keywords have their first character changed from "\" in IMAP | keywords have their first character changed from "\" in IMAP to | |||
| to "$" in JMAP and have particular semantic meaning: | "$" in JMAP and have particular semantic meaning: | |||
| * "$draft": The email is a draft the user is composing. | * "$draft": The email is a draft the user is composing. | |||
| * "$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. | |||
| skipping to change at page 20, line 26 ¶ | skipping to change at page 20, line 43 ¶ | |||
| and disable links and attachments. | and disable links and attachments. | |||
| * "$junk": The email is definitely spam. Clients SHOULD set this | * "$junk": The email is definitely spam. Clients SHOULD set this | |||
| flag when users report spam to help train automated spam- | flag when users report spam to help train automated spam- | |||
| detection systems. | detection systems. | |||
| * "$notjunk": The email is definitely not spam. Clients SHOULD | * "$notjunk": The email is definitely not spam. Clients SHOULD | |||
| set this flag when users indicate an email is legitimate, to | set this flag when users indicate an email is legitimate, to | |||
| help train automated spam-detection systems. | help train automated spam-detection systems. | |||
| o *size*: "Number" (immutable; server-set) The size, in octets, of | o *size*: "PositiveInt" (immutable; server-set) The size, in octets, | |||
| the raw data for the [RFC5322] message (as referenced by the | of the raw data for the [RFC5322] message (as referenced by the | |||
| _blobId_, i.e. the number of octets in the file the user would | _blobId_, i.e. the number of octets in the file the user would | |||
| download). | download). | |||
| o *receivedAt*: "UTCDate" (immutable; default: time of creation on | o *receivedAt*: "UTCDate" (immutable; default: time of creation on | |||
| server) The date the email was received by the message store. | server) The date the email was received by the message store. | |||
| This is the _internal date_ in IMAP. | This is the _internal date_ in IMAP. | |||
| 4.1.2. Header fields | 4.1.2. Header fields parsed forms | |||
| These properties are derived from the [RFC5322] and [RFC6532] message | Header field properties are derived from the [RFC5322] and [RFC6532] | |||
| header fields. All header fields may be fetched in a raw form. Some | message header fields. All header fields may be fetched in a raw | |||
| headers may also be fetched in a parsed form. The structured form | form. Some headers may also be fetched in a parsed form. The | |||
| that may be fetched depends on the header. The following forms are | structured form that may be fetched depends on the header. The | |||
| defined: | following forms are defined: | |||
| o *Raw* ("String") The raw octets of the header field value from the | 4.1.2.1. Raw | |||
| first octet following the header field name terminating colon, up | ||||
| to but excluding the header field terminating CRLF. Any | ||||
| standards-compliant message MUST be either ASCII (RFC5322) or | ||||
| UTF-8 (RFC6532), however other encodings exist in the wild. A | ||||
| server MAY use heuristics to determine a charset and decode the | ||||
| octets, or MAY replace any octet 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 dropped. | ||||
| o *Text* ("String") The header field value with: | Type: "String" | |||
| 1. White space unfolded (as defined in [RFC5322] section 2.2.3) | The raw octets of the header field value from the first octet | |||
| following the header field name terminating colon, up to but | ||||
| excluding the header field terminating CRLF. Any standards-compliant | ||||
| message MUST be either ASCII (RFC5322) or UTF-8 (RFC6532), however | ||||
| other encodings exist in the wild. A server MAY use heuristics to | ||||
| determine a charset and decode the octets, or MAY replace any octet | ||||
| 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 | ||||
| dropped. | ||||
| 2. The terminating CRLF at the end of the value removed | 4.1.2.2. Text | |||
| 3. Any SP characters at the beginning of the value removed | Type: "String" | |||
| 4. Any syntactically correct [RFC2047] encoded sections with a | The header field value with: | |||
| known character set decoded. Any [RFC2047] encoded NUL octets | ||||
| or control characters are dropped from the decoded value. Any | ||||
| text that looks like [RFC2047] syntax but violates [RFC2047] | ||||
| placement or whitespace rules MUST NOT be decoded. | ||||
| 5. Any [RFC6532] UTF-8 values decoded. | 1. White space unfolded (as defined in [RFC5322] section 2.2.3) | |||
| 6. The resulting unicode converted to NFC form. | 2. The terminating CRLF at the end of the value removed | |||
| If any decodings fail, the parser SHOULD insert a unicode | 3. Any SP characters at the beginning of the value removed | |||
| replacement character (U+FFFD) and attempt to continue as much as | ||||
| possible. To prevent obviously nonsense behaviour, which can lead | ||||
| to interoperability issues, this form may only be fetched or set | ||||
| for the following header fields: | ||||
| * Subject | 4. Any syntactically correct [RFC2047] encoded sections with a known | |||
| character set decoded. Any [RFC2047] encoded NUL octets or | ||||
| control characters are dropped from the decoded value. Any text | ||||
| that looks like [RFC2047] syntax but violates [RFC2047] placement | ||||
| or whitespace rules MUST NOT be decoded. | ||||
| * Comment | 5. Any [RFC6532] UTF-8 values decoded. | |||
| * List-Id | 6. The resulting unicode converted to NFC form. | |||
| * Any header not defined in [RFC5322] or [RFC2369] | If any decodings fail, the parser SHOULD insert a unicode replacement | |||
| character (U+FFFD) and attempt to continue as much as possible. | ||||
| o *Addresses* ("EmailAddress[]") The header is parsed as an | To prevent obviously nonsense behaviour, which can lead to | |||
| "address-list" value, as specified in [RFC5322] section 3.4, into | interoperability issues, this form may only be fetched or set for the | |||
| the "EmailAddress[]" type. The *EmailAddress* object has the | following header fields: | |||
| following properties: | ||||
| * *name*: "String|null" The _display-name_ of the [RFC5322] | o Subject | |||
| _mailbox_ or _group_, or "null" if none. If this is a _quoted- | ||||
| string_: | ||||
| 1. The surrounding DQUOTE characters are removed. | o Comment | |||
| 2. Any _quoted-pair_ is decoded. | o List-Id | |||
| 3. White-space is unfolded, and then any leading or trailing | o Any header not defined in [RFC5322] or [RFC2369] | |||
| white-space is removed. | ||||
| * *email*: "String|null" The _addr-spec_ of the [RFC5322] | 4.1.2.3. Addresses | |||
| _mailbox_, or "null" if a _group_. | ||||
| Any syntactically correct [RFC2047] encoded sections with a known | Type: "EmailAddress[]" | |||
| encoding MUST be decoded, following the same rules as for the | ||||
| _Text_ form. Any [RFC6532] UTF-8 values MUST be decoded. Parsing | ||||
| SHOULD be best-effort in the face of invalid structure to | ||||
| accommodate invalid messages and semi-complete drafts. | ||||
| EmailAddress objects MAY have an _email_ property that does not | ||||
| conform to the _addr-spec_ form (for example, may not contain an @ | ||||
| symbol). To prevent obviously nonsense behaviour, which can lead | ||||
| to interoperability issues, this form may only be fetched or set | ||||
| for the following header fields: | ||||
| * From | The header is parsed as an "address-list" value, as specified in | |||
| [RFC5322] section 3.4, into the "EmailAddress[]" type. There is an | ||||
| EmailAddress item for each "mailbox" parsed from the "address-list". | ||||
| Group and comment information is discarded. | ||||
| * Sender | The *EmailAddress* object has the following properties: | |||
| * Reply-To | o *name*: "String|null" The _display-name_ of the [RFC5322] | |||
| _mailbox_, or "null" if none. If this is a _quoted-string_: | ||||
| * To | 1. The surrounding DQUOTE characters are removed. | |||
| * Cc | 2. Any _quoted-pair_ is decoded. | |||
| * Bcc | 3. White-space is unfolded, and then any leading and trailing | |||
| white-space is removed. | ||||
| * Resent-From | o *email*: "String" The _addr-spec_ of the [RFC5322] _mailbox_. | |||
| * Resent-Sender | Any syntactically correct [RFC2047] encoded sections with a known | |||
| encoding MUST be decoded, following the same rules as for the _Text_ | ||||
| form. Any [RFC6532] UTF-8 values MUST be decoded. | ||||
| * Resent-Reply-To | Parsing SHOULD be best-effort in the face of invalid structure to | |||
| accommodate invalid messages and semi-complete drafts. EmailAddress | ||||
| objects MAY have an _email_ property that does not conform to the | ||||
| _addr-spec_ form (for example, may not contain an @ symbol). | ||||
| * Resent-To | For example, the following "address-list" string: | |||
| * Resent-Cc | " James Smythe" <james@example.com>, Friends: jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= <john@example.com>; | |||
| would be parsed as: | ||||
| * Resent-Bcc | [ | |||
| { "name": "James Smythe", "email": "james@example.com" }, | ||||
| { "name": null, "email": "jane@example.com" }, | ||||
| { "name": "John Smith", "email": "john@example.com" }, | ||||
| ] | ||||
| * Any header not defined in [RFC5322] or [RFC2369] | To prevent obviously nonsense behaviour, which can lead to | |||
| interoperability issues, this form may only be fetched or set for the | ||||
| following header fields: | ||||
| o *MessageIds* ("String[]|null") The header is parsed as a list of | o From | |||
| "msg-id" values, as specified in [RFC5322] section 3.6.4, into the | ||||
| "String[]" type. CFWS and surrounding angle brackets ("<>") are | ||||
| removed. If parsing fails, the value is "null". To prevent | ||||
| obviously nonsense behaviour, which can lead to interoperability | ||||
| issues, this form may only be fetched or set for the following | ||||
| header fields: | ||||
| * Message-ID | o Sender | |||
| * In-Reply-To | o Reply-To | |||
| * References | o To | |||
| * Resent-Message-ID | o Cc | |||
| * Any header not defined in [RFC5322] or [RFC2369] | o Bcc | |||
| o *Date* ("Date|null") The header is parsed as a "date-time" value, | o Resent-From | |||
| as specified in [RFC5322] section 3.3, into the "Date" type. If | ||||
| parsing fails, the value is "null". To prevent obviously nonsense | ||||
| behaviour, which can lead to interoperability issues, this form | ||||
| may only be fetched or set for the following header fields: | ||||
| * Date | o Resent-Sender | |||
| * Resent-Date | o Resent-Reply-To | |||
| * Any header not defined in [RFC5322] or [RFC2369] | o Resent-To | |||
| o *URLs* ("String[]|null") The header is parsed as a list of URLs, | o Resent-Cc | |||
| as described in [RFC2369], into the "String[]" type. Values do | ||||
| not include the surrounding angle brackets or any comments in the | ||||
| header with the URLs. If parsing fails, the value is "null". To | ||||
| prevent obviously nonsense behaviour, which can lead to | ||||
| interoperability issues, this form may only be fetched or set for | ||||
| the following header fields: | ||||
| * List-Help | o Resent-Bcc | |||
| * List-Unsubscribe | o Any header not defined in [RFC5322] or [RFC2369] | |||
| * List-Subscribe | 4.1.2.4. GroupedAddresses | |||
| * List-Post | Type: "EmailAddressGroup[]" | |||
| * List-Owner | This is similar to the Addresses form but preserves group | |||
| information. The header is parsed as an "address-list" value, as | ||||
| specified in [RFC5322] section 3.4, into the "GroupedAddresses[]" | ||||
| type. Consecutive mailboxes that are not part of a group are still | ||||
| collected under an EmailAddressGroup object to provide a uniform | ||||
| type. | ||||
| * List-Archive | The *EmailAddressGroup* object has the following properties: | |||
| * Any header not defined in [RFC5322] or [RFC2369] | o *name*: "String|null" The _display-name_ of the [RFC5322] _group_, | |||
| or "null" if the addresses are not part of a group. If this is a | ||||
| _quoted-string_ it is processed the same as the _name_ in the | ||||
| _EmailAddress_ type. | ||||
| o *addresses*: "EmailAddress[]" The _mailbox_es that belong to this | ||||
| group, represented as EmailAddress objects. | ||||
| Any syntactically correct [RFC2047] encoded sections with a known | ||||
| encoding MUST be decoded, following the same rules as for the _Text_ | ||||
| form. Any [RFC6532] UTF-8 values MUST be decoded. | ||||
| Parsing SHOULD be best-effort in the face of invalid structure to | ||||
| accommodate invalid messages and semi-complete drafts. | ||||
| 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>; | ||||
| would be parsed as: | ||||
| [ | ||||
| { "name": null, "addresses": [ | ||||
| { "name": "James Smythe", "email": "james@example.com" } | ||||
| ]}, | ||||
| { "name": "Friends", "addresses": [ | ||||
| { "name": null, "email": "jane@example.com" }, | ||||
| { "name": "John Smith", "email": "john@example.com" } | ||||
| ]} | ||||
| ] | ||||
| To prevent obviously nonsense behaviour, which can lead to | ||||
| interoperability issues, this form may only be fetched or set for the | ||||
| same header fields as the _Addresses_ form. | ||||
| 4.1.2.5. MessageIds | ||||
| Type: "String[]|null" | ||||
| The header is parsed as a list of "msg-id" values, as specified in | ||||
| [RFC5322] section 3.6.4, into the "String[]" type. CFWS and | ||||
| surrounding angle brackets ("<>") are removed. If parsing fails, the | ||||
| value is "null". | ||||
| To prevent obviously nonsense behaviour, which can lead to | ||||
| interoperability issues, this form may only be fetched or set for the | ||||
| following header fields: | ||||
| o Message-ID | ||||
| o In-Reply-To | ||||
| o References | ||||
| o Resent-Message-ID | ||||
| o Any header not defined in [RFC5322] or [RFC2369] | ||||
| 4.1.2.6. Date | ||||
| Type: "Date|null" | ||||
| The header is parsed as a "date-time" value, as specified in | ||||
| [RFC5322] section 3.3, into the "Date" type. If parsing fails, the | ||||
| value is "null". | ||||
| To prevent obviously nonsense behaviour, which can lead to | ||||
| interoperability issues, this form may only be fetched or set for the | ||||
| following header fields: | ||||
| o Date | ||||
| o Resent-Date | ||||
| o Any header not defined in [RFC5322] or [RFC2369] | ||||
| 4.1.2.7. URLs | ||||
| Type: "String[]|null" | ||||
| The header is parsed as a list of URLs, as described in [RFC2369], | ||||
| into the "String[]" type. Values do not include the surrounding | ||||
| angle brackets or any comments in the header with the URLs. If | ||||
| parsing fails, the value is "null". | ||||
| To prevent obviously nonsense behaviour, which can lead to | ||||
| interoperability issues, this form may only be fetched or set for the | ||||
| following header fields: | ||||
| o List-Help | ||||
| o List-Unsubscribe | ||||
| o List-Subscribe | ||||
| o List-Post | ||||
| o List-Owner | ||||
| o List-Archive | ||||
| o Any header not defined in [RFC5322] or [RFC2369] | ||||
| 4.1.3. Header fields properties | ||||
| The following low-level *Email* property is specified for complete | The following low-level *Email* property is specified for complete | |||
| access to the header data of the message: | access to the header data of the message: | |||
| o *headers*: "EmailHeader[]" (immutable) This is a list of all | o *headers*: "EmailHeader[]" (immutable) This is a list of all | |||
| [RFC5322] header fields, in the same order they appear in the | [RFC5322] header fields, in the same order they appear in the | |||
| message. An *EmailHeader* object has the following properties: | message. An *EmailHeader* object has the following properties: | |||
| * *name*: "String" The header _field name_ as defined in | * *name*: "String" The header _field name_ as defined in | |||
| [RFC5322], with the same capitalization that it has in the | [RFC5322], with the same capitalization that it has in the | |||
| skipping to change at page 26, line 5 ¶ | skipping to change at page 28, line 21 ¶ | |||
| o *replyTo*: "EmailAddress[]|null" (immutable) The value is | o *replyTo*: "EmailAddress[]|null" (immutable) The value is | |||
| identical to the value of _header:Reply-To:asAddresses_. | identical to the value of _header:Reply-To:asAddresses_. | |||
| o *subject*: "String|null" (immutable) The value is identical to the | o *subject*: "String|null" (immutable) The value is identical to the | |||
| value of _header:Subject:asText_. | value of _header:Subject:asText_. | |||
| o *sentAt*: "Date|null" (immutable; default on creation: current | o *sentAt*: "Date|null" (immutable; default on creation: current | |||
| server time) The value is identical to the value of | server time) The value is identical to the value of | |||
| _header:Date:asDate_. | _header:Date:asDate_. | |||
| 4.1.3. Body parts | 4.1.4. Body parts | |||
| These properties are derived from the [RFC5322] message body and its | These properties are derived from the [RFC5322] message body and its | |||
| [RFC2045] MIME entities. | [RFC2045] MIME entities. | |||
| A *EmailBodyPart* object has the following properties: | A *EmailBodyPart* object has the following properties: | |||
| 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 same the same blob id if | |||
| their decoded octets are identical and the server is using a | their decoded octets are identical and the server is using a | |||
| secure hash of the data for the blob id. | secure hash of the data for the blob id. | |||
| o *size*: "Number" 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. 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 | |||
| _Content-Type_ header field, if present, or "null" if the header | _Content-Type_ header field, if present, or "null" if the header | |||
| field is present but has no charset parameter. If there is no | field is present but not of type "text/*". If there is no | |||
| _Content-Type_ header field, this is the implicit charset as per | _Content-Type_ header field, or it exists and is of type "text/*" | |||
| the MIME standard ("us-ascii"). | but has no charset parameter, this is the implicit charset as per | |||
| the MIME standard: "us-ascii". | ||||
| o *disposition*: "String|null" The value of the _Content- | o *disposition*: "String|null" The value of the _Content- | |||
| Disposition_ header field of the part, if present, otherwise | Disposition_ header field of the part, if present, otherwise | |||
| "null". CFWS is removed and any parameters are stripped. | "null". CFWS is removed and any parameters are stripped. | |||
| o *cid*: "String|null" The value of the _Content-Id_ header field of | o *cid*: "String|null" The value of the _Content-Id_ header field of | |||
| the part, if present, otherwise "null". CFWS and surrounding | the part, if present, otherwise "null". CFWS and surrounding | |||
| angle brackets ("<>") are removed. This may be used to reference | angle brackets ("<>") are removed. This may be used to reference | |||
| the content from within an html body part using the "cid:" | the content from within an html body part using the "cid:" | |||
| protocol. | protocol. | |||
| o *language*: "String[]|null" The list of language tags, as defined | o *language*: "String[]|null" The list of language tags, as defined | |||
| in [RFC3282], in the _Content-Language_ header field of the part, | in [RFC3282], in the _Content-Language_ header field of the part, | |||
| if present. | if present. | |||
| o *location*: "String|null" The URI, as defined in [RFC2557], in the | o *location*: "String|null" The URI, as defined in [RFC2557], in the | |||
| _Content-Location_ header field of the part, if present. | _Content-Location_ header field of the part, if present. | |||
| o *subParts*: "EmailBodyPart[]" (optional) If type is "multipart/*", | o *subParts*: "EmailBodyPart[]|null" If type is "multipart/*", this | |||
| this contains the body parts of each child. | contains the body parts of each child. | |||
| In addition, the client may request/send EmailBodyPart properties | In addition, the client may request/send EmailBodyPart properties | |||
| representing individual header fields, following the same syntax and | representing individual header fields, following the same syntax and | |||
| semantics as for the Email object, e.g. "header:Content-Type". | semantics as for the Email object, e.g. "header:Content-Type". | |||
| The following *Email* properties are specified for access to the body | The following *Email* properties are specified for access to the body | |||
| data of the message: | data of the message: | |||
| o *bodyStructure*: "EmailBodyPart" (immutable) This is the full MIME | o *bodyStructure*: "EmailBodyPart" (immutable) This is the full MIME | |||
| structure of the message body, represented as an array of the | structure of the message body, represented as an array of the | |||
| skipping to change at page 28, line 5 ¶ | skipping to change at page 30, line 22 ¶ | |||
| with a single LF. The server MAY use heuristics to determine | with a single LF. The server MAY use heuristics to determine | |||
| the charset to use for decoding if the charset is unknown, or | the charset to use for decoding if the charset is unknown, or | |||
| if no charset is given, or if it believes the charset given is | if no charset is given, or if it believes the charset given is | |||
| incorrect. Decoding is best-effort and SHOULD insert the | incorrect. Decoding is best-effort and SHOULD insert the | |||
| unicode replacement character (U+FFFD) and continue when a | unicode replacement character (U+FFFD) and continue when a | |||
| malformed section is encountered. Note that due to the charset | malformed section is encountered. Note that due to the charset | |||
| decoding and line ending normalisation, the length of this | decoding and line ending normalisation, the length of this | |||
| string will probably not be exactly the same as the _size_ | string will probably not be exactly the same as the _size_ | |||
| property on the corresponding EmailBodyPart. | property on the corresponding EmailBodyPart. | |||
| * *isEncodingProblem*: "Boolean" (default: "false") This is | * *isEncodingProblem*: "Boolean" (default: false) This is "true" | |||
| "true" if malformed sections were found while decoding the | if malformed sections were found while decoding the charset, or | |||
| charset, or the charset was unknown. | the charset was unknown. | |||
| * *isTruncated*: "Boolean" (default: "false") This is "true" if | * *isTruncated*: "Boolean" (default: false) This is "true" if the | |||
| the _value_ has been truncated. | _value_ has been truncated. | |||
| See the security considerations section for issues related to | See the security considerations section for issues related to | |||
| truncation and heuristic determination of content-type and | truncation and heuristic determination of content-type and | |||
| charset. | charset. | |||
| o *textBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", | o *textBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", | |||
| "text/html", "image/*", "audio/*" and/or "video/*" parts to | "text/html", "image/*", "audio/*" and/or "video/*" parts to | |||
| display (sequentially) as the message body, with a preference for | display (sequentially) as the message body, with a preference for | |||
| "text/plain" when alternative versions are available. | "text/plain" when alternative versions are available. | |||
| skipping to change at page 32, line 7 ¶ | skipping to change at page 34, line 29 ¶ | |||
| text/plain, content-disposition=inline - K | text/plain, content-disposition=inline - K | |||
| In this case, the above algorithm would decompose this to: | In this case, the above algorithm would decompose this to: | |||
| textBody => [ A, B, C, D, K ] | textBody => [ A, B, C, D, K ] | |||
| htmlBody => [ A, E, K ] | htmlBody => [ A, E, K ] | |||
| attachments => [ C, F, G, H, J ] | attachments => [ C, F, G, H, J ] | |||
| 4.2. Email/get | 4.2. Email/get | |||
| Standard _/get_ method, with the following additional arguments: | Standard "/get" method, with the following additional arguments: | |||
| o *bodyProperties*: "String[]" (optional) A list of properties to | o *bodyProperties*: "String[]" A list of properties to fetch for | |||
| fetch for each EmailBodyPart returned. If omitted, this defaults | each EmailBodyPart returned. If omitted, this defaults to: | |||
| to: [ "partId", "blobId", "size", "name", "type", "charset", | ||||
| "disposition", cid", "language", "location" ] | ||||
| o *fetchTextBodyValues*: "Boolean" (default: "false") If "true", the | [ "partId", "blobId", "size", "name", "type", "charset", | |||
| "disposition", "cid", "language", "location" ] | ||||
| o *fetchTextBodyValues*: "Boolean" (default: false) If "true", the | ||||
| _bodyValues_ property includes any "text/*" part in the "textBody" | _bodyValues_ property includes any "text/*" part in the "textBody" | |||
| property. | property. | |||
| o *fetchHTMLBodyValues*: "Boolean" (default: "false") If "true", the | o *fetchHTMLBodyValues*: "Boolean" (default: false) If "true", the | |||
| _bodyValues_ property includes any "text/*" part in the "htmlBody" | _bodyValues_ property includes any "text/*" part in the "htmlBody" | |||
| property. | property. | |||
| o *fetchAllBodyValues*: "Boolean" (default: "false") If "true", the | o *fetchAllBodyValues*: "Boolean" (default: false) If "true", the | |||
| _bodyValues_ property includes any "text/*" part in the | _bodyValues_ property includes any "text/*" part in the | |||
| "bodyStructure" property. | "bodyStructure" property. | |||
| o *maxBodyValueBytes*: "Number" (optional) If supplied by the | o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than | |||
| client, the value MUST be a positive integer greater than 0. If a | zero, the _value_ property of any EmailBodyValue object returned | |||
| value outside of this range is given, the server MUST reject the | in _bodyValues_ MUST be truncated if necessary so it does not | |||
| call with an "invalidArguments" error. When given, the _value_ | exceed this number of octets in size. If "0" (the default), no | |||
| property of any EmailBodyValue object returned in _bodyValues_ | truncation occurs. The server MUST ensure the truncation results | |||
| MUST be truncated if necessary so it does not exceed this number | ||||
| of octets in size. 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">". There | |||
| is no requirement for the truncated form to be a balanced tree or | is no requirement for the truncated form to be a balanced tree or | |||
| valid HTML (indeed, the original source may well be neither of | valid HTML (indeed, the original source may well be neither of | |||
| these things). | these things). | |||
| If the standard _properties_ argument is omitted or "null", the | If the standard _properties_ argument is omitted or "null", the | |||
| following default MUST be used instead of "all" properties: | following default MUST be used instead of "all" properties: | |||
| skipping to change at page 34, line 9 ¶ | 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@bloggs.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 35, line 43 ¶ | skipping to change at page 37, line 43 ¶ | |||
| }, | }, | |||
| "2": { | "2": { | |||
| "isEncodingProblem": false, | "isEncodingProblem": false, | |||
| "isTruncated": false, | "isTruncated": false, | |||
| "value": "-- \nSent by your friendly mailing list ..." | "value": "-- \nSent by your friendly mailing list ..." | |||
| } | } | |||
| } | } | |||
| } | } | |||
| ], | ], | |||
| notFound: [ "f123u456" ] | notFound: [ "f123u456" ] | |||
| }, "#1"] | }, "#1" ] | |||
| 4.3. Email/changes | 4.3. Email/changes | |||
| Standard _/changes_ method. | Standard "/changes" method. If generating intermediate states for a | |||
| large set of changes, it is recommended that newer changes are | ||||
| 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 | o *collapseThreads*: "Boolean" (default: false) If "true", emails in | |||
| in the same thread as a previous email in the list (given the | the same thread as a previous email in the list (given the filter | |||
| filter and sort order) will be removed from the list. This means | and sort order) will be removed from the list. This means at most | |||
| at most only one email will be included in the list for any given | only one email will be included in the list for any given thread. | |||
| thread. | ||||
| In quality implementations, the query "total" property is expected to | ||||
| be fast to calculate when the filter consists solely of a single | ||||
| "inMailbox" property, as it is the same as the totalEmails or | ||||
| totalThreads properties (depending on whether collapseThreads is | ||||
| 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 of the email must be | |||
| before this date to match the condition. | before this date to match the condition. | |||
| o *after*: "UTCDate" The _receivedAt_ date of the email must be on | o *after*: "UTCDate" The _receivedAt_ date of the email must be on | |||
| or after this date to match the condition. | or after this date to match the condition. | |||
| o *minSize*: "Number" 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*: "Number" The size of the email in octets must be less | o *maxSize*: "PositiveInt" The size of the email in octets must be | |||
| 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. | |||
| o *noneInThreadHaveKeyword*: "String" All emails (including this | o *noneInThreadHaveKeyword*: "String" All emails (including this | |||
| skipping to change at page 37, line 35 ¶ | skipping to change at page 39, line 39 ¶ | |||
| o *cc*: "String" Looks for the text in the _Cc_ header field of the | o *cc*: "String" Looks for the text in the _Cc_ header field of the | |||
| message. | message. | |||
| o *bcc*: "String" Looks for the text in the _Bcc_ header field of | o *bcc*: "String" Looks for the text in the _Bcc_ header field of | |||
| the message. | the message. | |||
| o *subject*: "String" Looks for the text in the _subject_ property | o *subject*: "String" Looks for the text in the _subject_ property | |||
| of the email. | of the email. | |||
| o *body*: "String" Looks for the text in one of the "text/*" body | o *body*: "String" Looks for the text in one of the body parts of | |||
| parts of the email. | the email. The server MAY exclude MIME body parts with content | |||
| media types other than "text/_" and "message/_" from consideration | ||||
| o *attachments*: "String" Looks for the text in the attachments of | in search matching. Care should be taken to match based on the | |||
| the email. Servers MAY handle text extraction when possible for | text content actually presented to an end-user by viewers for that | |||
| the different kinds of media. | media type, or otherwise identified as appropriate for search | |||
| indexing. Matching document metadata uninteresting to an end-user | ||||
| (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 39, line 13 ¶ | skipping to change at page 41, line 21 ¶ | |||
| "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. | |||
| o *subject* - This is taken to be the base subject of the email, as | o *subject* - This is taken to be the base subject of the email, as | |||
| defined in section 2.1 of [RFC5256]. | defined in section 2.1 of [RFC5256]. | |||
| o *sentAt* - The _sentAt_ property on the Email object. | o *sentAt* - The _sentAt_ property on the Email object. | |||
| o *hasKeyword* - This value MUST be considered "true" if the email | o *hasKeyword* - This value MUST be considered "true" if the email | |||
| has the keyword given as the _keyword_ property on this | has the keyword given as an additional _keyword_ property on the | |||
| _Comparator_ object, or "false" otherwise. | _Comparator_ object, or "false" otherwise. | |||
| o *allInThreadHaveKeyword* - This value MUST be considered "true" | o *allInThreadHaveKeyword* - This value MUST be considered "true" | |||
| for the email if *all* of the emails in the same thread | for the email if *all* of the emails in the same thread | |||
| (regardless of mailbox) have the keyword given as the _keyword_ | (regardless of mailbox) have the keyword given as an additional | |||
| property on this _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 the _keyword_ | (regardless of mailbox) have the keyword given as an additional | |||
| property on this _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). | |||
| Example sort: | Example sort: | |||
| [{ | [{ | |||
| "property": "someInThreadHaveKeyword", | "property": "someInThreadHaveKeyword", | |||
| "keyword": "$flagged", | "keyword": "$flagged", | |||
| skipping to change at page 40, line 7 ¶ | skipping to change at page 42, line 13 ¶ | |||
| }] | }] | |||
| 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 == true", then after filtering and sorting the | When _collapseThreads_ is "true", then after filtering and sorting | |||
| email list, the list is further winnowed by removing any emails for a | the email list, the list is further winnowed by removing any emails | |||
| thread id that has already been seen (when passing through the list | for a thread id that has already been seen (when passing through the | |||
| sequentially). A thread will therefore only appear *once* in the | list sequentially). A thread will therefore only appear *once* in | |||
| "threadIds" list of the result, at the position of the first email in | the result, at the position of the first email in the list that | |||
| the list that belongs to the thread. | belongs to the thread (given the current sort/filter). | |||
| 4.4.4. Response | 4.4.4. Response | |||
| The response has the following additional argument: | The response has the following additional argument: | |||
| o *collapseThreads*: "Boolean" The _collapseThreads_ value that was | o *collapseThreads*: "Boolean" The _collapseThreads_ value that was | |||
| used when calculating the email list for this call. | 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: | The response has the following additional argument: | |||
| o *collapseThreads*: "Boolean" The _collapseThreads_ value that was | o *collapseThreads*: "Boolean" The _collapseThreads_ value that was | |||
| used when calculating the email list for this call. | 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) | |||
| o Deleting emails | o Deleting emails | |||
| Due to the format of the Email object, when creating an email there | Due to the format of the Email object, when creating an email there | |||
| are a number of ways to specify the same information. To ensure that | are a number of ways to specify the same information. To ensure that | |||
| the RFC5322 email to create is unambiguous, the following constraints | the RFC5322 email to create is unambiguous, the following constraints | |||
| apply to Email objects submitted for creation: | apply to Email objects submitted for creation: | |||
| o The _headers_ property MUST NOT be given, on either the top-level | o The _headers_ property MUST NOT be given, on either the top-level | |||
| email or an EmailBodyPart - the client must set each header field | email or an EmailBodyPart - the client must set each header field | |||
| as an individual property. | as an individual property. | |||
| o There MUST NOT be two properties that represent the same header | o There MUST NOT be two properties that represent the same header | |||
| skipping to change at page 42, line 27 ¶ | skipping to change at page 44, line 33 ¶ | |||
| 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 | |||
| just remove the Trash mailbox from the email. | just remove the Trash mailbox from the email. | |||
| For successfully created Email objects, the _created_ response MUST | For successfully created Email objects, the _created_ response | |||
| contain the _id_, _blobId_, _threadId_ and _size_ properties of the | contains the _id_, _blobId_, _threadId_ and _size_ properties of the | |||
| object. | object. | |||
| The following extra _SetError_ types are defined: | The following extra _SetError_ types are defined: | |||
| For *create*: | For *create*: | |||
| o "blobNotFound": At least one blob id given for an EmailBodyPart | o "blobNotFound": At least one blob id given for an EmailBodyPart | |||
| doesn't exist. An extra _notFound_ property of type "String[]" | doesn't exist. An extra _notFound_ property of type "String[]" | |||
| MUST be included in the error object containing every _blobId_ | MUST be included in the error object containing every _blobId_ | |||
| referenced by an EmailBodyPart that could not be found on the | referenced by an EmailBodyPart that could not be found on the | |||
| server. | server. | |||
| For *create* and *update*: | For *create* and *update*: | |||
| o "tooManyKeywords": The change to the email's keywords would exceed | o "tooManyKeywords": The change to the email's keywords would exceed | |||
| a server-defined maximum. | a server-defined maximum. | |||
| o "tooManyMailboxes": The change to the email's mailboxes would | o "tooManyMailboxes": The change to the email's mailboxes would | |||
| exceed a server-defined maximum. | exceed a server-defined maximum. | |||
| 4.7. Email/import | 4.7. Email/copy | |||
| Standard "/copy" method, except only the _mailboxIds_, _keywords_ and | ||||
| _receivedAt_ properties may be set during the copy. This method | ||||
| cannot modify the RFC5322 representation of an email. | ||||
| The server MAY forbid two email objects with the same exact [RFC5322] | ||||
| content, or even just with the same [RFC5322] Message-ID, to coexist | ||||
| within an account. If duplicates are allowed though, the "from" | ||||
| account may be the same as the "to" account to copy emails within an | ||||
| account. | ||||
| For successfully copied Email objects, the _created_ response | ||||
| contains the _id_, _blobId_, _threadId_ and _size_ properties of the | ||||
| new object. | ||||
| 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 a user's set of | |||
| emails. The messages must first be uploaded as a file using the | emails. The messages must first be uploaded as a file using the | |||
| standard upload mechanism. It takes the following arguments: | standard upload mechanism. It takes the following arguments: | |||
| o *accountId*: "String|null" The id of the account to use for this | o *accountId*: "String|null" The id of the account to use for this | |||
| call. If "null", defaults to the "urn:ietf:params:jmap:mail" | call. If "null", defaults to the "urn:ietf:params:jmap:mail" | |||
| primary account. | primary account. | |||
| o *emails*: "String[EmailImport]" A map of creation id (client | o *emails*: "String[EmailImport]" A map of creation id (client | |||
| specified) to EmailImport objects | specified) to EmailImport objects | |||
| An *EmailImport* object has the following properties: | An *EmailImport* object has the following properties: | |||
| o *blobId*: "String" The id of the blob containing the raw [RFC5322] | o *blobId*: "String" The id of the blob containing the raw [RFC5322] | |||
| message. | message. | |||
| o *mailboxIds* "String[Boolean]" The ids of the 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 | o *keywords*: "String[Boolean]" (default: ) The keywords to apply to | |||
| apply to the email. | the email. | |||
| o *receivedAt*: "UTCDate" (default: time of import on server) The | o *receivedAt*: "UTCDate" (default: time of import on server) The | |||
| _receivedAt_ date to set on the email. | _receivedAt_ date to set on the email. | |||
| Each email to import is considered an atomic unit which may succeed | Each email to import is considered an atomic unit which may succeed | |||
| or fail individually. Importing successfully creates a new email | or fail individually. Importing successfully creates a new email | |||
| object from the data reference by the blobId and applies the given | object from the data 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 | |||
| _emailId_ property of type "String" MUST be included on the error | _emailId_ property of type "String" MUST be included on the error | |||
| object with the id of the existing email. | object with the id of the existing email. | |||
| If the _blobId_, _mailboxIds_, or _keywords_ properties are invalid | If the _blobId_, _mailboxIds_, or _keywords_ properties are invalid | |||
| (e.g. missing, wrong type, id not found), the server MUST reject the | (e.g. missing, wrong type, id not found), the server MUST reject the | |||
| import with an "invalidProperties" SetError. | import with an "invalidProperties" SetError. | |||
| If the email cannot be imported because it would take the account | If the email cannot be imported because it would take the account | |||
| over quota, the import should be rejected with a "maxQuotaReached" | over quota, the import should be rejected with a "overQuota" | |||
| SetError. | SetError. | |||
| If the blob referenced is not a valid [RFC5322] message, the server | If the blob referenced is not a valid [RFC5322] message, the server | |||
| MAY modify the message to fix errors (such as removing NUL octets or | MAY modify the message to fix errors (such as removing NUL octets or | |||
| fixing invalid headers). If it does this, the _blobId_ on the | fixing invalid headers). If it does this, the _blobId_ on the | |||
| response MUST represent the new representation and therefore be | response MUST represent the new representation and therefore be | |||
| different to the _blobId_ on the EmailImport object. Alternatively, | different to the _blobId_ on the EmailImport object. Alternatively, | |||
| the server MAY reject the import with an "invalidEmail" SetError. | the server MAY reject the import with an "invalidEmail" SetError. | |||
| The response has the following arguments: | The response has the following arguments: | |||
| skipping to change at page 44, line 24 ¶ | skipping to change at page 46, line 41 ¶ | |||
| o *accountId*: "String" The id of the account used for this call. | o *accountId*: "String" The id of the account used for this call. | |||
| o *created*: "String[Email]" A map of the creation id to an object | o *created*: "String[Email]" A map of the creation id to an object | |||
| containing the _id_, _blobId_, _threadId_ and _size_ properties | containing the _id_, _blobId_, _threadId_ and _size_ properties | |||
| 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. | |||
| 4.8. Email/copy | ||||
| The only way to move messages *between* two different accounts is to | ||||
| copy them using the _Email/copy_ method, then once the copy has | ||||
| succeeded, delete the original. The _onSuccessDestroyOriginal_ | ||||
| argument allows you to try to do this in one method call, however | ||||
| note that the two different actions are not atomic, and so it is | ||||
| possible for the copy to succeed but the original not to be destroyed | ||||
| for some reason. | ||||
| The _Email/copy_ method takes the following arguments: | ||||
| o *fromAccountId*: "String|null" The id of the account to copy | ||||
| emails from. If "null", defaults to the | ||||
| "urn:ietf:params:jmap:mail" primary account. | ||||
| o *toAccountId*: "String|null" The id of the account to copy emails | ||||
| to. If "null", defaults to the "urn:ietf:params:jmap:mail" | ||||
| primary account. | ||||
| o *create*: "String[EmailCopy]" A map of _creation id_ to an | ||||
| EmailCopy object. | ||||
| o *onSuccessDestroyOriginal*: "Boolean" (default: "false") If | ||||
| "true", an attempt will be made to destroy the emails that were | ||||
| successfully copied: after emitting the _Email/copy_ response, but | ||||
| before processing the next method, the server MUST make a single | ||||
| call to _Email/set_ to destroy the original of each successfully | ||||
| copied message; the output of this is added to the responses as | ||||
| normal to be returned to the client. | ||||
| An *EmailCopy* object has the following properties: | ||||
| o *id*: "String" The id of the email to be copied in the "from" | ||||
| account. | ||||
| o *mailboxIds*: "String[Boolean]" The ids of the mailboxes (in the | ||||
| "to" account) to add the copied email to. At least one mailbox | ||||
| MUST be given. | ||||
| o *keywords*: "String[Boolean]" (default: "{}") The _keywords_ | ||||
| property for the copy. | ||||
| o *receivedAt*: "UTCDate" (default: _receivedAt_ date of original) | ||||
| The _receivedAt_ date to set on the copy. | ||||
| The server MAY forbid two email objects with the same exact [RFC5322] | ||||
| content, or even just with the same [RFC5322] Message-ID, to coexist | ||||
| within an account. If duplicates are allowed though, the "from" | ||||
| account may be the same as the "to" account to copy emails within an | ||||
| account. | ||||
| Each email copy is considered an atomic unit which may succeed or | ||||
| fail individually. Copying successfully MUST create a new email | ||||
| object, with separate ids and mutable properties (e.g. mailboxes and | ||||
| keywords) to the original email. | ||||
| The response has the following arguments: | ||||
| o *fromAccountId*: "String" The id of the account emails were copied | ||||
| from. | ||||
| o *toAccountId*: "String" The id of the account emails were copied | ||||
| to. | ||||
| o *created*: "String[Email]|null" A map of the creation id to an | ||||
| object containing the _id_, _blobId_, _threadId_ and _size_ | ||||
| properties for each successfully copied Email. | ||||
| o *notCreated*: "String[SetError]|null" A map of creation id to a | ||||
| SetError object for each Email that failed to be copied, "null" if | ||||
| none. | ||||
| The *SetError* may be any of the standard set errors that may be | ||||
| returned for a _create_. The following extra _SetError_ type is also | ||||
| defined: | ||||
| "alreadyExists": The server forbids duplicates and the email already | ||||
| exists in the target account. An _emailId_ property of type "String" | ||||
| MUST be included on the error object with the id of the existing | ||||
| email. | ||||
| The following additional errors may be returned instead of the | ||||
| _Email/copy_ response: | ||||
| "fromAccountNotFound": A _fromAccountId_ was explicitly included with | ||||
| the request, but it does not correspond to a valid account; or, | ||||
| _fromAccountId_ was null but there is no primary account for | ||||
| "urn:ietf:params:jmap:mail". | ||||
| "toAccountNotFound": A _toAccountId_ was explicitly included with the | ||||
| request, but it does not correspond to a valid account; or, | ||||
| _toAccountId_ was null but there is no primary account for | ||||
| "urn:ietf:params:jmap:mail". | ||||
| "fromAccountNotSupportedByMethod": The _fromAccountId_ given | ||||
| corresponds to a valid account, but does not contain any mail data. | ||||
| "toAccountNotSupportedByMethod": The _toAccountId_ given corresponds | ||||
| to a valid account, but does not contain any mail data. | ||||
| 4.9. Email/parse | 4.9. Email/parse | |||
| This method allows you to parse blobs as [RFC5322] messages to get | This method allows you to parse blobs as [RFC5322] messages to get | |||
| Email objects. The following metadata properties on the Email | Email objects. This can be used to parse and display attached emails | |||
| objects will be "null" if requested: | without having to import them as top-level email objects in the mail | |||
| store in their own right. | ||||
| o id | The following metadata properties on the Email objects will be "null" | |||
| if requested: | ||||
| o id | ||||
| o mailboxIds | o mailboxIds | |||
| o keywords | o keywords | |||
| o receivedAt | o receivedAt | |||
| The _threadId_ property of the Email MAY be present if the server can | The _threadId_ property of the Email MAY be present if the server can | |||
| calculate which thread the Email would be assigned to were it to be | calculate which thread the Email would be assigned to were it to be | |||
| imported. Otherwise, this too is "null" if fetched. | imported. Otherwise, this too is "null" if fetched. | |||
| skipping to change at page 47, line 12 ¶ | skipping to change at page 47, line 28 ¶ | |||
| o *blobIds*: "String[]" The ids of the blobs to parse. | o *blobIds*: "String[]" The ids of the blobs to parse. | |||
| o *properties*: "String[]" If supplied, only the properties listed | o *properties*: "String[]" If supplied, only the properties listed | |||
| in the array are returned for each Email object. If omitted, | in the array are returned for each Email object. If omitted, | |||
| defaults to: [ "messageId", "inReplyTo", "references", "sender", | defaults to: [ "messageId", "inReplyTo", "references", "sender", | |||
| "from", "to", "cc", "bcc", "replyTo", "subject", "sentAt", | "from", "to", "cc", "bcc", "replyTo", "subject", "sentAt", | |||
| "hasAttachment", "preview", "bodyValues", "textBody", "htmlBody", | "hasAttachment", "preview", "bodyValues", "textBody", "htmlBody", | |||
| "attachments" ] | "attachments" ] | |||
| o *bodyProperties*: "String[]" (optional) A list of properties to | o *bodyProperties*: "String[]" A list of properties to fetch for | |||
| fetch for each EmailBodyPart returned. If omitted, defaults to | each EmailBodyPart returned. If omitted, defaults to the same | |||
| the same value as the Email/get "bodyProperties" default argument. | value as the Email/get "bodyProperties" default argument. | |||
| o *fetchTextBodyValues*: "Boolean" (default: "false") If "true", the | o *fetchTextBodyValues*: "Boolean" (default: false) If "true", the | |||
| _bodyValues_ property includes any "text/*" part in the "textBody" | _bodyValues_ property includes any "text/*" part in the "textBody" | |||
| property. | property. | |||
| o *fetchHTMLBodyValues*: "Boolean" (default: "false") If "true", the | o *fetchHTMLBodyValues*: "Boolean" (default: false) If "true", the | |||
| _bodyValues_ property includes any "text/*" part in the "htmlBody" | _bodyValues_ property includes any "text/*" part in the "htmlBody" | |||
| property. | property. | |||
| o *fetchAllBodyValues*: "Boolean" (default: "false") If "true", the | o *fetchAllBodyValues*: "Boolean" (default: false) If "true", the | |||
| _bodyValues_ property includes any "text/*" part in the | _bodyValues_ property includes any "text/*" part in the | |||
| "bodyStructure" property. | "bodyStructure" property. | |||
| o *maxBodyValueBytes*: "Number" (optional) If supplied by the | o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than | |||
| client, the value MUST be a positive integer greater than 0. If a | zero, the _value_ property of any EmailBodyValue object returned | |||
| value outside of this range is given, the server MUST reject the | in _bodyValues_ MUST be truncated if necessary so it does not | |||
| call with an "invalidArguments" error. When given, the _value_ | exceed this number of octets in size. If "0" (the default), no | |||
| property of any EmailBodyValue object returned in _bodyValues_ | truncation occurs. The server MUST ensure the truncation results | |||
| MUST be truncated if necessary so it does not exceed this number | ||||
| of octets in size. 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. | tag e.g. in the middle of "<a href="https://example.com">". There | |||
| is no requirement for the truncated form to be a balanced tree or | ||||
| valid HTML (indeed, the original source may well be 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 48, line 14 ¶ | skipping to change at page 48, line 32 ¶ | |||
| As specified above, parsed forms of headers may only be used on | As specified above, parsed forms of headers may only be used on | |||
| appropriate header fields. Attempting to fetch a form that is | appropriate header fields. Attempting to fetch a form that is | |||
| forbidden (e.g. "header:From:asDate") MUST result in the method call | forbidden (e.g. "header:From:asDate") MUST result in the method call | |||
| being rejected with an "invalidArguments" error. | being rejected with an "invalidArguments" error. | |||
| Where a specific header is requested as a property, the | Where a specific header is requested as a property, the | |||
| capitalization of the property name in the response MUST be identical | capitalization of the property name in the response MUST be identical | |||
| to that used in the request. | to that used in the request. | |||
| 5. Identities | 4.10. Examples | |||
| A client logs in for the first time. It first fetches the set of | ||||
| mailboxes. Now it will display the inbox to the user, which we will | ||||
| presume has mailbox id "fb666a55". The inbox may be (very!) large, | ||||
| but the user's screen is only so big, so the client will just load | ||||
| the start and then can load in more as necessary. The client sends | ||||
| this request: | ||||
| [[ "Email/query",{ | ||||
| "accountId": "ue150411c", | ||||
| "filter": { | ||||
| "inMailbox": "fb666a55" | ||||
| }, | ||||
| "sort": [{ | ||||
| "isAscending": false, | ||||
| "property": "receivedAt" | ||||
| }], | ||||
| "collapseThreads": true, | ||||
| "position": 0, | ||||
| "limit": 30, | ||||
| "calculateTotal": true | ||||
| }, "0" ], | ||||
| [ "Email/get", { | ||||
| "accountId": "ue150411c", | ||||
| "#ids": { | ||||
| "resultOf": "0", | ||||
| "name": "Email/query", | ||||
| "path": "/ids" | ||||
| }, | ||||
| "properties": [ | ||||
| "threadId" | ||||
| ] | ||||
| }, "1" ], | ||||
| [ "Thread/get", { | ||||
| "accountId": "ue150411c", | ||||
| "#ids": { | ||||
| "resultOf": "1", | ||||
| "name": "Email/get", | ||||
| "path": "/list/*/threadId" | ||||
| } | ||||
| }, "2" ], | ||||
| [ "Email/get", { | ||||
| "accountId": "ue150411c", | ||||
| "#ids": { | ||||
| "resultOf": "2", | ||||
| "name": "Thread/get", | ||||
| "path": "/list/*/emailIds" | ||||
| }, | ||||
| "properties": [ | ||||
| "threadId", | ||||
| "mailboxIds", | ||||
| "keywords", | ||||
| "hasAttachment", | ||||
| "from", | ||||
| "subject", | ||||
| "receivedAt", | ||||
| "size", | ||||
| "preview" | ||||
| ] | ||||
| }, "3" ]] | ||||
| 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 | ||||
| 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 | ||||
| unique threads). | ||||
| 2. Now we use a backreference to fetch the thread ids for each of | ||||
| these email ids. | ||||
| 3. Another backreference fetches the Thread object for each of these | ||||
| thread ids. | ||||
| 4. Finally, we fetch the information we need to display the mailbox | ||||
| listing ( but no more!) for every message in each of these 30 | ||||
| threads. The client may aggregate this data for display, for | ||||
| example showing the thread as "flagged" if any of the messages in | ||||
| it contain the "$flagged" keyword. | ||||
| The response from the server may look something like this: | ||||
| [[ "Email/query", { | ||||
| "accountId": "ue150411c", | ||||
| "filter": { | ||||
| "inMailbox": "fb666a55" | ||||
| }, | ||||
| "sort": [{ | ||||
| "property": "receivedAt", | ||||
| "isAscending": false | ||||
| }], | ||||
| "collapseThreads": true, | ||||
| "queryState": "09aa9a075588-780599:0", | ||||
| "canCalculateChanges": true, | ||||
| "position": 0, | ||||
| "total": 115, | ||||
| "ids": [ "Ma783e5cdf5f2deffbc97930a", "M9bd17497e2a99cb345fc1d0a", ... ] | ||||
| }, "0" ], | ||||
| [ "Email/get", { | ||||
| "accountId": "ue150411c", | ||||
| "state": "780599", | ||||
| "list": [{ | ||||
| "id": "Ma783e5cdf5f2deffbc97930a", | ||||
| "threadId": "T36703c2cfe9bd5ed" | ||||
| }, { | ||||
| "id": "M9bd17497e2a99cb345fc1d0a" | ||||
| "threadId": "T0a22ad76e9c097a1", | ||||
| }, ... ], | ||||
| "notFound": [] | ||||
| }, "1" ], | ||||
| [ "Thread/get", { | ||||
| "accountId": "ue150411c", | ||||
| "state": "22a8728b", | ||||
| "list": [{ | ||||
| "id": "T36703c2cfe9bd5ed" | ||||
| "emailIds": [ "Ma783e5cdf5f2deffbc97930a" ], | ||||
| }, { | ||||
| "id": "T0a22ad76e9c097a1" | ||||
| "emailIds": [ "M3b568670a63e5d100f518fa5", "M9bd17497e2a99cb345fc1d0a" ], | ||||
| }, ... ], | ||||
| "notFound": [] | ||||
| }, "2" ], | ||||
| [ "Email/get", { | ||||
| "accountId": "ue150411c", | ||||
| "state": "780599", | ||||
| "list": [{ | ||||
| "id": "Ma783e5cdf5f2deffbc97930a" | ||||
| "threadId": "T36703c2cfe9bd5ed", | ||||
| "mailboxIds": { | ||||
| "fb666a55": true | ||||
| }, | ||||
| "keywords": { | ||||
| "$seen": true, | ||||
| "$flagged": true | ||||
| }, | ||||
| "hasAttachment": true, | ||||
| "from": [{ | ||||
| "email": "jdoe@example.com", | ||||
| "name": "Jane Doe" | ||||
| }], | ||||
| "subject": "The Big Reveal", | ||||
| "receivedAt": "2018-06-27T00:20:35Z", | ||||
| "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 ...", | ||||
| }, | ||||
| ... | ||||
| ], | ||||
| "notFound": [] | ||||
| }, "3" ]] | ||||
| Now, on another device the user marks the first message as unread, | ||||
| sending this API request: | ||||
| [[ "Email/set", { | ||||
| "accountId": "ue150411c", | ||||
| "update": { | ||||
| "Ma783e5cdf5f2deffbc97930a": { | ||||
| "keywords/$seen": null | ||||
| } | ||||
| } | ||||
| }, "0" ]] | ||||
| The server applies this and sends the success response: | ||||
| [[ "Email/set", { | ||||
| "accountId": "ue150411c", | ||||
| "oldState": "780605" | ||||
| "newState": "780606", | ||||
| "updated": { | ||||
| "Ma783e5cdf5f2deffbc97930a": null | ||||
| }, | ||||
| ... | ||||
| }, "0" ]] | ||||
| 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 | ||||
| string for Email is now "780800". As this does not match the | ||||
| client's current state, it issues a request for the changes: | ||||
| [[ "Email/changes", { | ||||
| "accountId": "ue150411c", | ||||
| "sinceState": "780605", | ||||
| "maxChanges": 50 | ||||
| }, "3" ], | ||||
| [ "Email/queryChanges", { | ||||
| "accountId": "ue150411c", | ||||
| "filter": { | ||||
| "inMailbox": "fb666a55" | ||||
| }, | ||||
| "sort": [{ | ||||
| "property": "receivedAt", | ||||
| "isAscending": false | ||||
| }], | ||||
| "collapseThreads": true, | ||||
| "sinceQueryState": "09aa9a075588-780599:0", | ||||
| "upToId": "Mc2781d5e856a908d8a35a564", | ||||
| "maxChanges": 25, | ||||
| "calculateTotal": true | ||||
| }, "11" ]] | ||||
| The response: | ||||
| [ "Email/changes", { | ||||
| "accountId": "ue150411c", | ||||
| "oldState": "780605", | ||||
| "newState": "780800", | ||||
| "hasMoreChanges": false, | ||||
| "created": [ "Me8de6c9f6de198239b982ea2" ], | ||||
| "updated": [ "Ma783e5cdf5f2deffbc97930a" ], | ||||
| "destroyed": [ "M9bd17497e2a99cb345fc1d0a", ... ], | ||||
| }, "3" ], | ||||
| [ "Email/queryChanges", { | ||||
| "accountId": "ue150411c", | ||||
| "oldQueryState": "09aa9a075588-780599:0" | ||||
| "newQueryState": "e35e9facf117-780615:0", | ||||
| "filter": { | ||||
| "inMailbox": "fb666a55" | ||||
| }, | ||||
| "sort": [{ | ||||
| "property": "receivedAt", | ||||
| "isAscending": false | ||||
| }], | ||||
| "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 | ||||
| removing "M9bd17497e2a99cb345fc1d0a" and then splicing in | ||||
| "Me8de6c9f6de198239b982ea2" at position 0. As it does not have the | ||||
| data for this new email, it will then fetch it (it also could have | ||||
| done this in the same request using backreferences). | ||||
| It knows something has changed about "Ma783e5cdf5f2deffbc97930a", so | ||||
| it will refetch the mailboxes and keywords (the only mutable | ||||
| properties) for this email too. | ||||
| The user composes a new message and saves a draft. The client sends: | ||||
| [[ "Email/set", { | ||||
| "accountId": "ue150411c", | ||||
| "create": { | ||||
| "k1546": { | ||||
| "mailboxIds": { | ||||
| "2ea1ca41b38e": true | ||||
| }, | ||||
| "keywords": { | ||||
| "$seen": true, | ||||
| "$draft": true | ||||
| }, | ||||
| "from": [{ | ||||
| "name": "Joe Bloggs", | ||||
| "email": "joe@example.com" | ||||
| }], | ||||
| "to": [{ | ||||
| "name": "John", | ||||
| "email": "john@example.com" | ||||
| }], | ||||
| "subject": "World domination", | ||||
| "receivedAt": "2018-07-10T01:05:08Z", | ||||
| "sentAt": "2018-07-10T11:05:08+10:00", | ||||
| "bodyStructure": { | ||||
| "type": "multipart/alternative", | ||||
| "subParts": [{ | ||||
| "partId": "49db", | ||||
| "type": "text/html" | ||||
| }, { | ||||
| "partId": "bd48", | ||||
| "type": "text/plain" | ||||
| }] | ||||
| }, | ||||
| "bodyValues": { | ||||
| "bd48": { | ||||
| "value": "I have the most brilliant plan. Let me tell you all about it. What we do is, we", | ||||
| "isTruncated": false | ||||
| }, | ||||
| "49db": { | ||||
| "value": "<!DOCTYPE html><html><head><title></title><style type=\"text/css\">div{font-size:16px}</style></head><body><div>I have the most brilliant plan. Let me tell you all about it. What we do is, we</div></body></html>", | ||||
| "isTruncated": false | ||||
| } | ||||
| } | ||||
| } | ||||
| } | ||||
| }, "0" ]] | ||||
| The server creates the message and sends the success response: | ||||
| [[ "Email/set", { | ||||
| "accountId": "ue150411c", | ||||
| "oldState": "780823", | ||||
| "newState": "780839", | ||||
| "created": { | ||||
| "k1546": { | ||||
| "id": "Md45b47b4877521042cec0938", | ||||
| "blobId": "Ge8de6c9f6de198239b982ea214e0f3a704e4af74", | ||||
| "threadId": "Td957e72e89f516dc", | ||||
| "size": 11721 | ||||
| } | ||||
| }, | ||||
| ... | ||||
| }, "0" ]] | ||||
| The client moves this draft to a different account. The only way to | ||||
| do this is via the "/copy" method. It MUST set a new mailboxIds | ||||
| property, since the current value will not be valid mailbox ids in | ||||
| the destination account: | ||||
| [[ "Email/copy", { | ||||
| "fromAccountId": "ue150411c", | ||||
| "toAccountId": "6c6c41ac", | ||||
| "create": { | ||||
| "k45": { | ||||
| "id": "Md45b47b4877521042cec0938", | ||||
| "mailboxIds": { | ||||
| "75a4c956": true | ||||
| } | ||||
| } | ||||
| }, | ||||
| "onSuccessDestroyOriginal": true | ||||
| }, "0" ]] | ||||
| The server successfully copies the email and deletes the original. | ||||
| Due to the implicit call to "Email/set", there are two responses to | ||||
| the single method call, both with the same client id: | ||||
| [[ "Email/copy", { | ||||
| "fromAccountId": "ue150411c", | ||||
| "toAccountId": "6c6c41ac", | ||||
| "created": { | ||||
| "k45": { | ||||
| "id": "M138f9954a5cd2423daeafa55", | ||||
| "blobId": "G6b9fb047cba722c48c611e79233d057c6b0b74e8", | ||||
| "threadId": "T2f242ea424a4079a", | ||||
| "size": 11721 | ||||
| } | ||||
| }, | ||||
| "notCreated": null | ||||
| }, "0" ], | ||||
| [ "Email/set", { | ||||
| "accountId": "ue150411c", | ||||
| "oldState": "780839" | ||||
| "newState": "780871", | ||||
| "destroyed": [ "Ma783e5cdf5f2deffbc97930a" ], | ||||
| ... | ||||
| }, "0" ]] | ||||
| 5. Search snippets | ||||
| When doing a search on a "String" property, the client may wish to | ||||
| show the relevant section of the body that matches the search as a | ||||
| preview instead of the beginning of the message, and to highlight any | ||||
| matching terms in both this and the subject of the email. Search | ||||
| snippets represent this data. | ||||
| A *SearchSnippet* object has the following properties: | ||||
| o *emailId*: "String" The email id the snippet applies to. | ||||
| o *subject*: "String|null" If text from the filter matches the | ||||
| subject, this is the subject of the email HTML-escaped, with | ||||
| matching words/phrases wrapped in "<mark></mark>" tags. If it | ||||
| does not match, this is "null". | ||||
| o *preview*: "String|null" If text from the filter matches the | ||||
| plain-text or HTML body, this is the relevant section of the body | ||||
| (converted to plain text if originally HTML), HTML-escaped, with | ||||
| matching words/phrases wrapped in "<mark></mark>" tags. It MUST | ||||
| NOT be bigger than 255 octets in size. If it does not match, this | ||||
| is "null". | ||||
| It is server-defined what is a relevant section of the body for | ||||
| preview. If the server is unable to determine search snippets, it | ||||
| MUST return "null" for both the _subject_ and _preview_ roperties. | ||||
| Note, unlike most data types, a SearchSnippet DOES NOT have a | ||||
| property called "id". | ||||
| The following JMAP method is supported: | ||||
| 5.1. SearchSnippet/get | ||||
| To fetch search snippets, make a call to "SearchSnippet/get". It | ||||
| takes the following arguments: | ||||
| o *accountId*: "String|null" The id of the account to use for this | ||||
| call. If "null", defaults to the "urn:ietf:params:jmap:mail" | ||||
| primary account. | ||||
| o *filter*: "FilterOperator|FilterCondition|null" The same filter as | ||||
| passed to Email/query; see the description of this method for | ||||
| details. | ||||
| o *emailIds*: "String[]" The list of ids of emails to fetch the | ||||
| snippets for. | ||||
| The response has the following arguments: | ||||
| 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 | ||||
| the requested email ids. This may not be in the same order as the | ||||
| ids that were in the request. | ||||
| o *notFound*: "String[]|null" An array of email ids requested which | ||||
| could not be found, or "null" if all ids were found. | ||||
| Since snippets are only based on immutable properties, there is no | ||||
| state string or update mechanism needed. | ||||
| The following standard errors may be returned instead of the | ||||
| _searchSnippets_ response: | ||||
| "requestTooLarge": The number of _emailIds_ requested by the client | ||||
| exceeds the maximum number the server is willing to process in a | ||||
| single method call. | ||||
| "unsupportedFilter": The server is unable to process the given | ||||
| _filter_ for any reason. | ||||
| 5.2. Example | ||||
| Here we did an Email/query to search for any email in the account | ||||
| containing the word "foo", now we are fetching the search snippets | ||||
| for some of the ids that were returned in the results: | ||||
| [[ "SearchSnippet/get", { | ||||
| "accountId": "ue150411c", | ||||
| "filter": { | ||||
| "text": "foo" | ||||
| }, | ||||
| "emailIds": [ | ||||
| "M44200ec123de277c0c1ce69c", | ||||
| "M7bcbcb0b58d7729686e83d99", | ||||
| "M28d12783a0969584b6deaac0", | ||||
| ... | ||||
| ] | ||||
| }, "tag-0" ] | ||||
| Example response: | ||||
| [[ "SearchSnippet/get", { | ||||
| "accountId": "ue150411c", | ||||
| "filter": { | ||||
| "text": "foo" | ||||
| }, | ||||
| "list": [{ | ||||
| "emailId": "M44200ec123de277c0c1ce69c" | ||||
| "subject": null, | ||||
| "preview": null | ||||
| }, { | ||||
| "emailId": "M7bcbcb0b58d7729686e83d99", | ||||
| "subject": "The <mark>Foo</mark>sball competition", | ||||
| "preview": "...year the <mark>foo</mark>sball competition will be held in the Stadium de ..." | ||||
| }, { | ||||
| "emailId": "M28d12783a0969584b6deaac0", | ||||
| "subject": null, | ||||
| "preview": "...mail <mark>Foo</mark>/changes results often return current-state-minus-1 rather than new..." | ||||
| }, | ||||
| ... | ||||
| ], | ||||
| "notFound": null | ||||
| }, "tag-0" ]] | ||||
| 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 | o *name*: "String" (default: "") The "From" _name_ the client SHOULD | |||
| SHOULD use when creating a new message from this identity. | use when creating a new message from this identity. | |||
| o *email*: "String" (immutable) The "From" email address the client | o *email*: "String" (immutable) The "From" email address the client | |||
| MUST use when creating a new message from this identity. The | MUST use when creating a new message from this identity. The | |||
| value MAY alternatively be of the form "*@example.com", in which | value MAY alternatively be of the form "*@example.com", in which | |||
| case the client may use any valid email address ending in | case the client may use any valid email address ending in | |||
| "@example.com". | "@example.com". | |||
| o *replyTo*: "EmailAddress[]|null" (default: "null") The Reply-To | o *replyTo*: "EmailAddress[]|null" (default: null) The Reply-To | |||
| value the client SHOULD set when creating a new message from this | value the client SHOULD set when creating a new message from this | |||
| identity. | identity. | |||
| o *bcc*: "EmailAddress[]|null" (default: "null") The Bcc value the | o *bcc*: "EmailAddress[]|null" (default: null) The Bcc value the | |||
| client SHOULD set when creating a new message from this identity. | client SHOULD set when creating a new message from this identity. | |||
| o *textSignature*: "String" (default: """") Signature the client | o *textSignature*: "String" (default: "") Signature the client | |||
| SHOULD insert into new plain-text messages that will be sent from | SHOULD insert into new plain-text messages that will be sent from | |||
| this identity. Clients MAY ignore this and/or combine this with a | this identity. Clients MAY ignore this and/or combine this with a | |||
| client-specific signature preference. | client-specific signature preference. | |||
| o *htmlSignature*: "String" (default: """") Signature the client | o *htmlSignature*: "String" (default: "") Signature the client | |||
| SHOULD insert into new HTML messages that will be sent from this | SHOULD insert into new HTML messages that will be sent from this | |||
| identity. This text MUST be an HTML snippet to be inserted into | identity. This text MUST be an HTML snippet to be inserted into | |||
| the "<body></body>" section of the new email. Clients MAY ignore | the "<body></body>" section of the new email. Clients MAY ignore | |||
| this and/or combine this with a client-specific signature | this and/or combine this with a client-specific signature | |||
| preference. | preference. | |||
| o *mayDelete*: "Boolean" (server-set) Is the user allowed to delete | o *mayDelete*: "Boolean" (server-set) Is the user allowed to delete | |||
| this identity? Servers may wish to set this to "false" for the | this identity? Servers may wish to set this to "false" for the | |||
| user's username or other default address. | user's username or other default address. Attempts to destroy an | |||
| identity with "mayDelete: false" will be rejected with a standard | ||||
| "forbidden" SetError. | ||||
| See the "Addresses" header form description in the Email object for | See the "Addresses" header form description in the Email object for | |||
| the definition of _EmailAddress_. | the definition of _EmailAddress_. | |||
| Multiple identities with the same email address MAY exist, to allow | Multiple identities with the same email address MAY exist, to allow | |||
| for different settings the user wants to pick between (for example | for different settings the user wants to pick between (for example | |||
| with different names/signatures). | with different names/signatures). | |||
| The following JMAP methods are supported: | The following JMAP methods are supported: | |||
| 5.1. Identity/get | 6.1. Identity/get | |||
| Standard _/get_ method. The _ids_ argument may be "null" to fetch | Standard "/get" method. The _ids_ argument may be "null" to fetch | |||
| all at once. | all at once. | |||
| 5.2. Identity/changes | 6.2. Identity/changes | |||
| Standard _/changes_ method. | Standard "/changes" method. | |||
| 5.3. Identity/set | 6.3. Identity/set | |||
| Standard _/set_ method. The following extra _SetError_ types are | Standard "/set" method. The following extra _SetError_ types are | |||
| defined: | defined: | |||
| For *create*: | For *create*: | |||
| o "maxQuotaReached": The user has reached a server-defined limit on | o "forbiddenFrom": The user is not allowed to send from the address | |||
| the number of identities. | given as the _email_ property of the identity. | |||
| o "emailNotPermitted": The user is not allowed to send from the | ||||
| address given as the _email_ property of the identity. | ||||
| For *destroy*: | ||||
| o "forbidden": Returned if the identity's _mayDelete_ value is | ||||
| "false". | ||||
| 5.4. Example | 6.4. Example | |||
| Request: | Request: | |||
| [ "Identity/get", {}, "0" ] | [ "Identity/get", {}, "0" ] | |||
| with response: | with response: | |||
| [ "Identity/get", { | [ "Identity/get", { | |||
| "accountId": "acme", | "accountId": "acme", | |||
| "state": "99401312ae-11-333", | "state": "99401312ae-11-333", | |||
| skipping to change at page 50, line 36 ¶ | skipping to change at page 61, line 36 ¶ | |||
| "replyTo": null, | "replyTo": null, | |||
| "bcc": null, | "bcc": null, | |||
| "textSignature": "", | "textSignature": "", | |||
| "htmlSignature": "", | "htmlSignature": "", | |||
| "mayDelete": true | "mayDelete": true | |||
| } | } | |||
| ], | ], | |||
| "notFound": [] | "notFound": [] | |||
| }, "0" ] | }, "0" ] | |||
| 6. Email submission | 7. Email submission | |||
| An *EmailSubmission* object represents the submission of an email for | An *EmailSubmission* object represents the submission of an email for | |||
| delivery to one or more recipients. It has the following properties: | delivery to one or more recipients. It has the following properties: | |||
| o *id*: "String" (immutable; server-set) The id of the email | o *id*: "String" (immutable; server-set) The id of the email | |||
| submission. | submission. | |||
| o *identityId*: "String" (immutable) The id of the identity to | o *identityId*: "String" (immutable) The id of the identity to | |||
| associate with this submission. | associate with this submission. | |||
| 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") | o *envelope*: "Envelope|null" (immutable; default: null) Information | |||
| Information for use when sending via SMTP. An *Envelope* object | for use when sending via SMTP. An *Envelope* object has the | |||
| has the following properties: | following 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. | |||
| skipping to change at page 55, line 13 ¶ | skipping to change at page 66, line 13 ¶ | |||
| object. | object. | |||
| For efficiency, a server MAY destroy EmailSubmission objects a | For efficiency, a server MAY destroy EmailSubmission objects a | |||
| certain amount of time after the email is successfully sent or it has | certain amount of time after the email is successfully sent or it has | |||
| finished retrying sending the email. For very basic SMTP proxies, | finished retrying sending the email. For very basic SMTP proxies, | |||
| this MAY be immediately after creation, as it has no way to assign a | this MAY be immediately after creation, as it has no way to assign a | |||
| real id and return the information again if fetched later. | real id and return the information again if fetched later. | |||
| The following JMAP methods are supported: | The following JMAP methods are supported: | |||
| 6.1. EmailSubmission/get | 7.1. EmailSubmission/get | |||
| Standard _/get_ method. | Standard "/get" method. | |||
| 6.2. EmailSubmission/changes | 7.2. EmailSubmission/changes | |||
| Standard _/changes_ method. | Standard "/changes" method. | |||
| 6.3. EmailSubmission/query | 7.3. EmailSubmission/query | |||
| Standard _/query_ method. | Standard "/query" method. | |||
| A *FilterCondition* object has the following properties, any of which | A *FilterCondition* object has the following properties, any of which | |||
| may be omitted: | may be omitted: | |||
| o *emailIds*: "String[]" The EmailSubmission _emailId_ property must | o *emailIds*: "String[]" The EmailSubmission _emailId_ property must | |||
| be in this list to match the condition. | be in this list to match the condition. | |||
| o *threadIds*: "String[]" The EmailSubmission _threadId_ property | o *threadIds*: "String[]" The EmailSubmission _threadId_ property | |||
| must be in this list to match the condition. | must be in this list to match the condition. | |||
| skipping to change at page 56, line 6 ¶ | skipping to change at page 67, line 6 ¶ | |||
| given conditions given match. If zero properties are specified, it | given conditions given match. If zero properties are specified, it | |||
| is automatically "true" for all objects. | is automatically "true" for all objects. | |||
| The following properties MUST be supported for sorting: | The following properties MUST be supported for sorting: | |||
| o "emailId" | o "emailId" | |||
| o "threadId" | o "threadId" | |||
| o "sentAt" | o "sentAt" | |||
| 6.4. EmailSubmission/queryChanges | 7.4. EmailSubmission/queryChanges | |||
| Standard _/queryChanges_ method. | Standard "/queryChanges" method. | |||
| 6.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 | |||
| skipping to change at page 57, line 9 ¶ | skipping to change at page 68, line 9 ¶ | |||
| deliveries it represents. It purely removes the record of the email | deliveries it represents. It purely removes the record of the email | |||
| submission. The server MAY automatically destroy EmailSubmission | submission. The server MAY automatically destroy EmailSubmission | |||
| objects after a certain time or in response to other triggers, and | objects after a certain time or in response to other triggers, and | |||
| MAY forbid the client from manually destroying EmailSubmission | MAY forbid the client from manually destroying EmailSubmission | |||
| objects. | objects. | |||
| The following extra _SetError_ types are defined: | The following extra _SetError_ types are defined: | |||
| For *create*: | For *create*: | |||
| o "tooLarge" - The email size is larger than the server supports | o "emailNotFound" - The _emailId_ is not a valid id for an email in | |||
| sending. A _maxSize_ "Number" property MUST be present on the | the account. | |||
| SetError specifying the maximum size of an email that may be sent, | ||||
| in octets. | o "emailTooLarge" - The email size is larger than the server | |||
| supports sending. A _maxSize_ "PositiveInt" property MUST be | ||||
| present on the SetError specifying the maximum size of an email | ||||
| that may be sent, in octets. | ||||
| o "invalidEmail" - The email to be sent is invalid in some way. The | ||||
| SetError SHOULD contain a property called _properties_ of type | ||||
| "String[]" that lists *all* the properties of the email that were | ||||
| invalid. | ||||
| o "tooManyRecipients" - The envelope (supplied or generated) has | o "tooManyRecipients" - The envelope (supplied or generated) has | |||
| more recipients than the server allows. A _maxRecipients_ | more recipients than the server allows. A _maxRecipients_ | |||
| "Number" property MUST be present on the SetError specifying the | "PositiveInt" property MUST also be present on the SetError | |||
| maximum number of allowed recipients. | specifying the maximum number of allowed recipients. | |||
| o "noRecipients" - The envelope (supplied or generated) does not | o "noRecipients" - The envelope (supplied or generated) does not | |||
| have any rcptTo emails. | have any rcptTo emails. | |||
| o "invalidRecipients" - The _rcptTo_ property of the envelope | o "invalidRecipients" - The _rcptTo_ property of the envelope | |||
| (supplied or generated) contains at least one rcptTo value which | (supplied or generated) contains at least one rcptTo value which | |||
| is not a valid email for sending to. An _invalidRecipients_ | is not a valid email for sending to. An _invalidRecipients_ | |||
| "String[]" property MUST be present on the SetError, which is a | "String[]" property MUST also be present on the SetError, which is | |||
| list of the invalid addresses. | a list of the invalid addresses. | |||
| o "forbiddenMailFrom" - The server does not permit the user to send | o "forbiddenMailFrom" - The server does not permit the user to send | |||
| an email with the [RFC5321] envelope From. | an email with the [RFC5321] envelope From. | |||
| o "forbiddenFrom" - The server does not permit the user to send an | o "forbiddenFrom" - The server does not permit the user to send an | |||
| email with the [RFC5322] From header of the email to be sent. | email with the [RFC5322] From header of the email to be sent. | |||
| o "forbiddenToSend" - The user does not have permission to send at | o "forbiddenToSend" - The user does not have permission to send at | |||
| all right now for some reason. A _description_ "String" property | all right now for some reason. A _description_ "String" property | |||
| MAY be present on the SetError object to display to the user why | MAY be present on the SetError object to display to the user why | |||
| they are not permitted. The server MAY choose to localise this | they are not permitted. The server MAY choose to localise this | |||
| string into the user's preferred language, if known. | string into the user's preferred language, if known. | |||
| o "emailNotFound" - The _emailId_ is not a valid id for an email in | ||||
| the account. | ||||
| o "invalidEmail" - The email to be sent is invalid in some way. The | ||||
| SetError SHOULD contain a property called _properties_ of type | ||||
| "String[]" that lists *all* the properties of the email that were | ||||
| invalid. | ||||
| For *update*: | For *update*: | |||
| o "cannotUnsend": The client attempted to update the _undoStatus_ of | o "cannotUnsend": The client attempted to update the _undoStatus_ of | |||
| a valid EmailSubmission object from "pending" to "canceled", but | a valid EmailSubmission object from "pending" to "canceled", but | |||
| the email cannot be unsent. | the email cannot be unsent. | |||
| 6.5.1. Example | 7.5.1. Example | |||
| The following example presumes a draft of the message to be sent has | The following example presumes a draft of the message to be sent has | |||
| already been saved, and its Email id is "M7f6ed5bcfd7e2604d1753f6c". | already been saved, and its Email id is "M7f6ed5bcfd7e2604d1753f6c". | |||
| This call then sends the email immediately, and if successful removes | This call then sends the email immediately, and if successful removes | |||
| the draft flag and moves it from the Drafts folder (which has Mailbox | the draft flag and moves it from the Drafts folder (which has Mailbox | |||
| id "7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e") to the Sent folder (which | id "7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e") to the Sent folder (which | |||
| we presume has Mailbox id "73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6"). | we presume has Mailbox id "73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6"). | |||
| [ | [[ "EmailSubmission/set", { | |||
| "EmailSubmission/set", | "accountId": "ue411d190", | |||
| { | "create": { | |||
| "accountId": "ue411d190", | "k1490": { | |||
| "create": { | "identityId": "64588216", | |||
| "k1490": { | "emailId": "M7f6ed5bcfd7e2604d1753f6c", | |||
| "identityId": "64588216", | "envelope": { | |||
| "emailId": "M7f6ed5bcfd7e2604d1753f6c", | "mailFrom": { | |||
| "envelope": { | "email": "john@example.com", | |||
| "mailFrom": { | "parameters": null | |||
| "email": "john@example.com", | }, | |||
| "parameters": null | "rcptTo": [{ | |||
| }, | "email": "jane@example.com", | |||
| "rcptTo": [ | "parameters": null | |||
| { | }, | |||
| "email": "jane@example.com", | ... | |||
| "parameters": null | ] | |||
| } | } | |||
| ] | } | |||
| } | }, | |||
| } | "onSuccessUpdateEmail": { | |||
| }, | "#k1490": { | |||
| "onSuccessUpdateEmail": { | "mailboxIds/7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e": null, | |||
| "#k1490": { | "mailboxIds/73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6": true, | |||
| "mailboxIds/7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e": null, | "keywords/$draft": null | |||
| "mailboxIds/73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6": true, | } | |||
| "keywords/$draft": null | } | |||
| } | }, "0" ]] | |||
| } | ||||
| }, | ||||
| "0" | ||||
| ] | ||||
| A successful response might look like this. Note there are two | A successful response might look like this. Note there are two | |||
| responses due to the implicit Email/set call, but both have the same | responses due to the implicit Email/set call, but both have the same | |||
| tag as they are due to the same call in the request: | tag as they are due to the same call in the request: | |||
| [ | [[ "EmailSubmission/set", { | |||
| "EmailSubmission/set", | "accountId": "ue411d190", | |||
| { | "oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21", | |||
| "accountId": "ue411d190", | "newState": "355421f6-8aed-4cf4-a0c4-7377e951af36", | |||
| "oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21, | "created": { | |||
| "newState": "355421f6-8aed-4cf4-a0c4-7377e951af36", | "k1490": { | |||
| "created": { | "id": "3bab7f9a-623e-4acf-99a5-2e67facb02a0" | |||
| "k1490": { | } | |||
| "id": "3bab7f9a-623e-4acf-99a5-2e67facb02a0" | }, | |||
| } | "notCreated": null, | |||
| }, | "updated": null, | |||
| "notCreated": null, | "notUpdated": null, | |||
| "updated": null, | "destroyed": null, | |||
| "notUpdated": null, | "notDestroyed": null | |||
| "destroyed": null, | }, "0" ], | |||
| "notDestroyed": null | [ "Email/set", { | |||
| }, | "accountId": "neilj@fastmail.fm", | |||
| "0" | "oldState": "778193", | |||
| ], | "newState": "778197", | |||
| [ | "created": null, | |||
| "Email/set", | "notCreated": null, | |||
| { | "updated": { | |||
| "accountId": "neilj@fastmail.fm", | "M7f6ed5bcfd7e2604d1753f6c": null | |||
| "oldState": "778193", | }, | |||
| "newState": "778197", | "notUpdated": null, | |||
| "created": null, | "destroyed": null, | |||
| "notCreated": null, | "notDestroyed": null | |||
| "updated": { | }, "0" ]] | |||
| "M7f6ed5bcfd7e2604d1753f6c": null | ||||
| }, | ||||
| "notUpdated": null, | ||||
| "destroyed": null, | ||||
| "notDestroyed": null | ||||
| }, | ||||
| "0" | ||||
| ] | ||||
| 7. Search snippets | ||||
| When doing a search on a "String" property, the client may wish to | ||||
| show the relevant section of the body that matches the search as a | ||||
| preview instead of the beginning of the message, and to highlight any | ||||
| matching terms in both this and the subject of the email. Search | ||||
| snippets represent this data. | ||||
| A *SearchSnippet* object has the following properties: | ||||
| o *emailId*: "String" The email id the snippet applies to. | ||||
| o *subject*: "String|null" If text from the filter matches the | ||||
| subject, this is the subject of the email HTML-escaped, with | ||||
| matching words/phrases wrapped in "<mark></mark>" tags. If it | ||||
| does not match, this is "null". | ||||
| o *preview*: "String|null" If text from the filter matches the | ||||
| plain-text or HTML body, this is the relevant section of the body | ||||
| (converted to plain text if originally HTML), HTML-escaped, with | ||||
| matching words/phrases wrapped in "<mark></mark>" tags. It MUST | ||||
| NOT be bigger than 255 octets in size. If it does not match, this | ||||
| is "null". | ||||
| o *attachments*: "String|null" If text from the filter matches the | ||||
| text extracted from an attachment, this is the relevant section of | ||||
| the attachment (converted to plain text), with matching words/ | ||||
| phrases wrapped in "<mark></mark>" tags. It MUST NOT be bigger | ||||
| than 255 octets in size. If it does not match, this is "null". | ||||
| It is server-defined what is a relevant section of the body for | ||||
| preview. If the server is unable to determine search snippets, it | ||||
| MUST return "null" for both the _subject_, _preview_ and | ||||
| _attachments_ properties. | ||||
| Note, unlike most data types, a SearchSnippet DOES NOT have a | ||||
| property called "id". | ||||
| The following JMAP method is supported: | ||||
| 7.1. SearchSnippet/get | ||||
| To fetch search snippets, make a call to "SearchSnippet/get". It | ||||
| takes the following arguments: | ||||
| o *accountId*: "String|null" The id of the account to use for this | ||||
| call. If "null", defaults to the "urn:ietf:params:jmap:mail" | ||||
| primary account. | ||||
| o *filter*: "FilterOperator|FilterCondition|null" The same filter as | ||||
| passed to Email/query; see the description of this method for | ||||
| details. | ||||
| o *emailIds*: "String[]" The list of ids of emails to fetch the | ||||
| snippets for. | ||||
| The response has the following arguments: | ||||
| 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 | ||||
| the requested email ids. This may not be in the same order as the | ||||
| ids that were in the request. | ||||
| o *notFound*: "String[]|null" An array of email ids requested which | ||||
| could not be found, or "null" if all ids were found. | ||||
| Since snippets are only based on immutable properties, there is no | ||||
| state string or update mechanism needed. | ||||
| The following additional errors may be returned instead of the | ||||
| _searchSnippets_ response: | ||||
| "requestTooLarge": Returned if the number of _emailIds_ requested by | ||||
| the client exceeds the maximum number the server is willing to | ||||
| process in a single method call. | ||||
| "unsupportedFilter": Returned if the server is unable to process the | ||||
| given _filter_ for any reason. | ||||
| 7.2. Example | ||||
| Here we did an Email/query to search for any email in the account | ||||
| containing the word "foo", now we are fetching the search snippets | ||||
| for some of the ids that were returned in the results: | ||||
| [ | ||||
| "SearchSnippet/get", | ||||
| { | ||||
| "accountId": "ue150411c", | ||||
| "filter": { | ||||
| "text": "foo" | ||||
| }, | ||||
| "emailIds": [ | ||||
| "M44200ec123de277c0c1ce69c", | ||||
| "M7bcbcb0b58d7729686e83d99", | ||||
| "M28d12783a0969584b6deaac0", | ||||
| ... | ||||
| ] | ||||
| }, | ||||
| "tag-0" | ||||
| ] | ||||
| Example response: | If the email submission was not accepted on the other hand, the | |||
| response may look like this: | ||||
| [ | [[ "EmailSubmission/set", { | |||
| "SearchSnippet/get", { | "accountId": "ue411d190", | |||
| "accountId": "ue150411c", | "oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21", | |||
| "filter": { | "newState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21", | |||
| "text": "foo" | "created": null, | |||
| }, | "notCreated": { | |||
| "list": [{ | "k1490": { | |||
| "emailId": "M44200ec123de277c0c1ce69c" | "type": "tooManyRecipients", | |||
| "subject": null, | "maxRecipients": 10 | |||
| "preview": null | } | |||
| }, { | }, | |||
| "emailId": "M7bcbcb0b58d7729686e83d99", | "updated": null, | |||
| "subject": "The <mark>Foo</mark>sball competition", | "notUpdated": null, | |||
| "preview": "...year the <mark>foo</mark>sball competition will be held in the Stadium de ..." | "destroyed": null, | |||
| }, { | "notDestroyed": null | |||
| "emailId": "M28d12783a0969584b6deaac0", | }, "0" ]] | |||
| "subject": null, | ||||
| "preview": "...mail <mark>Foo</mark>/changes results often return current-state-minus-1 rather than new..." | ||||
| }, | ||||
| ... | ||||
| ], | ||||
| "notFound": null | ||||
| }, | ||||
| "0" | ||||
| ] | ||||
| 8. Vacation response | 8. Vacation response | |||
| A vacation response automatically sends a reply to messages sent to a | ||||
| particular account, to inform the original sender that their message | ||||
| may not be processed for some time. Automated message sending can | ||||
| produce undesireable behaviour. To avoid this, implementors MUST | ||||
| follow the 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) The id of the object. There is only | |||
| ever one vacation response object, and its id is ""singleton"". | ever one vacation response object, and its id is ""singleton"". | |||
| o *isEnabled* "Boolean" Should a vacation response be sent if an | o *isEnabled*: "Boolean" Should a vacation response be sent if an | |||
| 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", the date/time | |||
| in UTC after which emails that arrive should receive the user's | in UTC after which emails that arrive 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", the date/time | |||
| in UTC after which emails that arrive should no longer receive the | in UTC after which emails that arrive should no longer receive the | |||
| user's vacation response. If "null", the vacation response is | user's vacation response. If "null", the vacation response is | |||
| skipping to change at page 63, line 26 ¶ | skipping to change at page 72, line 7 ¶ | |||
| o *htmlBody*: "String|null" The HTML message to send in response to | o *htmlBody*: "String|null" The HTML message 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. | |||
| There MUST only be exactly one VacationResponse object in an account. | There MUST only be exactly one VacationResponse object in an account. | |||
| It MUST have the id ""singleton"". | It MUST have the id "singleton". | |||
| 8.2. VacationResponse/set | 8.2. VacationResponse/set | |||
| Standard _/set_ method. | Standard "/set" method. | |||
| 9. Security considerations | 9. Security considerations | |||
| All security considerations of JMAP {TODO: insert RFC ref} apply to | All security considerations of JMAP {TODO: insert RFC ref} apply to | |||
| this specification. | this specification. | |||
| 9.1. EmailBodyPart value | 9.1. EmailBodyPart value | |||
| Service providers typically perform security filtering on incoming | Service providers typically perform security filtering on incoming | |||
| email and it's important the detection of content-type and charset | email and it's important the detection of content-type and charset | |||
| skipping to change at page 66, line 37 ¶ | skipping to change at page 75, line 17 ¶ | |||
| 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 can be used to do | server. The de-facto XCLIENT extension to SMTP | |||
| (<http://www.postfix.org/XCLIENT_README.html>) can be used to do | ||||
| this, but use of an authenticated channel is recommended to limit use | this, but use of an authenticated channel is recommended to limit use | |||
| of that extension to explicitly authorized proxies. JMAP servers | of that extension to explicitly authorized proxies. | |||
| that proxy to an SMTP Submission server SHOULD allow use of the | ||||
| _submissions_ port [RFC8314] and SHOULD implement SASL PLAIN over TLS | JMAP servers that proxy to an SMTP Submission server SHOULD allow use | |||
| [RFC4616] and/or TLS client certificate authentication with SASL | of the _submissions_ port [RFC8314] and SHOULD implement SASL PLAIN | |||
| EXTERNAL [RFC4422] appendix A. Implementation of a mechanism similar | over TLS [RFC4616] and/or TLS client certificate authentication with | |||
| to SMTP XCLIENT is strongly encouraged. | SASL EXTERNAL [RFC4422] appendix A. Implementation of a mechanism | |||
| 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 | |||
| products that address security issues including anti-virus/anti-spam, | products that address security issues including anti-virus/anti-spam, | |||
| reputation protection, compliance archiving, and data loss | reputation protection, compliance archiving, and data loss | |||
| prevention. Proxying to a local SMTP Submission server may be a | prevention. Proxying to a local SMTP Submission server may be a | |||
| simpler way to provide such security services. | simpler way to provide such security services. | |||
| 10. IANA Considerations | 10. IANA considerations | |||
| 10.1. JMAP Capability Registration for "mail" | 10.1. JMAP capability registration for "mail" | |||
| IANA will register the "mail" JMAP Capability as follows: | IANA will register the "mail" JMAP Capability as follows: | |||
| Capability Name: "urn:ietf:params:jmap:mail" | Capability Name: "urn:ietf:params:jmap:mail" | |||
| Specification document: this document | Specification document: this document | |||
| Intended use: common | Intended use: common | |||
| Change Controller: IETF | Change Controller: IETF | |||
| Security and privacy considerations: this document, section 9 | Security and privacy considerations: this document, section 9 | |||
| 10.2. IMAP and JMAP Keywords Registry | 10.2. JMAP capability registration for "submission" | |||
| IANA will register the "submission" JMAP Capability as follows: | ||||
| Capability Name: "urn:ietf:params:jmap:submission" | ||||
| Specification document: this document | ||||
| Intended use: common | ||||
| Change Controller: IETF | ||||
| Security and privacy considerations: this document, section 9 | ||||
| 10.3. JMAP capability registration for "vacationresponse" | ||||
| IANA will register the "vacationresponse" JMAP Capability as follows: | ||||
| Capability Name: "urn:ietf:params:jmap:vacationresponse" | ||||
| Specification document: this document | ||||
| Intended use: common | ||||
| Change Controller: IETF | ||||
| Security and privacy considerations: this document, section 9 | ||||
| 10.4. IMAP and JMAP keywords registry | ||||
| This document makes two changes to the IMAP keywords registry as | This document makes two changes to the IMAP keywords registry as | |||
| defined in [RFC5788]. | defined in [RFC5788]. | |||
| 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 | |||
| skipping to change at page 68, line 5 ¶ | skipping to change at page 77, line 12 ¶ | |||
| 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. | |||
| 10.2.1. Registration of JMAP keyword '$draft' | 10.4.1. Registration of JMAP keyword '$draft' | |||
| This registers the JMAP-only keyword '$draft' in the "IMAP and JMAP | This registers the JMAP-only keyword '$draft' in the "IMAP and JMAP | |||
| keywords Registry". | keywords Registry". | |||
| Keyword name: "$draft" | Keyword name: "$draft" | |||
| Scope: JMAP-only | Scope: JMAP-only | |||
| Purpose (description): This is set when the user wants to treat the | Purpose (description): This is set when the user wants to treat the | |||
| message as a draft the user is composing. This is the JMAP | message as a draft the user is composing. This is the JMAP | |||
| skipping to change at page 68, line 45 ¶ | skipping to change at page 78, line 4 ¶ | |||
| Related keywords: None | Related keywords: None | |||
| Related IMAP/JMAP Capabilities: SPECIAL-USE [RFC6154] | Related IMAP/JMAP Capabilities: SPECIAL-USE [RFC6154] | |||
| Security Considerations: A server implementing this keyword as a | Security Considerations: A server implementing this keyword as a | |||
| shared keyword may disclose that a user considers the message a draft | shared keyword may disclose that a user considers the message a draft | |||
| message. This information would be exposed to other users with read | message. This information would be exposed to other users with read | |||
| permission for the mailbox keywords. | permission for the mailbox keywords. | |||
| Published specification (recommended): this document | Published specification (recommended): this document | |||
| Person & email address to contact for further information: (editor- | Person & email address to contact for further information: (editor- | |||
| contact-goes-here) | contact-goes-here) | |||
| Intended usage: COMMON | Intended usage: COMMON | |||
| Owner/Change controller: IESG | Owner/Change controller: IESG | |||
| 10.2.2. Registration of JMAP keyword '$seen' | 10.4.2. Registration of JMAP keyword '$seen' | |||
| This registers the JMAP-only keyword '$seen' in the "IMAP and JMAP | This registers the JMAP-only keyword '$seen' in the "IMAP and JMAP | |||
| keywords Registry". | keywords Registry". | |||
| Keyword name: "$seen" | Keyword name: "$seen" | |||
| Scope: JMAP-only | Scope: JMAP-only | |||
| Purpose (description): This is set when the user wants to treat the | Purpose (description): This is set when the user wants to treat the | |||
| message as read. This is the JMAP equivalent of the IMAP \Seen flag. | message as read. This is the JMAP equivalent of the IMAP \Seen flag. | |||
| skipping to change at page 69, line 47 ¶ | skipping to change at page 79, line 5 ¶ | |||
| Published specification (recommended): this document | Published specification (recommended): this document | |||
| Person & email address to contact for further information: (editor- | Person & email address to contact for further information: (editor- | |||
| contact-goes-here) | contact-goes-here) | |||
| Intended usage: COMMON | Intended usage: COMMON | |||
| Owner/Change controller: IESG | Owner/Change controller: IESG | |||
| 10.2.3. Registration of JMAP keyword '$flagged' | 10.4.3. Registration of JMAP keyword '$flagged' | |||
| This registers the JMAP-only keyword '$flagged' in the "IMAP and JMAP | This registers the JMAP-only keyword '$flagged' in the "IMAP and JMAP | |||
| keywords Registry". | keywords Registry". | |||
| Keyword name: "$flagged" | Keyword name: "$flagged" | |||
| Scope: JMAP-only | Scope: JMAP-only | |||
| Purpose (description): This is set when the user wants to treat the | Purpose (description): This is set when the user wants to treat the | |||
| message as flagged for urgent/special attention. This is the JMAP | message as flagged for urgent/special attention. This is the JMAP | |||
| equivalent of the IMAP \Flagged flag. | equivalent of the IMAP \Flagged flag. | |||
| Private or Shared on a server: BOTH | Private or Shared on a server: BOTH | |||
| Is it an advisory keyword or may it cause an automatic action: | Is it an advisory keyword or may it cause an automatic action: | |||
| Automatic. If the account has a mailbox marked with the \Flagged | Automatic. If the account has a mailbox marked with the \Flagged | |||
| skipping to change at page 70, line 40 ¶ | skipping to change at page 79, line 48 ¶ | |||
| Published specification (recommended): this document | Published specification (recommended): this document | |||
| Person & email address to contact for further information: (editor- | Person & email address to contact for further information: (editor- | |||
| contact-goes-here) | contact-goes-here) | |||
| Intended usage: COMMON | Intended usage: COMMON | |||
| Owner/Change controller: IESG | Owner/Change controller: IESG | |||
| 10.2.4. Registration of JMAP keyword '$answered' | 10.4.4. Registration of JMAP keyword '$answered' | |||
| This registers the JMAP-only keyword '$answered' in the "IMAP and | This registers the JMAP-only keyword '$answered' in the "IMAP and | |||
| JMAP keywords Registry". | JMAP keywords Registry". | |||
| Keyword name: "$answered" | Keyword name: "$answered" | |||
| Scope: JMAP-only | Scope: JMAP-only | |||
| Purpose (description): This is set when the message has been | Purpose (description): This is set when the message has been | |||
| answered. | answered. | |||
| skipping to change at page 71, line 34 ¶ | skipping to change at page 80, line 41 ¶ | |||
| Published specification (recommended): this document | Published specification (recommended): this document | |||
| Person & email address to contact for further information: (editor- | Person & email address to contact for further information: (editor- | |||
| contact-goes-here) | contact-goes-here) | |||
| Intended usage: COMMON | Intended usage: COMMON | |||
| Owner/Change controller: IESG | Owner/Change controller: IESG | |||
| 10.2.5. Registration of '$recent' Keyword | 10.4.5. Registration of '$recent' keyword | |||
| This registers the keyword '$recent' in the "IMAP and JMAP keywords | This registers the keyword '$recent' in the "IMAP and JMAP keywords | |||
| Registry". | Registry". | |||
| Keyword name: "$recent" | Keyword name: "$recent" | |||
| Scope: reserved | Scope: reserved | |||
| Purpose (description): This keyword is not used to avoid confusion | Purpose (description): This keyword is not used to avoid confusion | |||
| with the IMAP \Recent system flag. | with the IMAP \Recent system flag. | |||
| Published specification (recommended): this document | Published specification (recommended): this document | |||
| Person & email address to contact for further information: (editor- | Person & email address to contact for further information: (editor- | |||
| contact-goes-here) | contact-goes-here) | |||
| Owner/Change controller: IESG | Owner/Change controller: IESG | |||
| 10.5. JMAP Error Codes registry | ||||
| The following sub-sections register several new error codes in the | ||||
| JMAP Error Codes registry, as defined in RFC XXXX. | ||||
| 10.5.1. mailboxHasChild | ||||
| JMAP Error Code: mailboxHasChild | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 2.5 | ||||
| 10.5.2. mailboxHasEmail | ||||
| JMAP Error Code: mailboxHasEmail | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 2.5 | ||||
| 10.5.3. blobNotFound | ||||
| JMAP Error Code: blobNotFound | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 4.6 | ||||
| 10.5.4. tooManyKeywords | ||||
| JMAP Error Code: tooManyKeywords | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 4.6 | ||||
| 10.5.5. tooManyMailboxes | ||||
| JMAP Error Code: tooManyMailboxes | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 4.6 | ||||
| 10.5.6. emailNotFound | ||||
| JMAP Error Code: emailNotFound | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 7.5 | ||||
| 10.5.7. emailTooLarge | ||||
| JMAP Error Code: emailTooLarge | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 7.5 | ||||
| 10.5.8. invalidEmail | ||||
| JMAP Error Code: invalidEmail | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 7.5 | ||||
| 10.5.9. tooManyRecipients | ||||
| JMAP Error Code: tooManyRecipients | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 7.5 | ||||
| 10.5.10. noRecipients | ||||
| JMAP Error Code: noRecipients | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 7.5 | ||||
| 10.5.11. invalidRecipients | ||||
| JMAP Error Code: invalidRecipients | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 7.5 | ||||
| 10.5.12. forbiddenMailFrom | ||||
| JMAP Error Code: forbiddenMailFrom | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 7.5 | ||||
| 10.5.13. forbiddenFrom | ||||
| JMAP Error Code: forbiddenFrom | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, sections 6.3 and 7.5 | ||||
| 10.5.14. forbiddenToSend | ||||
| JMAP Error Code: forbiddenToSend | ||||
| Intended use: common | ||||
| Change controller: IETF | ||||
| Reference: This document, section 7.5 | ||||
| 11. References | 11. References | |||
| 11.1. Normative References | 11.1. Normative References | |||
| [RFC1870] Klensin, J., Freed, N., and K. Moore, "SMTP Service | [RFC1870] Klensin, J., Freed, N., and K. Moore, "SMTP Service | |||
| Extension for Message Size Declaration", STD 10, RFC 1870, | Extension for Message Size Declaration", STD 10, RFC 1870, | |||
| DOI 10.17487/RFC1870, November 1995, | DOI 10.17487/RFC1870, November 1995, | |||
| <https://www.rfc-editor.org/info/rfc1870>. | <https://www.rfc-editor.org/info/rfc1870>. | |||
| [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail | [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail | |||
| skipping to change at page 73, line 5 ¶ | skipping to change at page 85, line 18 ¶ | |||
| <https://www.rfc-editor.org/info/rfc2557>. | <https://www.rfc-editor.org/info/rfc2557>. | |||
| [RFC2852] Newman, D., "Deliver By SMTP Service Extension", RFC 2852, | [RFC2852] Newman, D., "Deliver By SMTP Service Extension", RFC 2852, | |||
| DOI 10.17487/RFC2852, June 2000, | DOI 10.17487/RFC2852, June 2000, | |||
| <https://www.rfc-editor.org/info/rfc2852>. | <https://www.rfc-editor.org/info/rfc2852>. | |||
| [RFC3282] Alvestrand, H., "Content Language Headers", RFC 3282, | [RFC3282] Alvestrand, H., "Content Language Headers", RFC 3282, | |||
| DOI 10.17487/RFC3282, May 2002, | DOI 10.17487/RFC3282, May 2002, | |||
| <https://www.rfc-editor.org/info/rfc3282>. | <https://www.rfc-editor.org/info/rfc3282>. | |||
| [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: | ||||
| Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002, | ||||
| <https://www.rfc-editor.org/info/rfc3339>. | ||||
| [RFC3461] Moore, K., "Simple Mail Transfer Protocol (SMTP) Service | [RFC3461] Moore, K., "Simple Mail Transfer Protocol (SMTP) Service | |||
| Extension for Delivery Status Notifications (DSNs)", | Extension for Delivery Status Notifications (DSNs)", | |||
| RFC 3461, DOI 10.17487/RFC3461, January 2003, | RFC 3461, DOI 10.17487/RFC3461, January 2003, | |||
| <https://www.rfc-editor.org/info/rfc3461>. | <https://www.rfc-editor.org/info/rfc3461>. | |||
| [RFC3463] Vaudreuil, G., "Enhanced Mail System Status Codes", | [RFC3463] Vaudreuil, G., "Enhanced Mail System Status Codes", | |||
| RFC 3463, DOI 10.17487/RFC3463, January 2003, | RFC 3463, DOI 10.17487/RFC3463, January 2003, | |||
| <https://www.rfc-editor.org/info/rfc3463>. | <https://www.rfc-editor.org/info/rfc3463>. | |||
| [RFC3464] Moore, K. and G. Vaudreuil, "An Extensible Message Format | [RFC3464] Moore, K. and G. Vaudreuil, "An Extensible Message Format | |||
| for Delivery Status Notifications", RFC 3464, | for Delivery Status Notifications", RFC 3464, | |||
| DOI 10.17487/RFC3464, January 2003, | DOI 10.17487/RFC3464, January 2003, | |||
| <https://www.rfc-editor.org/info/rfc3464>. | <https://www.rfc-editor.org/info/rfc3464>. | |||
| [RFC3798] Hansen, T., Ed. and G. Vaudreuil, Ed., "Message | [RFC3798] Hansen, T., Ed. and G. Vaudreuil, Ed., "Message | |||
| Disposition Notification", RFC 3798, DOI 10.17487/RFC3798, | Disposition Notification", RFC 3798, DOI 10.17487/RFC3798, | |||
| May 2004, <https://www.rfc-editor.org/info/rfc3798>. | May 2004, <https://www.rfc-editor.org/info/rfc3798>. | |||
| [RFC3834] Moore, K., "Recommendations for Automatic Responses to | ||||
| Electronic Mail", RFC 3834, DOI 10.17487/RFC3834, August | ||||
| 2004, <https://www.rfc-editor.org/info/rfc3834>. | ||||
| [RFC4422] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple | [RFC4422] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple | |||
| Authentication and Security Layer (SASL)", RFC 4422, | Authentication and Security Layer (SASL)", RFC 4422, | |||
| DOI 10.17487/RFC4422, June 2006, | DOI 10.17487/RFC4422, June 2006, | |||
| <https://www.rfc-editor.org/info/rfc4422>. | <https://www.rfc-editor.org/info/rfc4422>. | |||
| [RFC4616] Zeilenga, K., Ed., "The PLAIN Simple Authentication and | [RFC4616] Zeilenga, K., Ed., "The PLAIN Simple Authentication and | |||
| Security Layer (SASL) Mechanism", RFC 4616, | Security Layer (SASL) Mechanism", RFC 4616, | |||
| DOI 10.17487/RFC4616, August 2006, | DOI 10.17487/RFC4616, August 2006, | |||
| <https://www.rfc-editor.org/info/rfc4616>. | <https://www.rfc-editor.org/info/rfc4616>. | |||
| skipping to change at page 75, line 16 ¶ | skipping to change at page 87, line 26 ¶ | |||
| [1] TODO | [1] TODO | |||
| [2] https://www.iana.org/assignments/imap-keywords/imap- | [2] https://www.iana.org/assignments/imap-keywords/imap- | |||
| keywords.xhtml | keywords.xhtml | |||
| [3] https://www.w3.org/TR/CSP3/ | [3] https://www.w3.org/TR/CSP3/ | |||
| [4] https://www.w3.org/TR/referrer-policy/ | [4] https://www.w3.org/TR/referrer-policy/ | |||
| Author's Address | Authors' Addresses | |||
| Neil Jenkins | Neil Jenkins | |||
| FastMail | FastMail | |||
| PO Box 234, Collins St West | PO Box 234, Collins St West | |||
| Melbourne VIC 8007 | Melbourne VIC 8007 | |||
| Australia | Australia | |||
| Email: neilj@fastmailteam.com | Email: neilj@fastmailteam.com | |||
| URI: https://www.fastmail.com | URI: https://www.fastmail.com | |||
| Chris Newman | ||||
| Oracle | ||||
| 440 E. Huntington Dr., Suite 400 | ||||
| Arcadia CA 91006 | ||||
| United States of America | ||||
| Email: chris.newman@oracle.com | ||||
| End of changes. 206 change blocks. | ||||
| 903 lines changed or deleted | 1449 lines changed or added | |||
This html diff was produced by rfcdiff 1.48. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ | ||||