845 lines
31 KiB
Plaintext
845 lines
31 KiB
Plaintext
|
|
|
|
|
|
|
|
Internet Engineering Task Force (IETF) N. Jenkins, Ed.
|
|
Request for Comments: 9610 Fastmail
|
|
Category: Standards Track December 2024
|
|
ISSN: 2070-1721
|
|
|
|
|
|
JSON Meta Application Protocol (JMAP) for Contacts
|
|
|
|
Abstract
|
|
|
|
This document specifies a data model for synchronising contact data
|
|
with a server using the JSON Meta Application Protocol (JMAP).
|
|
|
|
Status of This Memo
|
|
|
|
This is an Internet Standards Track document.
|
|
|
|
This document is a product of the Internet Engineering Task Force
|
|
(IETF). It represents the consensus of the IETF community. It has
|
|
received public review and has been approved for publication by the
|
|
Internet Engineering Steering Group (IESG). Further information on
|
|
Internet Standards is available in Section 2 of RFC 7841.
|
|
|
|
Information about the current status of this document, any errata,
|
|
and how to provide feedback on it may be obtained at
|
|
https://www.rfc-editor.org/info/rfc9610.
|
|
|
|
Copyright Notice
|
|
|
|
Copyright (c) 2024 IETF Trust and the persons identified as the
|
|
document authors. All rights reserved.
|
|
|
|
This document is subject to BCP 78 and the IETF Trust's Legal
|
|
Provisions Relating to IETF Documents
|
|
(https://trustee.ietf.org/license-info) in effect on the date of
|
|
publication of this document. Please review these documents
|
|
carefully, as they describe your rights and restrictions with respect
|
|
to this document. Code Components extracted from this document must
|
|
include Revised BSD License text as described in Section 4.e of the
|
|
Trust Legal Provisions and are provided without warranty as described
|
|
in the Revised BSD License.
|
|
|
|
Table of Contents
|
|
|
|
1. Introduction
|
|
1.1. Notational Conventions
|
|
1.2. Terminology
|
|
1.3. Data Model Overview
|
|
1.4. Addition to the Capabilities Object
|
|
1.4.1. urn:ietf:params:jmap:contacts
|
|
2. AddressBooks
|
|
2.1. AddressBook/get
|
|
2.2. AddressBook/changes
|
|
2.3. AddressBook/set
|
|
3. ContactCards
|
|
3.1. ContactCard/get
|
|
3.2. ContactCard/changes
|
|
3.3. ContactCard/query
|
|
3.3.1. Filtering
|
|
3.3.2. Sorting
|
|
3.4. ContactCard/queryChanges
|
|
3.5. ContactCard/set
|
|
3.6. ContactCard/copy
|
|
4. Examples
|
|
4.1. Fetching Initial Data
|
|
4.2. Changing the Default Address Book
|
|
5. Internationalisation Considerations
|
|
6. Security Considerations
|
|
7. IANA Considerations
|
|
7.1. JMAP Capability Registration for "contacts"
|
|
7.2. JMAP Data Type Registration for "AddressBook"
|
|
7.3. JMAP Data Type Registration for "ContactCard"
|
|
7.4. JMAP Error Codes Registry
|
|
7.4.1. addressBookHasContents
|
|
7.5. JSContact Property Registrations
|
|
7.5.1. id
|
|
7.5.2. addressBookIds
|
|
7.5.3. blobId
|
|
8. References
|
|
8.1. Normative References
|
|
8.2. Informative References
|
|
Author's Address
|
|
|
|
1. Introduction
|
|
|
|
The JSON Meta Application Protocol (JMAP) [RFC8620] is a generic
|
|
protocol for synchronising data, such as mail, calendars, or
|
|
contacts, between a client and a server. It is optimised for mobile
|
|
and web environments and aims to provide a consistent interface to
|
|
different data types.
|
|
|
|
This specification defines a data model for synchronising contacts
|
|
between a client and a server using JMAP.
|
|
|
|
1.1. Notational Conventions
|
|
|
|
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
|
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
|
|
"OPTIONAL" in this document are to be interpreted as described in
|
|
BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
|
|
capitals, as shown here.
|
|
|
|
Type signatures, examples, and property descriptions in this document
|
|
follow the conventions established in Section 1.1 of [RFC8620]. The
|
|
Id, UnsignedInt, and UTCDate data types defined in Sections 1.2, 1.3,
|
|
and 1.4 of [RFC8620] are also used in this document.
|
|
|
|
1.2. Terminology
|
|
|
|
The same terminology used in the core JMAP specification (see
|
|
Section 1.6 of [RFC8620]) is also used in this document.
|
|
|
|
The terms AddressBook and ContactCard (with these specific
|
|
capitalizations) are used to refer to the data types defined in this
|
|
document and instances of those data types.
|
|
|
|
1.3. Data Model Overview
|
|
|
|
An Account (see Section 1.6.2 of [RFC8620]) with support for the
|
|
contact data model contains zero or more AddressBook objects, which
|
|
is a named collection of zero or more ContactCards. A ContactCard is
|
|
a representation of a person, company, entity, or a group of such
|
|
entities in JSContact Card format, as defined in Section 2 of
|
|
[RFC9553]. Each ContactCard belongs to one or more AddressBooks.
|
|
|
|
In servers with support for JMAP Sharing [RFC9670], users may see and
|
|
configure sharing of contact data with others. Sharing permissions
|
|
are managed per AddressBook.
|
|
|
|
1.4. Addition to the Capabilities Object
|
|
|
|
The capabilities object is returned as part of the JMAP Session
|
|
object; see Section 2 of [RFC8620]. This document defines one
|
|
additional capability URI.
|
|
|
|
1.4.1. urn:ietf:params:jmap:contacts
|
|
|
|
This represents support for the AddressBook and ContactCard data
|
|
types and associated API methods. The value of this property in the
|
|
JMAP Session "capabilities" property is an empty object.
|
|
|
|
The value of this property in an account's "accountCapabilities"
|
|
property is an object that MUST contain the following information on
|
|
server capabilities and permissions for that account:
|
|
|
|
*maxAddressBooksPerCard*: UnsignedInt|null
|
|
The maximum number of AddressBooks (see Section 2) that can be
|
|
assigned to a single ContactCard object (see Section 3). This
|
|
MUST be an integer >= 1, or null for no limit (or rather, the
|
|
limit is always the number of AddressBooks in the account).
|
|
|
|
*mayCreateAddressBook*: Boolean
|
|
The user may create an AddressBook in this account if, and only
|
|
if, this is true.
|
|
|
|
2. AddressBooks
|
|
|
|
An AddressBook is a named collection of ContactCards. All
|
|
ContactCards are associated with one or more AddressBooks.
|
|
|
|
An *AddressBook* object has the following properties:
|
|
|
|
*id*: Id (immutable; server-set)
|
|
The id of the AddressBook.
|
|
|
|
*name*: String
|
|
The user-visible name of the AddressBook. This MUST NOT be the
|
|
empty string and MUST NOT be greater than 255 octets in size when
|
|
encoded as UTF-8.
|
|
|
|
*description*: String|null (default: null)
|
|
An optional long-form description of the AddressBook that provides
|
|
context in shared environments where users need more than just the
|
|
name.
|
|
|
|
*sortOrder*: UnsignedInt (default: 0)
|
|
Defines the sort order of AddressBooks when presented in the
|
|
client's UI so it is consistent between devices. The number MUST
|
|
be an integer in the range 0 <= sortOrder < 2^31.
|
|
|
|
An AddressBook with a lower order is to be displayed before a
|
|
AddressBook with a higher order in any list of AddressBooks in the
|
|
client's UI. AddressBooks with equal order should be sorted in
|
|
alphabetical order by name. The sorting should take into account
|
|
locale-specific character order convention.
|
|
|
|
*isDefault*: Boolean (server-set)
|
|
This SHOULD be true for exactly one AddressBook in any account and
|
|
MUST NOT be true for more than one AddressBook within an account.
|
|
The default AddressBook should be used by clients whenever they
|
|
need to choose an AddressBook for the user within this account and
|
|
they do not have any other information on which to make a choice.
|
|
For example, if the user creates a new contact card, the client
|
|
may automatically set the card as belonging to the default
|
|
AddressBook from the user's primary account.
|
|
|
|
*isSubscribed*: Boolean
|
|
True if the user has indicated they wish to see this AddressBook
|
|
in their client. This SHOULD default to false for AddressBooks in
|
|
shared accounts that the user has access to and true for any new
|
|
AddressBooks created by the user themself.
|
|
|
|
If false, the AddressBook and its contents SHOULD only be
|
|
displayed when the user explicitly requests it. The UI may offer
|
|
to the user the option of subscribing to it.
|
|
|
|
*shareWith*: Id[AddressBookRights]|null (default: null)
|
|
A map of the Principal id (Section 2 of [RFC9670]) to rights for
|
|
Principals this AddressBook is shared with. The Principal to
|
|
which this AddressBook belongs MUST NOT be in this set. This is
|
|
null if the AddressBook is not shared with anyone or if the server
|
|
does not support [RFC9670]. The value may be modified only if the
|
|
user has the "mayShare" right. The account id for the Principals
|
|
may be found in the urn:ietf:params:jmap:principals:owner
|
|
capability of the Account to which the AddressBook belongs.
|
|
|
|
*myRights*: AddressBookRights (server-set)
|
|
The set of access rights the user has in relation to this
|
|
AddressBook.
|
|
|
|
An *AddressBookRights* object has the following properties:
|
|
|
|
*mayRead*: Boolean
|
|
The user may fetch the ContactCards in this AddressBook.
|
|
|
|
*mayWrite*: Boolean
|
|
The user may create, modify, or destroy all ContactCards in this
|
|
AddressBook, or move them to or from this AddressBook.
|
|
|
|
*mayShare*: Boolean
|
|
The user may modify the "shareWith" property for this AddressBook.
|
|
|
|
*mayDelete*: Boolean
|
|
The user may delete the AddressBook itself.
|
|
|
|
2.1. AddressBook/get
|
|
|
|
This is a standard "/get" method as described in Section 5.1 of
|
|
[RFC8620]. The "ids" argument may be null to fetch all at once.
|
|
|
|
2.2. AddressBook/changes
|
|
|
|
This is a standard "/changes" method as described in Section 5.2 of
|
|
[RFC8620].
|
|
|
|
2.3. AddressBook/set
|
|
|
|
This is a standard "/set" method as described in Section 5.3 of
|
|
[RFC8620], but with the following additional request arguments:
|
|
|
|
*onDestroyRemoveContents*: Boolean (default: false)
|
|
If false, any attempt to destroy an AddressBook that still has a
|
|
ContactCard in it will be rejected with an
|
|
"addressBookHasContents" SetError. If true, any ContactCard that
|
|
is in the AddressBook will be removed from it, and if such a
|
|
ContactCard does not belong to any other AddressBook, it will be
|
|
destroyed.
|
|
|
|
*onSuccessSetIsDefault*: Id|null
|
|
If an id is given, and all creates, updates, and destroys (if any)
|
|
succeed without error, the server will try to set this AddressBook
|
|
as the default. (For references to AddressBook creations, this is
|
|
equivalent to a creation-reference, so the id will be the creation
|
|
id prefixed with a "#".)
|
|
|
|
If the id is not found or if the change is not permitted by the
|
|
server for policy reasons, it MUST be ignored and the current default
|
|
AddressBook (if any) will remain as such. No error is returned to
|
|
the client in this case.
|
|
|
|
As per Section 5.3 of [RFC8620], if the default AddressBook is
|
|
successfully changed, any changed objects MUST be reported in either
|
|
the "created" or "updated" argument in the response as appropriate,
|
|
with the server-set value included.
|
|
|
|
The "shareWith" property may only be set by users that have the
|
|
"mayShare" right. When modifying the "shareWith" property, the user
|
|
cannot give a right to a Principal if the Principal did not already
|
|
have that right and the user making the change also does not have
|
|
that right. Any attempt to do so MUST be rejected with a "forbidden"
|
|
SetError.
|
|
|
|
Users can subscribe or unsubscribe to an AddressBook by setting the
|
|
"isSubscribed" property. The server MAY forbid users from
|
|
subscribing to certain AddressBooks even though they have permission
|
|
to see them, rejecting the update with a "forbidden" SetError.
|
|
|
|
The following extra SetError type is defined for "destroy":
|
|
|
|
*addressBookHasContents*: The AddressBook has at least one
|
|
ContactCard assigned to it and the "onDestroyRemoveContents"
|
|
argument was false.
|
|
|
|
3. ContactCards
|
|
|
|
A *ContactCard* object contains information about a person, company,
|
|
or other entity, or represents a group of such entities. It is a
|
|
JSContact Card object as defined in Section 2 of [RFC9553] with the
|
|
following additional properties:
|
|
|
|
*id*: Id (immutable; server-set)
|
|
The id of the ContactCard. The "id" property MAY be different to
|
|
the ContactCard's "uid" property (as defined in Section 2.1.9 of
|
|
[RFC9553]). However, there MUST NOT be more than one ContactCard
|
|
with the same uid in an Account.
|
|
|
|
*addressBookIds*: Id[Boolean]
|
|
The set of AddressBook ids that this ContactCard belongs to. A
|
|
card MUST belong to at least one AddressBook at all times (until
|
|
it is destroyed). The set is represented as an object, with each
|
|
key being an AddressBook id. The value for each key in the object
|
|
MUST be true.
|
|
|
|
For any Media object in the card (see Section 2.6.4 of [RFC9553]), a
|
|
new property is defined:
|
|
|
|
*blobId*: Id
|
|
An id for the Blob representing the binary contents of the
|
|
resource.
|
|
|
|
When returning ContactCards, any Media with a URI that uses the
|
|
"data:" URL scheme [RFC2397] SHOULD return a "blobId" property and
|
|
omit the "uri" property, as this lets clients load the (potentially
|
|
large) image file only when needed and avoids the overhead of Base64
|
|
encoding. The "mediaType" property MUST also be set. Similarly,
|
|
when creating or updating a ContactCard, clients MAY send a "blobId"
|
|
instead of the "uri" property for a Media object.
|
|
|
|
A contact card with a "kind" property equal to "group" represents a
|
|
group of contacts. Clients often present these separately from other
|
|
contact cards. The "members" property, as defined in Section 2.1.6
|
|
of [RFC9553], contains a set of uids (as defined in Section 2.1.9 of
|
|
[RFC9553]) for other contacts that are the members of this group.
|
|
Clients should consider the group to contain any ContactCard with a
|
|
matching uid from any account they have access to that has support
|
|
for the urn:ietf:params:jmap:contacts capability. Any uid that
|
|
cannot be found SHOULD be ignored but preserved. For example,
|
|
suppose a user adds contacts from a shared address book to their
|
|
private group, then temporarily loses access to this address book.
|
|
The uids cannot be resolved, so the contacts will disappear from the
|
|
group. However, if they are given permission to access the data
|
|
again, the uids will be found and the contacts will reappear.
|
|
|
|
3.1. ContactCard/get
|
|
|
|
This is a standard "/get" method as described in Section 5.1 of
|
|
[RFC8620].
|
|
|
|
3.2. ContactCard/changes
|
|
|
|
This is a standard "/changes" method as described in Section 5.2 of
|
|
[RFC8620].
|
|
|
|
3.3. ContactCard/query
|
|
|
|
This is a standard "/query" method as described in Section 5.5 of
|
|
[RFC8620].
|
|
|
|
3.3.1. Filtering
|
|
|
|
A *FilterCondition* object has the following properties, any of which
|
|
may be omitted:
|
|
|
|
*inAddressBook*: Id
|
|
An AddressBook id. A card must be in this address book to match
|
|
the condition.
|
|
|
|
*uid*: String
|
|
A card must have this string exactly as its uid (as defined in
|
|
Section 2.1.9 of [RFC9553]) to match.
|
|
|
|
*hasMember*: String
|
|
A card must have a "members" property (as defined in Section 2.1.6
|
|
of [RFC9553]) that contains this string as one of the uids in the
|
|
set to match.
|
|
|
|
*kind*: String
|
|
A card must have a "kind" property (as defined in Section 2.1.4 of
|
|
[RFC9553]) that equals this string exactly to match.
|
|
|
|
*createdBefore*: UTCDate
|
|
The "created" date-time of the ContactCard (as defined in
|
|
Section 2.1.3 of [RFC9553]) must be before this date-time to match
|
|
the condition.
|
|
|
|
*createdAfter*: UTCDate
|
|
The "created" date-time of the ContactCard (as defined in
|
|
Section 2.1.3 of [RFC9553]) must be the same or after this date-
|
|
time to match the condition.
|
|
|
|
*updatedBefore*: UTCDate
|
|
The "updated" date-time of the ContactCard (as defined in
|
|
Section 2.1.10 of [RFC9553]) must be before this date-time to
|
|
match the condition.
|
|
|
|
*updatedAfter*: UTCDate
|
|
The "updated" date-time of the ContactCard (as defined in
|
|
Section 2.1.10 of [RFC9553]) must be the same or after this date-
|
|
time to match the condition.
|
|
|
|
*text*: String
|
|
A card matches this condition if the text matches with text in the
|
|
card.
|
|
|
|
*name*: String
|
|
A card matches this condition if the value of any NameComponent in
|
|
the "name" property or the "full" property in the "name" property
|
|
of the card (as defined in Section 2.2.1.2 of [RFC9553]) matches
|
|
the value.
|
|
|
|
*name/given*: String
|
|
A card matches this condition if the value of a NameComponent with
|
|
kind "given" inside the "name" property of the card (as defined in
|
|
Section 2.2.1.2 of [RFC9553]) matches the value.
|
|
|
|
*name/surname*: String
|
|
A card matches this condition if the value of a NameComponent with
|
|
kind "surname" inside the "name" property of the card (as defined
|
|
in Section 2.2.1.2 of [RFC9553]) matches the value.
|
|
|
|
*name/surname2*: String
|
|
A card matches this condition if the value of a NameComponent with
|
|
kind "surname2" inside the "name" property of the card (as defined
|
|
in Section 2.2.1.2 of [RFC9553]) matches the value.
|
|
|
|
*nickname*: String
|
|
A card matches this condition if the "name" of any Nickname in the
|
|
"nicknames" property of the card (as defined in Section 2.2.2 of
|
|
[RFC9553]) matches the value.
|
|
|
|
*organization*: String
|
|
A card matches this condition if the "name" of any Organization in
|
|
the "organizations" property of the card (as defined in
|
|
Section 2.2.3 of [RFC9553]) matches the value.
|
|
|
|
*email*: String
|
|
A card matches this condition if the "address" or "label" of any
|
|
EmailAddress in the "emails" property of the card (as defined in
|
|
Section 2.3.1 of [RFC9553]) matches the value.
|
|
|
|
*phone*: String
|
|
A card matches this condition if the "number" or "label" of any
|
|
Phone in the "phones" property of the card (as defined in
|
|
Section 2.3.3 of [RFC9553]) matches the value.
|
|
|
|
*onlineService*: String
|
|
A card matches this condition if the "service", "uri", "user", or
|
|
"label" of any OnlineService in the "onlineServices" property of
|
|
the card (as defined in Section 2.3.2 of [RFC9553]) matches the
|
|
value.
|
|
|
|
*address*: String
|
|
A card matches this condition if the value of any AddressComponent
|
|
in the "addresses" property or the "full" property in the
|
|
"addresses" property of the card (as defined in Section 2.5.1 of
|
|
[RFC9553]) matches the value.
|
|
|
|
*note*: String
|
|
A card matches this condition if the "note" of any Note in the
|
|
"notes" property of the card (as defined in Section 2.8.3 of
|
|
[RFC9553]) matches the value.
|
|
|
|
If zero properties are specified on the FilterCondition, the
|
|
condition MUST always evaluate to true. If multiple properties are
|
|
specified, ALL must apply for the condition to be true (it is
|
|
equivalent to splitting the object into one-property conditions and
|
|
making them all the child of an AND filter operator).
|
|
|
|
The exact semantics for matching String fields is deliberately not
|
|
defined to allow for flexibility in indexing implementation, subject
|
|
to the following:
|
|
|
|
* Text SHOULD be matched in a case-insensitive manner.
|
|
|
|
* Text contained in either (but matched) single or double quotes
|
|
SHOULD be treated as a phrase search. That is, a match is
|
|
required for that exact sequence of words, excluding the
|
|
surrounding quotation marks. Use \", \', and \\ to match a
|
|
literal ", ', and \ respectively in a phrase.
|
|
|
|
* Outside of a phrase, whitespace SHOULD be treated as dividing
|
|
separate tokens that may be searched for separately in the
|
|
contact, but MUST all be present for the contact to match the
|
|
filter.
|
|
|
|
* Tokens MAY be matched on a whole-word basis using stemming (e.g.,
|
|
a text search for bus would match "buses", but not "business").
|
|
|
|
3.3.2. Sorting
|
|
|
|
The following values for the "property" field on the Comparator
|
|
object MUST be supported for sorting:
|
|
|
|
* "created" - The "created" date on the ContactCard.
|
|
|
|
* "updated" - The "updated" date on the ContactCard.
|
|
|
|
The following values for the "property" field on the Comparator
|
|
object SHOULD be supported for sorting:
|
|
|
|
* "name/given" - The value of the first NameComponent in the "name"
|
|
property whose "kind" is "given".
|
|
|
|
* "name/surname" - The value of the first NameComponent in the
|
|
"name" property whose "kind" is "surname".
|
|
|
|
* "name/surname2" - The value of the first NameComponent in the
|
|
"name" property whose "kind" is "surname2".
|
|
|
|
3.4. ContactCard/queryChanges
|
|
|
|
This is a standard "/queryChanges" method as described in Section 5.6
|
|
of [RFC8620].
|
|
|
|
3.5. ContactCard/set
|
|
|
|
This is a standard "/set" method as described in Section 5.3 of
|
|
[RFC8620].
|
|
|
|
To set a new photo, the file must first be uploaded using the upload
|
|
mechanism as described in Section 6.1 of [RFC8620]. This will give
|
|
the client a valid blobId, size, and type to use. The server MUST
|
|
reject attempts to set a file that is not a recognised image type as
|
|
the photo for a card.
|
|
|
|
3.6. ContactCard/copy
|
|
|
|
This is a standard "/copy" method as described in Section 5.4 of
|
|
[RFC8620].
|
|
|
|
4. Examples
|
|
|
|
For brevity, only the "methodCalls" property of the Request object
|
|
and the "methodResponses" property of the Response object is shown in
|
|
the following examples.
|
|
|
|
4.1. Fetching Initial Data
|
|
|
|
A user has authenticated and the client has fetched the JMAP Session
|
|
object. It finds a single Account with the
|
|
"urn:ietf:params:jmap:contacts" capability with id "a0x9" and wants
|
|
to fetch all the address books and contacts. It might make the
|
|
following request:
|
|
|
|
[
|
|
["AddressBook/get", {
|
|
"accountId": "a0x9"
|
|
}, "0"],
|
|
["ContactCard/get", {
|
|
"accountId": "a0x9"
|
|
}, "1"]
|
|
]
|
|
|
|
Figure 1: "methodCalls" Property of a JMAP Request
|
|
|
|
The server might respond with something like:
|
|
|
|
[
|
|
["AddressBook/get", {
|
|
"accountId": "a0x9",
|
|
"list": [{
|
|
"id": "062adcfa-105d-455c-bc60-6db68b69c3f3",
|
|
"name": "Personal",
|
|
"description": null,
|
|
"sortOrder": 0,
|
|
"isDefault": true,
|
|
"isSubscribed": true,
|
|
"shareWith": {
|
|
"3f1502e0-63fe-4335-9ff3-e739c188f5dd": {
|
|
"mayRead": true,
|
|
"mayWrite": false,
|
|
"mayShare": false,
|
|
"mayDelete": false
|
|
}
|
|
},
|
|
"myRights": {
|
|
"mayRead": true,
|
|
"mayWrite": true,
|
|
"mayShare": true,
|
|
"mayDelete": false
|
|
}
|
|
}, {
|
|
"id": "cd40089d-35f9-4fd7-980b-ba3a9f1d74fe",
|
|
"name": "Autosaved",
|
|
"description": null,
|
|
"sortOrder": 1,
|
|
"isDefault": false,
|
|
"isSubscribed": true,
|
|
"shareWith": null,
|
|
"myRights": {
|
|
"mayRead": true,
|
|
"mayWrite": true,
|
|
"mayShare": true,
|
|
"mayDelete": false
|
|
}
|
|
}],
|
|
"notFound": [],
|
|
"state": "~4144"
|
|
}, "0"],
|
|
["ContactCard/get", {
|
|
"accountId": "a0x9",
|
|
"list": [{
|
|
"id": "3",
|
|
"addressBookIds": {
|
|
"062adcfa-105d-455c-bc60-6db68b69c3f3": true
|
|
},
|
|
"name": {
|
|
"components": [
|
|
{ "kind": "given", "value": "Joe" },
|
|
{ "kind": "surname", "value": "Bloggs" }
|
|
],
|
|
"isOrdered": true
|
|
},
|
|
"emails": {
|
|
"0": {
|
|
"contexts": {
|
|
"private": true
|
|
},
|
|
"address": "joe.bloggs@example.com"
|
|
}
|
|
}
|
|
}],
|
|
"notFound": [],
|
|
"state": "ewarbckaqJ::112"
|
|
}, "1"]
|
|
]
|
|
|
|
Figure 2: "methodResponses" Property of a JMAP Response
|
|
|
|
4.2. Changing the Default Address Book
|
|
|
|
The client tries to change the default address book from "Personal"
|
|
to "Autosaved" (and makes no other change):
|
|
|
|
[
|
|
["AddressBook/set", {
|
|
"accountId": "a0x9",
|
|
"onSuccessSetIsDefault": "cd40089d-35f9-4fd7-980b-ba3a9f1d74fe"
|
|
}, "0"]
|
|
]
|
|
|
|
Figure 3: "methodCalls" Property of a JMAP Request
|
|
|
|
The server allows the change, returning the following response:
|
|
|
|
[
|
|
["AddressBook/set", {
|
|
"accountId": "a0x9",
|
|
"updated": {
|
|
"cd40089d-35f9-4fd7-980b-ba3a9f1d74fe": {
|
|
"isDefault": true
|
|
},
|
|
"062adcfa-105d-455c-bc60-6db68b69c3f3": {
|
|
"isDefault": false
|
|
},
|
|
"oldState": "~4144",
|
|
"newState": "~4148"
|
|
}
|
|
}, "0"]
|
|
]
|
|
|
|
Figure 4: "methodResponses" Property of a JMAP Response
|
|
|
|
5. Internationalisation Considerations
|
|
|
|
Experience has shown that unrestricted use of Unicode can lead to
|
|
problems such as inconsistent rendering, users reading text and
|
|
interpreting it differently than intended, and unexpected results
|
|
when copying text from one location to another. Servers MAY choose
|
|
to mitigate this by restricting the set of characters allowed in
|
|
otherwise unconstrained String fields. The FreeformClass, as
|
|
documented in Section 4.3 of [RFC8264], might be a good starting
|
|
point for this.
|
|
|
|
Attempts to set a value containing code points outside of the
|
|
permissible set can be handled in a few ways by the server. The
|
|
server could choose to strip the forbidden characters or replace them
|
|
with U+FFFD (the Unicode replacement character) and store the
|
|
resulting string. This is likely to be appropriate for non-printable
|
|
characters -- such as the "Control Codes" defined in Section 23.1
|
|
(https://www.unicode.org/versions/latest/core-spec/chapter-
|
|
23/#G20365) of [UNICODE], excluding newline (U+000A), carriage return
|
|
(U+000D), and tab (U+0009) -- that can end up in data accidentally
|
|
due to copy-and-paste issues but are invisible to the end user. JMAP
|
|
allows the server to transform data on create/update as long as any
|
|
changed properties are returned to the client in the "/set" response
|
|
so it knows what has changed, as per Section 5.3 of [RFC8620].
|
|
Alternatively, the server MAY just reject the create/update with an
|
|
"invalidProperties" SetError.
|
|
|
|
6. Security Considerations
|
|
|
|
All security considerations of JMAP [RFC8620] apply to this
|
|
specification. Additional considerations specific to the data types
|
|
and functionality introduced by this document are described in the
|
|
following subsection.
|
|
|
|
Contacts consist almost entirely of private, personally identifiable
|
|
information, and represent the social connections of users. Privacy
|
|
leaks can have real world consequences, and contact servers and
|
|
clients MUST be mindful of the need to keep all data secure.
|
|
|
|
Servers MUST enforce the Access Control Lists (ACLs) set on address
|
|
books to ensure only authorised data is shared.
|
|
|
|
7. IANA Considerations
|
|
|
|
7.1. JMAP Capability Registration for "contacts"
|
|
|
|
IANA has registered "contacts" in the "JMAP Capabilities" registry as
|
|
follows:
|
|
|
|
Capability Name: urn:ietf:params:jmap:contacts
|
|
Intended Use: common
|
|
Change Controller: IETF
|
|
Security and Privacy Considerations: this document, Section 6
|
|
Reference: this document
|
|
|
|
7.2. JMAP Data Type Registration for "AddressBook"
|
|
|
|
IANA has registered "AddressBook" in the "JMAP Data Types" registry
|
|
as follows:
|
|
|
|
Type Name: AddressBook
|
|
Can Reference Blobs: No
|
|
Can Use for State Change: Yes
|
|
Capability: urn:ietf:params:jmap:contacts
|
|
Reference: this document
|
|
|
|
7.3. JMAP Data Type Registration for "ContactCard"
|
|
|
|
IANA has registered "ContactCard" in the "JMAP Data Types" registry
|
|
as follows:
|
|
|
|
Type Name: ContactCard
|
|
Can Reference Blobs: Yes
|
|
Can Use for State Change: Yes
|
|
Capability: urn:ietf:params:jmap:contacts
|
|
Reference: this document
|
|
|
|
7.4. JMAP Error Codes Registry
|
|
|
|
The following subsection has registered a new error code in the "JMAP
|
|
Error Codes" registry, as defined in Section 9 of [RFC8620].
|
|
|
|
7.4.1. addressBookHasContents
|
|
|
|
JMAP Error Code: addressBookHasContents
|
|
Intended Use: common
|
|
Change Controller: IETF
|
|
Description: The AddressBook has at least one ContactCard assigned
|
|
to it, and the "onDestroyRemoveContents" argument was false.
|
|
Reference: This document, Section 2.3
|
|
|
|
7.5. JSContact Property Registrations
|
|
|
|
IANA has registered the following additional properties in the
|
|
"JSContact Properties" registry, as defined in Section 3 of
|
|
[RFC9553].
|
|
|
|
7.5.1. id
|
|
|
|
Property Name: id
|
|
Property Type: not applicable
|
|
Property Context: Card
|
|
Intended Usage: reserved
|
|
Since Version: 1.0
|
|
Change Controller: IETF
|
|
Reference: this document
|
|
|
|
7.5.2. addressBookIds
|
|
|
|
Property Name: addressBookIds
|
|
Property Type: not applicable
|
|
Property Context: Card
|
|
Intended Usage: reserved
|
|
Since Version: 1.0
|
|
Change Controller: IETF
|
|
Reference: this document
|
|
|
|
7.5.3. blobId
|
|
|
|
Property Name: blobId
|
|
Property Type: not applicable
|
|
Property Context: Media
|
|
Intended Usage: reserved
|
|
Since Version: 1.0
|
|
Change Controller: IETF
|
|
Reference: this document
|
|
|
|
8. References
|
|
|
|
8.1. Normative References
|
|
|
|
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
|
Requirement Levels", BCP 14, RFC 2119,
|
|
DOI 10.17487/RFC2119, March 1997,
|
|
<https://www.rfc-editor.org/info/rfc2119>.
|
|
|
|
[RFC2397] Masinter, L., "The "data" URL scheme", RFC 2397,
|
|
DOI 10.17487/RFC2397, August 1998,
|
|
<https://www.rfc-editor.org/info/rfc2397>.
|
|
|
|
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
|
|
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
|
|
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
|
|
|
|
[RFC8620] Jenkins, N. and C. Newman, "The JSON Meta Application
|
|
Protocol (JMAP)", RFC 8620, DOI 10.17487/RFC8620, July
|
|
2019, <https://www.rfc-editor.org/info/rfc8620>.
|
|
|
|
[RFC9553] Stepanek, R. and M. Loffredo, "JSContact: A JSON
|
|
Representation of Contact Data", RFC 9553,
|
|
DOI 10.17487/RFC9553, May 2024,
|
|
<https://www.rfc-editor.org/info/rfc9553>.
|
|
|
|
[RFC9670] Jenkins, N., Ed., "JSON Meta Application Protocol (JMAP)
|
|
Sharing", RFC 9670, DOI 10.17487/RFC9670, November 2024,
|
|
<https://www.rfc-editor.org/info/rfc9670>.
|
|
|
|
8.2. Informative References
|
|
|
|
[RFC8264] Saint-Andre, P. and M. Blanchet, "PRECIS Framework:
|
|
Preparation, Enforcement, and Comparison of
|
|
Internationalized Strings in Application Protocols",
|
|
RFC 8264, DOI 10.17487/RFC8264, October 2017,
|
|
<https://www.rfc-editor.org/info/rfc8264>.
|
|
|
|
[UNICODE] The Unicode Consortium, "The Unicode Standard",
|
|
<https://www.unicode.org/versions/latest/>.
|
|
|
|
Author's Address
|
|
|
|
Neil Jenkins (editor)
|
|
Fastmail
|
|
PO Box 234, Collins St West
|
|
Melbourne VIC 8007
|
|
Australia
|
|
Email: neilj@fastmailteam.com
|
|
URI: https://www.fastmail.com
|