idnits 2.17.00 (12 Aug 2021) /tmp/idnits40421/draft-ietf-jmap-blob-11.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- ** There are 32 instances of too long lines in the document, the longest one being 14 characters in excess of 72. -- The draft header indicates that this document updates RFC8620, but the abstract doesn't seem to directly say this. It does mention RFC8620 though, so this could be OK. Miscellaneous warnings: ---------------------------------------------------------------------------- == Using lowercase 'not' together with uppercase 'MUST', 'SHALL', 'SHOULD', or 'RECOMMENDED' is not an accepted usage according to RFC 2119. Please use uppercase 'NOT' together with RFC 2119 keywords (if that is what you mean). Found 'MUST not' in this paragraph: Servers MUST apply any access controls, such that if the authenticated user would be unable to discover the blobId by making queries, then this fact can't be discovered via a Blob/lookup. For example, if an Email exists in a Mailbox which the authenticated user does not have access to see, then that emailId MUST not be returned in a lookup for a blob which is referenced by that email. -- The document date (26 March 2022) is 49 days in the past. Is this intentional? Checking references for intended status: Proposed Standard ---------------------------------------------------------------------------- (See RFCs 3967 and 4897 for information about using normative references to lower-maturity documents in RFCs) == Missing Reference: 'UploadObject' is mentioned on line 205, but not defined == Missing Reference: 'DataSourceObject' is mentioned on line 245, but not defined == Missing Reference: 'String' is mentioned on line 892, but not defined == Missing Reference: 'Id' is mentioned on line 903, but not defined == Missing Reference: 'BlobInfo' is mentioned on line 909, but not defined == Unused Reference: 'RFC8621' is defined on line 1246, but no explicit reference was found in the text Summary: 1 error (**), 0 flaws (~~), 7 warnings (==), 2 comments (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 JMAP B. Gondwana, Ed. 3 Internet-Draft Fastmail 4 Updates: 8620 (if approved) 26 March 2022 5 Intended status: Standards Track 6 Expires: 27 September 2022 8 JMAP Blob management extension 9 draft-ietf-jmap-blob-11 11 Abstract 13 The JMAP base protocol (RFC8620) provides the ability to upload and 14 download arbitrary binary data via HTTP POST and GET on defined 15 endpoint. This binary data is called a "blob". 17 This extension adds additional ways to create and access blobs, by 18 making inline method calls within a standard JMAP request. 20 This extension also adds a reverse lookup mechanism to discover where 21 blobs are referenced within other data types. 23 Status of This Memo 25 This Internet-Draft is submitted in full conformance with the 26 provisions of BCP 78 and BCP 79. 28 Internet-Drafts are working documents of the Internet Engineering 29 Task Force (IETF). Note that other groups may also distribute 30 working documents as Internet-Drafts. The list of current Internet- 31 Drafts is at https://datatracker.ietf.org/drafts/current/. 33 Internet-Drafts are draft documents valid for a maximum of six months 34 and may be updated, replaced, or obsoleted by other documents at any 35 time. It is inappropriate to use Internet-Drafts as reference 36 material or to cite them other than as "work in progress." 38 This Internet-Draft will expire on 27 September 2022. 40 Copyright Notice 42 Copyright (c) 2022 IETF Trust and the persons identified as the 43 document authors. All rights reserved. 45 This document is subject to BCP 78 and the IETF Trust's Legal 46 Provisions Relating to IETF Documents (https://trustee.ietf.org/ 47 license-info) in effect on the date of publication of this document. 48 Please review these documents carefully, as they describe your rights 49 and restrictions with respect to this document. Code Components 50 extracted from this document must include Revised BSD License text as 51 described in Section 4.e of the Trust Legal Provisions and are 52 provided without warranty as described in the Revised BSD License. 54 Table of Contents 56 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 57 2. Conventions Used In This Document . . . . . . . . . . . . . . 3 58 3. Addition to the Capabilities Object . . . . . . . . . . . . . 3 59 3.1. urn:ietf:params:jmap:blob . . . . . . . . . . . . . . . . 3 60 3.1.1. Capability Example . . . . . . . . . . . . . . . . . 4 61 4. Blob Methods . . . . . . . . . . . . . . . . . . . . . . . . 4 62 4.1. Blob/upload . . . . . . . . . . . . . . . . . . . . . . . 5 63 4.1.1. Blob/upload simple example . . . . . . . . . . . . . 7 64 4.1.2. Blob/upload complex example . . . . . . . . . . . . . 8 65 4.2. Blob/get . . . . . . . . . . . . . . . . . . . . . . . . 11 66 4.2.1. Blob/get simple example . . . . . . . . . . . . . . . 13 67 4.2.2. Blob/get example with range and encoding errors . . . 14 68 4.3. Blob/lookup . . . . . . . . . . . . . . . . . . . . . . . 20 69 4.3.1. Blob/lookup example . . . . . . . . . . . . . . . . . 21 70 5. Security considerations . . . . . . . . . . . . . . . . . . . 23 71 6. IANA considerations . . . . . . . . . . . . . . . . . . . . . 23 72 6.1. JMAP Capability registration for "blob" . . . . . . . . . 23 73 6.2. JMAP Error Codes Registration for "unknownDataType" . . . 24 74 6.3. Creation of "JMAP Data Types" Registry . . . . . . . . . 24 75 7. Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 76 8. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 28 77 9. Normative References . . . . . . . . . . . . . . . . . . . . 28 78 10. Informative References . . . . . . . . . . . . . . . . . . . 28 79 Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 29 81 1. Introduction 83 Sometimes JMAP ([RFC8620]) interactions require creating a blob and 84 then referencing it. In the same way that IMAP Literals were 85 extended by [RFC7888], embedding small blobs directly into the JMAP 86 method calls array can be an option for reducing roundtrips. 88 Likewise, when fetching an object, it can be useful to also fetch the 89 raw content of that object without a separate roundtrip. 91 Since raw blobs may contain arbitrary binary data, this document 92 defines a use of the base64 coding specified in [RFC4648] for both 93 creating and fetching blob data. 95 Where JMAP is being proxied through a system which applies additional 96 access restrictions, it can be useful to know which objects reference 97 any particular blob, and this document defines a way to discover 98 those references. 100 2. Conventions Used In This Document 102 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 103 "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 104 "OPTIONAL" in this document are to be interpreted as described in BCP 105 14 [RFC2119] [RFC8174] when, and only when, they appear in all 106 capitals, as shown here. 108 3. Addition to the Capabilities Object 110 The capabilities object is returned as part of the JMAP Session 111 object; see [RFC8620], Section 2. 113 This document defines an additional capability URI. 115 3.1. urn:ietf:params:jmap:blob 117 The capability urn:ietf:params:jmap:blob being present in the 118 "accountCapabilities" property of an account represents support for 119 additional API methods on the Blob datatype. Servers that include 120 the capability in one or more "accountCapabilities" properties MUST 121 also include the property in the "capabilities" property. 123 The value of this property in the JMAP session "capabilities" 124 property MUST be an empty object. 126 The value of this property in an account's "accountCapabilities" 127 property is an object that MAY contain the following information on 128 server capabilities and permissions for that account: 130 * maxSizeBlobSet: UnsignedInt|null 132 If present, this is the maximum size of blob (in octets) that the 133 server will allow to be created (including blobs created by 134 concatenating multiple data sources together). 136 Clients MUST NOT attempt to create blobs larger than this size. 138 If this value is not present or null, then clients are not 139 required to limit the size of blob they try to create, though 140 servers can always reject creation of blobs regardless of size; 141 e.g. due to lack of disk space, or per-user rate limits. 143 * maxDataSources: UnsignedInt|null 145 If present, gives the maximum number of of DataSourceObjects 146 allowed per creation in a Blob/upload. Servers MUST allow at 147 least 64 items. 149 If this value is not present or null, then clients SHOULD assume a 150 limit of 64 items. 152 * supportedTypeNames: [String]|null 154 An array of data type names that are supported for Blob/lookup. 155 If the server does not support lookups then this could be the 156 empty list, not present or null. 158 3.1.1. Capability Example 160 { 161 "capabilities": { 162 ..., 163 "urn:ietf:params:jmap:blob": {} 164 }, 165 "accounts": { 166 "A13842": { 167 ... 168 "accountCapabilities": { 169 "urn:ietf:params:jmap:blob": { 170 "maxSizeBlobSet": 50000000, 171 "maxDataSources": 100, 172 "supportedTypeNames" : [ 173 "Mailbox", 174 "Thread", 175 "Email" 176 ] 177 } 178 } 179 } 180 } 181 } 183 4. Blob Methods 185 A blob is a sequence of zero or more octets. 187 The JMAP base spec [RFC8620] defines the Blob/copy method, which is 188 unchanged by this specfication, and is selected by the 189 urn:ietf:params:jmap:core capability. 191 The following JMAP Methods are selected by the 192 urn:ietf:params:jmap:blob capability. 194 4.1. Blob/upload 196 This is similar to a Foo/set from [RFC8620], but only has a create 197 argument as blobs can't be updated or deleted. 199 *Parameters* 201 * accountId: Id 203 The id of the account in which the blobs will be created. 205 * create: Id[UploadObject] 207 A map of creation id to UploadObjects. 209 *Result* 211 The result is the same as for Foo/set in RFC8620, with created and 212 notCreated objects mapping from the creationId. 214 The created objects contain: 216 * id: Id 218 the blobId which was created 220 * type: String|null 222 the media type as given in the creation (if any); or detected from 223 content; or null 225 * size: UnsignedInt 227 as per RFC8620 - the size of the created blob in octets 229 Plus any other properties identical to those that would be returned 230 in the JSON response of the RFC8620 upload endpoint (which may be 231 extended in the future - this document anticipates that 232 implementations will extend both the upload endpoint and the Blob/ 233 upload responses in the same way) 235 Or if there is a problem with a creation, then the server will return 236 a notCreated response with a map from the failed creationId to a 237 SetError object. 239 NOTE: Servers MUST set the creationIds value to the blobId returned 240 in any successful Blob/upload's created response to allow 241 backreferences, as that is the main purpose of this extensions. 243 *UploadObject* 245 * data: [DataSourceObject] 247 an array of zero or more octet sources in order (zero to create an 248 empty blob). The result of each of these sources is concatenated 249 together in order to create the blob. 251 * type: String|null (default: null) 253 hint for media type of the data 255 *DataSourceObject* 257 Exactly one of: 259 * data:asText: String|null 261 * data:asBase64: String|null 263 or a blobId source: 265 * blobId: Id 267 * offset: UnsignedInt|null (MAY be zero) 269 * length: UnsignedInt|null (MUST NOT be zero) 271 If null then offset is assumed to be zero. 273 If null then length is the remaining octets in the blob. 275 If the range can not be fully satisfied (i.e. extends past the end of 276 the data in the blob) then the DataSourceObject is invalid and 277 results in a notCreated response for this creation id. 279 If the data properties have any invalid references or invalid data 280 contained in them, the server MUST NOT guess as to the user's intent, 281 and MUST reject the creation and return a notCreated response for 282 that creation id. 284 Likewise, invalid characters in the base64 of data:asBase64, or 285 invalid UTF-8 in data:asText MUST result in a nonCreated response. 287 It is envisaged that the definition for DataSourceObject might be 288 extended in future, for example to fetch external content. 290 A server MUST accept at least 64 DataSourceObjects per create, as 291 described in Section 3.1 of this document. 293 4.1.1. Blob/upload simple example 295 The data:asBase64 field is set over multiple lines for ease of 296 publication here, however all data:asBase64 would be sent as a 297 continuous string with no whitespace on the wire. 299 Method Call: 301 [ 302 "Blob/upload", 303 { 304 "accountId": "account1", 305 "create": { 306 "1": { 307 "data" : [ 308 { 309 "data:asBase64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKA 310 AAAA1BMVEX/AAAZ4gk3AAAAAXRSTlN/gFy0ywAAAApJRE 311 FUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=", 312 } 313 ], 314 "type": "image/png" 315 }, 316 }, 317 }, 318 "R1" 319 ] 321 Response: 323 [ 324 "Blob/upload", 325 { 326 "accountId" : "account1", 327 "created" : { 328 "1": { 329 "id" : "G4c6751edf9dd6903ff54b792e432fba781271beb", 330 "type" : "image/png", 331 "size" : 95 332 }, 333 }, 334 }, 335 "R1" 336 ] 338 4.1.2. Blob/upload complex example 340 Method Calls: 342 [ 343 [ 344 "Blob/upload", 345 { 346 "create": { 347 "b4": { 348 "data": [ 349 { 350 "data:asText": "The quick brown fox jumped over the lazy dog." 351 } 352 ] 353 } 354 } 355 }, 356 "S4" 357 ], 358 [ 359 "Blob/upload", 360 { 361 "create": { 362 "cat": { 363 "data": [ 364 { 365 "data:asText": "How" 366 }, 367 { 368 "blobId": "#b4", 369 "length": 7, 370 "offset": 3 371 }, 372 { 373 "data:asText": "was t" 374 }, 375 { 376 "blobId": "#b4", 377 "length": 1, 378 "offset": 1 379 }, 380 { 381 "data:asBase64": "YXQ/" 382 } 383 ] 384 } 385 } 386 }, 387 "CAT" 388 ], 389 [ 390 "Blob/get", 391 { 392 "properties": [ 393 "data:asText", 394 "size" 396 ], 397 "ids": [ 398 "#cat" 399 ] 400 }, 401 "G4" 402 ] 403 ] 405 Responses: 407 [ 408 [ 409 "Blob/upload", 410 { 411 "oldState": null, 412 "created": { 413 "b4": { 414 "id": "Gc0854fb9fb03c41cce3802cb0d220529e6eef94e", 415 "size": 45, 416 "type": "application/octet-stream" 417 } 418 }, 419 "updated": null, 420 "destroyed": null, 421 "notCreated": null, 422 "notUpdated": null, 423 "notDestroyed": null, 424 "accountId": "account1" 425 }, 426 "S4" 427 ], 428 [ 429 "Blob/upload", 430 { 431 "oldState": null, 432 "created": { 433 "cat": { 434 "id": "Gcc60576f036321ae6e8037ffc56bdee589bd3e23", 435 "size": 19, 436 "type": "application/octet-stream" 437 } 438 }, 439 "updated": null, 440 "destroyed": null, 441 "notCreated": null, 442 "notUpdated": null, 443 "notDestroyed": null, 444 "accountId": "account1" 445 }, 446 "CAT" 447 ], 448 [ 449 "Blob/get", 450 { 451 "list": [ 452 { 453 "id": "Gcc60576f036321ae6e8037ffc56bdee589bd3e23", 454 "data:asText": "How quick was that?", 455 "size": 19 456 } 457 ], 458 "notFound": [], 459 "accountId": "account1" 460 }, 461 "G4" 462 ] 463 ] 465 4.2. Blob/get 467 A standard JMAP get, with two additional optional parameters: 469 * offset: UnsignedInt|null 471 start this many octets into the blob data. If null or 472 unspecified, this defaults to zero. 474 * length: UnsignedInt|null 476 return at most this many octets of the blob data. If null or 477 unspecified, then all remaining octets in the blob are returned. 478 This can be considered equivalent to an infinitely large length 479 value, except that the isTruncated warning is not given unless the 480 start offset is past the end of the blob. 482 *Request Properties:* 484 Any of 486 * data:asText 488 * data:asBase64 490 * data (returns data:asText if the selected octets are valid UTF-8, 491 or data:asBase64) 493 * size 495 If not given, properties defaults to data and size. 497 *Result Properties:* 499 * data:asText: String|null 501 the raw octets of the selected range if they are valid UTF-8, 502 otherwise null 504 * data:asBase64: String 506 the base64 encoding of the octets in the selected range 508 * isEncodingProblem: Boolean (default: false) 510 * isTruncated: Boolean (default: false) 512 * size: UnsignedInt 514 the number of octets in the entire blob 516 The size value MUST always be the number of octets in the underlying 517 blob, regardless of offset and length. 519 The data fields contain a representation of the octets within the 520 selected range that are present in the blob. If the octets selected 521 are not valid UTF-8 (including truncating in the middle of a multi- 522 octet sequence) and data or data:asText was requested, then the key 523 isEncodingProblem MUST be set to true and the data:asText response 524 value MUST be null. In the case where data was requested and the 525 data is not valid UTF-8, then data:asBase64 MUST be returned. 527 If the selected range requests data outside the blob (i.e. the 528 offset+length is larger than the blob) then the result is either just 529 the octets from the offset to the end of the blob, or an empty string 530 if the offset is past the end of the blob. Either way, the 531 isTruncated property in the result MUST be set to true to tell the 532 client that the requested range could not be fully satisfied. 534 Servers SHOULD store the size for blobs in a format which is 535 efficient to read, and clients SHOULD limit their request to just the 536 size parameter if that is all they need, as fetching blob content 537 could be significantly more expensive and slower for the server. 539 4.2.1. Blob/get simple example 541 Where a blob containing the string "The quick brown fox jumped over 542 the lazy dog!" has blobId G6ec94756e3e046be78fcb33953b85b944e70673e. 544 The first method call requests just the size for multiple blobs, and 545 the second requests both size and a short range of the data for one 546 of the blobs. 548 Method Calls: 550 [ 551 [ 552 "Blob/get", 553 { 554 "ids" : [ 555 "G6ec94756e3e046be78fcb33953b85b944e70673e", 556 "not-a-blob" 557 ], 558 "properties" : [ 559 "size" 560 ] 561 }, 562 "R1" 563 ], 564 [ 565 "Blob/get", 566 { 567 "accountId" : "account1", 568 "ids" : [ 569 "G6ec94756e3e046be78fcb33953b85b944e70673e" 570 ], 571 "properties" : [ 572 "data:asText", 573 "data:asBase64", 574 "size" 575 ], 576 "offset" : 4, 577 "length" : 9 578 }, 579 "R2" 580 ] 581 ] 583 Responses: 585 [ 586 [ 587 "Blob/get", 588 { 589 "accountId": "account1", 590 "list": [ 591 { 592 "id": "G6ec94756e3e046be78fcb33953b85b944e70673e", 593 "size": 46 594 } 595 ], 596 "notFound": [ 597 "not-a-blob" 598 ] 599 }, 600 "R1" 601 ], 602 [ 603 "Blob/get", 604 { 605 "accountId": "account1", 606 "list": [ 607 { 608 "id": "G6ec94756e3e046be78fcb33953b85b944e70673e", 609 "data:asText": "quick bro", 610 "data:asBase64": "cXVpY2sgYnJvCg==", 611 "size": 46 612 } 613 ] 614 }, 615 "R2" 616 ] 617 ] 619 4.2.2. Blob/get example with range and encoding errors 621 The b1 value is the text: "The quick brown fox jumped over the 622 \x81\x81 fox" which contains an invalid utf8 sequence. 624 The results have the following interesting properties: 626 * G1: defaults to data and size - so b1 returns isEncodingProblem 627 and a base64 value. 629 * G2: since data:asText was explicitly selected, does not attempt to 630 return a value for the data, just isEncodingProblem for b1. 632 * G3: since only data:asBase64 was requested, there is no encoding 633 problem and both values are returned. 635 * G4: since the requested range could be satisfied as text, both 636 blobs are returned as data:asText and there is no encoding 637 problem. 639 * G5: both blobs cannot satisfy the requested range, so isTruncated 640 is true for both. 642 Note: some values have been wrapped for line length - there would be 643 no whitespace in the data:asBase64 values on the wire 645 Method calls: 647 [ 648 [ 649 "Blob/upload", 650 { 651 "create": { 652 "b1": { 653 "data": [ 654 { 655 "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW 656 Qgb3ZlciB0aGUggYEgZG9nLg==" 657 } 658 ] 659 }, 660 "b2": { 661 "data": [ 662 { 663 "data:asText": "hello world" 664 } 665 ], 666 "type" : "text/plain" 667 } 668 } 669 }, 670 "S1" 671 ], 672 [ 673 "Blob/get", 674 { 675 "ids": [ 676 "#b1", 677 "#b2" 678 ] 679 }, 680 "G1" 681 ], 682 [ 683 "Blob/get", 684 { 685 "ids": [ 686 "#b1", 687 "#b2" 688 ], 689 "properties": [ 690 "data:asText", 691 "size" 692 ] 693 }, 694 "G2" 695 ], 696 [ 697 "Blob/get", 698 { 699 "ids": [ 700 "#b1", 701 "#b2" 702 ], 703 "properties": [ 704 "data:asBase64", 705 "size" 706 ] 707 }, 708 "G3" 709 ], 710 [ 711 "Blob/get", 712 { 713 "offset": 0, 714 "length": 5, 715 "ids": [ 716 "#b1", 717 "#b2" 718 ] 719 }, 720 "G4" 721 ], 722 [ 723 "Blob/get", 724 { 725 "offset": 20, 726 "length": 100, 727 "ids": [ 728 "#b1", 729 "#b2" 730 ] 732 }, 733 "G5" 734 ] 735 ] 737 Responses: 739 [ 740 [ 741 "Blob/upload", 742 { 743 "oldState": null, 744 "created": { 745 "b2": { 746 "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", 747 "size": 11, 748 "type": "application/octet-stream" 749 }, 750 "b1": { 751 "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", 752 "size": 43, 753 "type": "text/plain" 754 } 755 }, 756 "updated": null, 757 "destroyed": null, 758 "notCreated": null, 759 "notUpdated": null, 760 "notDestroyed": null, 761 "accountId": "account1" 762 }, 763 "S1" 764 ], 765 [ 766 "Blob/get", 767 { 768 "list": [ 769 { 770 "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", 771 "isEncodingProblem": true, 772 "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW 773 Qgb3ZlciB0aGUggYEgZG9nLg==", 774 "size": 43 775 }, 776 { 777 "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", 778 "data:asText": "hello world", 779 "size": 11 781 } 782 ], 783 "notFound": [], 784 "accountId": "account1" 785 }, 786 "G1" 787 ], 788 [ 789 "Blob/get", 790 { 791 "list": [ 792 { 793 "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", 794 "isEncodingProblem": true, 795 "size": 43 796 }, 797 { 798 "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", 799 "data:asText": "hello world", 800 "size": 11 801 } 802 ], 803 "notFound": [], 804 "accountId": "account1" 805 }, 806 "G2" 807 ], 808 [ 809 "Blob/get", 810 { 811 "list": [ 812 { 813 "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", 814 "data:asBase64": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZW 815 Qgb3ZlciB0aGUggYEgZG9nLg==", 816 "size": 43 817 }, 818 { 819 "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", 820 "data:asBase64": "aGVsbG8gd29ybGQ=", 821 "size": 11 822 } 823 ], 824 "notFound": [], 825 "accountId": "account1" 826 }, 827 "G3" 828 ], 830 [ 831 "Blob/get", 832 { 833 "list": [ 834 { 835 "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", 836 "data:asText": "The q", 837 "size": 43 838 }, 839 { 840 "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", 841 "data:asText": "hello", 842 "size": 11 843 } 844 ], 845 "notFound": [], 846 "accountId": "account1" 847 }, 848 "G4" 849 ], 850 [ 851 "Blob/get", 852 { 853 "list": [ 854 { 855 "id": "G72cfa4804194563685d9a4b695f7ba20e7739576", 856 "isTruncated": true, 857 "isEncodingProblem": true, 858 "data:asBase64": "anVtcGVkIG92ZXIgdGhlIIGBIGRvZy4=", 859 "size": 43 860 }, 861 { 862 "id": "G2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", 863 "isTruncated": true, 864 "data:asText": "", 865 "size": 11 866 } 867 ], 868 "notFound": [], 869 "accountId": "account1" 870 }, 871 "G5" 872 ] 873 ] 875 4.3. Blob/lookup 877 Given a list of blobIds, this method does a reverse lookup in each of 878 the provided type names to find the list of Ids within that data type 879 which reference the provided blob. 881 The definition of reference is somewhat loosely defined, but roughly 882 means "you could discover this blobId by looking inside this object", 883 for example if a Mailbox contains an Email which references the 884 blobId, then it references that blobId. Likewise for a Thread. 886 *Parameters* 888 * accountId: Id 890 The id of the account used for the call. 892 * typeNames: [String] 894 A list of names from the "JMAP Data Types" registry. Only names 895 for which "Can reference blobs" is true may be specified, and the 896 capability which defines each type must also be used by the 897 overall JMAP request in which this method is called. 899 If a type name is not known by the server, or the associated 900 capability has not been requested, then the server returns an 901 "unknownDataType" error. 903 * ids: [Id] 905 A list of blobId values to be looked for. 907 *Response* 909 * list: [BlobInfo] 911 A list of BlobInfo objects. 913 *BlobInfo Object* 915 * id: Id 917 The Blob Identifier. 919 * matchedIds: String[Id List] 921 A map from type name to list of Ids of that data type (e.g. the 922 name "Email" maps to a list of emailIds) 924 If a blob is not visible to a user at all, then the server SHOULD 925 return that blobId in the notFound array, however it may also return 926 an empty list for each type name, as it may not be able to know if 927 other data types do reference that blob. 929 4.3.1. Blob/lookup example 930 Method call: 932 [ 933 "Blob/lookup", 934 { 935 "typeNames": [ 936 "Mailbox", 937 "Thread", 938 "Email" 939 ], 940 "ids": [ 941 "Gd2f81008cf07d2425418f7f02a3ca63a8bc82003", 942 "not-a-blob" 943 ] 944 }, 945 "R1" 946 ] 948 Response: 950 [ 951 "Blob/lookup", 952 { 953 "list": [ 954 { 955 "id": "Gd2f81008cf07d2425418f7f02a3ca63a8bc82003", 956 "matchedIds": { 957 "Mailbox": [ 958 "M54e97373", 959 "Mcbe6b662" 960 ], 961 "Thread": [ 962 "T1530616e" 963 ], 964 "Email": [ 965 "E16e70a73eb4", 966 "E84b0930cf16" 967 ] 968 } 969 } 970 ], 971 "notFound": [ 972 "not-a-blob" 973 ] 974 }, 975 "R1" 976 ] 978 5. Security considerations 980 JSON parsers are not all consistent in handling non-UTF-8 data. JMAP 981 requires that all JSON data be UTF-8 encoded, so servers MUST only 982 return a null value if data:asText is requested for a range of octets 983 which is not valid UTF-8, and set isEncodingProblem: true. 985 Servers MUST apply any access controls, such that if the 986 authenticated user would be unable to discover the blobId by making 987 queries, then this fact can't be discovered via a Blob/lookup. For 988 example, if an Email exists in a Mailbox which the authenticated user 989 does not have access to see, then that emailId MUST not be returned 990 in a lookup for a blob which is referenced by that email. 992 If a server might sometimes return all names empty rather than 993 putting a blobId in the notFound response to a Blob/get, then the 994 server SHOULD always return the same type of response, regardless of 995 whether a blob exists but the user can't access it, or doesn't exist 996 at all. This avoids leaking information about the existence of the 997 blob. 999 The server MUST NOT trust that the data given to a Blob/upload is a 1000 well formed instance of the specified media type, and if the server 1001 attempts to parse the given blob, only hardened parsers designed to 1002 deal with arbitrary untrusted data should be used. The server SHOULD 1003 NOT reject data on the grounds that it is not a valid specimen of the 1004 stated type. 1006 Blob/upload with carefully chosen data sources can be used to 1007 recreate dangerous content on the far side of security scanners 1008 (anti-virus or exfiltration scanners for example) which may be 1009 watching the upload endpoint. Server implementations SHOULD provide 1010 a hook to allow security scanners to check the resulting blob after 1011 concatenating the data sources in the same way that they do for the 1012 upload endpoint. 1014 6. IANA considerations 1016 6.1. JMAP Capability registration for "blob" 1018 IANA is requested to register the "blob" JMAP Capability as follows: 1020 Capability Name: urn:ietf:params:jmap:blob 1022 Specification document: this document 1024 Intended use: common 1025 Change Controller: IETF 1027 Security and privacy considerations: this document, Section XXX 1029 6.2. JMAP Error Codes Registration for "unknownDataType" 1031 IANA is requested to register the "unknownDataType" JMAP Error Code 1032 as follows: 1034 JMAP Error Code: unknownDataType 1036 Intended use: common 1038 Change Controller: IETF 1040 Reference: this document 1042 Description: The server does not recognise this data type, or the 1043 capability to enable it was not present. 1045 6.3. Creation of "JMAP Data Types" Registry 1047 IANA is requested to create a new registry "JMAP Data Types" with the 1048 initial content: 1050 +================+=========+======+=====================================+=========+ 1051 |Type Name |Can |Can |Capability |Reference| 1052 | |reference|use | | | 1053 | |blobs |for | | | 1054 | | |state | | | 1055 | | |change| | | 1056 +================+=========+======+=====================================+=========+ 1057 |Core |No |No |urn:ietf:params:jmap:core |[RFC8620]| 1058 +----------------+---------+------+-------------------------------------+---------+ 1059 |PushSubscription|No |No |urn:ietf:params:jmap:core |[RFC8620]| 1060 +----------------+---------+------+-------------------------------------+---------+ 1061 |Mailbox |Yes |Yes |urn:ietf:params:jmap:mail |[RFC8621]| 1062 +----------------+---------+------+-------------------------------------+---------+ 1063 |Thread |Yes |Yes |urn:ietf:params:jmap:mail |[RFC8621]| 1064 +----------------+---------+------+-------------------------------------+---------+ 1065 |Email |Yes |Yes |urn:ietf:params:jmap:mail |[RFC8621]| 1066 +----------------+---------+------+-------------------------------------+---------+ 1067 |EmailDelivery |No |Yes |urn:ietf:params:jmap:mail |[RFC8621]| 1068 +----------------+---------+------+-------------------------------------+---------+ 1069 |SearchSnippet |No |No |urn:ietf:params:jmap:mail |[RFC8621]| 1070 +----------------+---------+------+-------------------------------------+---------+ 1071 |Identity |No |Yes |urn:ietf:params:jmap:submission |[RFC8621]| 1072 +----------------+---------+------+-------------------------------------+---------+ 1073 |EmailSubmission |No |Yes |urn:ietf:params:jmap:submission |[RFC8621]| 1074 +----------------+---------+------+-------------------------------------+---------+ 1075 |VacationResponse|No |Yes |urn:ietf:params:jmap:vacationresponse|[RFC8621]| 1076 +----------------+---------+------+-------------------------------------+---------+ 1077 |MDN |No |No |urn:ietf:params:jmap:mdn |[RFC9007]| 1078 +----------------+---------+------+-------------------------------------+---------+ 1080 Table 1 1082 7. Changes 1084 EDITOR: please remove this section before publication. 1086 The source of this document exists on github at: 1087 https://github.com/brong/draft-gondwana-jmap-blob/ 1088 (https://github.com/brong/draft-gondwana-jmap-blob/) 1090 *draft-ietf-jmap-blob-11*: 1092 * updates based on IETF113 feedback: 1094 - added wording to suggest the a Blob/get of just size might be 1095 faster 1097 - added an example with just the size field being selected 1099 *draft-ietf-jmap-blob-10*: 1101 * removed remaining references to catenate. 1103 *draft-ietf-jmap-blob-09*: 1105 * tidied up introduction text 1107 * replaced Blob/set with Blob/upload 1109 * made all upload creates take an array of sources to normalise 1110 behaviour at the cost of a slightly more complex default case. 1112 *draft-ietf-jmap-blob-08*: 1114 * Fixed spelling of Neil's name in acknowledgements 1116 * Last call review (thanks Jim Fenton) 1118 - fixed mmark sillyness causing RFC8620 to be non-normative in 1119 the references 1121 - clarified the capability object and accountCapability object 1122 requirements 1124 - made capability keys much more tightly defined, with mandatory 1125 minimum catenate limit and default values. 1127 - increased use of normative language generally 1129 - lowercased 'blob' anywhere it wasn't explicitly the object 1131 - lowercased titles of the columns in the registry 1133 *draft-ietf-jmap-blob-07*: 1135 * more examples to cover the interactions of offset, length and 1136 encoding checks. 1138 *draft-ietf-jmap-blob-06*: 1140 * removed asHex - we only need base64 and text 1142 * added reference to where base64 is defined 1144 * made 'destroy' not be allowed 1146 * expanded JSON examples for readability 1147 * removed 'expires' from examples 1149 *draft-ietf-jmap-blob-05*: 1151 * discovered I hadn't actually included typeNames and matchedIds 1152 anywhere except the updates section, oops! 1154 * added a catenate example 1156 * tightened up some text 1158 *draft-ieft-jmap-blob-04*: 1160 * added security considerations for scanning catenate results 1162 *draft-ieft-jmap-blob-03*: 1164 * added capabilities object 1166 * renamed types to typeNames and matchedIds 1168 * added details of how to handle non-UTF8 data and truncation in 1169 Blob/get 1171 * added isTruncated and isEncodingProblem to Blob/get to tell the 1172 client if the request wasn't entirely satisfied. 1174 *draft-ieft-jmap-blob-02*: 1176 * fixed incorrect RFC number in reference and HTTP PUT -> POST, 1177 thanks Ken. 1179 * added acknowledgements section 1181 * removed all 'datatype' text and changed to 'data type' or 'type 1182 name' as appropriate (issue #1 proposal) 1184 * expanded security considerations section and moved optional Blob/ 1185 lookup empty case into Blob/lookup section 1187 *draft-ieft-jmap-blob-01*: 1189 * renamed 'datatypes' to 'types' to align with PushSubscription from 1190 RFC8620. 1192 * added example for Blob/get 1194 * specified offset and length precisely 1195 *draft-ieft-jmap-blob-00*: 1197 * initial adoption as an IETF document, otherwise identical to 1198 draft-gondwana-jmap-blob-02 1200 *draft-gondwana-jmap-blob-02* 1202 * renamed 'objects' to 'datatypes' 1204 * specified Blob/lookup 1206 * added IANA registry for datatypes 1208 *draft-gondwana-jmap-blob-01* 1210 * added an example 1212 *draft-gondwana-jmap-blob-00* 1214 * initial proposal 1216 8. Acknowledgements 1218 Joris Baum, Jim Fenton, Neil Jenkins, Alexey Melnikov, Ken Murchison, 1219 Robert Stepanek and the JMAP working group at the IETF. 1221 9. Normative References 1223 [RFC8620] Jenkins, N. and C. Newman, "The JSON Meta Application 1224 Protocol (JMAP)", RFC 8620, DOI 10.17487/RFC8620, July 1225 2019, . 1227 [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data 1228 Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, 1229 . 1231 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 1232 Requirement Levels", BCP 14, RFC 2119, 1233 DOI 10.17487/RFC2119, March 1997, 1234 . 1236 [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 1237 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, 1238 May 2017, . 1240 10. Informative References 1242 [RFC7888] Melnikov, A., Ed., "IMAP4 Non-synchronizing Literals", 1243 RFC 7888, DOI 10.17487/RFC7888, May 2016, 1244 . 1246 [RFC8621] Jenkins, N. and C. Newman, "The JSON Meta Application 1247 Protocol (JMAP) for Mail", RFC 8621, DOI 10.17487/RFC8621, 1248 August 2019, . 1250 Author's Address 1252 Bron Gondwana (editor) 1253 Fastmail 1254 Level 2, 114 William St 1255 Melbourne VIC 3000 1256 Australia 1257 Email: brong@fastmailteam.com 1258 URI: https://www.fastmail.com