idnits 2.17.1 draft-ietf-jmap-mail-12.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 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 (December 3, 2018) is 1969 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 4028 -- Looks like a reference, but probably isn't: '2' on line 4031 -- Looks like a reference, but probably isn't: '3' on line 4034 -- Looks like a reference, but probably isn't: '4' on line 4036 Summary: 0 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: June 6, 2019 December 3, 2018 8 JMAP for Mail 9 draft-ietf-jmap-mail-12 11 Abstract 13 This document specifies a data model for synchronising email data 14 with a server using JMAP. Clients can use this to efficiently 15 search, access, organise and send messages, and get pushed 16 notifications for fast resynchronisation when new messages are 17 delivered or a change is made in another client. 19 Status of This Memo 21 This Internet-Draft is submitted in full conformance with the 22 provisions of BCP 78 and BCP 79. 24 Internet-Drafts are working documents of the Internet Engineering 25 Task Force (IETF). Note that other groups may also distribute 26 working documents as Internet-Drafts. The list of current Internet- 27 Drafts is at https://datatracker.ietf.org/drafts/current/. 29 Internet-Drafts are draft documents valid for a maximum of six months 30 and may be updated, replaced, or obsoleted by other documents at any 31 time. It is inappropriate to use Internet-Drafts as reference 32 material or to cite them other than as "work in progress." 34 This Internet-Draft will expire on June 6, 2019. 36 Copyright Notice 38 Copyright (c) 2018 IETF Trust and the persons identified as the 39 document authors. All rights reserved. 41 This document is subject to BCP 78 and the IETF Trust's Legal 42 Provisions Relating to IETF Documents 43 (https://trustee.ietf.org/license-info) in effect on the date of 44 publication of this document. Please review these documents 45 carefully, as they describe your rights and restrictions with respect 46 to this document. Code Components extracted from this document must 47 include Simplified BSD License text as described in Section 4.e of 48 the Trust Legal Provisions and are provided without warranty as 49 described in the Simplified BSD License. 51 Table of Contents 53 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4 54 1.1. Notational conventions . . . . . . . . . . . . . . . . . 4 55 1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4 56 1.3. Additions to the capabilities object . . . . . . . . . . 4 57 1.3.1. urn:ietf:params:jmap:mail . . . . . . . . . . . . . . 4 58 1.3.2. urn:ietf:params:jmap:submission . . . . . . . . . . . 5 59 1.3.3. urn:ietf:params:jmap:vacationresponse . . . . . . . . 6 60 1.4. Data type support in different accounts . . . . . . . . . 6 61 1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 6 62 1.6. Ids . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 63 2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 7 64 2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 10 65 2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 10 66 2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 11 67 2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 12 68 2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 12 69 2.6. Example . . . . . . . . . . . . . . . . . . . . . . . . . 12 70 3. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 71 3.1. Thread/get . . . . . . . . . . . . . . . . . . . . . . . 17 72 3.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 17 73 3.2. Thread/changes . . . . . . . . . . . . . . . . . . . . . 17 74 4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 75 4.1. Properties of the Email object . . . . . . . . . . . . . 18 76 4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 19 77 4.1.2. Header fields parsed forms . . . . . . . . . . . . . 21 78 4.1.3. Header fields properties . . . . . . . . . . . . . . 26 79 4.1.4. Body parts . . . . . . . . . . . . . . . . . . . . . 28 80 4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 34 81 4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 36 82 4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 37 83 4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 38 84 4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 38 85 4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 40 86 4.4.3. Thread collapsing . . . . . . . . . . . . . . . . . . 42 87 4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 42 88 4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 42 89 4.7. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 45 90 4.8. Email/import . . . . . . . . . . . . . . . . . . . . . . 45 91 4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 47 92 4.10. Examples . . . . . . . . . . . . . . . . . . . . . . . . 49 93 5. Search snippets . . . . . . . . . . . . . . . . . . . . . . . 56 94 5.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 57 95 5.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 58 97 6. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 59 98 6.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 60 99 6.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 60 100 6.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 60 101 6.4. Example . . . . . . . . . . . . . . . . . . . . . . . . . 60 102 7. Email submission . . . . . . . . . . . . . . . . . . . . . . 61 103 7.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 66 104 7.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 66 105 7.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 66 106 7.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 67 107 7.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 67 108 7.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 69 109 8. Vacation response . . . . . . . . . . . . . . . . . . . . . . 70 110 8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 71 111 8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 71 112 9. Security considerations . . . . . . . . . . . . . . . . . . . 72 113 9.1. EmailBodyPart value . . . . . . . . . . . . . . . . . . . 72 114 9.2. HTML email display . . . . . . . . . . . . . . . . . . . 72 115 9.3. Email submission . . . . . . . . . . . . . . . . . . . . 74 116 10. IANA considerations . . . . . . . . . . . . . . . . . . . . . 75 117 10.1. JMAP capability registration for "mail" . . . . . . . . 75 118 10.2. JMAP capability registration for "submission" . . . . . 75 119 10.3. JMAP capability registration for "vacationresponse" . . 76 120 10.4. IMAP and JMAP keywords registry . . . . . . . . . . . . 76 121 10.4.1. Registration of JMAP keyword '$draft' . . . . . . . 77 122 10.4.2. Registration of JMAP keyword '$seen' . . . . . . . . 78 123 10.4.3. Registration of JMAP keyword '$flagged' . . . . . . 78 124 10.4.4. Registration of JMAP keyword '$answered' . . . . . . 79 125 10.4.5. Registration of '$recent' keyword . . . . . . . . . 80 126 10.5. Registration of "inbox" role in . . . . . . . . . . . . 81 127 10.6. JMAP Error Codes registry . . . . . . . . . . . . . . . 81 128 10.6.1. mailboxHasChild . . . . . . . . . . . . . . . . . . 81 129 10.6.2. mailboxHasEmail . . . . . . . . . . . . . . . . . . 81 130 10.6.3. blobNotFound . . . . . . . . . . . . . . . . . . . . 81 131 10.6.4. tooManyKeywords . . . . . . . . . . . . . . . . . . 82 132 10.6.5. tooManyMailboxes . . . . . . . . . . . . . . . . . . 82 133 10.6.6. invalidEmail . . . . . . . . . . . . . . . . . . . . 82 134 10.6.7. tooManyRecipients . . . . . . . . . . . . . . . . . 82 135 10.6.8. noRecipients . . . . . . . . . . . . . . . . . . . . 82 136 10.6.9. invalidRecipients . . . . . . . . . . . . . . . . . 83 137 10.6.10. forbiddenMailFrom . . . . . . . . . . . . . . . . . 83 138 10.6.11. forbiddenFrom . . . . . . . . . . . . . . . . . . . 83 139 10.6.12. forbiddenToSend . . . . . . . . . . . . . . . . . . 83 140 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 83 141 11.1. Normative References . . . . . . . . . . . . . . . . . . 84 142 11.2. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 87 143 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 87 145 1. Introduction 147 JMAP (RFC XXXX) is a generic protocol for synchronising data, such as 148 mail, calendars or contacts, between a client and a server. It is 149 optimised for mobile and web environments, and aims to provide a 150 consistent interface to different data types. 152 This specification defines a data model for mail over 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 RFC XXXX. Data 162 types defined in the core specification are also used in this 163 document. 165 1.2. Terminology 167 The same terminology is used in this document as in the core JMAP 168 specification. 170 1.3. Additions to the capabilities object 172 The capabilities object is returned as part of the standard JMAP 173 Session object; see RFC XXXX, section 2. 175 This document defines three additional capability URIs. 177 1.3.1. urn:ietf:params:jmap:mail 179 This represents support for the Mailbox, Thread, Email, and 180 SearchSnippet data types and associated API methods. The value of 181 this property is an object which MUST contain the following 182 information on server capabilities: 184 o *maxMailboxesPerEmail*: "PositiveInt|null" The maximum number of 185 mailboxes that can be can assigned to a single email. This MUST 186 be an integer >= 1, or "null" for no limit (or rather, the limit 187 is always the number of mailboxes in the account). 189 o *maxMailboxDepth*: "PositiveInt|null" The maximum depth of the 190 mailbox hierarchy (i.e. one more than the maximum number of 191 ancestors a mailbox may have), or "null" for no limit. 193 o *maxSizeMailboxName*: "PositiveInt" The maximum length, in (UTF-8) 194 octets, allowed for the name of a mailbox. This MUST be at least 195 100, although it is recommended servers allow more. 197 o *maxSizeAttachmentsPerEmail*: "PositiveInt" The maximum total size 198 of attachments, in octets, allowed for a single email. A server 199 MAY still reject import or creation of emails with a lower 200 attachment size total (for example, if the body includes several 201 megabytes of text, causing the size of the encoded MIME structure 202 to be over some server-defined limit). Note, this limit is for 203 the sum of unencoded attachment sizes. Users are generally not 204 knowledgeable about encoding overhead etc., nor should they need 205 to be, so marketing and help materials normally tell them the "max 206 size attachments". This is the unencoded size they see on their 207 hard drive, and so this capability matches that and allows the 208 client to consistently enforce what the user understands as the 209 limit. The server may separately have a limit for the total size 210 of the RFC5322 message, which will have attachments Base64 encoded 211 and message headers and bodies too. For example, suppose the 212 server advertises "maxSizeAttachmentsPerEmail: 50000000" (50 MB). 213 The enforced server limit may be for an RFC5322 size of 70000000 214 octets (70 MB). Even with Base64 encoding and a 2 MB HTML body, 215 50 MB attachments would fit under this limit. 217 o *emailsListSortOptions*: "String[]" A list of all the email 218 properties the server supports sorting by. This MAY include 219 properties the client does not recognise (for example custom 220 properties specified in a vendor extension). Clients MUST ignore 221 any unknown properties in the list. 223 1.3.2. urn:ietf:params:jmap:submission 225 This represents support for the Identity and MessageSubmission data 226 types and associated API methods. The value of this property is an 227 object which MUST contain the following information on server 228 capabilities: 230 o *maxDelayedSend*: "PositiveInt" The number in seconds of the 231 maximum delay the server supports in sending (see the 232 EmailSubmission object description). This is "0" if the server 233 does not support delayed send. 235 o *submissionExtensions*: "String[String[]]" A JMAP implementation 236 that talks to a Submission [RFC6409] server SHOULD have a 237 configuration setting that allows an administrator to expose a new 238 submission EHLO capability in this field. This allows a JMAP 239 server to gain access to a new submission extension without code 240 changes. By default, the JMAP server should hide EHLO 241 capabilities that are to do with the transport mechanism and thus 242 are only relevant to the JMAP server (for example PIPELINING, 243 CHUNKING, or STARTTLS). Each key in the object is the _ehlo- 244 name_, and the value is a list of _ehlo-args_. Examples of 245 Submission extensions to include: 247 * FUTURERELEASE ([RFC4865]) 249 * SIZE ([RFC1870]) 251 * DSN ([RFC3461]) 253 * DELIVERYBY ([RFC2852]) 255 * MT-PRIORITY ([RFC6710]) 257 A JMAP server MAY advertise an extension and implement the 258 semantics of that extension locally on the JMAP server even if a 259 submission server used by JMAP doesn't implement it. The full 260 IANA registry of submission extensions can be found at 261 . 264 1.3.3. urn:ietf:params:jmap:vacationresponse 266 This represents support for the VacationResponse data type and 267 associated API methods. The value of this property is an empty 268 object. 270 1.4. Data type support in different accounts 272 The server MUST include the appropriate capability strings in the 273 _hasDataFor_ property of any account with which the user may use the 274 data types represented by that URI. Supported data types may differ 275 between accounts the user has access to. For example, in the user's 276 personal account they may have access to all three sets of data, but 277 in a shared account they may only have data for 278 "urn:ietf:params:jmap:mail". This means they can access 279 Mailbox/Thread/Email data in the shared account but are not allowed 280 to send as that account (and so do not have access to Identity/ 281 MessageSubmission objects) or view/set its vacation response. 283 1.5. Push 285 Servers MUST support the standard JMAP push mechanisms to receive 286 notifications when the state changes for any of the types defined in 287 this specification. 289 In addition, servers MUST support a psuedo-type called 290 "EmailDelivery" in the push mechanisms. The state string for this 291 MUST change whenever a new Email is added to the store, but SHOULD 292 NOT change upon any other change to the Email objects. 294 Clients in battery constrained environments may wish to delay 295 fetching changes initiated by the user, but fetch new messages 296 immediately so they can notify the user. 298 1.6. Ids 300 If a JMAP Mail server also provides an IMAP interface to the data and 301 supports [RFC8474] IMAP Extension for Object Identifiers, the ids 302 SHOULD be the same for mailbox, thread, and email objects in JMAP. 304 2. Mailboxes 306 A mailbox represents a named set of emails. This is the primary 307 mechanism for organising emails within an account. It is analogous 308 to a folder or a label in other systems. A mailbox may perform a 309 certain role in the system; see below for more details. 311 For compatibility with IMAP, an email MUST belong to one or more 312 mailboxes. The email id does not change if the email changes 313 mailboxes. 315 A *Mailbox* object has the following properties: 317 o *id*: "Id" (immutable; server-set) The id of the mailbox. 319 o *name*: "String" User-visible name for the mailbox, e.g. "Inbox". 320 This may be any Net-Unicode string ([RFC5198]) of at least 1 321 character in length, subject to the maximum size given in the 322 capability object. Servers MUST forbid sibling Mailboxes with the 323 same name. Servers MAY reject names that violate server policy 324 (e.g., names containing slash (/) or control characters). 326 o *parentId*: "Id|null" (default: null) The mailbox id for the 327 parent of this mailbox, or "null" if this mailbox is at the top 328 level. Mailboxes form acyclic graphs (forests) directed by the 329 child-to-parent relationship. There MUST NOT be a loop. 331 o *role*: "String|null" (default: null) Identifies mailboxes that 332 have a particular common purpose (e.g. the "inbox"), regardless of 333 the _name_ (which may be localised). This value is shared with 334 IMAP (exposed in IMAP via the [RFC6154] SPECIAL-USE extension). 335 However, unlike in IMAP, a mailbox may only have a single role, 336 and no two mailboxes in the same account may have the same role. 338 The value MUST be one of the mailbox attribute names listed in the 339 IANA IMAP Mailbox Name Attributes Registry [1], as established in 340 [RFC8457], converted to lower-case. New roles may be established 341 here in the future. An account is not required to have mailboxes 342 with any particular roles. 344 o *sortOrder*: "PositiveInt" (default: 0) Defines the sort order of 345 mailboxes when presented in the client's UI, so it is consistent 346 between devices. The number MUST be an integer in the range 0 <= 347 sortOrder < 2^31. A mailbox with a lower order should be 348 displayed before a mailbox with a higher order (that has the same 349 parent) in any mailbox listing in the client's UI. Mailboxes with 350 equal order SHOULD be sorted in alphabetical order by name. The 351 sorting SHOULD take into account locale-specific character order 352 convention. 354 o *totalEmails*: "PositiveInt" (server-set) The number of emails in 355 this mailbox. 357 o *unreadEmails*: "PositiveInt" (server-set) The number of emails in 358 this mailbox that have neither the "$seen" keyword nor the 359 "$draft" keyword. 361 o *totalThreads*: "PositiveInt" (server-set) The number of threads 362 where at least one email in the thread is in this mailbox. 364 o *unreadThreads*: "PositiveInt" (server-set) An indication of the 365 number of "unread" threads in the mailbox. This may be presented 366 by the client as a badge or marker associated with the mailbox. 367 For compatibility with existing implementations, the way "unread 368 threads" is determined is not mandated in this document. The 369 simplest solution to implement is simply the number of threads 370 where at least one email in the thread is both in this mailbox and 371 has neither the "$seen" nor "$draft" keywords. However, a quality 372 implementation will return the number of unread items the user 373 would see if they opened that mailbox. A thread is shown as 374 unread if it contains any unread messages that will be displayed 375 when the thread is opened. Therefore "unreadThreads" should be 376 the number of threads where at least one email in the thread has 377 neither the "$seen" nor the "$draft" keyword AND at least one 378 email in the thread is in this mailbox. Note, the unread email 379 does not need to be the one in this mailbox. In addition, the 380 Trash mailbox (that is a mailbox whose "role" is "trash") is 381 treated specially: 383 1. Emails that are *only* in the Trash (and no other mailbox) are 384 ignored when calculating the "unreadThreads" count of other 385 mailboxes. 387 2. Emails that are *not* in the Trash are ignored when 388 calculating the "unreadThreads" count for the Trash mailbox. 390 The result of this is that emails in the Trash are treated as 391 though they are in a separate thread for the purposes of unread 392 counts. It is expected that clients will hide emails in the Trash 393 when viewing a thread in another mailbox and vice versa. This 394 allows you to delete a single email to the Trash out of a thread. 395 So for example, suppose you have an account where the entire 396 contents is a single thread with 2 emails: an unread email in the 397 Trash and a read email in the Inbox. The "unreadThreads" count 398 would be "1" for the Trash and "0" for the Inbox. 400 o *myRights*: "MailboxRights" (server-set) The set of rights (ACLs) 401 the user has in relation to this mailbox. A _MailboxRights_ 402 object has the following properties: 404 * *mayReadItems*: "Boolean" If true, the user may use this 405 mailbox as part of a filter in a _Email/query_ call and the 406 mailbox may be included in the _mailboxIds_ set of _Email_ 407 objects. If a sub-mailbox is shared but not the parent 408 mailbox, this may be "false". Corresponds to IMAP ACLs "lr". 410 * *mayAddItems*: "Boolean" The user may add mail to this mailbox 411 (by either creating a new email or moving an existing one). 412 Corresponds to IMAP ACL "i". 414 * *mayRemoveItems*: "Boolean" The user may remove mail from this 415 mailbox (by either changing the mailboxes of an email or 416 deleting it). Corresponds to IMAP ACLs "te". 418 * *maySetSeen*: "Boolean" The user may add or remove the "$seen" 419 keyword to/from an email. If an email belongs to multiple 420 mailboxes, the user may only modify "$seen" if *all* of the 421 mailboxes have this permission. Corresponds to IMAP ACL "s". 423 * *maySetKeywords*: "Boolean" The user may add or remove any 424 keyword _other than_ "$seen" to/from an email. If an email 425 belongs to multiple mailboxes, the user may only modify 426 keywords if *all* of the mailboxes have this permission. 427 Corresponds to IMAP ACL "w". 429 * *mayCreateChild*: "Boolean" The user may create a mailbox with 430 this mailbox as its parent. Corresponds to IMAP ACL "k". 432 * *mayRename*: "Boolean" The user may rename the mailbox or make 433 it a child of another mailbox. Corresponds to IMAP ACL "x". 435 * *mayDelete*: "Boolean" The user may delete the mailbox itself. 436 Corresponds to IMAP ACL "x". 438 * *maySubmit*: "Boolean" Messages may be submitted directly to 439 this mailbox. Corresponds to IMAP ACL "p". 441 o *isSubscribed*: "Boolean" Has the user indicated they wish to see 442 this mailbox in their client? This SHOULD default to "false" for 443 mailboxes in shared accounts the user has access to, and "true" 444 for any new mailboxes created by the user themself. This MUST be 445 stored separately per-user where multiple users have access to a 446 shared mailbox. A user may have permission to access a large 447 number of shared accounts, or a shared account with a very large 448 set of mailboxes, but only be interested in the contents of a few 449 of these. Clients may choose only to display mailboxes to the 450 user that have the "isSubscribed" property set to "true", and 451 offer a separate UI to allow the user to see and subscribe/ 452 unsubscribe from the full set of mailboxes. However, clients MAY 453 choose to ignore this property, either entirely for ease of 454 implementation, or just for the primary account (which is normally 455 the user's own, rather than a shared account). This property 456 corresponds to IMAP mailbox subscriptions. 458 For IMAP compatibility, an email in both the Trash and another 459 mailbox SHOULD be treated by the client as existing in both places 460 (i.e. when emptying the trash, the client SHOULD just remove the 461 Trash mailbox and leave it in the other mailbox). 463 The following JMAP methods are supported: 465 2.1. Mailbox/get 467 Standard "/get" method. The _ids_ argument may be "null" to fetch 468 all at once. 470 2.2. Mailbox/changes 472 Standard "/changes" method, but with one extra argument to the 473 response: 475 o *updatedProperties*: "String[]|null" If only the mailbox counts 476 (unread/total emails/threads) have changed since the old state, 477 this will be the list of properties that may have changed, i.e. 478 "["totalEmails", "unreadEmails", "totalThreads", 479 "unreadThreads"]". If the server is unable to tell if only counts 480 have changed, it MUST just be "null". 482 Since counts frequently change but other properties are generally 483 only changed rarely, the server can help the client optimise data 484 transfer by keeping track of changes to email/thread counts 485 separately to other state changes. The _updatedProperties_ array may 486 be used directly via a back-reference in a subsequent Mailbox/get 487 call in the same single request so only these properties are returned 488 if nothing else has changed. 490 2.3. Mailbox/query 492 Standard "/query" method, but with the following additional argument: 494 o *sortAsTree*: "Boolean" (default: false) If "true", when sorting 495 the query results and comparing two mailboxes a and b: 497 * If a is an ancestor of b, it always comes first regardless of 498 the _sort_ comparators. Similarly, if a is descendant of b, 499 then b always comes first. 501 * Otherwise, if a and b do not share a _parentId_, find the 502 nearest ancestors of each that do have the same _parentId_ and 503 compare the sort properties on those mailboxes instead. 505 The result of this is that the mailboxes are sorted as a tree 506 according to the parentId properties, with each set of children 507 with a common parent sorted according to the standard sort 508 comparators. 510 o *filterAsTree*: "Boolean" (default: false) If "true", a mailbox is 511 only included in the query if all its ancestors are also included 512 in the query according to the filter. 514 A *FilterCondition* object has the following properties, any of which 515 may be omitted: 517 o *parentId*: "Id|null" The Mailbox _parentId_ property must match 518 the given value exactly. 520 o *name*: "String" The Mailbox _name_ property contains the given 521 string. 523 o *role*: "String|null" The Mailbox _role_ property must match the 524 given value exactly. 526 o *hasAnyRole*: "Boolean" If "true", a Mailbox matches if it has any 527 non-"null" value for its _role_ property. 529 o *isSubscribed*: "Boolean" The "isSubscribed" property of the 530 mailbox must be identical to the value given to match the 531 condition. 533 A Mailbox object matches the filter if and only if all of the given 534 conditions given match. If zero properties are specified, it is 535 automatically "true" for all objects. 537 The following properties MUST be supported for sorting: 539 o "sortOrder" 541 o "name" 543 2.4. Mailbox/queryChanges 545 Standard "/queryChanges" method. 547 2.5. Mailbox/set 549 Standard "/set" method, but with the following additional argument: 551 o *onDestroyRemoveMessages*: "Boolean" (default: false) If "false", 552 any attempt to destroy a mailbox that still has messages in it 553 will be rejected with a "mailboxHasEmail" SetError. If "true", 554 any messages that were in the mailbox will be removed from it, and 555 if in no other mailboxes will be destroyed when the mailbox is 556 destroyed. 558 The following extra _SetError_ types are defined: 560 For *destroy*: 562 o "mailboxHasChild": The mailbox still has at least one child 563 mailbox. The client MUST remove these before it can delete the 564 parent mailbox. 566 o "mailboxHasEmail": The mailbox has at least one message assigned 567 to it and the _onDestroyRemoveMessages_ argument was "false". 569 2.6. Example 571 Fetching all mailboxes in an account: 573 [[ "Mailbox/get", { 574 "accountId": "u33084183", 575 "ids": null 576 }, "0" ]] 578 And response: 580 [[ "Mailbox/get", { 581 "accountId": "u33084183", 582 "state": "78540", 583 "list": [{ 584 "id": "23cfa8094c0f41e6", 585 "name": "Inbox", 586 "parentId": null, 587 "role": "inbox", 588 "sortOrder": 10, 589 "totalEmails": 16307, 590 "unreadEmails": 13905, 591 "totalThreads": 5833, 592 "unreadThreads": 5128, 593 "myRights": { 594 "mayAddItems": true, 595 "mayRename": false, 596 "maySubmit": true, 597 "mayDelete": false, 598 "maySetKeywords": true, 599 "mayRemoveItems": true, 600 "mayCreateChild": true, 601 "maySetSeen": true, 602 "mayReadItems": true 603 }, 604 "isSubscribed": true 605 }, { 606 "id": "674cc24095db49ce", 607 "name": "Important mail", 608 ... 609 }, ... ], 610 "notFound": [] 611 }, "0" ]] 613 Now suppose a message is marked read and we get a push update that 614 the Mailbox state has changed. You might fetch the updates like 615 this: 617 [[ "Mailbox/changes", { 618 "accountId": "u33084183", 619 "sinceState": "78540" 620 }, "0" ], 621 [ "Mailbox/get", { 622 "accountId": "u33084183", 623 "#ids": { 624 "resultOf": "0", 625 "name": "Mailbox/changes", 626 "path": "/created" 627 } 628 }, "1" ], 629 [ "Mailbox/get", { 630 "accountId": "u33084183", 631 "#ids": { 632 "resultOf": "0", 633 "name": "Mailbox/changes", 634 "path": "/updated" 635 }, 636 "#properties": { 637 "resultOf": "0", 638 "name": "Mailbox/changes", 639 "path": "/updatedProperties" 640 } 641 }, "2" ]] 643 This fetches the list of ids for created/updated/destroyed mailboxes, 644 then using back-references fetches the data for just the created/ 645 updated mailboxes in the same request. The response may look 646 something like this: 648 [[ "Mailbox/changes", { 649 "accountId": "u33084183", 650 "oldState": "78541", 651 "newState": "78542", 652 "hasMoreChanges": false, 653 "updatedProperties": [ 654 "totalEmails", "unreadEmails", 655 "totalThreads", "unreadThreads" 656 ], 657 "created": [], 658 "updated": ["23cfa8094c0f41e6"], 659 "destroyed": [] 660 }, "0" ], 661 [ "Mailbox/get", { 662 "accountId": "u33084183", 663 "state": "78542", 664 "list": [], 665 "notFound": [] 666 }, "1" ], 667 [ "Mailbox/get", { 668 "accountId": "u33084183", 669 "state": "78542", 670 "list": [{ 671 "id": "23cfa8094c0f41e6", 672 "totalEmails": 16307, 673 "unreadEmails": 13903, 674 "totalThreads": 5833, 675 "unreadThreads": 5127 676 }], 677 "notFound": [] 678 }, "2" ]] 680 Here's an example where we try to rename one mailbox and destroy 681 another: 683 [[ "Mailbox/set", { 684 "accountId": "u33084183", 685 "ifInState": "78542", 686 "update": { 687 "674cc24095db49ce": { 688 "name": "Maybe important mail" 689 } 690 }, 691 "destroy": [ "23cfa8094c0f41e6" ] 692 }, "0" ]] 694 Suppose the rename succeeds, but we don't have permission to destroy 695 the mailbox we tried to destroy, we might get back: 697 [[ "Mailbox/set", { 698 "accountId": "u33084183", 699 "oldState": "78542", 700 "newState": "78549", 701 "updated": { 702 "674cc24095db49ce": null 703 }, 704 "notDestroyed": { 705 "23cfa8094c0f41e6": { 706 "type": "forbidden" 707 } 708 } 709 }, "0" ]] 711 3. Threads 713 Replies are grouped together with the original message to form a 714 thread. In JMAP, a thread is simply a flat list of emails, ordered 715 by date. Every email MUST belong to a thread, even if it is the only 716 email in the thread. 718 The exact algorithm for determining whether two emails belong to the 719 same thread is not mandated in this spec to allow for compatibility 720 with different existing systems. For new implementations, it is 721 suggested that two messages belong in the same thread if both of the 722 following conditions apply: 724 1. An identical RFC5322 message id appears in both messages in any 725 of the Message-Id, In-Reply-To and References headers. 727 2. After stripping automatically added prefixes such as "Fwd:", 728 "Re:", "[List-Tag]" etc. and ignoring whitespace, the subjects 729 are the same. This avoids the situation where a person replies 730 to an old message as a convenient way of finding the right 731 recipient to send to, but changes the subject and starts a new 732 conversation. 734 If emails are delivered out of order for some reason, a user may 735 receive two emails in the same thread but without headers that 736 associate them with each other. The arrival of a third email in the 737 thread may provide the missing references to join them all together 738 into a single thread. Since the _threadId_ of an email is immutable, 739 if the server wishes to merge the threads, it MUST handle this by 740 deleting and reinserting (with a new email id) the emails that change 741 threadId. 743 A *Thread* object has the following properties: 745 o *id*: "Id" (immutable; server-set) The id of the thread. 747 o *emailIds*: "Id[]" (server-set) The ids of the emails in the 748 thread, sorted by the _receivedAt_ date of the email, oldest 749 first. If two emails have an identical date, the sort is server- 750 dependent but MUST be stable (sorting by id is recommended). 752 The following JMAP methods are supported: 754 3.1. Thread/get 756 Standard "/get" method. 758 3.1.1. Example 760 Request: 762 [[ "Thread/get", { 763 "accountId": "acme", 764 "ids": ["f123u4", "f41u44"] 765 }, "#1" ]] 767 with response: 769 [[ "Thread/get", { 770 "accountId": "acme", 771 "state": "f6a7e214", 772 "list": [ 773 { 774 "id": "f123u4", 775 "emailIds": [ "eaa623", "f782cbb"] 776 }, 777 { 778 "id": "f41u44", 779 "emailIds": [ "82cf7bb" ] 780 } 781 ], 782 "notFound": [] 783 }, "#1" ]] 785 3.2. Thread/changes 787 Standard "/changes" method. 789 4. Emails 791 The *Email* object is a representation of an [RFC5322] message, which 792 allows clients to avoid the complexities of MIME parsing, transport 793 encoding and character encoding. 795 4.1. Properties of the Email object 797 Broadly, a message consists of two parts: a list of header fields, 798 then a body. The JMAP Email object provides a way to access the full 799 structure, or to use simplified properties and avoid some complexity 800 if this is sufficient for the client application. 802 While raw headers can be fetched and set, the vast majority of 803 clients should use an appropriate parsed form for each of the headers 804 it wants to process, as this allows it to avoid the complexities of 805 various encodings that are required in a valid RFC5322 message. 807 The body of a message is normally a MIME-encoded set of documents in 808 a tree structure. This may be arbitrarily nested, but the majority 809 of email clients present a flat model of an email body (normally 810 plain text or HTML), with a set of attachments. Flattening the MIME 811 structure to form this model can be difficult, and causes 812 inconsistency between clients. Therefore in addition to the 813 _bodyStructure_ property, which gives the full tree, the Email object 814 contains 3 alternate properties with flat lists of body parts: 816 o _textBody_/_htmlBody_: These provide a list of parts that should 817 be rendered sequentially as the "body" of the message. This is a 818 list rather than a single part as messages may have headers and/or 819 footers appended/prepended as separate parts as they are 820 transmitted, and some clients send text and images intended to be 821 displayed inline in the body (or even videos and sound clips) as 822 multiple parts rather than a single HTML part with referenced 823 images. 825 Because MIME allows for multiple representations of the same data 826 (using "multipart/alternative"), there is a textBody property (which 827 prefers a plain text representation) and an htmlBody property (which 828 prefers an HTML representation) to accommodate the two most common 829 client requirements. The same part may appear in both lists where 830 there is no alternative between the two. 832 o _attachments_: This provides a list of parts that should be 833 presented as "attachments" to the message. Some images may be 834 solely there for embedding within an HTML body part; clients may 835 wish to not present these as attachments in the user interface if 836 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*: "Id" (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*: "Id" (immutable; server-set) The id representing the raw 863 octets of the [RFC5322] message. This may be used to download the 864 raw original message, or to attach it directly to another Email 865 etc. 867 o *threadId*: "Id" (immutable; server-set) The id of the Thread to 868 which this Email belongs. 870 o *mailboxIds*: "Id[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 (including as part of any 897 mailbox counts). Users may add arbitrary keywords to an email. 898 For compatibility with IMAP, a keyword is a case-insensitive 899 string of 1-255 characters in the ASCII subset %x21-%x7e (excludes 900 control chars and space), and MUST NOT include any of these 901 characters: "( ) { ] % * " \" Because JSON is case-sensitive, 902 servers MUST return keywords in lower-case. The IANA Keyword 903 Registry [2] as established in [RFC5788] assigns semantic meaning 904 to some other keywords in common use. New keywords may be 905 established here in the future. In particular, note: 907 * "$forwarded": The email has been forwarded. 909 * "$phishing": The email is highly likely to be phishing. 910 Clients SHOULD warn users to take care when viewing this email 911 and disable links and attachments. 913 * "$junk": The email is definitely spam. Clients SHOULD set this 914 flag when users report spam to help train automated spam- 915 detection systems. 917 * "$notjunk": The email is definitely not spam. Clients SHOULD 918 set this flag when users indicate an email is legitimate, to 919 help train automated spam-detection systems. 921 o *size*: "PositiveInt" (immutable; server-set) The size, in octets, 922 of the raw data for the [RFC5322] message (as referenced by the 923 _blobId_, i.e. the number of octets in the file the user would 924 download). 926 o *receivedAt*: "UTCDate" (immutable; default: time of creation on 927 server) The date the email was received by the message store. 928 This is the _internal date_ in IMAP. 930 4.1.2. Header fields parsed forms 932 Header field properties are derived from the [RFC5322] and [RFC6532] 933 message header fields. All header fields may be fetched in a raw 934 form. Some headers may also be fetched in a parsed form. The 935 structured form that may be fetched depends on the header. The 936 following forms are defined: 938 4.1.2.1. Raw 940 Type: "String" 942 The raw octets of the header field value from the first octet 943 following the header field name terminating colon, up to but 944 excluding the header field terminating CRLF. Any standards-compliant 945 message MUST be either ASCII (RFC5322) or UTF-8 (RFC6532), however 946 other encodings exist in the wild. A server MAY use heuristics to 947 determine a charset and decode the octets, or MAY replace any octet 948 or octet run with the high bit set that violates UTF-8 syntax with 949 the unicode replacement character (U+FFFD). Any NUL octet MUST be 950 dropped. 952 4.1.2.2. Text 954 Type: "String" 956 The header field value with: 958 1. White space unfolded (as defined in [RFC5322] section 2.2.3). 960 2. The terminating CRLF at the end of the value removed. 962 3. Any SP characters at the beginning of the value removed. 964 4. Any syntactically correct [RFC2047] encoded sections with a known 965 character set decoded. Any [RFC2047] encoded NUL octets or 966 control characters are dropped from the decoded value. Any text 967 that looks like [RFC2047] syntax but violates [RFC2047] placement 968 or whitespace rules MUST NOT be decoded. 970 5. Any [RFC6532] UTF-8 values decoded. 972 6. The resulting unicode converted to NFC form. 974 If any decodings fail, the parser SHOULD insert a unicode replacement 975 character (U+FFFD) and attempt to continue as much as possible. 977 To prevent obviously nonsense behaviour, which can lead to 978 interoperability issues, this form may only be fetched or set for the 979 following header fields: 981 o Subject 983 o Comment 985 o List-Id 987 o Any header not defined in [RFC5322] or [RFC2369] 989 4.1.2.3. Addresses 991 Type: "EmailAddress[]" 993 The header is parsed as an "address-list" value, as specified in 994 [RFC5322] section 3.4, into the "EmailAddress[]" type. There is an 995 EmailAddress item for each "mailbox" parsed from the "address-list". 996 Group and comment information is discarded. 998 The *EmailAddress* object has the following properties: 1000 o *name*: "String|null" The _display-name_ of the [RFC5322] 1001 _mailbox_, or "null" if none. If this is a _quoted-string_: 1003 1. The surrounding DQUOTE characters are removed. 1005 2. Any _quoted-pair_ is decoded. 1007 3. White-space is unfolded, and then any leading and trailing 1008 white-space is removed. 1010 o *email*: "String" The _addr-spec_ of the [RFC5322] _mailbox_. 1012 Any syntactically correct [RFC2047] encoded sections with a known 1013 encoding MUST be decoded, following the same rules as for the _Text_ 1014 form. Any [RFC6532] UTF-8 values MUST be decoded. 1016 Parsing SHOULD be best-effort in the face of invalid structure to 1017 accommodate invalid messages and semi-complete drafts. EmailAddress 1018 objects MAY have an _email_ property that does not conform to the 1019 _addr-spec_ form (for example, may not contain an @ symbol). 1021 For example, the following "address-list" string: 1023 " James Smythe" , Friends: 1024 jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= 1025 ; 1027 would be parsed as: 1029 [ 1030 { "name": "James Smythe", "email": "james@example.com" }, 1031 { "name": null, "email": "jane@example.com" }, 1032 { "name": "John Smith", "email": "john@example.com" } 1033 ] 1035 To prevent obviously nonsense behaviour, which can lead to 1036 interoperability issues, this form may only be fetched or set for the 1037 following header fields: 1039 o From 1041 o Sender 1043 o Reply-To 1045 o To 1047 o Cc 1049 o Bcc 1051 o Resent-From 1053 o Resent-Sender 1055 o Resent-Reply-To 1057 o Resent-To 1059 o Resent-Cc 1061 o Resent-Bcc 1063 o Any header not defined in [RFC5322] or [RFC2369] 1065 4.1.2.4. GroupedAddresses 1067 Type: "EmailAddressGroup[]" 1069 This is similar to the Addresses form but preserves group 1070 information. The header is parsed as an "address-list" value, as 1071 specified in [RFC5322] section 3.4, into the "GroupedAddresses[]" 1072 type. Consecutive mailboxes that are not part of a group are still 1073 collected under an EmailAddressGroup object to provide a uniform 1074 type. 1076 The *EmailAddressGroup* object has the following properties: 1078 o *name*: "String|null" The _display-name_ of the [RFC5322] _group_, 1079 or "null" if the addresses are not part of a group. If this is a 1080 _quoted-string_ it is processed the same as the _name_ in the 1081 _EmailAddress_ type. 1083 o *addresses*: "EmailAddress[]" The _mailbox_es that belong to this 1084 group, represented as EmailAddress objects. 1086 Any syntactically correct [RFC2047] encoded sections with a known 1087 encoding MUST be decoded, following the same rules as for the _Text_ 1088 form. Any [RFC6532] UTF-8 values MUST be decoded. 1090 Parsing SHOULD be best-effort in the face of invalid structure to 1091 accommodate invalid messages and semi-complete drafts. 1093 For example, the following "address-list" string: 1095 " James Smythe" , Friends: 1096 jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?= 1097 ; 1099 would be parsed as: 1101 [ 1102 { "name": null, "addresses": [ 1103 { "name": "James Smythe", "email": "james@example.com" } 1104 ]}, 1105 { "name": "Friends", "addresses": [ 1106 { "name": null, "email": "jane@example.com" }, 1107 { "name": "John Smith", "email": "john@example.com" } 1108 ]} 1109 ] 1111 To prevent obviously nonsense behaviour, which can lead to 1112 interoperability issues, this form may only be fetched or set for the 1113 same header fields as the _Addresses_ form. 1115 4.1.2.5. MessageIds 1117 Type: "String[]|null" 1119 The header is parsed as a list of "msg-id" values, as specified in 1120 [RFC5322] section 3.6.4, into the "String[]" type. CFWS and 1121 surrounding angle brackets ("<>") are removed. If parsing fails, the 1122 value is "null". 1124 To prevent obviously nonsense behaviour, which can lead to 1125 interoperability issues, this form may only be fetched or set for the 1126 following header fields: 1128 o Message-ID 1130 o In-Reply-To 1132 o References 1134 o Resent-Message-ID 1136 o Any header not defined in [RFC5322] or [RFC2369] 1138 4.1.2.6. Date 1140 Type: "Date|null" 1142 The header is parsed as a "date-time" value, as specified in 1143 [RFC5322] section 3.3, into the "Date" type. If parsing fails, the 1144 value is "null". 1146 To prevent obviously nonsense behaviour, which can lead to 1147 interoperability issues, this form may only be fetched or set for the 1148 following header fields: 1150 o Date 1152 o Resent-Date 1154 o Any header not defined in [RFC5322] or [RFC2369] 1156 4.1.2.7. URLs 1158 Type: "String[]|null" 1160 The header is parsed as a list of URLs, as described in [RFC2369], 1161 into the "String[]" type. Values do not include the surrounding 1162 angle brackets or any comments in the header with the URLs. If 1163 parsing fails, the value is "null". 1165 To prevent obviously nonsense behaviour, which can lead to 1166 interoperability issues, this form may only be fetched or set for the 1167 following header fields: 1169 o List-Help 1171 o List-Unsubscribe 1173 o List-Subscribe 1175 o List-Post 1177 o List-Owner 1179 o List-Archive 1181 o Any header not defined in [RFC5322] or [RFC2369] 1183 4.1.3. Header fields properties 1185 The following low-level *Email* property is specified for complete 1186 access to the header data of the message: 1188 o *headers*: "EmailHeader[]" (immutable) This is a list of all 1189 [RFC5322] header fields, in the same order they appear in the 1190 message. An *EmailHeader* object has the following properties: 1192 * *name*: "String" The header _field name_ as defined in 1193 [RFC5322], with the same capitalization that it has in the 1194 message. 1196 * *value*: "String" The header _field value_ as defined in 1197 [RFC5322], in _Raw_ form. 1199 In addition, the client may request/send properties representing 1200 individual header fields of the form: 1202 header:{header-field-name} 1204 Where "{header-field-name}" means any series of one or more printable 1205 ASCII characters (i.e. characters that have values between 33 and 1206 126, inclusive), except colon. The property may also have the 1207 following suffixes: 1209 o *:as{header-form}* This means the value is in a parsed form, where 1210 "{header-form}" is one of the parsed-form names specified above. 1211 If not given, the value is in _Raw_ form. 1213 o *:all* This means the value is an array, with the items 1214 corresponding to each instance of the header field, in the order 1215 they appear in the message. If this suffix is not used, the 1216 result is the value of the *last* instance of the header field 1217 (i.e. identical to the *last* item in the array if :all is used), 1218 or "null" if none. 1220 If both suffixes are used, they MUST be specified in the order above. 1221 Header field names are matched case-insensitively. The value is 1222 typed according to the requested form, or an array of that type if 1223 :all is used. If no header fields exist in the message with the 1224 requested name, the value is "null" if fetching a single instance, or 1225 the empty array if requesting :all. 1227 As a simple example, if the client requests a property called 1228 "header:subject", this means find the _last_ header field in the 1229 message named "subject" (matched case-insensitively) and return the 1230 value in _Raw_ form, or "null" if no header of this name is found. 1232 For a more complex example, consider the client requesting a property 1233 called "header:Resent-To:asAddresses:all". This means: 1235 1. Find _all_ header fields named Resent-To (matched case- 1236 insensitively). 1238 2. For each instance parse the header field value in the _Addresses_ 1239 form. 1241 3. The result is of type "EmailAddress[][]" - each item in the array 1242 corresponds to the parsed value (which is itself an array) of the 1243 Resent-To header field instance. 1245 The following convenience properties are also specified for the 1246 *Email* object: 1248 o *messageId*: "String[]|null" (immutable) The value is identical to 1249 the value of _header:Message-ID:asMessageIds_. For messages 1250 conforming to RFC5322 this will be an array with a single entry. 1252 o *inReplyTo*: "String[]|null" (immutable) The value is identical to 1253 the value of _header:In-Reply-To:asMessageIds_. 1255 o *references*: "String[]|null" (immutable) The value is identical 1256 to the value of _header:References:asMessageIds_. 1258 o *sender*: "EmailAddress[]|null" (immutable) The value is identical 1259 to the value of _header:Sender:asAddresses_. 1261 o *from*: "EmailAddress[]|null" (immutable) The value is identical 1262 to the value of _header:From:asAddresses_. 1264 o *to*: "EmailAddress[]|null" (immutable) The value is identical to 1265 the value of _header:To:asAddresses_. 1267 o *cc*: "EmailAddress[]|null" (immutable) The value is identical to 1268 the value of _header:Cc:asAddresses_. 1270 o *bcc*: "EmailAddress[]|null" (immutable) The value is identical to 1271 the value of _header:Bcc:asAddresses_. 1273 o *replyTo*: "EmailAddress[]|null" (immutable) The value is 1274 identical to the value of _header:Reply-To:asAddresses_. 1276 o *subject*: "String|null" (immutable) The value is identical to the 1277 value of _header:Subject:asText_. 1279 o *sentAt*: "Date|null" (immutable; default on creation: current 1280 server time) The value is identical to the value of 1281 _header:Date:asDate_. 1283 4.1.4. Body parts 1285 These properties are derived from the [RFC5322] message body and its 1286 [RFC2045] MIME entities. 1288 A *EmailBodyPart* object has the following properties: 1290 o *partId*: "String|null" Identifies this part uniquely within the 1291 Email. This is scoped to the _emailId_ and has no meaning outside 1292 of the JMAP Email object representation. This is "null" if, and 1293 only if, the part is of type "multipart/*". 1295 o *blobId*: "Id|null" The id representing the raw octets of the 1296 contents of the part after decoding any _Content-Transfer- 1297 Encoding_ (as defined in [RFC2045]), or "null" if, and only if, 1298 the part is of type "multipart/*". Note, two parts may be 1299 transfer-encoded differently but have the same blob id if their 1300 decoded octets are identical and the server is using a secure hash 1301 of the data for the blob id. 1303 o *size*: "PositiveInt" The size, in octets, of the raw data after 1304 content transfer decoding (as referenced by the _blobId_, i.e. the 1305 number of octets in the file the user would download). 1307 o *headers*: "EmailHeader[]" This is a list of all header fields in 1308 the part, in the order they appear in the message. The values are 1309 in _Raw_ form. 1311 o *name*: "String|null" This is the [RFC2231] decoded _filename_ 1312 parameter of the _Content-Disposition_ header field, or (for 1313 compatibility with existing systems) if not present then the 1314 [RFC2047] decoded _name_ parameter of the _Content-Type_ header 1315 field. 1317 o *type*: "String" The value of the _Content-Type_ header field of 1318 the part, if present, otherwise the implicit type as per the MIME 1319 standard ("text/plain", or "message/rfc822" if inside a 1320 "multipart/digest"). CFWS is removed and any parameters are 1321 stripped. 1323 o *charset*: "String|null" The value of the charset parameter of the 1324 _Content-Type_ header field, if present, or "null" if the header 1325 field is present but not of type "text/*". If there is no 1326 _Content-Type_ header field, or it exists and is of type "text/*" 1327 but has no charset parameter, this is the implicit charset as per 1328 the MIME standard: "us-ascii". 1330 o *disposition*: "String|null" The value of the _Content- 1331 Disposition_ header field of the part, if present, otherwise 1332 "null". CFWS is removed and any parameters are stripped. 1334 o *cid*: "String|null" The value of the _Content-Id_ header field of 1335 the part, if present, otherwise "null". CFWS and surrounding 1336 angle brackets ("<>") are removed. This may be used to reference 1337 the content from within an html body part using the "cid:" 1338 protocol. 1340 o *language*: "String[]|null" The list of language tags, as defined 1341 in [RFC3282], in the _Content-Language_ header field of the part, 1342 if present. 1344 o *location*: "String|null" The URI, as defined in [RFC2557], in the 1345 _Content-Location_ header field of the part, if present. 1347 o *subParts*: "EmailBodyPart[]|null" If type is "multipart/*", this 1348 contains the body parts of each child. 1350 In addition, the client may request/send EmailBodyPart properties 1351 representing individual header fields, following the same syntax and 1352 semantics as for the Email object, e.g. "header:Content-Type". 1354 The following *Email* properties are specified for access to the body 1355 data of the message: 1357 o *bodyStructure*: "EmailBodyPart" (immutable) This is the full MIME 1358 structure of the message body, represented as an array of the 1359 message's top-level MIME parts, without recursing into "message/ 1360 rfc822" or "message/global" parts. Note that EmailBodyParts may 1361 have subParts if they are of type "multipart/*". 1363 o *bodyValues*: "String[EmailBodyValue]" (immutable) This is a map 1364 of _partId_ to an *EmailBodyValue* object for none, some or all 1365 "text/*" parts. Which parts are included and whether the value is 1366 truncated is determined by various arguments to _Email/get_ and 1367 _Email/parse_. An *EmailBodyValue* object has the following 1368 properties: 1370 * *value*: "String" The value of the body part after decoding 1371 _Content-Transport-Encoding_ and decoding the _Content-Type_ 1372 charset, if known to the server, and with any CRLF replaced 1373 with a single LF. The server MAY use heuristics to determine 1374 the charset to use for decoding if the charset is unknown, or 1375 if no charset is given, or if it believes the charset given is 1376 incorrect. Decoding is best-effort and SHOULD insert the 1377 unicode replacement character (U+FFFD) and continue when a 1378 malformed section is encountered. Note that due to the charset 1379 decoding and line ending normalisation, the length of this 1380 string will probably not be exactly the same as the _size_ 1381 property on the corresponding EmailBodyPart. 1383 * *isEncodingProblem*: "Boolean" (default: false) This is "true" 1384 if malformed sections were found while decoding the charset, or 1385 the charset was unknown. 1387 * *isTruncated*: "Boolean" (default: false) This is "true" if the 1388 _value_ has been truncated. 1390 See the security considerations section for issues related to 1391 truncation and heuristic determination of content-type and 1392 charset. 1394 o *textBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", 1395 "text/html", "image/*", "audio/*" and/or "video/*" parts to 1396 display (sequentially) as the message body, with a preference for 1397 "text/plain" when alternative versions are available. 1399 o *htmlBody*: "EmailBodyPart[]" (immutable) A list of "text/plain", 1400 "text/html", "image/*", "audio/*" and/or "video/*" parts to 1401 display (sequentially) as the message body, with a preference for 1402 "text/html" when alternative versions are available. 1404 o *attachments*: "EmailBodyPart[]" (immutable) A list of all parts 1405 in _bodyStructure_, traversing depth-first, which satisfy either 1406 of the following conditions: 1408 * not of type "multipart/*" and not included in _textBody_ or 1409 _htmlBody_ 1411 * of type "image/*", "audio/*" or "video/*" and not in both 1412 _textBody_ and _htmlBody_ 1414 None of these parts include subParts, including "message/*" types. 1415 Attached messages may be fetched using the Email/parse method and 1416 the blobId. Note, an HTML body part may reference image parts in 1417 attachments using "cid:" links to reference the _Content-Id_ or by 1418 referencing the _Content-Location_. 1420 o *hasAttachment*: "Boolean" (immutable; server-set) This is "true" 1421 if there are one or more parts in the message that a client UI 1422 should offer as downloadable. A server SHOULD set hasAttachment 1423 to "true" if the _attachments_ list contains at least one item 1424 that does not have "Content-Disposition: inline". The server MAY 1425 ignore parts in this list that are processed automatically in some 1426 way, or are referenced as embedded images in one of the "text/ 1427 html" parts of the message. The server MAY set hasAttachment 1428 based on implementation-defined or site configurable heuristics. 1430 o *preview*: "String" (immutable; server-set) A plain text fragment 1431 of the message body. This is intended to be shown as a preview 1432 line on a mailbox listing, and may be truncated when shown. The 1433 server may choose which part of the message to include in the 1434 preview; skipping quoted sections and salutations and collapsing 1435 white-space can result in a more useful preview. This MUST NOT be 1436 more than 256 characters in length. As this is derived from the 1437 message content by the server, and the algorithm for doing so 1438 could change over time, fetching this for an email a second time 1439 MAY return a different result. However, the previous value is not 1440 considered incorrect, and the change SHOULD NOT cause the Email 1441 object to be considered as changed by the server. 1443 The exact algorithm for decomposing bodyStructure into textBody, 1444 htmlBody and attachments part lists is not mandated, as this is a 1445 quality-of-service implementation issue and likely to require 1446 workarounds for malformed content discovered over time. However, the 1447 following algorithm (expressed here in JavaScript) is suggested as a 1448 starting point, based on real-world experience: 1450 function isInlineMediaType ( type ) { 1451 return type.startsWith( 'image/' ) || 1452 type.startsWith( 'audio/' ) || 1453 type.startsWith( 'video/' ); 1454 } 1456 function parseStructure ( parts, multipartType, inAlternative, 1457 htmlBody, textBody, attachments ) { 1459 // For multipartType == alternative 1460 let textLength = textBody ? textBody.length : -1; 1461 let htmlLength = htmlBody ? htmlBody.length : -1; 1463 for ( let i = 0; i < parts.length; i += 1 ) { 1464 let part = parts[i]; 1465 let isMultipart = part.type.startsWith( 'multipart/' ); 1466 // Is this a body part rather than an attachment 1467 let isInline = part.disposition != "attachment" && 1468 // Must be one of the allowed body types 1469 ( part.type == "text/plain" || 1470 part.type == "text/html" || 1471 isInlineMediaType( part.type ) ) && 1472 // If multipart/related, only the first part can be inline 1473 // If a text part with a filename, and not the first item 1474 // in the multipart, assume it is an attachment 1475 ( i === 0 || 1476 ( multipartType != "related" && 1477 ( isInlineMediaType( part.type ) || !part.name ) ) ); 1479 if ( isMultipart ) { 1480 let subMultiType = part.type.split( '/' )[1]; 1481 parseStructure( part.subParts, subMultiType, 1482 inAlternative || ( subMultiType == 'alternative' ), 1483 htmlBody, textBody, attachments ); 1484 } else if ( isInline ) { 1485 if ( multipartType == 'alternative' ) { 1486 switch ( part.type ) { 1487 case 'text/plain': 1488 textBody.push( part ); 1489 break; 1490 case 'text/html': 1491 htmlBody.push( part ); 1492 break; 1493 default: 1494 attachments.push( part ); 1495 break; 1496 } 1497 continue; 1499 } else if ( inAlternative ) { 1500 if ( part.type == 'text/plain' ) { 1501 htmlBody = null; 1502 } 1503 if ( part.type == 'text/html' ) { 1504 textBody = null; 1505 } 1506 } 1507 if ( textBody ) { 1508 textBody.push( part ); 1509 } 1510 if ( htmlBody ) { 1511 htmlBody.push( part ); 1512 } 1513 if ( ( !textBody || !htmlBody ) && 1514 isInlineMediaType( part.type ) ) { 1515 attachments.push( part ); 1516 } 1517 } else { 1518 attachments.push( part ); 1519 } 1520 } 1522 if ( multipartType == 'alternative' && textBody && htmlBody ) { 1523 // Found HTML part only 1524 if ( textLength == textBody.length && 1525 htmlLength != htmlBody.length ) { 1526 for ( let i = htmlLength; i < htmlBody.length; i += 1 ) { 1527 textBody.push( htmlBody[i] ); 1528 } 1529 } 1530 // Found plain text part only 1531 if ( htmlLength == htmlBody.length && 1532 textLength != textBody.length ) { 1533 for ( let i = textLength; i < textBody.length; i += 1 ) { 1534 htmlBody.push( textBody[i] ); 1535 } 1536 } 1537 } 1538 } 1540 // Usage: 1541 let htmlBody = []; 1542 let textBody = []; 1543 let attachments = []; 1545 parseStructure( [ bodyStructure ], 'mixed', false, 1546 htmlBody, textBody, attachments ); 1548 For instance, consider a message with both text and html versions 1549 that's then gone through a list software manager that attaches a 1550 header/footer. It might have a MIME structure something like: 1552 multipart/mixed 1553 text/plain, content-disposition=inline - A 1554 multipart/mixed 1555 multipart/alternative 1556 multipart/mixed 1557 text/plain, content-disposition=inline - B 1558 image/jpeg, content-disposition=inline - C 1559 text/plain, content-disposition=inline - D 1560 multipart/related 1561 text/html - E 1562 image/jpeg - F 1563 image/jpeg, content-disposition=attachment - G 1564 application/x-excel - H 1565 message/rfc822 - J 1566 text/plain, content-disposition=inline - K 1568 In this case, the above algorithm would decompose this to: 1570 textBody => [ A, B, C, D, K ] 1571 htmlBody => [ A, E, K ] 1572 attachments => [ C, F, G, H, J ] 1574 4.2. Email/get 1576 Standard "/get" method, with the following additional arguments: 1578 o *bodyProperties*: "String[]" A list of properties to fetch for 1579 each EmailBodyPart returned. If omitted, this defaults to: 1581 [ "partId", "blobId", "size", "name", "type", "charset", 1582 "disposition", "cid", "language", "location" ] 1584 o *fetchTextBodyValues*: "Boolean" (default: false) If "true", the 1585 _bodyValues_ property includes any "text/*" part in the "textBody" 1586 property. 1588 o *fetchHTMLBodyValues*: "Boolean" (default: false) If "true", the 1589 _bodyValues_ property includes any "text/*" part in the "htmlBody" 1590 property. 1592 o *fetchAllBodyValues*: "Boolean" (default: false) If "true", the 1593 _bodyValues_ property includes any "text/*" part in the 1594 "bodyStructure" property. 1596 o *maxBodyValueBytes*: "PositiveInt" (default: 0) If greater than 1597 zero, the _value_ property of any EmailBodyValue object returned 1598 in _bodyValues_ MUST be truncated if necessary so it does not 1599 exceed this number of octets in size. If "0" (the default), no 1600 truncation occurs. The server MUST ensure the truncation results 1601 in valid UTF-8 and does not occur mid-codepoint. If the part is 1602 of type "text/html", the server SHOULD NOT truncate inside an HTML 1603 tag, e.g. in the middle of "". 1604 There is no requirement for the truncated form to be a balanced 1605 tree or valid HTML (indeed, the original source may well be 1606 neither of these things). 1608 If the standard _properties_ argument is omitted or "null", the 1609 following default MUST be used instead of "all" properties: 1611 [ "id", "blobId", "threadId", "mailboxIds", "keywords", "size", 1612 "receivedAt", "messageId", "inReplyTo", "references", "sender", "from", 1613 "to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment", 1614 "preview", "bodyValues", "textBody", "htmlBody", "attachments" ] 1616 The following properties are expected to be fast to fetch in a 1617 quality implementation: 1619 o id 1621 o blobId 1623 o threadId 1625 o mailboxIds 1627 o keywords 1629 o size 1631 o receivedAt 1633 o messageId 1635 o inReplyTo 1637 o sender 1639 o from 1641 o to 1643 o cc 1644 o bcc 1646 o replyTo 1648 o subject 1650 o sentAt 1652 o hasAttachment 1654 o preview 1656 Clients SHOULD take care when fetching any other properties, as there 1657 may be significantly longer latency in fetching and returning the 1658 data. 1660 As specified above, parsed forms of headers may only be used on 1661 appropriate header fields. Attempting to fetch a form that is 1662 forbidden (e.g. "header:From:asDate") MUST result in the method call 1663 being rejected with an "invalidArguments" error. 1665 Where a specific header is requested as a property, the 1666 capitalization of the property name in the response MUST be identical 1667 to that used in the request. 1669 4.2.1. Example 1671 Request: 1673 [[ "Email/get", { 1674 "ids": [ "f123u456", "f123u457" ], 1675 "properties": [ "threadId", "mailboxIds", "from", "subject", 1676 "receivedAt", "header:List-POST:asURLs", 1677 "htmlBody", "bodyValues" ], 1678 "bodyProperties": [ "partId", "blobId", "size", "type" ], 1679 "fetchHTMLBodyValues": true, 1680 "maxBodyValueBytes": 256 1681 }, "#1" ]] 1683 and response: 1685 [[ "Email/get", { 1686 "accountId": "abc", 1687 "state": "41234123231", 1688 "list": [ 1689 { 1690 "id": "f123u457", 1691 "threadId": "ef1314a", 1692 "mailboxIds": { "f123": true }, 1693 "from": [{ "name": "Joe Bloggs", "email": "joe@example.com" }], 1694 "subject": "Dinner on Thursday?", 1695 "receivedAt": "2013-10-13T14:12:00Z", 1696 "header:List-POST:asURLs": [ 1697 "mailto:partytime@lists.example.com" 1698 ], 1699 "htmlBody": [{ 1700 "partId": "1", 1701 "blobId": "841623871", 1702 "size": 283331, 1703 "type": "text/html" 1704 }, { 1705 "partId": "2", 1706 "blobId": "319437193", 1707 "size": 10343, 1708 "type": "text/plain" 1709 }], 1710 "bodyValues": { 1711 "1": { 1712 "isEncodingProblem": false, 1713 "isTruncated": true, 1714 "value": "

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

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