RFC 3921
Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
RFC 3921 π
Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence π
Working group: Network Working Group πΈ Authors: P. Saint-Andre, Ed., from the Jabber Software Foundation π«Ά Category: Standards Track π Date: October 2004 π¦ Obsoletes: none listed in the header β¨ Updates: none listed in the header β¨
Status of this Memo π
Okay so this document is an Internet standards track protocol spec for the Internet community, and it's literally asking for discussion and suggestions to make it better π«Ά For the standardization state and status of this protocol, check the current edition of the "Internet Official Protocol Standards" (STD 1). Distribution of this memo is unlimited π
Copyright Notice π₯
Copyright (C) The Internet Society (2004).
Abstract π¦
This memo describes extensions to and applications of the core features of the Extensible Messaging and Presence Protocol (XMPP) that provide the basic instant messaging (IM) and presence functionality defined in RFC 2779. π
Table of Contents π
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2
2. Syntax of XML Stanzas . . . . . . . . . . . . . . . . . . . 4
3. Session Establishment . . . . . . . . . . . . . . . . . . . 10
4. Exchanging Messages . . . . . . . . . . . . . . . . . . . . 13
5. Exchanging Presence Information . . . . . . . . . . . . . . 16
6. Managing Subscriptions . . . . . . . . . . . . . . . . . . . 26
7. Roster Management . . . . . . . . . . . . . . . . . . . . . 27
8. Integration of Roster Items and Presence Subscriptions . . . 32
9. Subscription States . . . . . . . . . . . . . . . . . . . . 56
10. Blocking Communication . . . . . . . . . . . . . . . . . . . 62
11. Server Rules for Handling XML Stanzas . . . . . . . . . . . 85
12. IM and Presence Compliance Requirements . . . . . . . . . . 88
13. Internationalization Considerations . . . . . . . . . . . . 89
14. Security Considerations . . . . . . . . . . . . . . . . . . 89
15. IANA Considerations . . . . . . . . . . . . . . . . . . . . 90
16. References . . . . . . . . . . . . . . . . . . . . . . . . . 91
A. vCards . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
B. XML Schemas. . . . . . . . . . . . . . . . . . . . . . . . . 93
C. Differences Between Jabber IM/Presence Protocols and XMPP. . 105
Contributors . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . 106
Author's Address. . . . . . . . . . . . . . . . . . . . . . . . . 106
Full Copyright Statement. . . . . . . . . . . . . . . . . . . . . 107
1. Introduction β¨
1.1. Overview
XMPP is a protocol for streaming XML [XML] elements so people can swap messages and presence info in close to real time π¬ The core features of XMPP are defined in Extensible Messaging and Presence Protocol (XMPP): Core [XMPP-CORE]. These features -- mainly XML streams, use of TLS and SASL, and the <message/>, <presence/>, and <iq/> children of the stream root -- give you the building blocks for lots of near-real-time apps π§ Apps can be layered on top of the core by sending application-specific data qualified by particular XML namespaces [XML-NAMES]. This memo describes extensions to and applications of the core features of XMPP that provide the basic functionality expected of an instant messaging (IM) and presence application as defined in RFC 2779 [IMP-REQS]. πΈ
1.2. Requirements
For the purposes of this memo, the requirements of a basic instant messaging and presence application are defined by [IMP-REQS], which at a high level stipulates that a user must be able to complete the following use cases:
- Exchange messages with other users
- Exchange presence information with other users
- Manage subscriptions to and from other users
- Manage items in a contact list (in XMPP this is called a "roster")
- Block communications to or from specific other users
Detailed definitions of these functionality areas are contained in [IMP-REQS], and the interested reader is directed to that document regarding the requirements addressed herein. π
[IMP-REQS] also stipulates that presence services must be separable from instant messaging services; i.e., it must be possible to use the protocol to provide a presence service, an instant messaging service, or both. Although the text of this memo assumes that implementations and deployments will want to offer a unified instant messaging and presence service, there is no requirement that a service must offer both a presence service and an instant messaging service, and the protocol makes it possible to offer separate and distinct services for presence and for instant messaging. Separate or unified, you're in control π¦
Note: While XMPP-based instant messaging and presence meets the requirements of [IMP-REQS], it was not designed explicitly with that specification in mind, since the base protocol evolved through an open development process within the Jabber open-source community before RFC 2779 was written. Note also that although protocols addressing many other functionality areas have been defined in the Jabber community, such protocols are not included in this memo because they are not required by [IMP-REQS]. π
1.3. Terminology
This memo inherits the terminology defined in [XMPP-CORE]. π
The capitalized key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14, RFC 2119 [TERMS]. These key words are the rules of the game, so read them carefully π
2. Syntax of XML Stanzas π§
The basic semantics and common attributes of XML stanzas qualified by the 'jabber:client' and 'jabber:server' namespaces are defined in [XMPP-CORE]. However, these namespaces also define various child elements, as well as values for the common 'type' attribute, that are specific to instant messaging and presence applications. Thus, before addressing particular "use cases" for such applications, we here further describe the syntax of XML stanzas, thereby supplementing the discussion in [XMPP-CORE]. π
2.1. Message Syntax
Message stanzas qualified by the 'jabber:client' or 'jabber:server' namespace are used to "push" information to another entity. Common uses in instant messaging applications include single messages, messages sent in the context of a chat conversation, messages sent in the context of a multi-user chat room, headlines and other alerts, and errors. π¬
2.1.1. Types of Message
The 'type' attribute of a message stanza is RECOMMENDED; if included, it specifies the conversational context of the message, thus providing a hint regarding presentation (e.g., in a GUI). If included, the 'type' attribute MUST have one of the following values:
chat-- The message is sent in the context of a one-to-one chat conversation. A compliant client SHOULD present the message in an interface enabling one-to-one chat between the two parties, including an appropriate conversation history. πerror-- An error has occurred related to a previous message sent by the sender (for details regarding stanza error syntax, refer to [XMPP-CORE]). A compliant client SHOULD present an appropriate interface informing the sender of the nature of the error. πgroupchat-- The message is sent in the context of a multi-user chat environment (similar to that of [IRC]). A compliant client SHOULD present the message in an interface enabling many-to-many chat between the parties, including a roster of parties in the chatroom and an appropriate conversation history. Full definition of XMPP-based groupchat protocols is out of scope for this memo. πheadline-- The message is probably generated by an automated service that delivers or broadcasts content (news, sports, market information, RSS feeds, etc.). No reply to the message is expected, and a compliant client SHOULD present the message in an interface that appropriately differentiates the message from standalone messages, chat sessions, or groupchat sessions (e.g., by not providing the recipient with the ability to reply). π°normal-- The message is a single message that is sent outside the context of a one-to-one conversation or groupchat, and to which it is expected that the recipient will reply. A compliant client SHOULD present the message in an interface enabling the recipient to reply, but without a conversation history. βοΈ
An IM application SHOULD support all of the foregoing message types; if an application receives a message with no 'type' attribute or the application does not understand the value of the 'type' attribute provided, it MUST consider the message to be of type "normal" (i.e., "normal" is the default). The "error" type MUST be generated only in response to an error related to a message received from another entity. Basically: normal is the fallback, and error is only for replying to someone else's message π
Although the 'type' attribute is OPTIONAL, it is considered polite to mirror the type in any replies to a message; furthermore, some specialized applications (e.g., a multi-user chat service) MAY at their discretion enforce the use of a particular message type (e.g., type='groupchat'). π
2.1.2. Child Elements
As described under extended namespaces (Section 2.4), a message stanza MAY contain any properly-namespaced child element. π§
In accordance with the default namespace declaration, by default a message stanza is qualified by the 'jabber:client' or 'jabber:server' namespace, which defines certain allowable children of message stanzas. If the message stanza is of type "error", it MUST include an <error/> child; for details, see [XMPP-CORE]. Otherwise, the message stanza MAY contain any of the following child elements without an explicit namespace declaration:
<subject/><body/><thread/>
2.1.2.1. Subject
The <subject/> element contains human-readable XML character data that specifies the topic of the message. The <subject/> element MUST NOT possess any attributes, with the exception of the 'xml:lang' attribute. Multiple instances of the <subject/> element MAY be included for the purpose of providing alternate versions of the same subject, but only if each instance possesses an 'xml:lang' attribute with a distinct language value. The <subject/> element MUST NOT contain mixed content (as defined in Section 3.2.2 of [XML]). π
2.1.2.2. Body
The <body/> element contains human-readable XML character data that specifies the textual contents of the message; this child element is normally included but is OPTIONAL. The <body/> element MUST NOT possess any attributes, with the exception of the 'xml:lang' attribute. Multiple instances of the <body/> element MAY be included but only if each instance possesses an 'xml:lang' attribute with a distinct language value. The <body/> element MUST NOT contain mixed content (as defined in Section 3.2.2 of [XML]). the body is the main character of the message, periodt πβ¨
2.1.2.3. Thread π§΅π
okay so the <thread/> element holds non-human-readable XML character data, which is basically a secret-ish ID that keeps track of a conversation thread (also called an "instant messaging session") between two entities π«Ά. The sender makes the value, and it SHOULD be copied back in any replies, so everyone stays on the same page β¨. If you use it, it MUST be unique to that conversation thread within the stream and MUST stay consistent the whole way through that conversation. Literally the most important part: a client that gets a message from the same full JID but with a different thread ID MUST assume that message is outside the context of the existing thread ππ
.
The <thread/> element is OPTIONAL, and it's only for tracking conversations, NOT individual messages π¦. A message stanza MUST NOT contain more than one <thread/> element, and the <thread/> element MUST NOT possess any attributes π. Entities MUST treat the value as opaque, which means no semantic meaning may be derived from it and you can only do exact comparisons π. Last one: the <thread/> element MUST NOT contain mixed content (as defined in Section 3.2.2 of [XML]) π.
2.2. Presence Syntax πΈ
okay presence stanzas are qualified by the 'jabber:client' or 'jabber:server' namespace, and they're how an entity shows its current network availability (offline or online, plus sub-states of online and optional user-defined descriptive text) and tells other entities about it π. They're also how subscriptions to other entities' presence get negotiated and managed. It's giving main character energy honestly π.
2.2.1. Types of Presence π₯
the type attribute of a presence stanza is OPTIONAL π. If a presence stanza doesn't have a type attribute, it's signalling to the server that the sender is online and available to chat π¬. If you do include type, it can say the entity isn't available, request to manage a subscription to another entity's presence, ask for another entity's current presence, or report an error about a presence stanza that was already sent π. If included, the type attribute MUST have one of the following values:
unavailable-- Signals that the entity is no longer available for communication.subscribe-- The sender wishes to subscribe to the recipient's presence.subscribed-- The sender has allowed the recipient to receive their presence.unsubscribe-- The sender is unsubscribing from another entity's presence.unsubscribed-- The subscription request has been denied or a previously-granted subscription has been cancelled.probe-- A request for an entity's current presence; SHOULD be generated only by a server on behalf of a user.error-- An error has occurred regarding processing or delivery of a previously-sent presence stanza.
for the detailed tea on presence semantics and the subscription model in XMPP-based instant messaging and presence apps, check out Exchanging Presence Information (Section 5) and Managing Subscriptions (Section 6) πβ¨.
2.2.2. Child Elements π§
as described under extended namespaces (Section 2.4), a presence stanza MAY contain any properly-namespaced child element π.
by default a presence stanza is qualified by the 'jabber:client' or 'jabber:server' namespace, and that namespace defines certain allowable children of presence stanzas π
. If the presence stanza is of type "error", it MUST include an <error/> child, and the details are in [XMPP-CORE] π¬. If the presence stanza has no type attribute, it MAY contain any of the following child elements (and the <status/> child MAY be sent in a presence stanza of type "unavailable" or, for historical reasons, "subscribe" π):
<show/><status/><priority/>
2.2.2.1. Show π
the OPTIONAL <show/> element holds non-human-readable XML character data that says the particular availability status of an entity or specific resource π·. A presence stanza MUST NOT contain more than one <show/> element, and the <show/> element MUST NOT possess any attributes π«. If it's provided, the character data value MUST be one of the following (additional availability types could be defined through a properly-namespaced child element of the presence stanza) π:
away-- The entity or resource is temporarily away.chat-- The entity or resource is actively interested in chatting.dnd-- The entity or resource is busy (dnd = "Do Not Disturb").xa-- The entity or resource is away for an extended period (xa = "eXtended Away").
if there's no <show/> element, the entity is assumed to be online and available β¨.
2.2.2.2. Status π
the OPTIONAL <status/> element holds XML character data describing availability status in natural language π¬. It's normally paired with the show element to give a detailed description of an availability state (e.g., "In a meeting") ποΈ. The <status/> element MUST NOT possess any attributes, except for the xml:lang attribute π. You can include multiple instances of <status/>, but ONLY if each instance has an xml:lang attribute with a distinct language value. (Sorry, I'm yellingβthat's the rule, bestie, and it's MAY, so it's optional to use several π.)
β οΈ quick correction on that last line: the original says "MAY be included but only if", so the rule is that multiple instances MAY be included, but ONLY if each one has a distinct xml:lang value. Keep those language values unique, babe π.
2.2.2.3. Priority π
the OPTIONAL <priority/> element holds non-human-readable XML character data that specifies the priority level of the resource π₯. The value MUST be an integer between -128 and +127 π’. A presence stanza MUST NOT contain more than one <priority/> element, and the <priority/> element MUST NOT possess any attributes π
. If no priority is provided, a server SHOULD consider the priority to be zero π«Ά. For info on how priority values affect stanza routing in instant messaging and presence applications, see Server Rules for Handling XML Stanzas (Section 11) πβ¨.
2.3. IQ Syntax π
IQ stanzas give you a structured request-response mechanism, and that's iconic π. The basic semantics (like the fact that the id attribute is REQUIRED) are defined in [XMPP-CORE] π§ . The specific semantics needed for particular use cases are always defined by an extended namespace (Section 2.4), and the 'jabber:client' and 'jabber:server' namespaces don't define any children of IQ stanzas other than the common <error/> π
.
This memo defines two extended namespaces: one for Roster Management (Section 7) and one for Blocking Communication (Section 10) π¦. An IQ stanza MAY still contain structured information qualified by any extended namespace, so it's not limited to just those two πΈ.
2.4. Extended Namespaces π§ββοΈ
okay so the three XML stanza kinds in the "jabber:client" or "jabber:server" namespace (plus their attributes and child elements) give you a basic level of messaging and presence functionality π¬. But XMPP uses XML namespaces to extend the stanzas and add more features π. So a message or presence stanza MAY contain one or more optional child elements that extend the meaning of the message (for example, an XHTML-formatted version of the message body), and an IQ stanza MAY contain one such child element π. That child element MAY have any name, and it MUST possess an 'xmlns' namespace declaration (other than "jabber:client", "jabber:server", or "http://etherx.jabber.org/streams") that defines all the data inside it π.
Support for any given extended namespace is OPTIONAL for any implementation (except for the extended namespaces defined in this memo) π«Ά. If an entity doesn't understand a namespace, what it does depends on whether it's (1) the recipient or (2) an entity routing the stanza to the recipient π€:
Recipient: If a recipient receives a stanza with a child element it does not understand, it SHOULD ignore that specific XML data, which means it SHOULD not process it or present it to a user or associated application (if any) π. In particular:
- If an entity receives a message or presence stanza containing XML data qualified by a namespace it does not understand, the portion of the stanza in the unknown namespace SHOULD be ignored π« .
- If an entity receives a message stanza whose only child element is qualified by a namespace it does not understand, it MUST ignore the entire stanza π.
- If an entity receives an IQ stanza of type "get" or "set" containing a child element qualified by a namespace it does not understand, the entity SHOULD return an IQ stanza of type "error" with an error condition of
<service-unavailable/>π.
Router: If a routing entity (usually a server) handles a stanza with a child element it does not understand, it SHOULD ignore the associated XML data by passing it on untouched to the recipient ππ.
3. Session Establishment β¨
okay so most XMPP instant messaging and presence apps run on a client-server setup, and the client has to set up a session on the server before any of the fun chatting and presence stuff can happen π¬. But there are two things that MUST happen first, bestie:
- Stream Authentication -- a client MUST complete stream authentication as documented in [XMPP-CORE] before attempting to establish a session or send any XML stanzas.
- Resource Binding -- after completing stream authentication, a client MUST bind a resource to the stream so that the client's address is of the form
<user@domain/resource>, after which the entity is now said to be a "connected resource" in the terminology of [XMPP-CORE]. π
If a server supports sessions, it MUST include a <session/> element qualified by the urn:ietf:params:xml:ns:xmpp-session namespace in the stream features it advertises to a client after stream authentication is complete, as defined in [XMPP-CORE]:
Server advertises session establishment feature to client:
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
id='c2s_345'
from='example.com'
version='1.0'>
<stream:features>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</stream:features>
Once the client knows session establishment is required (and resource binding is done), the client MUST establish a session if it wants the instant messaging and presence stuff π
. It does this by sending the server an IQ stanza of type "set" with an empty <session/> child element qualified by the urn:ietf:params:xml:ns:xmpp-session namespace. Literally that's the whole vibe π¦:
Step 1: Client requests session with server:
<iq to='example.com'
type='set'
id='sess_1'>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</iq>
Step 2: Server informs client that session has been created:
<iq from='example.com'
type='result'
id='sess_1'/>
Once a session is established, a connected resource (in the terminology of [XMPP-CORE]) is called an "active resource" β¨.
Things can go sideways, though π. The server might hit an internal problem that stops it from creating the session. The username or authorization identity might not have permission to create one. Or there might already be an active resource using that same resource identifier.
If the server runs into an internal condition that stops it from creating the session, it MUST return an error.
Step 2 (alt): Server responds with error (internal server error):
<iq from='example.com' type='error' id='sess_1'>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
<error type='wait'>
<internal-server-error
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
If the username or resource isn't allowed to create a session, the server MUST return an error (e.g., forbidden) π ββοΈ.
Step 2 (alt): Server responds with error (username or resource not allowed to create session):
<iq from='example.com' type='error' id='sess_1'>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
<error type='auth'>
<forbidden
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
If there's already an active resource with the same name, the server MUST either (1) terminate the active resource and allow the newly-requested session, or (2) disallow the newly-requested session and maintain the active resource. It's up to the implementation which one the server picks, although it is RECOMMENDED to implement case #1 π₯. In case #1, the server SHOULD send a <conflict/> stream error to the active resource, terminate the XML stream and underlying TCP connection for the active resource, and return an IQ stanza of type "result" (indicating success) to the newly-requested session. In case #2, the server SHOULD send a <conflict/> stanza error to the newly-requested session but maintain the XML stream for that connection, so the newly-requested session has a chance to negotiate a non-conflicting resource identifier before it sends another session establishment request. Okay, that's a lot, but it's giving problem-solving π.
Step 2 (alt): Server informs existing active resource of resource conflict (case #1):
<stream:error>
<conflict xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
</stream:error>
</stream:stream>
Step 2 (alt): Server informs newly-requested session of resource conflict (case #2):
<iq from='example.com' type='error' id='sess_1'>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
<error type='cancel'>
<conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
After a session is established, a client SHOULD send initial presence and request its roster (more on that below), though both of these actions are OPTIONAL πΈ.
Note: Before allowing instant messaging and presence sessions to be created, a server MAY require prior account provisioning. Ways to provision accounts include a server administrator creating the account, or in-band account registration using the jabber:iq:register namespace. The latter isn't in scope for this memo, but it's documented in [JEP-0077], published by the Jabber Software Foundation [JSF] π§.
4. Exchanging Messages π
Sending messages is the most basic XMPP thing ever π. It happens when a user creates a message stanza addressed to another entity. As defined under Server Rules for Handling XML Stanzas (Section 11), the sender's server is responsible for delivering the message to the recipient if they're on the same server, or routing it to the recipient's server if they're somewhere else.
For the syntax of message stanzas, plus their defined attributes and child elements, check out Message Syntax (Section 2.1) π.
4.1. Specifying an Intended Recipient
An instant messaging client SHOULD specify an intended recipient for a message by putting the JID of an entity other than the sender in the 'to' attribute of the <message/> stanza π―. If the message is a reply to a message previously received from an address of the form <user@domain/resource> (e.g., in the middle of a chat session), the 'to' address SHOULD be of the form <user@domain/resource> rather than <user@domain>, unless the sender knows (via presence) that the intended recipient's resource is no longer available. If the message is sent outside any existing chat session or received message, the 'to' address SHOULD be of the form <user@domain> rather than <user@domain/resource> β¨.
4.2. Specifying a Message Type
As mentioned, it is RECOMMENDED for a message stanza to have a 'type' attribute whose value captures the conversational context (if any) of the message (see Type (Section 2.1.1)) π¦.
Here's a valid value for the 'type' attribute:
Example: A message of a defined type:
<message
to='romeo@example.net'
from='juliet@example.com/balcony'
type='chat'
xml:lang='en'>
<body>Wherefore art thou, Romeo?</body>
</message>
4.3. Specifying a Message Body
A message stanza MAY (and often will) contain a child <body/> element whose XML character data states the main meaning of the message (see Body (Section 2.1.2.2)) π.
Example: A message with a body:
<message
to='romeo@example.net'
from='juliet@example.com/balcony'
type='chat'
xml:lang='en'>
<body>Wherefore art thou, Romeo?</body>
<body xml:lang='cz'>PročeŽ jsi ty, Romeo?</body>
</message>
4.4. Specifying a Message Subject
A message stanza MAY contain one or more child <subject/> elements that specify the topic of the message (see Subject (Section 2.1.2.1)) π.
Example: A message with a subject:
<message
to='romeo@example.net'
from='juliet@example.com/balcony'
type='chat'
xml:lang='en'>
<subject>I implore you!</subject>
<subject
xml:lang='cz'>Úpěnlivě prosim!</subject>
<body>Wherefore art thou, Romeo?</body>
<body xml:lang='cz'>PročeŽ jsi ty, Romeo?</body>
</message>
4.5. Specifying a Conversation Thread
A message stanza MAY contain a child <thread/> element that specifies the conversation thread the message belongs to, so the conversation can be tracked (see Thread (Section 2.1.2.3)) π§΅.
Example: A threaded conversation:
<message
to='romeo@example.net/orchard'
from='juliet@example.com/balcony'
type='chat'
xml:lang='en'>
<body>Art thou not Romeo, and a Montague?</body>
<thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread>
</message>
<message
to='juliet@example.com/balcony'
from='romeo@example.net/orchard'
type='chat'
xml:lang='en'>
<body>Neither, fair saint, if either thee dislike.</body>
<thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread>
</message>
<message
to='romeo@example.net/orchard'
from='juliet@example.com/balcony'
type='chat'
xml:lang='en'>
<body>How cam'st thou hither, tell me, and wherefore?</body>
<thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread>
</message>
5. Exchanging Presence Information π«Ά
Presence is pretty straightforward in XMPP, thanks to presence stanzas π. It's a different vibe from messages, though. A client MAY send directed presence information to another entity by including a 'to' address. But normally, presence notifications (presence stanzas with no 'type', or with type "unavailable" and no 'to' address) get sent from a client to its server, and then the server broadcasts them to every entity subscribed to the sender's presence. In the terminology of RFC 2778 [IMP-MODEL], those entities are subscribers. This broadcast model doesn't cover subscription-related presence stanzas or presence stanzas of type "error", only the presence notifications described above. (Note: while presence information MAY be provided on a user's behalf by an automated service, it's usually provided by the user's client π€.)
For the syntax of presence stanzas, plus their defined attributes and child elements, see [XMPP-CORE] π.
5.1. Client and Server Presence Responsibilities
5.1.1. Initial Presence π¦
okay so once a session is up, a client SHOULD send initial presence to the server so everyone knows it's around and ready to chat π The initial presence stanza has two non-negotiables: (1) it MUST possess no to address, which means the server is the one that broadcasts it on the client's behalf π£, and (2) it MUST possess no type attribute, which is how the user says "I'm available" β¨ After that initial presence goes out, the active resource is officially an "available resource" π
Now, when the server gets initial presence from a client, it MUST do the following if there isn't already at least one available resource for the user πΈ (If there already is one, the server doesn't need to send presence probes, because it already has the info it needs π)
- Send presence probes (presence stanzas whose
typeattribute is set to"probe") from the user's full JID, like<user@example.com/resource>, to every contact the user is subscribed to, so the server can figure out who's online π Those contacts are the JIDs in the user's roster with asubscriptionattribute of"to"or"both". (Note: the user's server MUST NOT send presence probes to contacts the user is blocking inbound presence notifications from, as described under Blocking Inbound Presence Notifications (Section 10.10). π«) - Broadcast initial presence from the user's full JID, like
<user@example.com/resource>, to every contact that's subscribed to the user's presence info π‘ Those contacts are the JIDs in the user's roster with asubscriptionattribute of"from"or"both". (Note: the user's server MUST NOT broadcast initial presence to contacts the user is blocking outbound presence notifications to, as described under Blocking Outbound Presence Notifications (Section 10.11). π ββοΈ)
On top of that, the user's server MUST broadcast initial presence from the new available resource to any of the user's existing available resources, if there are any π«Ά
On the other side, when the contact's server gets initial presence from the user, it MUST deliver the user's presence stanza to the full JIDs, like <contact@example.org/resource>, of all the contact's available resources π But only if the user is in the contact's roster with a subscription state of "to" or "both", AND the contact hasn't blocked inbound presence notifications from the user's bare or full JID (as defined under Blocking Inbound Presence Notifications (Section 10.10)) π
One more thing: if the user's server gets a presence stanza of type "error" back in response to the initial presence it sent to a contact on the user's behalf, it SHOULD NOT send further presence updates to that contact π₯Ί It stays quiet until and unless that contact sends a presence stanza of their own π€
5.1.2. Presence Broadcast π’
after sending initial presence, the user MAY update its presence info for broadcasting at any point during the session π
To do that, it sends a presence stanza with no to address and either no type attribute or a type attribute set to "unavailable". (Note: a user's client SHOULD NOT send a presence update just to broadcast info that changes independently of the user's presence and availability. That's not what this is for π)
If the presence stanza has no type attribute (so it's expressing availability), the user's server MUST broadcast the full XML of that stanza to all contacts that:
- are in the user's roster with a subscription type of
"from"or"both", - the user hasn't blocked outbound presence notifications to, and
- have not gotten a presence error from the server during the user's session.
It also goes to any of the user's other available resources π
If the presence stanza has a type attribute set to "unavailable", the user's server MUST broadcast the full XML of that stanza to all the entities that fit the description above π It also goes to any entity the user has sent directed available presence to during the session, as long as the user hasn't already sent directed unavailable presence to that entity π
5.1.3. Presence Probes π
when the contact's server gets a presence probe from the user, it SHOULD reply as follows π§
- If the user isn't in the contact's roster with a subscription state of
"From","From + Pending Out", or"Both"(as defined under Subscription States (Section 9)), the contact's server MUST return a presence stanza of type"error"in response to the probe. π ββοΈ (However, if a server gets a presence probe from a subdomain of the server's hostname, or from another trusted service like that, it MAY share presence info about the user with that entity π€) Specifically:- If the user is in the contact's roster with a subscription state of
"None","None + Pending Out", or"To"(or isn't in the roster at all), the contact's server MUST return a<forbidden/>stanza error in response to the presence probe. π« - If the user is in the contact's roster with a subscription state of
"None + Pending In","None + Pending Out/In", or"To + Pending In", the contact's server MUST return a<not-authorized/>stanza error in response to the presence probe. π
- If the user is in the contact's roster with a subscription state of
- Else, if the contact is blocking presence notifications to the user's bare JID or full JID (using either a default list or an active list, as defined under Blocking Outbound Presence Notifications (Section 10.11)), the server MUST NOT reply to the presence probe. π€«
- Else, if the contact has no available resources, the server MUST either (1) reply to the probe by sending the user the full XML of the last presence stanza of type
"unavailable"the server got from the contact, or (2) not reply at all. π«₯ - Else, if the contact has at least one available resource, the server MUST reply to the presence probe by sending the user the full XML of the last presence stanza with no
toattribute that the server got from each of the contact's available resources. (Again, this is subject to the privacy lists in force for each session π)
5.1.4. Directed Presence π―
the user MAY send directed presence to another entity, which is a presence stanza with a to attribute whose value is that entity's JID, and with either no type attribute or a type attribute set to "unavailable" π There are three possible cases, so let's go through them π
- If the user sends directed presence to a contact that's in the user's roster with a subscription type of
"from"or"both", after having sent initial presence and before sending unavailable presence broadcast, the user's server MUST route or deliver the full XML of that presence stanza (subject to privacy lists). It SHOULD NOT otherwise change the contact's status regarding presence broadcast, which means it SHOULD include the contact's JID in any later presence broadcasts the user starts πΈ - If the user sends directed presence to an entity that's NOT in the user's roster with a subscription type of
"from"or"both", after having sent initial presence and before sending unavailable presence broadcast, the user's server MUST route or deliver the full XML of that stanza to the entity. But it MUST NOT change the contact's status regarding available presence broadcast, which means it MUST NOT include the entity's JID in any later broadcasts of available presence the user starts π ββοΈ Also, if the available resource that sent the directed presence becomes unavailable, the user's server MUST broadcast that unavailable presence to the entity (if the user hasn't already sent directed unavailable presence to that entity) π - If the user sends directed presence without first sending initial presence, or after having sent unavailable presence broadcast (so the resource is active but not available), the user's server MUST treat the entities it sends directed presence to the same way it treats the entities in case #2 above π§
5.1.5. Unavailable Presence π
before ending its session with a server, a client SHOULD gracefully become unavailable by sending a final presence stanza that has no to attribute and has a type attribute set to "unavailable" π«‘ (Optionally, that final presence stanza MAY contain one or more <status/> elements explaining why the user is no longer available π¬) That said, the user's server MUST NOT depend on getting a final presence from an available resource, because the resource might drop unexpectedly or get timed out by the server π¬
If one of the user's resources becomes unavailable for any reason, whether gracefully or ungracefully, the user's server MUST broadcast unavailable presence to all contacts that:
- are in the user's roster with a subscription type of
"from"or"both", - the user hasn't blocked outbound presence to, and
- have not gotten a presence error from the server during the user's session.
The user's server MUST also send that unavailable presence stanza to any of the user's other available resources β¨ It also goes to any entity the user sent directed presence to during the session for that resource, as long as the user hasn't already sent directed unavailable presence to that entity π¦
Finally, any presence stanza with no type attribute and no to attribute that gets sent after directed unavailable presence or broadcast unavailable presence MUST be broadcast by the server to all subscribers π
5.1.6. Presence Subscriptions β¨
okay so a subscription request is just a presence stanza with type="subscribe" π¦ if you're sending one to an instant messaging contact, the to JID SHOULD look like <contact@example.org> and NOT <contact@example.org/resource>. why? because you usually want presence from ALL of their resources, not just the one you named π
your server MUST NOT auto-approve subscription requests on your behalf (no sneaky yes-ing for you, bestie π ββοΈ). every request MUST go to your client, specifically to one or more of your available resources. if you have no available resource when the request shows up, your server MUST keep a record of it and deliver it the next time you make an available resource, and it keeps doing that until you approve or deny it π«Ά if you DO have more than one available resource, your server MUST broadcast the request to all of them, following Server Rules for Handling XML Stanzas (Section 11). (Note: if an active resource hasn't sent initial presence yet, the server MUST NOT count it as available, so it MUST NOT get subscription requests either π)
BUT, lowkey important twist: if you get a subscribe presence stanza from someone you ALREADY let see your presence (like when they're trying to resync subscription states), your server SHOULD auto-reply for you πΈ also, your server MAY re-send a pending, unapproved request to the contact based on whatever implementation-specific algorithm it uses (like whenever a new resource shows up, or after some amount of time). this is giving resilience π it helps recover from transient, silent errors that might have happened with the original request β¨
5.2. Specifying Availability Status π·
a client MAY add more info about its availability status with the <show/> element (see Show (Section 2.2.2.1)) π
Example: Availability status:
<presence>
<show>dnd</show>
</presence>
5.3. Specifying Detailed Status Information π
with <show/> in the mix, a client MAY also share detailed status info using the <status/> element (see Status (Section 2.2.2.2)) π
Example: Detailed status information:
<presence xml:lang='en'>
<show>dnd</show>
<status>Wooing Juliet</status>
<status xml:lang='cz'>Ja dvořím Juliet</status>
</presence>
5.4. Specifying Presence Priority π
a client MAY give its resource a priority using the <priority/> element (see Priority (Section 2.2.2.3)) π₯
Example: Presence priority:
<presence xml:lang='en'>
<show>dnd</show>
<status>Wooing Juliet</status>
<status xml:lang='cz'>Ja dvořím Juliet</status>
<priority>1</priority>
</presence>
5.5. Presence Examples π
these examples show all the presence stuff we just covered π§ our main character is romeo@example.net. he has an available resource called "orchard", and his roster looks like this:
juliet@example.com(subscription="both", and she has two available resources, one named "chamber" and another named "balcony")benvolio@example.org(subscription="to")mercutio@example.org(subscription="from")
Example 1: User sends initial presence:
<presence/>
Example 2: User's server sends presence probes to contacts with subscription="to" and subscription="both" on behalf of the user's available resource:
<presence
type='probe'
from='romeo@example.net/orchard'
to='juliet@example.com'/>
<presence
type='probe'
from='romeo@example.net/orchard'
to='benvolio@example.org'/>
Example 3: User's server sends initial presence to contacts with subscription="from" and subscription="both" on behalf of the user's available resource:
<presence
from='romeo@example.net/orchard'
to='juliet@example.com'/>
<presence
from='romeo@example.net/orchard'
to='mercutio@example.org'/>
Example 4: Contacts' servers reply to presence probe on behalf of all available resources:
<presence
from='juliet@example.com/balcony'
to='romeo@example.net/orchard'
xml:lang='en'>
<show>away</show>
<status>be right back</status>
<priority>0</priority>
</presence>
<presence
from='juliet@example.com/chamber'
to='romeo@example.net/orchard'>
<priority>1</priority>
</presence>
<presence
from='benvolio@example.org/pda'
to='romeo@example.net/orchard'
xml:lang='en'>
<show>dnd</show>
<status>gallivanting</status>
</presence>
Example 5: Contacts' servers deliver user's initial presence to all available resources or return error to user:
<presence
from='romeo@example.net/orchard'
to='juliet@example.com/chamber'/>
<presence
from='romeo@example.net/orchard'
to='juliet@example.com/balcony'/>
<presence
type='error'
from='mercutio@example.org'
to='romeo@example.net/orchard'>
<error type='cancel'>
<gone xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</presence>
Example 6: User sends directed presence to another user not in his roster:
<presence
from='romeo@example.net/orchard'
to='nurse@example.com'
xml:lang='en'>
<show>dnd</show>
<status>courting Juliet</status>
<priority>0</priority>
</presence>
Example 7: User sends updated available presence information for broadcasting:
<presence xml:lang='en'>
<show>away</show>
<status>I shall return!</status>
<priority>1</priority>
</presence>
Example 8: User's server broadcasts updated presence information only to one contact (not those from whom an error was received or to whom the user sent directed presence):
<presence
from='romeo@example.net/orchard'
to='juliet@example.com'
xml:lang='en'>
<show>away</show>
<status>I shall return!</status>
<priority>1</priority>
</presence>
Example 9: Contact's server delivers updated presence information to all of the contact's available resources:
[to "balcony" resource...]
<presence
from='romeo@example.net/orchard'
to='juliet@example.com'
xml:lang='en'>
<show>away</show>
<status>I shall return!</status>
<priority>1</priority>
</presence>
[to "chamber" resource...]
<presence
from='romeo@example.net/orchard'
to='juliet@example.com'
xml:lang='en'>
<show>away</show>
<status>I shall return!</status>
<priority>1</priority>
</presence>
Example 10: One of the contact's resources broadcasts final presence:
<presence from='juliet@example.com/balcony' type='unavailable'/>
Example 11: Contact's server sends unavailable presence information to user:
<presence
type='unavailable'
from='juliet@example.com/balcony'
to='romeo@example.net/orchard'/>
Example 12: User sends final presence:
<presence from='romeo@example.net/orchard'
type='unavailable'
xml:lang='en'>
<status>gone home</status>
</presence>
Example 13: User's server broadcasts unavailable presence information to contact as well as to the person to whom the user sent directed presence:
<presence
type='unavailable'
from='romeo@example.net/orchard'
to='juliet@example.com'
xml:lang='en'>
<status>gone home</status>
</presence>
<presence
from='romeo@example.net/orchard'
to='nurse@example.com'
xml:lang='en'>
<status>gone home</status>
</presence>
6. Managing Subscriptions π
okay privacy check π‘οΈ presence and availability info is only disclosed to entities the user has approved. when a user agrees that another entity may view their presence, that entity is said to have a subscription to the user's presence information. subscriptions last across sessions, honestly they stick around until the subscriber unsubscribes or the subscribee cancels the previously-granted subscription π subscriptions are managed in XMPP by sending presence stanzas with specially-defined attributes.
Note: subscriptions and rosters are deeply intertwined! the details live under Integration of Roster Items and Presence Subscriptions (Section 8), and you MUST read that section for the full picture on presence subscriptions. (The original says "the reader must refer to that section," so read it, bestie π)
6.1. Requesting a Subscription π
to ask to see someone's presence, you send a presence stanza of type "subscribe" π¦
Example: Sending a subscription request:
<presence to='juliet@example.com' type='subscribe'/>
For client and server responsibilities regarding presence subscription requests, refer to Presence Subscriptions (Section 5.1.6).
6.2. Handling a Subscription Request πββοΈ
when a client gets a subscription request from another entity, it MUST either approve it by sending a presence stanza of type "subscribed" or refuse it by sending a presence stanza of type "unsubscribed" π
Example: Approving a subscription request:
<presence to='romeo@example.net' type='subscribed'/>
Example: Refusing a presence subscription request:
<presence to='romeo@example.net' type='unsubscribed'/>
6.3. Cancelling a Subscription from Another Entity π«
if a user wants to cancel a subscription they already granted, they send a presence stanza of type "unsubscribed" π«
Example: Cancelling a previously granted subscription request:
<presence to='romeo@example.net' type='unsubscribed'/>
6.4. Unsubscribing from Another Entity's Presence π
if a user wants to stop getting another entity's presence, they send a presence stanza of type "unsubscribe" β¨
Example: Unsubscribing from an entity's presence:
<presence to='juliet@example.com' type='unsubscribe'/>
7. Roster Management π
in XMPP, your contact list is called a roster π it's made up of any number of roster items, and each one is identified by a unique JID (usually something like <contact@domain>). your server stores your roster for you so you can access it from any resource, which is honestly iconic π
Note: rosters and subscriptions have major interactions too! those are defined under Integration of Roster Items and Presence Subscriptions (Section 8), and you MUST check that section for a full understanding of roster management π₯Ή
7.1. Syntax and Semantics β¨
okay so rosters get managed with IQ stanzas π
specifically through a <query/> child element qualified by the jabber:iq:roster namespace. The <query/> element MAY hold one or more <item/> children, and each one describes a unique roster item, aka a "contact" π¦
The "key" (the unique identifier) for each roster item is a JID, and it lives in the jid attribute of the <item/> element. That attribute is REQUIRED π The value of jid SHOULD be of the form <user@domain> if the item belongs to another (human) instant messaging user.
The presence subscription state for a roster item lives in the subscription attribute of <item/>. The allowed values are:
none-- the user isn't subscribed to the contact's presence info, and the contact isn't subscribed to the user's presence info either. Total strangers πto-- the user is subscribed to the contact's presence, but the contact is NOT subscribed to the user's. One-way street πfrom-- the contact is subscribed to the user's presence, but the user isn't subscribed to the contact's. The reverse one-way πΈboth-- both of them are subscribed to each other's presence. Mutuals!! π
Each <item/> MAY have a name attribute, which sets the "nickname" for the JID. The user decides that nickname, not the contact π
The value of name is opaque.
Each <item/> MAY also have one or more <group/> child elements, which are for sorting roster items into categories ποΈ The XML character data inside <group/> is opaque too.
7.2. Business Rules
okay this part is all about who gets to change what π A server MUST ignore any to address on a roster "set", and MUST treat any roster "set" as applying to the sender. Literally no sneaking edits onto someone else's roster π
ββοΈ
For added safety, a client SHOULD check the from address of a "roster push" (that's an incoming IQ of type set containing a roster item) to make sure it's from a trusted source π‘οΈ Specifically, the stanza MUST either have no from attribute (meaning it's implicitly from the server), or have a from attribute whose value matches the user's bare JID (of the form <user@domain>) or full JID (of the form <user@domain/resource>). Otherwise, the client SHOULD ignore the "roster push". Bye bye, sus pushes π
7.3. Retrieving One's Roster on Login
so when you connect to the server and become an active resource, a client SHOULD request the roster before sending initial presence β¨ BUT, the roster request is OPTIONAL, because not every resource wants to receive the roster. Think a connection with limited bandwidth πΆ If an available resource doesn't request the roster during a session, the server MUST NOT send it presence subscriptions and associated roster updates. No roster, no updates, periodt π«
Example: Client requests current roster from server:
<iq from='juliet@example.com/balcony' type='get' id='roster_1'>
<query xmlns='jabber:iq:roster'/>
</iq>
Example: Client receives roster from server:
<iq to='juliet@example.com/balcony' type='result' id='roster_1'>
<query xmlns='jabber:iq:roster'>
<item jid='romeo@example.net'
name='Romeo'
subscription='both'>
<group>Friends</group>
</item>
<item jid='mercutio@example.org'
name='Mercutio'
subscription='from'>
<group>Friends</group>
</item>
<item jid='benvolio@example.org'
name='Benvolio'
subscription='both'>
<group>Friends</group>
</item>
</query>
</iq>
7.4. Adding a Roster Item
at any time, a user MAY add an item to their roster π
Example: Client adds a new item:
<iq from='juliet@example.com/balcony' type='set' id='roster_2'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='Nurse'>
<group>Servants</group>
</item>
</query>
</iq>
The server MUST update the roster information in persistent storage, and ALSO push the change out to all of the user's available resources that have requested the roster π€ This "roster push" is an IQ stanza of type set going from the server to the client, and it keeps every available resource in sync with the server-based roster. Sync sisters, unite π«Ά
Example: Server (1) pushes the updated roster information to all available resources that have requested the roster and (2) replies with an IQ result to the sending resource:
<iq to='juliet@example.com/balcony'
type='set'
id='a78b4q6ha463'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='Nurse'
subscription='none'>
<group>Servants</group>
</item>
</query>
</iq>
<iq to='juliet@example.com/chamber'
type='set'
id='a78b4q6ha464'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='Nurse'
subscription='none'>
<group>Servants</group>
</item>
</query>
</iq>
<iq to='juliet@example.com/balcony' type='result' id='roster_2'/>
As required by the semantics of the IQ stanza kind defined in [XMPP-CORE], each resource that received the roster push MUST reply with an IQ stanza of type result (or error). Every resource has to reply, no ghosting allowed π»
Example: Resources reply with an IQ result to the server:
<iq from='juliet@example.com/balcony'
to='example.com'
type='result'
id='a78b4q6ha463'/>
<iq from='juliet@example.com/chamber'
to='example.com'
type='result'
id='a78b4q6ha464'/>
7.5. Updating a Roster Item
updating an existing roster item (like changing its group) works the exact same way as adding one π You send the roster item in an IQ set to the server.
Example: User updates roster item (added group):
<iq from='juliet@example.com/chamber' type='set' id='roster_3'>
<query xmlns='jabber:iq:roster'>
<item jid='romeo@example.net'
name='Romeo'
subscription='both'>
<group>Friends</group>
<group>Lovers</group>
</item>
</query>
</iq>
Just like with adding a roster item, when updating one the server MUST update the roster information in persistent storage, and also initiate a roster push to all of the user's available resources that have requested the roster π
7.6. Deleting a Roster Item
at any time, a user MAY delete an item from their roster ποΈ To do it, they send an IQ set to the server, and the value of the subscription attribute has to be remove (a compliant server MUST ignore any other values of the subscription attribute when they come in from a client). Only remove gets the job done, bestie π
ββοΈ
Example: Client removes an item:
<iq from='juliet@example.com/balcony' type='set' id='roster_4'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com' subscription='remove'/>
</query>
</iq>
Just like with adding a roster item, when deleting one the server MUST update the roster information in persistent storage, initiate a roster push to all of the user's available resources that have requested the roster (with the subscription attribute set to a value of remove), and send an IQ result to the initiating resource π
For further information about what this command actually implies, check out Removing a Roster Item and Cancelling All Subscriptions (Section 8.6) π
8. Integration of Roster Items and Presence Subscriptions β¨
8.1. Overview
some level of integration between roster items and presence subscriptions is normally expected by an instant messaging user π¬ It covers the user's subscriptions to and from other contacts. This section describes the level of integration that MUST be supported within XMPP instant messaging applications π₯
There are four primary subscription states:
- None -- the user isn't subscribed to the contact's presence information, and the contact isn't subscribed to the user's presence information
- To -- the user is subscribed to the contact's presence information, but the contact is NOT subscribed to the user's
- From -- the contact is subscribed to the user's presence information, but the user isn't subscribed to the contact's
- Both -- both the user and the contact are subscribed to each other's presence information (basically the union of
fromandto) π
Each of these states shows up in the roster of both the user and the contact, which means the subscription states stick around durably π§·
The narrative explanations of how these subscription states work with roster items to complete certain use cases come in the sub-sections that follow. The full details on how servers and clients handle every subscription state (including the pending states that sit between the primary ones above) are in Subscription States (Section 9) π
The server MUST NOT send presence subscription requests or roster pushes to unavailable resources, nor to available resources that haven't requested the roster π«
The from and to addresses are OPTIONAL in roster pushes. If they're included, their values SHOULD be the full JID of the resource for that session π¦ A client MUST acknowledge each roster push with an IQ stanza of type result. (For the sake of brevity, these stanzas aren't shown in the following examples, but they're required by the IQ semantics defined in [XMPP-CORE] π)
8.2. User Subscribes to Contact β¨
okay so this is the whole "I want to see my bestie's presence" process, and it's giving very choreographed. The roster items and the subscription states have to stay in sync the entire time, so it's a little dance, bestie π
- First up, getting ready to render the contact in the user's client and letting the server track the subscription π The user's client SHOULD send a "roster set" for the new roster item. That's an IQ stanza of type='set' holding a <query/> element in the 'jabber:iq:roster' namespace, and inside that goes an <item/> element defining the new roster item. The <item/> MUST have a 'jid' attribute, MAY have a 'name' attribute, MUST NOT have a 'subscription' attribute, and MAY contain one or more <group/> child elements. Something like:
<iq type='set' id='set1'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
- Now the server does two things. First, it (1) MUST push the new roster item to every available resource on this user's account that has asked for the roster, with 'subscription' set to "none". Second, it (2) MUST reply to the sending resource with an IQ result confirming the roster set went through πΈ
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='none'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
<iq type='result' id='set1'/>
- If the user wants to ask for a subscription to the contact's presence info, the user's client MUST send a presence stanza of type='subscribe' to the contact π¦
<presence to='contact@example.org' type='subscribe'/>
- Next, the user's server MUST start a second roster push to every available resource that has requested the roster. This one puts the contact into the pending sub-state of the 'none' subscription state. You can spot pending by the ask='subscribe' attribute on the roster item π
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='none'
ask='subscribe'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
Note: If the user never made a roster item before sending the subscription request, the server MUST create one for them now, and then send a roster push to all of the user's available resources that have requested the roster. That item has no 'name' attribute and no <group/> child, which is why the example above looks the way it does π§
- The user's server MUST stamp the presence stanza of type "subscribe" with the user's bare JID (i.e., <user@example.com>) as the 'from' address. If the user supplied a 'from' address that includes their full JID, the server SHOULD strip the resource identifier π If the contact lives on a different host than the user, the user's server MUST route the presence stanza to the contact's server so it can be delivered (this case is assumed throughout; if the contact is on the same host, the server can just deliver the presence stanza directly π ):
<presence
from='user@example.com'
to='contact@example.org'
type='subscribe'/>
Note: If the user's server gets a presence stanza of type "error" back from the contact's server, it MUST deliver that error stanza to the user. The client MAY work out that the error is a response to the outgoing "subscribe" presence it sent earlier (for example, by tracking an 'id' attribute). After that it can choose to resend the "subscribe" request, or undo the roster change by sending a presence stanza of type "unsubscribe" to the contact π
- When the contact's server receives the presence stanza of type "subscribe" addressed to the contact, it MUST figure out whether at least one available resource exists where the contact has requested the roster. If so, it MUST deliver the subscription request to the contact. If not, the contact's server MUST store the subscription request offline so it can be delivered once that condition is next met. Usually this happens by adding a roster item for the contact to the user's roster, with a state of "None + Pending In" (defined under Subscription States, Section 9). Even so, a server SHOULD NOT push or deliver roster items in that state to the contact π«Ά
No matter when the request gets delivered, the contact has to decide whether to approve it. That depends on the contact's configured preferences, and the contact's client MAY approve or refuse the request without ever showing it to the contact π€·ββοΈ For this part we're following the "happy path," where the contact says yes. (The no-thanks flow is in Section 8.2.1.) In that case, the contact's client (1) SHOULD perform a roster set with the nickname and group it wants for the user (if any), and (2) MUST send a presence stanza of type "subscribed" to the user to approve the request π
<iq type='set' id='set2'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<presence to='user@example.com' type='subscribed'/>
- Now the contact's server gets to work. It (1) MUST push a roster item for the user to every available resource on the contact's account that has requested the roster, with the subscription state set to 'from'. The server MUST do this even if the contact never sent a roster set. It also (2) MUST return an IQ result to the sending resource to confirm the roster set. It (3) MUST route the presence stanza of type "subscribed" to the user, after stamping the 'from' address with the contact's bare JID (<contact@example.org>). And finally it (4) MUST send available presence from all of the contact's available resources to the user π
<iq type='set' to='contact@example.org/resource'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='from'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<iq type='result' to='contact@example.org/resource' id='set2'/>
<presence
from='contact@example.org'
to='user@example.com'
type='subscribed'/>
<presence
from='contact@example.org/resource'
to='user@example.com'/>
Note: If the contact's server gets a presence stanza of type "error" from the user's server, it MUST deliver that error stanza to the contact. The contact's client MAY work out that the error is a response to the outgoing "subscribed" presence it sent earlier (for example, by tracking an 'id' attribute). Then it can choose to resend the "subscribed" notification, or revert the roster by sending a presence stanza of type "unsubscribed" to the user π΅βπ«
- When the user's server receives the presence stanza of type "subscribed" addressed to the user, it MUST first check that the contact is in the user's roster in one of these two states: (a) subscription='none' and ask='subscribe', or (b) subscription='from' and ask='subscribe'. If the contact isn't in the roster in either of those states, the user's server MUST silently ignore the "subscribed" presence stanza. That means it MUST NOT route it to the user, MUST NOT modify the user's roster, and MUST NOT generate a roster push to the user's available resources. Tragic, but that's the rule π ββοΈ If the contact IS in the roster in one of those states, the user's server (1) MUST deliver the "subscribed" presence stanza from the contact to the user, (2) MUST push an updated roster item for the contact to all of the user's available resources that have requested the roster, with the 'subscription' attribute set to "to", and (3) MUST deliver the available presence from each of the contact's available resources to each of the user's available resources π
<presence
to='user@example.com'
from='contact@example.org'
type='subscribed'/>
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='to'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
<presence
from='contact@example.org/resource'
to='user@example.com/resource'/>
- When the user gets the "subscribed" presence stanza, the user SHOULD acknowledge that subscription state notification. They can do that by "affirming" it, which means sending a presence stanza of type "subscribe" to the contact, or by "denying" it, which means sending a presence stanza of type "unsubscribe" to the contact. This step doesn't necessarily change the subscription state (see Subscription States, Section 9, for the details). What it does do is tell the user's server that it MUST NO LONGER send notifications of the subscription state change to the user (see Section 9.4) π
From the user's point of view, there's now a subscription to the contact's presence info. From the contact's point of view, there's now a subscription from the user. Both sides are subscribed and it's giving mutual vibes πβ¨
8.2.1. Alternate Flow: Contact Declines Subscription Request π ββοΈ
okay so the bit above is the "happy path" for the user's subscription request to the contact πΈ. the main alternate flow is when the contact says no thanks to the request π , and it goes like this:
- okay so if the contact wants to refuse, the contact's client MUST send a presence stanza of type "unsubscribed" to the user. this replaces the presence stanza of type "subscribed" from Step 6 of Section 8.2 π
<presence to='user@example.com' type='unsubscribed'/>
- then the contact's server MUST route that "unsubscribed" presence stanza to the user, and it has to stamp the 'from' address with the contact's bare JID (<contact@example.org>) first β¨
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
Note: if the contact's server had previously added the user to the contact's roster for tracking purposes, it MUST remove that item right now π§Ή
- once the user's server gets the "unsubscribed" presence stanza addressed to the user, it (1) MUST deliver that stanza to the user and (2) MUST start a roster push to all of the user's available resources that have requested the roster π£. the push has an updated roster item for the contact with the 'subscription' attribute set to "none" and no 'ask' attribute at all π¦
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='none'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
- when the user gets that "unsubscribed" presence stanza, the user SHOULD acknowledge it π«Ά. they can "affirm" it by sending a presence stanza of type "unsubscribe" to the contact, or "deny" it by sending a presence stanza of type "subscribe" to the contact. this doesn't have to change the subscription state (see Subscription States (Section 9) for the deets π). what it does do is let the user's server know that it MUST stop sending the user notifications about subscription state changes (see Section 9.4) π
so the end result? the contact is now in the user's roster with a subscription state of "none" π, while the user isn't in the contact's roster at all. ouch, but iykyk π
8.3. Creating a Mutual Subscription π
the user and contact can build on the "happy path" from above to make a mutual subscription (aka a subscription of type "both") π. here's how it goes:
- if the contact wants a mutual subscription, the contact MUST send a subscription request to the user. (depending on the contact's preferences, the contact's client MAY send this automatically π€)
<presence to='user@example.com' type='subscribe'/>
- next, the contact's server (1) MUST start a roster push to all of the contact's available resources that have requested the roster π¬. the user is still in the 'from' subscription state, but there's now a pending 'to' subscription, shown by the ask='subscribe' attribute on the roster item. it also (2) MUST route the "subscribe" presence stanza to the user, stamping the 'from' address with the contact's bare JID (<contact@example.org>) first π
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='from'
ask='subscribe'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<presence
from='contact@example.org'
to='user@example.com'
type='subscribe'/>
Note: if the contact's server gets a presence stanza of type "error" from the user's server, it MUST deliver that error stanza to the contact π¨. the contact's client MAY figure out that the error is a response to the outgoing "subscribe" presence stanza it sent earlier (for example, by tracking an 'id' attribute). then it can either resend the "subscribe" request, or put the roster back the way it was by sending a presence stanza of type "unsubscribe" to the user π
- when the user gets the "subscribe" presence stanza addressed to them, the user's server must check whether there's at least one available resource that has requested the roster π. if there is, the user's server MUST deliver the subscription request to the user. if there isn't, it MUST store the request offline and deliver it once that condition is met π¦. no matter when it arrives, the user then has to decide whether to approve it (depending on the user's preferences, the user's client MAY approve or refuse the request without showing it to the user π). we're going with the "happy path" here, where the user approves. (the alternate flow where they decline is in Section 8.3.1 π ββοΈ.) in that case, the user's client MUST send a presence stanza of type "subscribed" to the contact to approve the request π₯°
<presence to='contact@example.org' type='subscribed'/>
- after that, the user's server (1) MUST start a roster push to all of the user's available resources that have requested the roster, with a roster item for the contact where the 'subscription' attribute is "both" π. it also (2) MUST route the "subscribed" presence stanza to the contact, stamping the 'from' address with the user's bare JID (<user@example.com>) first π. and (3) it MUST send the contact the full XML of the last presence stanza with no 'to' attribute that the server received from each of the user's available resources (subject to the privacy lists in force for each session) π
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='both'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
<presence
from='user@example.com'
to='contact@example.org'
type='subscribed'/>
<presence
from='user@example.com/resource'
to='contact@example.org'/>
Note: if the user's server gets a presence stanza of type "error" from the contact's server, it MUST deliver that error stanza to the user π¬. the user's client MAY work out that the error is a response to the outgoing "subscribed" presence stanza it sent earlier (for example, by tracking an 'id' attribute). then it can resend the subscription request, or revert the roster to its previous state by sending a presence stanza of type "unsubscribed" to the contact π«
- when the contact gets the "subscribed" presence stanza addressed to them, the contact's server MUST first check that the user is in the contact's roster with one of these two states: (a) subscription='none' and ask='subscribe', or (b) subscription='from' and ask='subscribe' π. if the user isn't in the contact's roster with either state, the contact's server MUST silently ignore the "subscribed" presence stanza. that means it MUST NOT route it to the contact, MUST NOT modify the contact's roster, and MUST NOT generate a roster push to the contact's available resources π. but if the user is in the roster with either of those states, the contact's server (1) MUST deliver the "subscribed" presence stanza from the user to the contact π. it also (2) MUST start a roster push to all of the contact's available resources that have requested the roster, with an updated roster item for the user where the 'subscription' attribute is "both" π². and (3) it MUST deliver the available presence stanza received from each of the user's available resources to each of the contact's available resources β¨
<presence
from='user@example.com'
to='contact@example.org'
type='subscribed'/>
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='both'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<presence
from='user@example.com/resource'
to='contact@example.org/resource'/>
- once the contact gets the "subscribed" presence stanza, the contact SHOULD acknowledge it π€. they can "affirm" it by sending a presence stanza of type "subscribe" to the user, or "deny" it by sending a presence stanza of type "unsubscribe" to the user. this doesn't have to change the subscription state (see Subscription States (Section 9) for details π§). what it does do is let the contact's server know that it MUST stop sending the contact notifications about subscription state changes (see Section 9.4) π
the user and the contact now have a mutual subscription to each other's presence ππ―ββοΈ -- aka the subscription is of type "both" β π
8.3.1. Alternate Flow: User Declines Subscription Request β¨
okay so the activity flow above is the "happy path" π for when the contact asks the user for a subscription. the main alternate flow is when the user says nah π ββοΈ to the contact's request, and it goes like this:
- if the user wants to refuse the request, the user's client MUST send a presence stanza of type "unsubscribed" to the contact (instead of the presence stanza of type "subscribed" sent in Step 3 of Section 8.3):
<presence to='contact@example.org' type='unsubscribed'/>
- as a result, the user's server MUST route the presence stanza of type "unsubscribed" to the contact, first stamping the 'from' address as the bare JID (<user@example.com>) of the user:
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribed'/>
- upon receiving the presence stanza of type "unsubscribed" addressed to the contact, the contact's server (1) MUST deliver that presence stanza to the contact; and (2) MUST initiate a roster push to all available resources associated with the contact that have requested the roster, containing an updated roster item for the user with the 'subscription' attribute set to a value of "from" and with no 'ask' attribute:
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribed'/>
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='from'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
- upon receiving the presence stanza of type "unsubscribed", the contact SHOULD acknowledge receipt of that subscription state notification through either "affirming" it by sending a presence stanza of type "unsubscribe" to the user or "denying" it by sending a presence stanza of type "subscribe" to the user; this step does not necessarily affect the subscription state (see Subscription States (Section 9) for details), but instead lets the contact's server know that it MUST no longer send notification of the subscription state change to the contact (see Section 9.4).
As a result of this activity, there has been no change in the subscription state; i.e., the contact is in the user's roster with a subscription state of "to" and the user is in the contact's roster with a subscription state of "from". no drama, nothing changes π§
8.4. Unsubscribing π«Ά
At any time after subscribing to a contact's presence information, a user MAY unsubscribe. While the XML that the user sends to make this happen is the same in all instances, the subsequent subscription state is different depending on the subscription state obtaining when the unsubscribe "command" is sent. Both possible scenarios are described below. two cases, same button, different outcomes π
8.4.1. Case #1: Unsubscribing When Subscription is Not Mutual πΈ
In the first case, the user has a subscription to the contact's presence information but the contact does not have a subscription to the user's presence information (i.e., the subscription is not yet mutual).
- if the user wants to unsubscribe from the contact's presence information, the user MUST send a presence stanza of type "unsubscribe" to the contact:
<presence to='contact@example.org' type='unsubscribe'/>
- as a result, the user's server (1) MUST send a roster push to all of the user's available resources that have requested the roster, containing an updated roster item for the contact with the 'subscription' attribute set to a value of "none"; and (2) MUST route the presence stanza of type "unsubscribe" to the contact, first stamping the 'from' address as the bare JID (<user@example.com>) of the user:
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='none'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribe'/>
- upon receiving the presence stanza of type "unsubscribe" addressed to the contact, the contact's server (1) MUST initiate a roster push to all available resources associated with the contact that have requested the roster, containing an updated roster item for the user with the 'subscription' attribute set to a value of "none" (if the contact is unavailable or has not requested the roster, the contact's server MUST modify the roster item and send that modified item the next time the contact requests the roster); and (2) MUST deliver the "unsubscribe" state change notification to the contact:
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='none'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribe'/>
- upon receiving the presence stanza of type "unsubscribe", the contact SHOULD acknowledge receipt of that subscription state notification through either "affirming" it by sending a presence stanza of type "unsubscribed" to the user or "denying" it by sending a presence stanza of type "subscribed" to the user; this step does not necessarily affect the subscription state (see Subscription States (Section 9) for details), but instead lets the contact's server know that it MUST no longer send notification of the subscription state change to the contact (see Section 9.4).
- the contact's server then (1) MUST send a presence stanza of type "unsubscribed" to the user; and (2) SHOULD send unavailable presence from all of the contact's available resources to the user:
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
<presence
from='contact@example.org/resource'
to='user@example.com'
type='unavailable'/>
- when the user's server receives the presence stanzas of type "unsubscribed" and "unavailable", it MUST deliver them to the user:
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
<presence
from='contact@example.org/resource'
to='user@example.com'
type='unavailable'/>
- upon receiving the presence stanza of type "unsubscribed", the user SHOULD acknowledge receipt of that subscription state notification through either "affirming" it by sending a presence stanza of type "unsubscribe" to the contact or "denying" it by sending a presence stanza of type "subscribe" to the contact; this step does not necessarily affect the subscription state (see Subscription States (Section 9) for details), but instead lets the user's server know that it MUST no longer send notification of the subscription state change to the user (see Section 9.4).
8.4.2. Case #2: Unsubscribing When Subscription is Mutual β¨
Okay so this is the glow-up case π The user is subscribed to the contact's presence info, AND the contact is subscribed to the user's presence info too. So the subscription is mutual, and there's a lot more going on π«Ά
- If the user wants to unsubscribe from the contact's presence information, the user MUST send a presence stanza of type "unsubscribe" to the contact π:
<presence to='contact@example.org' type='unsubscribe'/>
- Now the user's server gets busy π₯. It (1) MUST send a roster push to all of the user's available resources that have requested the roster, with an updated roster item for the contact where the
'subscription'attribute is set to "from"; and (2) MUST route the "unsubscribe" presence stanza to the contact, first stamping the'from'address as the bare JID (<user@example.com>) of the user:
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='from'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribe'/>
- When the contact's server receives that "unsubscribe" presence stanza addressed to the contact, it gets to work too π¦. It (1) MUST initiate a roster push to all of the contact's available resources that have requested the roster, with an updated roster item for the user where the
'subscription'attribute is set to "to" (if the contact is unavailable or hasn't requested the roster, the contact's server MUST modify the roster item and send that modified item the next time the contact requests the roster); and (2) MUST deliver the "unsubscribe" state change notification to the contact:
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='to'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribe'/>
- Next, the contact SHOULD acknowledge receipt of that "unsubscribe" notification πΈ. They can either "affirm" it by sending a presence stanza of type "unsubscribed" to the user, or "deny" it by sending a presence stanza of type "subscribed" to the user. This step doesn't necessarily change the subscription state (see Subscription States (Section 9) for the details), but it does tell the contact's server that it MUST no longer send notification of the subscription state change to the contact (see Section 9.4). Main character energy, but make it a receipt π§Ύ
- Then the contact's server (1) MUST send a presence stanza of type "unsubscribed" to the user; and (2) SHOULD send unavailable presence from all of the contact's available resources to the user π:
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
<presence
from='contact@example.org/resource'
to='user@example.com'
type='unavailable'/>
- When the user's server receives those "unsubscribed" and "unavailable" presence stanzas, it MUST deliver them to the user π¬:
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
<presence
from='contact@example.org/resource'
to='user@example.com'
type='unavailable'/>
- Finally, the user SHOULD acknowledge receipt of that "unsubscribed" notification π. They can "affirm" it by sending a presence stanza of type "unsubscribe" to the contact, or "deny" it by sending a presence stanza of type "subscribe" to the contact. Again, this step doesn't necessarily change the subscription state (see Subscription States (Section 9) for details), but it lets the user's server know that it MUST no longer send notification of the subscription state change to the user (see Section 9.4).
Note: Obviously this does NOT remove the roster item from the user's roster π, and the contact still has a subscription to the user's presence information. If the user wants to completely cancel a mutual subscription AND fully remove the roster item from their roster, the user SHOULD update the roster item with subscription='remove', as defined under Removing a Roster Item and Cancelling All Subscriptions (Section 8.6).
8.5. Cancelling a Subscription π
At any time after approving a subscription request from a user, a contact MAY cancel that subscription. The XML the contact sends to make this happen is the same in every case π, but what happens next depends on the subscription state at the moment the cancellation is sent. Both possible scenarios are covered below.
8.5.1. Case #1: Cancelling When Subscription is Not Mutual π
In this first case, the user has a subscription to the contact's presence information, but the contact does NOT have a subscription to the user's presence information (so the subscription isn't mutual yet).
- If the contact wants to cancel the user's subscription, the contact MUST send a presence stanza of type "unsubscribed" to the user π«:
<presence to='user@example.com' type='unsubscribed'/>
- Now the contact's server gets to work π§. It (1) MUST send a roster push to all of the contact's available resources that have requested the roster, with an updated roster item for the user where the
'subscription'attribute is set to "none"; (2) MUST route the "unsubscribed" presence stanza to the user, first stamping the'from'address as the bare JID (<contact@example.org>) of the contact; and (3) SHOULD send unavailable presence from all of the contact's available resources to the user:
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='none'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
<presence
from='contact@example.org/resource'
to='user@example.com'
type='unavailable'/>
- When the user's server receives the "unsubscribed" presence stanza addressed to the user, it gets busy too πΈ. It (1) MUST initiate a roster push to all of the user's available resources that have requested the roster, with an updated roster item for the contact where the
'subscription'attribute is set to "none" (if the user is unavailable or hasn't requested the roster, the user's server MUST modify the roster item and send that modified item the next time the user requests the roster); (2) MUST deliver the "unsubscribed" state change notification to all of the user's available resources; and (3) MUST deliver the unavailable presence to all of the user's available resources:
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='none'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
<presence
from='contact@example.org/resource'
to='user@example.com'
type='unavailable'/>
- Last but not least, the user SHOULD acknowledge receipt of that "unsubscribed" notification π«Ά. They can "affirm" it by sending a presence stanza of type "unsubscribe" to the contact, or "deny" it by sending a presence stanza of type "subscribe" to the contact. This step doesn't necessarily change the subscription state (see Subscription States (Section 9) for details), but it does let the user's server know that it MUST no longer send notification of the subscription state change to the user (see Section 9.4).
8.5.2. Case #2: Cancelling When Subscription is Mutual β¨
okay so this is the mutual one π«Ά the user is subscribed to the contact's presence info, AND the contact is subscribed to the user's presence info too. so the subscription goes both ways, bestie π
- If the contact wants to cancel the user's subscription, the contact MUST send a presence stanza of type "unsubscribed" to the user π
<presence to='user@example.com' type='unsubscribed'/>
- As a result, the contact's server (1) MUST send a roster push to all of the contact's available resources that have requested the roster, containing an updated roster item for the user with the 'subscription' attribute set to a value of "to"; (2) MUST route the presence stanza of type "unsubscribed" to the user, first stamping the 'from' address as the bare JID (
<contact@example.org>) of the contact; and (3) SHOULD send unavailable presence from all of the contact's available resources to all of the user's available resources π¦
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='to'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
<presence
from='contact@example.org/resource'
to='user@example.com'
type='unavailable'/>
- Upon receiving the presence stanza of type "unsubscribed" addressed to the user, the user's server (1) MUST initiate a roster push to all of the user's available resources that have requested the roster, containing an updated roster item for the contact with the 'subscription' attribute set to a value of "from" (if the user is unavailable or has not requested the roster, the user's server MUST modify the roster item and send that modified item the next time the user requests the roster); and (2) MUST deliver the "unsubscribed" state change notification to all of the user's available resources; and (3) MUST deliver the unavailable presence to all of the user's available resources πΈ
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='from'
name='MyContact'>
<group>MyBuddies</group>
</item>
</query>
</iq>
<presence
from='contact@example.org'
to='user@example.com'
type='unsubscribed'/>
<presence
from='contact@example.org/resource'
to='user@example.com'
type='unavailable'/>
- Upon receiving the presence stanza of type "unsubscribed", the user SHOULD acknowledge receipt of that subscription state notification through either "affirming" it by sending a presence stanza of type "unsubscribe" to the contact or "denying" it by sending a presence stanza of type "subscribe" to the contact; this step does not necessarily affect the subscription state (see Subscription States (Section 9) for details), but instead lets the user's server know that it MUST no longer send notification of the subscription state change to the user (see Section 9.4) π§
Note: Obviously this does not result in removal of the roster item from the contact's roster, and the contact still has a subscription to the user's presence information. In order to both completely cancel a mutual subscription and fully remove the roster item from the contact's roster, the contact should update the roster item with subscription='remove' as defined under Removing a Roster Item and Cancelling All Subscriptions (Section 8.6). π
8.6. Removing a Roster Item and Cancelling All Subscriptions
Because there may be many steps involved in completely removing a roster item and cancelling subscriptions in both directions, the roster management protocol includes a "shortcut" method for doing so. π The process may be initiated no matter what the current subscription state is by sending a roster set containing an item for the contact with the 'subscription' attribute set to a value of "remove":
<iq type='set' id='remove1'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='remove'/>
</query>
</iq>
When the user removes a contact from his or her roster by setting the 'subscription' attribute to a value of "remove", the user's server (1) MUST automatically cancel any existing presence subscription between the user and the contact (both 'to' and 'from' as appropriate); (2) MUST remove the roster item from the user's roster and inform all of the user's available resources that have requested the roster of the roster item removal; (3) MUST inform the resource that initiated the removal of success; and (4) SHOULD send unavailable presence from all of the user's available resources to the contact π
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribe'/>
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribed'/>
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='contact@example.org'
subscription='remove'/>
</query>
</iq>
<iq type='result' id='remove1'/>
<presence
from='user@example.com/resource'
to='contact@example.org'
type='unavailable'/>
Upon receiving the presence stanza of type "unsubscribe", the contact's server (1) MUST initiate a roster push to all available resources associated with the contact that have requested the roster, containing an updated roster item for the user with the 'subscription' attribute set to a value of "to" (if the contact is unavailable or has not requested the roster, the contact's server MUST modify the roster item and send that modified item the next time the contact requests the roster); and (2) MUST also deliver the "unsubscribe" state change notification to all of the contact's available resources π
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='to'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribe'/>
Upon receiving the presence stanza of type "unsubscribed", the contact's server (1) MUST initiate a roster push to all available resources associated with the contact that have requested the roster, containing an updated roster item for the user with the 'subscription' attribute set to a value of "none" (if the contact is unavailable or has not requested the roster, the contact's server MUST modify the roster item and send that modified item the next time the contact requests the roster); and (2) MUST also deliver the "unsubscribe" state change notification to all of the contact's available resources π
<iq type='set'>
<query xmlns='jabber:iq:roster'>
<item
jid='user@example.com'
subscription='none'
name='SomeUser'>
<group>SomeGroup</group>
</item>
</query>
</iq>
<presence
from='user@example.com'
to='contact@example.org'
type='unsubscribed'/>
Upon receiving the presence stanza of type "unavailable" addressed to the contact, the contact's server MUST deliver the unavailable presence to all of the user's available resources π¦
<presence
from='user@example.com/resource'
to='contact@example.org'
type='unavailable'/>
Note: When the user removes the contact from the user's roster, the end state of the contact's roster is that the user is still in the contact's roster with a subscription state of "none"; in order to completely remove the roster item for the user, the contact needs to also send a roster removal request. π₯Ί
9. Subscription States π
This section provides detailed information about subscription states and server handling of subscription-related presence stanzas (i.e., presence stanzas of type "subscribe", "subscribed", "unsubscribe", and "unsubscribed").
9.1. Defined States π
There are nine possible subscription states, which are described here from the user's (not contact's) perspective:
- "None" = contact and user are not subscribed to each other, and neither has requested a subscription from the other
- "None + Pending Out" = contact and user are not subscribed to each other, and user has sent contact a subscription request but contact has not replied yet
- "None + Pending In" = contact and user are not subscribed to each other, and contact has sent user a subscription request but user has not replied yet (note: contact's server SHOULD NOT push or deliver roster items in this state, but instead SHOULD wait until contact has approved subscription request from user)
- "None + Pending Out/In" = contact and user are not subscribed to each other, contact has sent user a subscription request but user has not replied yet, and user has sent contact a subscription request but contact has not replied yet
- "To" = user is subscribed to contact (one-way)
- "To + Pending In" = user is subscribed to contact, and contact has sent user a subscription request but user has not replied yet
- "From" = contact is subscribed to user (one-way)
- "From + Pending Out" = contact is subscribed to user, and user has sent contact a subscription request but contact has not replied yet
- "Both" = user and contact are subscribed to each other (two-way) π
9.2. Server Handling of Outbound Presence Subscription Stanzas β¨
Okay so this is the part where the user's server handles the stanzas it sends out π . Outbound presence subscription stanzas are how the user manages their own subscription to the contact's presence info (the "subscribe" and "unsubscribe" types), and how they control the contact's access to the user's presence (the "subscribed" and "unsubscribed" types) π¦
Servers can totally fall out of sync about subscription states π, so the user's server MUST without exception route all outbound presence stanzas of type "subscribe" or "unsubscribe" to the contact. That way the user can resynchronize their subscription to the contact's presence info if they need to π«Ά
For "subscribed" and "unsubscribed" stanzas, it's a little more chill. The user's server SHOULD NOT route one to the contact if it doesn't cause a subscription state change from the user's perspective, and it MUST NOT make a state change in that case. If the stanza does cause a state change, the user's server MUST route it to the contact and MUST make the appropriate state change π The tables below sum it all up.
Table 1: Recommended handling of outbound "subscribed" stanzas
+----------------------------------------------------------------+
| EXISTING STATE | ROUTE? | NEW STATE |
+----------------------------------------------------------------+
| "None" | no | no state change |
| "None + Pending Out" | no | no state change |
| "None + Pending In" | yes | "From" |
| "None + Pending Out/In" | yes | "From + Pending Out" |
| "To" | no | no state change |
| "To + Pending In" | yes | "Both" |
| "From" | no | no state change |
| "From + Pending Out" | no | no state change |
| "Both" | no | no state change |
+----------------------------------------------------------------+
Table 2: Recommended handling of outbound "unsubscribed" stanzas
+----------------------------------------------------------------+
| EXISTING STATE | ROUTE? | NEW STATE |
+----------------------------------------------------------------+
| "None" | no | no state change |
| "None + Pending Out" | no | no state change |
| "None + Pending In" | yes | "None" |
| "None + Pending Out/In" | yes | "None + Pending Out" |
| "To" | no | no state change |
| "To + Pending In" | yes | "To" |
| "From" | yes | "None" |
| "From + Pending Out" | yes | "None + Pending Out" |
| "Both" | yes | "To" |
+----------------------------------------------------------------+
9.3. Server Handling of Inbound Presence Subscription Stanzas π
Inbound stanzas are the ones coming in to the user from the contact, and there are three flavours of them. "subscribe" asks the user to do a subscription-related action, "unsubscribe" tells the user the contact has taken a subscription-related action, and "subscribed" and "unsubscribed" let the contact manage the user's access to the contact's presence info π§
First up, subscription requests. When the user's server gets a "subscribe" stanza for the user from the contact (aka a presence stanza of type "subscribe"), it MUST deliver that request to the user for approval, but only if the user hasn't already given the contact access to their presence info AND there's no pending inbound subscription request already waiting. Even then, the user's server SHOULD NOT deliver the new request when there's already a pending one, since the previous request will have been recorded already π And if the user has already granted access, the user's server SHOULD auto-reply to that "subscribe" stanza by sending a "subscribed" stanza to the contact on behalf of the user. This is what lets the contact resynchronize the subscription state if they need to π These rules are summarized in the following table.
Table 3: Recommended handling of inbound "subscribe" stanzas
+------------------------------------------------------------------+
| EXISTING STATE | DELIVER? | NEW STATE |
+------------------------------------------------------------------+
| "None" | yes | "None + Pending In" |
| "None + Pending Out" | yes | "None + Pending Out/In" |
| "None + Pending In" | no | no state change |
| "None + Pending Out/In" | no | no state change |
| "To" | yes | "To + Pending In" |
| "To + Pending In" | no | no state change |
| "From" | no * | no state change |
| "From + Pending Out" | no * | no state change |
| "Both" | no * | no state change |
+------------------------------------------------------------------+
- Server SHOULD auto-reply with "subscribed" stanza
Next, the "unsubscribe" stanzas. When the user's server gets one for the user from the contact, and it actually causes a subscription state change from the user's perspective, the user's server SHOULD auto-reply by sending an "unsubscribed" stanza to the contact on behalf of the user. It MUST also deliver the "unsubscribe" stanza to the user, and MUST change the state. If no state change happens, the user's server SHOULD NOT deliver the stanza and MUST NOT change the state. Tidy π These rules are summarized in the following table.
Table 4: Recommended handling of inbound "unsubscribe" stanzas
+------------------------------------------------------------------+
| EXISTING STATE | DELIVER? | NEW STATE |
+------------------------------------------------------------------+
| "None" | no | no state change |
| "None + Pending Out" | no | no state change |
| "None + Pending In" | yes * | "None" |
| "None + Pending Out/In" | yes * | "None + Pending Out" |
| "To" | no | no state change |
| "To + Pending In" | yes * | "To" |
| "From" | yes * | "None" |
| "From + Pending Out" | yes * | "None + Pending Out |
| "Both" | yes * | "To" |
+------------------------------------------------------------------+
- Server SHOULD auto-reply with "unsubscribed" stanza
Now the "subscribed" stanzas coming in. When the user's server gets one for the user from the contact, it MUST NOT deliver the stanza to the user and MUST NOT change the subscription state if there's no pending outbound request for access to the contact's presence info. If there is a pending outbound request and the "subscribed" stanza causes a subscription state change, the user's server MUST deliver the stanza to the user and MUST change the subscription state π And if the user already has access to the contact's presence info, the "subscribed" stanza doesn't cause a state change. So the user's server SHOULD NOT deliver it to the user and MUST NOT change the subscription state. These rules are summarized in the following table.
Table 5: Recommended handling of inbound "subscribed" stanzas
+------------------------------------------------------------------+
| EXISTING STATE | DELIVER? | NEW STATE |
+------------------------------------------------------------------+
| "None" | no | no state change |
| "None + Pending Out" | yes | "To" |
| "None + Pending In" | no | no state change |
| "None + Pending Out/In" | yes | "To + Pending In" |
| "To" | no | no state change |
| "To + Pending In" | no | no state change |
| "From" | no | no state change |
| "From + Pending Out" | yes | "Both" |
| "Both" | no | no state change |
+------------------------------------------------------------------+
Last but not least, inbound "unsubscribed" stanzas. When the user's server gets one for the user from the contact, it MUST deliver the stanza to the user and MUST change the subscription state if there's a pending outbound request for access to the contact's presence info, OR if the user currently has access to the contact's presence info. Otherwise, the user's server SHOULD NOT deliver the stanza and MUST NOT change the subscription state. These rules are summarized in the following table.
Table 6: Recommended handling of inbound "unsubscribed" stanzas
+------------------------------------------------------------------+
| EXISTING STATE | DELIVER? | NEW STATE |
+------------------------------------------------------------------+
| "None" | no | no state change |
| "None + Pending Out" | yes | "None" |
| "None + Pending In" | no | no state change |
| "None + Pending Out/In" | yes | "None + Pending In" |
| "To" | yes | "None" |
| "To + Pending In" | yes | "None + Pending In" |
| "From" | no | no state change |
| "From + Pending Out" | yes | "From" |
| "Both" | yes | "From" |
+------------------------------------------------------------------+
9.4. Server Delivery and Client Acknowledgement of Subscription Requests and State Change Notifications
okay so when a server gets an inbound presence stanza of type "subscribe" (aka a subscription request π) or of type "subscribed", "unsubscribe", or "unsubscribed" (aka a subscription state change notification β¨), it has a whole checklist π. First, on top of everything else, it sends the appropriate roster push (or the updated roster, if an available resource asks for it later π§). Then, and this part is not optional, it MUST deliver the request or notification to the intended recipient at least once π«Ά.
A server MAY require the recipient to acknowledge receipt of all state change notifications, and it MUST require acknowledgement for subscription requests (aka presence stanzas of type "subscribe") π. To actually require it, a server SHOULD send the request or notification to the recipient each time the recipient logs in, until the recipient acknowledges it by "affirming" or "denying" it. Here's the cheat sheet π
Table 7: Acknowledgement of subscription state change notifications
+--------------------------------------------------+
| STANZA TYPE | ACCEPT | DENY |
+--------------------------------------------------+
| subscribe | subscribed | unsubscribed |
| subscribed | subscribe | unsubscribe |
| unsubscribe | unsubscribed | subscribed |
| unsubscribed | unsubscribe | subscribe |
+--------------------------------------------------+
Obviously (duh π ), given the subscription state charts above, some of the acknowledgement stanzas get routed to the contact and cause subscription state changes, and some don't. However, any such stanzas MUST result in the server no longer sending the subscription state notification to the user π.
Also, a user's server MUST automatically generate outbound presence stanzas of type "unsubscribe" and "unsubscribed" whenever it gets a roster set with the 'subscription' attribute set to "remove" (see Removing a Roster Item and Cancelling All Subscriptions (Section 8.6)) ποΈ. So the server MUST treat a roster remove request as equivalent to sending both of those presence stanzas when it decides whether to keep sending subscription state change notifications of type "subscribe" or "subscribed" to the user π§ β¨.
10. Blocking Communication π«
Most instant messaging systems have found it necessary to give users a way to block communications from particular other users (this is also required by sections 5.1.5, 5.1.15, 5.3.2, and 5.4.10 of [IMP-REQS]). In XMPP, this is done by managing your privacy lists using the 'jabber:iq:privacy' namespace π.
Server-side privacy lists let you pull off the following use cases:
- Retrieving one's privacy lists.
- Adding, removing, and editing one's privacy lists.
- Setting, changing, or declining active lists.
- Setting, changing, or declining the default list (i.e., the list that is active by default).
- Allowing or blocking messages based on JID, group, or subscription type (or globally).
- Allowing or blocking inbound presence notifications based on JID, group, or subscription type (or globally).
- Allowing or blocking outbound presence notifications based on JID, group, or subscription type (or globally).
- Allowing or blocking IQ stanzas based on JID, group, or subscription type (or globally).
- Allowing or blocking all communications based on JID, group, or subscription type (or globally).
Note: Presence notifications do not include presence subscriptions. They're only the presence information that gets broadcast to entities subscribed to a user's presence information. So this includes presence stanzas with no'type'attribute or of type'unavailable'only π.
10.1. Syntax and Semantics
A user MAY define one or more privacy lists, and they're stored by the user's server ποΈ. Each <list/> element holds one or more rules in the form of <item/> elements. Each <item/> element uses attributes to define a privacy rule type, a specific value the rule applies to, the relevant action, and the item's place in the processing order π.
Here's the syntax:
<iq>
<query xmlns='jabber:iq:privacy'>
<list name='foo'>
<item
type='[jid|group|subscription]'
value='bar'
action='[allow|deny]'
order='unsignedInt'>
[<message/>]
[<presence-in/>]
[<presence-out/>]
[<iq/>]
</item>
</list>
</query>
</iq>
If the type is "jid", then the 'value' attribute MUST contain a valid Jabber ID. JIDs SHOULD be matched in the following order:
<user@domain/resource>(only that resource matches)<user@domain>(any resource matches)<domain/resource>(only that resource matches)<domain>(the domain itself matches, as does any user@domain, domain/resource, or address containing a subdomain)
If the type is "group", then the 'value' attribute SHOULD contain the name of a group in the user's roster. (If a client tries to update, create, or delete a list item with a group that isn't in the user's roster, the server SHOULD return an <item-not-found/> stanza error to the client π
.)
If the type is "subscription", then the 'value' attribute MUST be one of "both", "to", "from", or "none", as defined under Roster Syntax and Semantics (Section 7.1). "none" includes entities that are totally unknown to the user and therefore not in the user's roster at all π».
If no 'type' attribute is included, the rule is the "fall-through" case π.
The 'action' attribute MUST be included, and its value MUST be either "allow" or "deny" β
β.
The 'order' attribute MUST be included, and its value MUST be a non-negative integer that is unique among all items in the list. (If a client tries to create or update a list with non-unique order values, the server MUST return a <bad-request/> stanza error to the client π¨.)
The <item/> element MAY contain one or more child elements that let an entity get more granular about which kinds of stanzas get blocked (instead of blocking all of them) π―. The allowable child elements are:
<message/>-- blocks incoming message stanzas<iq/>-- blocks incoming IQ stanzas<presence-in/>-- blocks incoming presence notifications<presence-out/>-- blocks outgoing presence notifications
Within the 'jabber:iq:privacy' namespace, the <query/> child of an IQ stanza of type "set" MUST NOT include more than one child element (i.e., the stanza MUST contain only one <active/> element, one <default/> element, or one <list/> element). If a sending entity breaks this rule, the receiving entity MUST return a <bad-request/> stanza error π
ββοΈ.
When a client adds or updates a privacy list, the <list/> element SHOULD contain at least one <item/> child element. When a client removes a privacy list, the <list/> element MUST NOT contain any <item/> child elements π§Ή.
When a client updates a privacy list, it must include all of the desired items (i.e., not a "delta") π.
10.2. Business Rules
- If there's an active list set for a session, it only affects the session(s) it's activated for, and only for the duration of those session(s). The server MUST apply the active list only and MUST NOT apply the default list (i.e., there is no "layering" of lists) π°.
- The default list applies to the user as a whole. It's processed if there's no active list set for the target session/resource a stanza is addressed to, or if there are no current sessions for the user π«§.
- If there's no active list set for a session (or there are no current sessions for the user), and there's no default list, then all stanzas SHOULD BE accepted or appropriately processed by the server on behalf of the user, in accordance with the Server Rules for Handling XML Stanzas (Section 11) πΈ.
- Privacy lists MUST be the first delivery rule a server applies. They supersede (1) the routing and delivery rules in Server Rules for Handling XML Stanzas (Section 11), and (2) the handling of subscription-related presence stanzas (and the corresponding generation of roster pushes) specified in Integration of Roster Items and Presence Subscriptions (Section 8) π.
- The order in which privacy list items are processed is important π’. List items MUST be processed in ascending order, determined by the integer values of the
'order'attribute for each<item/>. - As soon as a stanza is matched against a privacy list rule, the server MUST handle the stanza appropriately, in accordance with the rule, and stop processing π.
- If no fall-through item is provided in a list, the fall-through action is assumed to be
"allow"π. - If a user updates the definition of an active list, subsequent processing based on that active list MUST use the updated definition (for all resources that the active list currently applies to) π.
- If a change to the subscription state or roster group of a roster item defined in an active or default list happens during a user's session, subsequent processing based on that list MUST take the changed state or group into account (for all resources that the list currently applies to) π§©.
- When the definition of a rule is modified, the server MUST send an IQ stanza of type
"set"to all connected resources. It contains a<query/>element with only one<list/>child element, where the'name'attribute is set to the name of the modified privacy list π£. These "privacy list pushes" follow the same semantics as the "roster pushes" used in roster management, except that only the list name itself (not the full list definition or the "delta") is pushed to the connected resources. It's up to the receiving resource to decide whether to retrieve the modified list definition, although a connected resource SHOULD do so if the list currently applies to it π«Ά. - When a resource tries to remove a list, or to specify a new default list, while that list applies to a connected resource other than the sending resource, the server MUST return a
<conflict/>error to the sending resource and MUST NOT make the requested change π§π .
10.3. Retrieving One's Privacy Lists β¨
okay so first things first, romeo asks his server what privacy lists he's got π bestie this is giving organized
<iq from='romeo@example.net/orchard' type='get' id='getlist1'>
<query xmlns='jabber:iq:privacy'/>
</iq>
and the server replies with the names of his lists, with the active list and the default list shown first π
<iq type='result' id='getlist1' to='romeo@example.net/orchard'>
<query xmlns='jabber:iq:privacy'>
<active name='private'/>
<default name='public'/>
<list name='public'/>
<list name='private'/>
<list name='special'/>
</query>
</iq>
next he asks for one specific list π¦
<iq from='romeo@example.net/orchard' type='get' id='getlist2'>
<query xmlns='jabber:iq:privacy'>
<list name='public'/>
</query>
</iq>
and the server sends it back, items and all β¨
<iq type='result' id='getlist2' to='romeo@example.net/orchard'>
<query xmlns='jabber:iq:privacy'>
<list name='public'>
<item type='jid'
value='tybalt@example.com'
action='deny'
order='1'/>
<item action='allow' order='2'/>
</list>
</query>
</iq>
now he wants the 'private' list πΈ
<iq from='romeo@example.net/orchard' type='get' id='getlist3'>
<query xmlns='jabber:iq:privacy'>
<list name='private'/>
</query>
</iq>
and the server delivers it π§
<iq type='result' id='getlist3' to='romeo@example.net/orchard'>
<query xmlns='jabber:iq:privacy'>
<list name='private'>
<item type='subscription'
value='both'
action='allow'
order='10'/>
<item action='deny' order='15'/>
</list>
</query>
</iq>
then he asks for the 'special' list π
<iq from='romeo@example.net/orchard' type='get' id='getlist4'>
<query xmlns='jabber:iq:privacy'>
<list name='special'/>
</query>
</iq>
and the server sends that one too π
<iq type='result' id='getlist4' to='romeo@example.net/orchard'>
<query xmlns='jabber:iq:privacy'>
<list name='special'>
<item type='jid'
value='juliet@example.com'
action='allow'
order='6'/>
<item type='jid'
value='benvolio@example.org'
action='allow'
order='7'/>
<item type='jid'
value='mercutio@example.org'
action='allow'
order='42'/>
<item action='deny' order='666'/>
</list>
</query>
</iq>
so here's the tea π« romeo has three lists in this example! 'public' lets in everyone except one specific entity (and it's his default list π ), 'private' only lets in people with a bidirectional subscription, aka his mutual besties (and it's his active list π«Ά), and 'special' only lets in three specific entities π
if romeo tries to retrieve a list that doesn't exist, the server MUST return an <item-not-found/> stanza error to him π
<iq to='romeo@example.net/orchard' type='error' id='getlist5'>
<query xmlns='jabber:iq:privacy'>
<list name='The Empty Set'/>
</query>
<error type='cancel'>
<item-not-found
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
romeo can only retrieve one list at a time, no greedy behaviour allowed π if he tries to grab more than one list in the same request, the server MUST return a <bad request/> stanza error to him
<iq to='romeo@example.net/orchard' type='error' id='getlist6'>
<query xmlns='jabber:iq:privacy'>
<list name='public'/>
<list name='private'/>
<list name='special'/>
</query>
<error type='modify'>
<bad-request
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
10.4. Managing Active Lists β¨
okay so if romeo wants to set or change the list that the server is currently applying, he MUST send an IQ stanza of type "set" with a <query/> element in the 'jabber:iq:privacy' namespace. that query has to contain an empty <active/> child with a 'name' attribute set to the list name he wants π¦
<iq from='romeo@example.net/orchard' type='set' id='active1'>
<query xmlns='jabber:iq:privacy'>
<active name='special'/>
</query>
</iq>
the server MUST activate and apply the requested list BEFORE it sends the result back to the client π so romeo's change is already live by the time he hears about it
<iq type='result' id='active1' to='romeo@example.net/orchard'/>
if romeo tries to set an active list that doesn't exist, the server MUST return an <item-not-found/> stanza error to him π
<iq to='romeo@example.net/orchard' type='error' id='active2'>
<query xmlns='jabber:iq:privacy'>
<active name='The Empty Set'/>
</query>
<error type='cancel'>
<item-not-found
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
and if romeo wants to stop using any active list at all, the connected resource MUST send an empty <active/> element with no 'name' attribute πΈ
<iq from='romeo@example.net/orchard' type='set' id='active3'>
<query xmlns='jabber:iq:privacy'>
<active/>
</query>
</iq>
and the server confirms β¨
<iq type='result' id='active3' to='romeo@example.net/orchard'/>
10.5. Managing the Default List β¨
so the default list is special because it applies to romeo as a whole, not just the resource that sends the request π to change it, he MUST send an IQ stanza of type "set" with a <query/> element in the 'jabber:iq:privacy' namespace, containing an empty <default/> child with a 'name' attribute set to the list name he wants
<iq from='romeo@example.net/orchard' type='set' id='default1'>
<query xmlns='jabber:iq:privacy'>
<default name='special'/>
</query>
</iq>
and the server acknowledges the change π
<iq type='result' id='default1' to='romeo@example.net/orchard'/>
but here's the drama π if romeo tries to change the default list while at least one other connected resource is still using it, the server MUST return a <conflict/> stanza error to the sending resource
<iq to='romeo@example.net/orchard' type='error' id='default1'>
<query xmlns='jabber:iq:privacy'>
<default name='special'/>
</query>
<error type='cancel'>
<conflict
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
if romeo tries to set a default list that doesn't exist, the server MUST return an <item-not-found/> stanza error to him π
<iq to='romeo@example.net/orchard' type='error' id='default1'>
<query xmlns='jabber:iq:privacy'>
<default name='The Empty Set'/>
</query>
<error type='cancel'>
<item-not-found
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
now if romeo wants to stop using a default list altogether (i.e., to rely on the domain's stanza routing rules at all times), he MUST send an empty <default/> element with no 'name' attribute π¦
<iq from='romeo@example.net/orchard' type='set' id='default2'>
<query xmlns='jabber:iq:privacy'>
<default/>
</query>
</iq>
and the server acknowledges that too β¨
<iq type='result' id='default2' to='romeo@example.net/orchard'/>
last one in this bit π
if one connected resource tries to decline the default list for romeo as a whole, but that default list still applies to at least one other connected resource, the server MUST return a <conflict/> error to the sending resource
<iq to='romeo@example.net/orchard' type='error' id='default3'>
<query xmlns='jabber:iq:privacy'>
<default/>
</query>
<error type='cancel'>
<conflict
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
10.6. Editing a Privacy List β¨
okay so to edit a privacy list, romeo MUST send an IQ stanza of type "set" with a <query/> element in the 'jabber:iq:privacy' namespace. that query has to contain exactly one <list/> child whose 'name' attribute is the list he wants to edit π the <list/> element MUST contain one or more <item/> elements, and they describe his desired changes by including every element in the list, not just the "delta" π
<iq from='romeo@example.net/orchard' type='set' id='edit1'>
<query xmlns='jabber:iq:privacy'>
<list name='public'>
<item type='jid'
value='tybalt@example.com'
action='deny'
order='3'/>
<item type='jid'
value='paris@example.org'
action='deny'
order='5'/>
<item action='allow' order='68'/>
</list>
</query>
</iq>
and the server acknowledges the edit β¨
<iq type='result' id='edit1' to='romeo@example.net/orchard'/>
Note: the order value for any item is NOT fixed, okay? so in that example, if romeo wants to squeeze 4 items between the "tybalt@example.com" item and the "paris@example.org" item, his client MUST renumber the relevant items before it submits the list to the server π§
after that, the server MUST now send a "privacy list push" to all connected resources π£
<iq to='romeo@example.net/orchard' type='set' id='push1'>
<query xmlns='jabber:iq:privacy'>
<list name='public'/>
</query>
</iq>
<iq to='romeo@example.net/home' type='set' id='push2'>
<query xmlns='jabber:iq:privacy'>
<list name='public'/>
</query>
</iq>
and per the IQ rules in [XMPP-CORE], each connected resource MUST also return an IQ result to the server π«Ά
<iq from='romeo@example.net/orchard'
type='result'
id='push1'/>
<iq from='romeo@example.net/home'
type='result'
id='push2'/>
10.7. Adding a New Privacy List π
the same protocol used to edit an existing list also creates a brand new one π¦ if the list name matches an existing list, the new request overwrites the old one, so be careful bestie π just like with list edits, the server MUST also send a "privacy list push" to all connected resources π
10.8. Removing a Privacy List ποΈ
okay so bestie, if you wanna remove a privacy list, you MUST send an IQ stanza of type set with a <query/> element in the jabber:iq:privacy namespace. That query has to contain one empty <list/> child with a name attribute set to whatever list you want gone π
Example: Client removes a privacy list:
<iq from='romeo@example.net/orchard' type='set' id='remove1'>
<query xmlns='jabber:iq:privacy'>
<list name='private'/>
</query>
</iq>
Example: Server acknowledges success of list removal:
<iq type='result' id='remove1' to='romeo@example.net/orchard'/>
the server doesn't just let you yeet any list whenever π
ββοΈ if you try to remove a list that's currently being applied to at least one resource other than the one sending the request, the server MUST return a <conflict/> stanza error to you. like, literally, you MUST first set a different list to active or default before you can remove it (aka the list has to be off the table first π). if you try to remove a list that doesn't even exist by that name, the server MUST return an <item-not-found/> stanza error. and if you try to remove more than one list in the same request, the server MUST return a <bad request/> stanza error. big no-no energy π
10.9. Blocking Messages π«π¬
okay so server-side privacy lists let you block incoming messages from other entities based on their JID, their roster group, or their subscription status, or you can just block everything globally π«Ά the examples below show how it works. (Note: to keep things short, IQ stanzas of type "result" aren't shown in these examples, and neither are "privacy list pushes".)
Example: User blocks based on JID:
<iq from='romeo@example.net/orchard' type='set' id='msg1'>
<query xmlns='jabber:iq:privacy'>
<list name='message-jid-example'>
<item type='jid'
value='tybalt@example.com'
action='deny'
order='3'>
<message/>
</item>
</list>
</query>
</iq>
once you create and apply that list, you won't get messages from the entity with that JID π ββοΈ tybalt is blocked, periodt π
Example: User blocks based on roster group:
<iq from='romeo@example.net/orchard' type='set' id='msg2'>
<query xmlns='jabber:iq:privacy'>
<list name='message-group-example'>
<item type='group'
value='Enemies'
action='deny'
order='4'>
<message/>
</item>
</list>
</query>
</iq>
after you apply this one, you won't get messages from anyone in the roster group you picked (here, "Enemies") π«£ the group is giving full no-fly zone vibes πΈ
Example: User blocks based on subscription type:
<iq from='romeo@example.net/orchard' type='set' id='msg3'>
<query xmlns='jabber:iq:privacy'>
<list name='message-sub-example'>
<item type='subscription'
value='none'
action='deny'
order='5'>
<message/>
</item>
</list>
</query>
</iq>
with this list in place, you won't get messages from anybody who has the subscription type you specified (in this case, none) β¨ it's like a vibe check, but make it protocol π§
Example: User blocks globally:
<iq from='romeo@example.net/orchard' type='set' id='msg4'>
<query xmlns='jabber:iq:privacy'>
<list name='message-global-example'>
<item action='deny' order='6'>
<message/>
</item>
</list>
</query>
</iq>
and if you go full global, you won't get messages from any other users at all π that's a whole main character move, no cap π
10.10. Blocking Inbound Presence Notifications π
okay so same deal, but for presence notifications: server-side privacy lists let you block incoming presence notifications from other entities based on JID, roster group, or subscription status, or globally π the examples are right here.
Note: presence notifications don't include presence subscriptions. they're only the presence info that gets broadcast to you because you're already subscribed to a contact's presence. so this covers presence stanzas with no type attribute or with type='unavailable' only π§
Example: User blocks based on JID:
<iq from='romeo@example.net/orchard' type='set' id='presin1'>
<query xmlns='jabber:iq:privacy'>
<list name='presin-jid-example'>
<item type='jid'
value='tybalt@example.com'
action='deny'
order='7'>
<presence-in/>
</item>
</list>
</query>
</iq>
after applying this list, you won't get presence notifications from the entity with that JID π tybalt's presence is officially invisible to you π¦
Example: User blocks based on roster group:
<iq from='romeo@example.net/orchard' type='set' id='presin2'>
<query xmlns='jabber:iq:privacy'>
<list name='presin-group-example'>
<item type='group'
value='Enemies'
action='deny'
order='8'>
<presence-in/>
</item>
</list>
</query>
</iq>
with this one, presence notifications from anyone in the specified roster group are blocked π« "Enemies" really said "you're not welcome in my status bar" π
Example: User blocks based on subscription type:
<iq from='romeo@example.net/orchard' type='set' id='presin3'>
<query xmlns='jabber:iq:privacy'>
<list name='presin-sub-example'>
<item type='subscription'
value='to'
action='deny'
order='9'>
<presence-in/>
</item>
</list>
</query>
</iq>
with this list applied, you won't get presence notifications from any entities with the specified subscription type (here, to) π very selective, very iconic
Example: User blocks globally:
<iq from='romeo@example.net/orchard' type='set' id='presin4'>
<query xmlns='jabber:iq:privacy'>
<list name='presin-global-example'>
<item action='deny' order='11'>
<presence-in/>
</item>
</list>
</query>
</iq>
and globally? you won't get presence notifications from any other users π radio silence, bestie π€«
10.11. Blocking Outbound Presence Notifications π€
okay so this one flips it around: server-side privacy lists let you block outgoing presence notifications to other entities, based on their JID, roster group, or subscription status, or globally π¦ here's how it looks.
Note: presence notifications don't include presence subscriptions. these are only the presence info that gets broadcast to your contacts because they're already subscribed to your presence. so this covers presence stanzas with no type attribute or with type='unavailable' only π§
Example: User blocks based on JID:
<iq from='romeo@example.net/orchard' type='set' id='presout1'>
<query xmlns='jabber:iq:privacy'>
<list name='presout-jid-example'>
<item type='jid'
value='tybalt@example.com'
action='deny'
order='13'>
<presence-out/>
</item>
</list>
</query>
</iq>
after you create and apply this, you won't send presence notifications to the entity with that JID π ββοΈ tybalt is not getting a single status update from you π
Example: User blocks based on roster group:
<iq from='romeo@example.net/orchard' type='set' id='presout2'>
<query xmlns='jabber:iq:privacy'>
<list name='presout-group-example'>
<item type='group'
value='Enemies'
action='deny'
order='15'>
<presence-out/>
</item>
</list>
</query>
</iq>
with this applied, you won't send presence notifications to anyone in that roster group π«£ the "Enemies" group is on read-only, no updates for them π
Example: User blocks based on subscription type:
<iq from='romeo@example.net/orchard' type='set' id='presout3'>
<query xmlns='jabber:iq:privacy'>
<list name='presout-sub-example'>
<item type='subscription'
value='from'
action='deny'
order='17'>
<presence-out/>
</item>
</list>
</query>
</iq>
once this list is live, you won't send presence notifications to any entities with that subscription type (here, from) β¨ you're curating who gets the tea π
Example: User blocks globally:
<iq from='romeo@example.net/orchard' type='set' id='presout4'>
<query xmlns='jabber:iq:privacy'>
<list name='presout-global-example'>
<item action='deny' order='23'>
<presence-out/>
</item>
</list>
</query>
</iq>
global mode means you won't send presence notifications to any other users at all π going dark on purpose is a whole mood, ngl π
10.12. Blocking IQ Stanzas π§±
okay so server-side privacy lists also let you block incoming IQ stanzas from other entities, based on JID, roster group, or subscription status, or globally π examples incoming.
Example: User blocks based on JID:
<iq from='romeo@example.net/orchard' type='set' id='iq1'>
<query xmlns='jabber:iq:privacy'>
<list name='iq-jid-example'>
<item type='jid'
value='tybalt@example.com'
action='deny'
order='29'>
<iq/>
</item>
</list>
</query>
</iq>
once this is applied, you won't get IQ stanzas from the entity with that JID π tybalt's requests just bounce off π
Example: User blocks based on roster group:
<iq from='romeo@example.net/orchard' type='set' id='iq2'>
<query xmlns='jabber:iq:privacy'>
<list name='iq-group-example'>
<item type='group'
value='Enemies'
action='deny'
order='31'>
<iq/>
</item>
</list>
</query>
</iq>
with this in place, you won't get IQ stanzas from anybody in the specified roster group π« "Enemies" can knock all they want, nobody's answering πͺ
Example: User blocks based on subscription type:
<iq from='romeo@example.net/orchard' type='set' id='iq3'>
<query xmlns='jabber:iq:privacy'>
<list name='iq-sub-example'>
<item type='subscription'
value='none'
action='deny'
order='17'>
<iq/>
</item>
</list>
</query>
</iq>
after you apply this list, you won't get IQ stanzas from any entities with that subscription type (here, none) π very targeted, very chic
Example: User blocks globally:
<iq from='romeo@example.net/orchard' type='set' id='iq4'>
<query xmlns='jabber:iq:privacy'>
<list name='iq-global-example'>
<item action='deny' order='1'>
<iq/>
</item>
</list>
</query>
</iq>
and if you go global with it, you won't get IQ stanzas from any other users πΈ total lockdown, and honestly? iconic π β¨
10.13. Blocking All Communication π«β¨
okay so server-side privacy lists let a user block literally every stanza going to and from other entities, based on their JID, roster group, or subscription status (or globally, like, everything π). heads up: this includes subscription-related presence stanzas, which are excluded by Blocking Inbound Presence Notifications (Section 10.10). here are some examples π
Example: User blocks based on JID:
<iq from='romeo@example.net/orchard' type='set' id='all1'>
<query xmlns='jabber:iq:privacy'>
<list name='all-jid-example'>
<item type='jid'
value='tybalt@example.com'
action='deny'
order='23'/>
</list>
</query>
</iq>
after creating and applying that list, romeo won't get any communications from tybalt, and he won't send any stanzas to him either. π ββοΈ
Example: User blocks based on roster group:
<iq from='romeo@example.net/orchard' type='set' id='all2'>
<query xmlns='jabber:iq:privacy'>
<list name='all-group-example'>
<item type='group'
value='Enemies'
action='deny'
order='13'/>
</list>
</query>
</iq>
with that list active, romeo won't get anything from anyone in the Enemies roster group, and he won't send them stanzas either. the group is literally the vibe check π«
Example: User blocks based on subscription type:
<iq from='romeo@example.net/orchard' type='set' id='all3'>
<query xmlns='jabber:iq:privacy'>
<list name='all-sub-example'>
<item type='subscription'
value='none'
action='deny'
order='11'/>
</list>
</query>
</iq>
once this list is created and applied, romeo won't talk to any entities with that subscription type. π¦
Example: User blocks globally:
<iq from='romeo@example.net/orchard' type='set' id='all4'>
<query xmlns='jabber:iq:privacy'>
<list name='all-global-example'>
<item action='deny' order='7'/>
</list>
</query>
</iq>
this one is the nuclear option π₯. after creating and applying it, romeo won't receive communications from any other users, and he won't send them stanzas either. main character energy, but make it lonely π
10.14. Blocked Entity Attempts to Communicate with User π
if a blocked entity tries to send message or presence stanzas to the user, the user's server SHOULD silently drop the stanza, and it MUST NOT return an error to the sending entity. the blocked entity just gets ghosted, no notification, no drama π»
if a blocked entity tries to send an IQ stanza of type "get" or "set" to the user, the user's server MUST return a <service-unavailable/> stanza error to the sending entity. that's because it's the standard error code a client sends when it doesn't understand the namespace of an IQ get or set. IQ stanzas of other types SHOULD be silently dropped by the server. π«Ά
Example: Blocked entity attempts to send IQ get:
<iq type='get'
to='romeo@example.net'
from='tybalt@example.com/pda'
id='probing1'>
<query xmlns='jabber:iq:version'/>
</iq>
Example: Server returns error to blocked entity:
<iq type='error'
from='romeo@example.net'
to='tybalt@example.com/pda'
id='probing1'>
<query xmlns='jabber:iq:version'/>
<error type='cancel'>
<service-unavailable
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
10.15. Higher-Level Heuristics π§
when you're building a representation of a higher-level privacy heuristic, a client SHOULD use the simplest possible representation. bestie, keep it simple π
for example, the heuristic "block all communications with any user not in my roster" could be built in any of these ways:
- allow communications from all JIDs in my roster (i.e., listing each JID as a separate list item), but block communications with everyone else
- allow communications from any user who is in one of the groups that make up my roster (i.e., listing each group as a separate list item), but block communications from everyone else
- allow communications from any user with whom I have a subscription of 'both' or 'to' or 'from' (i.e., listing each subscription value separately), but block communications from everyone else
- block communications from anyone whose subscription state is 'none'
the final representation is the simplest one and SHOULD be used πΈ. here's the XML that would get sent in this case:
<iq type='set' id='heuristic1'>
<query xmlns='jabber:iq:privacy'>
<list name='heuristic-example'>
<item type='subscription'
value='none'
action='deny'
order='437'/>
</list>
</query>
</iq>
11. Server Rules for Handling XML Stanzas π
basic routing and delivery rules for servers are defined in [XMPP-CORE]. this section adds extra rules for XMPP-compliant instant messaging and presence servers, so pay attention π
11.1. Inbound Stanzas π₯
if the hostname of the domain identifier portion of the JID in an inbound stanza's 'to' attribute matches a hostname of the server itself, and the JID in that 'to' attribute is <user@example.com> or <user@example.com/resource>, the server MUST first apply any privacy lists (Section 10) that are in force. then it follows the rules below:
- if the JID is
<user@domain/resource>and an available resource matches the full JID, the recipient's server MUST deliver the stanza to that resource. π― - else if the JID is
<user@domain>or<user@domain/resource>and the associated user account does not exist, the recipient's server (a) SHOULD silently ignore the stanza (i.e., neither deliver it nor return an error) if it is a presence stanza, (b) MUST return a<service-unavailable/>stanza error to the sender if it is an IQ stanza, and (c) SHOULD return a<service-unavailable/>stanza error to the sender if it is a message stanza. π - else if the JID is
<user@domain/resource>and no available resource matches the full JID, the recipient's server (a) SHOULD silently ignore the stanza (i.e., neither deliver it nor return an error) if it is a presence stanza, (b) MUST return a<service-unavailable/>stanza error to the sender if it is an IQ stanza, and (c) SHOULD treat the stanza as if it were addressed to<user@domain>if it is a message stanza. π - else if the JID is
<user@domain>and there is at least one available resource for the user, the recipient's server MUST follow these rules:- for message stanzas, the server SHOULD deliver the stanza to the highest-priority available resource (if the resource did not provide a value for the
<priority/>element, the server SHOULD consider it to have provided a value of zero). if two or more available resources have the same priority, the server MAY use some other rule (e.g., most recent connect time, most recent activity time, or highest availability as determined by some hierarchy of<show/>values) to choose between them, or MAY deliver the message to all such resources. however, the server MUST NOT deliver the stanza to an available resource with a negative priority. if the only available resource has a negative priority, the server SHOULD handle the message as if there were no available resources (defined below). in addition, the server MUST NOT rewrite the 'to' attribute (i.e., it MUST leave it as<user@domain>rather than change it to<user@domain/resource>). π§ - for presence stanzas other than those of type "probe", the server MUST deliver the stanza to all available resources. for presence probes, the server SHOULD reply based on the rules defined in Presence Probes (Section 5.1.3). in addition, the server MUST NOT rewrite the 'to' attribute (i.e., it MUST leave it as
<user@domain>rather than change it to<user@domain/resource>). π‘ - for IQ stanzas, the server itself MUST reply on behalf of the user with either an IQ result or an IQ error, and MUST NOT deliver the IQ stanza to any of the available resources. specifically, if the semantics of the qualifying namespace define a reply that the server can provide, the server MUST reply to the stanza on behalf of the user. if not, the server MUST reply with a
<service-unavailable/>stanza error. π€
- for message stanzas, the server SHOULD deliver the stanza to the highest-priority available resource (if the resource did not provide a value for the
- else if the JID is
<user@domain>and there are no available resources associated with the user, how the stanza is handled depends on the stanza type:- for presence stanzas of type "subscribe", "subscribed", "unsubscribe", and "unsubscribed", the server MUST maintain a record of the stanza and deliver the stanza at least once (i.e., when the user next creates an available resource). in addition, the server MUST continue to deliver presence stanzas of type "subscribe" until the user either approves or denies the subscription request (see also Presence Subscriptions (Section 5.1.6)). π
- for all other presence stanzas, the server SHOULD silently ignore the stanza by not storing it for later delivery or replying to it on behalf of the user. π€«
- for message stanzas, the server MAY choose to store the stanza on behalf of the user and deliver it when the user next becomes available, or forward the message to the user via some other means (e.g., to the user's email account). however, if offline message storage or message forwarding is not enabled, the server MUST return a
<service-unavailable/>stanza error to the sender. (Note: Offline message storage and message forwarding are not defined in XMPP, since they are strictly a matter of implementation and service provisioning.) π¬ - for IQ stanzas, the server itself MUST reply on behalf of the user with either an IQ result or an IQ error. specifically, if the semantics of the qualifying namespace define a reply that the server can provide, the server MUST reply to the stanza on behalf of the user. if not, the server MUST reply with a
<service-unavailable/>stanza error. π«
11.2. Outbound Stanzas β¨
okay so if the domain part of the address in an outbound stanza's to attribute matches one of the server's own hostnames, the server MUST deliver it to a local entity by following the rules for Inbound Stanzas (Section 11.1). π π
If that domain part does NOT match any of the server's hostnames, the server MUST attempt to route the stanza to the foreign domain. π The recommended order of actions goes like this:
- First, resolve the foreign hostname with an [SRV] Service of "xmpp-server" and a Proto of "tcp". You'll get records like
_xmpp-server._tcp.example.com., as specified in [XMPP-CORE]. π - If the "xmpp-server" address record resolution fails, try resolving the "_im" or "_pres" [SRV] Service, as specified in [IMP-SRV]. Use "_im" for <message/> stanzas and "_pres" for <presence/> stanzas. (How to handle <iq/> stanzas is up to the implementation π€·ββοΈ.) This gives you one or more resolutions like
_im.<proto>.example.com.or_pres.<proto>.example.com.. Here,<proto>is a label from the Instant Messaging SRV Protocol Label registry or the Presence SRV Protocol Label registry: either "_xmpp" for an XMPP-aware domain, or some other IANA-registered label (for example "_simple") for a non-XMPP-aware domain. - If BOTH SRV address record resolutions fail, do a normal IPv4/IPv6 address record resolution to get the IP address, using the "xmpp-server" port of 5269 registered with the IANA, as specified in [XMPP-CORE]. π―
Administrators of server deployments are strongly encouraged to keep the _im._xmpp, _pres._xmpp, and _xmpp._tcp SRV records properly synchronized π. The reason is that different implementations might perform the "_im" and "_pres" lookups before the "xmpp-server" lookup, so the records need to agree.
12. IM and Presence Compliance Requirements π
This section sums up the specific parts of the Extensible Messaging and Presence Protocol that instant messaging and presence servers and clients MUST support in order to count as compliant implementations. π Every one of these applications MUST comply with the requirements in [XMPP-CORE]. This section adds extra compliance requirements for instant messaging and presence servers and clients, and note well: these requirements supplement the core ones but do NOT supersede them. π
Also, a server or client MAY support only presence or only instant messaging. It doesn't have to support both if all you want is one of those services. π§
12.1. Servers
On top of the core server compliance requirements, an instant messaging and presence server MUST also support the following:
- All server-related instant messaging and presence syntax and semantics defined in this document. That includes presence broadcast on behalf of clients, presence subscriptions, roster storage and manipulation, privacy lists, and IM-specific routing and delivery rules. ποΈ
12.2. Clients
On top of the core client compliance requirements, an instant messaging and presence client MUST also support the following:
- Generation and handling of the IM-specific semantics of XML stanzas, as defined by the XML schemas. This includes the 'type' attribute of message and presence stanzas, as well as their child elements. π¨
- All client-related instant messaging syntax and semantics defined in this document. That covers presence subscriptions, roster management, and privacy lists. π
- End-to-end object encryption, as defined in End-to-End Object Encryption in the Extensible Messaging and Presence Protocol (XMPP) [XMPP-E2E]. π
A client MUST also handle addresses that are encoded as "im:" URIs, as specified in [CPIM]. It MAY do so by removing the "im:" scheme and handing address resolution over to the server, as described under Outbound Stanzas (Section 11.2). π«Ά
13. Internationalization Considerations π
For internationalization considerations, check the relevant section of [XMPP-CORE]. π
14. Security Considerations ππ
Core security considerations for XMPP are defined in the relevant section of [XMPP-CORE]. Please go read that one too, it matters. π
Extra considerations that only apply to instant messaging and presence applications of XMPP are spread through this memo. Specifically:
- When a server processes an inbound stanza of any kind whose intended recipient is a user associated with one of the server's hostnames, the server MUST first apply any privacy lists (Section 10) that are in force (see Server Rules for Handling XML Stanzas (Section 11)). π‘οΈ
- When a server processes an inbound presence stanza of type "probe" whose intended recipient is a user associated with one of the server's hostnames, the server MUST NOT reveal the user's presence information if the sender is an entity that is NOT authorized to receive it. Authorization is determined by presence subscriptions (see Client and Server Presence Responsibilities (Section 5.1)). π«π
- When a server processes an outbound presence stanza with no type or of type "unavailable", it MUST follow the rules defined under Client and Server Presence Responsibilities (Section 5.1). That way, presence information isn't broadcast to entities that aren't authorized to know it. π‘
- When a server generates an error stanza in response to a stanza for a user who does not exist, using the
<service-unavailable/>error condition helps protect against well-known dictionary attacks. That's because this is the same error condition returned in other situations too, like when the namespace of an IQ child element isn't understood, or when offline message storage or message forwarding isn't enabled for a domain. π΅οΈββοΈβ¨
15. IANA Considerations π
For a bunch of related IANA considerations, check the relevant section of [XMPP-CORE]. ποΈ
15.1. XML Namespace Name for Session Data
A URN sub-namespace for session-related data in the Extensible Messaging and Presence Protocol (XMPP) is defined as follows. (This namespace name follows the format defined in The IETF XML Registry [XML-REG].) πΈ
URI: urn:ietf:params:xml:ns:xmpp-session
Specification: RFC 3921
Description: This is the XML namespace name for session-related data
in the Extensible Messaging and Presence Protocol (XMPP) as
defined by RFC 3921.
Registrant Contact: IETF, XMPP Working Group, <xmppwg@jabber.org>
15.2. Instant Messaging SRV Protocol Label Registration
Address Resolution for Instant Messaging and Presence [IMP-SRV] defines an Instant Messaging SRV Protocol Label registry for protocols that can provide services conforming to the "_im" SRV Service label. XMPP is one of those protocols, so the IANA registers the "_xmpp" protocol label in the appropriate registry, like this: π§
Protocol label: _xmpp
Specification: RFC 3921
Description: Instant messaging protocol label for the Extensible
Messaging and Presence Protocol (XMPP) as defined by RFC 3921.
Registrant Contact: IETF, XMPP Working Group, <xmppwg@jabber.org>
15.3. Presence SRV Protocol Label Registration
Address Resolution for Instant Messaging and Presence [IMP-SRV] defines a Presence SRV Protocol Label registry for protocols that can provide services conforming to the "_pres" SRV Service label. XMPP is one of those protocols too, so the IANA registers the "_xmpp" protocol label in the appropriate registry, like this: π·
Protocol label: _xmpp
Specification: RFC 3921
Description: Presence protocol label for the Extensible Messaging and
Presence Protocol (XMPP) as defined by RFC 3921.
Registrant Contact: IETF, XMPP Working Group, <xmppwg@jabber.org>
16. References π
16.1. Normative References
[CPIM]: Peterson, J., "Common Profile for Instant Messaging (CPIM)", RFC 3860, August 2004. π¬[IMP-REQS]: Day, M., Aggarwal, S., Mohr, G., and J. Vincent, "Instant Messaging/Presence Protocol Requirements", RFC 2779, February 2000.[IMP-SRV]: Peterson, J., "Address Resolution for Instant Messaging and Presence", RFC 3861, August 2004.[SRV]: Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for specifying the location of services (DNS SRV)", RFC 2782, February 2000.[TERMS]: Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997. π[XML]: Bray, T., Paoli, J., Sperberg-McQueen, C., and E. Maler, "Extensible Markup Language (XML) 1.0 (2nd ed)", W3C REC-xml, October 2000,http://www.w3.org/TR/REC-xml.[XML-NAMES]: Bray, T., Hollander, D., and A. Layman, "Namespaces in XML", W3C REC-xml-names, January 1999,http://www.w3.org/TR/REC-xml-names.[XMPP-CORE]: Saint-Andre, P., "Extensible Messaging and Presence Protocol (XMPP): Core", RFC 3920, October 2004. π§[XMPP-E2E]: Saint-Andre, P., "End-to-End Object Encryption in the Extensible Messaging and Presence Protocol (XMPP)", RFC 3923, October 2004. π
16.2. Informative References
[IMP-MODEL]: Day, M., Rosenberg, J., and H. Sugano, "A Model for Presence and Instant Messaging", RFC 2778, February 2000. π[IRC]: Oikarinen, J. and D. Reed, "Internet Relay Chat Protocol", RFC 1459, May 1993. π¦[JEP-0054]: Saint-Andre, P., "vcard-temp", JSF JEP 0054, March 2003.[JEP-0077]: Saint-Andre, P., "In-Band Registration", JSF JEP 0077, August 2004.[JEP-0078]: Saint-Andre, P., "Non-SASL Authentication", JSF JEP 0078, July 2004.[JSF]: Jabber Software Foundation, "Jabber Software Foundation",http://www.jabber.org/.[VCARD]: Dawson, F. and T. Howes, "vCard MIME Directory Profile", RFC 2426, September 1998. π[XML-REG]: Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688, January 2004.
Appendix A. vCards πͺͺ
Sections 3.1.3 and 4.1.4 of [IMP-REQS] require that it be possible to retrieve out-of-band contact information for other users (for example, a telephone number or email address). βοΈ An XML representation of the vCard specification defined in RFC 2426 [VCARD] is in common use within the Jabber community to provide that information. It's out of scope for XMPP, though. Documentation for this protocol is in [JEP-0054], published by the Jabber Software Foundation [JSF]. π
Appendix B. XML Schemas π§Ύ
The following XML schemas are descriptive, NOT normative. For schemas defining the core features of XMPP, refer to [XMPP-CORE]. π
B.1. jabber:client β¨
Okay bestie, this is the schema for the jabber:client namespace, and it's where all the message, presence, iq, and error stanzas get their rules π
Here it is, exactly as written π
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='jabber:client'
xmlns='jabber:client'
elementFormDefault='qualified'>
<xs:import namespace='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<xs:element name='message'>
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs='0' maxOccurs='unbounded'>
<xs:element ref='subject'/>
<xs:element ref='body'/>
<xs:element ref='thread'/>
</xs:choice>
<xs:any namespace='##other'
minOccurs='0'
maxOccurs='unbounded'/>
<xs:element ref='error'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='from'
type='xs:string'
use='optional'/>
<xs:attribute name='id'
type='xs:NMTOKEN'
use='optional'/>
<xs:attribute name='to'
type='xs:string'
use='optional'/>
<xs:attribute name='type' use='optional' default='normal'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='chat'/>
<xs:enumeration value='error'/>
<xs:enumeration value='groupchat'/>
<xs:enumeration value='headline'/>
<xs:enumeration value='normal'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='body'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:string'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='subject'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:string'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='thread' type='xs:NMTOKEN'/>
<xs:element name='presence'>
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs='0' maxOccurs='unbounded'>
<xs:element ref='show'/>
<xs:element ref='status'/>
<xs:element ref='priority'/>
</xs:choice>
<xs:any namespace='##other'
minOccurs='0'
maxOccurs='unbounded'/>
<xs:element ref='error'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='from'
type='xs:string'
use='optional'/>
<xs:attribute name='id'
type='xs:NMTOKEN'
use='optional'/>
<xs:attribute name='to'
type='xs:string'
use='optional'/>
<xs:attribute name='type' use='optional'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='error'/>
<xs:enumeration value='probe'/>
<xs:enumeration value='subscribe'/>
<xs:enumeration value='subscribed'/>
<xs:enumeration value='unavailable'/>
<xs:enumeration value='unsubscribe'/>
<xs:enumeration value='unsubscribed'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='show'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='away'/>
<xs:enumeration value='chat'/>
<xs:enumeration value='dnd'/>
<xs:enumeration value='xa'/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name='status'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:string'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='priority' type='xs:byte'/>
<xs:element name='iq'>
<xs:complexType>
<xs:sequence>
<xs:any namespace='##other'
minOccurs='0'/>
<xs:element ref='error'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='from'
type='xs:string'
use='optional'/>
<xs:attribute name='id'
type='xs:NMTOKEN'
use='required'/>
<xs:attribute name='to'
type='xs:string'
use='optional'/>
<xs:attribute name='type' use='required'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='error'/>
<xs:enumeration value='get'/>
<xs:enumeration value='result'/>
<xs:enumeration value='set'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='error'>
<xs:complexType>
<xs:sequence xmlns:err='urn:ietf:params:xml:ns:xmpp-stanzas'>
<xs:group ref='err:stanzaErrorGroup'/>
<xs:element ref='err:text'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='code' type='xs:byte' use='optional'/>
<xs:attribute name='type' use='required'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='auth'/>
<xs:enumeration value='cancel'/>
<xs:enumeration value='continue'/>
<xs:enumeration value='modify'/>
<xs:enumeration value='wait'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
B.2. jabber:server β¨
okay bestie, this one is the server-side version of the message, presence, and iq schemas π same vibe as the client one, just with the server's namespace. here's the whole thing, copied exactly π«Ά
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='jabber:server'
xmlns='jabber:server'
elementFormDefault='qualified'>
<xs:import namespace='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<xs:element name='message'>
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs='0' maxOccurs='unbounded'>
<xs:element ref='subject'/>
<xs:element ref='body'/>
<xs:element ref='thread'/>
</xs:choice>
<xs:any namespace='##other'
minOccurs='0'
maxOccurs='unbounded'/>
<xs:element ref='error'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='from'
type='xs:string'
use='required'/>
<xs:attribute name='id'
type='xs:NMTOKEN'
use='optional'/>
<xs:attribute name='to'
type='xs:string'
use='required'/>
<xs:attribute name='type' use='optional' default='normal'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='chat'/>
<xs:enumeration value='error'/>
<xs:enumeration value='groupchat'/>
<xs:enumeration value='headline'/>
<xs:enumeration value='normal'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='body'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:string'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='subject'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:string'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='thread' type='xs:NMTOKEN'/>
<xs:element name='presence'>
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs='0' maxOccurs='unbounded'>
<xs:element ref='show'/>
<xs:element ref='status'/>
<xs:element ref='priority'/>
</xs:choice>
<xs:any namespace='##other'
minOccurs='0'
maxOccurs='unbounded'/>
<xs:element ref='error'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='from'
type='xs:string'
use='required'/>
<xs:attribute name='id'
type='xs:NMTOKEN'
use='optional'/>
<xs:attribute name='to'
type='xs:string'
use='required'/>
<xs:attribute name='type' use='optional'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='error'/>
<xs:enumeration value='probe'/>
<xs:enumeration value='subscribe'/>
<xs:enumeration value='subscribed'/>
<xs:enumeration value='unavailable'/>
<xs:enumeration value='unsubscribe'/>
<xs:enumeration value='unsubscribed'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='show'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='away'/>
<xs:enumeration value='chat'/>
<xs:enumeration value='dnd'/>
<xs:enumeration value='xa'/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name='status'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:string'>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='priority' type='xs:byte'/>
<xs:element name='iq'>
<xs:complexType>
<xs:sequence>
<xs:any namespace='##other'
minOccurs='0'/>
<xs:element ref='error'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='from'
type='xs:string'
use='required'/>
<xs:attribute name='id'
type='xs:NMTOKEN'
use='required'/>
<xs:attribute name='to'
type='xs:string'
use='required'/>
<xs:attribute name='type' use='required'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='error'/>
<xs:enumeration value='get'/>
<xs:enumeration value='result'/>
<xs:enumeration value='set'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute ref='xml:lang' use='optional'/>
</xs:complexType>
</xs:element>
<xs:element name='error'>
<xs:complexType>
<xs:sequence xmlns:err='urn:ietf:params:xml:ns:xmpp-stanzas'>
<xs:group ref='err:stanzaErrorGroup'/>
<xs:element ref='err:text'
minOccurs='0'/>
</xs:sequence>
<xs:attribute name='code' type='xs:byte' use='optional'/>
<xs:attribute name='type' use='required'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='auth'/>
<xs:enumeration value='cancel'/>
<xs:enumeration value='continue'/>
<xs:enumeration value='modify'/>
<xs:enumeration value='wait'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
B.3. session π
next up is the tiny session schema, and honestly it's so simple it's giving minimalism πΈ
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='urn:ietf:params:xml:ns:xmpp-session'
xmlns='urn:ietf:params:xml:ns:xmpp-session'
elementFormDefault='qualified'>
<xs:element name='session' type='empty'/>
<xs:simpleType name='empty'>
<xs:restriction base='xs:string'>
<xs:enumeration value=''/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
B.4. jabber:iq:privacy π¦
last one in this part is the privacy schema, which is the lists-and-rules one π it's a whole thing, so read it slowly bestie π
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='jabber:iq:privacy'
xmlns='jabber:iq:privacy'
elementFormDefault='qualified'>
<xs:element name='query'>
<xs:complexType>
<xs:sequence>
<xs:element ref='active'
minOccurs='0'/>
<xs:element ref='default'
minOccurs='0'/>
<xs:element ref='list'
minOccurs='0'
maxOccurs='unbounded'/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name='active'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:NMTOKEN'>
<xs:attribute name='name'
type='xs:string'
use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='default'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base='xs:NMTOKEN'>
<xs:attribute name='name'
type='xs:string'
use='optional'/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name='list'>
<xs:complexType>
<xs:sequence>
<xs:element ref='item'
minOccurs='0'
maxOccurs='unbounded'/>
</xs:sequence>
<xs:attribute name='name'
type='xs:string'
use='required'/>
</xs:complexType>
</xs:element>
<xs:element name='item'>
<xs:complexType>
<xs:sequence>
<xs:element name='iq'
minOccurs='0'
type='empty'/>
<xs:element name='message'
minOccurs='0'
type='empty'/>
<xs:element name='presence-in'
minOccurs='0'
type='empty'/>
<xs:element name='presence-out'
minOccurs='0'
type='empty'/>
</xs:sequence>
<xs:attribute name='action' use='required'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='allow'/>
<xs:enumeration value='deny'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name='order'
type='xs:unsignedInt'
use='required'/>
<xs:attribute name='type' use='optional'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='group'/>
<xs:enumeration value='jid'/>
<xs:enumeration value='subscription'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name='value'
type='xs:string'
use='optional'/>
</xs:complexType>
</xs:element>
<xs:simpleType name='empty'>
<xs:restriction base='xs:string'>
<xs:enumeration value=''/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
B.5. jabber:iq:roster β¨
Okay so this is the roster schema, and it's giving main character energy π Here's the full XML so you can see every single piece:
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='jabber:iq:roster'
xmlns='jabber:iq:roster'
elementFormDefault='qualified'>
<xs:element name='query'>
<xs:complexType>
<xs:sequence>
<xs:element ref='item'
minOccurs='0'
maxOccurs='unbounded'/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name='item'>
<xs:complexType>
<xs:sequence>
<xs:element ref='group'
minOccurs='0'
maxOccurs='unbounded'/>
</xs:sequence>
<xs:attribute name='ask' use='optional'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='subscribe'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name='jid' type='xs:string' use='required'/>
<xs:attribute name='name' type='xs:string' use='optional'/>
<xs:attribute name='subscription' use='optional'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='both'/>
<xs:enumeration value='from'/>
<xs:enumeration value='none'/>
<xs:enumeration value='remove'/>
<xs:enumeration value='to'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name='group' type='xs:string'/>
</xs:schema>
Quick vibe check on the pieces: query holds zero or more item elements, and each item can hold zero or more group elements. The jid attribute is the one that's required, so every item needs it π«Ά ask can only be subscribe, and subscription can be both, from, none, remove, or to π¦
Appendix C. Differences Between Jabber IM/Presence Protocols and XMPP
This section is non-normative. πΈ
Okay so XMPP grew out of the protocols the Jabber open-source community built, which you can basically think of as "XMPP 0.9" π There's a huge installed base of Jabber implementations and deployments out there, so it's helpful to spell out the key differences between the relevant Jabber protocols and XMPP. That way, upgrading those implementations and deployments to XMPP feels way less scary and more like a glow-up β¨ This appendix focuses on the differences that matter for instant messaging and presence. The matching section of [XMPP-CORE] covers the differences that apply to all XMPP applications.
C.1. Session Establishment π
The client-to-server authentication protocol from the Jabber community assumed that every client is an IM client. So it started an IM session as soon as authentication succeeded and resource binding happened, and those two things were done together. (If you want the docs for this protocol, they're in [JEP-0078], published by the Jabber Software Foundation [JSF].) XMPP is way more intentional about keeping core functionality separate from IM functionality π§ So an IM session doesn't get created until the client actually asks for one, using the protocol defined under Session Establishment (Section 3).
C.2. Privacy Lists π
The Jabber community started working on a protocol for communications blocking (privacy lists) in late 2001. Then that effort got deprecated once the XMPP Working Group formed π So the protocol defined under Blocking Communication (Section 10) is the only one of these defined for use in the Jabber community.
Contributors π
Most of the core stuff in the Extensible Messaging and Presence Protocol was originally developed inside the Jabber open-source community back in 1999 π That community was founded by Jeremie Miller, who released the source code for the first version of the jabberd server in January 1999. Major early contributors to the base protocol also included Ryan Eatmon, Peter Millard, Thomas Muldowney, and Dave Smith π The XMPP Working Group's instant messaging and presence work focused especially on IM session establishment and communication blocking (privacy lists). Rob Norris and Joe Hildebrand mainly developed the session establishment protocol, and Peter Millard originally contributed the privacy lists protocol π«Ά
Acknowledgements π¦
Thanks to a bunch of people beyond the contributors listed above! It's hard to name everyone, but these individuals were especially helpful in defining the protocols or commenting on the specifications in this memo: Thomas Charron, Richard Dobson, Schuyler Heath, Jonathan Hogg, Craig Kaes, Jacek Konieczny, Lisa Dusseault, Alexey Melnikov, Keith Minkler, Julian Missig, Pete Resnick, Marshall Rose, Jean-Louis Seguineau, Alexey Shchepin, Iain Shigeoka, and David Waite π Thanks also to the members of the XMPP Working Group and the IETF community for their comments and feedback throughout the life of this memo. Absolutely iconic of them π
Author's Address πΈ
Peter Saint-Andre (editor) Jabber Software Foundation
EMail: stpeter@jabber.org
Full Copyright Statement β
Copyright (C) The Internet Society (2004).
This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.
This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/S HE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Intellectual Property π₯
The IETF doesn't take a position on whether any Intellectual Property Rights or other rights that someone might claim over the technology in this document are valid or how far they reach. It also doesn't say whether licenses under those rights are or aren't available, and it hasn't independently checked for any such rights π§ For info on the IETF's procedures around rights in IETF Documents, check BCP 78 and BCP 79.
Copies of IPR disclosures sent to the IETF Secretariat, along with any assurances of licenses that will be made available, or the results of an attempt to get a general license or permission to use those proprietary rights by implementers or users of this specification, can be found in the IETF on-line IPR repository at http://www.ietf.org/ipr.
The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please send that information to the IETF at ietf-ipr@ietf.org.
Acknowledgement π
Funding for the RFC Editor function is currently provided by the Internet Society. π
π¦ where this page comes from
- ietf: rfc3921 2004-10
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms