<?xml version="1.0"encoding="utf-8"?>encoding="UTF-8"?> <!DOCTYPE rfc [ <!ENTITY nbsp " "> <!ENTITY zwsp "​"> <!ENTITY nbhy "‑"> <!ENTITY wj "⁠"> ]> <rfc version="3" ipr="trust200902" docName="draft-ietf-jmap-contacts-10" number="9610" submissionType="IETF" category="std" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclude"indexInclude="true" consensus="true">sortRefs="true" symRefs="true" tocInclude="true" consensus="true" updates="" obsoletes=""> <front> <title abbrev="JMAPContacts">JMAPContacts">JSON Meta Application Protocol (JMAP) forContacts</title><seriesInfo value="draft-ietf-jmap-contacts-10" stream="IETF" status="standard" name="Internet-Draft"></seriesInfo>Contacts</title> <seriesInfo name="RFC" value="9610"/> <author role="editor"initials="N.M."initials="N." surname="Jenkins" fullname="Neil Jenkins"> <organization>Fastmail</organization> <address> <postal> <street>PO Box 234, Collins St West</street> <city>Melbourne</city> <code>VIC 8007</code> <country>Australia</country> </postal> <email>neilj@fastmailteam.com</email> <uri>https://www.fastmail.com</uri> </address> </author> <date year="2024"month="June" day="07"></date> <area>Applications</area> <workgroup>JMAP</workgroup>month="December"/> <area>ART</area> <workgroup>jmap</workgroup> <keyword>JMAP</keyword> <keyword>JSON</keyword> <keyword>contacts</keyword> <abstract> <t>This document specifies a data model for synchronisingcontactscontact data with a server usingJMAP.</t>the JSON Meta Application Protocol (JMAP).</t> </abstract> </front> <middle> <section anchor="introduction"><name>Introduction</name><t>JMAP (<xref target="RFC8620"></xref><t>The JSON Meta ApplicationProtocol)Protocol (JMAP) <xref target="RFC8620"></xref> is a generic protocol for synchronising data, such as mail,calendarscalendars, or contacts, between a client and a server. It is optimised for mobile and webenvironments,environments and aims to provide a consistent interface to different data types.</t> <t>This specification defines a data model for synchronising contacts between a client and a server using JMAP.</t> <section anchor="notational-conventions"><name>Notational Conventions</name><t>The<t> The key words"MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY","<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>", "<bcp14>MAY</bcp14>", and"OPTIONAL""<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as described inBCP 14BCP 14 <xreftarget="RFC2119"></xref>target="RFC2119"/> <xreftarget="RFC8174"></xref>target="RFC8174"/> when, and only when, they appear in all capitals, as shownhere.</t>here. </t> <t>Type signatures,examplesexamples, and property descriptions in this document follow the conventions established in <xref target="RFC8620" section="1.1" sectionFormat="of" />. The Id, UnsignedInt, and UTCDate data types defined in Sections <xref target="RFC8620" section="1.2" sectionFormat="bare" />, <xref target="RFC8620" section="1.3" sectionFormat="bare" />, and <xref target="RFC8620" section="1.4" sectionFormat="bare" /> of <xref target="RFC8620" /> are also used in this document.</t> </section> <section anchor="terminology"><name>Terminology</name> <t>The same terminologyisused inthis document as inthe core JMAPspecification, seespecification (see <xref target="RFC8620" section="1.6"sectionFormat="comma"></xref>.</t>sectionFormat="of"></xref>) is also used in this document.</t> <t>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.</t> </section> <section anchor="data-model-overview"><name>Data Model Overview</name> <t>An Account (see <xref target="RFC8620" section="1.6.2"sectionFormat="comma"></xref>)sectionFormat="of"></xref>) with support for thecontactscontact 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,or otherentity, or a group of suchentities,entities in JSContact Card format, as defined in <xref target="RFC9553" section="2" />. Each ContactCard belongs to one or more AddressBooks.</t> <t>In servers with support for JMAP Sharing <xreftarget="I-D.ietf-jmap-sharing"target="RFC9670" />, users may see and configure sharing of contact data with others. Sharing permissions are managed per AddressBook.</t> </section> <section anchor="addition-to-the-capabilities-object"><name>Addition to the Capabilities Object</name> <t>The capabilities object is returned as part of the JMAP Session object; see <xref target="RFC8620" section="2"sectionFormat="comma"></xref>.sectionFormat="of"></xref>. This document defines one additional capability URI.</t> <section anchor="urn-ietf-params-jmap-contacts"><name>urn:ietf:params:jmap:contacts</name> <t>This represents support for the AddressBook and ContactCard data types and associated API methods. The value of this property in the JMAP Sessioncapabilities"capabilities" property is an empty object.</t> <t>The value of this property in an account'saccountCapabilities"accountCapabilities" property is an object thatMUST<bcp14>MUST</bcp14> contain the following information on server capabilities and permissions for that account:</t><ul spacing="compact"> <li><t><strong>maxAddressBooksPerCard</strong>: <tt>UnsignedInt|null</tt></t> <t>The<dl spacing="normal" newline="true"> <dt><strong>maxAddressBooksPerCard</strong>: <tt>UnsignedInt|null</tt></dt> <dd>The maximum number of AddressBooks (see <xref target="addressbooks" />) that can be assigned to a single ContactCard object (see <xref target="contactcards" />). ThisMUST<bcp14>MUST</bcp14> be an integer >= 1, or null for no limit (or rather, the limit is always the number of AddressBooks in theaccount).</t></li> <li><t><strong>mayCreateAddressBook</strong>: <tt>Boolean</tt></t> <t>Theaccount).</dd> <dt><strong>mayCreateAddressBook</strong>: <tt>Boolean</tt></dt> <dd>The user may create an AddressBook in this account if, and only if, this istrue.</t></li> </ul>true.</dd> </dl> </section> </section> </section> <section anchor="addressbooks"><name>AddressBooks</name> <t>An AddressBook is a named collection of ContactCards. All ContactCards are associated with one or moreAddressBook.</t> <t>AAddressBooks.</t> <t>An <strong>AddressBook</strong> object has the following properties:</t><ul> <li><t><strong>id</strong>:<dl spacing="normal" newline="true"> <dt><strong>id</strong>: <tt>Id</tt> (immutable;server-set)</t> <t>Theserver-set)</dt> <dd>The id of theAddressBook.</t></li> <li><t><strong>name</strong>: <tt>String</tt></t> <t>TheAddressBook.</dd> <dt><strong>name</strong>: <tt>String</tt></dt> <dd>The user-visible name of the AddressBook. ThisMUST NOT<bcp14>MUST NOT</bcp14> be the empty string andMUST NOT<bcp14>MUST NOT</bcp14> be greater than 255 octets in size when encoded asUTF-8.</t></li> <li><t><strong>description</strong>:UTF-8.</dd> <dt><strong>description</strong>: <tt>String|null</tt> (default:null)</t> <t>Annull)</dt> <dd>An optionallonger-formlong-form description of theAddressBook, to provideAddressBook that provides context in shared environments where users need more than just thename.</t></li> <li><t><strong>sortOrder</strong>:name.</dd> <dt><strong>sortOrder</strong>: <tt>UnsignedInt</tt> (default:0)</t> <t>Defines0)</dt> <dd><t>Defines the sort order of AddressBooks when presented in the client'sUI,UI so it is consistent between devices. The numberMUST<bcp14>MUST</bcp14> be an integer in the range 0 <= sortOrder <2<sup>31.</sup></t>2<sup>31</sup>.</t> <t>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 orderconvention.</t> </li> <li><t><strong>isDefault</strong>:convention.</t></dd> <dt><strong>isDefault</strong>: <tt>Boolean</tt>(server-set)</t> <t>This SHOULD(server-set)</dt> <dd>This <bcp14>SHOULD</bcp14> be true for exactly one AddressBook in anyaccount,account andMUST NOT<bcp14>MUST NOT</bcp14> 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 thisaccount,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 primaryaccount.</t> </li> <li><t><strong>isSubscribed</strong>: <tt>Boolean</tt></t> <t>Trueaccount.</dd> <dt><strong>isSubscribed</strong>: <tt>Boolean</tt></dt> <dd><t>True if the user has indicated they wish to see this AddressBook in their client. ThisSHOULD<bcp14>SHOULD</bcp14> 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.</t> <t>If false, the AddressBook and its contentsSHOULD<bcp14>SHOULD</bcp14> only be displayed when the user explicitly requestsit or toit. The UI may offerit forto the user the option of subscribing tosubscribe to.</t> </li> <li><t><strong>shareWith</strong>:it.</t> </dd> <dt><strong>shareWith</strong>: <tt>Id[AddressBookRights]|null</tt> (default:null)</t> <t>Anull)</dt> <dd>A map of the Principal id (<xreftarget="I-D.ietf-jmap-sharing"target="RFC9670" section="2" />)idto rights forprincipalsPrincipals this AddressBook is shared with. TheprincipalPrincipal to which this AddressBook belongsMUST NOT<bcp14>MUST NOT</bcp14> be in this set. This is null if the AddressBook is not shared withanyone,anyone or if the server does not support <xreftarget="I-D.ietf-jmap-sharing"target="RFC9670" />. The value may be modified only if the user has themayShare"mayShare" right. The account id for theprincipalsPrincipals may be found in the <tt>urn:ietf:params:jmap:principals:owner</tt> capability of the Account to which the AddressBookbelongs.</t> </li> <li><t><strong>myRights</strong>:belongs.</dd> <dt><strong>myRights</strong>: <tt>AddressBookRights</tt>(server-set)</t> <t>The(server-set)</dt> <dd>The set of access rights the user has in relation to thisAddressBook.</t> </li> </ul>AddressBook.</dd> </dl> <t>An <strong>AddressBookRights</strong> object has the following properties:</t><ul spacing="compact"> <li><t><strong>mayRead</strong>: <tt>Boolean</tt></t> <t>The<dl spacing="normal" newline="true"> <dt><strong>mayRead</strong>: <tt>Boolean</tt></dt> <dd>The user may fetch the ContactCards in thisAddressBook.</t></li> <li><t><strong>mayWrite</strong>: <tt>Boolean</tt></t> <t>TheAddressBook.</dd> <dt><strong>mayWrite</strong>: <tt>Boolean</tt></dt> <dd>The user may create,modifymodify, or destroy all ContactCards in this AddressBook, or move them to or from thisAddressBook.</t></li> <li><t><strong>mayShare</strong>: <tt>Boolean</tt></t> <t>TheAddressBook.</dd> <dt><strong>mayShare</strong>: <tt>Boolean</tt></dt> <dd>The user may modify the "shareWith" property for thisAddressBook.</t></li> <li><t><strong>mayDelete</strong>: <tt>Boolean</tt></t> <t>TheAddressBook.</dd> <dt><strong>mayDelete</strong>: <tt>Boolean</tt></dt> <dd>The user may delete the AddressBookitself.</t></li> </ul>itself.</dd> </dl> <section anchor="addressbook-get"><name>AddressBook/get</name> <t>This is a standard "/get" method as described in <xref target="RFC8620" section="5.1"sectionFormat="comma"></xref>.sectionFormat="of"></xref>. The<em>ids</em>"ids" argument may be<tt>null</tt>null to fetch all at once.</t> </section> <section anchor="addressbook-changes"><name>AddressBook/changes</name> <t>This is a standard "/changes" method as described in <xref target="RFC8620" section="5.2"sectionFormat="comma"></xref>.</t>sectionFormat="of"></xref>.</t> </section> <section anchor="addressbook-set"><name>AddressBook/set</name> <t>This is a standard "/set" method as described in <xref target="RFC8620" section="5.3"sectionFormat="comma"></xref>sectionFormat="of"></xref>, but with the following additional requestargument:</t> <ul> <li><t><strong>onDestroyRemoveContents</strong>:arguments:</t> <dl spacing="normal" newline="true"> <dt><strong>onDestroyRemoveContents</strong>: <tt>Boolean</tt> (default:false)</t> <t>Iffalse)</dt> <dd>If false, any attempt to destroy an AddressBook that still has a ContactCard in it will be rejected with an<tt>addressBookHasContents</tt>"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 otherAddressBookAddressBook, it will bedestroyed.</t> </li> <li><t><strong>onSuccessSetIsDefault</strong>: <tt>Id|null</tt></t> <t>Ifdestroyed.</dd> <dt><strong>onSuccessSetIsDefault</strong>: <tt>Id|null</tt></dt> <dd>If an id is given, and all creates,updatesupdates, 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"#".)</t> </li> </ul>"#".)</dd> </dl> <t>If the id is notfound,found or if the change is not permitted by the server for policy reasons, itMUST<bcp14>MUST</bcp14> be ignored and thecurrentlycurrent default AddressBook (if any) will remain as such. No error is returned to the client in this case.</t> <t>As per <xref target="RFC8620" section="5.3"sectionFormat="comma"></xref>,sectionFormat="of"></xref>, if the default AddressBook is successfully changed, any changed objectsMUST<bcp14>MUST</bcp14> be reported in either the "created" or "updated" argument in the response as appropriate, with the server-set value included.</t> <t>The "shareWith" property may only be set by users that have themayShare"mayShare" right. When modifying theshareWith"shareWith" property, the user cannot give a right to aprincipalPrincipal if theprincipalPrincipal did not already have that right and the user making the change also does not have that right. Any attempt to do soMUST<bcp14>MUST</bcp14> be rejected with a<tt>forbidden</tt>"forbidden" SetError.</t> <t>Users can subscribe or unsubscribe to an AddressBook by setting the "isSubscribed" property. The serverMAY<bcp14>MAY</bcp14> forbid users from subscribing to certain AddressBooks even though they have permission to see them, rejecting the update with a<tt>forbidden</tt>"forbidden" SetError.</t> <t>The following extra SetError type isdefined:</t> <t>Fordefined for "destroy":</t><ul spacing="compact"> <li><strong>addressBookHasContents</strong>: The<dl spacing="normal"> <dt><strong>addressBookHasContents</strong>:</dt> <dd>The AddressBook has at least one ContactCard assigned toit,it and the "onDestroyRemoveContents" argument wasfalse.</li> </ul>false.</dd> </dl> </section> </section> <section anchor="contactcards"><name>ContactCards</name> <t>A <strong>ContactCard</strong> object contains information about a person, company, or other entity, or represents a group of such entities. It is a JSContact Cardobject,object as defined in <xref target="RFC9553" section="2"/>,/> with the following additional properties:</t><ul spacing="compact"> <li><t><strong>id</strong>:<dl spacing="normal" newline="true"> <dt><strong>id</strong>: <tt>Id</tt> (immutable;server-set)</t> <t>Theserver-set)</dt> <dd>The id of the ContactCard. Theid"id" propertyMAY<bcp14>MAY</bcp14> be different to the ContactCard's "uid" property (as defined in <xref target="RFC9553" section="2.1.9" />).HoweverHowever, thereMUST NOT<bcp14>MUST NOT</bcp14> be more than one ContactCard with the same"uid"uid in anAccount.</t></li> <li><t><strong>addressBookIds</strong>: <tt>Id[Boolean]</tt></t> <t>TheAccount.</dd> <dt><strong>addressBookIds</strong>: <tt>Id[Boolean]</tt></dt> <dd>The set of AddressBook ids that this ContactCard belongs to. A cardMUST<bcp14>MUST</bcp14> 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 objectMUST<bcp14>MUST</bcp14> betrue.</t></li> </ul>true.</dd> </dl> <t>For any Media object in the card (see <xref target="RFC9553" section="2.6.4" />), a new property is defined:</t><ul spacing="compact"> <li><t><strong>blobId</strong>: <tt>Id</tt></t> <t>An<dl spacing="normal" newline="true"> <dt><strong>blobId</strong>: <tt>Id</tt></dt> <dd>An id for the Blob representing the binary contents of theresource.</t></li> </ul>resource.</dd> </dl> <t>When returning ContactCards, any Media with a URI that uses the<tt>data</tt>"data:" URL scheme(<xref<xref target="RFC2397"/>) SHOULD/> <bcp14>SHOULD</bcp14> return a<tt>blobId</tt>"blobId" property and omit the<tt>uri</tt>"uri" property, as this lets clients load the (potentially large) image file only whenneeded,needed and avoids the overhead of Base64 encoding. The "mediaType" propertyMUST<bcp14>MUST</bcp14> also be set. Similarly, when creating or updating a ContactCard, clientsMAY<bcp14>MAY</bcp14> send a<tt>blobId</tt>"blobId" instead of the<tt>uri</tt>"uri" property for a Media object.</t> <t>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 <xref target="RFC9553" section="2.1.6" />, contains a set ofUIDsuids (as defined in <xref target="RFC9553" section="2.1.9" />) for other contacts that are the members of this group. Clients should consider the group to contain any ContactCard with a matchingUID,uid from any account they have access towiththat has support for the <tt>urn:ietf:params:jmap:contacts</tt> capability.UIDsAny uid that cannot be foundSHOULD<bcp14>SHOULD</bcp14> 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. TheUIDsuids cannot beresolvedresolved, so the contacts will disappear from the group. However, if they are given permission to access the dataagainagain, theUIDsuids will be found and the contacts will reappear.</t> <section anchor="contactcard-get"><name>ContactCard/get</name> <t>This is a standard "/get" method as described in <xref target="RFC8620" section="5.1"sectionFormat="comma"></xref>.</t>sectionFormat="of"></xref>.</t> </section> <section anchor="contactcard-changes"><name>ContactCard/changes</name> <t>This is a standard "/changes" method as described in <xref target="RFC8620" section="5.2"sectionFormat="comma"></xref>.</t>sectionFormat="of"></xref>.</t> </section> <section anchor="contactcard-query"><name>ContactCard/query</name> <t>This is a standard "/query" method as described in <xref target="RFC8620" section="5.5"sectionFormat="comma"></xref>.</t>sectionFormat="of"></xref>.</t> <section anchor="filtering"><name>Filtering</name> <t>A <strong>FilterCondition</strong> object has the following properties, any of which may be omitted:</t><ul spacing="compact"> <li><t><strong>inAddressBook</strong>: <tt>Id</tt></t> <t>An<dl spacing="normal" newline="true"> <dt><strong>inAddressBook</strong>: <tt>Id</tt></dt> <dd>An AddressBook id. A card must be in this address book to match thecondition.</t></li> <li><t><strong>uid</strong>: <tt>String</tt></t> <t>Acondition.</dd> <dt><strong>uid</strong>: <tt>String</tt></dt> <dd>A card must have this string exactly as its uid (as defined in <xref target="RFC9553" section="2.1.9" />) tomatch.</t></li> <li><t><strong>hasMember</strong>: <tt>String</tt></t> <t>Amatch.</dd> <dt><strong>hasMember</strong>: <tt>String</tt></dt> <dd>A card must have a "members" property (as defined in <xref target="RFC9553" section="2.1.6" />) that contains this string as one of the uids in the set tomatch.</t></li> <li><t><strong>kind</strong>: <tt>String</tt></t> <t>Amatch.</dd> <dt><strong>kind</strong>: <tt>String</tt></dt> <dd>A card must have akind"kind" property (as defined in <xref target="RFC9553" section="2.1.4" />) that equals this string exactly tomatch.</t></li> <li><t><strong>createdBefore</strong>: <tt>UTCDate</tt></t> <t>Thematch.</dd> <dt><strong>createdBefore</strong>: <tt>UTCDate</tt></dt> <dd>The "created" date-time of the ContactCard (as defined in <xref target="RFC9553" section="2.1.3" />) must be before this date-time to match thecondition.</t></li> <li><t><strong>createdAfter</strong>: <tt>UTCDate</tt></t> <t>Thecondition.</dd> <dt><strong>createdAfter</strong>: <tt>UTCDate</tt></dt> <dd>The "created" date-time of the ContactCard (as defined in <xref target="RFC9553" section="2.1.3" />) must be the same or after this date-time to match thecondition.</t></li> <li><t><strong>updatedBefore</strong>: <tt>UTCDate</tt></t> <t>Thecondition.</dd> <dt><strong>updatedBefore</strong>: <tt>UTCDate</tt></dt> <dd>The "updated" date-time of the ContactCard (as defined in <xref target="RFC9553" section="2.1.10" />) must be before this date-time to match thecondition.</t></li> <li><t><strong>updatedAfter</strong>: <tt>UTCDate</tt></t> <t>Thecondition.</dd> <dt><strong>updatedAfter</strong>: <tt>UTCDate</tt></dt> <dd>The "updated" date-time of the ContactCard (as defined in <xref target="RFC9553" section="2.1.10" />) must be the same or after this date-time to match thecondition.</t></li> <li><t><strong>text</strong>: <tt>String</tt></t> <t>Acondition.</dd> <dt><strong>text</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the text matches with text in thecard.</t></li> <li><t><strong>name</strong>: <tt>String</tt></t> <t>Acard.</dd> <dt><strong>name</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the value of any NameComponent in the "name"property,property or the "full" property in the "name" property of thecard,card (as defined in <xref target="RFC9553" section="2.2.1.2" />) matches thevalue.</t></li> <li><t><strong>name/given</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>name/given</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the value of a NameComponent with kind "given" inside the "name" property of the card (as defined in <xref target="RFC9553" section="2.2.1.2" />) matches thevalue.</t></li> <li><t><strong>name/surname</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>name/surname</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the value of a NameComponent with kind "surname" inside the "name" property of the card (as defined in <xref target="RFC9553" section="2.2.1.2" />) matches thevalue.</t></li> <li><t><strong>name/surname2</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>name/surname2</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the value of a NameComponent with kind "surname2" inside the "name" property of the card (as defined in <xref target="RFC9553" section="2.2.1.2" />) matches thevalue.</t></li> <li><t><strong>nickname</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>nickname</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the "name" of any Nickname in the "nicknames" property of the card (as defined in <xref target="RFC9553" section="2.2.2" />) matches thevalue.</t></li> <li><t><strong>organization</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>organization</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the "name" of any Organization in the "organizations" property of the card (as defined in <xref target="RFC9553" section="2.2.3" />) matches thevalue.</t></li> <li><t><strong>email</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>email</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the "address" or "label" of any EmailAddress in the "emails" property of the card (as defined in <xref target="RFC9553" section="2.3.1" />) matches thevalue.</t></li> <li><t><strong>phone</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>phone</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the "number" or "label" of any Phone in the "phones" property of the card (as defined in <xref target="RFC9553" section="2.3.3" />) matches thevalue.</t></li> <li><t><strong>onlineService</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>onlineService</strong>: <tt>String</tt></dt> <dd>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 <xref target="RFC9553" section="2.3.2" />) matches thevalue.</t></li> <li><t><strong>address</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>address</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the value of any AddressComponent in the "addresses"property,property or the "full" property in the "addresses" property of thecard,card (as defined in <xref target="RFC9553" section="2.5.1" />) matches thevalue.</t></li> <li><t><strong>note</strong>: <tt>String</tt></t> <t>Avalue.</dd> <dt><strong>note</strong>: <tt>String</tt></dt> <dd>A card matches this condition if the "note" of any Note in the "notes" property of the card (as defined in <xref target="RFC9553" section="2.8.3" />) matches thevalue.</t></li> </ul>value.</dd> </dl> <t>If zero properties are specified on the FilterCondition, the conditionMUST<bcp14>MUST</bcp14> always evaluate to<tt>true</tt>.true. If multiple properties are specified, ALL must apply for the condition to be<tt>true</tt>true (it is equivalent to splitting the object into one-property conditions and making them all the child of an AND filter operator).</t> <t>The exact semantics for matching <tt>String</tt> fields is<strong>deliberatelydeliberately notdefined</strong>defined to allow for flexibility in indexing implementation, subject to the following:</t> <ulspacing="compact">spacing="normal"> <li>TextSHOULD<bcp14>SHOULD</bcp14> be matched in a case-insensitive manner.</li> <li>Text contained in either (but matched) single or double quotesSHOULD<bcp14>SHOULD</bcp14> be treated as a<strong>phrase search</strong>, that isphrase search. That is, a match is required for that exact sequence of words, excluding the surrounding quotation marks. Use <tt>\"</tt>,<tt>\'</tt><tt>\'</tt>, and <tt>\\</tt> to match a literal <tt>"</tt>,<tt>'</tt><tt>'</tt>, and <tt>\</tt> respectively in a phrase.</li> <li>Outside of a phrase,white-space SHOULDwhitespace <bcp14>SHOULD</bcp14> be treated as dividing separate tokens that may be searched for separately in the contact, butMUST<bcp14>MUST</bcp14> all be present for the contact to match the filter.</li> <li>TokensMAY<bcp14>MAY</bcp14> be matched on a whole-word basis using stemming(so for example(e.g., a text search for <tt>bus</tt> would match"buses""buses", but not "business").</li> </ul> </section> <section anchor="sorting"><name>Sorting</name> <t>The followingvaluevalues for the "property" field on the Comparator objectMUST<bcp14>MUST</bcp14> be supported for sorting:</t> <ulspacing="compact">spacing="normal"> <li>"created" - The "created" date on the ContactCard.</li> <li>"updated" - The "updated" date on the ContactCard.</li> </ul> <t>The following values for the "property" field on the Comparator objectSHOULD<bcp14>SHOULD</bcp14> be supported for sorting:</t> <ulspacing="compact">spacing="normal"> <li>"name/given" - The value of the first NameComponent in the "name" property whose "kind" is "given".</li> <li>"name/surname" - The value of the first NameComponent in the "name" property whose "kind" is "surname".</li> <li>"name/surname2" - The value of the first NameComponent in the "name" property whose "kind" is "surname2".</li> </ul> </section> </section> <section anchor="contactcard-querychanges"><name>ContactCard/queryChanges</name> <t>This is a standard "/queryChanges" method as described in <xref target="RFC8620" section="5.6"sectionFormat="comma"></xref>.</t>sectionFormat="of"></xref>.</t> </section> <section anchor="contactcard-set"><name>ContactCard/set</name> <t>This is a standard "/set" method as described in <xref target="RFC8620" section="5.3"sectionFormat="comma"></xref>.</t>sectionFormat="of"></xref>.</t> <t>To set a new photo, the file must first be uploaded using the upload mechanism as described in <xref target="RFC8620" section="6.1"sectionFormat="comma"></xref>.sectionFormat="of"></xref>. This will give the client a validblobId/size/typeblobId, size, and type to use. The serverMUST<bcp14>MUST</bcp14> reject attempts to set a file that is not a recognised image type as the photo for a card.</t> </section> <section anchor="contactcard-copy"><name>ContactCard/copy</name> <t>This is a standard "/copy" method as described in <xref target="RFC8620" section="5.4"sectionFormat="comma"></xref>.</t>sectionFormat="of"></xref>.</t> </section> </section> <section anchor="examples"><name>Examples</name> <t>For brevity,in the following examplesonly the "methodCalls" property of the Requestobject,object and the "methodResponses" property of the Response object isshown.</t>shown in the following examples.</t> <section anchor="fetching-initial-data"><name>Fetchinginitial data</name>Initial Data</name> <t>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,capability with id"a0x9","a0x9" and wants to fetch all the address books and contacts. It might make the following request:</t><artwork>[<figure> <name>"methodCalls" Property of a JMAP Request</name> <sourcecode type="json">[ ["AddressBook/get", { "accountId": "a0x9" }, "0"], ["ContactCard/get", { "accountId": "a0x9" }, "1"]] </artwork>]</sourcecode> </figure> <t>The server might respond with something like:</t><artwork>[<figure> <name>"methodResponses" Property of a JMAP Response</name> <sourcecode type="json">[ ["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"],["Contact/get",["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"]] </artwork>]</sourcecode> </figure> </section> <section anchor="changing-the-default-address-book"><name>Changing thedefault address book</name>Default Address Book</name> <t>The client tries to change the default address book from "Personal" to "Autosaved" (and makes no other change):</t><artwork>[<figure> <name>"methodCalls" Property of a JMAP Request</name> <sourcecode type="json">[ ["AddressBook/set", { "accountId": "a0x9", "onSuccessSetIsDefault": "cd40089d-35f9-4fd7-980b-ba3a9f1d74fe" }, "0"]] </artwork>]</sourcecode> </figure> <t>The server allows the change, returning the following response:</t><artwork>[<figure> <name>"methodResponses" Property of a JMAP Response</name> <sourcecode type="json">[ ["AddressBook/set", { "accountId": "a0x9", "updated": { "cd40089d-35f9-4fd7-980b-ba3a9f1d74fe": { "isDefault": true }, "062adcfa-105d-455c-bc60-6db68b69c3f3": { "isDefault": false}}, "oldState": "~4144", "newState": "~4148" } }, "0"]] </artwork>]</sourcecode> </figure> </section> </section> <section><name>Internationalisation Considerations</name> <t>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. ServersMAY<bcp14>MAY</bcp14> choose to mitigate this by restricting the set of characters allowed in otherwise unconstrained <tt>String</tt> fields. The FreeformClass, as documented in <xref target="RFC8264" section="4.3"sectionFormat="comma" />sectionFormat="of"/>, might be a good starting point for this.</t> <t>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 forbiddencharacters,characters or replace them with U+FFFD (the Unicode replacementcharacter),character) and store the resulting string. This is likely to be appropriate for non-printable characters—-- such as the "Control Codes" defined in <eref target="https://www.unicode.org/versions/latest/core-spec/chapter-23/#G20365">Section 23.1</eref> of <xreftarget="UNICODE"/> Section 23.1,target="UNICODE"/>, excluding newline (U+000A), carriage return (U+000D), and tab (U+0009)— which-- that can end up in data accidentally due to copy-and-pasteissues,issues but are invisible to the end user. JMAP allows the server to transform data oncreate/update,create/update as long as any changed properties are returned to the client in the<tt>/set</tt> response,"/set" response so it knows what has changed, as per <xref target="RFC8620" section="5.3"sectionFormat="comma"sectionFormat="of" />. Alternatively, the serverMAY<bcp14>MAY</bcp14> just reject the create/update with an<tt>invalidProperties</tt>"invalidProperties" SetError.</t> </section> <section anchor="security-considerations"><name>Security Considerations</name> <t>All security considerations of JMAP(<xref target="RFC8620"></xref>)<xref target="RFC8620"></xref> apply to this specification. Additional considerations specific to the data types and functionality introduced by this document are described in the following subsection.</t> <t>Contacts consist almost entirely of private, personally identifiable information, and represent the social connections of users. Privacy leaks can have real world consequences, andcontactscontact servers and clientsMUST<bcp14>MUST</bcp14> be mindful of the need to keep all data secure.</t> <t>ServersMUST<bcp14>MUST</bcp14> enforce theACLsAccess Control Lists (ACLs) set on address books to ensure only authorised data is shared.</t> </section> <section anchor="iana-considerations"><name>IANA Considerations</name> <section anchor="jmap-capability-registration-for-contacts"><name>JMAPcapability registrationCapability Registration for "contacts"</name> <t>IANAwill registerhas registered "contacts" in the "JMAP Capabilities" registry as follows:</t><t>Capability Name: <tt>urn:ietf:params:jmap:contacts</tt></t> <t>Specification document: this document</t> <t>Intended use: common</t> <t>Change Controller: IETF</t> <t>Security<dl newline="false" spacing="compact"> <dt>Capability Name:</dt><dd><tt>urn:ietf:params:jmap:contacts</tt></dd> <dt>Intended Use:</dt><dd>common</dd> <dt>Change Controller:</dt><dd>IETF</dd> <dt>Security andprivacy considerations: thisPrivacy Considerations:</dt><dd>this document, <xref target="security-considerations"/></t>/></dd> <dt>Reference:</dt><dd>this document</dd> </dl> </section> <section anchor="jmap-data-type-registration-for-addressbook"><name>JMAP Data Type Registration for "AddressBook"</name> <t>IANAwill registerhas registered "AddressBook" in the "JMAP Data Types" registry as follows:</t><t>Type Name: <tt>AddressBook</tt></t> <t>Can reference blobs: no</t> <t>Can<dl newline="false" spacing="compact"> <dt>Type Name:</dt><dd><tt>AddressBook</tt></dd> <dt>Can Reference Blobs:</dt><dd>No</dd> <dt>Can Use for StateChange: yes</t> <t>Capability: <tt>urn:ietf:params:jmap:contacts</tt></t> <t>Specification document: this document</t>Change:</dt><dd>Yes</dd> <dt>Capability:</dt><dd><tt>urn:ietf:params:jmap:contacts</tt></dd> <dt>Reference:</dt><dd>this document</dd> </dl> </section> <section anchor="jmap-data-type-registration-for-contactcard"><name>JMAP Data Type Registration for "ContactCard"</name> <t>IANAwill registerhas registered "ContactCard" in the "JMAP Data Types" registry as follows:</t><t>Type Name: <tt>ContactCard</tt></t> <t>Can reference blobs: yes</t> <t>Can<dl newline="false" spacing="compact"> <dt>Type Name:</dt><dd><tt>ContactCard</tt></dd> <dt>Can Reference Blobs:</dt><dd>Yes</dd> <dt>Can Use for StateChange: yes</t> <t>Capability: <tt>urn:ietf:params:jmap:contacts</tt></t> <t>Specification document: this document</t>Change:</dt><dd>Yes</dd> <dt>Capability:</dt><dd><tt>urn:ietf:params:jmap:contacts</tt></dd> <dt>Reference:</dt><dd>this document</dd> </dl> </section> <section anchor="jmap-error-codes-registry" title="JMAP Error Codes Registry"> <t>The following subsectionregistershas registered a new error code in the "JMAP Error Codes" registry, as defined in <xref target="RFC8620" section="9"/>. </t> <section anchor="addressbookhascontents" title="addressBookHasContents"><t>JMAP<dl newline="false" spacing="compact"> <dt>JMAP ErrorCode: addressBookHasContents</t> <t>Intended use: common</t> <t>Change controller: IETF</t> <t>Reference: This document, <xref target="addressbook-set"/></t> <t>Description: TheCode:</dt><dd>addressBookHasContents</dd> <dt>Intended Use:</dt><dd>common</dd> <dt>Change Controller:</dt><dd>IETF</dd> <dt>Description:</dt><dd>The AddressBook has at least one ContactCard assigned to it, and the "onDestroyRemoveContents" argument wasfalse.</t>false.</dd> <dt>Reference:</dt><dd>This document, <xref target="addressbook-set"/></dd> </dl> </section> </section> <section anchor="jscontact-property-registrations"> <name>JSContact Property Registrations</name> <t>IANAwill registerhas registered the following additional properties in theJSContact Properties Registry,"JSContact Properties" registry, as defined in <xref target="RFC9553" section="3"/>.</t> <section anchor="id"><name>id</name><t>Property Name: id</t> <t>Property Type: Not applicable</t> <t>Property Context: Card</t> <t>Intended Use: Reserved</t> <t>Since Version: 1.0</t> <t>Change Controller: IETF</t><dl newline="false" spacing="compact"> <dt>Property Name:</dt><dd>id</dd> <dt>Property Type:</dt><dd>not applicable</dd> <dt>Property Context:</dt><dd>Card</dd> <dt>Intended Usage:</dt><dd>reserved</dd> <dt>Since Version:</dt><dd>1.0</dd> <dt>Change Controller:</dt><dd>IETF</dd> <dt>Reference:</dt><dd>this document</dd> </dl> </section> <section anchor="addressBookIds"><name>addressBookIds</name><t>Property Name: addressBookIds</t> <t>Property Type: Not applicable</t> <t>Property Context: Card</t> <t>Intended Use: Reserved</t> <t>Since Version: 1.0</t> <t>Change Controller: IETF</t><dl newline="false" spacing="compact"> <dt>Property Name:</dt><dd>addressBookIds</dd> <dt>Property Type:</dt><dd>not applicable</dd> <dt>Property Context:</dt><dd>Card</dd> <dt>Intended Usage:</dt><dd>reserved</dd> <dt>Since Version:</dt><dd>1.0</dd> <dt>Change Controller:</dt><dd>IETF</dd> <dt>Reference:</dt><dd>this document</dd> </dl> </section> <section anchor="blobId"><name>blobId</name><t>Property Name: blobId</t> <t>Property Type: Not applicable</t> <t>Property Context: Media</t> <t>Intended Use: Reserved</t> <t>Since Version: 1.0</t> <t>Change Controller: IETF</t><dl newline="false" spacing="compact"> <dt>Property Name:</dt><dd>blobId</dd> <dt>Property Type:</dt><dd>not applicable</dd> <dt>Property Context:</dt><dd>Media</dd> <dt>Intended Usage:</dt><dd>reserved</dd> <dt>Since Version:</dt><dd>1.0</dd> <dt>Change Controller:</dt><dd>IETF</dd> <dt>Reference:</dt><dd>this document</dd> </dl> </section> </section> </section> </middle> <back> <references> <name>References</name> <references><name>Normative References</name> <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/> <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2397.xml"/> <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/> <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8620.xml"/> <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9553.xml"/> <xi:includehref="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-jmap-sharing.xml"/>href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9670.xml"/> </references> <references><name>Informative References</name> <xi:includehref="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8264.xml"/>href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8264.xml"/> <reference anchor="UNICODE"target="http://www.unicode.org/versions/latest/">target="https://www.unicode.org/versions/latest/"> <front> <title abbrev="Unicode">The Unicode Standard</title> <author><organization>The Unicode Consortium</organization><address /></author> </front><annotation>Note that this reference is to the latest version of Unicode, rather than to a specific release. It is not expected that future changes in the Unicode Standard will affect the referenced definitions.</annotation></reference> </references> </references> </back> </rfc>