idnits 2.17.1 draft-ietf-jmap-mail-07.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- ** There are 14 instances of too long lines in the document, the longest one being 161 characters in excess of 72. == There are 2 instances of lines with non-RFC2606-compliant FQDNs in the document. -- The draft header indicates that this document updates RFC5788, but the abstract doesn't seem to mention this, which it should. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year (Using the creation date from RFC5788, updated by this document, for RFC5378 checks: 2002-06-21) -- The document seems to lack a disclaimer for pre-RFC5378 work, but may have content which was first submitted before 10 November 2008. If you have contacted all the original authors and they are all willing to grant the BCP78 rights to the IETF Trust, then this is fine, and you can ignore this comment. If not, you may need to add the pre-RFC5378 disclaimer. (See the Legal Provisions document at https://trustee.ietf.org/license-info for more information.) -- The document date (August 7, 2018) is 2061 days in the past. Is this intentional? -- Found something which looks like a code comment -- if you have code sections in the document, please surround them with '' and '' lines. Checking references for intended status: Proposed Standard ---------------------------------------------------------------------------- (See RFCs 3967 and 4897 for information about using normative references to lower-maturity documents in RFCs) -- Looks like a reference, but probably isn't: '1' on line 4001 -- Looks like a reference, but probably isn't: '2' on line 4003 -- Looks like a reference, but probably isn't: '3' on line 4006 -- Looks like a reference, but probably isn't: '4' on line 4008 ** Obsolete normative reference: RFC 3798 (Obsoleted by RFC 8098) Summary: 2 errors (**), 0 flaws (~~), 2 warnings (==), 8 comments (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 JMAP N. Jenkins 3 Internet-Draft FastMail 4 Updates: 5788 (if approved) C. Newman 5 Intended status: Standards Track Oracle 6 Expires: February 8, 2019 August 7, 2018 8 JMAP for Mail 9 draft-ietf-jmap-mail-07 11 Abstract 13 This document specifies a data model for synchronising email data 14 with a server using JMAP. 16 Status of This Memo 18 This Internet-Draft is submitted in full conformance with the 19 provisions of BCP 78 and BCP 79. 21 Internet-Drafts are working documents of the Internet Engineering 22 Task Force (IETF). Note that other groups may also distribute 23 working documents as Internet-Drafts. The list of current Internet- 24 Drafts is at https://datatracker.ietf.org/drafts/current/. 26 Internet-Drafts are draft documents valid for a maximum of six months 27 and may be updated, replaced, or obsoleted by other documents at any 28 time. It is inappropriate to use Internet-Drafts as reference 29 material or to cite them other than as "work in progress." 31 This Internet-Draft will expire on February 8, 2019. 33 Copyright Notice 35 Copyright (c) 2018 IETF Trust and the persons identified as the 36 document authors. All rights reserved. 38 This document is subject to BCP 78 and the IETF Trust's Legal 39 Provisions Relating to IETF Documents 40 (https://trustee.ietf.org/license-info) in effect on the date of 41 publication of this document. Please review these documents 42 carefully, as they describe your rights and restrictions with respect 43 to this document. Code Components extracted from this document must 44 include Simplified BSD License text as described in Section 4.e of 45 the Trust Legal Provisions and are provided without warranty as 46 described in the Simplified BSD License. 48 Table of Contents 50 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4 51 1.1. Notational conventions . . . . . . . . . . . . . . . . . 4 52 1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4 53 1.3. Additions to the capabilities object . . . . . . . . . . 4 54 1.3.1. urn:ietf:params:jmap:mail . . . . . . . . . . . . . . 5 55 1.3.2. urn:ietf:params:jmap:submission . . . . . . . . . . . 5 56 1.3.3. urn:ietf:params:jmap:vacationresponse . . . . . . . . 6 57 1.4. Data type support in different accounts . . . . . . . . . 6 58 1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 7 59 2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 7 60 2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 10 61 2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 10 62 2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 11 63 2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 11 64 2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 12 65 2.6. Example . . . . . . . . . . . . . . . . . . . . . . . . . 12 66 3. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 67 3.1. Thread/get . . . . . . . . . . . . . . . . . . . . . . . 17 68 3.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 17 69 3.2. Thread/changes . . . . . . . . . . . . . . . . . . . . . 18 70 4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 71 4.1. Properties of the Email object . . . . . . . . . . . . . 18 72 4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 19 73 4.1.2. Header fields parsed forms . . . . . . . . . . . . . 21 74 4.1.3. Header fields properties . . . . . . . . . . . . . . 26 75 4.1.4. Body parts . . . . . . . . . . . . . . . . . . . . . 28 76 4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 34 77 4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 36 78 4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 37 79 4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 38 80 4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 38 81 4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 40 82 4.4.3. Thread collapsing . . . . . . . . . . . . . . . . . . 42 83 4.4.4. Response . . . . . . . . . . . . . . . . . . . . . . 42 84 4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 42 85 4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 42 86 4.7. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 45 87 4.8. Email/import . . . . . . . . . . . . . . . . . . . . . . 45 88 4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 46 89 4.10. Examples . . . . . . . . . . . . . . . . . . . . . . . . 48 90 5. Search snippets . . . . . . . . . . . . . . . . . . . . . . . 56 91 5.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 57 92 5.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 58 93 6. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 59 94 6.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 60 95 6.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 60 96 6.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 60 97 6.4. Example . . . . . . . . . . . . . . . . . . . . . . . . . 60 98 7. Email submission . . . . . . . . . . . . . . . . . . . . . . 61 99 7.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 66 100 7.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 66 101 7.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 66 102 7.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 67 103 7.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 67 104 7.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 69 105 8. Vacation response . . . . . . . . . . . . . . . . . . . . . . 71 106 8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 72 107 8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 72 108 9. Security considerations . . . . . . . . . . . . . . . . . . . 72 109 9.1. EmailBodyPart value . . . . . . . . . . . . . . . . . . . 72 110 9.2. HTML email display . . . . . . . . . . . . . . . . . . . 72 111 9.3. Email submission . . . . . . . . . . . . . . . . . . . . 75 112 10. IANA considerations . . . . . . . . . . . . . . . . . . . . . 75 113 10.1. JMAP capability registration for "mail" . . . . . . . . 75 114 10.2. JMAP capability registration for "submission" . . . . . 76 115 10.3. JMAP capability registration for "vacationresponse" . . 76 116 10.4. IMAP and JMAP keywords registry . . . . . . . . . . . . 76 117 10.4.1. Registration of JMAP keyword '$draft' . . . . . . . 77 118 10.4.2. Registration of JMAP keyword '$seen' . . . . . . . . 78 119 10.4.3. Registration of JMAP keyword '$flagged' . . . . . . 79 120 10.4.4. Registration of JMAP keyword '$answered' . . . . . . 79 121 10.4.5. Registration of '$recent' keyword . . . . . . . . . 80 122 10.5. JMAP Error Codes registry . . . . . . . . . . . . . . . 81 123 10.5.1. mailboxHasChild . . . . . . . . . . . . . . . . . . 81 124 10.5.2. mailboxHasEmail . . . . . . . . . . . . . . . . . . 81 125 10.5.3. blobNotFound . . . . . . . . . . . . . . . . . . . . 81 126 10.5.4. tooManyKeywords . . . . . . . . . . . . . . . . . . 81 127 10.5.5. tooManyMailboxes . . . . . . . . . . . . . . . . . . 82 128 10.5.6. emailNotFound . . . . . . . . . . . . . . . . . . . 82 129 10.5.7. emailTooLarge . . . . . . . . . . . . . . . . . . . 82 130 10.5.8. invalidEmail . . . . . . . . . . . . . . . . . . . . 82 131 10.5.9. tooManyRecipients . . . . . . . . . . . . . . . . . 83 132 10.5.10. noRecipients . . . . . . . . . . . . . . . . . . . . 83 133 10.5.11. invalidRecipients . . . . . . . . . . . . . . . . . 83 134 10.5.12. forbiddenMailFrom . . . . . . . . . . . . . . . . . 83 135 10.5.13. forbiddenFrom . . . . . . . . . . . . . . . . . . . 83 136 10.5.14. forbiddenToSend . . . . . . . . . . . . . . . . . . 84 137 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 84 138 11.1. Normative References . . . . . . . . . . . . . . . . . . 84 139 11.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 87 140 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 87 142 1. Introduction 144 JMAP is a 145 generic protocol for synchronising data, such as mail, calendars or 146 contacts, between a client and a server. It is optimised for mobile 147 and web environments, and aims to provide a consistent interface to 148 different data types. 150 This specification defines a data model for synchronising mail 151 between a client and a server using JMAP. 153 1.1. Notational conventions 155 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 156 "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 157 document are to be interpreted as described in [RFC2119]. 159 Type signatures, examples and property descriptions in this document 160 follow the conventions established in Section 1.1 of 161 . 163 Object properties may also have a set of attributes defined along 164 with the type signature. These have the following meanings: 166 o *server-set*: Only the server can set the value for this property. 167 The client MUST NOT send this property when creating a new object 168 of this type. 170 o *immutable*: The value MUST NOT change after the object is 171 created. 173 o *default*: (This is followed by a JSON value). The value that 174 will be used for this property if it is omitted in an argument, or 175 when creating a new object of this type. 177 Data types defined in the core specification are used in this 178 document. 180 1.2. Terminology 182 The same terminology is used in this document as in the core JMAP 183 specification. 185 1.3. Additions to the capabilities object 187 The capabilities object is returned as part of the standard JMAP 188 Session object; see the JMAP Core specification. 190 This document defines three additional capability objects. 192 1.3.1. urn:ietf:params:jmap:mail 194 This represents support for the Mailbox, Thread, Email, and 195 SearchSnippet data types and associated API methods. The value of 196 this property is an object which MUST contain the following 197 information on server capabilities: 199 o *maxMailboxesPerEmail*: "PositiveInt|null" The maximum number of 200 mailboxes that can be can assigned to a single email. This MUST 201 be an integer >= 1, or "null" for no limit (or rather, the limit 202 is always the number of mailboxes in the account). 204 o *maxSizeAttachmentsPerEmail*: "PositiveInt" The maximum total size 205 of attachments, in octets, allowed for a single email. A server 206 MAY still reject import or creation of emails with a lower 207 attachment size total (for example, if the body includes several 208 megabytes of text, causing the size of the encoded MIME structure 209 to be over some server-defined limit). Note, this limit is for 210 the sum of unencoded attachment sizes. Users are generally not 211 knowledgeable about encoding overhead etc., nor should they need 212 to be, so services marketing and help materials normally tells 213 them the "max size attachments". This is the unencoded size they 214 see on their hard drive, and so this capability matches that and 215 allows the client to consistently enforce what the user 216 understands as the limit. The server may separately have a limit 217 for the total size of the RFC5322 message, which will have 218 attachments Base64 encoded and message headers and bodies too. 219 For example, suppose the server advertises 220 "maxSizeAttachmentsPerEmail: 50000000" (50 MB). The enforced 221 server limit may be for an RFC5322 size of 70000000 octets (70 222 MB). Even with Base64 encoding and a 2 MB HTML body, 50 MB 223 attachments would fit under this limit. 225 o *emailsListSortOptions*: "String[]" A list of all the email 226 properties the server supports for sorting by. This MAY include 227 properties the client does not recognise (for example custom 228 properties specified in a vendor extension). Clients MUST ignore 229 any unknown properties in the list. 231 1.3.2. urn:ietf:params:jmap:submission 233 This represents support for the Identity and MessageSubmission data 234 types and associated API methods. The value of this property is an 235 object which MUST contain the following information on server 236 capabilities: 238 o *maxDelayedSend*: "PositiveInt" The number in seconds of the 239 maximum delay the server supports in sending (see the 240 EmailSubmission object description). This is "0" if the server 241 does not support delayed send. 243 o *submissionExtensions*: "String[String[]]" A JMAP implementation 244 that talks to a Submission [RFC6409] server SHOULD have a 245 configuration setting that allows an administrator to expose a new 246 submission EHLO capability in this field. This allows a JMAP 247 server to gain access to a new submission extension without code 248 changes. By default, the JMAP server should show only known safe- 249 to-expose EHLO capabilities in this field, and hide EHLO 250 capabilities that are only relevant to the JMAP server. Each key 251 in the object is the _ehlo-name_, and the value is a list of 252 _ehlo-args_. Examples of safe-to-expose Submission extensions 253 include: 255 * FUTURERELEASE ([RFC4865]) 257 * SIZE ([RFC1870]) 259 * DSN ([RFC3461]) 261 * DELIVERYBY ([RFC2852]) 263 * MT-PRIORITY ([RFC6710]) 265 A JMAP server MAY advertise an extension and implement the 266 semantics of that extension locally on the JMAP server even if a 267 submission server used by JMAP doesn't implement it. The full 268 IANA registry of submission extensions can be found at 269 272 1.3.3. urn:ietf:params:jmap:vacationresponse 274 This represents support for the VacationResponse data type and 275 associated API methods. The value of this property is an empty 276 object. 278 1.4. Data type support in different accounts 280 The server MUST include the appropriate capability strings in the 281 _hasDataFor_ property of any account in which the user may use the 282 data types represented by that URN. Supported data types may differ 283 between accounts the user has access to. For example, in the user's 284 personal account they may have access to all three sets of data, but 285 in a shared account they may only have data for 286 "urn:ietf:params:jmap:mail". This means they can access 287 Mailbox/Thread/Email data in the shared account but are not allowed 288 to send as that account (and so do not have access to Identity/ 289 MessageSubmission objects) or view/set its vacation response. 291 1.5. Push 293 Servers MUST support the standard JMAP push mechanisms to receive 294 notifications when the state changes for any of the types defined in 295 this specification. 297 In addition, servers MUST support a psuedo-type called 298 "EmailDelivery" in the push mechanisms. The state string for this 299 MUST change whenever a new Email is added to the store, but SHOULD 300 NOT change upon any other change to the Email objects. 302 Clients in battery constrained environments may wish to delay 303 fetching changes initiated by the user, but fetch new messages 304 immediately so they can notify the user. 306 2. Mailboxes 308 A mailbox represents a named set of emails. This is the primary 309 mechanism for organising emails within an account. It is analogous 310 to a folder or a label in other systems. A mailbox may perform a 311 certain role in the system; see below for more details. 313 For compatibility with IMAP, an email MUST belong to one or more 314 mailboxes. The email id does not change if the email changes 315 mailboxes. 317 A *Mailbox* object has the following properties: 319 o *id*: "String" (immutable; server-set) The id of the mailbox. 321 o *name*: "String" User-visible name for the mailbox, e.g. "Inbox". 322 This may be any Net-Unicode string ([RFC5198]) of at least 1 323 character in length and maximum 255 octets in size. Servers MUST 324 forbid sibling Mailboxes with the same name. Servers MAY reject 325 names that violate server policy (e.g., names containing slash (/) 326 or control characters). 328 o *parentId*: "String|null" (default: null) The mailbox id for the 329 parent of this mailbox, or "null" if this mailbox is at the top 330 level. Mailboxes form acyclic graphs (forests) directed by the 331 child-to-parent relationship. There MUST NOT be a loop. 333 o *role*: "String|null" (default: null) Identifies mailboxes that 334 have a particular common purpose (e.g. the "inbox"), regardless of 335 the _name_ (which may be localised). This value is shared with 336 IMAP (exposed in IMAP via the [RFC6154] SPECIAL-USE extension). 337 However, unlike in IMAP, a mailbox may only have a single role, 338 and no two mailboxes in the same account may have the same role. 339 The value MUST be one of the mailbox attribute names listed in the 340 IANA Mailbox Name Attributes Registry [1], as established in 341 [TODO:being established in EXTRA], converted to lower-case. New 342 roles may be established here in the future. An account is not 343 required to have mailboxes with any particular roles. 345 o *sortOrder*: "PositiveInt" (default: 0) Defines the sort order of 346 mailboxes when presented in the client's UI, so it is consistent 347 between devices. The number MUST be an integer in the range 0 <= 348 sortOrder < 2^31. A mailbox with a lower order should be 349 displayed before a mailbox with a higher order (that has the same 350 parent) in any mailbox listing in the client's UI. Mailboxes with 351 equal order SHOULD be sorted in alphabetical order by name. The 352 sorting SHOULD take into account locale-specific character order 353 convention. 355 o *totalEmails*: "PositiveInt" (server-set) The number of emails in 356 this mailbox. 358 o *unreadEmails*: "PositiveInt" (server-set) The number of emails in 359 this mailbox that have neither the "$seen" keyword nor the 360 "$draft" keyword. 362 o *totalThreads*: "PositiveInt" (server-set) The number of threads 363 where at least one email in the thread is in this mailbox. 365 o *unreadThreads*: "PositiveInt" (server-set) The number of threads 366 where at least one email in the thread has neither the "$seen" 367 keyword nor the "$draft" keyword AND at least one email in the 368 thread is in this mailbox (but see below for special case handling 369 of Trash). Note, the unread email does not need to be the one in 370 this mailbox. 372 o *myRights*: "MailboxRights" (server-set) The set of rights (ACLs) 373 the user has in relation to this mailbox. A _MailboxRights_ 374 object has the following properties: 376 * *mayReadItems*: "Boolean" If true, the user may use this 377 mailbox as part of a filter in a _Email/query_ call and the 378 mailbox may be included in the _mailboxIds_ set of _Email_ 379 objects. If a sub-mailbox is shared but not the parent 380 mailbox, this may be "false". Corresponds to IMAP ACLs "lr". 382 * *mayAddItems*: "Boolean" The user may add mail to this mailbox 383 (by either creating a new email or moving an existing one). 384 Corresponds to IMAP ACL "i". 386 * *mayRemoveItems*: "Boolean" The user may remove mail from this 387 mailbox (by either changing the mailboxes of an email or 388 deleting it). Corresponds to IMAP ACLs "te". 390 * *maySetSeen*: "Boolean" The user may add or remove the "$seen" 391 keyword to/from an email. If an email belongs to multiple 392 mailboxes, the user may only modify "$seen" if *all* of the 393 mailboxes have this permission. Corresponds to IMAP ACL "s". 395 * *maySetKeywords*: "Boolean" The user may add or remove any 396 keyword _other than_ "$seen" to/from an email. If an email 397 belongs to multiple mailboxes, the user may only modify 398 keywords if *all* of the mailboxes have this permission. 399 Corresponds to IMAP ACL "w". 401 * *mayCreateChild*: "Boolean" The user may create a mailbox with 402 this mailbox as its parent. Corresponds to IMAP ACL "k". 404 * *mayRename*: "Boolean" The user may rename the mailbox or make 405 it a child of another mailbox. Corresponds to IMAP ACL "x". 407 * *mayDelete*: "Boolean" The user may delete the mailbox itself. 408 Corresponds to IMAP ACL "x". 410 * *maySubmit*: "Boolean" Messages may be submitted directly to 411 this mailbox. Corresponds to IMAP ACL "p". 413 o *isSubscribed*: "Boolean" Has the user indicated they wish to see 414 this mailbox in their client? This SHOULD default to "false" for 415 mailboxes in shared accounts the user has access to, and "true" 416 for any new mailboxes created by the user themself. This MUST be 417 stored separately per-user where multiple users have access to a 418 shared mailbox. A user may have permission to access a large 419 number of shared accounts, or a shared account with a very large 420 set of mailboxes, but only be interested in the contents of a few 421 of these. Clients may choose only to display mailboxes to the 422 user that have the "isSubscribed" property set to "true", and 423 offer a separate UI to allow the user to see and subscribe/ 424 unsubscribe from the full set of mailboxes. However, clients MAY 425 choose to ignore this property, either entirely, for ease of 426 implementation, or just for the primary account (which is normally 427 the user's own, rather than a shared account). 429 The Trash mailbox (that is a mailbox with "role == "trash"") MUST be 430 treated specially for the purpose of unread counts: 432 1. Emails that are *only* in the Trash (and no other mailbox) are 433 ignored when calculating the "unreadThreads" count of other 434 mailboxes. 436 2. Emails that are *not* in the Trash are ignored when calculating 437 the "unreadThreads" count for the Trash mailbox. 439 The result of this is that emails in the Trash are treated as though 440 they are in a separate thread for the purposes of unread counts. It 441 is expected that clients will hide emails in the Trash when viewing a 442 thread in another mailbox and vice versa. This allows you to delete 443 a single email to the Trash out of a thread. 445 So for example, suppose you have an account where the entire contents 446 is a single conversation with 2 emails: an unread email in the Trash 447 and a read email in the Inbox. The "unreadThreads" count would be 448 "1" for the Trash and "0" for the Inbox. 450 For IMAP compatibility, an email in both the Trash and another 451 mailbox SHOULD be treated by the client as existing in both places 452 (i.e. when emptying the trash, the client SHOULD just remove the 453 Trash mailbox and leave it in the other mailbox). 455 The following JMAP methods are supported: 457 2.1. Mailbox/get 459 Standard "/get" method. The _ids_ argument may be "null" to fetch 460 all at once. 462 2.2. Mailbox/changes 464 Standard "/changes" method, but with one extra argument to the 465 response: 467 o *updatedProperties*: "String[]|null" If only the mailbox counts 468 (unread/total emails/threads) have changed since the old state, 469 this will be the list of properties that may have changed, i.e. 470 "["totalEmails", "unreadEmails", "totalThreads", 471 "unreadThreads"]". If the server is unable to tell if only counts 472 have changed, it MUST just be "null". 474 Since counts frequently change but the rest of the mailboxes state 475 for most use cases changes rarely, the server can help the client 476 optimise data transfer by keeping track of changes to email/thread 477 counts separately to other state changes. The _updatedProperties_ 478 array may be used directly via a result reference in a subsequent 479 Mailbox/get call in a single request. 481 2.3. Mailbox/query 483 Standard "/query" method. 485 A *FilterCondition* object has the following properties, any of which 486 may be omitted: 488 o *parentId*: "String|null" The Mailbox _parentId_ property must 489 match the given value exactly. 491 o *name*: "String" The Mailbox _name_ property contains the given 492 string. 494 o *role*: "String|null" The Mailbox _role_ property must match the 495 given value exactly. 497 o *hasAnyRole*: "Boolean" If "true", a Mailbox matches if it has any 498 non-"null" value for its _role_ property. 500 o *isSubscribed*: "Boolean" The "isSubscribed" property of the 501 mailbox must be identical to the value given to match the 502 condition. 504 A Mailbox object matches the filter if and only if all of the given 505 conditions given match. If zero properties are specified, it is 506 automatically "true" for all objects. 508 The following properties MUST be supported for sorting: 510 o "sortOrder" 512 o "name" 514 o "parent/name": This is a pseudo-property, just for sorting, with 515 the following semantics: if two mailboxes have a common parent, 516 sort them by name. Otherwise, find the nearest ancestors of each 517 that share a common parent and sort by their names instead. (i.e. 518 This sorts the mailbox list in tree order). 520 2.4. Mailbox/queryChanges 522 Standard "/queryChanges" method. 524 2.5. Mailbox/set 526 Standard "/set" method, but with the following additional argument: 528 o *onDestroyRemoveMessages*: "Boolean" (default: false) If "false", 529 attempts to destroy a mailbox that still has any messages in it 530 will be rejected with a "mailboxHasEmail" SetError. If "true", 531 any messages that were in the mailbox will be removed from it, and 532 if in no other mailboxes will be destroyed when the mailbox is 533 destroyed. 535 The following extra _SetError_ types are defined: 537 For *destroy*: 539 o "mailboxHasChild": The mailbox still has at least one child 540 mailbox. The client MUST remove these before it can delete the 541 parent mailbox. 543 o "mailboxHasEmail": The mailbox has at least one message assigned 544 to it and the _onDestroyRemoveMessages_ argument was "false". 546 2.6. Example 548 Fetching all mailboxes in an account: 550 [[ "Mailbox/get", { 551 "accountId": "u33084183", 552 "ids": null 553 }, "0" ]] 555 And response: 557 [[ "Mailbox/get", { 558 "accountId": "u33084183", 559 "state": "78540", 560 "list": [ 561 { 562 "id": "23cfa8094c0f41e6", 563 "name": "Inbox", 564 "parentId": null, 565 "role": "inbox", 566 "sortOrder": 10, 567 "totalEmails": 16307, 568 "unreadEmails": 13905, 569 "totalThreads": 5833, 570 "unreadThreads": 5128, 571 "myRights": { 572 "mayAddItems": true, 573 "mayRename": false, 574 "maySubmit": true, 575 "mayDelete": false, 576 "maySetKeywords": true, 577 "mayRemoveItems": true, 578 "mayCreateChild": true, 579 "maySetSeen": true, 580 "mayReadItems": true 581 }, 582 "isSubscribed": true 583 }, 584 { 585 "id": "674cc24095db49ce", 586 "name": "Important mail", 587 ... 588 } 589 ... 590 ], 591 "notFound": [] 592 }, "0" ]] 594 Now suppose a message is marked read and we get a push update that 595 the Mailbox state has changed. You might fetch the updates like 596 this: 598 [[ "Mailbox/changes", { 599 "accountId": "u33084183", 600 "sinceState": "78540" 601 }, "0" ], 602 [ "Mailbox/get", { 603 "accountId": "u33084183", 604 "#ids": { 605 "resultOf": "0", 606 "name": "Mailbox/changes", 607 "path": "/created" 608 } 609 }, "1" ], 610 [ "Mailbox/get", { 611 "accountId": "u33084183", 612 "#ids": { 613 "resultOf": "0", 614 "name": "Mailbox/changes", 615 "path": "/updated" 616 }, 617 "#properties": { 618 "resultOf": "0", 619 "name": "Mailbox/changes", 620 "path": "/updatedProperties" 621 } 622 }, "2" ]] 624 This fetches the list of ids for created/updated/destroyed mailboxes, 625 then using back references fetches the data for just the created/ 626 updated mailboxes in the same request. The response may look 627 something like this: 629 [[ "Mailbox/changes", { 630 "accountId": "u33084183", 631 "oldState": "78541", 632 "newState": "78542", 633 "hasMoreChanges": false, 634 "updatedProperties": [ 635 "totalEmails", "unreadEmails", 636 "totalThreads", "unreadThreads" 637 ], 638 "created": [], 639 "updated": ["23cfa8094c0f41e6"], 640 "destroyed": [] 641 }, "0" ], 642 [ "Mailbox/get", { 643 "accountId": "u33084183", 644 "state": "78542", 645 "list": [], 646 "notFound": [] 647 }, "1" ], 648 [ "Mailbox/get", { 649 "accountId": "u33084183", 650 "state": "78542", 651 "list": [{ 652 "id": "23cfa8094c0f41e6", 653 "totalEmails": 16307, 654 "unreadEmails": 13903, 655 "totalThreads": 5833, 656 "unreadThreads": 5127 657 }], 658 "notFound": [] 659 }, "2" ]] 661 Here's an example where we try to rename one mailbox and destroy 662 another: 664 [[ "Mailbox/set", { 665 "accountId": "u33084183", 666 "ifInState": "78542", 667 "update": { 668 "674cc24095db49ce": { 669 "name": "Maybe important mail" 670 } 671 }, 672 "destroy": [ "23cfa8094c0f41e6" ] 673 }, "0" ]] 675 Suppose the rename succeeds, but we don't have permission to destroy 676 the mailbox we tried to destroy, we might get back: 678 [[ "Mailbox/set", { 679 "accountId": "u33084183", 680 "oldState": "78542", 681 "newState": "78549", 682 "created": null, 683 "notCreated": null, 684 "updated": { 685 "674cc24095db49ce": null 686 }, 687 "notUpdated": null, 688 "destroyed": null, 689 "notDestroyed": { 690 "23cfa8094c0f41e6": { 691 "type": "forbidden" 692 } 693 } 694 }, "0" ]] 696 3. Threads 698 Replies are grouped together with the original message to form a 699 thread. In JMAP, a thread is simply a flat list of emails, ordered 700 by date. Every email MUST belong to a thread, even if it is the only 701 email in the thread. 703 The exact algorithm for determining whether two emails belong to the 704 same thread is not mandated in this spec to allow for compatibility 705 with different existing systems. For new implementations, it is 706 suggested that two messages belong in the same thread if both of the 707 following conditions apply: 709 1. An identical RFC5322 message id appears in both messages in any 710 of the Message-Id, In-Reply-To and References headers. 712 2. After stripping automatically added prefixes such as "Fwd:", 713 "Re:", "[List-Tag]" etc. and ignoring whitespace, the subjects 714 are the same. This avoids the situation where a person replies 715 to an old message as a convenient way of finding the right 716 recipient to send to, but changes the subject and starts a new 717 conversation. 719 If emails are delivered out of order for some reason, a user may 720 receive two emails in the same thread but without headers that 721 associate them with each other. The arrival of a third email in the 722 thread may provide the missing references to join them all together 723 into a single thread. Since the _threadId_ of an email is immutable, 724 if the server wishes to merge the threads, it MUST handle this by 725 deleting and reinserting (with a new email id) the emails that change 726 threadId. 728 A *Thread* object has the following properties: 730 o *id*: "String" (immutable) The id of the thread. 732 o *emailIds*: "String[]" The ids of the emails in the thread, sorted 733 by the _receivedAt_ date of the email, oldest first. If two 734 emails have an identical date, the sort is server-dependent but 735 MUST be stable (sorting by id is recommended). 737 The following JMAP methods are supported: 739 3.1. Thread/get 741 Standard "/get" method. 743 3.1.1. Example 745 Request: 747 [[ "Thread/get", { 748 "ids": ["f123u4", "f41u44"], 749 }, "#1" ]] 751 with response: 753 [[ "Thread/get", { 754 "accountId": "acme", 755 "state": "f6a7e214", 756 "list": [ 757 { 758 "id": "f123u4", 759 "emailIds": [ "eaa623", "f782cbb"] 760 }, 761 { 762 "id": "f41u44", 763 "emailIds": [ "82cf7bb" ] 764 } 765 ], 766 "notFound": [] 767 }, "#1" ]] 769 3.2. Thread/changes 771 Standard "/changes" method. 773 4. Emails 775 The *Email* object is a representation of an [RFC5322] message, which 776 allows clients to avoid the complexities of MIME parsing, transport 777 encoding and character encoding. 779 4.1. Properties of the Email object 781 Broadly, a message consists of two parts: a list of header fields, 782 then a body. The JMAP Email object provides a way to access the full 783 structure, or to use simplified properties and avoid some complexity 784 if this is sufficient for the client application. 786 While raw headers can be fetched and set, the vast majority of 787 clients should use an appropriate parsed form for each of the headers 788 it wants to process, as this allows it to avoid the complexities of 789 various encodings that are required in a valid RFC5322 message. 791 The body of a message is normally a MIME-encoded set of documents in 792 a tree structure. This may be arbitrarily nested, but the majority 793 of email clients present a flat model of an email body (normally 794 plain text or HTML), with a set of attachments. Flattening the MIME 795 structure to form this model can be difficult, and causes 796 inconsistency between clients. Therefore in addition to the 797 _bodyStructure_ property, which gives the full tree, the Email object 798 contains 3 alternate properties with flat lists of body parts: 800 o _textBody_/_htmlBody_: These provide a list of parts that should 801 be rendered sequentially as the "body" of the message. This is a 802 list rather than a single part as messages may have headers and/or 803 footers appended/prepended as separate parts as they are 804 transmitted, and some clients send text and images, or even videos 805 and sound clips, intended to be displayed inline in the body as 806 multiple parts rather than a single HTML part with referenced 807 images. 809 Because MIME allows for multiple representations of the same data 810 (using "multipart/alternative"), there is a textBody property (which 811 prefers a plain text representation) and an htmlBody property (which 812 prefers an HTML representation) to accommodate the two most common 813 client requirements. The same part may appear in both lists where 814 there is no alternative between the two. 816 o _attachments_: This provides a list of parts that should be 817 presented as "attachments" to the message. Some images may be 818 solely there for embedding within an HTML body part; clients may 819 wish to not present these as attachments in the user interface if 820 they are displaying the HTML with the embedded images directly. 821 Some parts may also be in htmlBody/textBody; again, clients may 822 wish to not present these as attachments in the user interface if 823 rendered as part of the body. 825 The _bodyValues_ property allows for clients to fetch the value of 826 text parts directly without having to do a second request for the 827 blob, and have the server handle decoding the charset into unicode. 828 This data is in a separate property rather than on the EmailBodyPart 829 object to avoid duplication of large amounts of data, as the same 830 part may be included twice if the client fetches more than one of 831 bodyStructure, textBody and htmlBody. 833 Due to the number of properties involved, the set of _Email_ 834 properties is specified over the following three sub-sections. 836 4.1.1. Metadata 838 These properties represent metadata about the [RFC5322] message, and 839 are not derived from parsing the message itself. 841 o *id*: "String" (immutable; server-set) The id of the Email object. 842 Note, this is the JMAP object id, NOT the [RFC5322] Message-ID 843 header field value. 845 o *blobId*: "String" (immutable; server-set) The id representing the 846 raw octets of the [RFC5322] message. This may be used to download 847 the raw original message, or to attach it directly to another 848 Email etc. 850 o *threadId*: "String" (immutable; server-set) The id of the Thread 851 to which this Email belongs. 853 o *mailboxIds*: "String[Boolean]" The set of mailbox ids this email 854 belongs to. An email MUST belong to one or more mailboxes at all 855 times (until it is deleted). The set is represented as an object, 856 with each key being a _Mailbox id_. The value for each key in the 857 object MUST be "true". 859 o *keywords*: "String[Boolean]" (default: ) A set of keywords that 860 apply to the email. The set is represented as an object, with the 861 keys being the _keywords_. The value for each key in the object 862 MUST be "true". Keywords are shared with IMAP. The six system 863 keywords from IMAP are treated specially. The following four 864 keywords have their first character changed from "\" in IMAP to 865 "$" in JMAP and have particular semantic meaning: 867 * "$draft": The email is a draft the user is composing. 869 * "$seen": The email has been read. 871 * "$flagged": The email has been flagged for urgent/special 872 attention. 874 * "$answered": The email has been replied to. 876 The IMAP "\Recent" keyword is not exposed via JMAP. The IMAP 877 "\Deleted" keyword is also not present: IMAP uses a delete+expunge 878 model, which JMAP does not. Any message with the "\Deleted" 879 keyword MUST NOT be visible via JMAP. Users may add arbitrary 880 keywords to an email. For compatibility with IMAP, a keyword is a 881 case-insensitive string of 1-255 characters in the ASCII subset 882 %x21-%x7e (excludes control chars and space), and MUST NOT include 883 any of these characters: "( ) { ] % * " \" Because JSON is case- 884 sensitive, servers MUST return keywords in lower-case. The IANA 885 Keyword Registry [2] as established in [RFC5788] assigns semantic 886 meaning to some other keywords in common use. New keywords may be 887 established here in the future. In particular, note: 889 * "$forwarded": The email has been forwarded. 891 * "$phishing": The email is highly likely to be phishing. 892 Clients SHOULD warn users to take care when viewing this email 893 and disable links and attachments. 895 * "$junk": The email is definitely spam. Clients SHOULD set this 896 flag when users report spam to help train automated spam- 897 detection systems. 899 * "$notjunk": The email is definitely not spam. Clients SHOULD 900 set this flag when users indicate an email is legitimate, to 901 help train automated spam-detection systems. 903 o *size*: "PositiveInt" (immutable; server-set) The size, in octets, 904 of the raw data for the [RFC5322] message (as referenced by the 905 _blobId_, i.e. the number of octets in the file the user would 906 download). 908 o *receivedAt*: "UTCDate" (immutable; default: time of creation on 909 server) The date the email was received by the message store. 910 This is the _internal date_ in IMAP. 912 4.1.2. Header fields parsed forms 914 Header field properties are derived from the [RFC5322] and [RFC6532] 915 message header fields. All header fields may be fetched in a raw 916 form. Some headers may also be fetched in a parsed form. The 917 structured form that may be fetched depends on the header. The 918 following forms are defined: 920 4.1.2.1. Raw 922 Type: "String" 924 The raw octets of the header field value from the first octet 925 following the header field name terminating colon, up to but 926 excluding the header field terminating CRLF. Any standards-compliant 927 message MUST be either ASCII (RFC5322) or UTF-8 (RFC6532), however 928 other encodings exist in the wild. A server MAY use heuristics to 929 determine a charset and decode the octets, or MAY replace any octet 930 or octet run with the high bit set that violates UTF-8 syntax with 931 the unicode replacement character (U+FFFD). Any NUL octet MUST be 932 dropped. 934 4.1.2.2. Text 936 Type: "String" 938 The header field value with: 940 1. White space unfolded (as defined in [RFC5322] section 2.2.3) 942 2. The terminating CRLF at the end of the value removed 944 3. Any SP characters at the beginning of the value removed 946 4. Any syntactically correct [RFC2047] encoded sections with a known 947 character set decoded. Any [RFC2047] encoded NUL octets or 948 control characters are dropped from the decoded value. Any text 949 that looks like [RFC2047] syntax but violates [RFC2047] placement 950 or whitespace rules MUST NOT be decoded. 952 5. Any [RFC6532] UTF-8 values decoded. 954 6. The resulting unicode converted to NFC form. 956 If any decodings fail, the parser SHOULD insert a unicode replacement 957 character (U+FFFD) and attempt to continue as much as possible. 959 To prevent obviously nonsense behaviour, which can lead to 960 interoperability issues, this form may only be fetched or set for the 961 following header fields: 963 o Subject 965 o Comment 967 o List-Id 969 o Any header not defined in [RFC5322] or [RFC2369] 971 4.1.2.3. Addresses 973 Type: "EmailAddress[]" 975 The header is parsed as an "address-list" value, as specified in 976 [RFC5322] section 3.4, into the "EmailAddress[]" type. There is an 977 EmailAddress item for each "mailbox" parsed from the "address-list". 978 Group and comment information is discarded. 980 The *EmailAddress* object has the following properties: 982 o *name*: "String|null" The _display-name_ of the [RFC5322] 983 _mailbox_, or "null" if none. If this is a _quoted-string_: 985 1. The surrounding DQUOTE characters are removed. 987 2. Any _quoted-pair_ is decoded. 989 3. White-space is unfolded, and then any leading and trailing 990 white-space is removed. 992 o *email*: "String" The _addr-spec_ of the [RFC5322] _mailbox_. 994 Any syntactically correct [RFC2047] encoded sections with a known 995 encoding MUST be decoded, following the same rules as for the _Text_ 996 form. Any [RFC6532] UTF-8 values MUST be decoded. 998 Parsing SHOULD be best-effort in the face of invalid structure to 999 accommodate invalid messages and semi-complete drafts. EmailAddress 1000 objects MAY have an _email_ property that does not conform to the 1001 _addr-spec_ form (for example, may not contain an @ symbol). 1003 For example, the following "address-list" string: 1005 " James Smythe" , Friends: jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= ; 1006 would be parsed as: 1008 [ 1009 { "name": "James Smythe", "email": "james@example.com" }, 1010 { "name": null, "email": "jane@example.com" }, 1011 { "name": "John Smith", "email": "john@example.com" }, 1012 ] 1014 To prevent obviously nonsense behaviour, which can lead to 1015 interoperability issues, this form may only be fetched or set for the 1016 following header fields: 1018 o From 1020 o Sender 1022 o Reply-To 1024 o To 1026 o Cc 1028 o Bcc 1030 o Resent-From 1032 o Resent-Sender 1034 o Resent-Reply-To 1036 o Resent-To 1038 o Resent-Cc 1040 o Resent-Bcc 1042 o Any header not defined in [RFC5322] or [RFC2369] 1044 4.1.2.4. GroupedAddresses 1046 Type: "EmailAddressGroup[]" 1048 This is similar to the Addresses form but preserves group 1049 information. The header is parsed as an "address-list" value, as 1050 specified in [RFC5322] section 3.4, into the "GroupedAddresses[]" 1051 type. Consecutive mailboxes that are not part of a group are still 1052 collected under an EmailAddressGroup object to provide a uniform 1053 type. 1055 The *EmailAddressGroup* object has the following properties: 1057 o *name*: "String|null" The _display-name_ of the [RFC5322] _group_, 1058 or "null" if the addresses are not part of a group. If this is a 1059 _quoted-string_ it is processed the same as the _name_ in the 1060 _EmailAddress_ type. 1062 o *addresses*: "EmailAddress[]" The _mailbox_es that belong to this 1063 group, represented as EmailAddress objects. 1065 Any syntactically correct [RFC2047] encoded sections with a known 1066 encoding MUST be decoded, following the same rules as for the _Text_ 1067 form. Any [RFC6532] UTF-8 values MUST be decoded. 1069 Parsing SHOULD be best-effort in the face of invalid structure to 1070 accommodate invalid messages and semi-complete drafts. 1072 For example, the following "address-list" string: 1074 " James Smythe" , Friends: jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= ; 1076 would be parsed as: 1078 [ 1079 { "name": null, "addresses": [ 1080 { "name": "James Smythe", "email": "james@example.com" } 1081 ]}, 1082 { "name": "Friends", "addresses": [ 1083 { "name": null, "email": "jane@example.com" }, 1084 { "name": "John Smith", "email": "john@example.com" } 1085 ]} 1086 ] 1088 To prevent obviously nonsense behaviour, which can lead to 1089 interoperability issues, this form may only be fetched or set for the 1090 same header fields as the _Addresses_ form. 1092 4.1.2.5. MessageIds 1094 Type: "String[]|null" 1096 The header is parsed as a list of "msg-id" values, as specified in 1097 [RFC5322] section 3.6.4, into the "String[]" type. CFWS and 1098 surrounding angle brackets ("<>") are removed. If parsing fails, the 1099 value is "null". 1101 To prevent obviously nonsense behaviour, which can lead to 1102 interoperability issues, this form may only be fetched or set for the 1103 following header fields: 1105 o Message-ID 1107 o In-Reply-To 1109 o References 1111 o Resent-Message-ID 1113 o Any header not defined in [RFC5322] or [RFC2369] 1115 4.1.2.6. Date 1117 Type: "Date|null" 1119 The header is parsed as a "date-time" value, as specified in 1120 [RFC5322] section 3.3, into the "Date" type. If parsing fails, the 1121 value is "null". 1123 To prevent obviously nonsense behaviour, which can lead to 1124 interoperability issues, this form may only be fetched or set for the 1125 following header fields: 1127 o Date 1129 o Resent-Date 1131 o Any header not defined in [RFC5322] or [RFC2369] 1133 4.1.2.7. URLs 1135 Type: "String[]|null" 1137 The header is parsed as a list of URLs, as described in [RFC2369], 1138 into the "String[]" type. Values do not include the surrounding 1139 angle brackets or any comments in the header with the URLs. If 1140 parsing fails, the value is "null". 1142 To prevent obviously nonsense behaviour, which can lead to 1143 interoperability issues, this form may only be fetched or set for the 1144 following header fields: 1146 o List-Help 1148 o List-Unsubscribe 1149 o List-Subscribe 1151 o List-Post 1153 o List-Owner 1155 o List-Archive 1157 o Any header not defined in [RFC5322] or [RFC2369] 1159 4.1.3. Header fields properties 1161 The following low-level *Email* property is specified for complete 1162 access to the header data of the message: 1164 o *headers*: "EmailHeader[]" (immutable) This is a list of all 1165 [RFC5322] header fields, in the same order they appear in the 1166 message. An *EmailHeader* object has the following properties: 1168 * *name*: "String" The header _field name_ as defined in 1169 [RFC5322], with the same capitalization that it has in the 1170 message. 1172 * *value*: "String" The header _field value_ as defined in 1173 [RFC5322], in _Raw_ form. 1175 In addition, the client may request/send properties representing 1176 individual header fields of the form: 1178 header:{header-field-name} 1180 Where "{header-field-name}" means any series of one or more printable 1181 ASCII characters (i.e. characters that have values between 33 and 1182 126, inclusive), except colon. The property may also have the 1183 following suffixes: 1185 o *:as{header-form}* This means the value is in a parsed form, where 1186 "{header-form}" is one of the parsed-form names specified above. 1187 If not given, the value is in _Raw_ form. 1189 o *:all* This means the value is an array, with the items 1190 corresponding to each instance of the header field, in the order 1191 they appear in the message. If this suffix is not used, the 1192 result is the value of the *last* instance of the header field 1193 (i.e. identical to the *last* item in the array if :all is used), 1194 or "null" if none. 1196 If both suffixes are used, they MUST be specified in the order above. 1197 Header field names are matched case-insensitively. The value is 1198 typed according to the requested form, or an array of that type if 1199 :all is used. If no header fields exist in the message with the 1200 requested name, the value is "null" if fetching a single instance, or 1201 the empty array if requesting :all. 1203 As a simple example, if the client requests a property called 1204 "header:subject", this means find the _last_ header field in the 1205 message named "subject" (matched case-insensitively) and return the 1206 value in _Raw_ form, or "null" if no header of this name is found. 1208 For a more complex example, consider the client requesting a property 1209 called "header:Resent-To:asAddresses:all". This means: 1211 1. Find _all_ header fields named Resent-To (matched case- 1212 insensitively). 1214 2. For each instance parse the header field value in the _Addresses_ 1215 form. 1217 3. The result is of type "EmailAddress[][]" - each item in the array 1218 corresponds to the parsed value (which is itself an array) of the 1219 Resent-To header field instance. 1221 The following convenience properties are also specified for the 1222 *Email* object: 1224 o *messageId*: "String[]|null" (immutable) The value is identical to 1225 the value of _header:Message-ID:asMessageIds_. For messages 1226 conforming to RFC5322 this will be an array with a single entry. 1228 o *inReplyTo*: "String[]|null" (immutable) The value is identical to 1229 the value of _header:In-Reply-To:asMessageIds_. 1231 o *references*: "String[]|null" (immutable) The value is identical 1232 to the value of _header:References:asMessageIds_. 1234 o *sender*: "EmailAddress[]|null" (immutable) The value is identical 1235 to the value of _header:Sender:asAddresses_. 1237 o *from*: "EmailAddress[]|null" (immutable) The value is identical 1238 to the value of _header:From:asAddresses_. 1240 o *to*: "EmailAddress[]|null" (immutable) The value is identical to 1241 the value of _header:To:asAddresses_. 1243 o *cc*: "EmailAddress[]|null" (immutable) The value is identical to 1244 the value of _header:Cc:asAddresses_. 1246 o *bcc*: "EmailAddress[]|null" (immutable) The value is identical to 1247 the value of _header:Bcc:asAddresses_. 1249 o *replyTo*: "EmailAddress[]|null" (immutable) The value is 1250 identical to the value of _header:Reply-To:asAddresses_. 1252 o *subject*: "String|null" (immutable) The value is identical to the 1253 value of _header:Subject:asText_. 1255 o *sentAt*: "Date|null" (immutable; default on creation: current 1256 server time) The value is identical to the value of 1257 _header:Date:asDate_. 1259 4.1.4. Body parts 1261 These properties are derived from the [RFC5322] message body and its 1262 [RFC2045] MIME entities. 1264 A *EmailBodyPart* object has the following properties: 1266 o *partId*: "String|null" Identifies this part uniquely within the 1267 Email. This is scoped to the _emailId_ and has no meaning outside 1268 of the JMAP Email object representation. This is "null" if, and 1269 only if, the part is of type "multipart/*". 1271 o *blobId*: "String|null" The id representing the raw octets of the 1272 contents of the part after decoding any _Content-Transfer- 1273 Encoding_ (as defined in [RFC2045]), or "null" if, and only if, 1274 the part is of type "multipart/*". Note, two parts may be 1275 transfer-encoded differently but have same the same blob id if 1276 their decoded octets are identical and the server is using a 1277 secure hash of the data for the blob id. 1279 o *size*: "PositiveInt" The size, in octets, of the raw data after 1280 content transfer decoding (as referenced by the _blobId_, i.e. the 1281 number of octets in the file the user would download). 1283 o *headers*: "EmailHeader[]" This is a list of all header fields in 1284 the part, in the order they appear. The values are in _Raw_ form. 1286 o *name*: "String|null" This is the [RFC2231] decoded _filename_ 1287 parameter of the _Content-Disposition_ header field, or (for 1288 compatibility with existing systems) if not present then the 1289 [RFC2047] decoded _name_ parameter of the _Content-Type_ header 1290 field. 1292 o *type*: "String" The value of the _Content-Type_ header field of 1293 the part, if present, otherwise the implicit type as per the MIME 1294 standard ("text/plain", or "message/rfc822" if inside a 1295 "multipart/digest"). CFWS is removed and any parameters are 1296 stripped. 1298 o *charset*: "String|null" The value of the charset parameter of the 1299 _Content-Type_ header field, if present, or "null" if the header 1300 field is present but not of type "text/*". If there is no 1301 _Content-Type_ header field, or it exists and is of type "text/*" 1302 but has no charset parameter, this is the implicit charset as per 1303 the MIME standard: "us-ascii". 1305 o *disposition*: "String|null" The value of the _Content- 1306 Disposition_ header field of the part, if present, otherwise 1307 "null". CFWS is removed and any parameters are stripped. 1309 o *cid*: "String|null" The value of the _Content-Id_ header field of 1310 the part, if present, otherwise "null". CFWS and surrounding 1311 angle brackets ("<>") are removed. This may be used to reference 1312 the content from within an html body part using the "cid:" 1313 protocol. 1315 o *language*: "String[]|null" The list of language tags, as defined 1316 in [RFC3282], in the _Content-Language_ header field of the part, 1317 if present. 1319 o *location*: "String|null" The URI, as defined in [RFC2557], in the 1320 _Content-Location_ header field of the part, if present. 1322 o *subParts*: "EmailBodyPart[]|null" If type is "multipart/*", this 1323 contains the body parts of each child. 1325 In addition, the client may request/send EmailBodyPart properties 1326 representing individual header fields, following the same syntax and 1327 semantics as for the Email object, e.g. "header:Content-Type". 1329 The following *Email* properties are specified for access to the body 1330 data of the message: 1332 o *bodyStructure*: "EmailBodyPart" (immutable) This is the full MIME 1333 structure of the message body, represented as an array of the 1334 message's top-level MIME parts, without recursing into "message/ 1335 rfc822" or "message/global" parts. Note that EmailBodyParts may 1336 have subParts if they are of type "multipart/*". 1338 o *bodyValues*: "String[EmailBodyValue]" (immutable) This is a map 1339 of _partId_ to an *EmailBodyValue* object for none, some or all 1340 "text/*" parts. Which parts are included and whether the value is 1341 truncated is determined by various arguments to _Email/get_ and 1342 _Email/parse_. An *EmailBodyValue* object has the following 1343 properties: 1345 * *value*: "String" The value of the body part after decoding 1346 _Content-Transport-Encoding_ and decoding the _Content-Type_ 1347 charset, if known to the server, and with any CRLF replaced 1348 with a single LF. The server MAY use heuristics to determine 1349 the charset to use for decoding if the charset is unknown, or 1350 if no charset is given, or if it believes the charset given is 1351 incorrect. Decoding is best-effort and SHOULD insert the 1352 unicode replacement character (U+FFFD) and continue when a 1353 malformed section is encountered. Note that due to the charset 1354 decoding and line ending normalisation, the length of this 1355 string will probably not be exactly the same as the _size_ 1356 property on the corresponding EmailBodyPart. 1358 * *isEncodingProblem*: "Boolean" (default: false) This is "true" 1359 if malformed sections were found while decoding the charset, or 1360 the charset was unknown. 1362 * *isTruncated*: "Boolean" (default: false) This is "true" if the 1363 _value_ has been truncated. 1365 See the security considerations section for issues related to 1366 truncation and heuristic determination of content-type and 1367 charset. 1369 o *textBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", 1370 "text/html", "image/*", "audio/*" and/or "video/*" parts to 1371 display (sequentially) as the message body, with a preference for 1372 "text/plain" when alternative versions are available. 1374 o *htmlBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", 1375 "text/html", "image/*", "audio/*" and/or "video/*" parts to 1376 display (sequentially) as the message body, with a preference for 1377 "text/html" when alternative versions are available. 1379 o *attachments*: "EmailBodyPart[]" (immutable) A list of all parts 1380 in _bodyStructure_, traversing depth-first, which satisfy either 1381 of the following conditions: 1383 * not of type "multipart/*" and not included in _textBody_ or 1384 _htmlBody_ 1386 * of type "image/*", "audio/*" or "video/*" and not in both 1387 _textBody_ and _htmlBody_ 1389 None of these parts include subParts, including "message/*" types. 1390 Attached messages may be fetched using the Email/parse method and 1391 the blobId. Note, an HTML body part may reference image parts in 1392 attachments using "cid:" links to reference the _Content-Id_ or by 1393 referencing the _Content-Location_. 1395 o *hasAttachment*: "Boolean" (immutable; server-set) This is "true" 1396 if there are one or more parts in the message that a client UI 1397 should offer as downloadable. A server SHOULD set hasAttachment 1398 if either: 1400 * The _attachments_ list contains at least one item that does not 1401 have "Content-Disposition: inline". The server MAY ignore 1402 parts in this list that are processed automatically in some 1403 way, or are referenced as embedded images in one of the "text/ 1404 html" parts of the message. 1406 The server MAY set hasAttachment based on implementation-defined 1407 or site configurable heuristics. 1409 o *preview*: "String" (immutable; server-set) Up to 255 octets of 1410 plain text, summarising the message body. This is intended to be 1411 shown as a preview line on a mailbox listing, and may be truncated 1412 when shown. The server may choose which part of the message to 1413 include in the preview, for example skipping quoted sections and 1414 salutations and collapsing white-space can result in a more useful 1415 preview. 1417 The exact algorithm for decomposing bodyStructure into textBody, 1418 htmlBody and attachments part lists is not mandated, as this is a 1419 quality-of-service implementation issue and likely to require 1420 workarounds for malformed content discovered over time. However, the 1421 following algorithm (expressed here in JavaScript) is suggested as a 1422 starting point, based on real-world experience: 1424 function isInlineMediaType ( type ) { 1425 return type.startsWith( 'image/' ) || 1426 type.startsWith( 'audio/' ) || 1427 type.startsWith( 'video/' ); 1428 } 1430 function parseStructure ( parts, multipartType, inAlternative, 1431 htmlBody, textBody, attachments ) { 1433 // For multipartType == alternative 1434 let textLength = textBody ? textBody.length : -1; 1435 let htmlLength = htmlBody ? htmlBody.length : -1; 1436 for ( let i = 0; i < parts.length; i += 1 ) { 1437 let part = parts[i]; 1438 let isMultipart = part.type.startsWith( 'multipart/' ); 1439 // Is this a body part rather than an attachment 1440 let isInline = part.disposition != "attachment" && 1441 // Must be one of the allowed body types 1442 ( part.type == "text/plain" || 1443 part.type == "text/html" || 1444 isInlineMediaType( part.type ) ) && 1445 // If multipart/related, only the first part can be inline 1446 // If a text part with a filename, and not the first item in the 1447 // multipart, assume it is an attachment 1448 ( i === 0 || 1449 ( multipartType != "related" && 1450 ( isInlineMediaType( part.type ) || !part.name ) ) ); 1452 if ( isMultipart ) { 1453 let subMultiType = part.type.split( '/' )[1]; 1454 parseStructure( part.subParts, subMultiType, 1455 inAlternative || ( subMultiType == 'alternative' ), 1456 htmlBody, textBody, attachments ); 1457 } else if ( isInline ) { 1458 if ( multipartType == 'alternative' ) { 1459 switch ( part.type ) { 1460 case 'text/plain': 1461 textBody.push( part ); 1462 break; 1463 case 'text/html': 1464 htmlBody.push( part ); 1465 break; 1466 default: 1467 attachments.push( part ); 1468 break; 1469 } 1470 continue; 1471 } else if ( inAlternative ) { 1472 if ( part.type == 'text/plain' ) { 1473 htmlBody = null; 1474 } 1475 if ( part.type == 'text/html' ) { 1476 textBody = null; 1477 } 1478 } 1479 if ( textBody ) { 1480 textBody.push( part ); 1481 } 1482 if ( htmlBody ) { 1483 htmlBody.push( part ); 1485 } 1486 if ( ( !textBody || !htmlBody ) && 1487 isInlineMediaType( part.type ) ) { 1488 attachments.push( part ); 1489 } 1490 } else { 1491 attachments.push( part ); 1492 } 1493 } 1495 if ( multipartType == 'alternative' && textBody && htmlBody ) { 1496 // Found HTML part only 1497 if ( textLength == textBody.length && 1498 htmlLength != htmlBody.length ) { 1499 for ( let i = htmlLength; i < htmlBody.length; i += 1 ) { 1500 textBody.push( htmlBody[i] ); 1501 } 1502 } 1503 // Found plain text part only 1504 if ( htmlLength == htmlBody.length && 1505 textLength != textBody.length ) { 1506 for ( let i = textLength; i < textBody.length; i += 1 ) { 1507 htmlBody.push( textBody[i] ); 1508 } 1509 } 1510 } 1511 } 1513 // Usage: 1514 let htmlBody = []; 1515 let textBody = []; 1516 let attachments = []; 1518 parseStructure( [ bodyStructure ], 'mixed', false, 1519 htmlBody, textBody, attachments ); 1521 For instance, consider a message with both text and html versions 1522 that's then gone through a list software manager that attaches a 1523 header/footer. It might have a MIME structure something like: 1525 multipart/mixed 1526 text/plain, content-disposition=inline - A 1527 multipart/mixed 1528 multipart/alternative 1529 multipart/mixed 1530 text/plain, content-disposition=inline - B 1531 image/jpeg, content-disposition=inline - C 1532 text/plain, content-disposition=inline - D 1533 multipart/related 1534 text/html - E 1535 image/jpeg - F 1536 image/jpeg, content-disposition=attachment - G 1537 application/x-excel - H 1538 message/rfc822 - J 1539 text/plain, content-disposition=inline - K 1541 In this case, the above algorithm would decompose this to: 1543 textBody => [ A, B, C, D, K ] 1544 htmlBody => [ A, E, K ] 1545 attachments => [ C, F, G, H, J ] 1547 4.2. Email/get 1549 Standard "/get" method, with the following additional arguments: 1551 o *bodyProperties*: "String[]" A list of properties to fetch for 1552 each EmailBodyPart returned. If omitted, this defaults to: 1554 [ "partId", "blobId", "size", "name", "type", "charset", 1555 "disposition", "cid", "language", "location" ] 1557 o *fetchTextBodyValues*: "Boolean" (default: false) If "true", the 1558 _bodyValues_ property includes any "text/*" part in the "textBody" 1559 property. 1561 o *fetchHTMLBodyValues*: "Boolean" (default: false) If "true", the 1562 _bodyValues_ property includes any "text/*" part in the "htmlBody" 1563 property. 1565 o *fetchAllBodyValues*: "Boolean" (default: false) If "true", the 1566 _bodyValues_ property includes any "text/*" part in the 1567 "bodyStructure" property. 1569 o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than 1570 zero, the _value_ property of any EmailBodyValue object returned 1571 in _bodyValues_ MUST be truncated if necessary so it does not 1572 exceed this number of octets in size. If "0" (the default), no 1573 truncation occurs. The server MUST ensure the truncation results 1574 in valid UTF-8 and does not occur mid-codepoint. If the part is 1575 of type "text/html", the server SHOULD NOT truncate inside an HTML 1576 tag e.g. in the middle of "". There 1577 is no requirement for the truncated form to be a balanced tree or 1578 valid HTML (indeed, the original source may well be neither of 1579 these things). 1581 If the standard _properties_ argument is omitted or "null", the 1582 following default MUST be used instead of "all" properties: 1584 [ "id", "blobId", "threadId", "mailboxIds", "keywords", "size", 1585 "receivedAt", "messageId", "inReplyTo", "references", "sender", "from", 1586 "to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment", 1587 "preview", "bodyValues", "textBody", "htmlBody", "attachments" ] 1589 The following properties are expected to be fast to fetch in a 1590 quality implementation: 1592 o id 1594 o blobId 1596 o threadId 1598 o mailboxIds 1600 o keywords 1602 o size 1604 o receivedAt 1606 o messageId 1608 o inReplyTo 1610 o sender 1612 o from 1614 o to 1616 o cc 1618 o bcc 1620 o replyTo 1621 o subject 1623 o sentAt 1625 o hasAttachment 1627 o preview 1629 Clients SHOULD take care when fetching any other properties, as there 1630 may be significantly longer latency in fetching and returning the 1631 data. 1633 As specified above, parsed forms of headers may only be used on 1634 appropriate header fields. Attempting to fetch a form that is 1635 forbidden (e.g. "header:From:asDate") MUST result in the method call 1636 being rejected with an "invalidArguments" error. 1638 Where a specific header is requested as a property, the 1639 capitalization of the property name in the response MUST be identical 1640 to that used in the request. 1642 4.2.1. Example 1644 Request: 1646 [ "Email/get", { 1647 "ids": [ "f123u456", "f123u457" ], 1648 "properties": [ "threadId", "mailboxIds", "from", "subject", "receivedAt", "header:List-POST:asURLs", "htmlBody", "bodyValues" ], 1649 "bodyProperties": [ "partId", "blobId", "size", "type" ], 1650 "fetchHTMLBodyValues": true, 1651 "maxBodyValueBytes": 256 1652 }, "#1" ] 1654 and response: 1656 [ "Email/get", { 1657 "accountId": "abc", 1658 "state": "41234123231", 1659 "list": [ 1660 { 1661 "id": "f123u457", 1662 "threadId": "ef1314a", 1663 "mailboxIds": { "f123": true }, 1664 "from": [{name: "Joe Bloggs", email: "joe@example.com"}], 1665 "subject": "Dinner on Thursday?", 1666 "receivedAt": "2013-10-13T14:12:00Z", 1667 "header:List-POST:asURLs": [ "mailto:partytime@lists.example.com" ], 1668 "htmlBody": [{ 1669 "partId": "1", 1670 "blobId": "841623871", 1671 "size": 283331, 1672 "type": "text/html" 1673 }, { 1674 "partId": "2", 1675 "blobId": "319437193", 1676 "size": 10343, 1677 "type": "text/plain" 1678 }], 1679 "bodyValues": { 1680 "1": { 1681 "isEncodingProblem": false, 1682 "isTruncated": true, 1683 "value": "

Hello ..." 1684 }, 1685 "2": { 1686 "isEncodingProblem": false, 1687 "isTruncated": false, 1688 "value": "-- \nSent by your friendly mailing list ..." 1689 } 1690 } 1691 } 1692 ], 1693 notFound: [ "f123u456" ] 1694 }, "#1" ] 1696 4.3. Email/changes 1698 Standard "/changes" method. If generating intermediate states for a 1699 large set of changes, it is recommended that newer changes are 1700 returned first, as these are generally of more interest to users. 1702 4.4. Email/query 1704 Standard "/query" method, but with the following additional 1705 arguments: 1707 o *collapseThreads*: "Boolean" (default: false) If "true", emails in 1708 the same thread as a previous email in the list (given the filter 1709 and sort order) will be removed from the list. This means at most 1710 only one email will be included in the list for any given thread. 1712 In quality implementations, the query "total" property is expected to 1713 be fast to calculate when the filter consists solely of a single 1714 "inMailbox" property, as it is the same as the totalEmails or 1715 totalThreads properties (depending on whether collapseThreads is 1716 true) of the associated Mailbox object. 1718 4.4.1. Filtering 1720 A *FilterCondition* object has the following properties, any of which 1721 may be omitted: 1723 o *inMailbox*: "String" A mailbox id. An email must be in this 1724 mailbox to match the condition. 1726 o *inMailboxOtherThan*: "String[]" A list of mailbox ids. An email 1727 must be in at least one mailbox not in this list to match the 1728 condition. This is to allow messages solely in trash/spam to be 1729 easily excluded from a search. 1731 o *before*: "UTCDate" The _receivedAt_ date of the email must be 1732 before this date to match the condition. 1734 o *after*: "UTCDate" The _receivedAt_ date of the email must be on 1735 or after this date to match the condition. 1737 o *minSize*: "PositiveInt" The _size_ of the email in octets must be 1738 equal to or greater than this number to match the condition. 1740 o *maxSize*: "PositiveInt" The size of the email in octets must be 1741 less than this number to match the condition. 1743 o *allInThreadHaveKeyword*: "String" All emails (including this one) 1744 in the same thread as this email must have the given keyword to 1745 match the condition. 1747 o *someInThreadHaveKeyword*: "String" At least one email (possibly 1748 this one) in the same thread as this email must have the given 1749 keyword to match the condition. 1751 o *noneInThreadHaveKeyword*: "String" All emails (including this 1752 one) in the same thread as this email must *not* have the given 1753 keyword to match the condition. 1755 o *hasKeyword*: "String" This email must have the given keyword to 1756 match the condition. 1758 o *notKeyword*: "String" This email must not have the given keyword 1759 to match the condition. 1761 o *hasAttachment*: "Boolean" The "hasAttachment" property of the 1762 email must be identical to the value given to match the condition. 1764 o *text*: "String" Looks for the text in emails. The server SHOULD 1765 look up text in the _from_, _to_, _cc_, _bcc_, _subject_ header 1766 fields of the message, and inside any "text/*" or other body parts 1767 that may be converted to text by the server. The server MAY 1768 extend the search to any additional textual property. 1770 o *from*: "String" Looks for the text in the _From_ header field of 1771 the message. 1773 o *to*: "String" Looks for the text in the _To_ header field of the 1774 message. 1776 o *cc*: "String" Looks for the text in the _Cc_ header field of the 1777 message. 1779 o *bcc*: "String" Looks for the text in the _Bcc_ header field of 1780 the message. 1782 o *subject*: "String" Looks for the text in the _subject_ property 1783 of the email. 1785 o *body*: "String" Looks for the text in one of the body parts of 1786 the email. The server MAY exclude MIME body parts with content 1787 media types other than "text/_" and "message/_" from consideration 1788 in search matching. Care should be taken to match based on the 1789 text content actually presented to an end-user by viewers for that 1790 media type, or otherwise identified as appropriate for search 1791 indexing. Matching document metadata uninteresting to an end-user 1792 (e.g., markup tag and attribute names), is undesirable. 1794 o *header*: "String[]" The array MUST contain either one or two 1795 elements. The first element is the name of the header field to 1796 match against. The second (optional) element is the text to look 1797 for in the header field value. If not supplied, the message 1798 matches simply if it _has_ a header field of the given name. 1800 If zero properties are specified on the FilterCondition, the 1801 condition MUST always evaluate to "true". If multiple properties are 1802 specified, ALL must apply for the condition to be "true" (it is 1803 equivalent to splitting the object into one-property conditions and 1804 making them all the child of an AND filter operator). 1806 The exact semantics for matching "String" fields is *deliberately not 1807 defined* to allow for flexibility in indexing implementation, subject 1808 to the following: 1810 o Any syntactically correct [RFC2047] encoded sections of header 1811 fields with a known encoding SHOULD be decoded before attempting 1812 to match text. 1814 o When searching inside a "text/html" body part, any text considered 1815 markup rather than content SHOULD be ignored, including HTML tags 1816 and most attributes, anything inside the "" tag, CSS and 1817 JavaScript. Attribute content intended for presentation to the 1818 user such as "alt" and "title" SHOULD be considered in the search. 1820 o Text SHOULD be matched in a case-insensitive manner. 1822 o Text contained in either (but matched) single or double quotes 1823 SHOULD be treated as a *phrase search*, that is a match is 1824 required for that exact word or sequence of words, excluding the 1825 surrounding quotation marks. Use "\"", "\'" and "\\" to match a 1826 literal """, "'" and "\" respectively in a phrase. 1828 o Outside of a phrase, white-space SHOULD be treated as dividing 1829 separate tokens that may be searched for separately, but MUST all 1830 be present for the email to match the filter. 1832 o Tokens MAY be matched on a whole-word basis using stemming (so for 1833 example a text search for "bus" would match "buses" but not 1834 "business"). 1836 4.4.2. Sorting 1838 The following properties MUST be supported for sorting: 1840 o *receivedAt* - The _receivedAt_ date as returned in the Email 1841 object. 1843 The following properties SHOULD be supported for sorting: 1845 o *size* - The size as returned in the Email object. 1847 o *from* - This is taken to be either the "name" part, or if 1848 "null"/empty then the "email" part, of the *first* EmailAddress 1849 object in the _from_ property. If still none, consider the value 1850 to be the empty string. 1852 o *to* - This is taken to be either the "name" part, or if 1853 "null"/empty then the "email" part, of the *first* EmailAddress 1854 object in the _to_ property. If still none, consider the value to 1855 be the empty string. 1857 o *subject* - This is taken to be the base subject of the email, as 1858 defined in section 2.1 of [RFC5256]. 1860 o *sentAt* - The _sentAt_ property on the Email object. 1862 o *hasKeyword* - This value MUST be considered "true" if the email 1863 has the keyword given as an additional _keyword_ property on the 1864 _Comparator_ object, or "false" otherwise. 1866 o *allInThreadHaveKeyword* - This value MUST be considered "true" 1867 for the email if *all* of the emails in the same thread 1868 (regardless of mailbox) have the keyword given as an additional 1869 _keyword_ property on the _Comparator_ object. 1871 o *someInThreadHaveKeyword* - This value MUST be considered "true" 1872 for the email if *any* of the emails in the same thread 1873 (regardless of mailbox) have the keyword given as an additional 1874 _keyword_ property on the _Comparator_ object. 1876 The server MAY support sorting based on other properties as well. A 1877 client can discover which properties are supported by inspecting the 1878 server's _capabilities_ object (see section 1). 1880 Example sort: 1882 [{ 1883 "property": "someInThreadHaveKeyword", 1884 "keyword": "$flagged", 1885 "isAscending": false, 1886 }, { 1887 "property": "subject", 1888 "collation": "i;ascii-casemap" 1889 }, { 1890 "property": "receivedAt", 1891 "isAscending": false, 1892 }] 1894 This would sort emails in flagged threads first (the thread is 1895 considered flagged if any email within it is flagged), and then in 1896 subject order, then newest first for messages with the same subject. 1897 If two emails have both identical flagged status, subject and date, 1898 the order is server-dependent but must be stable. 1900 4.4.3. Thread collapsing 1902 When _collapseThreads_ is "true", then after filtering and sorting 1903 the email list, the list is further winnowed by removing any emails 1904 for a thread id that has already been seen (when passing through the 1905 list sequentially). A thread will therefore only appear *once* in 1906 the result, at the position of the first email in the list that 1907 belongs to the thread (given the current sort/filter). 1909 4.4.4. Response 1911 The response has the following additional argument: 1913 o *collapseThreads*: "Boolean" The _collapseThreads_ value that was 1914 used when calculating the email list for this call. 1916 4.5. Email/queryChanges 1918 Standard "/queryChanges" method, with the following additional 1919 arguments: 1921 o *collapseThreads*: "Boolean" (default: false) The 1922 _collapseThreads_ argument that was used with _Email/query_. 1924 The response has the following additional argument: 1926 o *collapseThreads*: "Boolean" The _collapseThreads_ value that was 1927 used when calculating the email list for this call. 1929 4.6. Email/set 1931 Standard "/set" method. The _Email/set_ method encompasses: 1933 o Creating a draft 1935 o Changing the keywords of an email (e.g. unread/flagged status) 1937 o Adding/removing an email to/from mailboxes (moving a message) 1939 o Deleting emails 1940 Due to the format of the Email object, when creating an email there 1941 are a number of ways to specify the same information. To ensure that 1942 the RFC5322 email to create is unambiguous, the following constraints 1943 apply to Email objects submitted for creation: 1945 o The _headers_ property MUST NOT be given, on either the top-level 1946 email or an EmailBodyPart - the client must set each header field 1947 as an individual property. 1949 o There MUST NOT be two properties that represent the same header 1950 field (e.g. "header:from" and "from") within the Email or 1951 particular EmailBodyPart. 1953 o Header fields MUST NOT be specified in parsed forms that are 1954 forbidden for that particular field. 1956 o Header fields beginning "Content-" MUST NOT be specified on the 1957 Email object, only on EmailBodyPart objects. 1959 o If a bodyStructure property is given, there MUST NOT be textBody, 1960 htmlBody or attachments properties. 1962 o If given, the bodyStructure EmailBodyPart MUST NOT contain a 1963 property representing a header field that is already defined on 1964 the top-level Email object. 1966 o If given, textBody MUST contain exactly one body part, of type 1967 "text/plain". 1969 o If given, htmlBody MUST contain exactly one body part, of type 1970 "text/html". 1972 o Within an EmailBodyPart: 1974 * The client may specify a partId OR a blobId but not both. If a 1975 partId is given, this partId MUST be present in the bodyValues 1976 property. 1978 * The charset property MUST be omitted if a partId is given (the 1979 part's content is included in bodyValues and the server may 1980 choose any appropriate encoding). 1982 * The size property MUST be omitted if a partId is given. If a 1983 blobId is given, it may be omitted, but otherwise MUST match 1984 the size of the blob. 1986 * A "Content-Transfer-Encoding" header field MUST NOT be given. 1988 o Within an EmailBodyValue object, isEncodingProblem and isTruncated 1989 MUST be either "false" or omitted. 1991 Creation attempts that violate any of this SHOULD be rejected with an 1992 "invalidProperties" error, however a server MAY choose to modify the 1993 Email (e.g. choose between conflicting headers, use a different 1994 content-encoding etc.) to comply with its requirements instead. 1996 The server MAY also choose to set additional headers. If not 1997 included, the server MUST generate and set a "Message-ID" header 1998 field in conformance with [RFC5322] section 3.6.4, and a "Date" 1999 header field in conformance with section 3.6.1. 2001 The final RFC5322 email generated may be invalid. For example, if it 2002 is a half-finished draft, the "To" field may data that does not 2003 currently conform to the required syntax for this header field. The 2004 message will be checked for strict conformance when submitted for 2005 sending (see the EmailSubmission object description). 2007 Destroying an email removes it from all mailboxes to which it 2008 belonged. To just delete an email to trash, simply change the 2009 "mailboxIds" property so it is now in the mailbox with "role == 2010 "trash"", and remove all other mailbox ids. 2012 When emptying the trash, clients SHOULD NOT destroy emails which are 2013 also in a mailbox other than trash. For those emails, they SHOULD 2014 just remove the Trash mailbox from the email. 2016 For successfully created Email objects, the _created_ response 2017 contains the _id_, _blobId_, _threadId_ and _size_ properties of the 2018 object. 2020 The following extra _SetError_ types are defined: 2022 For *create*: 2024 o "blobNotFound": At least one blob id given for an EmailBodyPart 2025 doesn't exist. An extra _notFound_ property of type "String[]" 2026 MUST be included in the error object containing every _blobId_ 2027 referenced by an EmailBodyPart that could not be found on the 2028 server. 2030 For *create* and *update*: 2032 o "tooManyKeywords": The change to the email's keywords would exceed 2033 a server-defined maximum. 2035 o "tooManyMailboxes": The change to the email's mailboxes would 2036 exceed a server-defined maximum. 2038 4.7. Email/copy 2040 Standard "/copy" method, except only the _mailboxIds_, _keywords_ and 2041 _receivedAt_ properties may be set during the copy. This method 2042 cannot modify the RFC5322 representation of an email. 2044 The server MAY forbid two email objects with the same exact [RFC5322] 2045 content, or even just with the same [RFC5322] Message-ID, to coexist 2046 within an account. If duplicates are allowed though, the "from" 2047 account may be the same as the "to" account to copy emails within an 2048 account. 2050 For successfully copied Email objects, the _created_ response 2051 contains the _id_, _blobId_, _threadId_ and _size_ properties of the 2052 new object. 2054 4.8. Email/import 2056 The _Email/import_ method adds [RFC5322] messages to a user's set of 2057 emails. The messages must first be uploaded as a file using the 2058 standard upload mechanism. It takes the following arguments: 2060 o *accountId*: "String|null" The id of the account to use for this 2061 call. If "null", defaults to the "urn:ietf:params:jmap:mail" 2062 primary account. 2064 o *emails*: "String[EmailImport]" A map of creation id (client 2065 specified) to EmailImport objects 2067 An *EmailImport* object has the following properties: 2069 o *blobId*: "String" The id of the blob containing the raw [RFC5322] 2070 message. 2072 o *mailboxIds*: "String[Boolean]" The ids of the mailboxes to assign 2073 this email to. At least one mailbox MUST be given. 2075 o *keywords*: "String[Boolean]" (default: ) The keywords to apply to 2076 the email. 2078 o *receivedAt*: "UTCDate" (default: time of import on server) The 2079 _receivedAt_ date to set on the email. 2081 Each email to import is considered an atomic unit which may succeed 2082 or fail individually. Importing successfully creates a new email 2083 object from the data referenced by the blobId and applies the given 2084 mailboxes, keywords and receivedAt date. 2086 The server MAY forbid two email objects with the same exact [RFC5322] 2087 content, or even just with the same [RFC5322] Message-ID, to coexist 2088 within an account. In this case, it MUST reject attempts to import 2089 an email considered a duplicate with an "alreadyExists" SetError. An 2090 _emailId_ property of type "String" MUST be included on the error 2091 object with the id of the existing email. 2093 If the _blobId_, _mailboxIds_, or _keywords_ properties are invalid 2094 (e.g. missing, wrong type, id not found), the server MUST reject the 2095 import with an "invalidProperties" SetError. 2097 If the email cannot be imported because it would take the account 2098 over quota, the import should be rejected with a "overQuota" 2099 SetError. 2101 If the blob referenced is not a valid [RFC5322] message, the server 2102 MAY modify the message to fix errors (such as removing NUL octets or 2103 fixing invalid headers). If it does this, the _blobId_ on the 2104 response MUST represent the new representation and therefore be 2105 different to the _blobId_ on the EmailImport object. Alternatively, 2106 the server MAY reject the import with an "invalidEmail" SetError. 2108 The response has the following arguments: 2110 o *accountId*: "String" The id of the account used for this call. 2112 o *created*: "String[Email]" A map of the creation id to an object 2113 containing the _id_, _blobId_, _threadId_ and _size_ properties 2114 for each successfully imported Email. 2116 o *notCreated*: "String[SetError]" A map of creation id to a 2117 SetError object for each Email that failed to be created. The 2118 possible errors are defined above. 2120 4.9. Email/parse 2122 This method allows you to parse blobs as [RFC5322] messages to get 2123 Email objects. This can be used to parse and display attached emails 2124 without having to import them as top-level email objects in the mail 2125 store in their own right. 2127 The following metadata properties on the Email objects will be "null" 2128 if requested: 2130 o id 2131 o mailboxIds 2133 o keywords 2135 o receivedAt 2137 The _threadId_ property of the Email MAY be present if the server can 2138 calculate which thread the Email would be assigned to were it to be 2139 imported. Otherwise, this too is "null" if fetched. 2141 The _Email/parse_ method takes the following arguments: 2143 o *accountId*: "String|null" The id of the Account to use. If 2144 "null", the primary account is used. 2146 o *blobIds*: "String[]" The ids of the blobs to parse. 2148 o *properties*: "String[]" If supplied, only the properties listed 2149 in the array are returned for each Email object. If omitted, 2150 defaults to: [ "messageId", "inReplyTo", "references", "sender", 2151 "from", "to", "cc", "bcc", "replyTo", "subject", "sentAt", 2152 "hasAttachment", "preview", "bodyValues", "textBody", "htmlBody", 2153 "attachments" ] 2155 o *bodyProperties*: "String[]" A list of properties to fetch for 2156 each EmailBodyPart returned. If omitted, defaults to the same 2157 value as the Email/get "bodyProperties" default argument. 2159 o *fetchTextBodyValues*: "Boolean" (default: false) If "true", the 2160 _bodyValues_ property includes any "text/*" part in the "textBody" 2161 property. 2163 o *fetchHTMLBodyValues*: "Boolean" (default: false) If "true", the 2164 _bodyValues_ property includes any "text/*" part in the "htmlBody" 2165 property. 2167 o *fetchAllBodyValues*: "Boolean" (default: false) If "true", the 2168 _bodyValues_ property includes any "text/*" part in the 2169 "bodyStructure" property. 2171 o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than 2172 zero, the _value_ property of any EmailBodyValue object returned 2173 in _bodyValues_ MUST be truncated if necessary so it does not 2174 exceed this number of octets in size. If "0" (the default), no 2175 truncation occurs. The server MUST ensure the truncation results 2176 in valid UTF-8 and does not occur mid-codepoint. If the part is 2177 of type "text/html", the server SHOULD NOT truncate inside an HTML 2178 tag e.g. in the middle of "". There 2179 is no requirement for the truncated form to be a balanced tree or 2180 valid HTML (indeed, the original source may well be neither of 2181 these things). 2183 The response has the following arguments: 2185 o *accountId*: "String" The id of the account used for the call. 2187 o *parsed*: "String[Email]|null" A map of blob id to parsed Email 2188 representation for each successfully parsed blob, or "null" if 2189 none. 2191 o *notParsable*: "String[]|null" A list of ids given that 2192 corresponded to blobs that could not be parsed as emails, or 2193 "null" if none. 2195 o *notFound*: "String[]|null" A list of blob ids given that could 2196 not be found, or "null" if none. 2198 As specified above, parsed forms of headers may only be used on 2199 appropriate header fields. Attempting to fetch a form that is 2200 forbidden (e.g. "header:From:asDate") MUST result in the method call 2201 being rejected with an "invalidArguments" error. 2203 Where a specific header is requested as a property, the 2204 capitalization of the property name in the response MUST be identical 2205 to that used in the request. 2207 4.10. Examples 2209 A client logs in for the first time. It first fetches the set of 2210 mailboxes. Now it will display the inbox to the user, which we will 2211 presume has mailbox id "fb666a55". The inbox may be (very!) large, 2212 but the user's screen is only so big, so the client will just load 2213 the start and then can load in more as necessary. The client sends 2214 this request: 2216 [[ "Email/query",{ 2217 "accountId": "ue150411c", 2218 "filter": { 2219 "inMailbox": "fb666a55" 2220 }, 2221 "sort": [{ 2222 "isAscending": false, 2223 "property": "receivedAt" 2224 }], 2225 "collapseThreads": true, 2226 "position": 0, 2227 "limit": 30, 2228 "calculateTotal": true 2229 }, "0" ], 2230 [ "Email/get", { 2231 "accountId": "ue150411c", 2232 "#ids": { 2233 "resultOf": "0", 2234 "name": "Email/query", 2235 "path": "/ids" 2236 }, 2237 "properties": [ 2238 "threadId" 2239 ] 2240 }, "1" ], 2241 [ "Thread/get", { 2242 "accountId": "ue150411c", 2243 "#ids": { 2244 "resultOf": "1", 2245 "name": "Email/get", 2246 "path": "/list/*/threadId" 2247 } 2248 }, "2" ], 2249 [ "Email/get", { 2250 "accountId": "ue150411c", 2251 "#ids": { 2252 "resultOf": "2", 2253 "name": "Thread/get", 2254 "path": "/list/*/emailIds" 2255 }, 2256 "properties": [ 2257 "threadId", 2258 "mailboxIds", 2259 "keywords", 2260 "hasAttachment", 2261 "from", 2262 "subject", 2263 "receivedAt", 2264 "size", 2265 "preview" 2266 ] 2267 }, "3" ]] 2269 Let's break down the 4 method calls to see what they're doing: 2271 1. This asks the server for the ids of the first 30 Email objects in 2272 the inbox, sorted newest first, ignoring messages from the same 2273 thread as a newer message in the mailbox (i.e. it is the first 30 2274 unique threads). 2276 2. Now we use a backreference to fetch the thread ids for each of 2277 these email ids. 2279 3. Another backreference fetches the Thread object for each of these 2280 thread ids. 2282 4. Finally, we fetch the information we need to display the mailbox 2283 listing ( but no more!) for every message in each of these 30 2284 threads. The client may aggregate this data for display, for 2285 example showing the thread as "flagged" if any of the messages in 2286 it contain the "$flagged" keyword. 2288 The response from the server may look something like this: 2290 [[ "Email/query", { 2291 "accountId": "ue150411c", 2292 "filter": { 2293 "inMailbox": "fb666a55" 2294 }, 2295 "sort": [{ 2296 "property": "receivedAt", 2297 "isAscending": false 2298 }], 2299 "collapseThreads": true, 2300 "queryState": "09aa9a075588-780599:0", 2301 "canCalculateChanges": true, 2302 "position": 0, 2303 "total": 115, 2304 "ids": [ "Ma783e5cdf5f2deffbc97930a", "M9bd17497e2a99cb345fc1d0a", ... ] 2305 }, "0" ], 2306 [ "Email/get", { 2307 "accountId": "ue150411c", 2308 "state": "780599", 2309 "list": [{ 2310 "id": "Ma783e5cdf5f2deffbc97930a", 2311 "threadId": "T36703c2cfe9bd5ed" 2312 }, { 2313 "id": "M9bd17497e2a99cb345fc1d0a" 2314 "threadId": "T0a22ad76e9c097a1", 2315 }, ... ], 2316 "notFound": [] 2317 }, "1" ], 2318 [ "Thread/get", { 2319 "accountId": "ue150411c", 2320 "state": "22a8728b", 2321 "list": [{ 2322 "id": "T36703c2cfe9bd5ed" 2323 "emailIds": [ "Ma783e5cdf5f2deffbc97930a" ], 2325 }, { 2326 "id": "T0a22ad76e9c097a1" 2327 "emailIds": [ "M3b568670a63e5d100f518fa5", "M9bd17497e2a99cb345fc1d0a" ], 2328 }, ... ], 2329 "notFound": [] 2330 }, "2" ], 2331 [ "Email/get", { 2332 "accountId": "ue150411c", 2333 "state": "780599", 2334 "list": [{ 2335 "id": "Ma783e5cdf5f2deffbc97930a" 2336 "threadId": "T36703c2cfe9bd5ed", 2337 "mailboxIds": { 2338 "fb666a55": true 2339 }, 2340 "keywords": { 2341 "$seen": true, 2342 "$flagged": true 2343 }, 2344 "hasAttachment": true, 2345 "from": [{ 2346 "email": "jdoe@example.com", 2347 "name": "Jane Doe" 2348 }], 2349 "subject": "The Big Reveal", 2350 "receivedAt": "2018-06-27T00:20:35Z", 2351 "size": 175047, 2352 "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 ...", 2353 }, 2354 ... 2355 ], 2356 "notFound": [] 2357 }, "3" ]] 2359 Now, on another device the user marks the first message as unread, 2360 sending this API request: 2362 [[ "Email/set", { 2363 "accountId": "ue150411c", 2364 "update": { 2365 "Ma783e5cdf5f2deffbc97930a": { 2366 "keywords/$seen": null 2367 } 2368 } 2369 }, "0" ]] 2371 The server applies this and sends the success response: 2373 [[ "Email/set", { 2374 "accountId": "ue150411c", 2375 "oldState": "780605" 2376 "newState": "780606", 2377 "updated": { 2378 "Ma783e5cdf5f2deffbc97930a": null 2379 }, 2380 ... 2381 }, "0" ]] 2383 The user also deletes a few messages, and then a new message arrives. 2385 Back on our original machine, we receive a push update that the state 2386 string for Email is now "780800". As this does not match the 2387 client's current state, it issues a request for the changes: 2389 [[ "Email/changes", { 2390 "accountId": "ue150411c", 2391 "sinceState": "780605", 2392 "maxChanges": 50 2393 }, "3" ], 2394 [ "Email/queryChanges", { 2395 "accountId": "ue150411c", 2396 "filter": { 2397 "inMailbox": "fb666a55" 2398 }, 2399 "sort": [{ 2400 "property": "receivedAt", 2401 "isAscending": false 2402 }], 2403 "collapseThreads": true, 2404 "sinceQueryState": "09aa9a075588-780599:0", 2405 "upToId": "Mc2781d5e856a908d8a35a564", 2406 "maxChanges": 25, 2407 "calculateTotal": true 2408 }, "11" ]] 2410 The response: 2412 [ "Email/changes", { 2413 "accountId": "ue150411c", 2414 "oldState": "780605", 2415 "newState": "780800", 2416 "hasMoreChanges": false, 2417 "created": [ "Me8de6c9f6de198239b982ea2" ], 2418 "updated": [ "Ma783e5cdf5f2deffbc97930a" ], 2419 "destroyed": [ "M9bd17497e2a99cb345fc1d0a", ... ], 2420 }, "3" ], 2421 [ "Email/queryChanges", { 2422 "accountId": "ue150411c", 2423 "oldQueryState": "09aa9a075588-780599:0" 2424 "newQueryState": "e35e9facf117-780615:0", 2425 "filter": { 2426 "inMailbox": "fb666a55" 2427 }, 2428 "sort": [{ 2429 "property": "receivedAt", 2430 "isAscending": false 2431 }], 2432 "collapseThreads": true, 2433 "upToId": "Mc2781d5e856a908d8a35a564", 2434 "added": [{ 2435 "id": "Me8de6c9f6de198239b982ea2", 2436 "index": 0 2437 }], 2438 "removed": [ "M9bd17497e2a99cb345fc1d0a" ], 2439 "total": 115, 2440 }, "11" ], 2442 The client can update its local cache of the query results by 2443 removing "M9bd17497e2a99cb345fc1d0a" and then splicing in 2444 "Me8de6c9f6de198239b982ea2" at position 0. As it does not have the 2445 data for this new email, it will then fetch it (it also could have 2446 done this in the same request using backreferences). 2448 It knows something has changed about "Ma783e5cdf5f2deffbc97930a", so 2449 it will refetch the mailboxes and keywords (the only mutable 2450 properties) for this email too. 2452 The user composes a new message and saves a draft. The client sends: 2454 [[ "Email/set", { 2455 "accountId": "ue150411c", 2456 "create": { 2457 "k1546": { 2458 "mailboxIds": { 2459 "2ea1ca41b38e": true 2460 }, 2461 "keywords": { 2462 "$seen": true, 2463 "$draft": true 2464 }, 2465 "from": [{ 2466 "name": "Joe Bloggs", 2467 "email": "joe@example.com" 2468 }], 2469 "to": [{ 2470 "name": "John", 2471 "email": "john@example.com" 2472 }], 2473 "subject": "World domination", 2474 "receivedAt": "2018-07-10T01:05:08Z", 2475 "sentAt": "2018-07-10T11:05:08+10:00", 2476 "bodyStructure": { 2477 "type": "multipart/alternative", 2478 "subParts": [{ 2479 "partId": "49db", 2480 "type": "text/html" 2481 }, { 2482 "partId": "bd48", 2483 "type": "text/plain" 2484 }] 2485 }, 2486 "bodyValues": { 2487 "bd48": { 2488 "value": "I have the most brilliant plan. Let me tell you all about it. What we do is, we", 2489 "isTruncated": false 2490 }, 2491 "49db": { 2492 "value": "

", 2493 "isTruncated": false 2494 } 2495 } 2496 } 2497 } 2498 }, "0" ]] 2500 The server creates the message and sends the success response: 2502 [[ "Email/set", { 2503 "accountId": "ue150411c", 2504 "oldState": "780823", 2505 "newState": "780839", 2506 "created": { 2507 "k1546": { 2508 "id": "Md45b47b4877521042cec0938", 2509 "blobId": "Ge8de6c9f6de198239b982ea214e0f3a704e4af74", 2510 "threadId": "Td957e72e89f516dc", 2511 "size": 11721 2512 } 2513 }, 2514 ... 2515 }, "0" ]] 2517 The client moves this draft to a different account. The only way to 2518 do this is via the "/copy" method. It MUST set a new mailboxIds 2519 property, since the current value will not be valid mailbox ids in 2520 the destination account: 2522 [[ "Email/copy", { 2523 "fromAccountId": "ue150411c", 2524 "toAccountId": "6c6c41ac", 2525 "create": { 2526 "k45": { 2527 "id": "Md45b47b4877521042cec0938", 2528 "mailboxIds": { 2529 "75a4c956": true 2530 } 2531 } 2532 }, 2533 "onSuccessDestroyOriginal": true 2534 }, "0" ]] 2536 The server successfully copies the email and deletes the original. 2537 Due to the implicit call to "Email/set", there are two responses to 2538 the single method call, both with the same client id: 2540 [[ "Email/copy", { 2541 "fromAccountId": "ue150411c", 2542 "toAccountId": "6c6c41ac", 2543 "created": { 2544 "k45": { 2545 "id": "M138f9954a5cd2423daeafa55", 2546 "blobId": "G6b9fb047cba722c48c611e79233d057c6b0b74e8", 2547 "threadId": "T2f242ea424a4079a", 2548 "size": 11721 2549 } 2550 }, 2551 "notCreated": null 2552 }, "0" ], 2553 [ "Email/set", { 2554 "accountId": "ue150411c", 2555 "oldState": "780839" 2556 "newState": "780871", 2557 "destroyed": [ "Ma783e5cdf5f2deffbc97930a" ], 2558 ... 2559 }, "0" ]] 2561 5. Search snippets 2563 When doing a search on a "String" property, the client may wish to 2564 show the relevant section of the body that matches the search as a 2565 preview instead of the beginning of the message, and to highlight any 2566 matching terms in both this and the subject of the email. Search 2567 snippets represent this data. 2569 A *SearchSnippet* object has the following properties: 2571 o *emailId*: "String" The email id the snippet applies to. 2573 o *subject*: "String|null" If text from the filter matches the 2574 subject, this is the subject of the email HTML-escaped, with 2575 matching words/phrases wrapped in "" tags. If it 2576 does not match, this is "null". 2578 o *preview*: "String|null" If text from the filter matches the 2579 plain-text or HTML body, this is the relevant section of the body 2580 (converted to plain text if originally HTML), HTML-escaped, with 2581 matching words/phrases wrapped in "" tags. It MUST 2582 NOT be bigger than 255 octets in size. If it does not match, this 2583 is "null". 2585 It is server-defined what is a relevant section of the body for 2586 preview. If the server is unable to determine search snippets, it 2587 MUST return "null" for both the _subject_ and _preview_ roperties. 2589 Note, unlike most data types, a SearchSnippet DOES NOT have a 2590 property called "id". 2592 The following JMAP method is supported: 2594 5.1. SearchSnippet/get 2596 To fetch search snippets, make a call to "SearchSnippet/get". It 2597 takes the following arguments: 2599 o *accountId*: "String|null" The id of the account to use for this 2600 call. If "null", defaults to the "urn:ietf:params:jmap:mail" 2601 primary account. 2603 o *filter*: "FilterOperator|FilterCondition|null" The same filter as 2604 passed to Email/query; see the description of this method for 2605 details. 2607 o *emailIds*: "String[]" The list of ids of emails to fetch the 2608 snippets for. 2610 The response has the following arguments: 2612 o *accountId*: "String" The id of the account used for the call. 2614 o *filter*: "FilterOperator|FilterCondition|null" Echoed back from 2615 the call. 2617 o *list*: "SearchSnippet[]" An array of SearchSnippet objects for 2618 the requested email ids. This may not be in the same order as the 2619 ids that were in the request. 2621 o *notFound*: "String[]|null" An array of email ids requested which 2622 could not be found, or "null" if all ids were found. 2624 Since snippets are only based on immutable properties, there is no 2625 state string or update mechanism needed. 2627 The following standard errors may be returned instead of the 2628 _searchSnippets_ response: 2630 "requestTooLarge": The number of _emailIds_ requested by the client 2631 exceeds the maximum number the server is willing to process in a 2632 single method call. 2634 "unsupportedFilter": The server is unable to process the given 2635 _filter_ for any reason. 2637 5.2. Example 2639 Here we did an Email/query to search for any email in the account 2640 containing the word "foo", now we are fetching the search snippets 2641 for some of the ids that were returned in the results: 2643 [[ "SearchSnippet/get", { 2644 "accountId": "ue150411c", 2645 "filter": { 2646 "text": "foo" 2647 }, 2648 "emailIds": [ 2649 "M44200ec123de277c0c1ce69c", 2650 "M7bcbcb0b58d7729686e83d99", 2651 "M28d12783a0969584b6deaac0", 2652 ... 2653 ] 2654 }, "tag-0" ] 2656 Example response: 2658 [[ "SearchSnippet/get", { 2659 "accountId": "ue150411c", 2660 "filter": { 2661 "text": "foo" 2662 }, 2663 "list": [{ 2664 "emailId": "M44200ec123de277c0c1ce69c" 2665 "subject": null, 2666 "preview": null 2667 }, { 2668 "emailId": "M7bcbcb0b58d7729686e83d99", 2669 "subject": "The Foosball competition", 2670 "preview": "...year the foosball competition will be held in the Stadium de ..." 2671 }, { 2672 "emailId": "M28d12783a0969584b6deaac0", 2673 "subject": null, 2674 "preview": "...mail Foo/changes results often return current-state-minus-1 rather than new..." 2675 }, 2676 ... 2677 ], 2678 "notFound": null 2679 }, "tag-0" ]] 2680 6. Identities 2682 An *Identity* object stores information about an email address (or 2683 domain) the user may send from. It has the following properties: 2685 o *id*: "String" (immutable; server-set) The id of the identity. 2687 o *name*: "String" (default: "") The "From" _name_ the client SHOULD 2688 use when creating a new message from this identity. 2690 o *email*: "String" (immutable) The "From" email address the client 2691 MUST use when creating a new message from this identity. The 2692 value MAY alternatively be of the form "*@example.com", in which 2693 case the client may use any valid email address ending in 2694 "@example.com". 2696 o *replyTo*: "EmailAddress[]|null" (default: null) The Reply-To 2697 value the client SHOULD set when creating a new message from this 2698 identity. 2700 o *bcc*: "EmailAddress[]|null" (default: null) The Bcc value the 2701 client SHOULD set when creating a new message from this identity. 2703 o *textSignature*: "String" (default: "") Signature the client 2704 SHOULD insert into new plain-text messages that will be sent from 2705 this identity. Clients MAY ignore this and/or combine this with a 2706 client-specific signature preference. 2708 o *htmlSignature*: "String" (default: "") Signature the client 2709 SHOULD insert into new HTML messages that will be sent from this 2710 identity. This text MUST be an HTML snippet to be inserted into 2711 the "" section of the new email. Clients MAY ignore 2712 this and/or combine this with a client-specific signature 2713 preference. 2715 o *mayDelete*: "Boolean" (server-set) Is the user allowed to delete 2716 this identity? Servers may wish to set this to "false" for the 2717 user's username or other default address. Attempts to destroy an 2718 identity with "mayDelete: false" will be rejected with a standard 2719 "forbidden" SetError. 2721 See the "Addresses" header form description in the Email object for 2722 the definition of _EmailAddress_. 2724 Multiple identities with the same email address MAY exist, to allow 2725 for different settings the user wants to pick between (for example 2726 with different names/signatures). 2728 The following JMAP methods are supported: 2730 6.1. Identity/get 2732 Standard "/get" method. The _ids_ argument may be "null" to fetch 2733 all at once. 2735 6.2. Identity/changes 2737 Standard "/changes" method. 2739 6.3. Identity/set 2741 Standard "/set" method. The following extra _SetError_ types are 2742 defined: 2744 For *create*: 2746 o "forbiddenFrom": The user is not allowed to send from the address 2747 given as the _email_ property of the identity. 2749 6.4. Example 2751 Request: 2753 [ "Identity/get", {}, "0" ] 2755 with response: 2757 [ "Identity/get", { 2758 "accountId": "acme", 2759 "state": "99401312ae-11-333", 2760 "list": [ 2761 { 2762 "id": "3301-222-11_22AAz", 2763 "name": "Joe Bloggs", 2764 "email": "joe@example.com", 2765 "replyTo": null, 2766 "bcc": [{ 2767 "name": null, 2768 "email": "joe+archive@example.com" 2769 }], 2770 "textSignature": "-- \nJoe Bloggs\nMaster of Email", 2771 "htmlSignature": "
Joe Bloggs
Master of Email
", 2772 "mayDelete": false, 2773 }, 2774 { 2775 "id": "9911312-11_22AAz", 2776 "name": "Joe B", 2777 "email": "joebloggs@example.com", 2778 "replyTo": null, 2779 "bcc": null, 2780 "textSignature": "", 2781 "htmlSignature": "", 2782 "mayDelete": true 2783 } 2784 ], 2785 "notFound": [] 2786 }, "0" ] 2788 7. Email submission 2790 An *EmailSubmission* object represents the submission of an email for 2791 delivery to one or more recipients. It has the following properties: 2793 o *id*: "String" (immutable; server-set) The id of the email 2794 submission. 2796 o *identityId*: "String" (immutable) The id of the identity to 2797 associate with this submission. 2799 o *emailId*: "String" (immutable) The id of the email to send. The 2800 email being sent does not have to be a draft, for example when 2801 "redirecting" an existing email to a different address. 2803 o *threadId*: "String" (immutable; server-set) The thread id of the 2804 email to send. This is set by the server to the _threadId_ 2805 property of the email referenced by the _emailId_. 2807 o *envelope*: "Envelope|null" (immutable; default: null) Information 2808 for use when sending via SMTP. An *Envelope* object has the 2809 following properties: 2811 * *mailFrom*: "Address" The email address to use as the return 2812 address in the SMTP submission, plus any parameters to pass 2813 with the MAIL FROM address. The JMAP server MAY allow the 2814 address to be the empty string. When a JMAP server performs an 2815 SMTP message submission, it MAY use the same id string for the 2816 [RFC3461] ENVID parameter and the EmailSubmission object id. 2817 Servers that do this MAY replace a client-provided value for 2818 ENVID with a server-provided value. 2820 * *rcptTo*: "Address[]" The email addresses to send the message 2821 to, and any RCPT TO parameters to pass with the recipient. 2823 An *Address* object has the following properties: 2825 * *email*: "String" The email address being represented by the 2826 object. This as a "Mailbox" as used in the Reverse-path or 2827 Forward-path of the MAIL FROM or RCPT TO command in [RFC5321]. 2829 * *parameters*: "Object|null" Any parameters to send with the 2830 email (either mail-parameter or rcpt-parameter as appropriate, 2831 as specified in [RFC5321]). If supplied, each key in the 2832 object is a parameter name, and the value either the parameter 2833 value (type "String") or if the parameter does not take a value 2834 then "null". For both name and value, any xtext or unitext 2835 encodings are removed ([RFC3461], [RFC6533]) and JSON string 2836 encoding applied. 2838 If the _envelope_ property is "null" or omitted on creation, the 2839 server MUST generate this from the referenced email as follows: 2841 * *mailFrom*: The email in the _Sender_ header, if present, 2842 otherwise the _From_ header, if present, and no parameters. If 2843 multiple addresses are present in one of these headers, or 2844 there is more than one _Sender_/_From_ header, the server 2845 SHOULD reject the email as invalid but otherwise MUST take the 2846 first address in the last _Sender_/_From_ header in the 2847 [RFC5322] version of the message. If the address found from 2848 this is not allowed by the identity associated with this 2849 submission, the _email_ property from the identity MUST be used 2850 instead. 2852 * *rcptTo*: The deduplicated set of email addresses from the 2853 _To_, _Cc_ and _Bcc_ headers, if present, with no parameters 2854 for any of them. 2856 o *sendAt*: "UTCDate" (immutable; server-set) The date the email 2857 was/will be released for delivery. If the client successfully 2858 used [RFC4865] FUTURERELEASE with the email, this MUST be the time 2859 when the server will release the email; otherwise it MUST be the 2860 time the EmailSubmission was created. 2862 o *undoStatus*: "String" (server-set) This represents whether the 2863 submission may be canceled. This is server set and MUST be one of 2864 the following values: 2866 * "pending": It MAY be possible to cancel this submission. 2868 * "final": The email has been relayed to at least one recipient 2869 in a manner that cannot be recalled. It is no longer possible 2870 to cancel this submission. 2872 * "canceled": The email submission was canceled and will not be 2873 delivered to any recipient. 2875 On systems that do not support unsending, the value of this 2876 property will always be "final". On systems that do support 2877 canceling submission, it will start as "pending", and MAY 2878 transition to "final" when the server knows it definitely cannot 2879 recall the email, but MAY just remain "pending". If in pending 2880 state, a client can attempt to cancel the submission by setting 2881 this property to "canceled"; if the update succeeds, the 2882 submission was successfully canceled and the email has not been 2883 delivered to any of the original recipients. 2885 o *deliveryStatus*: "String[DeliveryStatus]|null" (server-set) This 2886 represents the delivery status for each of the email's recipients, 2887 if known. This property MAY not be supported by all servers, in 2888 which case it will remain "null". Servers that support it SHOULD 2889 update the EmailSubmission object each time the status of any of 2890 the recipients changes, even if some recipients are still being 2891 retried. This value is a map from the email address of each 2892 recipient to a _DeliveryStatus_ object. A *DeliveryStatus* object 2893 has the following properties: 2895 * *smtpReply*: "String" The SMTP reply string returned for this 2896 recipient when the server last tried to relay the email, or in 2897 a later DSN response for the email. This SHOULD be the 2898 response to the RCPT TO stage, unless this was accepted and the 2899 email as a whole rejected at the end of the DATA stage, in 2900 which case the DATA stage reply SHOULD be used instead. Multi- 2901 line SMTP responses should be concatenated to a single string 2902 as follows: 2904 + The hyphen following the SMTP code on all but the last line 2905 is replaced with a space. 2907 + Any prefix in common with the first line is stripped from 2908 lines after the first. 2910 + CRLF is replaced by a space. 2912 For example: 2914 550-5.7.1 Our system has detected that this message is 2915 550 5.7.1 likely spam, sorry. 2917 would become: 2919 550 5.7.1 Our system has detected that this message is likely spam, sorry. 2921 For emails relayed via an alternative to SMTP, the server MAY 2922 generate a synthetic string representing the status instead. 2923 If it does this, the string MUST be of the following form: 2925 + A 3-digit SMTP reply code, as defined in [RFC5321], section 2926 4.2.3. 2928 + Then a single space character. 2930 + Then an SMTP Enhanced Mail System Status Code as defined in 2931 [RFC3463], with a registry defined in [RFC5248]. 2933 + Then a single space character. 2935 + Then an implementation-specific information string with a 2936 human readable explanation of the response. 2938 * *delivered*: "String" Represents whether the email has been 2939 successfully delivered to the recipient. This MUST be one of 2940 the following values: 2942 + "queued": The email is in a local mail queue and status will 2943 change once it exits the local mail queues. The _smtpReply_ 2944 property may still change. 2946 + "yes": The email was successfully delivered to the mailbox 2947 of the recipient. The _smtpReply_ property is final. 2949 + "no": Delivery to the recipient permanently failed. The 2950 _smtpReply_ property is final. 2952 + "unknown": The final delivery status is unknown, (e.g. it 2953 was relayed to an external machine and no further 2954 information is available). The _smtpReply_ property may 2955 still change if a DSN arrives. 2957 Note, successful relaying to an external SMTP server SHOULD NOT 2958 be taken as an indication that the email has successfully 2959 reached the final mailbox. In this case though, the server MAY 2960 receive a DSN response, if requested. If a DSN is received for 2961 the recipient with Action equal to "delivered", as per 2962 [RFC3464] section 2.3.3, then the _delivered_ property SHOULD 2963 be set to "yes"; if the Action equals "failed", the property 2964 SHOULD be set to "no". Receipt of any other DSN SHOULD NOT 2965 affect this property. The server MAY also set this property 2966 based on other feedback channels. 2968 * *displayed*: "String" Represents whether the email has been 2969 displayed to the recipient. This MUST be one of the following 2970 values: 2972 + "unknown": The display status is unknown. This is the 2973 initial value. 2975 + "yes": The recipient's system claims the email content has 2976 been displayed to the recipient. Note, there is no 2977 guarantee that the recipient has noticed, read, or 2978 understood the content. 2980 If an MDN is received for this recipient with Disposition-Type 2981 (as per [RFC3798] section 3.2.6.2) equal to "displayed", this 2982 property SHOULD be set to "yes". The server MAY also set this 2983 property based on other feedback channels. 2985 o *dsnBlobIds*: "String[]" (server-set) A list of blob ids for DSNs 2986 received for this submission, in order of receipt, oldest first. 2988 o *mdnBlobIds*: "String[]" (server-set) A list of blob ids for MDNs 2989 received for this submission, in order of receipt, oldest first. 2991 JMAP servers MAY choose not to expose DSN and MDN responses as Email 2992 objects if they correlate to a EmailSubmission object. It SHOULD 2993 only do this if it exposes them in the _dsnBlobIds_ and _mdnblobIds_ 2994 fields instead, and expects the user to be using clients capable of 2995 fetching and displaying delivery status via the EmailSubmission 2996 object. 2998 For efficiency, a server MAY destroy EmailSubmission objects a 2999 certain amount of time after the email is successfully sent or it has 3000 finished retrying sending the email. For very basic SMTP proxies, 3001 this MAY be immediately after creation, as it has no way to assign a 3002 real id and return the information again if fetched later. 3004 The following JMAP methods are supported: 3006 7.1. EmailSubmission/get 3008 Standard "/get" method. 3010 7.2. EmailSubmission/changes 3012 Standard "/changes" method. 3014 7.3. EmailSubmission/query 3016 Standard "/query" method. 3018 A *FilterCondition* object has the following properties, any of which 3019 may be omitted: 3021 o *emailIds*: "String[]" The EmailSubmission _emailId_ property must 3022 be in this list to match the condition. 3024 o *threadIds*: "String[]" The EmailSubmission _threadId_ property 3025 must be in this list to match the condition. 3027 o *undoStatus*: "String" The EmailSubmission _undoStatus_ property 3028 must be identical to the value given to match the condition. 3030 o *before*: "UTCDate" The _sendAt_ property of the EmailSubmission 3031 object must be before this date to match the condition. 3033 o *after*: "UTCDate" The _sendAt_ property of the EmailSubmission 3034 object must be after this date to match the condition. 3036 A EmailSubmission object matches the filter if and only if all of the 3037 given conditions given match. If zero properties are specified, it 3038 is automatically "true" for all objects. 3040 The following properties MUST be supported for sorting: 3042 o "emailId" 3044 o "threadId" 3045 o "sentAt" 3047 7.4. EmailSubmission/queryChanges 3049 Standard "/queryChanges" method. 3051 7.5. EmailSubmission/set 3053 Standard "/set" method, with the following two extra arguments: 3055 o *onSuccessUpdateEmail*: "String[Email]|null" A map of 3056 _EmailSubmission id_ to an object containing properties to update 3057 on the Email object referenced by the EmailSubmission if the 3058 create/update/destroy succeeds. (For references to 3059 EmailSubmission creations, this is equivalent to a back reference 3060 so the id will be the creation id prefixed with a "#".) 3062 o *onSuccessDestroyEmail*: "String[]|null" A list of 3063 _EmailSubmission ids_ for which the email with the corresponding 3064 emailId should be destroyed if the create/update/destroy succeeds. 3065 (For references to EmailSubmission creations, this is equivalent 3066 to a back reference so the id will be the creation id prefixed 3067 with a "#".) 3069 A single implicit _Email/set_ call MUST be made after all 3070 EmailSubmission create/update/destroy requests have been processed to 3071 perform any changes requested in these two arguments. The response 3072 to this MUST be returned after the _EmailSubmission/set_ response. 3074 An email is sent by creating a EmailSubmission object. When 3075 processing each create, the server must check that the email is 3076 valid, and the user has sufficient authorization to send it. If the 3077 creation succeeds, the email will be sent to the recipients given in 3078 the envelope _rcptTo_ parameter. The server MUST remove any _Bcc_ 3079 header present on the email during delivery. The server MAY add or 3080 remove other headers from the submitted email, or make further 3081 alterations in accordance with the server's policy during delivery. 3083 If the referenced email is destroyed at any point after the 3084 EmailSubmission object is created, this MUST NOT change the behaviour 3085 of the email submission (i.e. it does not cancel a future send). 3087 Similarly, destroying a EmailSubmission object MUST NOT affect the 3088 deliveries it represents. It purely removes the record of the email 3089 submission. The server MAY automatically destroy EmailSubmission 3090 objects after a certain time or in response to other triggers, and 3091 MAY forbid the client from manually destroying EmailSubmission 3092 objects. 3094 The following extra _SetError_ types are defined: 3096 For *create*: 3098 o "emailNotFound" - The _emailId_ is not a valid id for an email in 3099 the account. 3101 o "emailTooLarge" - The email size is larger than the server 3102 supports sending. A _maxSize_ "PositiveInt" property MUST be 3103 present on the SetError specifying the maximum size of an email 3104 that may be sent, in octets. 3106 o "invalidEmail" - The email to be sent is invalid in some way. The 3107 SetError SHOULD contain a property called _properties_ of type 3108 "String[]" that lists *all* the properties of the email that were 3109 invalid. 3111 o "tooManyRecipients" - The envelope (supplied or generated) has 3112 more recipients than the server allows. A _maxRecipients_ 3113 "PositiveInt" property MUST also be present on the SetError 3114 specifying the maximum number of allowed recipients. 3116 o "noRecipients" - The envelope (supplied or generated) does not 3117 have any rcptTo emails. 3119 o "invalidRecipients" - The _rcptTo_ property of the envelope 3120 (supplied or generated) contains at least one rcptTo value which 3121 is not a valid email for sending to. An _invalidRecipients_ 3122 "String[]" property MUST also be present on the SetError, which is 3123 a list of the invalid addresses. 3125 o "forbiddenMailFrom" - The server does not permit the user to send 3126 an email with the [RFC5321] envelope From. 3128 o "forbiddenFrom" - The server does not permit the user to send an 3129 email with the [RFC5322] From header of the email to be sent. 3131 o "forbiddenToSend" - The user does not have permission to send at 3132 all right now for some reason. A _description_ "String" property 3133 MAY be present on the SetError object to display to the user why 3134 they are not permitted. The server MAY choose to localise this 3135 string into the user's preferred language, if known. 3137 For *update*: 3139 o "cannotUnsend": The client attempted to update the _undoStatus_ of 3140 a valid EmailSubmission object from "pending" to "canceled", but 3141 the email cannot be unsent. 3143 7.5.1. Example 3145 The following example presumes a draft of the message to be sent has 3146 already been saved, and its Email id is "M7f6ed5bcfd7e2604d1753f6c". 3147 This call then sends the email immediately, and if successful removes 3148 the draft flag and moves it from the Drafts folder (which has Mailbox 3149 id "7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e") to the Sent folder (which 3150 we presume has Mailbox id "73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6"). 3152 [[ "EmailSubmission/set", { 3153 "accountId": "ue411d190", 3154 "create": { 3155 "k1490": { 3156 "identityId": "64588216", 3157 "emailId": "M7f6ed5bcfd7e2604d1753f6c", 3158 "envelope": { 3159 "mailFrom": { 3160 "email": "john@example.com", 3161 "parameters": null 3162 }, 3163 "rcptTo": [{ 3164 "email": "jane@example.com", 3165 "parameters": null 3166 }, 3167 ... 3168 ] 3169 } 3170 } 3171 }, 3172 "onSuccessUpdateEmail": { 3173 "#k1490": { 3174 "mailboxIds/7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e": null, 3175 "mailboxIds/73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6": true, 3176 "keywords/$draft": null 3177 } 3178 } 3179 }, "0" ]] 3181 A successful response might look like this. Note there are two 3182 responses due to the implicit Email/set call, but both have the same 3183 tag as they are due to the same call in the request: 3185 [[ "EmailSubmission/set", { 3186 "accountId": "ue411d190", 3187 "oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21", 3188 "newState": "355421f6-8aed-4cf4-a0c4-7377e951af36", 3189 "created": { 3190 "k1490": { 3191 "id": "3bab7f9a-623e-4acf-99a5-2e67facb02a0" 3192 } 3193 }, 3194 "notCreated": null, 3195 "updated": null, 3196 "notUpdated": null, 3197 "destroyed": null, 3198 "notDestroyed": null 3199 }, "0" ], 3200 [ "Email/set", { 3201 "accountId": "neilj@fastmail.fm", 3202 "oldState": "778193", 3203 "newState": "778197", 3204 "created": null, 3205 "notCreated": null, 3206 "updated": { 3207 "M7f6ed5bcfd7e2604d1753f6c": null 3208 }, 3209 "notUpdated": null, 3210 "destroyed": null, 3211 "notDestroyed": null 3212 }, "0" ]] 3214 If the email submission was not accepted on the other hand, the 3215 response may look like this: 3217 [[ "EmailSubmission/set", { 3218 "accountId": "ue411d190", 3219 "oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21", 3220 "newState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21", 3221 "created": null, 3222 "notCreated": { 3223 "k1490": { 3224 "type": "tooManyRecipients", 3225 "maxRecipients": 10 3226 } 3227 }, 3228 "updated": null, 3229 "notUpdated": null, 3230 "destroyed": null, 3231 "notDestroyed": null 3232 }, "0" ]] 3234 8. Vacation response 3236 A vacation response automatically sends a reply to messages sent to a 3237 particular account, to inform the original sender that their message 3238 may not be processed for some time. Automated message sending can 3239 produce undesireable behaviour. To avoid this, implementors MUST 3240 follow the recommendations set forth in [RFC3834]. 3242 The *VacationResponse* object represents the state of vacation- 3243 response related settings for an account. It has the following 3244 properties: 3246 o *id*: "String" (immutable) The id of the object. There is only 3247 ever one vacation response object, and its id is ""singleton"". 3249 o *isEnabled*: "Boolean" Should a vacation response be sent if an 3250 email arrives between the _fromDate_ and _toDate_? 3252 o *fromDate*: "UTCDate|null" If _isEnabled_ is "true", the date/time 3253 in UTC after which emails that arrive should receive the user's 3254 vacation response. If "null", the vacation response is effective 3255 immediately. 3257 o *toDate*: "UTCDate|null" If _isEnabled_ is "true", the date/time 3258 in UTC after which emails that arrive should no longer receive the 3259 user's vacation response. If "null", the vacation response is 3260 effective indefinitely. 3262 o *subject*: "String|null" The subject that will be used by the 3263 message sent in response to emails when the vacation response is 3264 enabled. If null, an appropriate subject SHOULD be set by the 3265 server. 3267 o *textBody*: "String|null" The plain text part of the message to 3268 send in response to emails when the vacation response is enabled. 3269 If this is "null", when the vacation message is sent a plain-text 3270 body part SHOULD be generated from the _htmlBody_ but the server 3271 MAY choose to send the response as HTML only. 3273 o *htmlBody*: "String|null" The HTML message to send in response to 3274 emails when the vacation response is enabled. If this is "null", 3275 when the vacation message is sent an HTML body part MAY be 3276 generated from the _textBody_, or the server MAY choose to send 3277 the response as plain-text only. 3279 The following JMAP methods are supported: 3281 8.1. VacationResponse/get 3283 Standard "/get" method. 3285 There MUST only be exactly one VacationResponse object in an account. 3286 It MUST have the id "singleton". 3288 8.2. VacationResponse/set 3290 Standard "/set" method. 3292 9. Security considerations 3294 All security considerations of JMAP {TODO: insert RFC ref} apply to 3295 this specification. 3297 9.1. EmailBodyPart value 3299 Service providers typically perform security filtering on incoming 3300 email and it's important the detection of content-type and charset 3301 for the security filter aligns with the heuristics performed by JMAP 3302 servers. Servers that apply heuristics to determine the content-type 3303 or charset for _EmailBodyValue_ SHOULD document the heuristics and 3304 provide a mechanism to turn them off in the event they are misaligned 3305 with the security filter used at a particular mailbox host. 3307 Automatic conversion of charsets that allow hidden channels for ASCII 3308 text, such as UTF-7, have been problematic for security filters in 3309 the past so server implementations can mitigate this risk by having 3310 such conversions off-by-default and/or separately configurable. 3312 To allow the client to restrict the volume of data it can receive in 3313 response to a request, a maximum length may be requested for the data 3314 returned for a textual body part. However, truncating the data may 3315 change the semantic meaning, for example truncating a URL changes its 3316 location. Servers that scan for links to malicious sites should take 3317 care to either ensure truncation is not at a semantically significant 3318 point, or to rescan the truncated value for malicious content before 3319 returning it. 3321 9.2. HTML email display 3323 HTML message bodies provide richer formatting for emails but present 3324 a number of security challenges, especially when embedded in a 3325 webmail context in combination with interface HTML. Clients that 3326 render HTML email should make careful consideration of the potential 3327 risks, including: 3329 o Embedded JavaScript can rewrite the email to change its content on 3330 subsequent opening, allowing users to be mislead. In webmail 3331 systems, if run in the same origin as the interface it can access 3332 and exfiltrate all private data accessible to the user, including 3333 all other emails and potentially contacts, calendar events, 3334 settings, and credentials. It can also rewrite the interface to 3335 undetectably phish passwords. A compromise is likely to be 3336 persistent, not just for the duration of page load, due to 3337 exfiltration of session credentials or installation of a service 3338 worker that can intercept all subsequent network requests (this 3339 however would only be possible if blob downloads are also 3340 available on the same origin, and the service worker script is 3341 attached to the message). 3343 o HTML documents may load content directly from the internet, rather 3344 than just referencing attached resources. For example you may 3345 have an "" tag with an external "src" attribute. This may 3346 leak to the sender when a message is opened, as well as the IP 3347 address of the recipient. Cookies may also be sent and set by the 3348 server, allowing tracking between different emails and even 3349 website visits and advertising profiles. 3351 o In webmail systems, CSS can break the layout or create phishing 3352 vulnerabilities. For example, the use of "position:fixed" can 3353 allow an email to draw content outside of its normal bounds, 3354 potentially clickjacking a real interface element. 3356 o If in a webmail context and not inside a separate frame, any 3357 styles defined in CSS rules will apply to interface elements as 3358 well if the selector matches, allowing the interface to be 3359 modified. Similarly, any interface styles that match elements in 3360 the email will alter their appearance, potentially breaking the 3361 layout of the email. 3363 o The link text in HTML has no necessary correlation with the actual 3364 target of the link, which can be used to make phishing attacks 3365 more convincing. 3367 o Links opened from an email or embedded external content may leak 3368 private info in the "Referer" header sent by default in most 3369 systems. 3371 o Forms can be used to mimic login boxes, providing a potent 3372 phishing vector if allowed to submit directly from the email 3373 display. 3375 There are a number of ways clients can mitigate these issues, and a 3376 defence-in-depth approach that uses a combination of techniques will 3377 provide the strongest security. 3379 o HTML can be filtered before rendering, stripping potentially 3380 malicious content. Sanitizing HTML correctly is tricky, and 3381 implementers are strongly recommended to use a well-tested library 3382 with a carefully vetted whitelist-only approach. New features 3383 with unexpected security characteristics may be added to HTML 3384 rendering engines in the future; a blacklist approach is likely to 3385 result in security issues. 3387 Subtle differences in parsing of HTML can introduce security flaws: 3388 to filter with 100% accurately you need to use the same parser when 3389 sanitizing that the HTML rendering engine will use. 3391 o Encapsulating the message in an "