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

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

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