| rfc9404.original.xml | rfc9404.xml | |||
|---|---|---|---|---|
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="UTF-8"?> | |||
| <!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Process | ||||
| or - mmark.miek.nl" --> | <!DOCTYPE rfc [ | |||
| <rfc version="3" ipr="trust200902" docName="draft-ietf-jmap-blob-18" submissionT | <!ENTITY nbsp " "> | |||
| ype="IETF" category="std" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclud | <!ENTITY zwsp "​"> | |||
| e" updates="8620" indexInclude="true" consensus="true"> | <!ENTITY nbhy "‑"> | |||
| <!ENTITY wj "⁠"> | ||||
| ]> | ||||
| <rfc version="3" ipr="trust200902" docName="draft-ietf-jmap-blob-18" number="940 | ||||
| 4" submissionType="IETF" category="std" consensus="true" xml:lang="en" xmlns:xi= | ||||
| "http://www.w3.org/2001/XInclude" updates="8620" obsoletes="" | ||||
| symRefs="true" sortRefs="true" tocInclude="true"> | ||||
| <front> | <front> | |||
| <title abbrev="JMAP Blob">JMAP Blob management extension</title><seriesInfo valu | ||||
| e="draft-ietf-jmap-blob-18" stream="IETF" status="standard" name="Internet-Draft | ||||
| "></seriesInfo> | ||||
| <author role="editor" initials="B." surname="Gondwana" fullname="Bron Gondwana"> | ||||
| <organization>Fastmail</organization><address><postal><street>Level 2, 114 Willi | ||||
| am St</street> | ||||
| <city>Melbourne</city> | ||||
| <code>VIC 3000</code> | ||||
| <country>Australia</country> | ||||
| </postal><email>brong@fastmailteam.com</email> | ||||
| <uri>https://www.fastmail.com</uri> | ||||
| </address></author><date year="2023" month="January" day="4"></date> | ||||
| <area>Applications</area> | ||||
| <workgroup>JMAP</workgroup> | ||||
| <keyword>jmap</keyword> | ||||
| <abstract> | <title abbrev="JMAP Blob">JSON Meta Application Protocol (JMAP) Blob Managemen | |||
| <t>The JMAP base protocol (RFC8620) provides the ability to upload and download | t Extension</title> | |||
| arbitrary binary data via HTTP POST and GET on defined endpoint. This binary | <seriesInfo name="RFC" value="9404"/> | |||
| data is called a "blob".</t> | <author role="editor" initials="B." surname="Gondwana" fullname="Bron Gondwana | |||
| <t>This extension adds additional ways to create and access blobs, by making | "> | |||
| <organization>Fastmail</organization> | ||||
| <address> | ||||
| <postal> | ||||
| <street>Level 2, 114 William St</street> | ||||
| <city>Melbourne</city> | ||||
| <region>VIC</region> | ||||
| <code>3000</code> | ||||
| <country>Australia</country> | ||||
| </postal> | ||||
| <email>brong@fastmailteam.com</email> | ||||
| <uri>https://www.fastmail.com</uri> | ||||
| </address></author> | ||||
| <date year="2023" month="August"></date> | ||||
| <area>art</area> | ||||
| <workgroup>jmap</workgroup> | ||||
| <keyword>jmap</keyword> | ||||
| <abstract> | ||||
| <t>The JSON Meta Application Protocol (JMAP) base protocol (RFC 8620) provides | ||||
| the ability to upload and download arbitrary binary data via HTTP POST and GET | ||||
| on a defined endpoint. This binary data is called a "blob".</t> | ||||
| <t>This extension adds additional ways to create and access blobs by making | ||||
| inline method calls within a standard JMAP request.</t> | inline method calls within a standard JMAP request.</t> | |||
| <t>This extension also adds a reverse lookup mechanism to discover where blobs | <t>This extension also adds a reverse lookup mechanism to discover where blobs | |||
| are referenced within other data types.</t> | are referenced within other data types.</t> | |||
| </abstract> | </abstract> | |||
| </front> | </front> | |||
| <middle> | <middle> | |||
| <section anchor="introduction"><name>Introduction</name> | <section anchor="introduction"><name>Introduction</name> | |||
| <t>Sometimes JMAP (<xref target="RFC8620"></xref>) interactions require creating | <t>Sometimes JMAP <xref target="RFC8620"></xref> interactions require creating | |||
| a blob and then | a blob and then referencing it. In the same way that IMAP literals were | |||
| referencing it. In the same way that IMAP Literals were extended by <xref targe | extended by <xref target="RFC7888"></xref>, embedding small blobs directly | |||
| t="RFC7888"></xref>, | into the JMAP method calls array can be an option for reducing round trips.</t> | |||
| embedding small blobs directly into the JMAP method calls array can be an | ||||
| option for reducing roundtrips.</t> | ||||
| <t>Likewise, when fetching an object, it can be useful to also fetch the raw | <t>Likewise, when fetching an object, it can be useful to also fetch the raw | |||
| content of that object without a separate roundtrip.</t> | content of that object without a separate round trip.</t> | |||
| <t>Since raw blobs may contain arbitrary binary data, this document defines | <t>Since raw blobs may contain arbitrary binary data, this document defines | |||
| a use of the base64 coding specified in <xref target="RFC4648"></xref> for both creating and | a use of the base64 coding specified in <xref target="RFC4648"></xref> for both creating and | |||
| fetching blob data.</t> | fetching blob data.</t> | |||
| <t>Where JMAP is being proxied through a system which applies additional | <t>When JMAP is proxied through a system that applies additional | |||
| access restrictions, it can be useful to know which objects reference | access restrictions, it can be useful to know which objects reference | |||
| any particular blob, and this document defines a way to discover those | any particular blob; this document defines a way to discover those | |||
| references.</t> | references.</t> | |||
| </section> | </section> | |||
| <section anchor="conventions-used-in-this-document"><name>Conventions Used In Th | <section anchor="conventions-used-in-this-document"><name>Conventions Used in Th | |||
| is Document</name> | is Document</name> | |||
| <t>The key words "MUST", "MUST NOT", "REQUIRED", & | <t> | |||
| quot;SHALL", "SHALL | The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", | |||
| NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", | "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL | |||
| "NOT RECOMMENDED", | NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", | |||
| "MAY", and "OPTIONAL" in this document are to be interpreted | "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>", | |||
| as | "<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are | |||
| described in BCP 14 <xref target="RFC2119"></xref> <xref target="RFC8174"></xref | to be interpreted as described in BCP 14 <xref target="RFC2119"/> | |||
| > when, and only when, | <xref target="RFC8174"/> when, and only when, they appear in all capitals, | |||
| they appear in all capitals, as shown here.</t> | as shown here. | |||
| <t>The definitions of JSON keys and datatypes in the document follow | </t> | |||
| the conventions described in the core JMAP specification <xref target="RFC8620"> | <t>The definitions of JSON keys and datatypes in the document follow the | |||
| </xref>.</t> | conventions described in <xref target="RFC8620"></xref>.</t> | |||
| </section> | </section> | |||
| <section anchor="addition-to-the-capabilities-object"><name>Addition to the Capa bilities Object</name> | <section anchor="addition-to-the-capabilities-object"><name>Addition to the Capa bilities Object</name> | |||
| <t>The capabilities object is returned as part of the JMAP Session | <t>The capabilities object is returned as part of the JMAP Session | |||
| object; see <xref target="RFC8620"></xref>, Section 2.</t> | object; see <xref target="RFC8620" sectionFormat="comma" section="2"></xref>.</t > | |||
| <t>This document defines an additional capability URI.</t> | <t>This document defines an additional capability URI.</t> | |||
| <section anchor="urn-ietf-params-jmap-blob"><name>urn:ietf:params:jmap:blob</nam e> | <section anchor="urn-ietf-params-jmap-blob"><name>urn:ietf:params:jmap:blob</nam e> | |||
| <t>The capability <tt>urn:ietf:params:jmap:blob</tt> being present in the | <t>The presence of the capability <tt>urn:ietf:params:jmap:blob</tt> in the | |||
| "accountCapabilities" property of an account represents support | accountCapabilities property of an account represents support for additional | |||
| for additional API methods on the Blob datatype. Servers that | API methods on the Blob datatype. Servers that include the capability in one | |||
| include the capability in one or more "accountCapabilities" | or more accountCapabilities properties <bcp14>MUST</bcp14> also include the | |||
| properties MUST also include the property in the "capabilities" | property in the capabilities property.</t> | |||
| property.</t> | ||||
| <t>The value of this property in the JMAP session "capabilities" | <t>The value of this property in the JMAP Session capabilities | |||
| property MUST be an empty object.</t> | property <bcp14>MUST</bcp14> be an empty object.</t> | |||
| <t>The value of this property in an account's "accountCapabilities" | <t>The value of this property in an account's accountCapabilities | |||
| property is an object that MUST contain the following information | property is an object that <bcp14>MUST</bcp14> contain the following information | |||
| on server capabilities and permissions for that account:</t> | on server capabilities and permissions for that account:</t> | |||
| <ul> | <ul> | |||
| <li><t>maxSizeBlobSet: <tt>UnsignedInt|null</tt></t> | <li><t>maxSizeBlobSet: "UnsignedInt|null"</t> | |||
| <t>This is the maximum size of blob (in octets) that the server | <t>The maximum size of the blob (in octets) that the server will | |||
| will allow to be created (including blobs created by concatenating | allow to be created (including blobs created by concatenating multiple data | |||
| multiple data sources together).</t> | sources together).</t> | |||
| <t>Clients MUST NOT attempt to create blobs larger than this size.</t> | <t>Clients <bcp14>MUST NOT</bcp14> attempt to create blobs larger than this | |||
| <t>If this value is <tt>null</tt>, then clients are not required to limit the | size.</t> | |||
| size of blob they try to create, though servers can always reject | ||||
| creation of blobs regardless of size; e.g. due to lack of disk space, | <t>If this value is <tt>null</tt>, then clients are not required to limit | |||
| or per-user rate limits.</t> | the size of the blob they try to create, though servers can always reject | |||
| </li> | creation of blobs regardless of size, e.g., due to lack of disk space or | |||
| <li><t>maxDataSources: <tt>UnsignedInt</tt></t> | per-user rate limits.</t></li> | |||
| <t>The maximum number of DataSourceObjects allowed per | <li><t>maxDataSources: "UnsignedInt"</t> | |||
| creation in a Blob/upload.</t> | <t>The maximum number of DataSourceObjects allowed per creation in a | |||
| <t>Servers MUST allow at least 64 DataSourceObjects per creation.</t> | Blob/upload.</t> | |||
| </li> | <t>Servers <bcp14>MUST</bcp14> allow at least 64 DataSourceObjects per | |||
| <li><t>supportedTypeNames: <tt>String[]</tt></t> | creation.</t></li> | |||
| <t>An array of data type names that are supported for <tt>Blob/lookup</tt>. If | <li><t>supportedTypeNames: "String[]"</t> | |||
| the | <t>An array of data type names that are supported for <tt>Blob/lookup</tt>. | |||
| server does not support lookups then this will be the empty list.</t> | If the server does not support lookups, then this will be the empty list.</t> | |||
| <t>NOTE, the supportedTypeNames list may include private types which are not | <t>Note that the supportedTypeNames list may include private types that are no | |||
| in the JMAP Types Registry defined by this document. Clients MUST ignore | t | |||
| type names they do not recognise.</t> | in the "JMAP Data Types" registry defined by this document. Clients | |||
| </li> | <bcp14>MUST</bcp14> ignore type names they do not recognise.</t></li> | |||
| <li><t>supportedDigestAlgorithms: <tt>String[]</tt></t> | <li><t>supportedDigestAlgorithms: "String[]"</t> | |||
| <t>An array of supported digest algorithms that are supported for <tt>Blob/get</ | <t>An array of supported digest algorithms that are supported for | |||
| tt>. | <tt>Blob/get</tt>. If the server does not support calculating blob digests, | |||
| If the server does not support calculating blob digests, then this will be | then this will be the empty list. Algorithms in this list | |||
| the empty list. Algorithms in this list MUST be present in the HTTP Digest | <bcp14>MUST</bcp14> be present in the "HTTP Digest Algorithm Values" | |||
| Algorithms registry defined by <xref target="RFC3230"></xref>, and are always lo | registry defined by <xref target="RFC3230"></xref>; however, in JMAP, they | |||
| wercased.</t> | must be lowercased, e.g., "md5" rather than "MD5".</t> | |||
| <t>Clients SHOULD prefer algorithms listed earlier in this list.</t> | <t>Clients <bcp14>SHOULD</bcp14> prefer algorithms listed earlier in this | |||
| </li> | list.</t></li> | |||
| </ul> | </ul> | |||
| <section anchor="capability-example"><name>Capability Example</name> | <section anchor="capability-example"><name>Capability Example</name> | |||
| <artwork>{ | <sourcecode type="json"><![CDATA[{ | |||
| "capabilities": { | "capabilities": { | |||
| ..., | ..., | |||
| "urn:ietf:params:jmap:blob": {} | "urn:ietf:params:jmap:blob": {} | |||
| }, | }, | |||
| "accounts": { | "accounts": { | |||
| "A13842": { | "A13842": { | |||
| ... | ... | |||
| "accountCapabilities": { | "accountCapabilities": { | |||
| "urn:ietf:params:jmap:blob": { | "urn:ietf:params:jmap:blob": { | |||
| "maxSizeBlobSet": 50000000, | "maxSizeBlobSet": 50000000, | |||
| "maxDataSources": 100, | "maxDataSources": 100, | |||
| "supportedTypeNames" : [ | "supportedTypeNames" : [ | |||
| "Mailbox", | "Mailbox", | |||
| "Thread", | "Thread", | |||
| "Email" | "Email" | |||
| ], | ], | |||
| "supportedDigestAlgorithms" : [ | "supportedDigestAlgorithms" : [ | |||
| "sha", | "sha", | |||
| "sha-256" | "sha-256" | |||
| ] | ] | |||
| } | } | |||
| } | } | |||
| } | } | |||
| } | } | |||
| } | }]]></sourcecode> | |||
| </artwork> | ||||
| </section> | </section> | |||
| </section> | </section> | |||
| </section> | </section> | |||
| <section anchor="blob-methods"><name>Blob Methods</name> | <section anchor="blob-methods"><name>Blob Methods</name> | |||
| <t>A blob is a sequence of zero or more octets.</t> | <t>A blob is a sequence of zero or more octets.</t> | |||
| <t>The JMAP base spec <xref target="RFC8620"></xref> defines the <tt>Blob/copy</ | <t>JMAP <xref target="RFC8620" format="default"/> defines the | |||
| tt> method, which | <tt>Blob/copy</tt> method, which is unchanged by this specification and is | |||
| is unchanged by this specification, and is selected by the | selected by the <tt>urn:ietf:params:jmap:core</tt> capability.</t> | |||
| <tt>urn:ietf:params:jmap:core</tt> capability.</t> | <t>The following JMAP methods are selected by the | |||
| <t>The following JMAP Methods are selected by the | ||||
| <tt>urn:ietf:params:jmap:blob</tt> capability.</t> | <tt>urn:ietf:params:jmap:blob</tt> capability.</t> | |||
| <section anchor="blob-upload"><name>Blob/upload</name> | <section anchor="blob-upload"><name>Blob/upload</name> | |||
| <t>This is similar to a Foo/set from <xref target="RFC8620"></xref> in some ways | <t>This is similar to a Foo/set in <xref target="RFC8620"></xref> in some | |||
| , however blobs can not | ways. However, blobs cannot be updated or deleted, so only <tt>create</tt> is | |||
| be updated or deleted, so only <tt>create</tt> is allowed in the method call, an | allowed in the method call. Also, blobs do not have state, so there is no | |||
| d blobs | <tt>state</tt> field present in the method response.</t> | |||
| do not have state, so there is no <tt>state</tt> field present in the method res | ||||
| ponse.</t> | ||||
| <t><strong>Parameters</strong></t> | ||||
| <t><strong>Parameters</strong></t> | ||||
| <ul> | <ul> | |||
| <li><t>accountId: <tt>Id</tt></t> | <li><t>accountId: "Id"</t> | |||
| <t>The id of the account in which the blobs will be created.</t> | <t>The id of the account in which the blobs will be created.</t></li> | |||
| </li> | <li><t>create: "Id[UploadObject]"</t> | |||
| <li><t>create: <tt>Id[UploadObject]</tt></t> | <t>A map of creation id to UploadObjects.</t></li> | |||
| <t>A map of creation id to UploadObjects.</t> | ||||
| </li> | ||||
| </ul> | </ul> | |||
| <t><strong>Result</strong></t> | <t><strong>Result</strong></t> | |||
| <t>The result is the same as for Foo/set in RFC8620, with <tt>created</tt> and < | <t>The result is the same as for Foo/set in <xref target="RFC8620" format="defau | |||
| tt>notCreated</tt> objects | lt"/>, with <tt>created</tt> and <tt>notCreated</tt> objects | |||
| mapping from the creationId.</t> | mapping from the creation id.</t> | |||
| <t>The <tt>created</tt> objects contain:</t> | <t>The <tt>created</tt> objects contain:</t> | |||
| <ul> | <ul> | |||
| <li><t>id: <tt>Id</tt></t> | <li><t>id: "Id"</t> | |||
| <t>the blobId which was created</t> | <t>The blobId that was created.</t></li> | |||
| </li> | <li><t>type: "String|null"</t> | |||
| <li><t>type: <tt>String|null</tt></t> | <t>The media type as given in the creation (if any). If not provided, the | |||
| <t>the media type as given in the creation (if any); or detected from content by | server <bcp14>MAY</bcp14> perform content analysis and return one of the | |||
| the | following: the calculated value, "application/octet-string", or | |||
| server; or null</t> | null.</t></li> | |||
| </li> | <li><t>size: "UnsignedInt"</t> | |||
| <li><t>size: <tt>UnsignedInt</tt></t> | <t>As per <xref target="RFC8620" format="default"/>, the size of the | |||
| <t>as per RFC8620 - the size of the created blob in octets</t> | created blob in octets.</t></li> | |||
| </li> | ||||
| </ul> | </ul> | |||
| <t>It will also contain any other properties identical to those that would | ||||
| be returned in the JSON response of the RFC8620 upload endpoint (which may | <t>The created objects will also contain any other properties identical to | |||
| be extended in the future - this document anticipates that implementations | those that would be returned in the JSON response of the upload endpoint | |||
| will extend both the upload endpoint and the Blob/upload responses in the | described in <xref target="RFC8620" format="default"/>. This may be | |||
| same way)</t> | extended in the future; in this document, it is anticipated that | |||
| <t>Or if there is a problem with a creation, then the server will return a <tt>n | implementations will extend both the upload endpoint and the Blob/upload | |||
| otCreated</tt> | responses in the same way.</t> | |||
| response with a map from the failed creationId to a <tt>SetError</tt> object.</t | ||||
| > | <t>If there is a problem with a creation, then the server will return a | |||
| <t>For each successful upload, servers MUST add an entry to the <tt>creationIds< | <tt>notCreated</tt> response with a map from the failed creation id to a | |||
| /tt> map | <tt>SetError</tt> object.</t> | |||
| for the request. This allows the blob id to be used via back-reference in | <t>For each successful upload, servers <bcp14>MUST</bcp14> add an entry to the | |||
| <tt>createdIds</tt> map (<xref target="RFC8620" sectionFormat="comma" | ||||
| section="3.3"/>) for the request; even if the caller did not explicitly pass a | ||||
| createdIds, the value must be available to later methods defined in the same | ||||
| Request Object. This allows the blobId to be used via back-reference in | ||||
| subsequent method calls.</t> | subsequent method calls.</t> | |||
| <t>The created blob will have the same lifetime and same expiry semantics as any | <t>The created blob will have the same lifetime and same expiry semantics as | |||
| other binary object created via the mechanism specified in [!@RFC8620] section 6 | any other binary object created via the mechanism specified in <xref | |||
| .</t> | target="RFC8620" sectionFormat="comma" section="6"/>.</t> | |||
| <t>Uploads using with this mechanism will be restricted by the maxUploadSize lim | ||||
| it for | <t>Uploads using this mechanism will be restricted by the maxUploadSize limit | |||
| JMAP requests specified by the server, and clients SHOULD consider using the upl | for JMAP requests specified by the server, and clients <bcp14>SHOULD</bcp14> | |||
| oad | consider using the upload mechanism defined by <xref target="RFC8620" | |||
| mechanism defined by [!@RFC8620] for blobs larger than a megabyte.</t> | format="default"/> for blobs larger than a megabyte.</t> | |||
| <t><strong>UploadObject</strong></t> | <t><strong>UploadObject</strong></t> | |||
| <ul> | <ul> | |||
| <li><t>data: <tt>DataSourceObject[]</tt></t> | <li><t>data: "DataSourceObject[]"</t> | |||
| <t>an array of zero or more octet sources in order (zero to create an empty blob | <t>An array of zero or more octet sources in order (zero to create an empty | |||
| ). | blob). The result of each of these sources is concatenated in | |||
| The result of each of these sources is concatenated together in order to create | order to create the blob.</t></li> | |||
| the blob.</t> | <li><t>type: "String|null" (default: null)</t> | |||
| </li> | <t>A hint for media type of the data.</t></li> | |||
| <li><t>type: <tt>String|null</tt> (default: null)</t> | ||||
| <t>hint for media type of the data</t> | ||||
| </li> | ||||
| </ul> | </ul> | |||
| <t><strong>DataSourceObject</strong></t> | <t><strong>DataSourceObject</strong></t> | |||
| <t>Exactly one of:</t> | <t>Exactly one of:</t> | |||
| <ul> | <ul spacing="normal"> | |||
| <li><t>data:asText: <tt>String|null</tt> (raw octets, must be UTF-8)</t> | <li><t>data:asText: "String|null" (raw octets, must be UTF-8)</t> | |||
| </li> | </li> | |||
| <li><t>data:asBase64: <tt>String|null</tt> (base64 representation of octets)</t> | <li><t>data:asBase64: "String|null" (base64 representation of octets)</t> | |||
| </li> | </li> | |||
| </ul> | </ul> | |||
| <t>or a blobId source:</t> | <t>or a blobId source:</t> | |||
| <ul> | <ul spacing="normal"> | |||
| <li><t>blobId: <tt>Id</tt></t> | <li><t>blobId: "Id"</t> | |||
| </li> | </li> | |||
| <li><t>offset: <tt>UnsignedInt|null</tt> (MAY be zero)</t> | <li><t>offset: "UnsignedInt|null" (<bcp14>MAY</bcp14> be zero)</t> | |||
| </li> | </li> | |||
| <li><t>length: <tt>UnsignedInt|null</tt> (MAY be zero)</t> | <li><t>length: "UnsignedInt|null" (<bcp14>MAY</bcp14> be zero)</t> | |||
| </li> | </li> | |||
| </ul> | </ul> | |||
| <t>If <tt>null</tt> then offset is assumed to be zero.</t> | ||||
| <t>If <tt>null</tt> then length is the remaining octets in the blob.</t> | <t>If <tt>null</tt>, then offset is assumed to be zero.</t> | |||
| <t>If the range can not be fully satisfied (i.e. begins or extends past | <t>If <tt>null</tt>, then length is the remaining octets in the blob.</t> | |||
| the end of the data in the blob) then the DataSourceObject is invalid | <t>If the range cannot be fully satisfied (i.e., it begins or extends past | |||
| the end of the data in the blob), then the DataSourceObject is invalid | ||||
| and results in a notCreated response for this creation id.</t> | and results in a notCreated response for this creation id.</t> | |||
| <t>If the data properties have any invalid references or invalid data | <t>If the data properties have any invalid references or invalid data | |||
| contained in them, the server MUST NOT guess as to the user's intent, | contained in them, the server <bcp14>MUST NOT</bcp14> guess the user's intent | |||
| and MUST reject the creation and return a notCreated response for that | and <bcp14>MUST</bcp14> reject the creation and return a notCreated response for | |||
| that | ||||
| creation id.</t> | creation id.</t> | |||
| <t>Likewise, invalid characters in the base64 of data:asBase64, or invalid | <t>Likewise, invalid characters in the base64 of data:asBase64 or invalid | |||
| UTF-8 in data:asText MUST result in a nonCreated response.</t> | UTF-8 in data:asText <bcp14>MUST</bcp14> result in a notCreated response.</t> | |||
| <t>It is envisaged that the definition for DataSourceObject might be | <t>It is envisaged that the definition for DataSourceObject might be | |||
| extended in the future, for example to fetch external content.</t> | extended in the future, for example, to fetch external content.</t> | |||
| <t>A server MUST accept at least 64 DataSourceObjects per create, as | <t>A server <bcp14>MUST</bcp14> accept at least 64 DataSourceObjects per create, | |||
| described in Section 3.1 of this document.</t> | as | |||
| described in <xref target="urn-ietf-params-jmap-blob" format="default"/> of this | ||||
| document.</t> | ||||
| <section anchor="blob-upload-simple-example"><name>Blob/upload simple example</n | <section anchor="blob-upload-simple-example"><name>Blob/upload Simple Example</n | |||
| ame> | ame> | |||
| <t>The data:asBase64 field is set over multiple lines for ease of | <t>The data:asBase64 field is set over multiple lines for ease of publication | |||
| publication here, however all data:asBase64 would be sent as a | here; however, the entire data:asBase64 field would be sent as a continuous | |||
| continuous string with no whitespace on the wire.</t> | string with no wrapping on the wire.</t> | |||
| <artwork>Method Call: | <t>Method Call:</t> | |||
| <sourcecode type="json"><![CDATA[ | ||||
| [ | [ | |||
| "Blob/upload", | "Blob/upload", | |||
| { | { | |||
| "accountId": "account1", | "accountId": "account1", | |||
| "create": { | "create": { | |||
| "1": { | "1": { | |||
| "data" : [ | "data" : [ | |||
| { | { | |||
| "data:asBase64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQM | "data:asBase64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKA | |||
| AAAAl21bKA | AAAA1BMVEX/AAAZ4gk3AAAAAXRSTlN/gFy0ywAAAApJRE | |||
| AAAA1BMVEX/AAAZ4gk3AAAAAXRSTlN/gFy0ywAAAApJRE | FUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=" | |||
| FUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=", | } | |||
| } | ], | |||
| ], | "type": "image/png" | |||
| "type": "image/png" | } | |||
| }, | } | |||
| }, | }, | |||
| }, | "R1" | |||
| "R1" | ]]]></sourcecode> | |||
| ] | ||||
| Response: | <t>Response:</t> | |||
| [ | <sourcecode type="json"><![CDATA[[ | |||
| "Blob/upload", | "Blob/upload", | |||
| { | { | |||
| "accountId" : "account1", | "accountId" : "account1", | |||
| "created" : { | "created" : { | |||
| "1": { | "1": { | |||
| "id" : "G4c6751edf9dd6903ff54b792e432fba781271beb", | "id" : "G4c6751edf9dd6903ff54b792e432fba781271beb", | |||
| "type" : "image/png", | "type" : "image/png", | |||
| "size" : 95 | "size" : 95 | |||
| }, | } | |||
| }, | } | |||
| }, | }, | |||
| "R1" | "R1" | |||
| ] | ]]]></sourcecode> | |||
| </artwork> | ||||
| </section> | </section> | |||
| <section anchor="blob-upload-complex-example"><name>Blob/upload complex example< /name> | <section anchor="blob-upload-complex-example"><name>Blob/upload Complex Example< /name> | |||
| <artwork>Method Calls: | <t>Method Calls:</t> | |||
| <sourcecode type="json"><![CDATA[ | ||||
| [ | [ | |||
| [ | [ | |||
| "Blob/upload", | "Blob/upload", | |||
| { | { | |||
| "create": { | "create": { | |||
| "b4": { | "b4": { | |||
| "data": [ | "data": [ | |||
| { | { | |||
| "data:asText": "The quick brown fox jumped over the | "data:asText": "The quick brown fox jumped over the lazy dog." | |||
| lazy dog." | ||||
| } | ||||
| ] | ||||
| } | ||||
| } | ||||
| }, | ||||
| "S4" | ||||
| ], | ||||
| [ | ||||
| "Blob/upload", | ||||
| { | ||||
| "create": { | ||||
| "cat": { | ||||
| "data": [ | ||||
| { | ||||
| "data:asText": "How" | ||||
| }, | ||||
| { | ||||
| "blobId": "#b4", | ||||
| "length": 7, | ||||
| "offset": 3 | ||||
| }, | ||||
| { | ||||
| "data:asText": "was t" | ||||
| }, | ||||
| { | ||||
| "blobId": "#b4", | ||||
| "length": 1, | ||||
| "offset": 1 | ||||
| }, | ||||
| { | ||||
| "data:asBase64": "YXQ/" | ||||
| } | ||||
| ] | ||||
| } | ||||
| } | } | |||
| }, | ] | |||
| "CAT" | } | |||
| ], | } | |||
| [ | }, | |||
| "Blob/get", | "S4" | |||
| { | ], | |||
| "properties": [ | [ | |||
| "data:asText", | "Blob/upload", | |||
| "size" | { | |||
| ], | "create": { | |||
| "ids": [ | "cat": { | |||
| "#cat" | "data": [ | |||
| ] | { | |||
| }, | "data:asText": "How" | |||
| "G4" | }, | |||
| ] | { | |||
| ] | "blobId": "#b4", | |||
| "length": 7, | ||||
| "offset": 3 | ||||
| }, | ||||
| { | ||||
| "data:asText": "was t" | ||||
| }, | ||||
| { | ||||
| "blobId": "#b4", | ||||
| "length": 1, | ||||
| "offset": 1 | ||||
| }, | ||||
| { | ||||
| "data:asBase64": "YXQ/" | ||||
| } | ||||
| ] | ||||
| } | ||||
| } | ||||
| }, | ||||
| "CAT" | ||||
| ], | ||||
| [ | ||||
| "Blob/get", | ||||
| { | ||||
| "properties": [ | ||||
| "data:asText", | ||||
| "size" | ||||
| ], | ||||
| "ids": [ | ||||
| "#cat" | ||||
| ] | ||||
| }, | ||||
| "G4" | ||||
| ] | ||||
| ]]]></sourcecode> | ||||
| Responses: | <t>Responses:</t> | |||
| [ | <sourcecode type="json"><![CDATA[[ | |||
| [ | [ | |||
| "Blob/upload", | "Blob/upload", | |||
| { | { | |||
| "oldState": null, | "oldState": null, | |||
| "created": { | "created": { | |||
| "b4": { | "b4": { | |||
| "id": "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e", | "id": "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e", | |||
| "size": 45, | "size": 45, | |||
| "type": "application/octet-stream" | "type": "application/octet-stream" | |||
| } | } | |||
| }, | }, | |||
| "notCreated": null, | "notCreated": null, | |||
| "accountId": "account1" | "accountId": "account1" | |||
| }, | }, | |||
| "S4" | "S4" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/upload", | "Blob/upload", | |||
| { | { | |||
| "oldState": null, | "oldState": null, | |||
| "created": { | "created": { | |||
| "cat": { | "cat": { | |||
| "id": "Gcc60576f036321ae6e8037ffc56bdee589bd3e23", | "id": "Gcc60576f036321ae6e8037ffc56bdee589bd3e23", | |||
| "size": 19, | "size": 19, | |||
| "type": "application/octet-stream" | "type": "application/octet-stream" | |||
| } | } | |||
| }, | }, | |||
| "notCreated": null, | "notCreated": null, | |||
| "accountId": "account1" | "accountId": "account1" | |||
| }, | }, | |||
| "CAT" | "CAT" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "list": [ | "list": [ | |||
| { | { | |||
| "id": "Gcc60576f036321ae6e8037ffc56bdee589bd3e23", | "id": "Gcc60576f036321ae6e8037ffc56bdee589bd3e23", | |||
| "data:asText": "How quick was that?", | "data:asText": "How quick was that?", | |||
| "size": 19 | "size": 19 | |||
| } | } | |||
| ], | ], | |||
| "notFound": [], | "notFound": [], | |||
| "accountId": "account1" | "accountId": "account1" | |||
| }, | }, | |||
| "G4" | "G4" | |||
| ] | ] | |||
| ] | ]]]></sourcecode> | |||
| </artwork> | ||||
| </section> | </section> | |||
| </section> | </section> | |||
| <section anchor="blob-get"><name>Blob/get</name> | <section anchor="blob-get"><name>Blob/get</name> | |||
| <t>A standard JMAP get, with two additional optional parameters:</t> | <t>A standard JMAP get, with two additional optional parameters:</t> | |||
| <ul> | <ul> | |||
| <li><t>offset: <tt>UnsignedInt|null</tt></t> | <li><t>offset: "UnsignedInt|null"</t> | |||
| <t>start this many octets into the blob data. If null or | <t>Start this many octets into the blob data. If null or unspecified, this | |||
| unspecified, this defaults to zero.</t> | defaults to zero.</t></li> | |||
| </li> | <li><t>length: "UnsignedInt|null"</t> | |||
| <li><t>length: <tt>UnsignedInt|null</tt></t> | <t>Return at most this many octets of the blob data. If null or | |||
| <t>return at most this many octets of the blob data. If null or | unspecified, then all remaining octets in the blob are returned. This can | |||
| unspecified, then all remaining octets in the blob are returned. | be considered equivalent to an infinitely large length value, except that | |||
| This can be considered equivalent to an infinitely large length | the isTruncated warning is not given unless the start offset is past the end | |||
| value, except that the isTruncated warning is not given unless | of the blob.</t></li> | |||
| the start offset is past the end of the blob.</t> | ||||
| </li> | ||||
| </ul> | </ul> | |||
| <t><strong>Request Properties:</strong></t> | <t><strong>Request Properties:</strong></t> | |||
| <t>Any of</t> | <t>Any of:</t> | |||
| <ul spacing="compact"> | <ul spacing="normal"> | |||
| <li>data:asText</li> | <li>data:asText</li> | |||
| <li>data:asBase64</li> | <li>data:asBase64</li> | |||
| <li>data (returns data:asText if the selected octets are valid UTF-8, or data:as Base64)</li> | <li>data (returns data:asText if the selected octets are valid UTF-8 or data:asB ase64)</li> | |||
| <li>digest:<algorithm> (where <algorithm> is one of the named algori thms in the <tt>supportedDigestAlgorithms</tt> capability)</li> | <li>digest:<algorithm> (where <algorithm> is one of the named algori thms in the <tt>supportedDigestAlgorithms</tt> capability)</li> | |||
| <li>size</li> | <li>size</li> | |||
| </ul> | </ul> | |||
| <t>If not given, properties defaults to <tt>data</tt> and <tt>size</tt>.</t> | <t>If not given, the properties default to <tt>data</tt> and <tt>size</tt>.</t> | |||
| <t><strong>Result Properties:</strong></t> | <t><strong>Result Properties:</strong></t> | |||
| <ul> | <ul> | |||
| <li><t>data:asText: <tt>String|null</tt></t> | <li><t>data:asText: "String|null"</t> | |||
| <t>the raw octets of the selected range if they are valid UTF-8, otherwise null< | <t>The raw octets of the selected range if they are valid UTF-8; otherwise, | |||
| /t> | null.</t></li> | |||
| </li> | <li><t>data:asBase64: "String"</t> | |||
| <li><t>data:asBase64: <tt>String</tt></t> | <t>The base64 encoding of the octets in the selected range.</t></li> | |||
| <t>the base64 encoding of the octets in the selected range</t> | <li><t>digest:<algorithm>: "String"</t> | |||
| </li> | <t>The base64 encoding of the digest of the octets in the selected range, | |||
| <li><t>digest:<algorithm> <tt>String</tt></t> | calculated using the named algorithm.</t></li> | |||
| <t>the base64 encoding of the digest of the octets in the selected range, | <li><t>isEncodingProblem: "Boolean" (default: false)</t></li> | |||
| calculated using the named algorithm</t> | <li><t>isTruncated: "Boolean" (default: false)</t></li> | |||
| </li> | <li><t>size: "UnsignedInt"</t> | |||
| <li><t>isEncodingProblem: <tt>Boolean</tt> (default: false)</t> | <t>The number of octets in the entire blob.</t></li> | |||
| </li> | ||||
| <li><t>isTruncated: <tt>Boolean</tt> (default: false)</t> | ||||
| </li> | ||||
| <li><t>size: <tt>UnsignedInt</tt></t> | ||||
| <t>the number of octets in the entire blob</t> | ||||
| </li> | ||||
| </ul> | </ul> | |||
| <t>The size value MUST always be the number of octets in the underlying blob, | ||||
| <t>The size value <bcp14>MUST</bcp14> always be the number of octets in the unde | ||||
| rlying blob, | ||||
| regardless of offset and length.</t> | regardless of offset and length.</t> | |||
| <t>The data fields contain a representation of the octets within the selected | <t>The data fields contain a representation of the octets within the selected | |||
| range that are present in the blob. If the octets selected are not valid | range that are present in the blob. If the octets selected are not valid | |||
| UTF-8 (including truncating in the middle of a multi-octet sequence) | UTF-8 (including truncating in the middle of a multi-octet sequence) | |||
| and <tt>data</tt> or <tt>data:asText</tt> was requested, then the key <tt>isEnco dingProblem</tt> | and <tt>data</tt> or <tt>data:asText</tt> was requested, then the key <tt>isEnco dingProblem</tt> | |||
| MUST be set to <tt>true</tt> and the <tt>data:asText</tt> response value MUST be <tt>null</tt>. | <bcp14>MUST</bcp14> be set to <tt>true</tt>, and the <tt>data:asText</tt> respon se value <bcp14>MUST</bcp14> be <tt>null</tt>. | |||
| In the case where <tt>data</tt> was requested and the data is not valid UTF-8, | In the case where <tt>data</tt> was requested and the data is not valid UTF-8, | |||
| then <tt>data:asBase64</tt> MUST be returned.</t> | then <tt>data:asBase64</tt> <bcp14>MUST</bcp14> be returned.</t> | |||
| <t>If the selected range requests data outside the blob (i.e. the offset+length | <t>If the selected range requests data outside the blob (i.e., the | |||
| is larger than the blob) then the result is either just the octets from the | offset+length is larger than the blob), then the result is either just the | |||
| offset to the end of the blob, or an empty string if the offset is past the | octets from the offset to the end of the blob or an empty string if the | |||
| end of the blob. Either way, the <tt>isTruncated</tt> property in the result MU | offset is past the end of the blob. Either way, the isTruncated | |||
| ST | property in the result <bcp14>MUST</bcp14> be set to <tt>true</tt> to tell the | |||
| be set to <tt>true</tt> to tell the client that the requested range could not be | client that the requested range could not be fully satisfied. If digest was | |||
| fully satisfied. If digest was requested, any <tt>digest</tt> is calculated on | requested, any <tt>digest</tt> is calculated on the octets that would be | |||
| the | returned for a <tt>data</tt> field.</t> | |||
| octets that would be returned for a <tt>data</tt> field.</t> | <t>Servers <bcp14>SHOULD</bcp14> store the size for blobs in a format that is | |||
| <t>Servers SHOULD store the size for blobs in a format which is efficient to | efficient to read, and clients <bcp14>SHOULD</bcp14> limit their request to | |||
| read, and clients SHOULD limit their request to just the size parameter if | just the size parameter if that is all they need, as fetching blob content | |||
| that is all they need, as fetching blob content could be significantly more | could be significantly more expensive and slower for the server.</t> | |||
| expensive and slower for the server.</t> | ||||
| <section anchor="blob-get-simple-example"><name>Blob/get simple example</name> | <section anchor="blob-get-simple-example"><name>Blob/get Simple Example</name> | |||
| <t>Where a blob containing the string "The quick brown fox jumped over | <t>In this example, a blob containing the string "The quick brown fox jumped ove | |||
| the lazy dog." has blobId <tt>Gc0854fb9fb03c41cce3802cb0d220529e6eef94e</tt | r | |||
| >.</t> | the lazy dog." has blobId <tt>Gc0854fb9fb03c41cce3802cb0d220529e6eef94e</tt>.</t | |||
| > | ||||
| <t>The first method call requests just the size for multiple blobs, and | <t>The first method call requests just the size for multiple blobs, and | |||
| the second requests both size and a short range of the data for one | the second requests both the size and a short range of the data for one | |||
| of the blobs.</t> | of the blobs.</t> | |||
| <artwork>Method Calls: | <t>Method Calls:</t> | |||
| [ | <sourcecode type="json"><![CDATA[[ | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "accountId" : "account1", | "accountId" : "account1", | |||
| "ids" : [ | "ids" : [ | |||
| "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e", | "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e", | |||
| "not-a-blob" | "not-a-blob" | |||
| ], | ], | |||
| "properties" : [ | "properties" : [ | |||
| "data:asText", | "data:asText", | |||
| "digest:sha", | "digest:sha", | |||
| "size" | "size" | |||
| ] | ] | |||
| }, | }, | |||
| "R1" | "R1" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "accountId" : "account1", | "accountId" : "account1", | |||
| "ids" : [ | "ids" : [ | |||
| "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e" | "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e" | |||
| ], | ], | |||
| "properties" : [ | "properties" : [ | |||
| "data:asText", | "data:asText", | |||
| "digest:sha", | "digest:sha", | |||
| "digest:sha-256", | "digest:sha-256", | |||
| "size" | "size" | |||
| ], | ], | |||
| "offset" : 4, | "offset" : 4, | |||
| "length" : 9 | "length" : 9 | |||
| }, | }, | |||
| "R2" | "R2" | |||
| ] | ] | |||
| ] | ]]]></sourcecode> | |||
| Responses: | <t>Responses:</t> | |||
| <sourcecode type="json"><![CDATA[ | ||||
| [ | [ | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | ||||
| "accountId": "account1", | ||||
| "list": [ | ||||
| { | { | |||
| "accountId": "account1", | "id": "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e", | |||
| "list": [ | "data:asText": "The quick brown fox jumped over the lazy dog.", | |||
| { | "digest:sha": "wIVPufsDxBzOOALLDSIFKebu+U4=", | |||
| "id": "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e", | "size": 45 | |||
| "data:asText": "The quick brown fox jumped over the laz | } | |||
| y dog.", | ||||
| "digest:sha": "wIVPufsDxBzOOALLDSIFKebu+U4=", | ||||
| "size": 45 | ||||
| } | ||||
| ], | ||||
| "notFound": [ | ||||
| "not-a-blob" | ||||
| ] | ||||
| }, | ||||
| "R1" | ||||
| ], | ], | |||
| [ | "notFound": [ | |||
| "Blob/get", | "not-a-blob" | |||
| { | ||||
| "accountId": "account1", | ||||
| "list": [ | ||||
| { | ||||
| "id": "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e", | ||||
| "data:asText": "quick bro", | ||||
| "digest:sha": "QiRAPtfyX8K6tm1iOAtZ87Xj3Ww=", | ||||
| "digest:sha-256": "gdg9INW7lwHK6OQ9u0dwDz2ZY/gubi0En0xl | ||||
| FpKt0OA=", | ||||
| "size": 45 | ||||
| } | ||||
| ] | ||||
| }, | ||||
| "R2" | ||||
| ] | ] | |||
| ] | }, | |||
| "R1" | ||||
| ], | ||||
| [ | ||||
| "Blob/get", | ||||
| { | ||||
| "accountId": "account1", | ||||
| "list": [ | ||||
| { | ||||
| "id": "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e", | ||||
| "data:asText": "quick bro", | ||||
| "digest:sha": "QiRAPtfyX8K6tm1iOAtZ87Xj3Ww=", | ||||
| "digest:sha-256": "gdg9INW7lwHK6OQ9u0dwDz2ZY/gubi0En0xlFpKt0OA=", | ||||
| "size": 45 | ||||
| } | ||||
| ] | ||||
| }, | ||||
| "R2" | ||||
| ] | ||||
| ]]]></sourcecode> | ||||
| </artwork> | ||||
| </section> | </section> | |||
| <section anchor="blob-get-example-with-range-and-encoding-errors"><name>Blob/get | <section anchor="blob-get-example-with-range-and-encoding-errors"><name>Blob/get | |||
| example with range and encoding errors</name> | Example with Range and Encoding Errors</name> | |||
| <t>The <tt>b1</tt> value is the text: "The quick brown fox jumped over the | ||||
| \x81\x81 fox" | <t>The <tt>b1</tt> value is the text "The quick brown fox jumped over the \x81\x | |||
| which contains an invalid utf8 sequence.</t> | 81 dog.", which contains an invalid UTF-8 sequence.</t> | |||
| <t>The results have the following interesting properties:</t> | <t>The results have the following properties:</t> | |||
| <ul> | <ul> | |||
| <li><t>G1: defaults to <tt>data</tt> and <tt>size</tt> - so b1 returns <tt>isEnc | <li><t>G1: Defaults to <tt>data</tt> and <tt>size</tt>, so b1 returns | |||
| odingProblem</tt> | <tt>isEncodingProblem</tt> and a base64 value.</t> | |||
| and a base64 value.</t> | ||||
| </li> | </li> | |||
| <li><t>G2: since <tt>data:asText</tt> was explicitly selected, does not attempt | <li><t>G2: Since <tt>data:asText</tt> was explicitly selected, does not | |||
| to | attempt to return a value for the data, just <tt>isEncodingProblem</tt> for | |||
| return a value for the data, just <tt>isEncodingProblem</tt> for b1.</t> | b1.</t> | |||
| </li> | </li> | |||
| <li><t>G3: since only <tt>data:asBase64</tt> was requested, there is no encoding | <li><t>G3: Since only <tt>data:asBase64</tt> was requested, there is no | |||
| problem and both values are returned.</t> | encoding problem, and both values are returned.</t> | |||
| </li> | </li> | |||
| <li><t>G4: since the requested range could be satisfied as text, both blobs | <li><t>G4: Since the requested range could be satisfied as text, both blobs | |||
| are returned as <tt>data:asText</tt> and there is no encoding problem.</t> | are returned as <tt>data:asText</tt>, and there is no encoding problem.</t> | |||
| </li> | </li> | |||
| <li><t>G5: both blobs cannot satisfy the requested range, so isTruncated is | <li><t>G5: Both blobs cannot satisfy the requested range, so isTruncated is | |||
| true for both.</t> | true for both.</t> | |||
| </li> | </li> | |||
| </ul> | </ul> | |||
| <t>Note: some values have been wrapped for line length - there would be | <aside><t>Note: Some values have been wrapped for line length. There would be | |||
| no whitespace in the <tt>data:asBase64</tt> values on the wire</t> | no wrapping in the <tt>data:asBase64</tt> values on the wire.</t></aside> | |||
| <artwork>Method calls: | <t>Method Calls:</t> | |||
| [ | <sourcecode type="json"><![CDATA[[ | |||
| [ | [ | |||
| "Blob/upload", | "Blob/upload", | |||
| { | { | |||
| "create": { | "create": { | |||
| "b1": { | "b1": { | |||
| "data": [ | "data": [ | |||
| { | { | |||
| "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZ | "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW | |||
| W | Qgb3ZlciB0aGUggYEgZG9nLg==" | |||
| Qgb3ZlciB0aGUggYEgZG9nLg==" | ||||
| } | } | |||
| ] | ] | |||
| }, | }, | |||
| "b2": { | "b2": { | |||
| "data": [ | "data": [ | |||
| { | { | |||
| "data:asText": "hello world" | "data:asText": "hello world" | |||
| } | } | |||
| ], | ], | |||
| "type" : "text/plain" | "type" : "text/plain" | |||
| } | } | |||
| } | } | |||
| }, | }, | |||
| "S1" | "S1" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "ids": [ | "ids": [ | |||
| "#b1", | "#b1", | |||
| "#b2" | "#b2" | |||
| ] | ] | |||
| }, | }, | |||
| "G1" | "G1" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "ids": [ | "ids": [ | |||
| "#b1", | "#b1", | |||
| "#b2" | "#b2" | |||
| ], | ], | |||
| "properties": [ | "properties": [ | |||
| "data:asText", | "data:asText", | |||
| "size" | "size" | |||
| ] | ] | |||
| }, | }, | |||
| "G2" | "G2" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "ids": [ | "ids": [ | |||
| "#b1", | "#b1", | |||
| "#b2" | "#b2" | |||
| ], | ], | |||
| "properties": [ | "properties": [ | |||
| "data:asBase64", | "data:asBase64", | |||
| "size" | "size" | |||
| ] | ] | |||
| }, | }, | |||
| "G3" | "G3" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "offset": 0, | "offset": 0, | |||
| "length": 5, | "length": 5, | |||
| "ids": [ | "ids": [ | |||
| "#b1", | "#b1", | |||
| "#b2" | "#b2" | |||
| ] | ] | |||
| }, | }, | |||
| "G4" | "G4" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "offset": 20, | "offset": 20, | |||
| "length": 100, | "length": 100, | |||
| "ids": [ | "ids": [ | |||
| "#b1", | "#b1", | |||
| "#b2" | "#b2" | |||
| ] | ] | |||
| }, | }, | |||
| "G5" | "G5" | |||
| ] | ] | |||
| ] | ]]]></sourcecode> | |||
| Responses: | <t>Responses:</t> | |||
| [ | <sourcecode type="json"><![CDATA[[ | |||
| [ | [ | |||
| "Blob/upload", | "Blob/upload", | |||
| { | { | |||
| "oldState": null, | "oldState": null, | |||
| "created": { | "created": { | |||
| "b2": { | "b2": { | |||
| "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | |||
| "size": 11, | "size": 11, | |||
| "type": "application/octet-stream" | "type": "application/octet-stream" | |||
| }, | }, | |||
| "b1": { | "b1": { | |||
| "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | |||
| "size": 43, | "size": 43, | |||
| "type": "text/plain" | "type": "text/plain" | |||
| } | } | |||
| }, | }, | |||
| "updated": null, | "updated": null, | |||
| "destroyed": null, | "destroyed": null, | |||
| "notCreated": null, | "notCreated": null, | |||
| "notUpdated": null, | "notUpdated": null, | |||
| "notDestroyed": null, | "notDestroyed": null, | |||
| "accountId": "account1" | "accountId": "account1" | |||
| }, | }, | |||
| "S1" | "S1" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "list": [ | "list": [ | |||
| { | { | |||
| "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | |||
| "isEncodingProblem": true, | "isEncodingProblem": true, | |||
| "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW | "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW | |||
| Qgb3ZlciB0aGUggYEgZG9nLg==", | Qgb3ZlciB0aGUggYEgZG9nLg==", | |||
| "size": 43 | "size": 43 | |||
| }, | }, | |||
| { | { | |||
| "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | |||
| "data:asText": "hello world", | "data:asText": "hello world", | |||
| "size": 11 | "size": 11 | |||
| } | } | |||
| ], | ], | |||
| "notFound": [], | "notFound": [], | |||
| "accountId": "account1" | "accountId": "account1" | |||
| }, | }, | |||
| "G1" | "G1" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "list": [ | "list": [ | |||
| { | { | |||
| "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | |||
| "isEncodingProblem": true, | "isEncodingProblem": true, | |||
| "size": 43 | "size": 43 | |||
| }, | }, | |||
| { | { | |||
| "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | |||
| "data:asText": "hello world", | "data:asText": "hello world", | |||
| "size": 11 | "size": 11 | |||
| } | } | |||
| ], | ], | |||
| "notFound": [], | "notFound": [], | |||
| "accountId": "account1" | "accountId": "account1" | |||
| }, | }, | |||
| "G2" | "G2" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "list": [ | "list": [ | |||
| { | { | |||
| "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | |||
| "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW | "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW | |||
| Qgb3ZlciB0aGUggYEgZG9nLg==", | Qgb3ZlciB0aGUggYEgZG9nLg==", | |||
| "size": 43 | "size": 43 | |||
| }, | }, | |||
| { | { | |||
| "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | |||
| "data:asBase64": "aGVsbG8gd29ybGQ=", | "data:asBase64": "aGVsbG8gd29ybGQ=", | |||
| "size": 11 | "size": 11 | |||
| } | } | |||
| ], | ], | |||
| "notFound": [], | "notFound": [], | |||
| "accountId": "account1" | "accountId": "account1" | |||
| }, | }, | |||
| "G3" | "G3" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "list": [ | "list": [ | |||
| { | { | |||
| "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | |||
| "data:asText": "The q", | "data:asText": "The q", | |||
| "size": 43 | "size": 43 | |||
| }, | }, | |||
| { | { | |||
| "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | |||
| "data:asText": "hello", | "data:asText": "hello", | |||
| "size": 11 | "size": 11 | |||
| } | } | |||
| ], | ], | |||
| "notFound": [], | "notFound": [], | |||
| "accountId": "account1" | "accountId": "account1" | |||
| }, | }, | |||
| "G4" | "G4" | |||
| ], | ], | |||
| [ | [ | |||
| "Blob/get", | "Blob/get", | |||
| { | { | |||
| "list": [ | "list": [ | |||
| { | { | |||
| "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", | |||
| "isTruncated": true, | "isTruncated": true, | |||
| "isEncodingProblem": true, | "isEncodingProblem": true, | |||
| "data:asBase64": "anVtcGVkIG92ZXIgdGhlIIGBIGRvZy4=" | "data:asBase64": "anVtcGVkIG92ZXIgdGhlIIGBIGRvZy4=", | |||
| ;, | "size": 43 | |||
| "size": 43 | ||||
| }, | }, | |||
| { | { | |||
| "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", | |||
| "isTruncated": true, | "isTruncated": true, | |||
| "data:asText": "", | "data:asText": "", | |||
| "size": 11 | "size": 11 | |||
| } | } | |||
| ], | ], | |||
| "notFound": [], | "notFound": [], | |||
| "accountId": "account1" | "accountId": "account1" | |||
| }, | }, | |||
| "G5" | "G5" | |||
| ] | ] | |||
| ] | ]]]></sourcecode> | |||
| </artwork> | ||||
| </section> | </section> | |||
| </section> | </section> | |||
| <section anchor="blob-lookup"><name>Blob/lookup</name> | <section anchor="blob-lookup"><name>Blob/lookup</name> | |||
| <t>Given a list of blobIds, this method does a reverse lookup in each of | <t>Given a list of blobIds, this method does a reverse lookup in each of | |||
| the provided type names to find the list of Ids within that data type | the provided type names to find the list of Ids within that data type | |||
| which reference the provided blob.</t> | that reference the provided blob.</t> | |||
| <t>Since different datatypes will have different semantics of "contains&quo | <t>Since different datatypes will have different semantics of "contains", | |||
| t;, | the definition of "reference" is somewhat loose but roughly | |||
| the definition of reference is somewhat loosely defined, but roughly | means "you could discover this blobId by looking at this object or | |||
| means "you could discover this blobId by looking at this object or | at other objects recursively contained within this object".</t> | |||
| at other objects recursively contained within this object".</t> | <t>For example, with a server that supports <xref target="RFC8621" | |||
| <t>For example with an [RFC8621] server, if checking whether a Mailbox | format="default"/>, if a Mailbox references a blob and if any Emails | |||
| references a blob, then if any Emails within that Mailbox reference | within that Mailbox reference the blobId, then the Mailbox references that | |||
| the blobId, then the Mailbox references that blobId. For any Thread | blobId. For any Thread that references an Email that references a blobId, it | |||
| which references an Email that references a blobId, it can be said | can be said that the Thread references the blobId.</t> | |||
| that the Thread references the blobId.</t> | <t>However, this does not mean that if an Email references a Mailbox in its | |||
| <t>But this does not mean that if an Email references a Mailbox in its | ||||
| mailboxIds property, then any blobId referenced by other Emails in | mailboxIds property, then any blobId referenced by other Emails in | |||
| that Mailbox are also referenced by the initial Email.</t> | that Mailbox are also referenced by the initial Email.</t> | |||
| <t><strong>Parameters</strong></t> | <t><strong>Parameters</strong></t> | |||
| <ul> | <ul> | |||
| <li><t>accountId: <tt>Id</tt></t> | <li><t>accountId: "Id"</t> | |||
| <t>The id of the account used for the call.</t> | <t>The id of the account used for the call.</t> | |||
| </li> | </li> | |||
| <li><t>typeNames: <tt>String[]</tt></t> | <li><t>typeNames: "String[]"</t> | |||
| <t>A list of names from the "JMAP Data Types" registry, or defined by | <t>A list of names from the "JMAP Data Types" registry or defined by | |||
| private extensions which the client has requested. Only names | private extensions that the client has requested. Only names | |||
| for which "Can reference blobs" is true may be specified, and the | for which "Can reference blobs" is true may be specified, and the | |||
| capability which defines each type must also be used by the overall | capability that defines each type must also be used by the overall | |||
| JMAP request in which this method is called.</t> | JMAP request in which this method is called.</t> | |||
| <t>If a type name is not known by the server, or the associated capability | <t>If a type name is not known by the server, or the associated capability | |||
| has not been requested, then the server returns an "unknownDataType" | has not been requested, then the server returns an "unknownDataType" | |||
| error.</t> | error.</t> | |||
| </li> | </li> | |||
| <li><t>ids: <tt>Id[]</tt></t> | <li><t>ids: "Id[]"</t> | |||
| <t>A list of blobId values to be looked for.</t> | <t>A list of blobId values to be looked for.</t> | |||
| </li> | </li> | |||
| </ul> | </ul> | |||
| <t><strong>Response</strong></t> | <t><strong>Response</strong></t> | |||
| <ul> | <ul> | |||
| <li><t>list: <tt>BlobInfo[]</tt></t> | <li><t>list: "BlobInfo[]"</t> | |||
| <t>A list of BlobInfo objects.</t> | <t>A list of BlobInfo objects.</t> | |||
| </li> | </li> | |||
| </ul> | </ul> | |||
| <t><strong>BlobInfo Object</strong></t> | <t><strong>BlobInfo Object</strong></t> | |||
| <ul> | <ul> | |||
| <li><t>id: <tt>Id</tt></t> | <li><t>id: "Id"</t> | |||
| <t>The Blob Identifier.</t> | <t>The blobId.</t> | |||
| </li> | </li> | |||
| <li><t>matchedIds: <tt>String[Id[]]</tt></t> | <li><t>matchedIds: "String[Id[]]"</t> | |||
| <t>A map from type name to list of Ids of that data type (e.g. the name | <t>A map from type name to a list of Ids of that data type (e.g., the name | |||
| "Email" maps to a list of emailIds)</t> | "Email" maps to a list of emailIds).</t> | |||
| </li> | </li> | |||
| </ul> | </ul> | |||
| <t>If a blob is not visible to a user, or does not exist on the server at all, | <t>If a blob is not visible to a user or does not exist on the server at all, | |||
| then the server MUST still return an empty array for each type as this | then the server <bcp14>MUST</bcp14> still return an empty array for each type | |||
| doesn't leak any information about whether the blob is on the server but | as this doesn't leak any information about whether the blob is on the server | |||
| not visible to the requesting user.</t> | but not visible to the requesting user.</t> | |||
| <section anchor="blob-lookup-example"><name>Blob/lookup example</name> | <section anchor="blob-lookup-example"><name>Blob/lookup Example</name> | |||
| <artwork>Method call: | <t>Method Call:</t> | |||
| [ | <sourcecode type="json"><![CDATA[[ | |||
| "Blob/lookup", | "Blob/lookup", | |||
| { | { | |||
| "typeNames": [ | "typeNames": [ | |||
| "Mailbox", | "Mailbox", | |||
| "Thread", | "Thread", | |||
| "Email" | "Email" | |||
| ], | ], | |||
| "ids": [ | "ids": [ | |||
| "Gd2f81008cf07d2425418f7f02a3ca63a8bc82003", | "Gd2f81008cf07d2425418f7f02a3ca63a8bc82003", | |||
| "not-a-blob" | "not-a-blob" | |||
| ] | ] | |||
| }, | }, | |||
| "R1" | "R1" | |||
| ] | ]]]></sourcecode> | |||
| Response: | <t>Response:</t> | |||
| [ | <sourcecode type="json"><![CDATA[[ | |||
| "Blob/lookup", | "Blob/lookup", | |||
| { | { | |||
| "list": [ | "list": [ | |||
| { | { | |||
| "id": "Gd2f81008cf07d2425418f7f02a3ca63a8bc82003", | "id": "Gd2f81008cf07d2425418f7f02a3ca63a8bc82003", | |||
| "matchedIds": { | "matchedIds": { | |||
| "Mailbox": [ | "Mailbox": [ | |||
| "M54e97373", | "M54e97373", | |||
| "Mcbe6b662" | "Mcbe6b662" | |||
| ], | ], | |||
| "Thread": [ | "Thread": [ | |||
| "T1530616e" | "T1530616e" | |||
| ], | ], | |||
| "Email": [ | "Email": [ | |||
| "E16e70a73eb4", | "E16e70a73eb4", | |||
| "E84b0930cf16" | "E84b0930cf16" | |||
| ] | ] | |||
| } | } | |||
| } | } | |||
| ], | ], | |||
| "notFound": [ | "notFound": [ | |||
| "not-a-blob" | "not-a-blob" | |||
| ] | ] | |||
| }, | }, | |||
| "R1" | "R1" | |||
| ] | ]]]></sourcecode> | |||
| </artwork> | ||||
| </section> | </section> | |||
| </section> | </section> | |||
| </section> | </section> | |||
| <section anchor="security-considerations"><name>Security considerations</name> | <section anchor="security-considerations"><name>Security Considerations</name> | |||
| <t>All security considerations of JMAP <xref target="RFC8620"></xref> apply to t | <t>All security considerations for JMAP <xref target="RFC8620"></xref> apply to | |||
| his specification. | this specification. | |||
| Additional considerations specific to the data types and functionality | Additional considerations specific to the data types and functionality | |||
| introduced by this document are described here.</t> | introduced by this document are described here.</t> | |||
| <t>JSON parsers are not all consistent in handling non-UTF-8 data. JMAP require | <t>JSON parsers are not all consistent in handling non-UTF-8 data. | |||
| s | JMAP requires that all JSON data be UTF-8 encoded, so servers | |||
| that all JSON data be UTF-8 encoded, so servers MUST only return a null value | <bcp14>MUST</bcp14> only return a null value if <tt>data:asText</tt> is | |||
| if <tt>data:asText</tt> is requested for a range of octets which is not valid UT | requested for a range of octets that is not valid UTF-8 and set | |||
| F-8, | <tt>isEncodingProblem: true</tt>.</t> | |||
| and set <tt>isEncodingProblem: true</tt>.</t> | <t>Servers <bcp14>MUST</bcp14> apply any access controls, such that if the authe | |||
| <t>Servers MUST apply any access controls, such that if the authenticated user w | nticated user would | |||
| ould | be unable to discover the blobId by making queries, then this fact cannot be | |||
| be unable to discover the blobId by making queries, then this fact can not be | discovered via a Blob/lookup. For example, if an Email exists in a Mailbox that | |||
| discovered via a Blob/lookup. For example, if an Email exists in a Mailbox whic | the authenticated user does not have access to see, then that emailId <bcp14>MUS | |||
| h | T NOT</bcp14> be | |||
| the authenticated user does not have access to see, then that emailId MUST NOT b | returned in a lookup for a blob that is referenced by that email.</t> | |||
| e | <t>The server <bcp14>MUST NOT</bcp14> trust that the data given to a | |||
| returned in a lookup for a blob which is referenced by that email.</t> | Blob/upload is a well-formed instance of the specified media type. Also, if | |||
| <t>The server MUST NOT trust that the data given to a Blob/upload is a well form | the server attempts to parse the given blob, only hardened parsers designed to | |||
| ed | deal with arbitrary untrusted data should be used. The server <bcp14>SHOULD | |||
| instance of the specified media type, and if the server attempts to parse | NOT</bcp14> reject data on the grounds that it is not a valid specimen of the | |||
| the given blob, only hardened parsers designed to deal with arbitrary untrusted | stated type.</t> | |||
| data should be used. The server SHOULD NOT reject data on the grounds that | <t>With carefully chosen data sources, Blob/upload can be used to recreate | |||
| it is not a valid specimen of the stated type.</t> | dangerous content on the far side of security scanners (anti-virus or | |||
| <t>Blob/upload with carefully chosen data sources can be used to recreate danger | exfiltration scanners, for example) that may be watching the upload endpoint. | |||
| ous | Server implementations <bcp14>SHOULD</bcp14> provide a hook to allow security | |||
| content on the far side of security scanners (anti-virus or exfiltration scanner | scanners to check the resulting blob after concatenating the data sources in | |||
| s | the same way that they do for the upload endpoint.</t> | |||
| for example) which may be watching the upload endpoint. Server implementations | <t>Digest algorithms can be expensive for servers to calculate. Servers that | |||
| SHOULD provide a hook to allow security scanners to check the resulting blob aft | share resources between multiple users should track resource usage by clients | |||
| er | ||||
| concatenating the data sources in the same way that they do for the upload endpo | ||||
| int.</t> | ||||
| <t>Digest algorithms can be expensive for servers to calculate. Servers which | ||||
| share resources between multiple users should track resource usage by clients, | ||||
| and rate-limit expensive operations to avoid resource starvation.</t> | and rate-limit expensive operations to avoid resource starvation.</t> | |||
| </section> | </section> | |||
| <section anchor="iana-considerations"><name>IANA considerations</name> | <section anchor="iana-considerations"><name>IANA Considerations</name> | |||
| <section anchor="jmap-capability-registration-for-blob"><name>JMAP Capability Re | ||||
| gistration for "blob"</name> | ||||
| <t>IANA has registered the "blob" JMAP capability as follows:</t> | ||||
| <dl newline="false" spacing="compact"> | ||||
| <dt>Capability Name:</dt> <dd>urn:ietf:params:jmap:blob</dd> | ||||
| <dt>Specification document:</dt> <dd>RFC 9404</dd> | ||||
| <dt>Intended use:</dt> <dd>common</dd> | ||||
| <dt>Change Controller:</dt> <dd>IETF</dd> | ||||
| <dt>Security and privacy considerations:</dt> <dd>RFC 9404, <xref | ||||
| target="security-considerations" format="default"/></dd> | ||||
| </dl> | ||||
| <section anchor="jmap-capability-registration-for-blob"><name>JMAP Capability re | ||||
| gistration for "blob"</name> | ||||
| <t>IANA is requested to register the "blob" JMAP Capability as follows | ||||
| :</t> | ||||
| <t>Capability Name: urn:ietf:params:jmap:blob</t> | ||||
| <t>Specification document: this document</t> | ||||
| <t>Intended use: common</t> | ||||
| <t>Change Controller: IETF</t> | ||||
| <t>Security and privacy considerations: this document, Section XXX</t> | ||||
| </section> | </section> | |||
| <section anchor="jmap-error-codes-registration-for-unknowndatatype"><name>JMAP E | <section anchor="jmap-error-codes-registration-for-unknowndatatype"><name>JMAP E | |||
| rror Codes Registration for "unknownDataType"</name> | rror Codes Registration for "unknownDataType"</name> | |||
| <t>IANA is requested to register the "unknownDataType" JMAP Error Code | <t>IANA has registered the "unknownDataType" JMAP error code as follows:</t> | |||
| as follows:</t> | ||||
| <t>JMAP Error Code: unknownDataType</t> | <dl newline="false" spacing="compact"> | |||
| <t>Intended use: common</t> | <dt>JMAP Error Code:</dt> <dd>unknownDataType</dd> | |||
| <t>Change Controller: IETF</t> | <dt>Intended use:</dt> <dd>common</dd> | |||
| <t>Reference: this document</t> | <dt>Change Controller:</dt> <dd>IETF</dd> | |||
| <t>Description: The server does not recognise this data type, or the capability | <dt>Reference:</dt> <dd>RFC 9404</dd> | |||
| to enable it was not present.</t> | <dt>Description:</dt> <dd>The server does not recognise this data type, or | |||
| the capability to enable it is not present in the current Request | ||||
| Object.</dd> | ||||
| </dl> | ||||
| </section> | </section> | |||
| <section anchor="creation-of-jmap-data-types-registry"><name>Creation of "J | <section anchor="creation-of-jmap-data-types-registry"><name>Creation of "JMAP D | |||
| MAP Data Types" Registry</name> | ata Types" Registry</name> | |||
| <t>IANA is requested to create a new registry "JMAP Data Types" with t | <t>IANA has created a new registry called "JMAP Data Types". <xref | |||
| he initial content:</t> | target="ianatable" format="default"/> shows the initial contents of this | |||
| <table> | new registry.</t> | |||
| <table anchor="ianatable"> | ||||
| <thead> | <thead> | |||
| <tr> | <tr> | |||
| <th>Type Name</th> | <th>Type Name</th> | |||
| <th>Can reference blobs</th> | <th>Can Ref Blobs</th> | |||
| <th>Can use for state change</th> | <th>Can Use for State Change</th> | |||
| <th>Capability</th> | <th>Capability</th> | |||
| <th>Reference</th> | <th>Reference</th> | |||
| </tr> | </tr> | |||
| </thead> | </thead> | |||
| <tbody> | <tbody> | |||
| <tr> | <tr> | |||
| <td>Core</td> | <td>Core</td> | |||
| <td>No</td> | <td>No</td> | |||
| <td>No</td> | <td>No</td> | |||
| skipping to change at line 1056 ¶ | skipping to change at line 1110 ¶ | |||
| <td>No</td> | <td>No</td> | |||
| <td>No</td> | <td>No</td> | |||
| <td>urn:ietf:params:jmap:mail</td> | <td>urn:ietf:params:jmap:mail</td> | |||
| <td><xref target="RFC8621"></xref></td> | <td><xref target="RFC8621"></xref></td> | |||
| </tr> | </tr> | |||
| <tr> | <tr> | |||
| <td>Identity</td> | <td>Identity</td> | |||
| <td>No</td> | <td>No</td> | |||
| <td>Yes</td> | <td>Yes</td> | |||
| <td>urn:ietf:params:jmap:submission</td> | <td>urn:ietf:params:jmap:&zwsp;submission</td> | |||
| <td><xref target="RFC8621"></xref></td> | <td><xref target="RFC8621"></xref></td> | |||
| </tr> | </tr> | |||
| <tr> | <tr> | |||
| <td>EmailSubmission</td> | <td>EmailSubmission</td> | |||
| <td>No</td> | <td>No</td> | |||
| <td>Yes</td> | <td>Yes</td> | |||
| <td>urn:ietf:params:jmap:submission</td> | <td>urn:ietf:params:jmap:&zwsp;submission</td> | |||
| <td><xref target="RFC8621"></xref></td> | <td><xref target="RFC8621"></xref></td> | |||
| </tr> | </tr> | |||
| <tr> | <tr> | |||
| <td>VacationResponse</td> | <td>VacationResponse</td> | |||
| <td>No</td> | <td>No</td> | |||
| <td>Yes</td> | <td>Yes</td> | |||
| <td>urn:ietf:params:jmap:vacationresponse</td> | <td>urn:ietf:params:jmap:&zwsp;vacationresponse</td> | |||
| <td><xref target="RFC8621"></xref></td> | <td><xref target="RFC8621"></xref></td> | |||
| </tr> | </tr> | |||
| <tr> | <tr> | |||
| <td>MDN</td> | <td>MDN</td> | |||
| <td>No</td> | <td>No</td> | |||
| <td>No</td> | <td>No</td> | |||
| <td>urn:ietf:params:jmap:mdn</td> | <td>urn:ietf:params:jmap:mdn</td> | |||
| <td>[RFC9007]</td> | <td><xref target="RFC9007"></xref></td> | |||
| </tr> | </tr> | |||
| </tbody> | </tbody> | |||
| </table><t>This policy for this registry is "Specification required", | </table> | |||
| either an RFC or a similarly | ||||
| stable reference document which defines a JMAP Data Type and associated capabili | ||||
| ty.</t> | ||||
| <t>IANA is asked to appoint designated experts to review requests for additions | ||||
| to this | ||||
| registry, with guidance to allow any registration which provides a stable docume | ||||
| nt describing | ||||
| the capability, and control over the URI namespace where the capability URI poin | ||||
| ts.</t> | ||||
| </section> | ||||
| </section> | ||||
| <section anchor="changes"><name>Changes</name> | ||||
| <t>EDITOR: please remove this section before publication.</t> | ||||
| <t>The source of this document exists on github at: <eref target="https://github | ||||
| .com/brong/draft-gondwana-jmap-blob/">https://github.com/brong/draft-gondwana-jm | ||||
| ap-blob/</eref></t> | ||||
| <t><strong>draft-ietf-jmap-blob-18</strong></t> | ||||
| <ul spacing="compact"> | ||||
| <li>add security considerations for Digest algorithm performance (was supposed | ||||
| to be in -13 but I had a commit that never got pushed)</li> | ||||
| <li><t>artart review:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>clarify that created blobs behave identically to RFC8620 section 6 binary | ||||
| objects.</li> | ||||
| <li>clearer text about "references a blob"</li> | ||||
| <li>remove contractions</li> | ||||
| </ul></li> | ||||
| <li><t>Roman Danyliw DISCUSS</t> | ||||
| <ul spacing="compact"> | ||||
| <li>corrected example text - missing comma and extra data:asBase64.</li> | ||||
| <li>simplify Blob/lookup to not have multiple ways of saying "not found&quo | ||||
| t; | ||||
| for a blob and then need a security considerations around it.</li> | ||||
| <li>said that clients SHOULD prefer algorithms earlier in the list.</li> | ||||
| <li>clarified that supportedTypeNames could include private extensions.</li> | ||||
| <li>editorial/spelling fixes</li> | ||||
| </ul></li> | ||||
| <li><t>genart review</t> | ||||
| <ul spacing="compact"> | ||||
| <li>editorial/spelling fixes</li> | ||||
| </ul></li> | ||||
| <li><t>Robert Winton review</t> | ||||
| <ul spacing="compact"> | ||||
| <li>added a suggestion to use the regular upload mechanism for blobs over a | ||||
| megabyte in size.</li> | ||||
| </ul></li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-17</strong></t> | ||||
| <ul spacing="compact"> | ||||
| <li>AD review, one more wording nit</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-16</strong></t> | ||||
| <ul spacing="compact"> | ||||
| <li>secdir last-call review changes - nit fixes and security considerations</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-15</strong></t> | ||||
| <ul spacing="compact"> | ||||
| <li>changed capabilities object to MUST contain all specified keys, to align | ||||
| with all other published JMAP extensions.</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-14</strong></t> | ||||
| <ul spacing="compact"> | ||||
| <li>AD review - fixed MUST usage</li> | ||||
| <li>AD review - added instructions regarding expert review for IANA</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-13</strong></t> | ||||
| <ul spacing="compact"> | ||||
| <li>added examples of digest responses</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-12</strong></t> | ||||
| <ul spacing="compact"> | ||||
| <li><t>updates based on Neil Jenkins' feedback:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>fixed [] positions for type specs</li> | ||||
| <li>documented delta between /upload and /set better</li> | ||||
| <li>allowed zero-length blobId sources</li> | ||||
| <li>fixed examples with /set leftovers</li> | ||||
| <li>documented datatypes registry policy</li> | ||||
| </ul></li> | ||||
| <li>added optional "digest" support</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-11</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li><t>updates based on IETF113 feedback:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>added wording to suggest the a Blob/get of just size might be faster</li> | ||||
| <li>added an example with just the size field being selected</li> | ||||
| </ul></li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-10</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>removed remaining references to <tt>catenate</tt>.</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-09</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>tidied up introduction text</li> | ||||
| <li>replaced Blob/set with Blob/upload</li> | ||||
| <li>made all upload creates take an array of sources to normalise behaviour at t | ||||
| he cost of a slightly more | ||||
| complex default case.</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-08</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>Fixed spelling of Neil's name in acknowledgements</li> | ||||
| <li><t>Last call review (thanks Jim Fenton)</t> | ||||
| <ul spacing="compact"> | ||||
| <li>fixed mmark sillyness causing RFC8620 to be non-normative in the references< | ||||
| /li> | ||||
| <li>clarified the capability object and accountCapability object requirements</l | ||||
| i> | ||||
| <li>made capability keys much more tightly defined, with mandatory minimum | ||||
| catenate limit and default values.</li> | ||||
| <li>increased use of normative language generally</li> | ||||
| <li>lowercased 'blob' anywhere it wasn't explicitly the object</li> | ||||
| <li>lowercased titles of the columns in the registry</li> | ||||
| </ul></li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-07</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>more examples to cover the interactions of offset, length and encoding check | ||||
| s.</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-06</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>removed asHex - we only need base64 and text</li> | ||||
| <li>added reference to where base64 is defined</li> | ||||
| <li>made 'destroy' not be allowed</li> | ||||
| <li>expanded JSON examples for readability</li> | ||||
| <li>removed 'expires' from examples</li> | ||||
| </ul> | ||||
| <t><strong>draft-ietf-jmap-blob-05</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>discovered I hadn't actually included <tt>typeNames</tt> and <tt>matchedIds< | ||||
| /tt> anywhere except the | ||||
| updates section, oops!</li> | ||||
| <li>added a catenate example</li> | ||||
| <li>tightened up some text</li> | ||||
| </ul> | ||||
| <t><strong>draft-ieft-jmap-blob-04</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>added security considerations for scanning <tt>catenate</tt> results</li> | ||||
| </ul> | ||||
| <t><strong>draft-ieft-jmap-blob-03</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>added capabilities object</li> | ||||
| <li>renamed types to typeNames and matchedIds</li> | ||||
| <li>added details of how to handle non-UTF8 data and truncation in Blob/get</li> | ||||
| <li>added isTruncated and isEncodingProblem to Blob/get to tell the client | ||||
| if the request wasn't entirely satisfied.</li> | ||||
| </ul> | ||||
| <t><strong>draft-ieft-jmap-blob-02</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>fixed incorrect RFC number in reference and HTTP PUT -> POST, thanks Ken. | ||||
| </li> | ||||
| <li>added acknowledgements section</li> | ||||
| <li>removed all 'datatype' text and changed to 'data type' or 'type name' as | ||||
| appropriate (issue #1 proposal)</li> | ||||
| <li>expanded security considerations section and moved optional Blob/lookup | ||||
| empty case into Blob/lookup section</li> | ||||
| </ul> | ||||
| <t><strong>draft-ieft-jmap-blob-01</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>renamed 'datatypes' to 'types' to align with PushSubscription from RFC8620.< | ||||
| /li> | ||||
| <li>added example for Blob/get</li> | ||||
| <li>specified offset and length precisely</li> | ||||
| </ul> | ||||
| <t><strong>draft-ieft-jmap-blob-00</strong>:</t> | ||||
| <ul spacing="compact"> | ||||
| <li>initial adoption as an IETF document, otherwise identical to draft-gondwana- | ||||
| jmap-blob-02</li> | ||||
| </ul> | ||||
| <t><strong>draft-gondwana-jmap-blob-02</strong></t> | ||||
| <ul spacing="compact"> | ||||
| <li>renamed 'objects' to 'datatypes'</li> | ||||
| <li>specified Blob/lookup</li> | ||||
| <li>added IANA registry for datatypes</li> | ||||
| </ul> | ||||
| <t><strong>draft-gondwana-jmap-blob-01</strong></t> | ||||
| <ul spacing="compact"> | ||||
| <li>added an example</li> | ||||
| </ul> | ||||
| <t><strong>draft-gondwana-jmap-blob-00</strong></t> | ||||
| <ul spacing="compact"> | <t>The registration policy for this registry is "Specification Required" <xref t | |||
| <li>initial proposal</li> | arget="RFC8126"></xref>. Either an RFC or a similarly stable reference document | |||
| </ul> | defines a JMAP Data Type | |||
| and associated capability.</t> | ||||
| <t>IANA will appoint designated experts to review requests for additions to this | ||||
| registry, with guidance to allow any registration that provides a stable documen | ||||
| t describing | ||||
| the capability and control over the URI namespace to which the capability URI po | ||||
| ints.</t> | ||||
| </section> | </section> | |||
| <section anchor="acknowledgements"><name>Acknowledgements</name> | ||||
| <t>Joris Baum, Jim Fenton, Neil Jenkins, Alexey Melnikov, Ken Murchison, Robert | ||||
| Stepanek and | ||||
| the JMAP working group at the IETF.</t> | ||||
| </section> | </section> | |||
| </middle> | </middle> | |||
| <back> | <back> | |||
| <references><name>References</name> | ||||
| <references><name>Normative References</name> | <references><name>Normative References</name> | |||
| <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.2119. xml"/> | <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.2119. xml"/> | |||
| <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.3230. xml"/> | <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.3230. xml"/> | |||
| <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.4648. xml"/> | <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.4648. xml"/> | |||
| <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8174. xml"/> | <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8174. xml"/> | |||
| <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8620. xml"/> | <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8620. xml"/> | |||
| </references> | </references> | |||
| <references><name>Informative References</name> | <references><name>Informative References</name> | |||
| <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7888. xml"/> | <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7888. xml"/> | |||
| <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8621. xml"/> | <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8621. xml"/> | |||
| <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.9007. | ||||
| xml"/> | ||||
| <xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8126. | ||||
| xml"/> | ||||
| </references> | ||||
| </references> | </references> | |||
| <section anchor="acknowledgements" numbered="false" toc="default"> | ||||
| <name>Acknowledgements</name> | ||||
| <t><contact fullname="Joris Baum"/>, <contact fullname="Jim Fenton"/>, <contact | ||||
| fullname="Neil Jenkins"/>, <contact fullname="Alexey Melnikov"/>, <contact fulln | ||||
| ame="Ken Murchison"/>, <contact fullname="Robert Stepanek"/>, and | ||||
| the JMAP Working Group in the IETF.</t> | ||||
| </section> | ||||
| </back> | </back> | |||
| </rfc> | </rfc> | |||
| End of changes. 212 change blocks. | ||||
| 938 lines changed or deleted | 752 lines changed or added | |||
This html diff was produced by rfcdiff 1.48. | ||||