git-svn-id: file:///home/ksmith/gitmigration/svn/xmpp/trunk@677 4b5297f7-1745-476d-ba37-a9c6900126ab
This commit is contained in:
Peter Saint-Andre 2007-03-19 20:31:02 +00:00
parent 157a207988
commit 51d7de59d4
1 changed files with 154 additions and 28 deletions

View File

@ -26,6 +26,13 @@
</schemaloc>
&hildjj;
&stpeter;
&remko;
<revision>
<version>1.3pre1</version>
<date>in progress, last updated 2007-03-19</date>
<initials>psa</initials>
<remark><p>Added developer-friendly introduction; specified that ext names must be stable across application versions; further clarified examples; added stream feature use case.</p></remark>
</revision>
<revision>
<version>1.2</version>
<date>2007-02-15</date>
@ -66,7 +73,7 @@
<version>0.4</version>
<date>2003-09-04</date>
<initials>jjh</initials>
<remark><p>IQ gets must be to a resource, since they are intended to go to a particular session.</p></remark>
<remark><p>IQ eets must be to a resource, since they are intended to go to a particular session.</p></remark>
</revision>
<revision>
<version>0.3</version>
@ -90,14 +97,124 @@
</revision>
</header>
<section1 topic='Introduction' anchor='intro'>
<p>It is often desirable for a Jabber/XMPP application (commonly but not necessarily a client) to take different actions depending on the capabilities of another application from which it receives presence information. Examples include:</p>
<ul>
<li>Showing a different set of icons depending on the capabilities of other clients.</li>
<li>Not sending &xep0071; content to plaintext clients such as cell phones.</li>
<li>Allowing the initiation of Voice over IP (VoIP) sessions only to clients that support VoIP.</li>
<li>Not showing a "Send a File" button if another user's client does not support &xep0096;.</li>
</ul>
<p>Some older Jabber clients send one &xep0030; and one &xep0092; request to each entity from which they received presence after login. That "disco+version flood" results in an excessive use of bandwidth and is impractical on a larger scale, particularly for users or applications with large rosters. Therefore this document proposes a more robust and scalable solution: namely, a presence-based mechanism <note>This proposal is not limited to clients, and can be used by any entity that exchanges presence with another entity, e.g., a gateway. However, this document uses the example of clients throughout.</note> for exchanging information about entity capabilities. Clients SHOULD NOT engage in the older "disco+version flood" behavior and instead SHOULD use Entity Capabilities as specified herein.</p>
<section2 topic='Motivation' anchor='intro-motivation'>
<p>It is often desirable for a Jabber/XMPP application (commonly but not necessarily a client) to take different actions depending on the capabilities of another application from which it receives presence information. Examples include:</p>
<ul>
<li>Showing a different set of icons depending on the capabilities of other clients.</li>
<li>Not sending &xep0071; content to plaintext clients such as cell phones.</li>
<li>Allowing the initiation of Voice over IP (VoIP) sessions only to clients that support VoIP.</li>
<li>Not showing a "Send a File" button if another user's client does not support &xep0096;.</li>
</ul>
<p>Some older Jabber clients send one &xep0030; and one &xep0092; request to each entity from which they received presence after login. That "disco+version flood" results in an excessive use of bandwidth and is impractical on a larger scale, particularly for users or applications with large rosters. Therefore this document proposes a more robust and scalable solution: namely, a presence-based mechanism <note>This proposal is not limited to clients, and can be used by any entity that exchanges presence with another entity, e.g., a gateway. However, this document uses the example of clients throughout.</note> for exchanging information about entity capabilities. Clients SHOULD NOT engage in the older "disco+version flood" behavior and instead SHOULD use Entity Capabilities as specified herein.</p>
</section2>
<section2 topic='How It Works' anchor='intro-how'>
<p>This section provides a friendly, non-normative introduction to the workings of entity capabilities.</p>
<p>Suppose you are a Shakespearean character named Juliet and one of your contacts, a handsome fellow named Romeo, becomes available. His client wants to publish its capabilities, and does this by adding a &lt;c/&gt; element to its presence packets. As a result, your client receives the following presence packet:</p>
<code><![CDATA[
<presence from='romeo@montague.net/home'>
<c xmlns='http://jabber.org/protocol/caps'
node='http://exodus.jabberstudio.org/caps'
ver='0.9' />
</presence>
]]></code>
<p>The 'node' attribute represents the client Romeo is using, and the 'ver' attribute represents the specific version of this client. At this point, your client has no idea what the capabilities are of someone with a client string 'http://exodus.jabberstudio.org/caps' and a version string '0.9'. Your client therefore sends a query to Romeo, asking what his client version can do (using service discovery):</p>
<code><![CDATA[
<iq type='get' to='romeo@montague.net/home' id='1'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#0.9'/>
</iq>
]]></code>
<p>The response is:</p>
<code><![CDATA[
<iq type='result' from='romeo@montague.net/home' id='1'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#0.9'>
<identity category='client' type='pc'/>
<feature var='http://jabber.org/protocol/disco#info'/>
<feature var='http://jabber.org/protocol/disco#items'/>
<feature var='http://jabber.org/protocol/feature-neg'/>
<feature var='http://jabber.org/protocol/muc'/>
</query>
</iq>
]]></code>
<p>At this point, your client knows that anyone using the client string 'http://exodus.jabberstudio.org/caps' and the version string '0.9' has a client that can do MUC. Your client remembers this information, such that it does not need to explicitly query the capabilities of a contact with the exact same client and version string. For example, Benvolio may send you the following presence:</p>
<code><![CDATA[
<presence from='benvolio@capulet.com/230193'>
<c xmlns='http://jabber.org/protocol/caps'
node='http://exodus.jabberstudio.org/caps'
ver='0.9' />
</presence>
]]></code>
<p>Now your client automatically knows that Benvolio can do MUC, without needing to ask him explicitly via service discovery.</p>
<p>On the other hand, for a person with the following presence ...</p>
<code><![CDATA[
<presence from='bob@initech.com/Home'>
<c xmlns='http://jabber.org/protocol/caps'
node='http://exodus.jabberstudio.org/caps'
ver='0.10' />
</presence>
]]></code>
<p>... or the following presence ...</p>
<code><![CDATA[
<presence from='bard@shakespeare.lit/globe'>
<c xmlns='http://jabber.org/protocol/caps'
node='http://psi-im.org/caps'
ver='0.9' />
</presence>
]]></code>
<p>... you have no information about what this contact's client is capable of (as he is using a different client/version), and you therefore need to query his capabilities explicitly again.</p>
<p>So that is how the 'node' and 'ver' attributes work. But most clients also allow certain features to be turned on or off at run time. For example, clients can allow users to turn off the exchange of chat states (for privacy reasons). For such a client, this means that it cannot just advertise its capabilities as was done before, since this capabilitiy depends on whether or not the user has turned the option on or off (and capabilities for the same client/version are assumed to be identical for all contacts). Such capabilities are advertised using "extensions". For example:</p>
<code><![CDATA[
<presence from='benvolio@capulet.com/230193'>
<c xmlns='http://jabber.org/protocol/caps'
node='http://exodus.jabberstudio.org/caps'
ver='0.9'
ext='csn' />
</presence>
]]></code>
<p>In this case, Benvolio is using an extension that his client calls 'csn'. Again, your client has no clue what 'csn' means, as it is a name arbitrarily chosen by Benvolio's client. It therefore queries Benvolio's client to find out what a client 'http://exodus.jabberstudio.org/caps' means when it says it supports 'csn'.</p>
<code><![CDATA[
<iq type='get' to='romeo@montague.net/home' id='2'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#csn'/>
</iq>
]]></code>
<p>The response is:</p>
<code><![CDATA[
<iq type='result' from='romeo@montague.net/home' id='2'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#0.9'>
<feature var='http://jabber.org/protocol/chatstates'/>
</query>
</iq>
]]></code>
<p>Now, your client knows that Benvolio's client (and anyone using the same client node as Benvolio and extension 'csn') supports chat state notifications. On the other hand, suppose Bill logs on:</p>
<code><![CDATA[
<presence from='bard@shakespeare.lit/globe'>
<c xmlns='http://jabber.org/protocol/caps'
node='http://psi-im.org/caps'
ver='0.9'
ext='csn' />
</presence>
]]></code>
<p>Although you know that 'csn' meant "chat state notifications" for Benvolio (and for everyone using the same client as Benvolio), you do not know what this means for Bill, because he is using a different client. So you query his client:</p>
<code><![CDATA[
<iq type='get' to='bard@shakespeare.lit/globe' id='3'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://psi-im.org/caps#csn'/>
</iq>
]]></code>
<p>and the response ...</p>
<code><![CDATA[
<iq type='result' from='bard@shakespeare.lit/globe' id='3'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://psi-im.org/caps#0.9'>
<feature var='urn:xmpp:ssn'/>
</query>
</iq>
]]></code>
<p>... reveals that this client uses 'csn' to denote the capability of "stanza session negotiation" (formerly known as "chat session negotiation").</p>
</section2>
</section1>
<section1 topic='Assumptions' anchor='assumptions'>
<p>This document makes several assumptions:</p>
@ -132,8 +249,8 @@
<p>Each time a conformant client sends presence, it annotates that presence with an element that specifies the client type, the version of that client, and which feature bundles (if any) are currently enabled. Unless the server optimizations shown later are being used, the client MUST send this with every presence change (except for unavailable presence) to enable existing servers to remember the last presence for use in responding to probes. The client MUST send the <strong>'node'</strong> and <strong>'ver'</strong> attributes.</p>
<p>In addition, the client MAY send an <strong>'ext'</strong> attribute (short for "extensions") if it has one or more feature bundles to advertise. A feature bundle is any non-standard addition or extension to the core application, such as a client plugin. If more than one feature bundle is advertised, the value of the <strong>'ext'</strong> attribute MUST be a space-separated list of bundle names.
<note>Each extension name MUST be of type NMTOKEN, where multiple extension names are separated by the white space character #x20, resulting in a tokenized attribute type of NMTOKENS (see Section 3.3.1 of &w3xml;).</note>
The client MUST NOT send an <strong>'ext'</strong> attribute if there are no interesting non-core features enabled. The names of the feature bundles MUST NOT be used for semantic purposes: they are merely identifiers that will be used in other use cases. If bundles are added or substracted during an entity's session (e.g., a user plugs in a video camera), the entity SHOULD update the value of the 'ext' attribute to reflect the changed capabilities and send a new presence broadcast. If a feature bundle itself changes in any way (e.g., a user installs an updated version of a client plugin), the application MUST change the bundle name and SHOULD send a new presence broadcast.</p>
<p>The values of the <strong>'node'</strong>, <strong>'ver'</strong>, and <strong>'ext'</strong> attributes MUST NOT contain the '#' character, since that character is used as a separator in the <link url="#discover">Discovering Capabilities</link> use case.</p>
The client MUST NOT send an <strong>'ext'</strong> attribute if there are no interesting non-core features enabled. The names of the feature bundles MUST NOT be used for semantic purposes: they are merely opaque identifiers that will be used in other use cases. However, a client MUST ensure that the same 'ext' value refers to the same feature bundle across client versions (i.e., different values of the 'ver' attribute). If bundles are added or substracted during an entity's session (e.g., a user plugs in a video camera), the entity SHOULD update the value of the 'ext' attribute to reflect the changed capabilities and send a new presence broadcast. If a feature bundle itself changes in any way (e.g., a user installs an updated version of a client plugin), the application MUST change the bundle name and SHOULD send a new presence broadcast.</p>
<p>Note: The values of the <strong>'node'</strong>, <strong>'ver'</strong>, and <strong>'ext'</strong> attributes MUST NOT contain the '#' character, since that character is used as a separator in the <link url="#discover">Discovering Capabilities</link> use case.</p>
<example caption='Annotated presence sent'><![CDATA[
<presence>
@ -141,15 +258,15 @@
node='http://exodus.jabberstudio.org/caps'
ver='0.9'/>
</presence>
]]></example>
]]></example>
<example caption='Annotated presence sent, with feature extensions'><![CDATA[
<presence>
<c xmlns='http://jabber.org/protocol/caps'
node='http://exodus.jabberstudio.org/caps'
ver='0.9'
ext='ftrans xhtml'/>
ext='93j 1g'/>
</presence>
]]></example>
]]></example>
</section2>
<section2 topic="Discovering Capabilities" anchor='discover'>
@ -158,7 +275,7 @@
<p>The <strong>disco#info</strong> request is sent to a JID + node combination that consists of the chosen <strong>&lt;user@host/resource&gt;</strong> JID and a service discovery <strong>node</strong> that is constructed as follows: concatenate (1) the value of the caps <strong>'node'</strong> attribute, (2) the "#" character, and (3) the version number specified in the caps <strong>'ver'</strong> attribute.</p>
<example caption='Disco#info request for client#version'><![CDATA[
<iq type='get' from='bard@shakespeare.lit/globe' to='randomuser1@capulet.com/resource' iq='123'>
<iq type='get' from='bard@shakespeare.lit/globe' to='randomuser1@capulet.com/resource' id='123'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#0.9'/>
</iq>
@ -167,7 +284,7 @@
<p>The random user then returns all of the capabilities supported by the base installation of the application without plugins or other add-ons:</p>
<example caption='Disco#info response for client#version'><![CDATA[
<iq type='result' from='randomuser1@capulet.com/resource' to='bard@shakespeare.lit/globe' iq='123'>
<iq type='result' from='randomuser1@capulet.com/resource' to='bard@shakespeare.lit/globe' id='123'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#0.9'>
<identity category='client' type='pc'/>
@ -184,14 +301,14 @@
<example caption='Disco#info request for client#extension'><![CDATA[
<iq type='get' from='bard@shakespeare.lit/globe' to='randomuser3@capulet.com/resource' id='234'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#ftrans'/>
node='http://exodus.jabberstudio.org/caps#93j'/>
</iq>
]]></example>
<example caption='Disco#info response for client#extension'><![CDATA[
<iq type='result' from='randomuser3@capulet.com/resource' to='bard@shakespeare.lit/globe' id='234'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#ftrans'>
node='http://exodus.jabberstudio.org/caps#93j'>
<identity category='client' type='pc'/>
<feature var='http://jabber.org/protocol/bytestreams'/>
<feature var='http://jabber.org/protocol/si'/>
@ -203,14 +320,14 @@
<example caption='Disco#info request for client#extension'><![CDATA[
<iq type='get' from='bard@shakespeare.lit/globe' to='randomuser4@capulet.com/resource' id='345'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#xhtml'/>
node='http://exodus.jabberstudio.org/caps#1g'/>
</iq>
]]></example>
<example caption='Disco#info response for client#extension'><![CDATA[
<iq type='result' from='randomuser4@capulet.com/resource' to='bard@shakespeare.lit/globe' id='345'>
<query xmlns='http://jabber.org/protocol/disco#info'
node='http://exodus.jabberstudio.org/caps#xhtml'>
node='http://exodus.jabberstudio.org/caps#1g'>
<identity category='client' type='pc'/>
<feature var='http://jabber.org/protocol/xhtml-im'/>
</query>
@ -220,7 +337,7 @@
<p>Note: The set of features that a given entity advertises in response to a "client#version" request and all "client#extension" requests MUST be equivalent to the response it gives to a <strong>disco#info</strong> request with no 'node' attribute:</p>
<example caption='Generic disco#info response'><![CDATA[
<iq type='result' from='randomuser2@capulet.com/resource' to='bard@shakespeare.lit/globe' iq='456'>
<iq type='result' from='randomuser2@capulet.com/resource' to='bard@shakespeare.lit/globe' id='456'>
<query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='client' type='pc'/>
<feature var='http://jabber.org/protocol/disco#info'/>
@ -239,8 +356,7 @@
</section2>
<section2 topic='Sending Messages to Unsubscribed Entities' anchor='sendmsg'>
<p>If an application sends message to an entity from which it has not received presence, it MAY choose to append a capabilities annotation to <em>only</em> the first message sent to that entity within a particular conversation thread or "session". The application MUST NOT append a capabilities annotation to later messages unless its capabilities have changed (e.g., the value of the 'ext' has changed as described above) and MUST NOT send the annotation to entities from which it has received presence. Also, an application MUST NOT send the capabilities annotation to entities which are in a user's roster (or equivalent entity store, as in a gateway) with subscription='both' or subscription='to' (since presence would have been received from these entities if they were online).</p>
<p>If an application sends message to an entity from which it has not received presence, it MAY choose to append a capabilities annotation to <em>only</em> the first message sent to that entity within a particular conversation thread or "session" (alternatively, two users who normally do not share presence can send directed presence, either without prompting or as negotiated via &xep0155;). The application MUST NOT append a capabilities annotation to later messages unless its capabilities have changed (e.g., the value of the 'ext' has changed as described above) and MUST NOT send the annotation to entities from which it has received presence. Also, an application MUST NOT send the capabilities annotation to entities which are in a user's roster (or equivalent entity store, as in a gateway) with subscription='both' or subscription='to' (since presence would have been received from these entities if they were online).</p>
<example caption='Message including capabilities'><![CDATA[
<message to='romeo@example.net'
from='juliet@example.com/balcony'>
@ -249,10 +365,9 @@
<c xmlns='http://jabber.org/protocol/caps'
node='http://exodus.jabberstudio.org/caps'
ver='0.9'
ext='xhtml'/>
ext='1g'/>
</message>
]]></example>
]]></example>
<p>If the recipient responds to one of these annotated messages, the first message back in the other direction SHOULD be annotated with capabilities.</p>
<example caption='Response message including capabilities'><![CDATA[
<message from='romeo@example.net/orchard'
@ -263,9 +378,19 @@
node='http://exodus.jabberstudio.org/caps'
ver='0.9'/>
</message>
]]></example>
]]></example>
<p>Alternatively, unsubscribed entities MAY send directed presence to each other, for which the same rules apply as listed above for messages.</p>
</section2>
<section2 topic='Stream Feature' anchor='stream'>
<p>A server MAY include its own entity capabilities in a stream feature element so that connecting clients and peer servers do not need to send service discovery requests each time they connect:</p>
<example caption='Stream feature element including capabilities'><![CDATA[
<stream:features>
<c xmlns='http://jabber.org/protocol/caps'
node='http://jabberd.org/entity'
ver='1.6.1'/>
</stream:features>
]]></example>
</section2>
</section1>
<section1 topic='Server Optimizations' anchor='optimizations'>
@ -289,7 +414,7 @@
...
</query>
</iq>
]]></example>
]]></example>
</section1>
<section1 topic='Implementation Notes' anchor='impl'>
@ -308,6 +433,7 @@
<section1 topic='IANA Considerations' anchor='iana'>
<p>This document requires no interaction with &IANA;. </p>
</section1>
<section1 topic='XMPP Registrar Considerations' anchor='registrar'>
<p>The &REGISTRAR; includes 'http://jabber.org/protocol/caps' in its registries of protocol namespaces and service discovery features.</p>
<p>If it is useful or interesting, the Registrar may also provide registration of the URIs to be used in the <strong>'node'</strong> attribute, but since these URIs can be scoped according to well-defined existing rules, this is not necessary.</p>