<?xml version="1.0" encoding="utf-8"?>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc version="3" ipr="trust200902" docName="draft-gondwana-jmap-blob-00" submissionType="IETF" category="std" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclude" updates="5228" consensus="true">

<front>
<title abbrev="JMAP Blob">JMAP Blob management extension</title><seriesInfo value="draft-gondwana-jmap-blob-00" 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 William 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="2020" month="November" day="2"></date>
<area>Applications</area>
<workgroup>EXTRA</workgroup>
<keyword>jmap</keyword>

<abstract>
<t>The JMAP base protocol (RFC8620) provides the ability to upload and download
arbitrary binary data via HTTP PUT and GET on defined endpoint.  This binary
data is called a &quot;Blob&quot;.</t>
<t>This extension adds additional ways to handle Blobs, by making inline method
calls within a standard JMAP request.</t>
</abstract>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t>Sometimes JMAP (<xref target="RFC8620"></xref>) interactions require creating a Blob and then
referencing it.  In the same way that IMAP Literals (<xref target="RFC7888"></xref>) were extended
to reduce roundtrips for simple data, embedding simple small blobs into the
JMAP method stream can reduce roundtrips.</t>
<t>Likewise, when fetching an object, it can be useful to also fetch the raw
content of that object without a separate roundtrip.</t>
<t>Where JMAP is being proxied through a system which is providing additional
access restrictions, it can be useful to be able to see where a blob is
referenced in order to decide whether to allow it to be downloaded.</t>
</section>

<section anchor="conventions-used-in-this-document"><name>Conventions Used In This Document</name>
<t>The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL
NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;,
&quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"></xref> <xref target="RFC8174"></xref> when, and only when,
they appear in all capitals, as shown here.</t>
</section>

<section anchor="blobs"><name>Blobs</name>
<t>A blob is a sequence of zero or more octets.</t>
<t>The JMAP base spec <xref target="RFC8210"></xref> defines the <tt>Blob/copy</tt> method, which
is unchanged by this specfication.</t>

<section anchor="blob-set"><name>Blob/set</name>
<t>This is a standard JMAP <tt>set</tt> method.</t>

<section anchor="create"><name>create</name>
<t><strong>Properties:</strong></t>
<t>Any one of:</t>

<ul>
<li>data:asText: String|null</li>
<li>data:asBase64: String|null</li>
<li>data:asHex: String|null</li>
<li>catenate: [SetObject] <em>list of byte sources in order</em></li>
</ul>
<t>Also:</t>

<ul>
<li>type: String|null</li>
</ul>
<t>Result is:</t>

<ul>
<li>id: Id the blobId</li>
<li>type: String|null <em>as given in the creation (if any); or detected from content; or null</em></li>
<li>size: UnsignedInt <em>as per RFC8620 - the size of the file in Octets</em></li>
</ul>
<t>Any other properties identical to those that would be returned in the JSON response of the
RFC8620 upload endpoint.</t>
<t>SetObject:</t>
<t>Any one of</t>

<ul>
<li><t>data:asText: String|null</t>
</li>
<li><t>data:asBase64: String|null</t>
</li>
<li><t>data:asHex: String|null</t>
</li>
</ul>
<t>OR a blobId source:</t>

<ul>
<li>blobId: Id</li>
<li>offset: UnsignedInt|null</li>
<li>length: UnsignedInt|null</li>
</ul>
</section>

<section anchor="update"><name>update</name>
<t>It is not possible to update a Blob, so any update will result in a <tt>notUpdated</tt> response.</t>
</section>

<section anchor="destroy"><name>destroy</name>
<t>If an uploaded Blob is not referenced by any persistent object, the server SHOULD destroy the object.
Some systems use a content-based ID for blobs, so the server MAY respond <tt>destroyed</tt> and yet that
blobId still exist with the same content.</t>
</section>
</section>

<section anchor="blob-get"><name>Blob/get</name>
<t>A standard JMAP get.</t>
<t><strong>Properties:</strong></t>
<t>Any of</t>

<ul>
<li>data:asText</li>
<li>data:asBase64</li>
<li>data:asHex</li>
<li>data <em>selects data:asText if the content is UTF-8, or data:asBase64</em></li>
<li>size</li>
</ul>
<t>If not given, returns <tt>data</tt> and <tt>size</tt>.</t>
<t>QUESTION: do we want to add range operators?</t>

<ul>
<li>offset: UnsignedInt|null</li>
<li>length: UnsignedInt|null</li>
</ul>
<t>Returns that range of bytes (not characters!) from the blob</t>
</section>

<section anchor="blob-lookup"><name>Blob/lookup</name>
<t>A reverse lookup!</t>
<t>Work to be done here, but something like this.</t>
<t>Map from blobId to object type:</t>
<t>e.g.</t>

<artwork>[ &quot;Blob/lookup&quot;, {
  &quot;objects&quot;: [&quot;Mailbox&quot;, &quot;Thread&quot;, &quot;Email&quot;],
  &quot;ids&quot;: [&quot;Gd2f81008cf07d2425418f7f02a3ca63a8bc82003&quot;,
          &quot;G6f954bcb620f7f50fc8f21426bde3669da3d9067&quot;]
}, &quot;R1&quot; ]
</artwork>
<t>Response:</t>

<artwork>[ &quot;Blob/lookup&quot;, {
  &quot;list&quot;: [
    {
      &quot;id&quot;: &quot;Gd2f81008cf07d2425418f7f02a3ca63a8bc82003&quot;,
      &quot;Mailbox&quot;: [&quot;M54e97373&quot;, Mcbe6b662&quot;],
      &quot;Thread&quot;: [&quot;T1530616e&quot;],
      &quot;Email&quot;: [&quot;E16e70a73eb4&quot;, &quot;E84b0930cf16&quot;]
    },
  ],
  &quot;notFound&quot;: [&quot;G6f954bcb620f7f50fc8f21426bde3669da3d9067&quot;]
}, &quot;R1&quot;]
</artwork>
<t>This tells which objects of each type &quot;contain&quot; a reference to
that blobId.  &quot;Contain&quot; is defined somewhat losely here, so for
example &quot;the Mailbox contains an Email which references this
blobId&quot; is the standard in the response above, likewise for Thread.</t>
</section>
</section>

<section anchor="security-considerations"><name>Security considerations</name>
<t>TO BE IMPROVED:</t>
<t>JSON parsers are not all consistent in handling non-UTF-8 data.  JMAP requires
that all JSON data be UTF-8 encoded, so servers MUST either return
<tt>data:asBase64</tt> or <tt>isEncodingProblem: true</tt> and modify the data to be UTF-8
safe.</t>
</section>

<section anchor="iana-considerations"><name>IANA considerations</name>
<t>TBD</t>
</section>

<section anchor="acknowledgements"><name>Acknowledgements</name>
<t>TBD</t>
</section>

</middle>

<back>
<references><name>Normative References</name>
<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.2119.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.8210.xml"/>
</references>
<references><name>Informative References</name>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7888.xml"/>
</references>

</back>

</rfc>
