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. |