RFC 9007
Handling Message Disposition Notification with the JSON Meta Application Protocol (JMAP)
Handling Message Disposition Notification with the JSON Meta Application Protocol (JMAP) ๐
Okay bestie, this is RFC 9007 ๐ฌ Its number is 9007 and it's a Standards Track document, so it's an actual Internet Standard and not just a suggestion ๐ It was written by R. Ouazana, Ed. from Linagora and published in March 2021 ๐ธ It's listed under ISSN 2070-1721. The header doesn't list any RFC it obsoletes or updates, so it stands on its own โจ
Abstract โจ
This doc gives you a data model for handling Message Disposition Notifications (MDNs) (see RFC 8098) in the JSON Meta Application Protocol (JMAP) (see RFCs 8620 and 8621). MDNs are the "read receipts" we all have feelings about ๐
Status of This Memo
This is an Internet Standards Track document ๐ฆ
It's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). For more on Internet Standards, check out Section 2 of RFC 7841 ๐ง
For the current status of this document, any errata, and how to give feedback on it, go to https://www.rfc-editor.org/info/rfc9007 ๐
Copyright Notice
Copyright (c) 2021 IETF Trust and the persons identified as the document authors. All rights reserved ๐
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stand on the date of publication. Please read these carefully, because they describe your rights and restrictions for this document ๐ซถ Any Code Components pulled out of this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, as described in the Simplified BSD License โ ๏ธ
Table of Contents
1. Introduction
1.1. Notational Conventions
1.2. Terminology
1.3. Addition to the Capabilities Object
2. MDN
2.1. MDN/send
2.2. MDN/parse
3. Samples
3.1. Sending an MDN for a Received Email Message
3.2. Asking for an MDN When Sending an Email Message
3.3. Parsing a Received MDN
4. IANA Considerations
4.1. JMAP Capability Registration for "mdn"
4.2. JMAP Error Codes Registration for "mdnAlreadySent"
5. Security Considerations
6. Normative References
Author's Address
1. Introduction ๐
Okay so JMAP (that's "The JSON Meta Application Protocol (JMAP)" [RFC8620]) is a generic protocol for syncing data, like mail, calendars, or contacts, between a client and a server. It's optimised for mobile and web, and it gives you one consistent interface across all the different data types ๐
JMAP for Mail ("The JSON Meta Application Protocol (JMAP) for Mail" [RFC8621]) is the bit that defines a data model for syncing email with a server using JMAP. Clients use it to search, access, organise, and send messages, and it's honestly so efficient ๐ฅ
Message Disposition Notifications (MDNs) are defined in [RFC8098]. They're the things people call "read receipts", "acknowledgements", or "receipt notifications" ๐
A client can run into MDNs in three different ways:
- When an email message comes in, an MDN can be sent back to the sender. This spec defines an
MDN/sendmethod to cover that case ๐ค - When an email message is being sent, an MDN can be requested. This must be done with a header field, as already specified by [RFC8098]. The header field is already handled by the guidance in [RFC8621] ๐
- When an MDN comes in, it could be related to a sent message that already exists. That's already covered by [RFC8621] in the EmailSubmission object. A client might want to show detailed info about a received MDN, so this spec defines an
MDN/parsemethod for that ๐
1.1. Notational Conventions
Okay this part is the rulebook for the capital letter words ๐ง The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, just like they do here. Lowercase versions are just regular words, no cap ๐
Type signatures, examples, and property descriptions in this document follow the conventions set out in Section 1.1 of [RFC8620]. The data types defined in the core specification are also used here, so you'll see them everywhere ๐
Servers MUST support all properties specified for the new data types defined in this document. No skipping, bestie ๐
1.2. Terminology
The same terminology is used in this document as in the core JMAP specification, so you don't have to learn a whole new vocab ๐ฆ
Here's a fun one: keywords are case insensitive in IMAP but case sensitive in JMAP. So the $mdnsent keyword MUST always be used in lowercase. Lowercase only, periodt ๐
1.3. Addition to the Capabilities Object
Capabilities get announced as part of the standard JMAP Session resource, see [RFC8620], Section 2 ๐ธ This defines a new capability called urn:ietf:params:jmap:mdn โจ
If urn:ietf:params:jmap:mdn is present in the accountCapabilities property of an account, it means the server supports the MDN data type, parsing MDNs with the MDN/parse method, and creating and sending MDN messages with the MDN/send method. Servers that include the capability in one or more accountCapabilities properties MUST also include the property in the capabilities property. That's the rule, no exceptions ๐ซถ
The value of the urn:ietf:params:jmap:mdn property is an empty object, both in the account's accountCapabilities property and in the capabilities property. Empty object, full vibes ๐ง
2. MDN ๐ฌ
An MDN object has the following properties. Let's go through them ๐
forEmailId:"Id|null"
The Email id of the received message that this MDN is related to. This property MUST NOT be null for MDN/send, but it MAY be null in the response from the MDN/parse method ๐ง
subject:"String|null"
The subject that goes in the Subject header field for this MDN ๐
textBody:"String|null"
The human-readable part of the MDN, as plain text ๐
includeOriginalMessage:"Boolean"(default: false)
If true, the content of the original message will show up in the third component of the multipart/report generated for the MDN. See [RFC8098] for the details and the security considerations ๐
reportingUA:"String|null"
The name of the Mail User Agent (MUA) creating this MDN. It's used to build the MDN report part of the MDN. Fun fact: a "null" value may have better privacy properties ๐
disposition:"Disposition"
The object holding all the different MDN disposition options ๐
mdnGateway:"String|null"(server-set)
The name of the gateway or Message Transfer Agent (MTA) that translated a foreign (non-Internet) message disposition notification into this MDN ๐
originalRecipient:"String|null"(server-set)
The original recipient address, as specified by the sender of the message the MDN is being issued for ๐ฎ
finalRecipient:"String|null"
The recipient the MDN is being issued for. If it's set, it overrides the value the server would calculate from the Identity defined in the MDN/send method, unless the client explicitly sets it. Main character override ๐
originalMessageId:"String|null"(server-set)
The Message-ID header field [RFC5322] (not the JMAP id) of the message the MDN is being issued for ๐ชช
error:"String[]|null"(server-set)
Extra info in the form of text messages when the error disposition modifier shows up ๐จ
extensionFields:"String[String]|null"
The object where the keys are extension-field names and the values are extension-field values (see [RFC8098], Section 3.3) ๐งฉ
A Disposition object has the following properties:
actionMode:"String"
This MUST be one of the following strings: "manual-action" / "automatic-action" ๐ฏ
sendingMode:"String"
This MUST be one of the following strings: "mdn-sent-manually" / "mdn-sent-automatically" ๐
type:"String"
This MUST be one of the following strings: "deleted" / "dispatched" / "displayed" / "processed" ๐
Heads up: see [RFC8098] for the exact meaning of these fields. They're case insensitive in [RFC8098], but in this RFC they're case sensitive, so MDN/parse MUST convert them to lowercase. Don't skip this step, it's literally the whole point ๐ญ
๐ To be continued in part 2, bestie!! ๐
2.1. MDN/send โจ
okay so the "MDN/send" method is how you send a message in the style of [RFC5322] straight from an MDN object ๐ when you call it, the "using" property of the Request object MUST contain the capabilities "urn:ietf:params:jmap:mdn" and "urn:ietf:params:jmap:mail". the second one is needed bc of the implicit call to "Email/set" and the Identity objects (more on those below) ๐ง it takes these arguments:
accountId: "Id"- the id of the account to use ๐
identityId: "Id"- the id of the Identity to associate with these MDNs. the server uses this identity to define the sender of the MDNs and to set the
"finalRecipient"field ๐
- the id of the Identity to associate with these MDNs. the server uses this identity to define the sender of the MDNs and to set the
send: "Id[MDN]"- a map of the creation id (client specified) to MDN objects ๐ฌ
onSuccessUpdateEmail: "Id[PatchObject]|null"- a map of the id to an object holding properties to update on the Email object referenced by the
"MDN/send"if the sending succeeds. this will always be a backward reference to the creation id (see the example below in Section 3.1) ๐ฆ
- a map of the id to an object holding properties to update on the Email object referenced by the
the response has these arguments:
accountId: "Id"- the id of the account used for the call ๐ธ
sent: "Id[MDN]|null"- a map of the creation id to an MDN containing any properties the client did NOT set. this includes properties the client left out, which means the server filled in a default for them โจ this argument is null if no MDN objects were successfully sent ๐ฅฒ
notSent: "Id[SetError]|null"- a map of the creation id to a SetError object for each record that failed to be sent, or null if everything went through ๐
in this context, the existing SetError types defined in [RFC8620] and [RFC8621] get interpreted like this, bestie ๐
notFound: the reference"forEmailId"cannot be found, or has no valid"Disposition-Notification-To"header field ๐forbidden:"MDN/send"would violate an Access Control List (ACL) or other permissions policy ๐ซforbiddenFrom: the user is not allowed to use the given"finalRecipient"property ๐ซฃoverQuota:"MDN/send"would exceed a server-defined limit on the number or total size of sent MDNs. it could include limitations on sent messages ๐ฆtooLarge:"MDN/send"would result in an MDN that exceeds a server-defined limit for the maximum size of an MDN, or more generally, on email message ๐rateLimit: too many MDNs or email messages have been created recently, and a server-defined rate limit has been reached. it may work if tried again later โณinvalidProperties: the record given is invalid in some way ๐
and this one is NEW ๐
mdnAlreadySent: the message has the"$mdnsent"keyword already set ๐ญ
if the "accountId" or "identityId" given cannot be found, the method call gets rejected with an "invalidArguments" error. bestie, that's not it ๐
โโ๏ธ
the client MUST NOT issue an "MDN/send" request if the message has the "$mdnsent" keyword set. no cap, don't do it ๐
when sending the MDN, the server is in charge of generating the "originalRecipient" and "originalMessageId" fields according to [RFC8098]. "finalRecipient" also usually gets generated by the server based on the provided identity, but if the client specifies it and it's allowed (see Section 5), the server uses the client-provided value ๐ง
the client is expected to explicitly update each "Email" that had an "MDN/send" invoked on it, so the "$mdnsent" keyword gets set on these messages ๐ to make sure that happens, the server MUST reject an "MDN/send" that does not result in setting the keyword "$mdnsent". so, the server MUST check that the "onSuccessUpdateEmail" property of the method is correctly set to update this keyword. it's giving accountability ๐
2.2. MDN/parse ๐
this method lets a client parse blobs as messages in the style of [RFC5322] to get MDN objects ๐ง you can use it to parse and get detailed info about blobs referenced in the "mdnBlobIds" of the EmailSubmission object, or any email message the client expects to be an MDN ๐
the "forEmailId" property can be null or missing if the "originalMessageId" property is missing, or doesn't refer to an existing message, or if the server can't efficiently calculate the related message (for example, if several messages get the same "Message-ID" header field) ๐
the "MDN/parse" method takes these arguments:
accountId: "Id"- the id of the account to use ๐ก
blobIds: "Id[]"- the ids of the blobs to parse ๐งฉ
the response has these arguments:
accountId: "Id"- the id of the account used for the call ๐ท
parsed: "Id[MDN]|null"- a map of the blob id to a parsed MDN representation for each successfully parsed blob, or null if none ๐
notParsable: "Id[]|null"- a list of ids given that corresponds to blobs that could not be parsed as MDNs, or null if none ๐ซ
notFound: "Id[]|null"- a list of blob ids given that could not be found, or null if none ๐ต๏ธโโ๏ธ
the following additional errors may come back instead of the "MDN/parse" response ๐ฅบ
requestTooLarge: the number of ids the client requested is more than the maximum number the server is willing to process in a single method call ๐๏ธโโ๏ธinvalidArguments: if the given"accountId"cannot be found, the MDN parsing gets rejected with an"invalidArguments"error ๐
3. Samples ๐
3.1. Sending an MDN for a Received Email Message ๐
a client can use this request to send an MDN back to the sender ๐จ
[[ "MDN/send", {
"accountId": "ue150411c",
"identityId": "I64588216",
"send": {
"k1546": {
"forEmailId": "Md45b47b4877521042cec0938",
"subject": "Read receipt for: World domination",
"textBody": "This receipt shows that the email has been
displayed on your recipient's computer. There is no
guarantee it has been read or understood.",
"reportingUA": "joes-pc.cs.example.com; Foomail 97.1",
"disposition": {
"actionMode": "manual-action",
"sendingMode": "mdn-sent-manually",
"type": "displayed"
},
"extension": {
"EXTENSION-EXAMPLE": "example.com"
}
}
},
"onSuccessUpdateEmail": {
"#k1546": {
"keywords/$mdnsent": true
}
}
}, "0" ]]
if the email id matches an existing email message that doesn't have the "$mdnsent" keyword, the server can answer like this ๐
[[ "MDN/send", {
"accountId": "ue150411c",
"sent": {
"k1546": {
"finalRecipient": "rfc822; john@example.com",
"originalMessageId": "<199509192301.23456@example.org>"
}
}
}, "0" ],
[ "Email/set", {
"accountId": "ue150411c",
"oldState": "23",
"newState": "42",
"updated": {
"Md45b47b4877521042cec0938": {}
}
}, "0" ]]
if the "$mdnsent" keyword has already been set, the server can answer with an error ๐
โโ๏ธ
[[ "MDN/send", {
"accountId": "ue150411c",
"notSent": {
"k1546": {
"type": "mdnAlreadySent",
"description" : "$mdnsent keyword is already present"
}
}
}, "0" ]]
3.2. Asking for an MDN When Sending an Email Message โ๏ธ
this is done with the "Email/set" "create" method of [RFC8621] ๐ซถ
[[ "Email/set", {
"accountId": "ue150411c",
"create": {
"k2657": {
"mailboxIds": {
"2ea1ca41b38e": true
},
"keywords": {
"$seen": true,
"$draft": true
},
"from": [{
"name": "Joe Bloggs",
"email": "joe@example.com"
}],
"to": [{
"name": "John",
"email": "john@example.com"
}],
"header:Disposition-Notification-To:asText": "joe@example.com",
"subject": "World domination",
...
}
}
}, "0" ]]
note the specified "Disposition-Notification-To" header field, which says where to send the MDN (usually the sender of the message) ๐ฆ
3.3. Parsing a Received MDN ๐ง
the client issues a parse request ๐ค
[[ "MDN/parse", {
"accountId": "ue150411c",
"blobIds": [ "0f9f65ab-dc7b-4146-850f-6e4881093965" ]
}, "0" ]]
the server responds ๐ฅ
[[ "MDN/parse", {
"accountId": "ue150411c",
"parsed": {
"0f9f65ab-dc7b-4146-850f-6e4881093965": {
"forEmailId": "Md45b47b4877521042cec0938",
"subject": "Read receipt for: World domination",
"textBody": "This receipt shows that the email has been
displayed on your recipient's computer. There is no
guarantee it has been read or understood.",
"reportingUA": "joes-pc.cs.example.com; Foomail 97.1",
"disposition": {
"actionMode": "manual-action",
"sendingMode": "mdn-sent-manually",
"type": "displayed"
},
"finalRecipient": "rfc822; john@example.com",
"originalMessageId": "<199509192301.23456@example.org>"
}
}
}, "0" ]]
if a blob id isn't found, the server would respond like this ๐ฌ
[[ "MDN/parse", {
"accountId": "ue150411c",
"notFound": [ "0f9f65ab-dc7b-4146-850f-6e4881093965" ]
}, "0" ]]
if the blob id has been found but isn't parsable, the server would respond like this ๐ซ
[[ "MDN/parse", {
"accountId": "ue150411c",
"notParsable": [ "0f9f65ab-dc7b-4146-850f-6e4881093965" ]
}, "0" ]]
4. IANA Considerations ๐
4.1. JMAP Capability Registration for "mdn" ๐ธ
this section registers the "mdn" JMAP Capability in the "JMAP Capabilities" registry, like so ๐
Capability Name: "urn:ietf:params:jmap:mdn"
Intended Use: common
Change Controller: IETF
Security and Privacy Considerations: This document, Section 5.
Reference: This document
4.2. JMAP Error Codes Registration for "mdnAlreadySent" ๐
IANA has registered one new error code in the "JMAP Error Codes" registry, as defined in [RFC8620] โจ
JMAP Error Code: mdnAlreadySent
Intended Use: common
Change Controller: IETF
Description: The message has the "$mdnsent" keyword already set.
The client MUST NOT try again to send an MDN for this message.
Reference: This document, Section 2.1
5. Security Considerations ๐ก๏ธ
the same considerations about MDN (see [RFC8098] and [RFC3503]) apply to this document ๐
to reinforce trust between the user sending an email message and the identity of that user, the server SHOULD validate, in conformance to the provided Identity, that the user is permitted to use the "finalRecipient" value, and return a "forbiddenFrom" error if not. this is a big deal, bestie, so don't skip it ๐ญ๐
6. Normative References โจ
okay bestie, this is the reading list ๐ the spec leans on these docs, so you'd want them open in another tab too ๐ค
- [RFC2119]: Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI
10.17487/RFC2119, March 1997,https://www.rfc-editor.org/info/rfc2119. - [RFC3503]: Melnikov, A., "Message Disposition Notification (MDN) profile for Internet Message Access Protocol (IMAP)", RFC 3503, DOI
10.17487/RFC3503, March 2003,https://www.rfc-editor.org/info/rfc3503. - [RFC5322]: Resnick, P., Ed., "Internet Message Format", RFC 5322, DOI
10.17487/RFC5322, October 2008,https://www.rfc-editor.org/info/rfc5322. - [RFC8098]: Hansen, T., Ed. and A. Melnikov, Ed., "Message Disposition Notification", STD 85, RFC 8098, DOI
10.17487/RFC8098, February 2017,https://www.rfc-editor.org/info/rfc8098. - [RFC8174]: Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI
10.17487/RFC8174, May 2017,https://www.rfc-editor.org/info/rfc8174. - [RFC8620]: Jenkins, N. and C. Newman, "The JSON Meta Application Protocol (JMAP)", RFC 8620, DOI
10.17487/RFC8620, July 2019,https://www.rfc-editor.org/info/rfc8620. - [RFC8621]: Jenkins, N. and C. Newman, "The JSON Meta Application Protocol (JMAP) for Mail", RFC 8621, DOI
10.17487/RFC8621, August 2019,https://www.rfc-editor.org/info/rfc8621.
Author's Address ๐
this is where you find the editor, Raphaรซl Ouazana, at Linagora ๐ธ
Raphaรซl Ouazana (editor)
Linagora
100 Terrasse Boieldieu - Tour Franklin
92042 Paris - La Dรฉfense CEDEX
France
Email: rouazana@linagora.com
URI: https://www.linagora.com
and that's the whole spec, bestie ๐ฆโจ go be a JMAP main character ๐๐
๐ฆ where this page comes from
- ietf: rfc9007 2021-03
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms