管理名册
在XMPP中, 一个用户的名册包含任意数量的特定联系人。一个用户的花名册由用户的服务器代替用户储存,从而使用户可以从任何资源设备访问花名册信息。当用户添加或修改名册条目时,如果无错误发生,服务器应该尽可能不加修改地存储那些数据,并且当一个授权的客户端请求名册时,服务器必须返回那些数据。
- 安全警告: 因为用户的名册包含保密数据,因此服务器必须限制对这些数据的访问,只有经授权的实体(典型的为帐户拥有者)才有权利获取,修改和删除它。
RFC 3921 假定用户只把他们的名册存储在该用户帐号注册并验证访问XMPP网络的那台服务器上. 本协议移出了对名册存储和帐号注册以及网络验证的耦合的限制, 结果是用户可以把他们的名册存储在另一个地方, 或者可以有多个名册存在不同的地方. 无论如何, 在没有实现和部署实践一个更弹性化的名册存储模型的情况下, 本协议保留了
RFC 3921 的术语 "client" 和 "server" (并把 "the roster" 替换成 "a roster"), 而不是造出一个新的术语代表 "某个用户存储某一个名册的地方". 未来的文档可能为非服务器名册或多名册管理提供规范性规则, 但这些规则超出了本文的范围.
语法和语义
名册是用 <iq/> 节(见
XMPP‑CORE 的8.2.3节)管理的, 确切的说就是一个由'jabber:iq:roster'命名空间限定的<query/>子元素. 详细的语法和语义在接下来的章节中定义.
Ver属性
'ver'属性是一个标识名册信息的特定版本的字符串. 它的值必须仅由服务器生成并且必须由客户端不透明地处理. 服务器可以使用任何适当的方法来生成该版本ID, 类似名册数据的哈希值或一个严格递增的序列号.
建议包含'ver'属性.
对'ver'属性的使用的完整描述在
章节2.6.
- 互操作性备注: <query/>元素的'ver'属性在 RFC 3921 中没有定义,在本协议中是新定义的.
名册条目
一个
roster set中的<query/>元素包含一个<item/>子元素, 而一个
roster result典型地包含多个<item/>子元素. 每个<item/>元素描述了一个唯一的"名册条目"(又是也称为一个"联系人").
<item/>元素的语法参见接下来的章节.
Approved属性
布尔值'approved'属性值为"true"被用于发送
章节3.4所述的预批准信号(缺省为"false", 依据
XML‑DATATYPES).
服务器应该包含该'approved'属性来通知客户端订阅预批准. 客户端不能(MUST NOT)在它发送给服务器的roster sets中包含'approved'属性, 但必须用类型为"subscribed"和"unsubscribed"的presence节来管理
章节3.4所述的预批准.
- 互操作性备注: <item/>元素的'approved'属性在 RFC 3921 中没有被定义,是在本协议中新出现的.
Ask属性
<item/>元素的'ask'属性值为"subscribe"被用于发布订阅子状态,包括
章节3.1.2所述的"待处理"等等.
服务器应该包含'ask'属性来通知客户端"待处理"子状态. 客户端不能(MUST NOT)在它发送给该服务器的roster sets中包含'ask'属性, 而必须使用类型为"subscribe"和"unsubscribe" 的presence节来管理
章节3.1.2所述的这类子状态.
JID属性
<item/>元素的'jid'属性指定唯一性地标识该名册条目的Jabber标识符(JID).
'jid'属性是必需的,无论是客户端还是服务器添加, 更新, 删除, 或返回一个名册条目.
Name属性
<item/>元素的'name'属性指定和该JID相关的"处理", 这是由该用户(不是该联系人)决定的. 尽管'name'属性的值可以代表一个自然人用户, 对该服务器来说它是不透明的. 无论如何, 'name'属性在多个XMPP扩展的上下文中可被服务器用于匹配目的(一个可能的比较方法是
XMPP‑ADDR所述的XMPP资源部分).
对于客户端来说,当添加或更新一个名册条目的时候,包含'name'属性是可选的.
Subscription属性
出席信息订阅的状态被放在<item/>元素的'subscription'属性中. 已定义的订阅相关的值如下:
none:
该用户没有订阅一该联系人的出席信息, 并且联系人也没有订阅该用户的出席信息; 这是缺省值, 所以如果没有subscription属性,那么该状态被理解为"none"
to:
该用户定于了该联系人的出席信息, 但是联系人没有订阅用户的出席信息
from:
该联系人订阅了该用户的出席信息, 但是该用户没有订阅联系人的出席信息
both:
该用户和该联系人互相订阅了对方的出席信息(也称为"相互订阅")
在一个
roster result中, 除了 "none", "to", "from", 或 "both"值,该客户端必须忽略 'subscription'属性的(其他)值.
在一个
roster push中, 除了 "none", "to", "from", "both", 或 "remove"值,该客户端必须忽略'subscription'属性的(其他)值.
在一个
roster set中, 'subscription'属性的值可以包括 "remove", 它表示该条目已经从名册中移除了; 在一个 roster set 中,该服务器必须忽略除了"remove"之外'subscription'属性的所有值.
包含'subscription'属性是可选的.
Group属性
<group/>子元素指定一个类别或"桶" ,让客户端把名册条目分成组. 一个<item/>元素可以包含多个<group/>元素, 这意味着名册组不是排他的. 尽管<group/>元素的XML字符串元素可以意味着一个自然人用户, 它对于服务器是不透明的. 无论如何, 在各种XMPP扩展的上下文中, <group/>元素可以被服务器用于匹配的目的(一个可能的的比较方法用于XMPP资源部分,详见
XMPP‑ADDR).
对于客户端来说,在添加或更新一个名册条目的时候包含<group/>元素是可选的. 如果一个
roster set不包含<group/>元素, 那么该条目被理解为不属于任何组.
名册获取
一个 "roster get" 是一个客户端为了让服务器返回某名册而发出的请求; 在语法构成上它是一个从客户端到服务器的类型为"get"的IQ节,并且包含一个由'jabber:iq:roster'命名空间限定的<query/>元素, 这里的<query/>元素不能(MUST NOT)包含任何<item/>子元素.
C: <iq from='juliet@example.com/balcony'
id='bv1bs71f'
type='get'>
<query xmlns='jabber:iq:roster'/>
</iq>
发送一个roster get的预期结果是服务器返回一个roster result.
名册结果
一个"roster result"是服务器对一个名册获取的应答; 在语法构成上它是一个从服务器到客户端的类型为"result"的IQ节,并且包含一个由'jabber:iq:roster'命名空间限定的<query/>元素.
该 <query/> 元素是一个名册结果,为每一个联系人包含一个 <item/> 元素并且因而可以包含多个<item/>元素.
S: <iq id='bv1bs71f'
to='juliet@example.com/chamber'
type='result'>
<query xmlns='jabber:iq:roster' ver='ver7'>
<item jid='nurse@example.com'/>
<item jid='romeo@example.net'/>
</query>
</iq>
如果该名册存在但是在名册中没有联系人, 那么该服务器必须返回一个IQ-result,包含一个<query/>子元素而不包含任何<item/>子元素(即, 服务器不能(MUST NOT)返回空的类型为"error"的<iq/>节).
S: <iq id='bv1bs71f'
to='juliet@example.com/chamber'
type='result'>
<query xmlns='jabber:iq:roster' ver='ver9'/>
</iq>
如果该名册不存在, 那么该服务器必须返回一个节错误,条件为<item-not-found/>.
S: <iq id='bv1bs71f'
to='juliet@example.com/chamber'
type='error'>
<error type='cancel'>
<item-not-found
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
名册设置
一个"roster set"是一个客户端向服务器发出的编辑(即, 创建, 更新, 或删除)一个名册条目的请求; 在语法构成上它是一个从客户端到服务器的类型为"set"的IQ节,并且包含一个由'jabber:iq:roster'命名空间限定的<query/>元素.
以下规则适用于名册设置:
-
- <query/>元素必须包含一个并且只含有一个<item/>元素.
- 服务器必须忽略'subscription'属性的除"remove"以外的任何值(见章节2.1.2.5).
- 安全警告: 传统上, 名册设置的IQ节不包含'to'地址, 结果是所有名册设置都那个需要更新名册的帐号的某个已验证资源(全JID)发出. 此外, RFC 3921 要求一个服务器执行名册设置的特定情景的检查来忽略'to'地址; 无论如何, 本协议移除了那个特定情景, 这意味着一个名册设置可以包含一个和发送者不同的'to'地址. 所以, 处理一个名册设置的实体必须验证名册设置的发送者已被授权更新该名册, 如果没有授权则返回一个<forbidden/>错误.
C: <iq from='juliet@example.com/balcony'
id='rs1'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'/>
</query>
</iq>
名册推送
一个"roster push"是一个服务器向客户端发出的最新创建, 更新, 或删除的名册条目; 在语法构成上它是一个从服务器到客户端的类型为"set"的IQ节,并且包含一个由'jabber:iq:roster'命名空间限定的<query/>元素.
以下规则适用于名册推送:
-
- 在一个名册推送中的<query/>元素必须包含且仅包含一个<item/>元素.
- 一个接收的客户端必须忽略这个节,除非它满足以下条件:没有'from'属性(即, 隐式地来自该用户帐号的纯JID)或它有一个值和该用户的纯JID<user@domainpart>匹配的'from'属性.
S: <iq id='a78b4q6ha463'
to='juliet@example.com/chamber'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'/>
</query>
</iq>
作为
XMPP‑CORE定义的IQ节语义的强制要求, 每个接收一个来自服务器的名册推送的资源应该应答一个类型为"result"或"error"的IQ节(无论如何, 大家知道很多现有的客户端不应答名册推送).
C: <iq from='juliet@example.com/balcony'
id='a78b4q6ha463'
type='result'/>
C: <iq from='juliet@example.com/chamber'
id='a78b4q6ha463'
type='result'/>
- 安全警告: 传统上, 名册推送不包含'from'地址, 结果是所有名册推送都隐式地从该帐号本身的纯JID发出. 无论如何, 本协议允许非该用户的服务器实体来维护名册信息, 这意味着一个名册推送可以包含不同于该用户帐号的纯JID的'from'地址. 所以, 该客户端必须检查'from'地址来验证名册推送的发送者被售前更新名册. 如果该客户端从一个未授权实体接收到了一个名册推送, 它不能(MUST NOT)处理被推送的数据; 此外, 该客户端要么返回一个节错误<service-unavailable/>要么干脆拒绝返回一个节错误(后面那个行为覆盖了一个来自XMPP‑CORE的 必须-级 的要求, 目的是防止出席信息泄露).
- 实现备注: 对于名册推送的客户端处理没有错误情景; 如果服务器在对名册推送的应答中接收到了一个类型为"error"的IQ,那么它应该忽略那个错误.
登陆时接收名册
在通过一个服务器的验证并绑定一个资源之后(也就是成为一个
XMPP‑CORE中定义的已连接资源), 一个客户端应该在发送初始的出席信息之前请求名册(无论如何, 因为接收名册不是对所有的资源都是必要的, 例如, 一个有限带宽的连接, 该客户端对名册的请求不是强制的). 在一个已连接的资源发送初始出席信息之后(见
章节4.2), 它被称为一个"可用的资源". 如果一个已连接资源或可用资源请求名册, 它被成为一个"有兴趣的资源". 服务器必须发送名册推送到所有有兴趣的资源.
- 实现备注: 出席信息订阅请求被发送到可用的资源, 而和订阅状态改变相关的名册推送被发送到感兴趣的资源. 所以, 如果一个资源希望同时接收到订阅请求和名册推送, 它必须既发送初始出席信息又请求名册.
一个客户端通过向服务器发送一个roster get来请求名册.
C: <iq from='juliet@example.com/balcony'
id='hu2bac18'
type='get'>
<query xmlns='jabber:iq:roster'/>
</iq>
S: <iq id='hu2bac18'
to='juliet@example.com/balcony'
type='result'>
<query xmlns='jabber:iq:roster' ver='ver11'>
<item jid='romeo@example.net'
name='Romeo'
subscription='both'>
<group>Friends</group>
</item>
<item jid='mercutio@example.com'
name='Mercutio'
subscription='from'/>
<item jid='benvolio@example.net'
name='Benvolio'
subscription='both'/>
</query>
</iq>
如果服务器不处理该roster get, 它必须返回一个
XMPP‑CORE所述的适当的节错误(如果是名册命名空间不被支持则返回 <service-unavailable/>,如果该服务器处理过程或返回该名册时遇到麻烦则返回 <internal-server-error/> ).
添加一个名册条目
请求
任何时候, 一个客户端可以添加一个条目到名册. 只要发送一个包含一个新条目的roster set就可以做到.
C: <iq from='juliet@example.com/balcony'
id='ph1xaz53'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='Nurse'>
<group>Servants</group>
</item>
</query>
</iq>
成功情景
如果服务器成功地处理了关于该新条目的roster set(即, 如果没有错误发生), 它必须在该用户的名册中新增该条目并做如下处理.
该服务器必须返回一个类型为"result"的IQ节给发送该roster set的已连接资源.
S: <iq id='ph1xaz53'
to='juliet@example.com/balcony'
type='result'/>
该服务器也必须发送一个包含了该新名册条目的roster push到该用户的所有感兴趣的资源, 包括生成了该roster set的资源.
S: <iq to='juliet@example.com/balcony'
id='a78b4q6ha463'
type='set'>
<query xmlns='jabber:iq:roster' ver='ver13'>
<item jid='nurse@example.com'
name='Nurse'
subscription='none'>
<group>Servants</group>
</item>
</query>
</iq>
S: <iq to='juliet@example.com/chamber'
id='x81g3bdy4n19'
type='set'>
<query xmlns='jabber:iq:roster' ver='ver13'>
<item jid='nurse@example.com'
name='Nurse'
subscription='none'>
<group>Servants</group>
</item>
</query>
</iq>
作为在
XMPP‑CORE中定义的IQ节语义的强制要求, 建议每个从该服务器接收到一个roster push的资源应答一个类型为"result"或"error"的IQ节(无论如何, 大家知道很多现有的客户端不应答roster pushes).
C: <iq from='juliet@example.com/balcony'
id='a78b4q6ha463'
type='result'/>
C: <iq from='juliet@example.com/chamber'
id='x81g3bdy4n19'
type='result'/>
错误情景
如果服务器未能成功处理roster set, 它必须返回一个节错误. 以下错误场景已被定义. 自然的, 可能会发生其他节错误, 例如,如果服务器在处理roster get(译者注:这里应该是roster set,疑为原文笔误)时发生了内部问题则返回 <internal-server-error/>, 或甚至服务器只允许通过类似web接口这样的非XMPP方法来修改名册则返回<not-allowed/>.
如果roster set的发送者未被授权更新该名册(典型的情况是该帐号本身只有一个已验证的资源被授权),则服务器必须返回一个<forbidden/>节错误给客户端.
服务器必须返回一个<bad-request/>节错误给客户端,如果roster set包含以下任何违规情况:
-
- <query/>元素包含不止一个<item/>子元素.
- <item/>元素包含不止一个<group/>元素, 但是有重复的groups (一个可能的用来确定重复的比较方法见于XMPP‑ADDR中关于XMPP资源部分的描述).
服务器必须返回一个<not-acceptable/>节错误给客户端,如果roster set包含以下任何违规情况:
-
- 'name'属性的长度大于一个服务器配置的限制.
- <group/>元素的XML字符数据长度为零(为了把一个条目从所有组移除, 客户端需要从roster set排除任何<group/>元素).
- <group/>元素的XML字符数据长度大于一个服务器配置的限制.
错误: Roster set由未授权实体初始化
C: <iq from='juliet@example.com/balcony'
id='ix7s53v2'
to='romeo@example.net'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'/>
</query>
</iq>
S: <iq id='ix7s53v2'
to='juliet@example.com/balcony'
type='error'>
<error type='auth'>
<forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
错误: Roster set包含多个条目
C: <iq from='juliet@example.com/balcony'
id='nw83vcj4'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='Nurse'>
<group>Servants</group>
</item>
<item jid='mother@example.com'
name='Mom'>
<group>Family</group>
</item>
</query>
</iq>
S: <iq id='nw83vcj4'
to='juliet@example.com/balcony'
type='error'>
<error type='modify'>
<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
错误: Roster set包含的条目超长
C: <iq from='juliet@example.com/balcony'
id='yl491b3d'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='[ ... some-very-long-handle ... ]'>
<group>Servants</group>
</item>
</query>
</iq>
S: <iq id='yl491b3d'
to='juliet@example.com/balcony'
type='error'>
<error type='modify'>
<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
错误: Roster set包含重复的groups
C: <iq from='juliet@example.com/balcony'
id='tk3va749'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='Nurse'>
<group>Servants</group>
<group>Servants</group>
</item>
</query>
</iq>
S: <iq id='tk3va749'
to='juliet@example.com/balcony'
type='error'>
<error type='modify'>
<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
错误: Roster set包含空group
C: <iq from='juliet@example.com/balcony'
id='fl3b486u'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='Nurse'>
<group></group>
</item>
</query>
</iq>
S: <iq id='fl3b486u'
to='juliet@example.com/balcony'
type='error'>
<error type='modify'>
<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
错误: Roster set包含超长group名
C: <iq from='juliet@example.com/balcony'
id='qh3b4v19'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
name='Nurse'>
<group>[ ... some-very-long-group-name ... ]</group>
</item>
</query>
</iq>
S: <iq id='qh3b4v19'
to='juliet@example.com/balcony'
type='error'>
<error type='modify'>
<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
- 互操作性备注: 如果<item/>元素的'jid'属性值和该用户的帐号的纯JID<localpart@domainpart>匹配,一些服务器会返回<not-allowed/>节错误给客户端.
更新一个名册条目
请求
更新一个现有的名册条目和新增一个名册条目是以相同方法完成的, 即, 发送一个roster set给服务器. 因为名册条目是原子的, 该条目必须如roster set提供的那样被准确地更新.
关于为什么一个客户端可能更新一个名册条目有很多原因:
-
- 新增一个组
- 删除一个组
- 修改操作
- 删除操作
考虑一个定义如下的名册条目:
<item jid='romeo@example.net'
name='Romeo'>
<group>Friends</group>
</item>
在她的名册中拥有该用户的条目,她可能想把该条目添加到另一个组.
C: <iq from='juliet@example.com/balcony'
id='di43b2x9'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='romeo@example.net'
name='Romeo'>
<group>Friends</group>
<group>Lovers</group>
</item>
</query>
</iq>
有时候晚些时候, 该用户可能希望从原有的组移除该条目.
C: <iq from='juliet@example.com/balcony'
id='lf72v157'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='romeo@example.net'
name='Romeo'>
<group>Lovers</group>
</item>
</query>
</iq>
该用户可能想从所有组移除该条目.
C: <iq from='juliet@example.com/balcony'
id='ju4b62a5'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='romeo@example.net'/>
</query>
</iq>
该用户也可能想修改对该条目的操作.
C: <iq from='juliet@example.com/balcony'
id='gb3sv487'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='romeo@example.net'
name='MyRomeo'/>
</query>
</iq>
该用户然后可能想完全移除该操作.
C: <iq from='juliet@example.com/balcony'
id='o3bx66s5'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='romeo@example.net'
name=''/>
</query>
</iq>
- 实现备注: 包含一个空的'name'属性等价于不包含'name'属性; 两个操作都是把name设为空字符串.
成功情景
和添加一个名册条目一样, 如果该名册条目能被成功处理那么服务器必须在该用户的名册中更新该条目, 发送一个roster push到所有该用户的感兴趣资源, 并且发送一个IQ result给初始的资源; 详见
章节2.3.
错误情景
章节2.3.3中错误情景的描述也适用于更新一个名册条目.
删除一个名册条目
请求
任何时候, 一个客户端可以通过发送一个并把'subscription'属性值设为"remove"来把一个条目从他或她的名册中删除.
C: <iq from='juliet@example.com/balcony'
id='hm4hs97y'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
subscription='remove'/>
</query>
</iq>
成功情景
如同增加一个名册条目, 如果服务器能成功地处理roster set那么它必须在该用户的名册中更新该条目, 发送一个roster push到该用户的所有感兴趣的资源(其中的'subscription'属性值设为"remove"), 并发送一个IQ result给初始的资源; 详见
章节2.3.
另外, 该用户的服务器可能需要生成一个或更多subscription相关的presence节, 如下:
-
- 如果该用户对该联系人有一个出席信息订阅, 那么该用户的服务器必须发送一个type为"unsubscribe"的presence节给该联系人(为了对该联系人的出席信息取消订阅).
- 如果该联系人对该用户有一个出席信息订阅, 那么该用户的服务器必须发送一个type为"unsubscribed"的presence节给该联系人(为了取消该联系人对该用户的订阅).
- 如果出席信息订阅是相互的, 那么该用户的服务器必须同时发送type为"unsubscribe"presence节和type为"unsubscribed"的的presence节给该联系人.
S: <presence from='juliet@example.com'
id='lm3ba81g'
to='nurse@example.com'
type='unsubscribe'/>
S: <presence from='juliet@example.com'
id='xb2c1v4k'
to='nurse@example.com'
type='unsubscribed'/>
错误情景
如果'jid'属性的值指定的一个条目不在该名册中, 那么服务器必须返回一个 <item-not-found/> 节错误.
错误: 未发现名册条目
C: <iq from='juliet@example.com/balcony'
id='uj4b1ca8'
type='set'>
<query xmlns='jabber:iq:roster'>
<item jid='[ ... non-existent-jid ... ]'
subscription='remove'/>
</query>
</iq>
S: <iq id='uj4b1ca8'
to='juliet@example.com/balcony'
type='error'>
<error type='modify'>
<item-not-found
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
名册版本
流特性
如果一个服务器支持名册版本, 那么它必须在流协商期间声明以下流特性.
<ver xmlns='urn:xmpp:features:rosterver'/>
名册版本流特性很少见所以永远不会是强制协商的.
请求
如果一个客户端支持名册版本并且它所连接的服务器如前一节说的那样声明支持名册版本, 那么该客户端应该在它对名册请求中包含'ver'元素. 如果该服务器没有声明支持名册版本, 客户端不能(MUST NOT)包含'ver'属性. 如果客户端在它的roster get中包含了'ver'属性, 它就把该属性的值设成了它最后的名册缓存中的相关的版本ID.
C: <iq from='romeo@example.net/home'
id='r1h3vzp7'
to='romeo@example.net'
type='get'>
<query xmlns='jabber:iq:roster' ver='ver14'/>
</iq>
如果该客户端还未缓存该名册或该缓存遗失或被中断了, 但是该客户端希望引导名册版本的使用, 它必须把'ver'属性设为空字符串(即, ver="").
自然的, 如果该客户端不支持名册版本或不希望引导名册版本的使用, 它将不包含'ver'属性.
成功情景
无论名册从被客户端列举版本ID之后是否被修改过, 服务器必须要么如
章节2.1.4(包含一个'ver'属性代表最后的版本)所述返回完整的名册,要么返回一个空的IQ-result(这样就表示任何名册的修改将通过roster pushes被发送, 如下所述). 通常, 除非返回完整名册 (1) 将比发送独立的roster pushes给客户端使用更少的带宽(例如, 如果名册只包含很少的条目) 或 (2) 服务器不能把版本ID关联到任何之前已经在文件中有的版本, 服务器应该发送一个空的IQ-result然后通过roster pushes发送修改(如果有的话).
S: <iq from='romeo@example.net'
id='r1h3vzp7'
to='romeo@example.net/home'
type='result'/>
- 实现备注: 这个空的IQ-result不同于一个空的<query/>元素, 这和使用一个空名册是不同的.
如果允许使用名册版本并且名册从被客户端列举版本ID之后未被修改过, 服务器将简单地不发送任何roster pushes给客户端(直到并且除非在该客户端会话的生命周期中一些相关的事件触发一个roster push).
如果名册从被客户端列举版本ID之后被修改过, 那么该服务器必须为每一个从被客户端列举版本ID之后被修改过的条目发送一个roster push给该客户端. (我们把一个用于名册版本同步的被发送的roster push称为一个"暂时的roster push".)
- 定义: 一个"roster modification"表示对名册数据的任何改变并将导致一个roster push到已连接客户端. 所以, 和服务器对名册处理相关的内部状态将不会导致一个roster push到已连接客户端因而不需要改变版本.
S: <iq from='romeo@example.net'
id='ah382g67'
to='romeo@example.net/home'
type='set'>
<query xmlns='jabber:iq:roster' ver='ver34'>
<item jid='tybalt@example.org' subscription='remove'/>
</query>
</iq>
S: <iq from='romeo@example.net'
id='b2gs90j5'
to='romeo@example.net/home'
type='set'>
<query xmlns='jabber:iq:roster' ver='ver42'>
<item jid='bill@example.org' subscription='both'/>
</query>
</iq>
S: <iq from='romeo@example.net'
id='c73gs419'
to='romeo@example.net/home'
type='set'>
<query xmlns='jabber:iq:roster' ver='ver72'>
<item jid='nurse@example.org'
name='Nurse'
subscription='to'>
<group>Servants</group>
</item>
</query>
</iq>
S: <iq from='romeo@example.net'
id='dh361f35'
to='romeo@example.net/home'
type='set'>
<query xmlns='jabber:iq:roster' ver='ver96'>
<item jid='juliet@example.org'
name='Juliet'
subscription='both'>
<group>VIPs</group>
</item>
</query>
</iq>
这些"暂时的roster pushes"可以作如下理解:
-
- 想想客户端在它缓存的名册版本(假设是, "ver14")和新的名册版本(假设是, "ver96")之间有一个活跃的出席信息会话.
- 在此期间, 该客户端可能已经接收到一个和各种名册版本相关的roster pushes(它可能已经发生, 假设是, "ver51" 和 "ver79"). 无论如何, 这些roster pushes中的一些可能已经包含了同一个名册条目的中间更新(例如, 把对bill@example.org的订阅状态从"none"改为"to"又从"to"改为"both").
- 这个暂时的roster pushes将不包含所有中间步骤, 而只有在该客户端实际上离线的时候每个条目修改后的最终结果(这可能发生, 假设是, "ver34", "ver42", "ver72", 和 "ver96").
客户端必须和处理任何roster push一样处理一个"interim roster push"(实际上, 从客户端的视角不能说一个"暂时的"roster push和一个"live"的 roster push是不同的,因而当它没办法知道自己收到是否是暂时的roster pushes). 当重连后请求名册, 客户端应该请求和它在前一个会话中收到的最后的roster push相关的版本, 而不是它在前一个会话的开始时收到的roster result的相关版本.
当名册版本被允许, 服务器必须在每一个roster push中包含更新的名册版本. Roster pushes必须按照变更的顺序产生并且在一个roster push中包含的版本必须是唯一的. 即使客户端未在它的roster gets或sets 中包含'ver'属性, 服务器也应该在它发送给客户端的所有roster pushes和results中包含'ver'属性.
- 实现备注: 名册版本的指南和详细例子见XEP‑0237.