idnits 2.17.1 draft-ietf-jmap-mail-08.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 (September 10, 2018) is 2054 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 4040 -- Looks like a reference, but probably isn't: '2' on line 4043 -- Looks like a reference, but probably isn't: '3' on line 4046 -- Looks like a reference, but probably isn't: '4' on line 4048 ** 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: March 14, 2019 September 10, 2018 8 JMAP for Mail 9 draft-ietf-jmap-mail-08 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 March 14, 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 . . . . . . . . . . . 6 56 1.3.3. urn:ietf:params:jmap:vacationresponse . . . . . . . . 6 57 1.4. Data type support in different accounts . . . . . . . . . 7 58 1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 7 59 2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 7 60 2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 11 61 2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 11 62 2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 11 63 2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 12 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. Registration of "inbox" role in . . . . . . . . . . . . 81 123 10.6. JMAP Error Codes registry . . . . . . . . . . . . . . . 81 124 10.6.1. mailboxHasChild . . . . . . . . . . . . . . . . . . 81 125 10.6.2. mailboxHasEmail . . . . . . . . . . . . . . . . . . 81 126 10.6.3. blobNotFound . . . . . . . . . . . . . . . . . . . . 82 127 10.6.4. tooManyKeywords . . . . . . . . . . . . . . . . . . 82 128 10.6.5. tooManyMailboxes . . . . . . . . . . . . . . . . . . 82 129 10.6.6. emailNotFound . . . . . . . . . . . . . . . . . . . 82 130 10.6.7. emailTooLarge . . . . . . . . . . . . . . . . . . . 82 131 10.6.8. invalidEmail . . . . . . . . . . . . . . . . . . . . 83 132 10.6.9. tooManyRecipients . . . . . . . . . . . . . . . . . 83 133 10.6.10. noRecipients . . . . . . . . . . . . . . . . . . . . 83 134 10.6.11. invalidRecipients . . . . . . . . . . . . . . . . . 83 135 10.6.12. forbiddenMailFrom . . . . . . . . . . . . . . . . . 83 136 10.6.13. forbiddenFrom . . . . . . . . . . . . . . . . . . . 84 137 10.6.14. forbiddenToSend . . . . . . . . . . . . . . . . . . 84 138 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 84 139 11.1. Normative References . . . . . . . . . . . . . . . . . . 84 140 11.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 87 141 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 87 143 1. Introduction 145 JMAP is a 146 generic protocol for synchronising data, such as mail, calendars or 147 contacts, between a client and a server. It is optimised for mobile 148 and web environments, and aims to provide a consistent interface to 149 different data types. 151 This specification defines a data model for synchronising mail 152 between a client and a server using JMAP. 154 1.1. Notational conventions 156 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 157 "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 158 document are to be interpreted as described in [RFC2119]. 160 Type signatures, examples and property descriptions in this document 161 follow the conventions established in Section 1.1 of 162 . 164 Object properties may also have a set of attributes defined along 165 with the type signature. These have the following meanings: 167 o *server-set*: Only the server can set the value for this property. 168 The client MUST NOT send this property when creating a new object 169 of this type. 171 o *immutable*: The value MUST NOT change after the object is 172 created. 174 o *default*: (This is followed by a JSON value). The value that 175 will be used for this property if it is omitted in an argument, or 176 when creating a new object of this type. 178 Data types defined in the core specification are used in this 179 document. 181 1.2. Terminology 183 The same terminology is used in this document as in the core JMAP 184 specification. 186 1.3. Additions to the capabilities object 188 The capabilities object is returned as part of the standard JMAP 189 Session object; see the JMAP Core specification. 191 This document defines three additional capability objects. 193 1.3.1. urn:ietf:params:jmap:mail 195 This represents support for the Mailbox, Thread, Email, and 196 SearchSnippet data types and associated API methods. The value of 197 this property is an object which MUST contain the following 198 information on server capabilities: 200 o *maxMailboxesPerEmail*: "PositiveInt|null" The maximum number of 201 mailboxes that can be can assigned to a single email. This MUST 202 be an integer >= 1, or "null" for no limit (or rather, the limit 203 is always the number of mailboxes in the account). 205 o *maxMailboxDepth*: "PositiveInt|null" The maximum depth of the 206 mailbox hierarchy (i.e. one less than the maximum number of 207 ancestors a mailbox may have), or "null" for no limit. 209 o *maxSizeMailboxName*: "PositiveInt" The maximum length, in (UTF-8) 210 octets, allowed for the name of a mailbox. This MUST be >= 255. 212 o *maxSizeAttachmentsPerEmail*: "PositiveInt" The maximum total size 213 of attachments, in octets, allowed for a single email. A server 214 MAY still reject import or creation of emails with a lower 215 attachment size total (for example, if the body includes several 216 megabytes of text, causing the size of the encoded MIME structure 217 to be over some server-defined limit). Note, this limit is for 218 the sum of unencoded attachment sizes. Users are generally not 219 knowledgeable about encoding overhead etc., nor should they need 220 to be, so services marketing and help materials normally tells 221 them the "max size attachments". This is the unencoded size they 222 see on their hard drive, and so this capability matches that and 223 allows the client to consistently enforce what the user 224 understands as the limit. The server may separately have a limit 225 for the total size of the RFC5322 message, which will have 226 attachments Base64 encoded and message headers and bodies too. 227 For example, suppose the server advertises 228 "maxSizeAttachmentsPerEmail: 50000000" (50 MB). The enforced 229 server limit may be for an RFC5322 size of 70000000 octets (70 230 MB). Even with Base64 encoding and a 2 MB HTML body, 50 MB 231 attachments would fit under this limit. 233 o *emailsListSortOptions*: "String[]" A list of all the email 234 properties the server supports for sorting by. This MAY include 235 properties the client does not recognise (for example custom 236 properties specified in a vendor extension). Clients MUST ignore 237 any unknown properties in the list. 239 1.3.2. urn:ietf:params:jmap:submission 241 This represents support for the Identity and MessageSubmission data 242 types and associated API methods. The value of this property is an 243 object which MUST contain the following information on server 244 capabilities: 246 o *maxDelayedSend*: "PositiveInt" The number in seconds of the 247 maximum delay the server supports in sending (see the 248 EmailSubmission object description). This is "0" if the server 249 does not support delayed send. 251 o *submissionExtensions*: "String[String[]]" A JMAP implementation 252 that talks to a Submission [RFC6409] server SHOULD have a 253 configuration setting that allows an administrator to expose a new 254 submission EHLO capability in this field. This allows a JMAP 255 server to gain access to a new submission extension without code 256 changes. By default, the JMAP server should show only known safe- 257 to-expose EHLO capabilities in this field, and hide EHLO 258 capabilities that are only relevant to the JMAP server. Each key 259 in the object is the _ehlo-name_, and the value is a list of 260 _ehlo-args_. Examples of safe-to-expose Submission extensions 261 include: 263 * FUTURERELEASE ([RFC4865]) 265 * SIZE ([RFC1870]) 267 * DSN ([RFC3461]) 269 * DELIVERYBY ([RFC2852]) 271 * MT-PRIORITY ([RFC6710]) 273 A JMAP server MAY advertise an extension and implement the 274 semantics of that extension locally on the JMAP server even if a 275 submission server used by JMAP doesn't implement it. The full 276 IANA registry of submission extensions can be found at 277 280 1.3.3. urn:ietf:params:jmap:vacationresponse 282 This represents support for the VacationResponse data type and 283 associated API methods. The value of this property is an empty 284 object. 286 1.4. Data type support in different accounts 288 The server MUST include the appropriate capability strings in the 289 _hasDataFor_ property of any account in which the user may use the 290 data types represented by that URN. Supported data types may differ 291 between accounts the user has access to. For example, in the user's 292 personal account they may have access to all three sets of data, but 293 in a shared account they may only have data for 294 "urn:ietf:params:jmap:mail". This means they can access 295 Mailbox/Thread/Email data in the shared account but are not allowed 296 to send as that account (and so do not have access to Identity/ 297 MessageSubmission objects) or view/set its vacation response. 299 1.5. Push 301 Servers MUST support the standard JMAP push mechanisms to receive 302 notifications when the state changes for any of the types defined in 303 this specification. 305 In addition, servers MUST support a psuedo-type called 306 "EmailDelivery" in the push mechanisms. The state string for this 307 MUST change whenever a new Email is added to the store, but SHOULD 308 NOT change upon any other change to the Email objects. 310 Clients in battery constrained environments may wish to delay 311 fetching changes initiated by the user, but fetch new messages 312 immediately so they can notify the user. 314 2. Mailboxes 316 A mailbox represents a named set of emails. This is the primary 317 mechanism for organising emails within an account. It is analogous 318 to a folder or a label in other systems. A mailbox may perform a 319 certain role in the system; see below for more details. 321 For compatibility with IMAP, an email MUST belong to one or more 322 mailboxes. The email id does not change if the email changes 323 mailboxes. 325 A *Mailbox* object has the following properties: 327 o *id*: "String" (immutable; server-set) The id of the mailbox. 329 o *name*: "String" User-visible name for the mailbox, e.g. "Inbox". 330 This may be any Net-Unicode string ([RFC5198]) of at least 1 331 character in length, subject to the maximum size given in the 332 capability object. Servers MUST forbid sibling Mailboxes with the 333 same name. Servers MAY reject names that violate server policy 334 (e.g., names containing slash (/) or control characters). 336 o *parentId*: "String|null" (default: null) The mailbox id for the 337 parent of this mailbox, or "null" if this mailbox is at the top 338 level. Mailboxes form acyclic graphs (forests) directed by the 339 child-to-parent relationship. There MUST NOT be a loop. 341 o *role*: "String|null" (default: null) Identifies mailboxes that 342 have a particular common purpose (e.g. the "inbox"), regardless of 343 the _name_ (which may be localised). This value is shared with 344 IMAP (exposed in IMAP via the [RFC6154] SPECIAL-USE extension). 345 However, unlike in IMAP, a mailbox may only have a single role, 346 and no two mailboxes in the same account may have the same role. 347 The value MUST be one of the mailbox attribute names listed in the 348 IANA IMAP Mailbox Name Attributes Registry [1], as established in 349 [RFC8457], converted to lower-case. New roles may be established 350 here in the future. An account is not required to have mailboxes 351 with any particular roles. 353 o *sortOrder*: "PositiveInt" (default: 0) Defines the sort order of 354 mailboxes when presented in the client's UI, so it is consistent 355 between devices. The number MUST be an integer in the range 0 <= 356 sortOrder < 2^31. A mailbox with a lower order should be 357 displayed before a mailbox with a higher order (that has the same 358 parent) in any mailbox listing in the client's UI. Mailboxes with 359 equal order SHOULD be sorted in alphabetical order by name. The 360 sorting SHOULD take into account locale-specific character order 361 convention. 363 o *totalEmails*: "PositiveInt" (server-set) The number of emails in 364 this mailbox. 366 o *unreadEmails*: "PositiveInt" (server-set) The number of emails in 367 this mailbox that have neither the "$seen" keyword nor the 368 "$draft" keyword. 370 o *totalThreads*: "PositiveInt" (server-set) The number of threads 371 where at least one email in the thread is in this mailbox. 373 o *unreadThreads*: "PositiveInt" (server-set) An indication of the 374 number of "unread" threads in the mailbox. This may be presented 375 by the client as a badge or marker associated with the mailbox. 376 For compatibility with existing implementations, the way "unread 377 threads" is determined is not mandated in this document. The 378 simplest solution to implement is simply the number of threads 379 where at least one email in the thread is both in this mailbox and 380 has neither the "$seen" nor "$draft" keywords. However, a quality 381 implementation will make return the number of unread items the 382 user would see if they opened that mailbox. A thread is shown as 383 unread if it contains any unread messages that will be displayed 384 when the thread is opened. Therefore "unreadThreads" should be 385 the number of threads where at least one email in the thread has 386 neither the "$seen" nor the "$draft" keyword AND at least one 387 email in the thread is in this mailbox. Note, the unread email 388 does not need to be the one in this mailbox. In addition, the 389 Trash mailbox (that is a mailbox whose "role" is "trash") is 390 treated specially: 392 1. Emails that are *only* in the Trash (and no other mailbox) are 393 ignored when calculating the "unreadThreads" count of other 394 mailboxes. 396 2. Emails that are *not* in the Trash are ignored when 397 calculating the "unreadThreads" count for the Trash mailbox. 399 The result of this is that emails in the Trash are treated as 400 though they are in a separate thread for the purposes of unread 401 counts. It is expected that clients will hide emails in the Trash 402 when viewing a thread in another mailbox and vice versa. This 403 allows you to delete a single email to the Trash out of a thread. 404 So for example, suppose you have an account where the entire 405 contents is a single thread with 2 emails: an unread email in the 406 Trash and a read email in the Inbox. The "unreadThreads" count 407 would be "1" for the Trash and "0" for the Inbox. 409 o *myRights*: "MailboxRights" (server-set) The set of rights (ACLs) 410 the user has in relation to this mailbox. A _MailboxRights_ 411 object has the following properties: 413 * *mayReadItems*: "Boolean" If true, the user may use this 414 mailbox as part of a filter in a _Email/query_ call and the 415 mailbox may be included in the _mailboxIds_ set of _Email_ 416 objects. If a sub-mailbox is shared but not the parent 417 mailbox, this may be "false". Corresponds to IMAP ACLs "lr". 419 * *mayAddItems*: "Boolean" The user may add mail to this mailbox 420 (by either creating a new email or moving an existing one). 421 Corresponds to IMAP ACL "i". 423 * *mayRemoveItems*: "Boolean" The user may remove mail from this 424 mailbox (by either changing the mailboxes of an email or 425 deleting it). Corresponds to IMAP ACLs "te". 427 * *maySetSeen*: "Boolean" The user may add or remove the "$seen" 428 keyword to/from an email. If an email belongs to multiple 429 mailboxes, the user may only modify "$seen" if *all* of the 430 mailboxes have this permission. Corresponds to IMAP ACL "s". 432 * *maySetKeywords*: "Boolean" The user may add or remove any 433 keyword _other than_ "$seen" to/from an email. If an email 434 belongs to multiple mailboxes, the user may only modify 435 keywords if *all* of the mailboxes have this permission. 436 Corresponds to IMAP ACL "w". 438 * *mayCreateChild*: "Boolean" The user may create a mailbox with 439 this mailbox as its parent. Corresponds to IMAP ACL "k". 441 * *mayRename*: "Boolean" The user may rename the mailbox or make 442 it a child of another mailbox. Corresponds to IMAP ACL "x". 444 * *mayDelete*: "Boolean" The user may delete the mailbox itself. 445 Corresponds to IMAP ACL "x". 447 * *maySubmit*: "Boolean" Messages may be submitted directly to 448 this mailbox. Corresponds to IMAP ACL "p". 450 o *isSubscribed*: "Boolean" Has the user indicated they wish to see 451 this mailbox in their client? This SHOULD default to "false" for 452 mailboxes in shared accounts the user has access to, and "true" 453 for any new mailboxes created by the user themself. This MUST be 454 stored separately per-user where multiple users have access to a 455 shared mailbox. A user may have permission to access a large 456 number of shared accounts, or a shared account with a very large 457 set of mailboxes, but only be interested in the contents of a few 458 of these. Clients may choose only to display mailboxes to the 459 user that have the "isSubscribed" property set to "true", and 460 offer a separate UI to allow the user to see and subscribe/ 461 unsubscribe from the full set of mailboxes. However, clients MAY 462 choose to ignore this property, either entirely, for ease of 463 implementation, or just for the primary account (which is normally 464 the user's own, rather than a shared account). 466 For IMAP compatibility, an email in both the Trash and another 467 mailbox SHOULD be treated by the client as existing in both places 468 (i.e. when emptying the trash, the client SHOULD just remove the 469 Trash mailbox and leave it in the other mailbox). 471 The following JMAP methods are supported: 473 2.1. Mailbox/get 475 Standard "/get" method. The _ids_ argument may be "null" to fetch 476 all at once. 478 2.2. Mailbox/changes 480 Standard "/changes" method, but with one extra argument to the 481 response: 483 o *updatedProperties*: "String[]|null" If only the mailbox counts 484 (unread/total emails/threads) have changed since the old state, 485 this will be the list of properties that may have changed, i.e. 486 "["totalEmails", "unreadEmails", "totalThreads", 487 "unreadThreads"]". If the server is unable to tell if only counts 488 have changed, it MUST just be "null". 490 Since counts frequently change but the rest of the mailboxes state 491 for most use cases changes rarely, the server can help the client 492 optimise data transfer by keeping track of changes to email/thread 493 counts separately to other state changes. The _updatedProperties_ 494 array may be used directly via a result reference in a subsequent 495 Mailbox/get call in a single request. 497 2.3. Mailbox/query 499 Standard "/query" method. 501 A *FilterCondition* object has the following properties, any of which 502 may be omitted: 504 o *parentId*: "String|null" The Mailbox _parentId_ property must 505 match the given value exactly. 507 o *name*: "String" The Mailbox _name_ property contains the given 508 string. 510 o *role*: "String|null" The Mailbox _role_ property must match the 511 given value exactly. 513 o *hasAnyRole*: "Boolean" If "true", a Mailbox matches if it has any 514 non-"null" value for its _role_ property. 516 o *isSubscribed*: "Boolean" The "isSubscribed" property of the 517 mailbox must be identical to the value given to match the 518 condition. 520 A Mailbox object matches the filter if and only if all of the given 521 conditions given match. If zero properties are specified, it is 522 automatically "true" for all objects. 524 The following properties MUST be supported for sorting: 526 o "sortOrder" 528 o "name" 530 o "parent/name": This is a pseudo-property, just for sorting, with 531 the following semantics: if two mailboxes have a common parent, 532 sort them by name. Otherwise, find the nearest ancestors of each 533 that share a common parent and sort by their names instead. (i.e. 534 This sorts the mailbox list in tree order). 536 2.4. Mailbox/queryChanges 538 Standard "/queryChanges" method. 540 2.5. Mailbox/set 542 Standard "/set" method, but with the following additional argument: 544 o *onDestroyRemoveMessages*: "Boolean" (default: false) If "false", 545 attempts to destroy a mailbox that still has any messages in it 546 will be rejected with a "mailboxHasEmail" SetError. If "true", 547 any messages that were in the mailbox will be removed from it, and 548 if in no other mailboxes will be destroyed when the mailbox is 549 destroyed. 551 The following extra _SetError_ types are defined: 553 For *destroy*: 555 o "mailboxHasChild": The mailbox still has at least one child 556 mailbox. The client MUST remove these before it can delete the 557 parent mailbox. 559 o "mailboxHasEmail": The mailbox has at least one message assigned 560 to it and the _onDestroyRemoveMessages_ argument was "false". 562 2.6. Example 564 Fetching all mailboxes in an account: 566 [[ "Mailbox/get", { 567 "accountId": "u33084183", 568 "ids": null 569 }, "0" ]] 571 And response: 573 [[ "Mailbox/get", { 574 "accountId": "u33084183", 575 "state": "78540", 576 "list": [ 577 { 578 "id": "23cfa8094c0f41e6", 579 "name": "Inbox", 580 "parentId": null, 581 "role": "inbox", 582 "sortOrder": 10, 583 "totalEmails": 16307, 584 "unreadEmails": 13905, 585 "totalThreads": 5833, 586 "unreadThreads": 5128, 587 "myRights": { 588 "mayAddItems": true, 589 "mayRename": false, 590 "maySubmit": true, 591 "mayDelete": false, 592 "maySetKeywords": true, 593 "mayRemoveItems": true, 594 "mayCreateChild": true, 595 "maySetSeen": true, 596 "mayReadItems": true 597 }, 598 "isSubscribed": true 599 }, 600 { 601 "id": "674cc24095db49ce", 602 "name": "Important mail", 603 ... 604 } 605 ... 606 ], 607 "notFound": [] 608 }, "0" ]] 610 Now suppose a message is marked read and we get a push update that 611 the Mailbox state has changed. You might fetch the updates like 612 this: 614 [[ "Mailbox/changes", { 615 "accountId": "u33084183", 616 "sinceState": "78540" 617 }, "0" ], 618 [ "Mailbox/get", { 619 "accountId": "u33084183", 620 "#ids": { 621 "resultOf": "0", 622 "name": "Mailbox/changes", 623 "path": "/created" 624 } 625 }, "1" ], 626 [ "Mailbox/get", { 627 "accountId": "u33084183", 628 "#ids": { 629 "resultOf": "0", 630 "name": "Mailbox/changes", 631 "path": "/updated" 632 }, 633 "#properties": { 634 "resultOf": "0", 635 "name": "Mailbox/changes", 636 "path": "/updatedProperties" 637 } 638 }, "2" ]] 640 This fetches the list of ids for created/updated/destroyed mailboxes, 641 then using back references fetches the data for just the created/ 642 updated mailboxes in the same request. The response may look 643 something like this: 645 [[ "Mailbox/changes", { 646 "accountId": "u33084183", 647 "oldState": "78541", 648 "newState": "78542", 649 "hasMoreChanges": false, 650 "updatedProperties": [ 651 "totalEmails", "unreadEmails", 652 "totalThreads", "unreadThreads" 653 ], 654 "created": [], 655 "updated": ["23cfa8094c0f41e6"], 656 "destroyed": [] 657 }, "0" ], 658 [ "Mailbox/get", { 659 "accountId": "u33084183", 660 "state": "78542", 661 "list": [], 662 "notFound": [] 663 }, "1" ], 664 [ "Mailbox/get", { 665 "accountId": "u33084183", 666 "state": "78542", 667 "list": [{ 668 "id": "23cfa8094c0f41e6", 669 "totalEmails": 16307, 670 "unreadEmails": 13903, 671 "totalThreads": 5833, 672 "unreadThreads": 5127 673 }], 674 "notFound": [] 675 }, "2" ]] 677 Here's an example where we try to rename one mailbox and destroy 678 another: 680 [[ "Mailbox/set", { 681 "accountId": "u33084183", 682 "ifInState": "78542", 683 "update": { 684 "674cc24095db49ce": { 685 "name": "Maybe important mail" 686 } 687 }, 688 "destroy": [ "23cfa8094c0f41e6" ] 689 }, "0" ]] 691 Suppose the rename succeeds, but we don't have permission to destroy 692 the mailbox we tried to destroy, we might get back: 694 [[ "Mailbox/set", { 695 "accountId": "u33084183", 696 "oldState": "78542", 697 "newState": "78549", 698 "created": null, 699 "notCreated": null, 700 "updated": { 701 "674cc24095db49ce": null 702 }, 703 "notUpdated": null, 704 "destroyed": null, 705 "notDestroyed": { 706 "23cfa8094c0f41e6": { 707 "type": "forbidden" 708 } 709 } 710 }, "0" ]] 712 3. Threads 714 Replies are grouped together with the original message to form a 715 thread. In JMAP, a thread is simply a flat list of emails, ordered 716 by date. Every email MUST belong to a thread, even if it is the only 717 email in the thread. 719 The exact algorithm for determining whether two emails belong to the 720 same thread is not mandated in this spec to allow for compatibility 721 with different existing systems. For new implementations, it is 722 suggested that two messages belong in the same thread if both of the 723 following conditions apply: 725 1. An identical RFC5322 message id appears in both messages in any 726 of the Message-Id, In-Reply-To and References headers. 728 2. After stripping automatically added prefixes such as "Fwd:", 729 "Re:", "[List-Tag]" etc. and ignoring whitespace, the subjects 730 are the same. This avoids the situation where a person replies 731 to an old message as a convenient way of finding the right 732 recipient to send to, but changes the subject and starts a new 733 conversation. 735 If emails are delivered out of order for some reason, a user may 736 receive two emails in the same thread but without headers that 737 associate them with each other. The arrival of a third email in the 738 thread may provide the missing references to join them all together 739 into a single thread. Since the _threadId_ of an email is immutable, 740 if the server wishes to merge the threads, it MUST handle this by 741 deleting and reinserting (with a new email id) the emails that change 742 threadId. 744 A *Thread* object has the following properties: 746 o *id*: "String" (immutable) The id of the thread. 748 o *emailIds*: "String[]" The ids of the emails in the thread, sorted 749 by the _receivedAt_ date of the email, oldest first. If two 750 emails have an identical date, the sort is server-dependent but 751 MUST be stable (sorting by id is recommended). 753 The following JMAP methods are supported: 755 3.1. Thread/get 757 Standard "/get" method. 759 3.1.1. Example 761 Request: 763 [[ "Thread/get", { 764 "accountId": "acme", 765 "ids": ["f123u4", "f41u44"], 766 }, "#1" ]] 768 with response: 770 [[ "Thread/get", { 771 "accountId": "acme", 772 "state": "f6a7e214", 773 "list": [ 774 { 775 "id": "f123u4", 776 "emailIds": [ "eaa623", "f782cbb"] 777 }, 778 { 779 "id": "f41u44", 780 "emailIds": [ "82cf7bb" ] 781 } 782 ], 783 "notFound": [] 784 }, "#1" ]] 786 3.2. Thread/changes 788 Standard "/changes" method. 790 4. Emails 792 The *Email* object is a representation of an [RFC5322] message, which 793 allows clients to avoid the complexities of MIME parsing, transport 794 encoding and character encoding. 796 4.1. Properties of the Email object 798 Broadly, a message consists of two parts: a list of header fields, 799 then a body. The JMAP Email object provides a way to access the full 800 structure, or to use simplified properties and avoid some complexity 801 if this is sufficient for the client application. 803 While raw headers can be fetched and set, the vast majority of 804 clients should use an appropriate parsed form for each of the headers 805 it wants to process, as this allows it to avoid the complexities of 806 various encodings that are required in a valid RFC5322 message. 808 The body of a message is normally a MIME-encoded set of documents in 809 a tree structure. This may be arbitrarily nested, but the majority 810 of email clients present a flat model of an email body (normally 811 plain text or HTML), with a set of attachments. Flattening the MIME 812 structure to form this model can be difficult, and causes 813 inconsistency between clients. Therefore in addition to the 814 _bodyStructure_ property, which gives the full tree, the Email object 815 contains 3 alternate properties with flat lists of body parts: 817 o _textBody_/_htmlBody_: These provide a list of parts that should 818 be rendered sequentially as the "body" of the message. This is a 819 list rather than a single part as messages may have headers and/or 820 footers appended/prepended as separate parts as they are 821 transmitted, and some clients send text and images, or even videos 822 and sound clips, intended to be displayed inline in the body as 823 multiple parts rather than a single HTML part with referenced 824 images. 826 Because MIME allows for multiple representations of the same data 827 (using "multipart/alternative"), there is a textBody property (which 828 prefers a plain text representation) and an htmlBody property (which 829 prefers an HTML representation) to accommodate the two most common 830 client requirements. The same part may appear in both lists where 831 there is no alternative between the two. 833 o _attachments_: This provides a list of parts that should be 834 presented as "attachments" to the message. Some images may be 835 solely there for embedding within an HTML body part; clients may 836 wish to not present these as attachments in the user interface if 837 they are displaying the HTML with the embedded images directly. 838 Some parts may also be in htmlBody/textBody; again, clients may 839 wish to not present these as attachments in the user interface if 840 rendered as part of the body. 842 The _bodyValues_ property allows for clients to fetch the value of 843 text parts directly without having to do a second request for the 844 blob, and have the server handle decoding the charset into unicode. 845 This data is in a separate property rather than on the EmailBodyPart 846 object to avoid duplication of large amounts of data, as the same 847 part may be included twice if the client fetches more than one of 848 bodyStructure, textBody and htmlBody. 850 Due to the number of properties involved, the set of _Email_ 851 properties is specified over the following three sub-sections. 853 4.1.1. Metadata 855 These properties represent metadata about the [RFC5322] message, and 856 are not derived from parsing the message itself. 858 o *id*: "String" (immutable; server-set) The id of the Email object. 859 Note, this is the JMAP object id, NOT the [RFC5322] Message-ID 860 header field value. 862 o *blobId*: "String" (immutable; server-set) The id representing the 863 raw octets of the [RFC5322] message. This may be used to download 864 the raw original message, or to attach it directly to another 865 Email etc. 867 o *threadId*: "String" (immutable; server-set) The id of the Thread 868 to which this Email belongs. 870 o *mailboxIds*: "String[Boolean]" The set of mailbox ids this email 871 belongs to. An email MUST belong to one or more mailboxes at all 872 times (until it is deleted). The set is represented as an object, 873 with each key being a _Mailbox id_. The value for each key in the 874 object MUST be "true". 876 o *keywords*: "String[Boolean]" (default: ) A set of keywords that 877 apply to the email. The set is represented as an object, with the 878 keys being the _keywords_. The value for each key in the object 879 MUST be "true". Keywords are shared with IMAP. The six system 880 keywords from IMAP are treated specially. The following four 881 keywords have their first character changed from "\" in IMAP to 882 "$" in JMAP and have particular semantic meaning: 884 * "$draft": The email is a draft the user is composing. 886 * "$seen": The email has been read. 888 * "$flagged": The email has been flagged for urgent/special 889 attention. 891 * "$answered": The email has been replied to. 893 The IMAP "\Recent" keyword is not exposed via JMAP. The IMAP 894 "\Deleted" keyword is also not present: IMAP uses a delete+expunge 895 model, which JMAP does not. Any message with the "\Deleted" 896 keyword MUST NOT be visible via JMAP. Users may add arbitrary 897 keywords to an email. For compatibility with IMAP, a keyword is a 898 case-insensitive string of 1-255 characters in the ASCII subset 899 %x21-%x7e (excludes control chars and space), and MUST NOT include 900 any of these characters: "( ) { ] % * " \" Because JSON is case- 901 sensitive, servers MUST return keywords in lower-case. The IANA 902 Keyword Registry [2] as established in [RFC5788] assigns semantic 903 meaning to some other keywords in common use. New keywords may be 904 established here in the future. In particular, note: 906 * "$forwarded": The email has been forwarded. 908 * "$phishing": The email is highly likely to be phishing. 909 Clients SHOULD warn users to take care when viewing this email 910 and disable links and attachments. 912 * "$junk": The email is definitely spam. Clients SHOULD set this 913 flag when users report spam to help train automated spam- 914 detection systems. 916 * "$notjunk": The email is definitely not spam. Clients SHOULD 917 set this flag when users indicate an email is legitimate, to 918 help train automated spam-detection systems. 920 o *size*: "PositiveInt" (immutable; server-set) The size, in octets, 921 of the raw data for the [RFC5322] message (as referenced by the 922 _blobId_, i.e. the number of octets in the file the user would 923 download). 925 o *receivedAt*: "UTCDate" (immutable; default: time of creation on 926 server) The date the email was received by the message store. 927 This is the _internal date_ in IMAP. 929 4.1.2. Header fields parsed forms 931 Header field properties are derived from the [RFC5322] and [RFC6532] 932 message header fields. All header fields may be fetched in a raw 933 form. Some headers may also be fetched in a parsed form. The 934 structured form that may be fetched depends on the header. The 935 following forms are defined: 937 4.1.2.1. Raw 939 Type: "String" 941 The raw octets of the header field value from the first octet 942 following the header field name terminating colon, up to but 943 excluding the header field terminating CRLF. Any standards-compliant 944 message MUST be either ASCII (RFC5322) or UTF-8 (RFC6532), however 945 other encodings exist in the wild. A server MAY use heuristics to 946 determine a charset and decode the octets, or MAY replace any octet 947 or octet run with the high bit set that violates UTF-8 syntax with 948 the unicode replacement character (U+FFFD). Any NUL octet MUST be 949 dropped. 951 4.1.2.2. Text 953 Type: "String" 955 The header field value with: 957 1. White space unfolded (as defined in [RFC5322] section 2.2.3) 959 2. The terminating CRLF at the end of the value removed 961 3. Any SP characters at the beginning of the value removed 963 4. Any syntactically correct [RFC2047] encoded sections with a known 964 character set decoded. Any [RFC2047] encoded NUL octets or 965 control characters are dropped from the decoded value. Any text 966 that looks like [RFC2047] syntax but violates [RFC2047] placement 967 or whitespace rules MUST NOT be decoded. 969 5. Any [RFC6532] UTF-8 values decoded. 971 6. The resulting unicode converted to NFC form. 973 If any decodings fail, the parser SHOULD insert a unicode replacement 974 character (U+FFFD) and attempt to continue as much as possible. 976 To prevent obviously nonsense behaviour, which can lead to 977 interoperability issues, this form may only be fetched or set for the 978 following header fields: 980 o Subject 982 o Comment 984 o List-Id 986 o Any header not defined in [RFC5322] or [RFC2369] 988 4.1.2.3. Addresses 990 Type: "EmailAddress[]" 992 The header is parsed as an "address-list" value, as specified in 993 [RFC5322] section 3.4, into the "EmailAddress[]" type. There is an 994 EmailAddress item for each "mailbox" parsed from the "address-list". 995 Group and comment information is discarded. 997 The *EmailAddress* object has the following properties: 999 o *name*: "String|null" The _display-name_ of the [RFC5322] 1000 _mailbox_, or "null" if none. If this is a _quoted-string_: 1002 1. The surrounding DQUOTE characters are removed. 1004 2. Any _quoted-pair_ is decoded. 1006 3. White-space is unfolded, and then any leading and trailing 1007 white-space is removed. 1009 o *email*: "String" The _addr-spec_ of the [RFC5322] _mailbox_. 1011 Any syntactically correct [RFC2047] encoded sections with a known 1012 encoding MUST be decoded, following the same rules as for the _Text_ 1013 form. Any [RFC6532] UTF-8 values MUST be decoded. 1015 Parsing SHOULD be best-effort in the face of invalid structure to 1016 accommodate invalid messages and semi-complete drafts. EmailAddress 1017 objects MAY have an _email_ property that does not conform to the 1018 _addr-spec_ form (for example, may not contain an @ symbol). 1020 For example, the following "address-list" string: 1022 " James Smythe" , Friends: jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= ; 1023 would be parsed as: 1025 [ 1026 { "name": "James Smythe", "email": "james@example.com" }, 1027 { "name": null, "email": "jane@example.com" }, 1028 { "name": "John Smith", "email": "john@example.com" }, 1029 ] 1031 To prevent obviously nonsense behaviour, which can lead to 1032 interoperability issues, this form may only be fetched or set for the 1033 following header fields: 1035 o From 1037 o Sender 1039 o Reply-To 1041 o To 1043 o Cc 1045 o Bcc 1047 o Resent-From 1049 o Resent-Sender 1051 o Resent-Reply-To 1053 o Resent-To 1055 o Resent-Cc 1057 o Resent-Bcc 1059 o Any header not defined in [RFC5322] or [RFC2369] 1061 4.1.2.4. GroupedAddresses 1063 Type: "EmailAddressGroup[]" 1065 This is similar to the Addresses form but preserves group 1066 information. The header is parsed as an "address-list" value, as 1067 specified in [RFC5322] section 3.4, into the "GroupedAddresses[]" 1068 type. Consecutive mailboxes that are not part of a group are still 1069 collected under an EmailAddressGroup object to provide a uniform 1070 type. 1072 The *EmailAddressGroup* object has the following properties: 1074 o *name*: "String|null" The _display-name_ of the [RFC5322] _group_, 1075 or "null" if the addresses are not part of a group. If this is a 1076 _quoted-string_ it is processed the same as the _name_ in the 1077 _EmailAddress_ type. 1079 o *addresses*: "EmailAddress[]" The _mailbox_es that belong to this 1080 group, represented as EmailAddress objects. 1082 Any syntactically correct [RFC2047] encoded sections with a known 1083 encoding MUST be decoded, following the same rules as for the _Text_ 1084 form. Any [RFC6532] UTF-8 values MUST be decoded. 1086 Parsing SHOULD be best-effort in the face of invalid structure to 1087 accommodate invalid messages and semi-complete drafts. 1089 For example, the following "address-list" string: 1091 " James Smythe" , Friends: jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= ; 1093 would be parsed as: 1095 [ 1096 { "name": null, "addresses": [ 1097 { "name": "James Smythe", "email": "james@example.com" } 1098 ]}, 1099 { "name": "Friends", "addresses": [ 1100 { "name": null, "email": "jane@example.com" }, 1101 { "name": "John Smith", "email": "john@example.com" } 1102 ]} 1103 ] 1105 To prevent obviously nonsense behaviour, which can lead to 1106 interoperability issues, this form may only be fetched or set for the 1107 same header fields as the _Addresses_ form. 1109 4.1.2.5. MessageIds 1111 Type: "String[]|null" 1113 The header is parsed as a list of "msg-id" values, as specified in 1114 [RFC5322] section 3.6.4, into the "String[]" type. CFWS and 1115 surrounding angle brackets ("<>") are removed. If parsing fails, the 1116 value is "null". 1118 To prevent obviously nonsense behaviour, which can lead to 1119 interoperability issues, this form may only be fetched or set for the 1120 following header fields: 1122 o Message-ID 1124 o In-Reply-To 1126 o References 1128 o Resent-Message-ID 1130 o Any header not defined in [RFC5322] or [RFC2369] 1132 4.1.2.6. Date 1134 Type: "Date|null" 1136 The header is parsed as a "date-time" value, as specified in 1137 [RFC5322] section 3.3, into the "Date" type. If parsing fails, the 1138 value is "null". 1140 To prevent obviously nonsense behaviour, which can lead to 1141 interoperability issues, this form may only be fetched or set for the 1142 following header fields: 1144 o Date 1146 o Resent-Date 1148 o Any header not defined in [RFC5322] or [RFC2369] 1150 4.1.2.7. URLs 1152 Type: "String[]|null" 1154 The header is parsed as a list of URLs, as described in [RFC2369], 1155 into the "String[]" type. Values do not include the surrounding 1156 angle brackets or any comments in the header with the URLs. If 1157 parsing fails, the value is "null". 1159 To prevent obviously nonsense behaviour, which can lead to 1160 interoperability issues, this form may only be fetched or set for the 1161 following header fields: 1163 o List-Help 1165 o List-Unsubscribe 1166 o List-Subscribe 1168 o List-Post 1170 o List-Owner 1172 o List-Archive 1174 o Any header not defined in [RFC5322] or [RFC2369] 1176 4.1.3. Header fields properties 1178 The following low-level *Email* property is specified for complete 1179 access to the header data of the message: 1181 o *headers*: "EmailHeader[]" (immutable) This is a list of all 1182 [RFC5322] header fields, in the same order they appear in the 1183 message. An *EmailHeader* object has the following properties: 1185 * *name*: "String" The header _field name_ as defined in 1186 [RFC5322], with the same capitalization that it has in the 1187 message. 1189 * *value*: "String" The header _field value_ as defined in 1190 [RFC5322], in _Raw_ form. 1192 In addition, the client may request/send properties representing 1193 individual header fields of the form: 1195 header:{header-field-name} 1197 Where "{header-field-name}" means any series of one or more printable 1198 ASCII characters (i.e. characters that have values between 33 and 1199 126, inclusive), except colon. The property may also have the 1200 following suffixes: 1202 o *:as{header-form}* This means the value is in a parsed form, where 1203 "{header-form}" is one of the parsed-form names specified above. 1204 If not given, the value is in _Raw_ form. 1206 o *:all* This means the value is an array, with the items 1207 corresponding to each instance of the header field, in the order 1208 they appear in the message. If this suffix is not used, the 1209 result is the value of the *last* instance of the header field 1210 (i.e. identical to the *last* item in the array if :all is used), 1211 or "null" if none. 1213 If both suffixes are used, they MUST be specified in the order above. 1214 Header field names are matched case-insensitively. The value is 1215 typed according to the requested form, or an array of that type if 1216 :all is used. If no header fields exist in the message with the 1217 requested name, the value is "null" if fetching a single instance, or 1218 the empty array if requesting :all. 1220 As a simple example, if the client requests a property called 1221 "header:subject", this means find the _last_ header field in the 1222 message named "subject" (matched case-insensitively) and return the 1223 value in _Raw_ form, or "null" if no header of this name is found. 1225 For a more complex example, consider the client requesting a property 1226 called "header:Resent-To:asAddresses:all". This means: 1228 1. Find _all_ header fields named Resent-To (matched case- 1229 insensitively). 1231 2. For each instance parse the header field value in the _Addresses_ 1232 form. 1234 3. The result is of type "EmailAddress[][]" - each item in the array 1235 corresponds to the parsed value (which is itself an array) of the 1236 Resent-To header field instance. 1238 The following convenience properties are also specified for the 1239 *Email* object: 1241 o *messageId*: "String[]|null" (immutable) The value is identical to 1242 the value of _header:Message-ID:asMessageIds_. For messages 1243 conforming to RFC5322 this will be an array with a single entry. 1245 o *inReplyTo*: "String[]|null" (immutable) The value is identical to 1246 the value of _header:In-Reply-To:asMessageIds_. 1248 o *references*: "String[]|null" (immutable) The value is identical 1249 to the value of _header:References:asMessageIds_. 1251 o *sender*: "EmailAddress[]|null" (immutable) The value is identical 1252 to the value of _header:Sender:asAddresses_. 1254 o *from*: "EmailAddress[]|null" (immutable) The value is identical 1255 to the value of _header:From:asAddresses_. 1257 o *to*: "EmailAddress[]|null" (immutable) The value is identical to 1258 the value of _header:To:asAddresses_. 1260 o *cc*: "EmailAddress[]|null" (immutable) The value is identical to 1261 the value of _header:Cc:asAddresses_. 1263 o *bcc*: "EmailAddress[]|null" (immutable) The value is identical to 1264 the value of _header:Bcc:asAddresses_. 1266 o *replyTo*: "EmailAddress[]|null" (immutable) The value is 1267 identical to the value of _header:Reply-To:asAddresses_. 1269 o *subject*: "String|null" (immutable) The value is identical to the 1270 value of _header:Subject:asText_. 1272 o *sentAt*: "Date|null" (immutable; default on creation: current 1273 server time) The value is identical to the value of 1274 _header:Date:asDate_. 1276 4.1.4. Body parts 1278 These properties are derived from the [RFC5322] message body and its 1279 [RFC2045] MIME entities. 1281 A *EmailBodyPart* object has the following properties: 1283 o *partId*: "String|null" Identifies this part uniquely within the 1284 Email. This is scoped to the _emailId_ and has no meaning outside 1285 of the JMAP Email object representation. This is "null" if, and 1286 only if, the part is of type "multipart/*". 1288 o *blobId*: "String|null" The id representing the raw octets of the 1289 contents of the part after decoding any _Content-Transfer- 1290 Encoding_ (as defined in [RFC2045]), or "null" if, and only if, 1291 the part is of type "multipart/*". Note, two parts may be 1292 transfer-encoded differently but have same the same blob id if 1293 their decoded octets are identical and the server is using a 1294 secure hash of the data for the blob id. 1296 o *size*: "PositiveInt" The size, in octets, of the raw data after 1297 content transfer decoding (as referenced by the _blobId_, i.e. the 1298 number of octets in the file the user would download). 1300 o *headers*: "EmailHeader[]" This is a list of all header fields in 1301 the part, in the order they appear. The values are in _Raw_ form. 1303 o *name*: "String|null" This is the [RFC2231] decoded _filename_ 1304 parameter of the _Content-Disposition_ header field, or (for 1305 compatibility with existing systems) if not present then the 1306 [RFC2047] decoded _name_ parameter of the _Content-Type_ header 1307 field. 1309 o *type*: "String" The value of the _Content-Type_ header field of 1310 the part, if present, otherwise the implicit type as per the MIME 1311 standard ("text/plain", or "message/rfc822" if inside a 1312 "multipart/digest"). CFWS is removed and any parameters are 1313 stripped. 1315 o *charset*: "String|null" The value of the charset parameter of the 1316 _Content-Type_ header field, if present, or "null" if the header 1317 field is present but not of type "text/*". If there is no 1318 _Content-Type_ header field, or it exists and is of type "text/*" 1319 but has no charset parameter, this is the implicit charset as per 1320 the MIME standard: "us-ascii". 1322 o *disposition*: "String|null" The value of the _Content- 1323 Disposition_ header field of the part, if present, otherwise 1324 "null". CFWS is removed and any parameters are stripped. 1326 o *cid*: "String|null" The value of the _Content-Id_ header field of 1327 the part, if present, otherwise "null". CFWS and surrounding 1328 angle brackets ("<>") are removed. This may be used to reference 1329 the content from within an html body part using the "cid:" 1330 protocol. 1332 o *language*: "String[]|null" The list of language tags, as defined 1333 in [RFC3282], in the _Content-Language_ header field of the part, 1334 if present. 1336 o *location*: "String|null" The URI, as defined in [RFC2557], in the 1337 _Content-Location_ header field of the part, if present. 1339 o *subParts*: "EmailBodyPart[]|null" If type is "multipart/*", this 1340 contains the body parts of each child. 1342 In addition, the client may request/send EmailBodyPart properties 1343 representing individual header fields, following the same syntax and 1344 semantics as for the Email object, e.g. "header:Content-Type". 1346 The following *Email* properties are specified for access to the body 1347 data of the message: 1349 o *bodyStructure*: "EmailBodyPart" (immutable) This is the full MIME 1350 structure of the message body, represented as an array of the 1351 message's top-level MIME parts, without recursing into "message/ 1352 rfc822" or "message/global" parts. Note that EmailBodyParts may 1353 have subParts if they are of type "multipart/*". 1355 o *bodyValues*: "String[EmailBodyValue]" (immutable) This is a map 1356 of _partId_ to an *EmailBodyValue* object for none, some or all 1357 "text/*" parts. Which parts are included and whether the value is 1358 truncated is determined by various arguments to _Email/get_ and 1359 _Email/parse_. An *EmailBodyValue* object has the following 1360 properties: 1362 * *value*: "String" The value of the body part after decoding 1363 _Content-Transport-Encoding_ and decoding the _Content-Type_ 1364 charset, if known to the server, and with any CRLF replaced 1365 with a single LF. The server MAY use heuristics to determine 1366 the charset to use for decoding if the charset is unknown, or 1367 if no charset is given, or if it believes the charset given is 1368 incorrect. Decoding is best-effort and SHOULD insert the 1369 unicode replacement character (U+FFFD) and continue when a 1370 malformed section is encountered. Note that due to the charset 1371 decoding and line ending normalisation, the length of this 1372 string will probably not be exactly the same as the _size_ 1373 property on the corresponding EmailBodyPart. 1375 * *isEncodingProblem*: "Boolean" (default: false) This is "true" 1376 if malformed sections were found while decoding the charset, or 1377 the charset was unknown. 1379 * *isTruncated*: "Boolean" (default: false) This is "true" if the 1380 _value_ has been truncated. 1382 See the security considerations section for issues related to 1383 truncation and heuristic determination of content-type and 1384 charset. 1386 o *textBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", 1387 "text/html", "image/*", "audio/*" and/or "video/*" parts to 1388 display (sequentially) as the message body, with a preference for 1389 "text/plain" when alternative versions are available. 1391 o *htmlBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", 1392 "text/html", "image/*", "audio/*" and/or "video/*" parts to 1393 display (sequentially) as the message body, with a preference for 1394 "text/html" when alternative versions are available. 1396 o *attachments*: "EmailBodyPart[]" (immutable) A list of all parts 1397 in _bodyStructure_, traversing depth-first, which satisfy either 1398 of the following conditions: 1400 * not of type "multipart/*" and not included in _textBody_ or 1401 _htmlBody_ 1403 * of type "image/*", "audio/*" or "video/*" and not in both 1404 _textBody_ and _htmlBody_ 1406 None of these parts include subParts, including "message/*" types. 1407 Attached messages may be fetched using the Email/parse method and 1408 the blobId. Note, an HTML body part may reference image parts in 1409 attachments using "cid:" links to reference the _Content-Id_ or by 1410 referencing the _Content-Location_. 1412 o *hasAttachment*: "Boolean" (immutable; server-set) This is "true" 1413 if there are one or more parts in the message that a client UI 1414 should offer as downloadable. A server SHOULD set hasAttachment 1415 if either: 1417 * The _attachments_ list contains at least one item that does not 1418 have "Content-Disposition: inline". The server MAY ignore 1419 parts in this list that are processed automatically in some 1420 way, or are referenced as embedded images in one of the "text/ 1421 html" parts of the message. 1423 The server MAY set hasAttachment based on implementation-defined 1424 or site configurable heuristics. 1426 o *preview*: "String" (immutable; server-set) Up to 255 octets of 1427 plain text, summarising the message body. This is intended to be 1428 shown as a preview line on a mailbox listing, and may be truncated 1429 when shown. The server may choose which part of the message to 1430 include in the preview, for example skipping quoted sections and 1431 salutations and collapsing white-space can result in a more useful 1432 preview. 1434 The exact algorithm for decomposing bodyStructure into textBody, 1435 htmlBody and attachments part lists is not mandated, as this is a 1436 quality-of-service implementation issue and likely to require 1437 workarounds for malformed content discovered over time. However, the 1438 following algorithm (expressed here in JavaScript) is suggested as a 1439 starting point, based on real-world experience: 1441 function isInlineMediaType ( type ) { 1442 return type.startsWith( 'image/' ) || 1443 type.startsWith( 'audio/' ) || 1444 type.startsWith( 'video/' ); 1445 } 1447 function parseStructure ( parts, multipartType, inAlternative, 1448 htmlBody, textBody, attachments ) { 1450 // For multipartType == alternative 1451 let textLength = textBody ? textBody.length : -1; 1452 let htmlLength = htmlBody ? htmlBody.length : -1; 1453 for ( let i = 0; i < parts.length; i += 1 ) { 1454 let part = parts[i]; 1455 let isMultipart = part.type.startsWith( 'multipart/' ); 1456 // Is this a body part rather than an attachment 1457 let isInline = part.disposition != "attachment" && 1458 // Must be one of the allowed body types 1459 ( part.type == "text/plain" || 1460 part.type == "text/html" || 1461 isInlineMediaType( part.type ) ) && 1462 // If multipart/related, only the first part can be inline 1463 // If a text part with a filename, and not the first item in the 1464 // multipart, assume it is an attachment 1465 ( i === 0 || 1466 ( multipartType != "related" && 1467 ( isInlineMediaType( part.type ) || !part.name ) ) ); 1469 if ( isMultipart ) { 1470 let subMultiType = part.type.split( '/' )[1]; 1471 parseStructure( part.subParts, subMultiType, 1472 inAlternative || ( subMultiType == 'alternative' ), 1473 htmlBody, textBody, attachments ); 1474 } else if ( isInline ) { 1475 if ( multipartType == 'alternative' ) { 1476 switch ( part.type ) { 1477 case 'text/plain': 1478 textBody.push( part ); 1479 break; 1480 case 'text/html': 1481 htmlBody.push( part ); 1482 break; 1483 default: 1484 attachments.push( part ); 1485 break; 1486 } 1487 continue; 1488 } else if ( inAlternative ) { 1489 if ( part.type == 'text/plain' ) { 1490 htmlBody = null; 1491 } 1492 if ( part.type == 'text/html' ) { 1493 textBody = null; 1494 } 1495 } 1496 if ( textBody ) { 1497 textBody.push( part ); 1498 } 1499 if ( htmlBody ) { 1500 htmlBody.push( part ); 1502 } 1503 if ( ( !textBody || !htmlBody ) && 1504 isInlineMediaType( part.type ) ) { 1505 attachments.push( part ); 1506 } 1507 } else { 1508 attachments.push( part ); 1509 } 1510 } 1512 if ( multipartType == 'alternative' && textBody && htmlBody ) { 1513 // Found HTML part only 1514 if ( textLength == textBody.length && 1515 htmlLength != htmlBody.length ) { 1516 for ( let i = htmlLength; i < htmlBody.length; i += 1 ) { 1517 textBody.push( htmlBody[i] ); 1518 } 1519 } 1520 // Found plain text part only 1521 if ( htmlLength == htmlBody.length && 1522 textLength != textBody.length ) { 1523 for ( let i = textLength; i < textBody.length; i += 1 ) { 1524 htmlBody.push( textBody[i] ); 1525 } 1526 } 1527 } 1528 } 1530 // Usage: 1531 let htmlBody = []; 1532 let textBody = []; 1533 let attachments = []; 1535 parseStructure( [ bodyStructure ], 'mixed', false, 1536 htmlBody, textBody, attachments ); 1538 For instance, consider a message with both text and html versions 1539 that's then gone through a list software manager that attaches a 1540 header/footer. It might have a MIME structure something like: 1542 multipart/mixed 1543 text/plain, content-disposition=inline - A 1544 multipart/mixed 1545 multipart/alternative 1546 multipart/mixed 1547 text/plain, content-disposition=inline - B 1548 image/jpeg, content-disposition=inline - C 1549 text/plain, content-disposition=inline - D 1550 multipart/related 1551 text/html - E 1552 image/jpeg - F 1553 image/jpeg, content-disposition=attachment - G 1554 application/x-excel - H 1555 message/rfc822 - J 1556 text/plain, content-disposition=inline - K 1558 In this case, the above algorithm would decompose this to: 1560 textBody => [ A, B, C, D, K ] 1561 htmlBody => [ A, E, K ] 1562 attachments => [ C, F, G, H, J ] 1564 4.2. Email/get 1566 Standard "/get" method, with the following additional arguments: 1568 o *bodyProperties*: "String[]" A list of properties to fetch for 1569 each EmailBodyPart returned. If omitted, this defaults to: 1571 [ "partId", "blobId", "size", "name", "type", "charset", 1572 "disposition", "cid", "language", "location" ] 1574 o *fetchTextBodyValues*: "Boolean" (default: false) If "true", the 1575 _bodyValues_ property includes any "text/*" part in the "textBody" 1576 property. 1578 o *fetchHTMLBodyValues*: "Boolean" (default: false) If "true", the 1579 _bodyValues_ property includes any "text/*" part in the "htmlBody" 1580 property. 1582 o *fetchAllBodyValues*: "Boolean" (default: false) If "true", the 1583 _bodyValues_ property includes any "text/*" part in the 1584 "bodyStructure" property. 1586 o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than 1587 zero, the _value_ property of any EmailBodyValue object returned 1588 in _bodyValues_ MUST be truncated if necessary so it does not 1589 exceed this number of octets in size. If "0" (the default), no 1590 truncation occurs. The server MUST ensure the truncation results 1591 in valid UTF-8 and does not occur mid-codepoint. If the part is 1592 of type "text/html", the server SHOULD NOT truncate inside an HTML 1593 tag e.g. in the middle of "". There 1594 is no requirement for the truncated form to be a balanced tree or 1595 valid HTML (indeed, the original source may well be neither of 1596 these things). 1598 If the standard _properties_ argument is omitted or "null", the 1599 following default MUST be used instead of "all" properties: 1601 [ "id", "blobId", "threadId", "mailboxIds", "keywords", "size", 1602 "receivedAt", "messageId", "inReplyTo", "references", "sender", "from", 1603 "to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment", 1604 "preview", "bodyValues", "textBody", "htmlBody", "attachments" ] 1606 The following properties are expected to be fast to fetch in a 1607 quality implementation: 1609 o id 1611 o blobId 1613 o threadId 1615 o mailboxIds 1617 o keywords 1619 o size 1621 o receivedAt 1623 o messageId 1625 o inReplyTo 1627 o sender 1629 o from 1631 o to 1633 o cc 1635 o bcc 1637 o replyTo 1638 o subject 1640 o sentAt 1642 o hasAttachment 1644 o preview 1646 Clients SHOULD take care when fetching any other properties, as there 1647 may be significantly longer latency in fetching and returning the 1648 data. 1650 As specified above, parsed forms of headers may only be used on 1651 appropriate header fields. Attempting to fetch a form that is 1652 forbidden (e.g. "header:From:asDate") MUST result in the method call 1653 being rejected with an "invalidArguments" error. 1655 Where a specific header is requested as a property, the 1656 capitalization of the property name in the response MUST be identical 1657 to that used in the request. 1659 4.2.1. Example 1661 Request: 1663 [ "Email/get", { 1664 "ids": [ "f123u456", "f123u457" ], 1665 "properties": [ "threadId", "mailboxIds", "from", "subject", "receivedAt", "header:List-POST:asURLs", "htmlBody", "bodyValues" ], 1666 "bodyProperties": [ "partId", "blobId", "size", "type" ], 1667 "fetchHTMLBodyValues": true, 1668 "maxBodyValueBytes": 256 1669 }, "#1" ] 1671 and response: 1673 [ "Email/get", { 1674 "accountId": "abc", 1675 "state": "41234123231", 1676 "list": [ 1677 { 1678 "id": "f123u457", 1679 "threadId": "ef1314a", 1680 "mailboxIds": { "f123": true }, 1681 "from": [{name: "Joe Bloggs", email: "joe@example.com"}], 1682 "subject": "Dinner on Thursday?", 1683 "receivedAt": "2013-10-13T14:12:00Z", 1684 "header:List-POST:asURLs": [ "mailto:partytime@lists.example.com" ], 1685 "htmlBody": [{ 1686 "partId": "1", 1687 "blobId": "841623871", 1688 "size": 283331, 1689 "type": "text/html" 1690 }, { 1691 "partId": "2", 1692 "blobId": "319437193", 1693 "size": 10343, 1694 "type": "text/plain" 1695 }], 1696 "bodyValues": { 1697 "1": { 1698 "isEncodingProblem": false, 1699 "isTruncated": true, 1700 "value": "

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

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