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 "&#160;">
ype="IETF" category="std" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclud <!ENTITY zwsp "&#8203;">
e" updates="8620" indexInclude="true" consensus="true"> <!ENTITY nbhy "&#8209;">
<!ENTITY wj "&#8288;">
]>
<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 &quot;blob&quot;.</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 &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, & <t>
quot;SHALL&quot;, &quot;SHALL The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>",
NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
&quot;NOT RECOMMENDED&quot;, NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>",
&quot;MAY&quot;, and &quot;OPTIONAL&quot; 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&nbsp;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
&quot;accountCapabilities&quot; 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 &quot;accountCapabilities&quot; or more accountCapabilities properties <bcp14>MUST</bcp14> also include the
properties MUST also include the property in the &quot;capabilities&quot; property in the capabilities property.</t>
property.</t>
<t>The value of this property in the JMAP session &quot;capabilities&quot; <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 &quot;accountCapabilities&quot; <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[{
&quot;capabilities&quot;: { "capabilities": {
..., ...,
&quot;urn:ietf:params:jmap:blob&quot;: {} "urn:ietf:params:jmap:blob": {}
}, },
&quot;accounts&quot;: { "accounts": {
&quot;A13842&quot;: { "A13842": {
... ...
&quot;accountCapabilities&quot;: { "accountCapabilities": {
&quot;urn:ietf:params:jmap:blob&quot;: { "urn:ietf:params:jmap:blob": {
&quot;maxSizeBlobSet&quot;: 50000000, "maxSizeBlobSet": 50000000,
&quot;maxDataSources&quot;: 100, "maxDataSources": 100,
&quot;supportedTypeNames&quot; : [ "supportedTypeNames" : [
&quot;Mailbox&quot;, "Mailbox",
&quot;Thread&quot;, "Thread",
&quot;Email&quot; "Email"
], ],
&quot;supportedDigestAlgorithms&quot; : [ "supportedDigestAlgorithms" : [
&quot;sha&quot;, "sha",
&quot;sha-256&quot; "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[
[ [
&quot;Blob/upload&quot;, "Blob/upload",
{ {
&quot;accountId&quot;: &quot;account1&quot;, "accountId": "account1",
&quot;create&quot;: { "create": {
&quot;1&quot;: { "1": {
&quot;data&quot; : [ "data" : [
{ {
&quot;data:asBase64&quot;: &quot;iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQM "data:asBase64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKA
AAAAl21bKA AAAA1BMVEX/AAAZ4gk3AAAAAXRSTlN/gFy0ywAAAApJRE
AAAA1BMVEX/AAAZ4gk3AAAAAXRSTlN/gFy0ywAAAApJRE FUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII="
FUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=&quot;, }
} ],
], "type": "image/png"
&quot;type&quot;: &quot;image/png&quot; }
}, }
}, },
}, "R1"
&quot;R1&quot; ]]]></sourcecode>
]
Response: <t>Response:</t>
[ <sourcecode type="json"><![CDATA[[
&quot;Blob/upload&quot;, "Blob/upload",
{ {
&quot;accountId&quot; : &quot;account1&quot;, "accountId" : "account1",
&quot;created&quot; : { "created" : {
&quot;1&quot;: { "1": {
&quot;id&quot; : &quot;G4c6751edf9dd6903ff54b792e432fba781271beb&quot;, "id" : "G4c6751edf9dd6903ff54b792e432fba781271beb",
&quot;type&quot; : &quot;image/png&quot;, "type" : "image/png",
&quot;size&quot; : 95 "size" : 95
}, }
}, }
}, },
&quot;R1&quot; "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[
[ [
[ [
&quot;Blob/upload&quot;, "Blob/upload",
{ {
&quot;create&quot;: { "create": {
&quot;b4&quot;: { "b4": {
&quot;data&quot;: [ "data": [
{ {
&quot;data:asText&quot;: &quot;The quick brown fox jumped over the "data:asText": "The quick brown fox jumped over the lazy dog."
lazy dog.&quot;
}
]
}
}
},
&quot;S4&quot;
],
[
&quot;Blob/upload&quot;,
{
&quot;create&quot;: {
&quot;cat&quot;: {
&quot;data&quot;: [
{
&quot;data:asText&quot;: &quot;How&quot;
},
{
&quot;blobId&quot;: &quot;#b4&quot;,
&quot;length&quot;: 7,
&quot;offset&quot;: 3
},
{
&quot;data:asText&quot;: &quot;was t&quot;
},
{
&quot;blobId&quot;: &quot;#b4&quot;,
&quot;length&quot;: 1,
&quot;offset&quot;: 1
},
{
&quot;data:asBase64&quot;: &quot;YXQ/&quot;
}
]
}
} }
}, ]
&quot;CAT&quot; }
], }
[ },
&quot;Blob/get&quot;, "S4"
{ ],
&quot;properties&quot;: [ [
&quot;data:asText&quot;, "Blob/upload",
&quot;size&quot; {
], "create": {
&quot;ids&quot;: [ "cat": {
&quot;#cat&quot; "data": [
] {
}, "data:asText": "How"
&quot;G4&quot; },
] {
] "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[[
[ [
&quot;Blob/upload&quot;, "Blob/upload",
{ {
&quot;oldState&quot;: null, "oldState": null,
&quot;created&quot;: { "created": {
&quot;b4&quot;: { "b4": {
&quot;id&quot;: &quot;Gc0854fb9fb03c41cce3802cb0d220529e6eef94e&quot;, "id": "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e",
&quot;size&quot;: 45, "size": 45,
&quot;type&quot;: &quot;application/octet-stream&quot; "type": "application/octet-stream"
} }
}, },
&quot;notCreated&quot;: null, "notCreated": null,
&quot;accountId&quot;: &quot;account1&quot; "accountId": "account1"
}, },
&quot;S4&quot; "S4"
], ],
[ [
&quot;Blob/upload&quot;, "Blob/upload",
{ {
&quot;oldState&quot;: null, "oldState": null,
&quot;created&quot;: { "created": {
&quot;cat&quot;: { "cat": {
&quot;id&quot;: &quot;Gcc60576f036321ae6e8037ffc56bdee589bd3e23&quot;, "id": "Gcc60576f036321ae6e8037ffc56bdee589bd3e23",
&quot;size&quot;: 19, "size": 19,
&quot;type&quot;: &quot;application/octet-stream&quot; "type": "application/octet-stream"
} }
}, },
&quot;notCreated&quot;: null, "notCreated": null,
&quot;accountId&quot;: &quot;account1&quot; "accountId": "account1"
}, },
&quot;CAT&quot; "CAT"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;list&quot;: [ "list": [
{ {
&quot;id&quot;: &quot;Gcc60576f036321ae6e8037ffc56bdee589bd3e23&quot;, "id": "Gcc60576f036321ae6e8037ffc56bdee589bd3e23",
&quot;data:asText&quot;: &quot;How quick was that?&quot;, "data:asText": "How quick was that?",
&quot;size&quot;: 19 "size": 19
} }
], ],
&quot;notFound&quot;: [], "notFound": [],
&quot;accountId&quot;: &quot;account1&quot; "accountId": "account1"
}, },
&quot;G4&quot; "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:&lt;algorithm&gt; (where &lt;algorithm&gt; is one of the named algori thms in the <tt>supportedDigestAlgorithms</tt> capability)</li> <li>digest:&lt;algorithm&gt; (where &lt;algorithm&gt; 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:&lt;algorithm&gt;: "String"</t>
</li> <t>The base64 encoding of the digest of the octets in the selected range,
<li><t>digest:&lt;algorithm&gt; <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 &quot;The quick brown fox jumped over <t>In this example, a blob containing the string "The quick brown fox jumped ove
the lazy dog.&quot; 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[[
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;accountId&quot; : &quot;account1&quot;, "accountId" : "account1",
&quot;ids&quot; : [ "ids" : [
&quot;Gc0854fb9fb03c41cce3802cb0d220529e6eef94e&quot;, "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e",
&quot;not-a-blob&quot; "not-a-blob"
], ],
&quot;properties&quot; : [ "properties" : [
&quot;data:asText&quot;, "data:asText",
&quot;digest:sha&quot;, "digest:sha",
&quot;size&quot; "size"
] ]
}, },
&quot;R1&quot; "R1"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;accountId&quot; : &quot;account1&quot;, "accountId" : "account1",
&quot;ids&quot; : [ "ids" : [
&quot;Gc0854fb9fb03c41cce3802cb0d220529e6eef94e&quot; "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e"
], ],
&quot;properties&quot; : [ "properties" : [
&quot;data:asText&quot;, "data:asText",
&quot;digest:sha&quot;, "digest:sha",
&quot;digest:sha-256&quot;, "digest:sha-256",
&quot;size&quot; "size"
], ],
&quot;offset&quot; : 4, "offset" : 4,
&quot;length&quot; : 9 "length" : 9
}, },
&quot;R2&quot; "R2"
] ]
] ]]]></sourcecode>
Responses: <t>Responses:</t>
<sourcecode type="json"><![CDATA[
[ [
[ [
&quot;Blob/get&quot;, "Blob/get",
{
"accountId": "account1",
"list": [
{ {
&quot;accountId&quot;: &quot;account1&quot;, "id": "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e",
&quot;list&quot;: [ "data:asText": "The quick brown fox jumped over the lazy dog.",
{ "digest:sha": "wIVPufsDxBzOOALLDSIFKebu+U4=",
&quot;id&quot;: &quot;Gc0854fb9fb03c41cce3802cb0d220529e6eef94e&quot;, "size": 45
&quot;data:asText&quot;: &quot;The quick brown fox jumped over the laz }
y dog.&quot;,
&quot;digest:sha&quot;: &quot;wIVPufsDxBzOOALLDSIFKebu+U4=&quot;,
&quot;size&quot;: 45
}
],
&quot;notFound&quot;: [
&quot;not-a-blob&quot;
]
},
&quot;R1&quot;
], ],
[ "notFound": [
&quot;Blob/get&quot;, "not-a-blob"
{
&quot;accountId&quot;: &quot;account1&quot;,
&quot;list&quot;: [
{
&quot;id&quot;: &quot;Gc0854fb9fb03c41cce3802cb0d220529e6eef94e&quot;,
&quot;data:asText&quot;: &quot;quick bro&quot;,
&quot;digest:sha&quot;: &quot;QiRAPtfyX8K6tm1iOAtZ87Xj3Ww=&quot;,
&quot;digest:sha-256&quot;: &quot;gdg9INW7lwHK6OQ9u0dwDz2ZY/gubi0En0xl
FpKt0OA=&quot;,
&quot;size&quot;: 45
}
]
},
&quot;R2&quot;
] ]
] },
"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: &quot;The quick brown fox jumped over the
\x81\x81 fox&quot; <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[[
[ [
&quot;Blob/upload&quot;, "Blob/upload",
{ {
&quot;create&quot;: { "create": {
&quot;b1&quot;: { "b1": {
&quot;data&quot;: [ "data": [
{ {
&quot;data:asBase64&quot;: &quot;VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZ "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW
W Qgb3ZlciB0aGUggYEgZG9nLg=="
Qgb3ZlciB0aGUggYEgZG9nLg==&quot;
} }
] ]
}, },
&quot;b2&quot;: { "b2": {
&quot;data&quot;: [ "data": [
{ {
&quot;data:asText&quot;: &quot;hello world&quot; "data:asText": "hello world"
} }
], ],
&quot;type&quot; : &quot;text/plain&quot; "type" : "text/plain"
} }
} }
}, },
&quot;S1&quot; "S1"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;ids&quot;: [ "ids": [
&quot;#b1&quot;, "#b1",
&quot;#b2&quot; "#b2"
] ]
}, },
&quot;G1&quot; "G1"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;ids&quot;: [ "ids": [
&quot;#b1&quot;, "#b1",
&quot;#b2&quot; "#b2"
], ],
&quot;properties&quot;: [ "properties": [
&quot;data:asText&quot;, "data:asText",
&quot;size&quot; "size"
] ]
}, },
&quot;G2&quot; "G2"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;ids&quot;: [ "ids": [
&quot;#b1&quot;, "#b1",
&quot;#b2&quot; "#b2"
], ],
&quot;properties&quot;: [ "properties": [
&quot;data:asBase64&quot;, "data:asBase64",
&quot;size&quot; "size"
] ]
}, },
&quot;G3&quot; "G3"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;offset&quot;: 0, "offset": 0,
&quot;length&quot;: 5, "length": 5,
&quot;ids&quot;: [ "ids": [
&quot;#b1&quot;, "#b1",
&quot;#b2&quot; "#b2"
] ]
}, },
&quot;G4&quot; "G4"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;offset&quot;: 20, "offset": 20,
&quot;length&quot;: 100, "length": 100,
&quot;ids&quot;: [ "ids": [
&quot;#b1&quot;, "#b1",
&quot;#b2&quot; "#b2"
] ]
}, },
&quot;G5&quot; "G5"
] ]
] ]]]></sourcecode>
Responses: <t>Responses:</t>
[ <sourcecode type="json"><![CDATA[[
[ [
&quot;Blob/upload&quot;, "Blob/upload",
{ {
&quot;oldState&quot;: null, "oldState": null,
&quot;created&quot;: { "created": {
&quot;b2&quot;: { "b2": {
&quot;id&quot;: &quot;G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed&quot;, "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
&quot;size&quot;: 11, "size": 11,
&quot;type&quot;: &quot;application/octet-stream&quot; "type": "application/octet-stream"
}, },
&quot;b1&quot;: { "b1": {
&quot;id&quot;: &quot;G72cfa4804194563685d9a4b695f7ba20e7739576&quot;, "id": "G72cfa4804194563685d9a4b695f7ba20e7739576",
&quot;size&quot;: 43, "size": 43,
&quot;type&quot;: &quot;text/plain&quot; "type": "text/plain"
} }
}, },
&quot;updated&quot;: null, "updated": null,
&quot;destroyed&quot;: null, "destroyed": null,
&quot;notCreated&quot;: null, "notCreated": null,
&quot;notUpdated&quot;: null, "notUpdated": null,
&quot;notDestroyed&quot;: null, "notDestroyed": null,
&quot;accountId&quot;: &quot;account1&quot; "accountId": "account1"
}, },
&quot;S1&quot; "S1"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;list&quot;: [ "list": [
{ {
&quot;id&quot;: &quot;G72cfa4804194563685d9a4b695f7ba20e7739576&quot;, "id": "G72cfa4804194563685d9a4b695f7ba20e7739576",
&quot;isEncodingProblem&quot;: true, "isEncodingProblem": true,
&quot;data:asBase64&quot;: &quot;VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW
Qgb3ZlciB0aGUggYEgZG9nLg==&quot;, Qgb3ZlciB0aGUggYEgZG9nLg==",
&quot;size&quot;: 43 "size": 43
}, },
{ {
&quot;id&quot;: &quot;G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed&quot;, "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
&quot;data:asText&quot;: &quot;hello world&quot;, "data:asText": "hello world",
&quot;size&quot;: 11 "size": 11
} }
], ],
&quot;notFound&quot;: [], "notFound": [],
&quot;accountId&quot;: &quot;account1&quot; "accountId": "account1"
}, },
&quot;G1&quot; "G1"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;list&quot;: [ "list": [
{ {
&quot;id&quot;: &quot;G72cfa4804194563685d9a4b695f7ba20e7739576&quot;, "id": "G72cfa4804194563685d9a4b695f7ba20e7739576",
&quot;isEncodingProblem&quot;: true, "isEncodingProblem": true,
&quot;size&quot;: 43 "size": 43
}, },
{ {
&quot;id&quot;: &quot;G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed&quot;, "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
&quot;data:asText&quot;: &quot;hello world&quot;, "data:asText": "hello world",
&quot;size&quot;: 11 "size": 11
} }
], ],
&quot;notFound&quot;: [], "notFound": [],
&quot;accountId&quot;: &quot;account1&quot; "accountId": "account1"
}, },
&quot;G2&quot; "G2"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;list&quot;: [ "list": [
{ {
&quot;id&quot;: &quot;G72cfa4804194563685d9a4b695f7ba20e7739576&quot;, "id": "G72cfa4804194563685d9a4b695f7ba20e7739576",
&quot;data:asBase64&quot;: &quot;VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW
Qgb3ZlciB0aGUggYEgZG9nLg==&quot;, Qgb3ZlciB0aGUggYEgZG9nLg==",
&quot;size&quot;: 43 "size": 43
}, },
{ {
&quot;id&quot;: &quot;G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed&quot;, "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
&quot;data:asBase64&quot;: &quot;aGVsbG8gd29ybGQ=&quot;, "data:asBase64": "aGVsbG8gd29ybGQ=",
&quot;size&quot;: 11 "size": 11
} }
], ],
&quot;notFound&quot;: [], "notFound": [],
&quot;accountId&quot;: &quot;account1&quot; "accountId": "account1"
}, },
&quot;G3&quot; "G3"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;list&quot;: [ "list": [
{ {
&quot;id&quot;: &quot;G72cfa4804194563685d9a4b695f7ba20e7739576&quot;, "id": "G72cfa4804194563685d9a4b695f7ba20e7739576",
&quot;data:asText&quot;: &quot;The q&quot;, "data:asText": "The q",
&quot;size&quot;: 43 "size": 43
}, },
{ {
&quot;id&quot;: &quot;G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed&quot;, "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
&quot;data:asText&quot;: &quot;hello&quot;, "data:asText": "hello",
&quot;size&quot;: 11 "size": 11
} }
], ],
&quot;notFound&quot;: [], "notFound": [],
&quot;accountId&quot;: &quot;account1&quot; "accountId": "account1"
}, },
&quot;G4&quot; "G4"
], ],
[ [
&quot;Blob/get&quot;, "Blob/get",
{ {
&quot;list&quot;: [ "list": [
{ {
&quot;id&quot;: &quot;G72cfa4804194563685d9a4b695f7ba20e7739576&quot;, "id": "G72cfa4804194563685d9a4b695f7ba20e7739576",
&quot;isTruncated&quot;: true, "isTruncated": true,
&quot;isEncodingProblem&quot;: true, "isEncodingProblem": true,
&quot;data:asBase64&quot;: &quot;anVtcGVkIG92ZXIgdGhlIIGBIGRvZy4=&quot "data:asBase64": "anVtcGVkIG92ZXIgdGhlIIGBIGRvZy4=",
;, "size": 43
&quot;size&quot;: 43
}, },
{ {
&quot;id&quot;: &quot;G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed&quot;, "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
&quot;isTruncated&quot;: true, "isTruncated": true,
&quot;data:asText&quot;: &quot;&quot;, "data:asText": "",
&quot;size&quot;: 11 "size": 11
} }
], ],
&quot;notFound&quot;: [], "notFound": [],
&quot;accountId&quot;: &quot;account1&quot; "accountId": "account1"
}, },
&quot;G5&quot; "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 &quot;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 &quot;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&quot;.</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 &quot;JMAP Data Types&quot; 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 &quot;Can reference blobs&quot; 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 &quot;unknownDataType&quot; 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
&quot;Email&quot; 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[[
&quot;Blob/lookup&quot;, "Blob/lookup",
{ {
&quot;typeNames&quot;: [ "typeNames": [
&quot;Mailbox&quot;, "Mailbox",
&quot;Thread&quot;, "Thread",
&quot;Email&quot; "Email"
], ],
&quot;ids&quot;: [ "ids": [
&quot;Gd2f81008cf07d2425418f7f02a3ca63a8bc82003&quot;, "Gd2f81008cf07d2425418f7f02a3ca63a8bc82003",
&quot;not-a-blob&quot; "not-a-blob"
] ]
}, },
&quot;R1&quot; "R1"
] ]]]></sourcecode>
Response: <t>Response:</t>
[ <sourcecode type="json"><![CDATA[[
&quot;Blob/lookup&quot;, "Blob/lookup",
{ {
&quot;list&quot;: [ "list": [
{ {
&quot;id&quot;: &quot;Gd2f81008cf07d2425418f7f02a3ca63a8bc82003&quot;, "id": "Gd2f81008cf07d2425418f7f02a3ca63a8bc82003",
&quot;matchedIds&quot;: { "matchedIds": {
&quot;Mailbox&quot;: [ "Mailbox": [
&quot;M54e97373&quot;, "M54e97373",
&quot;Mcbe6b662&quot; "Mcbe6b662"
], ],
&quot;Thread&quot;: [ "Thread": [
&quot;T1530616e&quot; "T1530616e"
], ],
&quot;Email&quot;: [ "Email": [
&quot;E16e70a73eb4&quot;, "E16e70a73eb4",
&quot;E84b0930cf16&quot; "E84b0930cf16"
] ]
} }
} }
], ],
&quot;notFound&quot;: [ "notFound": [
&quot;not-a-blob&quot; "not-a-blob"
] ]
}, },
&quot;R1&quot; "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 &quot;blob&quot;</name>
<t>IANA is requested to register the &quot;blob&quot; 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 &quot;unknownDataType&quot;</name> rror Codes Registration for "unknownDataType"</name>
<t>IANA is requested to register the &quot;unknownDataType&quot; 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 &quot;J <section anchor="creation-of-jmap-data-types-registry"><name>Creation of "JMAP D
MAP Data Types&quot; Registry</name> ata Types" Registry</name>
<t>IANA is requested to create a new registry &quot;JMAP Data Types&quot; 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 &quot;Specification required&quot;, </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 &quot;references a blob&quot;</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 &quot;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 &quot;digest&quot; 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 -&gt; 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.