RFC6121
(→国际化事项) |
小 (→名册推送) |
||
(未显示3个用户的32个中间版本) | |||
第1行: | 第1行: | ||
− | + | '''本文的英文原文来自[http://xmpp.org/rfcs/rfc6121.html RFC 6121]''' | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
{|border="1" cellspacing="0" width="65%" | {|border="1" cellspacing="0" width="65%" | ||
第170行: | 第166行: | ||
该用户没有订阅一该联系人的出席信息, 并且联系人也没有订阅该用户的出席信息; 这是缺省值, 所以如果没有subscription属性,那么该状态被理解为"none" | 该用户没有订阅一该联系人的出席信息, 并且联系人也没有订阅该用户的出席信息; 这是缺省值, 所以如果没有subscription属性,那么该状态被理解为"none" | ||
to: | to: | ||
− | + | 该用户订阅了该联系人的出席信息, 但是联系人没有订阅用户的出席信息 | |
from: | from: | ||
该联系人订阅了该用户的出席信息, 但是该用户没有订阅联系人的出席信息 | 该联系人订阅了该用户的出席信息, 但是该用户没有订阅联系人的出席信息 | ||
第286行: | 第282行: | ||
</source> | </source> | ||
− | 作为[[RFC6121#规范引用|XMPP‑CORE]]定义的IQ节语义的强制要求, 每个接收一个来自服务器的名册推送的资源应该应答一个类型为"result"或"error"的IQ节( | + | 作为[[RFC6121#规范引用|XMPP‑CORE]]定义的IQ节语义的强制要求, 每个接收一个来自服务器的名册推送的资源应该应答一个类型为"result"或"error"的IQ节(然而, 大家知道很多现有的客户端不应答名册推送). |
<source lang="xml"> | <source lang="xml"> | ||
第1,354行: | 第1,350行: | ||
===出席信息原理=== | ===出席信息原理=== | ||
− | + | 出席信息是指在网络上通讯的一个实体的可用性. 在最基本的层面上, 出席信息是一个"on/off"的逻辑变量以标记一个实体可不可以通讯(也用术语"在线"和"离线"). 在XMPP中, 当客户端生成一个不带'type'属性的<presence/>节时就标记了一个实体的可用性, 当它的客户端生成一个'type'属性值为"unavailable"的<presence/>节时表示一个实体失去了可用性. | |
− | XMPP出席信息典型地遵循一个"发布-订阅"或"观察者"模式, | + | XMPP出席信息典型地遵循一个"发布-订阅"或"观察者"模式, 这里,一个实体发送出席信息给它的服务器, 它的服务器接着广播那个信息给所有订阅了该实体的出席信息的联系人(在[http://xmpp.org/rfcs/rfc6121.html#IMP-MODEL IMP‑MODEL]的术语中, 一个生成出席信息的实体就是一个"presentity"并且那些接收出席信息的实体们是"subscribers"). 一个客户端通过发送一个不带'to'地址的出席信息节给它的服务器来生成出席信息用于广播给所有已订阅的实体们, 这里的出席信息既没有'type'属性又没有值为"unavailable"的'type'属性. 这种出席信息被称为"广播出席信息". (一个客户端也可以发送"定向出席信息", 即, 一个带'to'地址的出席信息节; 这不太常见但是有时被用于发送出席信息给那些没有订阅该用户的出席信息的实体们; 见[[RFC6121#定向出席信息|4.6.]]) |
在一个客户端完成[[RFC6121#规范引用|XMPP‑CORE]]定义的先决条件之后, 它能通过发送初始化出席信息在它的服务器建立一个"出席信息会话", 通过发送unavailable出席信息可以中止这个出席信息会话. 在它的出席信息会话期间, 一个已连接的资源([[RFC6121#规范引用|XMPP‑CORE]]中的术语)被称为一个"可用的资源". | 在一个客户端完成[[RFC6121#规范引用|XMPP‑CORE]]定义的先决条件之后, 它能通过发送初始化出席信息在它的服务器建立一个"出席信息会话", 通过发送unavailable出席信息可以中止这个出席信息会话. 在它的出席信息会话期间, 一个已连接的资源([[RFC6121#规范引用|XMPP‑CORE]]中的术语)被称为一个"可用的资源". | ||
− | 在XMPP中, 应用集合了消息和出席信息功能, 它的出席信息标记的可用性的缺省通讯类型是消息; | + | 在XMPP中, 应用集合了消息和出席信息功能, 它的出席信息标记的可用性的缺省通讯类型是消息; 然而, 对于XMPP应用来说把消息和出席信息功能集合到一起并不是必要的, 它们能提供独立的不带消息的出席信息特性(另外, XMPP服务器们不会为了成功地路由message和IQ节而强制要求关于网络可用性的信息). |
:提示备注: 在接下来的例子中, 用户是<juliet@example.com>, 她有两个可用的资源("balcony"和"chamber"), 而在她的名册中有三个联系人且订阅状态为"from"或"both": <romeo@example.net>, <mercutio@example.com>, 和<benvolio@example.net>. | :提示备注: 在接下来的例子中, 用户是<juliet@example.com>, 她有两个可用的资源("balcony"和"chamber"), 而在她的名册中有三个联系人且订阅状态为"from"或"both": <romeo@example.net>, <mercutio@example.com>, 和<benvolio@example.net>. | ||
第1,948行: | 第1,944行: | ||
该用户的客户端应该在一个聊天会话中把初始消息的地址指向该联系人的纯JID <contact@domainpart> (而不是基于任何它可能之前从联系人那收到的出席信息通知的 <show/>, <status/>, 或 <priority/> 值来尝试猜测一个适当的全JID <contact@domainpart/resourcepart> ). 直到和除非该用户的客户端从该联系人收到一个应答, 它应该发送任何更多的消息给该联系人的纯JID. 该联系人的客户端应该把它的应答的地址指向该用户的全JID <user@domainpart/resourcepart>,即初始消息中提供的'from'地址. 一旦该用户的客户端从该联系人的全JID收到一个应答, 它应该把它随后的消息指向该联系人的全JID,即在该联系人的应答中提供的'from'地址, 从而"锁定"在那个全JID. 一个客户端应该在接收到一个来自对方节点控制的任何其他资源的<message/>或<presence/>节(或一个来自已锁定的资源的出席信息节)的时候"解除锁定"; 结果是, 它应该在聊天会话的下一个消息中指向对方节点的纯JID(从而"解锁"之前的"锁定"),直到它从对方的全JIDs收到一个消息. | 该用户的客户端应该在一个聊天会话中把初始消息的地址指向该联系人的纯JID <contact@domainpart> (而不是基于任何它可能之前从联系人那收到的出席信息通知的 <show/>, <status/>, 或 <priority/> 值来尝试猜测一个适当的全JID <contact@domainpart/resourcepart> ). 直到和除非该用户的客户端从该联系人收到一个应答, 它应该发送任何更多的消息给该联系人的纯JID. 该联系人的客户端应该把它的应答的地址指向该用户的全JID <user@domainpart/resourcepart>,即初始消息中提供的'from'地址. 一旦该用户的客户端从该联系人的全JID收到一个应答, 它应该把它随后的消息指向该联系人的全JID,即在该联系人的应答中提供的'from'地址, 从而"锁定"在那个全JID. 一个客户端应该在接收到一个来自对方节点控制的任何其他资源的<message/>或<presence/>节(或一个来自已锁定的资源的出席信息节)的时候"解除锁定"; 结果是, 它应该在聊天会话的下一个消息中指向对方节点的纯JID(从而"解锁"之前的"锁定"),直到它从对方的全JIDs收到一个消息. | ||
− | 当双方参加一个一个聊天会话但是没有基于一个出席信息订阅来互相分享出席信息, | + | 当双方参加一个一个聊天会话但是没有基于一个出席信息订阅来互相分享出席信息, 他们应该互相发送定向出席信息,这样双方能很容易地查询对方在该聊天会话期间是否下线. 然而, 一个客户端必须提供一个方法让一个用户能全局地禁止或只允许特定的实体进行这类出席信息分享. 更进一步的, 当出于某种原因相信该聊天会话结束了(例如, 如果, 在一段合理的时间之后, 双方没有交换随后的消息),一方应该发送定向unavailable出席信息给另一方 . |
聊天会话的例子参见[[RFC6121#示例会话|第7章]]. | 聊天会话的例子参见[[RFC6121#示例会话|第7章]]. | ||
第2,075行: | 第2,071行: | ||
====Thread元素==== | ====Thread元素==== | ||
− | XMPP <thread/>元素主要用于唯一性地标识一个由类型为'chat'的<message/>节实例化的两个实体之间的交谈线索或"聊天会话". | + | XMPP <thread/>元素主要用于唯一性地标识一个由类型为'chat'的<message/>节实例化的两个实体之间的交谈线索或"聊天会话". 然而, XMPP <thread/>元素也可以被用于唯一性地标识一个由类型为'headline'或'normal'的<message/>节实例化的两个实体之间, 或由类型为'groupchat'的<message/>节实例化的在一个多用户聊天室上下文中的多个实体之间的类似线索. 它也可用于和自然人无关的会话的<message/>节, 类似一个游戏会话或一个插件之间的互动. <thread/>元素不被用于标识个别的消息, 只用于交谈或消息会话. |
<thread/>元素是可选的. 因为<thread/>元素标识了一个消息所属的特定交谈线索, 一个消息节不能(MUST NOT)多个<thread/>元素. | <thread/>元素是可选的. 因为<thread/>元素标识了一个消息所属的特定交谈线索, 一个消息节不能(MUST NOT)多个<thread/>元素. | ||
第2,671行: | 第2,667行: | ||
==安全事项== | ==安全事项== | ||
+ | |||
+ | 关于XMPP的核心安全事项位于[[RFC6121#规范引用|XMPP‑CORE]]的第13章, 包括通道加密, 验证, 信息泄露, 拒绝服务攻击, 以及域间联盟的讨论. | ||
+ | |||
+ | [[RFC6121#规范引用|XMPP‑CORE]]第13.1节大概描述了在典型的XMPP部署中客户端和服务器的架构角色, 并讨论了和那些角色相关的安全特性. 这些角色对本文描述的即时消息,出席信息订阅,和出席信息通知的安全性上有影响. 本质上, 一个XMPP用户在一个XMPP服务器上注册或已经被预分配)一个帐号, 所以在这个服务器上给予了某些级别的信任以代表该用户完成各种任务, 强制安全性策略, 等等. 因此服务器的责任是: | ||
+ | |||
+ | :# 本地客户端和远端服务器之间的通讯最好强制使用通道加密. | ||
+ | :# 验证任何希望访问该用户的帐号的客户端. | ||
+ | :# 处理已经被验证的客户端收到和发出的XML节(特别是关于即时消息和出席信息功能, 存储该用户的名册, 处理入站和出站订阅请求和应答, 生成和处理出席信息探测, 广播出站出席信息通知, 路由出站消息, 以及递送入站消息和出席信息通知). | ||
+ | |||
+ | 如[[RFC6121#规范引用|XMPP‑CORE]]的第13.1节和第13.4讨论的, 即使该服务器履行了上述的责任, 该客户端对于节可能和另一个客户端(在同一台服务器上或在一个远端服务器上)的交换没有任何保证,它可能在XMPP通讯路径中的所有跳中被保护, 或只在该服务器本身被保护. 如果该客户端希望确保它的通讯的点对点的机密性和完整性,使用一个适当的技术来对XML节加密和签名是它的责任. | ||
+ | |||
+ | 只适用于XMPP的即时消息和出席信息应用的额外事项定义于本文的很多地方; 特别是: | ||
+ | |||
+ | :* 当一个服务器拥有一个类型为"probe"的入站出席信息节,且它的预期接收者是和该服务器的已配置域之一相关的一个用户, 如果该发送者是一个未被授权接收那个由出席信息订阅决定的信息的实体(见[[RFC6121#交换出席信息|第4章]]),该服务器不能(MUST NOT)透露该用户的出席信息. | ||
+ | :* 一个用户的服务器不能(MUST NOT)泄露该用户的网络可用性给未被授权知道该用户的出席信息的的实体们. 在XMPP本身, 授权的方式是一个显性的从联系人到用户的订阅(如[[RFC6121#管理出席信息订阅|第3章]]所述). 无论如何,如果在该实体和生成了出席信息的该用户之间已经存在一个信任关系(例如, 如果该组织把强制分享出席信息作为员工协议的一部分,一个XMPP的企业部署可能自动把该用户的出席信息添加到一个员工私有目录),一些XMPP部署可能认为一个实体已被授权. | ||
+ | :* 当一个服务器处理一个没有类型或类型为"unavailable"的出站出席信息节, 它必须遵循定义于[[RFC6121#交换出席信息|第4章]]的规则以确保这类出席信息不被发送给未被授权知道这类信息的实体们. | ||
+ | :* 一个客户端可以忽略<status/>元素,当该节包含在一个类型为"subscribe", "unsubscribe", "subscribed", 或 "unsubscribed"的出席信息节中的时候; 这有助于组织"出席信息订阅垃圾". | ||
+ | |||
==一致性需求== | ==一致性需求== | ||
− | ==参考 | + | |
+ | 本章描述了一个协议特性集合来概述本协议的一致性需求. 本特性集合适用于软件认证, 互操作性测试, 和实现报告. 对于每个特性, 本章提供了以下信息: | ||
+ | |||
+ | :* 自然人可读的名称 | ||
+ | :* 描述信息 | ||
+ | :* 本文特定章节的参考,该章节规范化地定义了该特性 | ||
+ | :* 该特性是否适用于客户端角色,服务器角色,或同时适用于两者(这里 "N/A" 表示该特性不适用于该特定角色) | ||
+ | :* 该特性是必须(MUST)还是应该(SHOULD)被实现, 这里大写的术语被视为[[RFC6121#规范引用|KEYWORDS]]所描述的含义 | ||
+ | |||
+ | 这里定义的特性集合尝试坚持Larry Masinter于2005年在IETF的NEWTRK工作组中建议的概念和格式, 摘自[[RFC6120#参考性文献|INTEROP]]. 尽管该特性集合比[[RFC6121#参考性文献|REPORTS]]中说的更详细, 它为实现报告的生成提供了一个合适的基础,这些报告将被提交以支持本协议按照[[RFC6121#参考性文献|PROCESS]]从建议标准发展成草案标准. | ||
+ | |||
+ | 特性: | ||
+ | message-body | ||
+ | 描述: | ||
+ | 支持<message/>节的<body/>子元素. | ||
+ | 章节: | ||
+ | [[RFC6121#Body元素|5.2.3]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 N/A. | ||
+ | |||
+ | 特性: | ||
+ | message-subject | ||
+ | 描述: | ||
+ | 支持<message/>节的<subject/>子元素. | ||
+ | 章节: | ||
+ | [[RFC6121#Subject元素|5.2.4]] | ||
+ | 角色: | ||
+ | 客户端 应该, 服务器 N/A. | ||
+ | |||
+ | 特性: | ||
+ | message-thread | ||
+ | 描述: | ||
+ | 支持<message/>节的<thread/>子元素. | ||
+ | 章节: | ||
+ | [[RFC6121#Thread元素|5.2.5]] | ||
+ | 角色: | ||
+ | 客户端 应该, 服务器 N/A. | ||
+ | |||
+ | 特性: | ||
+ | message-type-support | ||
+ | 描述: | ||
+ | 支持接收类型为"normal", "chat", "groupchat", "headline", 和 "error"的消息. | ||
+ | 章节: | ||
+ | [[RFC6121#Type属性|5.2.2]] | ||
+ | 角色: | ||
+ | 客户端 应该, 服务器 N/A. | ||
+ | |||
+ | 特性: | ||
+ | message-type-deliver | ||
+ | 描述: | ||
+ | 适当地递送类型为"normal", "chat", "groupchat", "headline", 和 "error"的消息. | ||
+ | 章节: | ||
+ | [[RFC6121#处理XML节的服务器规则|8]] | ||
+ | 角色: | ||
+ | 客户端 N/A, 服务器 应该. | ||
+ | |||
+ | 特性: | ||
+ | presence-notype | ||
+ | 描述: | ||
+ | 把没有'type'属性的出席信息节视为指示可用性(在线). | ||
+ | 章节: | ||
+ | [[RFC6121#Type属性|4.7.1]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | presence-probe | ||
+ | 描述: | ||
+ | 发送和接收'type'属性为"probe"的出席信息节用来查询出席信息. | ||
+ | 章节: | ||
+ | [[RFC6121#Type属性|4.7.1]] | ||
+ | 角色: | ||
+ | 客户端 N/A, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | presence-sub-approval | ||
+ | 描述: | ||
+ | 把类型为"subscribed"的出站出席信息节视为对之前从另一个实体接收到的出席信息订阅请求的批准动作, 并且把类型为"subscribed"的入站出席信息节视为来自另一个实体的订阅批准. | ||
+ | 章节: | ||
+ | [[RFC6121#请求订阅|3.1]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | presence-sub-cancel | ||
+ | 描述: | ||
+ | 把类型为"unsubscribed"的出站出席信息节视为对之前从另一个实体接收到的出席信息订阅请求的拒绝动作或对之前授予另一个实体的订阅批准的取消动作, 并且把类型为"unsubscribed"的入站出席信息节视为来自另一个实体的订阅拒绝或取消.. | ||
+ | 章节: | ||
+ | [[RFC6121#撤销被订阅|3.2]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | presence-sub-preapproval | ||
+ | 描述: | ||
+ | 把类型为"subscribed"的出站出席信息节视为对之前对从另一个实体接收到的订阅请求的预批准动作的确认; 这包括支持'jabber:iq:roster'命名空间的<item/>元素的'approved'属性. | ||
+ | 章节: | ||
+ | [[RFC6121#预批准被订阅请求|3.4]] | ||
+ | 角色: | ||
+ | 客户端 可以, 服务器 可以. | ||
+ | |||
+ | 特性: | ||
+ | presence-sub-request | ||
+ | 描述: | ||
+ | 把类型为"subscribe"的出站出席信息节视为请求订阅另一个实体的出席信息, 并且把类型为"subscribe"的入站出席信息节视为来自另一个实体的出席信息订阅请求. | ||
+ | 章节: | ||
+ | [[RFC6121#请求订阅|3.1]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | presence-sub-unsubscribe | ||
+ | 描述: | ||
+ | 把类型为"unsubscribe"的出站出席信息节视为从另一个实体取消订阅的动作, 并把类型为"unsubscribe"的入站出席信息视为来自另一个实体的取消订阅通知. | ||
+ | 章节: | ||
+ | [[RFC6121#取消订阅|3.3]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | presence-unavailable | ||
+ | 描述: | ||
+ | 把'type'属性为"unavailable"的出席信息节视为指示失去可用性. | ||
+ | 章节: | ||
+ | [[RFC6121#Type属性|4.7.1]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | roster-get | ||
+ | 描述: | ||
+ | 把类型为"get"包含一个空的由'jabber:iq:roster'命名空间限定的<query/>元素的IQ节视为一个获取某个服务器上的某个帐号的名册信息的请求. | ||
+ | 章节: | ||
+ | [[RFC6121#名册获取|2.1.3]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | roster-set | ||
+ | 描述: | ||
+ | 把类型为"set"包含一个由'jabber:iq:roster'命名空间限定的<query/>元素的IQ节视为一个添加或更新包含在该<query/>元素中的条目的请求. | ||
+ | 章节: | ||
+ | [[RFC6121#名册设置|2.1.5]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | roster-push | ||
+ | 描述: | ||
+ | 无论何时服务器那一边名册信息出现了实质上的改变,都要发送一个名册推送给感兴趣的资源, 或在从该服务器接收到的时候处理这类推送. | ||
+ | 章节: | ||
+ | [[RFC6121#名册推送|2.1.6]] | ||
+ | 角色: | ||
+ | 客户端 必须, 服务器 必须. | ||
+ | |||
+ | 特性: | ||
+ | roster-version | ||
+ | 描述: | ||
+ | 把由'jabber:iq:roster'命名空间限定的<query/>元素中的'ver'属性视为被发送或接收的名册信息的特定版本的标识符. | ||
+ | 章节: | ||
+ | [[RFC6121#Ver属性|2.1.1]] | ||
+ | 角色: | ||
+ | 客户端 应该, 服务器 必须. | ||
+ | |||
+ | ==参考== | ||
===规范引用=== | ===规范引用=== | ||
+ | |||
+ | [DELAY] Saint-Andre, P., “Delayed Delivery,” XSF XEP 0203, September 2009. | ||
+ | |||
+ | [KEYWORDS] Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels,” BCP 14, RFC 2119, March 1997 (TXT, HTML, XML). | ||
+ | |||
+ | [XML] Maler, E., Yergeau, F., Sperberg-McQueen, C., Paoli, J., and T. Bray, “Extensible Markup Language (XML) 1.0 (Fifth Edition),” World Wide Web Consortium Recommendation REC‑xml‑20081126, November 2008 (HTML). | ||
+ | |||
+ | [XML-NAMES] Bray, T., Hollander, D., and A. Layman, “Namespaces in XML,” W3C REC-xml-names, January 1999. | ||
+ | |||
+ | [XMPP-CORE] Saint-Andre, P., “Extensible Messaging and Presence Protocol (XMPP): Core,” RFC 6120, March 2011. | ||
+ | |||
===参考文献=== | ===参考文献=== | ||
+ | |||
+ | [CPIM] Peterson, J., “Common Profile for Instant Messaging (CPIM),” RFC 3860, August 2004 (TXT). | ||
+ | |||
+ | [CPP] Peterson, J., “Common Profile for Presence (CPP),” RFC 3859, August 2004 (TXT). | ||
+ | |||
+ | [DOS] Handley, M., Rescorla, E., and IAB, “Internet Denial-of-Service Considerations,” RFC 4732, December 2006 (TXT). | ||
+ | |||
+ | [IMP-MODEL] Day, M., Rosenberg, J., and H. Sugano, “A Model for Presence and Instant Messaging,” RFC 2778, February 2000. | ||
+ | |||
+ | [IMP-REQS] Day, M., Aggarwal, S., and J. Vincent, “Instant Messaging / Presence Protocol Requirements,” RFC 2779, February 2000 (TXT). | ||
+ | |||
+ | [IMP-SRV] Peterson, J., “Address Resolution for Instant Messaging and Presence,” RFC 3861, August 2004 (TXT). | ||
+ | |||
+ | [INTEROP] Masinter, L., “Formalizing IETF Interoperability Reporting,” Work in Progress, October 2005. | ||
+ | |||
+ | [IRC] Kalt, C., “Internet Relay Chat: Architecture,” RFC 2810, April 2000 (TXT). | ||
+ | |||
+ | [IRI] Duerst, M. and M. Suignard, “Internationalized Resource Identifiers (IRIs),” RFC 3987, January 2005 (TXT). | ||
+ | |||
+ | [PROCESS] Bradner, S., “The Internet Standards Process -- Revision 3,” BCP 9, RFC 2026, October 1996 (TXT). | ||
+ | |||
+ | [REPORTS] Dusseault, L. and R. Sparks, “Guidance on Interoperation and Implementation Reports for Advancement to Draft Standard,” BCP 9, RFC 5657, September 2009 (TXT). | ||
+ | |||
+ | [RFC3920] Saint-Andre, P., Ed., “Extensible Messaging and Presence Protocol (XMPP): Core,” RFC 3920, October 2004 (TXT, HTML, XML). | ||
+ | |||
+ | [RFC3921] Saint-Andre, P., Ed., “Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence,” RFC 3921, October 2004 (TXT, HTML, XML). | ||
+ | |||
+ | [SASL] Melnikov, A. and K. Zeilenga, “Simple Authentication and Security Layer (SASL),” RFC 4422, June 2006 (TXT). | ||
+ | |||
+ | [SIP-PRES] Rosenberg, J., “A Presence Event Package for the Session Initiation Protocol (SIP),” RFC 3856, August 2004 (TXT). | ||
+ | |||
+ | [TLS] Dierks, T. and E. Rescorla, “The Transport Layer Security (TLS) Protocol Version 1.2,” RFC 5246, August 2008 (TXT). | ||
+ | |||
+ | [TLS-CERTS] Saint-Andre, P. and J. Hodges, “Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS),” RFC 6125, March 2011. | ||
+ | |||
+ | [UNICODE] The Unicode Consortium, “The Unicode Standard, Version 6.0,” 2010. | ||
+ | |||
+ | [URI] Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax,” STD 66, RFC 3986, January 2005 (TXT). | ||
+ | |||
+ | [UUID] Leach, P., Mealling, M., and R. Salz, “A Universally Unique IDentifier (UUID) URN Namespace,” RFC 4122, July 2005 (TXT, HTML, XML). | ||
+ | |||
+ | [XEP-0016] Millard, P. and P. Saint-Andre, “Privacy Lists,” XSF XEP 0016, February 2007. | ||
+ | |||
+ | [XEP-0045] Saint-Andre, P., “Multi-User Chat,” XSF XEP 0045, July 2008. | ||
+ | |||
+ | [XEP-0054] Saint-Andre, P., “vcard-temp,” XSF XEP 0054, July 2008. | ||
+ | |||
+ | [XEP-0071] Saint-Andre, P., “XHTML-IM,” XSF XEP 0071, September 2008. | ||
+ | |||
+ | [XEP-0115] Hildebrand, J., Saint-Andre, P., and R. Tronçon, “Entity Capabilities,” XSF XEP 0115, February 2008. | ||
+ | |||
+ | [XEP-0147] Saint-Andre, P., “XMPP URI Scheme Query Components,” XSF XEP 0147, September 2006. | ||
+ | |||
+ | [XEP-0160] Saint-Andre, P., “Best Practices for Handling Offline Messages,” XSF XEP 0160, January 2006. | ||
+ | |||
+ | [XEP-0163] Saint-Andre, P. and K. Smith, “Personal Eventing Protocol,” XSF XEP 0163, July 2010. | ||
+ | |||
+ | [XEP-0191] Saint-Andre, P., “Simple Communications Blocking,” XSF XEP 0191, February 2007. | ||
+ | |||
+ | [XEP-0201] Saint-Andre, P., Paterson, I., and K. Smith, “Best Practices for Message Threads,” XSF XEP 0201, November 2010. | ||
+ | |||
+ | [XEP-0237] Saint-Andre, P. and D. Cridland, “Roster Versioning,” XSF XEP 0237, March 2010. | ||
+ | |||
+ | [XML-DATATYPES] Biron, P. and A. Malhotra, “XML Schema Part 2: Datatypes Second Edition,” W3C REC-xmlschema-2, October 2004 (HTML). | ||
+ | |||
+ | [XML-SCHEMA] Thompson, H., Maloney, M., Mendelsohn, N., and D. Beech, “XML Schema Part 1: Structures Second Edition,” World Wide Web Consortium Recommendation REC-xmlschema-1-20041028, October 2004 (HTML). | ||
+ | |||
+ | [XMPP-ADDR] Saint-Andre, P., “Extensible Messaging and Presence Protocol (XMPP): Address Format,” RFC 6122, March 2011. | ||
+ | |||
+ | [XMPP-URI] Saint-Andre, P., “Internationalized Resource Identifiers (IRIs) and Uniform Resource Identifiers (URIs) for the Extensible Messaging and Presence Protocol (XMPP),” RFC 5122, February 2008 (TXT). | ||
+ | |||
+ | [VCARD] Dawson, F. and T. Howes, “vCard MIME Directory Profile,” RFC 2426, September 1998 (HTML, XML). | ||
+ | |||
+ | ==附录A. 订阅状态== | ||
+ | |||
+ | 本章提供关于订阅状态的详细信息和订阅相关的出席信息节(即, 类型为"subscribe", "subscribed", "unsubscribe", 和"unsubscribed"的出席信息节)的服务器处理. | ||
+ | |||
+ | ===A.1. 已定义的状态=== | ||
+ | |||
+ | 有四个主要的订阅状态(这些状态是从用户而不是联系人的视角来描述的): | ||
+ | |||
+ | : None: | ||
+ | :: 该用户没有对该联系人的出席信息的订阅, 且该联系人也没有对该用户的出席信息的订阅. | ||
+ | : To: | ||
+ | :: 该用户有一个对该联系人的订阅, 但该联系人没有对该用户的出席信息的订阅. | ||
+ | : From: | ||
+ | :: 该联系人有一个对该用户的出席信息的订阅, 该用户没有对该联系人的出席信息的订阅. | ||
+ | : Both: | ||
+ | :: 该用户和该联系人对对方的出席信息都有订阅(即, 'from'和'to'的联合). | ||
+ | |||
+ | :实现备注: 为了如接下来的章节所述那样处理订阅相关的出席信息节, 订阅状态"None"包括该联系人根本不在该用户的名册中的情形, 即, 一个从用户的名册的视角来看是未知的实体. | ||
+ | |||
+ | 前述的状态被各种和未决的入站和出站订阅相关的子状态所补充, 所以有高达九种可能的订阅状态: | ||
+ | |||
+ | :# "None" = 联系人和用户未彼此订阅, 且未曾向对方请求一个订阅; 反映在该用户的名册中就是 subscription='none'. | ||
+ | :# "None + Pending Out" = 联系人和用户未彼此订阅, 而用户已经发送了一个订阅请求给联系人但是尚未被应答; 这反映在该用户的名册中就是 subscription='none' 且 ask='subscribe'. | ||
+ | :# "None + Pending In" = 联系人和用户未彼此订阅, 而联系人已经发送了一个订阅请求给用户但是尚未被应答. 这个状态可以反映也可以不反映在该用户的名册中, 如下: 如果该用户曾经为该联系人创建一个名册条目那么该服务器必须维护那个名册条目并且也注意到该入站出席信息订阅请求的存在, 反之如果该用户未曾为该联系人创建一个名册条目那么该用户的服务器必须注意该入站出席信息订阅请求的存在但是不能(MUST NOT)为该联系人创建一个名册条目(反之, 在把该联系人添加到该用户的名册之前,该服务器必须等到该用户已经批准该订阅请求). | ||
+ | :# "None + Pending Out+In" = 联系人和用户未彼此订阅, 联系人已经发送了一个订阅请求给用户但是尚未被应答, 而用户已经发送了一个订阅请求给联系人但是尚未被应答; 这反映在该用户的名册中就是 subscription='none' 且 ask='subscribe'. | ||
+ | :# "To" = 用户已经订阅了联系人(单向); 这反映在该用户的名册中就是 subscription='to'. | ||
+ | :# "To + Pending In" = 用户已经订阅了联系人, 而联系人已经发送了一个订阅请求给用户但是尚未被应答; 这反映在该用户的名册中就是 subscription='to'. | ||
+ | :# "From" = 联系人已经订阅了用户(单向); 这反映在该用户的名册中就是 subscription='from'. | ||
+ | :# "From + Pending Out" = 联系人已经订阅了用户, 而用户已经发送了一个订阅请求给联系人但是尚未被应答; 这反映在该用户的名册中就是 subscription='from' 且 ask='subscribe'. | ||
+ | :# "Both" = 用户和联系人彼此订阅了(双向); 这反映在该用户的名册中就是 subscription='both'. | ||
+ | |||
+ | ===A.2. 出站出席信息订阅节的服务器处理=== | ||
+ | |||
+ | 出站出席信息订阅节允许该用户管理他的或她对该联系人的出席信息的订阅(通过"subscribe"和"unsubscribe"类型), 并且管理联系人对该用户的出席信息的访问(通过"subscribed"和"unsubscribed"类型). | ||
+ | |||
+ | 以下规则适用于该节的出站路由和该用户的名册的变更. (这些规则是从该用户而不是该联系人的视角描述的. 另外, "S.N." 代表不应该(SHOULD NOT)而"M.N." 代表不能(MUST NOT).) | ||
+ | |||
+ | ====A.2.1. Subscribe==== | ||
+ | |||
+ | 表2: 出站"subscribe"节的处理 | ||
+ | |||
+ | <source lang="text"> | ||
+ | +------------------------------------------------------------------+ | ||
+ | | EXISTING STATE | ROUTE? | NEW STATE | | ||
+ | +------------------------------------------------------------------+ | ||
+ | | "None" | MUST [1] | "None + Pending Out" | | ||
+ | | "None + Pending Out" | MUST | no state change | | ||
+ | | "None + Pending In" | MUST [1] | "None + Pending Out+In" | | ||
+ | | "None + Pending Out+In" | MUST | no state change | | ||
+ | | "To" | MUST | no state change | | ||
+ | | "To + Pending In" | MUST | no state change | | ||
+ | | "From" | MUST [1] | "From + Pending Out" | | ||
+ | | "From + Pending Out" | MUST | no state change | | ||
+ | | "Both" | MUST | no state change | | ||
+ | +------------------------------------------------------------------+ | ||
+ | </source> | ||
+ | |||
+ | : [1] 一个状态变更到"pending out"包括了在该用户的名册中把'ask'标志的值设为"subscribe". | ||
+ | |||
+ | ====A.2.2. Unsubscribe==== | ||
+ | |||
+ | 表3: 出站"unsubscribe"节的处理 | ||
+ | |||
+ | <source lang="text"> | ||
+ | +-----------------------------------------------------------------+ | ||
+ | | EXISTING STATE | ROUTE? | NEW STATE | | ||
+ | +-----------------------------------------------------------------+ | ||
+ | | "None" | MUST | no state change | | ||
+ | | "None + Pending Out" | MUST | "None" | | ||
+ | | "None + Pending In" | MUST | no state change | | ||
+ | | "None + Pending Out+In" | MUST | "None + Pending In" | | ||
+ | | "To" | MUST | "None" | | ||
+ | | "To + Pending In" | MUST | "None + Pending In" | | ||
+ | | "From" | MUST | no state change | | ||
+ | | "From + Pending Out" | MUST | "From" | | ||
+ | | "Both" | MUST | "From" | | ||
+ | +-----------------------------------------------------------------+ | ||
+ | </source> | ||
+ | |||
+ | ====A.2.3. Subscribed==== | ||
+ | |||
+ | 表4: 出站"subscribed"节的处理 | ||
+ | |||
+ | <source lang="text"> | ||
+ | +-----------------------------------------------------------------+ | ||
+ | | EXISTING STATE | ROUTE? | NEW STATE | | ||
+ | +-----------------------------------------------------------------+ | ||
+ | | "None" | M.N. | pre-approval [1] | | ||
+ | | "None + Pending Out" | M.N. | pre-approval [1] | | ||
+ | | "None + Pending In" | MUST | "From" | | ||
+ | | "None + Pending Out+In" | MUST | "From + Pending Out" | | ||
+ | | "To" | M.N. | pre-approval [1] | | ||
+ | | "To + Pending In" | MUST | "Both" | | ||
+ | | "From" | M.N. | no state change | | ||
+ | | "From + Pending Out" | M.N. | no state change | | ||
+ | | "Both" | M.N. | no state change | | ||
+ | +-----------------------------------------------------------------+ | ||
+ | </source> | ||
+ | |||
+ | : [1] 关于订阅预批准的详细信息见[[RFC6121#预批准被订阅请求|3.4]]. | ||
+ | |||
+ | ====A.2.4. Unsubscribed==== | ||
+ | |||
+ | 表5: 出站"unsubscribed"节的处理 | ||
+ | |||
+ | <source lang="text"> | ||
+ | +-----------------------------------------------------------------+ | ||
+ | | EXISTING STATE | ROUTE? | NEW STATE | | ||
+ | +-----------------------------------------------------------------+ | ||
+ | | "None" | S.N. | no state change [1] | | ||
+ | | "None + Pending Out" | S.N. | no state change [1] | | ||
+ | | "None + Pending In" | MUST | "None" | | ||
+ | | "None + Pending Out+In" | MUST | "None + Pending Out" | | ||
+ | | "To" | S.N. | no state change [1] | | ||
+ | | "To + Pending In" | MUST | "To" | | ||
+ | | "From" | MUST | "None" | | ||
+ | | "From + Pending Out" | MUST | "None + Pending Out" | | ||
+ | | "Both" | MUST | "To" | | ||
+ | +-----------------------------------------------------------------+ | ||
+ | </source> | ||
+ | |||
+ | : [1] 这一时间可能导致取消一个订阅预批准, 如[[RFC6121#预批准被订阅请求|3.4]]所述. | ||
+ | |||
+ | ===A.3. 入站出席信息订阅节的服务器处理=== | ||
+ | |||
+ | 入站出席信息订阅节从该用户请求一个订阅相关的操作(通过"subscribe"类型), 向该用户通知由该联系人做的订阅相关的动作(通过"unsubscribe"类型),或允许该用户管理该联系人对该用户的的出席信息的访问(通过"subscribed"和"unsubscribed"类型). | ||
+ | |||
+ | 以下规则适用于该入站节的递送和该用户的名册的变更. (这些用于服务器处理入站出席信息订阅节的规则是从该用户而不是该联系人的视角描述的. 另外, "S.N." 代表不应该(SHOULD NOT).) | ||
+ | |||
+ | ====A.3.1. Subscribe==== | ||
+ | |||
+ | 表6: 入站"subscribe"节的处理 | ||
+ | |||
+ | <source lang="xml"> | ||
+ | +------------------------------------------------------------------+ | ||
+ | | EXISTING STATE | DELIVER? | NEW STATE | | ||
+ | +------------------------------------------------------------------+ | ||
+ | | "None" | MUST [1] | "None + Pending In" | | ||
+ | | "None + Pending Out" | MUST | "None + Pending Out+In" | | ||
+ | | "None + Pending In" | S.N. | no state change | | ||
+ | | "None + Pending Out+In" | S.N. | no state change | | ||
+ | | "To" | MUST | "To + Pending In" | | ||
+ | | "To + Pending In" | S.N. | no state change | | ||
+ | | "From" | S.N. [2] | no state change | | ||
+ | | "From + Pending Out" | S.N. [2] | no state change | | ||
+ | | "Both" | S.N. [2] | no state change | | ||
+ | +------------------------------------------------------------------+ | ||
+ | </source> | ||
+ | |||
+ | : [1] 如果该用户之前发送了类型为"subscribed"的出席信息,如[[RFC6121# A.2.3. Subscribed|附录A.2.3]] 和 [[RFC6121#预批准被订阅请求|3.4]]所述, 那么服务器可以以"subscribed"自动应答并把状态改为"From"而不是"None + Pending In". | ||
+ | |||
+ | : [2] 服务器应该自动应答"subscribed". | ||
+ | |||
+ | ====A.3.2. Unsubscribe==== | ||
+ | |||
+ | 当该用户的服务器为该用户接收到一个来自该联系人的类型为"unsubscribe"的出席信息节, 如果从该用户视角来看该节导致一个订阅状态的变更,那么该用户的服务器必需变更该状态, 必须从该联系人递送该出席信息节到该用户, 且代表该用户应该自动应答,发送一个类型为"unsubscribed"的出席信息节给该联系人. 否则该用户的服务器不能(MUST NOT)变更该状态并且(因为没有状态变更)不应该(SHOULD NOT)递送该节. 这些规则被总结在下表中. | ||
+ | |||
+ | 表7: 入站"unsubscribe"节的处理 | ||
+ | |||
+ | <source lang="text"> | ||
+ | +------------------------------------------------------------------+ | ||
+ | | EXISTING STATE | DELIVER? | NEW STATE | | ||
+ | +------------------------------------------------------------------+ | ||
+ | | "None" | S.N. | no state change | | ||
+ | | "None + Pending Out" | S.N. | no state change | | ||
+ | | "None + Pending In" | MUST [1] | "None" | | ||
+ | | "None + Pending Out+In" | MUST [1] | "None + Pending Out" | | ||
+ | | "To" | S.N. | no state change | | ||
+ | | "To + Pending In" | MUST [1] | "To" | | ||
+ | | "From" | MUST [1] | "None" | | ||
+ | | "From + Pending Out" | MUST [1] | "None + Pending Out" | | ||
+ | | "Both" | MUST [1] | "To" | | ||
+ | +------------------------------------------------------------------+ | ||
+ | </source> | ||
+ | |||
+ | :[1] 服务器应该自动应答"unsubscribed". | ||
+ | |||
+ | ====A.3.3. Subscribed==== | ||
+ | |||
+ | 当该用户的服务器为该用户接收到一个来自该联系人的类型为"subscribed"的出席信息节, 如果没有未决的访问该联系人的出席信息的出站请求, 那么它不能(MUST NOT)变更该订阅状态且(因为没有状态变更)不应该(SHOULD NOT)递送该节给该用户. 如果有一个未决的访问该联系人的出席信息的出站请求并且该类型为"subscribed"的入站出席信息节会导致一个订阅状态变更, 那么该用户的服务器必须变更该订阅状态且必须递送该节到该用户. 如果该用户已经订阅了该联系人的出席信息, 该类型为"subscribed"的入站出席信息节不会导致一个订阅状态变更; 所以该用户的服务器不能(MUST NOT)变更该订阅状态且(因为没有状态变更)不应该(SHOULD NOT)递送该节给该用户. 这些规则总结在下表中. | ||
+ | |||
+ | 表8: 入站"subscribed"节的处理 | ||
+ | |||
+ | <source lang="text"> | ||
+ | +------------------------------------------------------------------+ | ||
+ | | EXISTING STATE | DELIVER? | NEW STATE | | ||
+ | +------------------------------------------------------------------+ | ||
+ | | "None" | S.N. | no state change | | ||
+ | | "None + Pending Out" | MUST | "To" | | ||
+ | | "None + Pending In" | S.N. | no state change | | ||
+ | | "None + Pending Out+In" | MUST | "To + Pending In" | | ||
+ | | "To" | S.N. | no state change | | ||
+ | | "To + Pending In" | S.N. | no state change | | ||
+ | | "From" | S.N. | no state change | | ||
+ | | "From + Pending Out" | MUST | "Both" | | ||
+ | | "Both" | S.N. | no state change | | ||
+ | +------------------------------------------------------------------+ | ||
+ | </source> | ||
+ | |||
+ | ====A.3.4. Unsubscribed==== | ||
+ | |||
+ | 当该用户的服务器为该用户接收了一个来自该联系人的类型为"unsubscribed"的出席信息节, 如果没有一个访问该联系人的出席信息的未决出站请求或如果该用户目前已经订阅了该联系人的出席信息, 那么该用户的服务器必须变更该订阅状态且必须递送该节给该用户. 否则, 该用户的服务器不能(MUST NOT)变更该订阅状态且(因为没有状态变更)不应该(SHOULD NOT)递送该节. 这些规则总结在下笔中. | ||
+ | |||
+ | 表9: 入站"unsubscribed"节的处理 | ||
+ | |||
+ | <source lang="text"> | ||
+ | +------------------------------------------------------------------+ | ||
+ | | EXISTING STATE | DELIVER? | NEW STATE | | ||
+ | +------------------------------------------------------------------+ | ||
+ | | "None" | S.N. | no state change | | ||
+ | | "None + Pending Out" | MUST | "None" | | ||
+ | | "None + Pending In" | S.N. | no state change | | ||
+ | | "None + Pending Out+In" | MUST | "None + Pending In" | | ||
+ | | "To" | MUST | "None" | | ||
+ | | "To + Pending In" | MUST | "None + Pending In" | | ||
+ | | "From" | S.N. | no state change | | ||
+ | | "From + Pending Out" | MUST | "From" | | ||
+ | | "Both" | MUST | "From" | | ||
+ | +------------------------------------------------------------------+ | ||
+ | </source> | ||
+ | |||
+ | ==附录B. 限制通讯== | ||
+ | |||
+ | [[RFC6121#参考文献|IMP‑REQS]]的2.3.5节和5.4.10节要求一个兼容的即使消息和出席信息技术需要允许一个用户限制来自选定用户们的通讯. 实现这些的协议定义于[[RFC6121#参考文献|XEP‑0016]]和[[RFC6121#参考文献|XEP‑0191]]中. | ||
+ | |||
+ | ==附录C. vCards== | ||
+ | |||
+ | [[RFC6121#参考文献|IMP‑REQS]]的3.1.3节和4.1.4节要求有可能获取其他联系人的带外联系人信息(例如, 电话号码或email地址). 在XMPP社区中通常使用一个[[RFC6121#参考文献|RFC 2426]] [VCARD]定义的vCard的XML形式来提供这类信息, 但是这超出了本协议的范围(这个协议的文档被包含在[[RFC6121#参考文献|XEP‑0054]]). | ||
+ | |||
+ | ==附录D. 用于jabber:iq:roster的XML Schema== | ||
+ | |||
+ | 以下schema正式地定义本文中使用的'jabber:iq:roster'命名空间, 和[[RFC6121#参考文献|XML‑SCHEMA]]一致. 因为XML流和节的确认是可选的, 本schema不是规范的并且只被提供用于描述用. 关于定义核心XMPP命名空间的schemas, 参考[[RFC6121#规范引用|XMPP‑CORE]]. | ||
+ | |||
+ | <source lang="xml"> | ||
+ | <?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:attribute name='ver' | ||
+ | type='xs:string' | ||
+ | use='optional'/> | ||
+ | </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='approved' | ||
+ | type='xs:boolean' | ||
+ | use='optional'/> | ||
+ | <xs:attribute name='ask' | ||
+ | use='optional'> | ||
+ | <xs:simpleType> | ||
+ | <xs:restriction base='xs:NMTOKEN'> | ||
+ | <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' | ||
+ | default='none'> | ||
+ | <xs:simpleType> | ||
+ | <xs:restriction base='xs:NMTOKEN'> | ||
+ | <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> | ||
+ | </source> | ||
+ | |||
+ | ==附录E. 和RFC 3921的不同== | ||
+ | |||
+ | 在来自实现和应用实践的一致性和延续性以及正式的互操作性测试的基础上, 对[[RFC6121#参考文献|RFC3921]]做了以下大量修改(还有很多编辑方面的修改). | ||
+ | |||
+ | :* 会话建立的协议被决定是不必要的并且因而之前在RFC 3921的第3章定义的内容被移除. 无论如何, 为了向后兼容,鼓励服务器实现声明对该特性的支持, 即使会话建立是一个"no-op"(非选项). | ||
+ | :* 为了更加无缝地修复位于不同服务器上的名册之间缺少订阅状态同步的问题, 澄清和修改了和出席信息订阅请求,出席信息探测和出席信息通知相关的错误处理. | ||
+ | :* 为出席信息探测修改了'from'地址,现在它是纯JID, 而不是全JID. | ||
+ | :* 基于实现和部署实践调整和澄清了节递送规则. | ||
+ | :* 显式地定义了一个服务器被允许递送一个类型为"normal"或"chat"的消息节给所有资源,如果它有办法允许所有的资源选择这一行为. | ||
+ | :* 允许一个服务器使用它自己的机制来确定"最可用的"资源,用来递送消息, 但是提到了来自RFC 3921的机制(基于出席信息优先级)作为可能的机制. | ||
+ | :* 添加了可选的名册信息版本以在多个会话之间名册没有变更(或只变更了很少)的时候节省带宽; 有关的协议交互开始是在[[RFC6121#参考文献|XEP‑0237]]中描述的. | ||
+ | :* 添加了可选的对预批准出席信息订阅的服务器支持,通过类型为"subscribed"的出席信息节, 包括一个新的'approved'属性,该属性能被设为"true" (用于一个预批准订阅) 或 "false" (缺省值). | ||
+ | :* 给<thread/>元素添加了可选的'parent'属性. | ||
+ | :* 把关于通讯限制的协议(定义于RFC 3921的第10章)移回到[[RFC6121#参考文献|XEP‑0016]],那里是它最开始采用的地方. | ||
+ | :* 建议在对探测的应答中返回出席信息不可用(离线). | ||
+ | :* 澄清发送给全JIDs的出席信息探测的处理. | ||
+ | :* 显式地定义了出席信息<priority/>元素的缺省值为零. | ||
+ | :* 移除了对"_im"和"_pres"的SRV记录的支持的建议. | ||
+ | |||
+ | ==附录F. 致谢== | ||
+ | |||
+ | 本文衍生自自RFC 3921,是对它的更新. 没有贡献者们和评论者们的工作,本文是不可能完成的. | ||
+ | |||
+ | 从RFC 3921发布以来,数百人提供了实现反馈, 错误报告, 澄清的请求, 和改善建议. 尽管本文的编辑已经努力找出所有这类反馈, 但他独自对任何其余的错误和含糊的地方负责. | ||
+ | |||
+ | 一些关于名册版本的文本来自[[RFC6121#参考文献|XEP‑0237]],而一些关于消息线索的文本来自[[RFC6121#参考文献|XEP‑0201]]. | ||
+ | |||
+ | 特别感谢 Kevin Smith, Matthew Wild, Dave Cridland, Waqas Hussain, Philipp Hancke, Florian Zeitz, Jonas Lindberg, Jehan Pages, Tory Patnoe, 和其他在工作组最后召集期间做评论的人. | ||
+ | |||
+ | 也感谢代表安全局进行复审的 Richard Barnes. | ||
+ | |||
+ | 工作组主席是 Ben Campbell 和 Joe Hildebrand. 责任区域编辑是 Gonzalo Camarillo. | ||
+ | |||
+ | ==作者的地址== | ||
+ | |||
+ | |||
+ | Peter Saint-Andre | ||
+ | Cisco | ||
+ | 1899 Wyknoop Street, Suite 600 | ||
+ | Denver, CO 80202 | ||
+ | USA | ||
+ | Phone:+1-303-308-3282 | ||
+ | EMail:psaintan@cisco.com | ||
+ | |||
+ | [[Category:XMPP相关RFC]] | ||
+ | [[Category:XMPP核心RFC]] | ||
+ | [[Category:已翻译]] |
2014年3月17日 (一) 06:47的最后版本
本文的英文原文来自RFC 6121
互联网工程任务组(IETF) | P. Saint-Andre |
申请讨论: 6121 | Cisco |
取代: 3921 | 2011年3月 |
类别: 标准跟踪 | |
ISSN: 2070-1721 |
- 可扩展的消息和出席信息协议 (XMPP): 即时消息和出席信息
摘要
- 本文定义提供了遵循RFC2779要求的基本的即时消息(IM)和出席信息功能的可扩展的消息和出席信息协议(XMPP)的核心功能的扩展. 本文取代了 RFC 3921.
本文的状态
- 这是一个互联网标准跟踪文档.
- 本文是互联网工程工作组(IETF)的一个成果. 它代表了IETF社区的一致意见. 它已经公开审核并由互联网工程控制组(IESG)批准发布了. 更多关于互联网标准的信息请参见RFC 5741第2章.
- 关于本文当前状态的信息, 任何错误, 以及如何对它提出反馈,请到 http://www.rfc-editor.org/info/rfc6121 .
版权声明
- Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved.
- This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.
序论
概述
可扩展的消息和出席信息协议(XMPP)是可扩展的标记语言XML的一个应用范畴,它能使两个或更多网络实体之间的结构化并且可扩展的数据进行准实时的交换. 在XMPP‑CORE中定义的XMPP的核心特性提供了各种类型的准实时应用的积木, 通过发送由特定的XML命名空间(参考XML‑NAMES)所限定的应用特有的数据,它们可被叠加在核心之上. 本文定义的XMPP扩展提供一个IMP‑REQS所述的即时消息(IM)和出席信息应用所预期的基本功能.
历史
XMPP的基本语法和语义最开始是由Jabber开源社区开发的, 主要是在1999年. 2002年, 根据 IMP‑REQS ,XMPP工作组被允许基于Jabber协议开发一个适合IETF的即时消息和出席信息技术. 到了2004年10月, 发布了 RFC3920 和 RFC3921 , 意味着那时候XMPP的主要定义完成了.
从2004年开始,互联网社区已经获得了广泛的XMPP实现和布署经验, 包括XMPP标准基金会(XSF)主持下开展的正式的互操作性测试. 本文全面整合了从软件开发者和XMPP服务提供者得到的反馈, 包含了一系列向后兼容的修改,见 附录E . 结果是, 本文反映了互联网社区对于XMPP1.0的即时消息和出席信息特性的初步共识, 因此废止了RFC 3921.
需求
传统上, IM应用组合了以下因素:
- 关注的中心点是某人的联系人的列表或 "好友们" (在XMPP中这个列表被称为一个 "roster"(名册)).
- 使用这类应用的目标是和特定的联系人准实时地交换相关的短文本消息 -- 在章节5.1描述的一对一形式的"聊天会话"中, 快速连续的相关消息的数量经常是很大的.
- 交换消息的催化剂是 "presence" -- 即, 关于特定联系人们的网络可用性的信息(这样就知道谁在线并且有空进行一对一的聊天会话).
- 出席信息仅提供给通过一个被称为"出席信息订阅"的明确协议被授权的联系人.
所以在高层上本文假定用户能完成下列用例:
- 在某人的联系人列表中管理条目
- 和某人的联系人们交换消息
- 和某人的联系人们交换出席信息
- 管理对某人的联系人们发出和接受出席信息的订阅
这些功能性领域的详细定义被包含在 RFC 2779 IMP‑REQS 中, 关于深度需求感兴趣的读者可以参考那个文档. 尽管 XMPP IM 和这里定义的出席信息扩展满足了 RFC 2779 的需求, 它们不是特意为那个协议设计的, 因为在 RFC 2779 出现之前,Jabber开源社区就通过一个开放的开发流程进化出了基本协议. 尽管在XMP标准基金会的XEP系列中发表了一些其他功能领域的XMPP协议扩展(例如, XEP-0045定义的多用户文本聊天), 这类扩展未在本文中定义,因为它们未被 RFC 2779 授权.
- 实现备注: RFC 2779 规定出席信息服务必须独立于IM服务,反之亦然; 即, 必须有可能使用本协议提供一个出席信息服务, 一个消息服务, 或同时提供两者. 尽管本文的文本假定实现和部署会希望提供统一的IM和出席信息服务, 对于一个XMPP服务来说,并不强制同时提供一个出席信息服务和一个消息服务, 并且协议使得为出席信息和为消息提供独立和特别的服务成为可能. (例如, 如果一个客户端尝试发送一个<message/>节, 一个只有出席信息的服务可能返回一个<service-unavailable/>节错误.)
功能汇总
这个非规范性的章节提供一个对开发者友好的, 基于XMPP的IM和出席信息特性的功能汇总; 参考接下来的的章节来察看这些特性的规范性的定义.
XMPP‑CORE 定义了一个XMPP客户端如何连接到一个XMPP服务器. 特别是, 它定义了客户端在一个XMPP网络中被允许发送XML节(XMPP中的基本含意单位)给其他实体之前需要满足的前提条件. 这些前提条件包括,XML流的协商,XML流头的交换, 可选的通过传输层安全TLS的通道加密, 通过简单验证和安全层SASL强制验证, 和把资源绑定到客户端所在的流上. 关于这些前提条件的细节,读者可以参考 XMPP‑CORE, XMPP‑CORE 的知识假定已经在此.
- 互操作性备注: RFC3921 定义了一个额外的前提条件: 一个即时消息和出席信息会话的正式建立. 实现和部署经验已经表明这个额外的步骤是不必要的. 无论如何, 为了向后兼容,一个实现可以仍提供那个特性. 这使得新软件节省一个回合而旧软件能够连接.
满足了XMPP‑CORE中的前提条件之后, XMPP客户端就有了一个和一个XMPP服务器长连的XML流, 这使得该用户能控制客户端在该流上发送和接收基本上不限数量的XML节. 这样一个流可被用于交换消息, 分享出席信息, 并且准实时地进行结构化的 请求-应答 交互. 在XML流的协商之后, 即时消息和出席信息会话的典型流程如下:
术语
本文中的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", 和 "OPTIONAL" 的解释参见RFC 2119 关键字 .
本文继承了 XMPP‑CORE 中定义的术语.
术语 "automated client" 和 "interactive client" 的含义和 TLS‑CERTS 中定义的相同.
为了方便, 本文借用了术语 "user" 来指代一个XMPP帐号的所有者; 无论如何, 帐号所有者不需要是自然人并且可以是机器人, 设备, 或其他自动化的应用.
一些其他的术语, 类似 "interested resource", 在本文的正文中定义.
接下来的"XML Notation"用在IRI中展示无法用纯ASCII文档表达的字符串, 本文的一些例子中使用了格式 "&#x...." 作为一个符号设备来展示 UNICODE 字符串(例如, 字符串 "ř" 代表 Unicode 字符串 LATIN SMALL LETTER R WITH CARON); 这个格式在XMPP系统中绝对不会在网络上被发送.
在例子中, 每一行已经被封装成便于阅读的样子, "[...]" 意味着省略, 并且使用了以下前缀(这些前缀不会在通讯中被发送):
- C: = 客户端
- CC: = 联系人的客户端
- CS: = 联系人的服务器
- S: = 服务器
- UC: = 用户的客户端
- US: = 用户的服务器
读者需要注意这些例子不包括细节, 并且例子里的一些协议流程中, 展示的备用步骤不一定是由前一个步骤发送的确切的数据触发的; 本文或常用参考文档中的协议规范所用到的所有用例里面提供的例子都遵从上述规则. 所有例子都是虚构的并且交换的信息 (例如, 用户名和密码) 不代表任何现存的用户和服务器.
管理名册
在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.
管理出席信息订阅
为了保护XMPP用户们的隐私, 出席信息仅向某个用户已经批准的其他实体披露. 当某个同意允许其他实体察看其出席信息时, 该实体被称为对该用户的出席信息有一个"订阅(subscription)". 一个对某用户的出席信息有订阅的实体或一个用户对其有出席信息订阅的实体被称为"联系人(contact)" (在本文中术语"联系人(contact)"在不严格的概念上也用于指代潜在的联系人或一个用户的名册中的任何条目).
在XMPP中, 一个订阅是超越出席信息会话存在的; 实际上, 直到联系人取消订阅或用户撤销之前已授权的订阅之前它都会持续存在. (这个模式不同于会话初始协议(SIP)中对出新信息订阅的使用, 参见SIP‑PRES.)
在XMPP中订阅的管理是通过发送包含特别定义的属性("subscribe", "unsubscribe", "subscribed", 和 "unsubscribed")的presence节来实现的.
- 实现备注: 当一个服务器处理或生成一个类型为"subscribe", "subscribed", "unsubscribe", 或 "unsubscribed"的出站出席信息节, 该服务器必须在出站出席信息节中标记发送的实体的纯JID <localpart@domainpart>, 而不是全JID <localpart@domainpart/resourcepart>. 这个规则的强制性简化了出席信息订阅的模式并且有助于防止出席信息泄露; 关于出席信息泄露的信息, 参见XMPP‑CORE的安全事项.
订阅状态在用户和联系人两边的名册中都可以被反映. 本章不覆盖每个和出席信息订阅相关的可能的情景, 而主要是讲述一个用户和一个联系人之间开启一个共同的订阅的协议流程. 关于订阅状态的完整细节可以在附录A中找到.
请求订阅
一个"订阅请求(subscription request)"是一个某用户为了得到永久性地订阅某个联系人出席信息的授权而发出的请求; 语法构成上它是一个'type'属性值为"subscribe"的presence节. 一个订阅请求由某用户客户端生成, 由(潜在的)联系人的服务器处理, 从表现上来看则是联系人通过联系人的客户端来处理. 以下章节描述其工作流程.
- 实现备注: 出席信息订阅请求被发送到可用的资源, 然而和订阅状态变更相关的名册推送被发送到感兴趣的资源. 所以, 如果一个资源希望同时接收到订阅请求和名册推送, 它必须同时发送初始出席信息和请求名册.
客户端生成出站订阅请求
一个用户的客户端发送一个类型为"subscribe"并且指定了一个潜在联系人的纯JID<contact@domainpart>为'to'地址的presence节,生成一个订阅请求.
UC: <presence id='xk3h1v69' to='juliet@example.com' type='subscribe'/>
当一个用户发送一个出席信息订阅请求给一个潜在的即时消息和出席信息联系人, 其'to'属性的值必须是一个纯JID <contact@domainpart>而不是一个全JID <contact@domainpart/resourcepart>, 因为预期的结果是该用户从该联系人的所有资源接收出席信息, 而不仅是在'to'属性中指定的特定资源. 纯JIDs的使用也简化了该用户的服务器和联系人的服务器的订阅过程, 出席信息探测, 和出席信息通知.
为了跟踪的目的, 客户端应该在一个出席信息订阅请求中包含一个'id'属性.
- 实现备注: 很多XMPP客户端在生成一个出站出席信息订阅的时候向用户提示关于潜在联系人的信息(例如, "handle" 以及期望的名册组),因而在发送该出站出席信息订阅请求之前发送一个roster set. 这个行为是可选的, 因为客户端在上传用户提供的关于该联系人的信息之前可以先等待从服务器接收初始的roster push. 服务器必须以任何顺序处理roster set和出站出席信息订阅请求(即, 以客户端生成的任何顺序).
服务器处理出站订阅请求
接收出站出席信息订阅请求之后, 该用户的服务器必须处理如下.
- 在处理该请求之前, 该用户的服务器必须检查包含在'to'属性中的JID的语法(无论如何, 据信一些现有的实现不执行这个检查). 如果该JID的格式是<contact@domainpart/resourcepart>而不是<contact@domainpart>, 该用户的服务器应该把该请求当成是指向该联系人的纯JID并据此修改'to'地址. 该服务器也可以遵循XMPP‑ADDR]定义的格式来检查该JID地址并可能返回一个<jid-malformed/>节错误.
- 如果潜在的联系人位于和该用户相同的服务器上, 那么在处理该订阅请求和递送它到本地联系人的时候该服务器必须遵循章节3.1.3定义的规则.
- 如果潜在的联系人位于一个远端服务器, 受本地服务策略的约束该用户的服务器必须接着按照核心XMPP节处理规则路由该节到那个远端的域. (这可能导致返回一个适当的节错误给该用户, 类似<remote-server-timeout/>.)
如上所述, 在本地递送或远程路由该出席信息订阅请求之前, 该用户的服务器必须给该出站订阅请求标记该用户的纯JID <user@domainpart>.
US: <presence from='romeo@example.net' id='xk3h1v69' to='juliet@example.com' type='subscribe'/>
如果该出席信息订阅请求不是本地递送或远程路由的(例如, 因为该请求格式异常, 该本地联系人不存在, 该远程服务器不存在, 尝试联系远端服务器超时, 或任何被该用户的服务器经历或确认的其他错误), 那么该用户的服务器必须返回一个适当的错误节给该用户. 例子如下.
US: <presence from='juliet@example.com' id='xk3h1v69' to='romeo@example.net' type='error'> <error type='modify'> <remote-server-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence>
在本地递送或远程路由该出席信息订阅请求之后, 该用户的服务器必须接着发送一个roster push给所有该用户的感兴趣的资源, 包含带一个"none"订阅状态并且带一个表明订阅等待批准符号(一个值为"subscribe"的'ask'属性)的潜在联系人 .
US: <iq id='b89c5r7ib574' to='romeo@example.net/foo' type='set'> <query xmlns='jabber:iq:roster'> <item ask='subscribe' jid='juliet@example.com' subscription='none'/> </query> </iq> US: <iq id='b89c5r7ib575' to='romeo@example.net/bar' type='set'> <query xmlns='jabber:iq:roster'> <item ask='subscribe' jid='juliet@example.com' subscription='none'/> </query> </iq>
如果一个远端联系人在可配置的时间内不批准或拒绝该订阅请求, 该用户的服务器应该基于一个实现特有的机制重新发送该订阅请求给该联系人(例如, 任何该用户的一个新资源可用的时候, 或在过了一个特定数量的时间之后); 这有助于从原有的订阅请求被路由到远程域发生的短暂的,无声的错误中恢复. 这样做的时候, 建议该服务器包含一个'id'属性,这样它能跟踪重发的订阅请求的应答.
服务器处理入站订阅请求
在处理入站出席信息订阅请求之前, 联系人的服务器应该检查包含在'to'属性中的JID的语法.如果该JID的格式是<contact@domainpart/resourcepart>而不是<contact@domainpart>, 该联系人的服务器应该把该请求当成是被指向该联系人的纯JID并据此修改'to'地址. 该服务器也可以遵循XMPP‑ADDR]定义的格式来检查该JID地址并可能返回一个<jid-malformed/>节错误.
处理入站出席信息订阅请求的时候, 联系人的服务器必须遵循以下规则:
- 1. 首先, 联系人的服务器不能(MUST NOT)以联系人的身份批准订阅请求 -- 除非该来联系人已经 (a) 如章节3.4所述预批准来自用户的订阅请求, (b) 配置它的账户自动批准订阅请求, 或 (c) 从它的服务器提供者那里接受了一个协议以允许自动批准(例如, 在一个企业部署之中通过一个雇佣协议). 否则, 如果一个订阅请求要求批准那么联系人的服务器必须递送那个请求到联系人的可用资源并由该联系人批准或拒绝.
- 2. 如果联系人存在并且该用户已经有一个对该联系人的出席信息的订阅, 那么该联系人的服务器必须把一个类型为"subscribed"的presence节从该联系人的纯JID发送到该用户的纯JID以代表该联系人自动应答. 同样, 如果该联系人以前发送过一个类型为"subscribed"的presence节并且联系人的服务器假设对于该用户的出席信息订阅是"预批准的"(见章节3.4), 那么该联系人的服务器也应该以该联系人的身份自动应答.
CS: <presence from='juliet@example.com' id='xk3h1v69' to='romeo@example.net' type='subscribed'/>
- 3. 另外, 如果在订阅请求被联系人的服务器接收到的时候该联系人有至少一个可用的资源, 那么该联系人的服务器必须依据第八章发送那个订阅请求给所有可用的资源. 作为获得该出席信息订阅请求的回执的一个方法, 该联系人的服务器可以从该联系人的纯JID发送一个类型为"unavailable"的presence节到该用户的纯JID(该用户的客户端不能(MUST NOT)假定这个回执提供了关于该联系人的出席信息, 因为它来自该联系人的纯JID并且在该订阅请求被批准之前就收到了).
- 4. 另外, 如果在订阅请求被联系人的服务器接收到的时候该联系人没有可用的资源, 那么该联系人的服务器必须保持一个完整的包括该订阅请求的presence节的记录, 包括其中任何扩展的内容(见XMPP‑CORE的章节8.4), 并且在该联系人接下来有一个可用的资源的时候递送该请求. 无论何时该联系人建立了一个可用的资源该联系人的服务器必须继续递送该订阅请求, 直到该联系人要么批准要么拒绝该请求. (当该联系人接下来有一个可用的资源的时候,该联系人的服务器不能(MUST NOT)从任何给定用户递送多个订阅请求; 例如, 如果该用户发送多个订阅请求到该联系人而此时该联系人是离线的, 该联系人的服务器应该只存储那些请求中的一个, 比如第一个请求或最后一个请求, 并且当该联系人接下来有一个可用资源的时候必须只递送这些请求中的一个; 这有助于防止"垃圾订阅请求".)
- 安全警告: 直到以及除非该联系人如章节3.1.4所述批准该订阅请求, 该联系人的服务器不能(MUST NOT)在该联系人的名册上添加该用户的条目.
- 安全警告: 对于联系人的服务器存储完整的出席信息订阅请求的节的强制要求带来了一个应用程序资源衰竭攻击的可能性(见DOS的章节2.1.2), 例如, 由一个流氓服务器或一个协调好的用户群(例如, 一个 botnet) 攻击该联系人的服务器或特定联系人. 提醒服务器实现者们关注这类攻击的可能性并为对抗它提供工具, 比如允许服务管理员设置服务器整体或任何给定联系人存储的入站出席信息订阅请求的数量或大小的限制.
客户端处理入站订阅请求
当一个可交互的客户端接收到一个订阅请求, 它必须把该请求展示给控制该客户端的自然人(即, 该 "联系人") 来批准, 除非该联系人被显式地配置成该客户端自动批准或拒绝如上所述的某些或全部订阅请求. 一个不是被自然人控制的自动客户端将有自己的应用特有的规则以批准或拒绝订阅请求.
客户端通过发送一个类型为"subscribed"的presence节来批准一个订阅请求, 联系人的服务器会作章节3.1.5所述的处理,用户的服务器会作章节3.1.6所述的处理.
CC: <presence id='h4v1c4kj' to='romeo@example.net' type='subscribed'/>
客户端通过发送一个类型为"unsubscribed"的presence节来拒绝一个订阅请求, 联系人的服务器和用户的服务器都会作章节3.2所述的处理.
CC: <presence id='tb2m1b59' to='romeo@example.net' type='unsubscribed'/>
为了跟踪的目的, 客户端应该在一个订阅批准或订阅拒绝中包含一个'id'属性; 这个'id'属性不能(MUST NOT)镜像该订阅请求的'id'属性.
服务器处理出站订阅批准
当联系人的客户端发送订阅批准的时候, 联系人的服务器必须以联系人的纯JID<contact@domainpart>标记这个出站的节并本地递送或远程路由该节到用户那里.
CS: <presence from='juliet@example.com' id='h4v1c4kj' to='romeo@example.net' type='subscribed'/>
联系人的服务器接着必须发送一个updated roster push(更新名册推送)给所有联系人的感兴趣的资源, 其中的'subscription'属性值设为"from". (这里假定该联系人未曾订阅该用户; 如果是那种情况(译者注:即该联系人已经订阅该用户), 则'subscription'属性值将设置为"both", 如附录A所述.)
CS: <iq id='a78b4q6ha463' to='juliet@example.com/balcony' type='set'> <query xmlns='jabber:iq:roster'> <item jid='romeo@example.net' subscription='from'/> </query> </iq> CS: <iq id='x81g3bdy4n19' to='juliet@example.com/chamber' type='set'> <query xmlns='jabber:iq:roster'> <item jid='romeo@example.net' subscription='from'/> </query> </iq>
从联系人的视角来看, 现在存在一个来自用户的订阅, 这就是为什么'subscription'属性值要设为"from". (这里假定该联系人未曾订阅该用户; 如果是那种情况(译者注:即该联系人已经订阅该用户), 则'subscription'属性值将设置为"both", 如附录A所述.)
该联系人的服务器接着也必须从该联系人的可用资源发送当前的出席信息给给该用户.
CS: <presence from='juliet@example.com/balcony' id='pw72bc5j' to='romeo@example.net'/> CS: <presence from='juliet@example.com/chamber' id='ux31da4q' to='romeo@example.net'/>
为了订阅该用户的出席信息, 该联系人需要发送一个订阅请求给该用户. (XMPP客户端经常会自动发送订阅请求而不是要求联系人发起这个订阅请求, 因为假定期望的结束状态是一个双向订阅.) 自然, 当该联系人发送一个订阅请求给该用户, 这个订阅状态将不同于前面示例中展示的状态(见 附录A) 并且角色将被反转.
服务器处理入站订阅批准
当用户的服务器收到一个订阅批准, 它必须首先检查是否该联系人在该用户的名册中并且 subscription='none' 或 subscription='from' 以及 'ask' 标记设为 "subscribe" (即, 订阅状态为 "None + Pending Out", "None + Pending Out+In", 或 "From + Pending Out"; 见 附录A). 如果这个检查成功了, 那么该用户的服务器必须:
- 1. 递送入站订阅批准到该用户的所有感兴趣的资源(这有助于给该用户的客户端关于订阅批准的正确的上下文,使得它们能分辨由该用户的另一个资源生成的名册推送和从联系人收到的订阅批准的不同). 这一步必须在下一步的发送名册推送之前去做.
US: <presence from='juliet@example.com' id='h4v1c4kj' to='romeo@example.net' type='subscribed'/>
- 2. 发起一个roster push给该用户的所有感兴趣的资源, 包含一个该;联系人的更新的名册条目,其'subscription'属性值设为"to" (如果订阅状态是 "None + Pending Out" 或 "None + Pending Out+In") 或 "both" (如果订阅状态是 "From + Pending Out").
US: <iq id='b89c5r7ib576' to='romeo@example.net/foo' type='set'> <query xmlns='jabber:iq:roster'> <item jid='juliet@example.com' subscription='to'/> </query> </iq> US: <iq id='b89c5r7ib577' to='romeo@example.net/bar' type='set'> <query xmlns='jabber:iq:roster'> <item jid='juliet@example.com' subscription='to'/> </query> </iq>
- 3. 该用户的服务器也必须把从该联系人的每个可用资源收到的可用的出席信息节递送给该用户的每个可用资源.
[ ... to resource1 ... ] US: <presence from='juliet@example.com/balcony' id='pw72bc5j' to='romeo@example.net'/> [ ... to resource2 ... ] US: <presence from='juliet@example.com/balcony' id='pw72bc5j' to='romeo@example.net'/> [ ... to resource1 ... ] US: <presence from='juliet@example.com/chamber' id='ux31da4q' to='romeo@example.net'/> [ ... to resource2 ... ] US: <presence from='juliet@example.com/chamber' id='ux31da4q' to='romeo@example.net'/>
- 实现备注: 如果在收到入站订阅批准通知的时候该用户的帐号没有可用资源, 该用户的服务器可以保持该通知的记录(理想的是完整的出席信息节)并且等到该帐号有一个可用资源的时候再递送给它. 这一行为向用户提供了当用户离线的时候发生名册变更的原因的更完整的信息.
另外 -- 就是, 如果该用户不存在, 如果该联系人不在该用户的名册中, 或如果该联系人在该用户的名册中但是订阅状态不在前述的检查中描述的情况之中 -- 那么该用户的服务器必须安静地忽略该订阅批准通知而不递送给该用户, 也不修改该用户的名册, 也不生成名册推送到该用户的感兴趣的资源.
从该用户的角度来看, 现在存在一个到该联系人的出席信息的订阅(这就是为什么'subscription'属性值要设为"to").
撤销被订阅
客户端生成撤销被订阅
如果一个联系人想撤销它之前接受某个用户订阅的授权, 以撤销一个预批准的被订阅(章节3.4), 或拒绝一个订阅请求, 它发送一个类型为"unsubscribed"的出席信息节.
CC: <presence id='ij5b1v7g' to='romeo@example.net' type='unsubscribed'/>
服务器处理出站被订阅撤销
在接收到出站被订阅撤销之后, 该联系人的服务器必须做如下处理.
- 如果该用户的纯JID还不在该联系人的名册或在联系人名册中的状态是"None", "None + Pending Out", 或 "To", 该联系人的服务器不能(SHOULD NOT)路由或递送类型为"unsubscribed"的出席信息节给该用户,并且不能(MUST NOT)发送接下来所说的类型为"unavailable"的出席信息通知给该用户.
- 如果该用户的纯JID在该联系人的名册的状态为"None", "None + Pending Out", 或 "To" 并且 'approved' 标记被设为 "true" (所以代表了章节3.4所述的订阅预批准), 该联系人的服务器必须移除该 预批准 并且不能(MUST NOT)路由或递送类型为"unsubscribed"的出席信息节给该用户.
- 否则, 按如下的示例, 该联系人的服务器必须同时路由或递送类型为"unavailable"的出席信息通知和类型为"unsubscribed"的出席信息节给该用户,并且必须发送一个名册推送给该联系人.
因为该用户仍然订阅了该联系人的出席信息(即, 在该联系人的服务器路由或递送类型为"unsubscribed"的出席信息给该用户之前), 该联系人的服务器必须从该联系人的所有在线资源发送一个类型为"unavailable"的出席信息节给该用户.
CS: <presence from='juliet@example.com/balcony' id='i8bsg3h3' type='unavailable'/> CS: <presence from='juliet@example.com/chamber' id='bvx2c9mk' type='unavailable'/>
然后该联系人的服务器必须路由或递送类型为"unsubscribed"的出席信息节给该用户, 确保在出站被订阅取消要带上该联系人的纯JID<contact@domainpart>.
CS: <presence from='juliet@example.com' id='ij5b1v7g' to='romeo@example.net' type='unsubscribed'/>
该联系人从的服务器必须发送一个带有更新的名册条目的名册推送给该联系人的所有感兴趣的资源, 这里订阅状态现在要么是"none"要么是"to" (见附录A).
CS: <iq id='pw3f2v175b34' to='juliet@example.com/balcony' type='set'> <query xmlns='jabber:iq:roster'> <item jid='romeo@example.net' subscription='none'/> </query> </iq> CS: <iq id='zu2y3f571v35' to='juliet@example.com/chamber' type='set'> <query xmlns='jabber:iq:roster'> <item jid='romeo@example.net' subscription='none'/> </query> </iq>
服务器处理入站被订阅撤销
当用户的服务器接收到入站被订阅撤销后, 它必须首先检查该联系人是否在该用户的名册中并且 subscription='to' 或 subscription='both' (见附录A). 如果这个检查成功了, 那么该服务器必须:
- 1. 递送该入站被订阅撤销到该用户的所有感兴趣的资源(这有助于给该用户的客户端正确的关于订阅被撤销的上下文,使得它们能区分由用户的另一个资源产生的名册推送和从该联系人收到的订阅被撤销). 这个动作必须在发送下一步所述的名册推送之前进行.
US: <presence from='juliet@example.com' id='ij5b1v7g' to='romeo@example.net' type='unsubscribed'/>
- 2. 发起一个名册推送给该用户的所有感兴趣的资源, 包含一个该联系人的更新的名册条目,其'subscription'属性值设为"none"(如果之前的订阅状态为"To"或"To + Pending In") 或"from"(如果订阅状态为"Both").
US: <iq id='h37h3u1bv400' to='romeo@example.net/foo' type='set'> <query xmlns='jabber:iq:roster'> <item jid='juliet@example.com' subscription='none'/> </query> </iq> US: <iq id='h37h3u1bv401' to='romeo@example.net/bar' type='set'> <query xmlns='jabber:iq:roster'> <item jid='juliet@example.com' subscription='none'/> </query> </iq>
该用户的服务器也必须递送类型为"unavailable"的入站出席信息节.
- 实现备注: 如果收到该入站被撤销订阅通知的时候,该用户的帐号没有可用的资源, 该用户的服务器可以保持一个通知记录(理想的情况是完整的出席信息节),然后在该帐号下次有可用资源的时候递送该通知. 这一行为把该用户离线期间发生的名册变更的原因提供了更完整的信号给该用户.
另外 -- 就是, 如果该用户不存在, 如果该联系人不在该用户的名册中, 或如果该联系人在该用户的名册中的订阅状态不是前述检查所描述的那些状态 -- 那么该用户的服务器必须安静地忽略该被取消订阅通知而不把它递送给该用户, 也不修改该用户的名册, 以及不生成名册推送给该用户的感兴趣的资源.
取消订阅
客户端生成取消订阅
如果一个用户想取消订阅一个联系人的出席信息, 它发送一个类型为"unsubscribe"的出席信息节.
UC: <presence id='ul4bs71n' to='juliet@example.com' type='unsubscribe'/>
服务器处理出站取消订阅
在接收到出站取消订阅之后, 该用户的服务器必须做如下处理.
- 如果联系人和该用户在同一台服务器上, 那么当该服务器处理订阅请求时必须遵循3.3.3定义的规则.
- 如果联系人位于远端服务器上, 按照本地服务策略,该用户的服务器必须接着把该节路由到那个远端域以满足核心XMPP节处理规则. (这可能导致返回适当的节错误给该用户, 类似<remote-server-timeout/>.)
如前所述, 在本地递送或远程路由取消订阅之前, 该用户的服务器必须在该节上标记该用户的纯JID<user@domainpart>.
US: <presence from='romeo@example.net' id='ul4bs71n' to='juliet@example.com' type='unsubscribe'/>
该用户的服务器接着必须发送一个带有更新的名册条目的名册推送给该用户的所有感兴趣的资源, 这里的订阅状态就变成要么是"none"要么是"from" (见[[RFC6121#附录 A:订阅状态|附录A).
US: <iq id='h37h3u1bv402' to='romeo@example.net/foo' type='set'> <query xmlns='jabber:iq:roster'> <item jid='juliet@example.com' subscription='none'/> </query> </iq> US: <iq to='romeo@example.net/bar' type='set' id='h37h3u1bv403'> <query xmlns='jabber:iq:roster'> <item jid='juliet@example.com' subscription='none'/> </query> </iq>
服务器处理入站取消订阅
当联系人的服务器收到取消订阅通知, 它必须首先检查该用户的纯JID是否在该联系人的名册中且subscription='from'或subscription='both' (即, 订阅状态为 "From", "From + Pending Out", 或 "Both"; 见附录A). 如果这个检查成功了, 接着该联系人的服务器必须:
- 1. 把入站取消订阅递送给该联系人的所有感兴趣的资源(这有助于向联系人的客户端们提供关于取消订阅的正确的上下文,这样它们就能区分是由联系人的另一个资源产生的名册推送还是从该用户收到的取消订阅). 这个动作必须发生在下面所说的发送名册推送步骤之前.
CS: <presence from='romeo@example.net' id='ul4bs71n' to='juliet@example.com' type='unsubscribe'/>
- 2. 发起一个名册推送给该联系人的所有感兴趣的资源, 为该用户包含一个更新的条目并把'subscription'属性值设为"none" (如果之前的订阅状态是"From"或"From + Pending Out") 或"to" (如果之前的订阅状态是 "Both").
CS: <iq id='tn2b5893g1s4' to='juliet@example.com/balcony' type='set'> <query xmlns='jabber:iq:roster'> <item jid='romeo@example.net' subscription='none'/> </query> </iq> CS: <iq id='sp3b56n27hrp' to='juliet@example.com/chamber' type='set'> <query xmlns='jabber:iq:roster'> <item jid='romeo@example.net' subscription='none'/> </query> </iq>
- 3. 从该联系人的每一个可用资源生成一个类型为"unavailable"的出站出席信息节给该用户.
CS: <presence from='juliet@example.com/balcony' id='o5v91w49' to='romeo@example.net' type='unavailable'/> CS: <presence from='juliet@example.com/chamber' id='n6b1c37k' to='romeo@example.net' type='unavailable'/>
- 实现备注: 如果在收到入站取消订阅通知的时候该联系人的帐号没有可用资源, 该联系人的服务器可以保持一个通知记录(理想情况下是完整的出席信息节)并且接着在该帐号下次有一个可用资源的时候递送该通知. 这一行为为该用户提供了关于该用户离线时发生的名册变更的更完整的信息.
另外 -- 就是, 如果该联系人不存在, 如果该用户不在该联系人的名册中, 或如果该用户的纯JID在该联系人的名册中并且订阅状态不同于前面检查所述的结果 -- 那么该联系人的服务器必须安静地忽略该取消订阅节,不递送给该联系人, 不修改联系人的名册, 也不生成名册推送给该联系人的所有感兴趣的资源. 无论如何, 如果该联系人的服务器持续跟踪一个从该用户到该联系人的入站出席信息订阅请求但是该用户仍不在该联系人的名册中(功能上相当于一个订阅状态"None + Pending In",这里该联系人从未添加该用户到联系人的名册中), 那么该联系人的服务器必须简单地移除该入站出新信息订阅请求的记录(它无法从该联系人的名册中移除该用户,因为该用户从未被加入到该联系人的名册中).
- 实现备注: 该用户的客户端不能(MUST NOT)依赖于从该联系人收到的取消订阅出席信息通知, 因为它必须考虑对该联系人的出席信息订阅, 以及关于该联系人的出席信息, 当它发送类型为"unsubscribe"的出席信息节或它收到由取消订阅请求触发的名册推送的时候为无或空.
预批准被订阅请求
如果一个用户还未从一个联系人那里接收到订阅请求, 该用户可以"预批准"这样一个请求,这样它将被该用户的服务器自动地批准.
对订阅预批准的支持在客户端和服务器上都是可选的. 如果一个服务器支持订阅预批准, 那么它必须在流协商时声明以下流特性.
<sub xmlns='urn:xmpp:features:pre-approval'/>
订阅预批准流特性只不过有一点用,所以永远不会强制协商.
客户端生成被订阅预批准
如果客户端所连接的服务器声明了订阅预批准, 该客户端可以通过发送一个类型为"subscribed"的节给该联系人以生成一个订阅预批准.
UC: <presence id='pg81vx64' to='juliet@example.com' type='subscribed'/>
如果该服务器未声明支持订阅预批准, 该客户端不能(MUST NOT)尝试预批准来自潜在的或者实际的联系人的订阅请求.
服务器处理被订阅预批准
在接收到类型为"subscribed"的出席信息节之后, 该用户的服务器如果支持订阅预批准,必须做如下处理.
- 如果该联系人在该用户的名册中且状态为 "Both", "From", 或 "From + Pending Out", 该用户的服务器必须安静地忽略该节.
- 如果该联系人在该用户的名册中且状态为 "To + Pending In", "None + Pending In", 或 "None + Pending Out+In", 该用户的服务器必须如普通的订阅批准那样处理该节 (见3.1.5),更新现有的名册条目的状态成为 "Both", "From", 或 "From + Pending Out" (分别的), 推送修改的名册条目到该用户的所有感兴趣的资源, 并路由该类型为"subscribed"的出席信息节到该联系人.
- 如果该联系人在该用户的名册中且状态为 "To", "None", 或 "None + Pending Out", 该用户的服务器必须把'approved'标记的值设置为"true"来记录订阅预批准, 然后推送修改的名册条目到该用户的所有感兴趣的资源. 无论如何, 该用户的服务器不能(MUST NOT)路由该类型为"subscribed"的出席信息节到该联系人.
- 如果该联系人还不在该用户的名册中, 那么该用户的服务器必须为该联系人创建一个名册条目且状态为"None",并且把'approved'标记的值设为"true", 然后推送该名册条目到该用户的所有感兴趣的资源. 无论如何, 该用户的服务器不能(MUST NOT)路由该类型为"subscribed"的出席信息节到该联系人.
一个名册推送的示例如下.
US: <iq id='h3bs81vs763f' to='romeo@example.net/bar' type='set'> <query xmlns='jabber:iq:roster'> <item approved='true' jid='juliet@example.com' subscription='none'/> </query> </iq>
当'approved'标记被设为"true", 该用户的服务器不能(MUST NOT)从该联系人递送一个类型为"subscribe"的出席信息节给该用户, 而是必须代表该用户自动应答这样一个节,即从该用户的纯JID返回一个类型为"subscribed"的出席信息节给该联系人的纯JID.
- 实现备注: 服务器在已经从该联系人接收到了一个出席信息订阅请求之后是否维护一个订阅批准记录,对于实现或本地服务策略来说是个问题. 如果该服务器不维护这样一个记录, 在接收该订阅请求之后它将在该名册条目中不为该联系人包含'approved'属性(即, 在随后的名册推送和名册结果中). 如果该服务器维护这样一个记录, 它将在名册条目中总是为该联系人包含'approved'属性(设为"true"), 直到或除非该用户发送一个类型为"unsubscribed"的出席信息节到该联系人(或从名册中完全移除该联系人).
- 实现备注: 客户端能通过发送一个类型为"unsubscribed"的出席信息节取消一个预批准, 更全面的描述在3.2. 在这种情况下, 该用户的服务器将发送一个名册推送给该有用户的所有感兴趣的资源,而其'approved'属性是被移除的. (另一种选择是, 该客户端能简单地完全移除该名册条目.)
交换出席信息
出席信息原理
出席信息是指在网络上通讯的一个实体的可用性. 在最基本的层面上, 出席信息是一个"on/off"的逻辑变量以标记一个实体可不可以通讯(也用术语"在线"和"离线"). 在XMPP中, 当客户端生成一个不带'type'属性的<presence/>节时就标记了一个实体的可用性, 当它的客户端生成一个'type'属性值为"unavailable"的<presence/>节时表示一个实体失去了可用性.
XMPP出席信息典型地遵循一个"发布-订阅"或"观察者"模式, 这里,一个实体发送出席信息给它的服务器, 它的服务器接着广播那个信息给所有订阅了该实体的出席信息的联系人(在IMP‑MODEL的术语中, 一个生成出席信息的实体就是一个"presentity"并且那些接收出席信息的实体们是"subscribers"). 一个客户端通过发送一个不带'to'地址的出席信息节给它的服务器来生成出席信息用于广播给所有已订阅的实体们, 这里的出席信息既没有'type'属性又没有值为"unavailable"的'type'属性. 这种出席信息被称为"广播出席信息". (一个客户端也可以发送"定向出席信息", 即, 一个带'to'地址的出席信息节; 这不太常见但是有时被用于发送出席信息给那些没有订阅该用户的出席信息的实体们; 见4.6.)
在一个客户端完成XMPP‑CORE定义的先决条件之后, 它能通过发送初始化出席信息在它的服务器建立一个"出席信息会话", 通过发送unavailable出席信息可以中止这个出席信息会话. 在它的出席信息会话期间, 一个已连接的资源(XMPP‑CORE中的术语)被称为一个"可用的资源".
在XMPP中, 应用集合了消息和出席信息功能, 它的出席信息标记的可用性的缺省通讯类型是消息; 然而, 对于XMPP应用来说把消息和出席信息功能集合到一起并不是必要的, 它们能提供独立的不带消息的出席信息特性(另外, XMPP服务器们不会为了成功地路由message和IQ节而强制要求关于网络可用性的信息).
- 提示备注: 在接下来的例子中, 用户是<juliet@example.com>, 她有两个可用的资源("balcony"和"chamber"), 而在她的名册中有三个联系人且订阅状态为"from"或"both": <romeo@example.net>, <mercutio@example.com>, 和<benvolio@example.net>.
初始化出席信息
客户端生成初始化出席信息
在完成XMPP‑CORE(必需的)中所述的前提条件并请求名册(推荐的)之后, 一个客户端通过发送"初始化出席信息"给它的服务器来标记它的通讯可用性, 即, 一个不带'to'地址(意思是由该服务器代表该客户端广播它)和'type'属性(表示该用户的可用性)的出席信息节.
UC: <presence/>
该初始化出席信息可以包含<priority/>元素, <show/>元素, 一个一个或多个<status/>元素实例, 以及扩展内容; 细节见4.7.
服务器处理出站初始化出席信息
从一个客户端接收到初始化出席信息之后, 该用户的服务器必须从该用户的全JID<user@domainpart/resourcepart>发送该出席信息节给所有订阅了该用户的出席信息的联系人; 这些联系人的JID目前正在该用户的名册中且'subscription'属性值为"from"或"both".
US: <presence from='juliet@example.com/balcony' to='romeo@example.net'/> US: <presence from='juliet@example.com/balcony' to='mercutio@example.com'/> US: <presence from='juliet@example.com/balcony' to='benvolio@example.net'/>
该用户的服务器也必须从该用户的新的可用资源广播初始化出席信息给该用户的所有可用资源, 包括在第一个地方生成该出席信息通知的那个资源(即, 一个实体隐形地订阅了它自己的出席信息).
[... to the "balcony" resource ...] US: <presence from='juliet@example.com/balcony' to='juliet@example.com'/> [... to the "chamber" resource ...] US: <presence from='juliet@example.com/balcony' to='juliet@example.com'/>
在缺少关于该用户的联系人的出席信息的情况下, 该用户的服务器也必须代表该用户发送出席信息探测给该用户的联系人,如4.3所述.
服务器处理入站初始化出席信息
在从该用户接收到出席信息之后, 该联系人的服务器必须递送该用户的出席信息节给该联系人的所有可用资源.
[ ... to resource1 ... ] CS: <presence from='juliet@example.com/balcony' to='romeo@example.net'/> [ ... to resource2 ... ] CS: <presence from='juliet@example.com/balcony' to='romeo@example.net'/>
客户端处理初始化出席信息
当该联系人的客户端从该用户接收到出席信息之后, 对于可交互的客户端推荐以下行为:
出席信息探测
一个"出席信息探测"是一个对某联系人的当前出席信息的请求, 由代表某个用户的服务器代表该用户发送; 语法上它是一个'type'属性值为"probe"的出席信息节. 在出席信息订阅的上下文中, 'from'地址的值必须是订阅的用户的纯JID而'to'地址的值必须是被订阅的联系人的纯JID, 因为出席信息订阅是基于纯JID的.
US: <presence from='juliet@example.com' id='ign291v5' to='romeo@example.net' type='probe'/>
- 互操作性备注: RFC 3921 定义了探测是从全JID发出的, 而不是从纯JID (规则的改变是因为订阅是基于纯JID的). 一些现存的实现仍然从全JID而不是纯JID发送.
探测也能由一个从某个出席信息订阅上下文的外部接收到了出席信息的实体发送, 典型的是当该联系人已经发送了4.6所述的定向出席信息的时候; 在这种情况下'from'或'to'地址的值可以是一个全JID而不是一个纯JID. 完整的讨论参见4.6.
出席信息探测不应该(SHOULD NOT)由一个客户端发送, 因为通常一个客户端不需要发送它们, 原因是从一个用户的联系人那里收集出席信息的任务是由该用户的服务器管理的. 无论如何, 如果一个用户的客户端生成了一个出站出席信息探测, 那么该用户的服务器应该路由这个探测(如果该联系人位于另一个服务器上)或处理这个探测(如果该联系人在同一台服务器上)并且不能(MUST NOT)从一个已连接的客户端使用该出席信息探测的回执来作为唯一的理由来返回一个节错误或流错误给该客户端.
服务器生成出站出席信息探测
当一个服务器需要查询一个用户的联系人的可用性的时候, 它从该用户的纯JID <user@domainpart>发送一个出席信息探测到该联系人的纯JID <contact@domainpart>.
- 实现备注: 尽管出席信息探测是用来发送给联系人的(即, 一个用户所订阅的那些实体), 一个服务器可以发送出席信息探测给该用户在当前会话时收到了出席信息的某个实体的全JID.
当用户在任何时候通过发送初始化出席信息来开始一个新的出席信息会话,该用户的服务器应该发送一个出席信息探测; 无论如何, 如果它认为关于该用户的联系人的出席信息是可靠的和最新的,该服务器可以选择在那个时间点不发送出席信息探测(例如, 因为该用户有另外一个可用资源或因为该用户在新的出席信息开始之前刚刚注销登陆). 另外, 如果一个服务器在一个可配置的时间段内没有从某个联系人收到出席信息或其他通讯,它可以定期发送出席信息探测给那个联系人; 这有助于防止看起来在线但是实际上没有在线的"幽灵"联系人.
US: <presence from='juliet@example.com' id='ign291v5' to='romeo@example.net' type='probe'/> US: <presence from='juliet@example.com' id='xv291f38' to='mercutio@example.com' type='probe'/>
自然的, 该用户的服务器不需要发送一个出席信息探测给一个联系人,如果该联系人的帐号位于该用户的同一台服务器, 因为该服务器本地就拥有该联系人的信息.
服务器处理入站出席信息探测
在接收到一个用户的服务器代表该用户发送到该联系人的纯JID的出席信息探测之后, 该联系人的服务器必须应答如下:
- 1. 如果该联系人帐号不存在或该用户的纯JID在该联系人的名册中并且订阅状态不是"From", "From + Pending Out", 或"Both" (如附录A所述), 那么该联系人的服务器应该在对该出席信息探测的应答中返回一个类型为"unsubscribed"的出席信息节(这将触发一个协议流用于3.2所述的撤销该用户对该联系人的订阅; 无论如何, 这不能(MUST NOT)导致3.4所述的订阅预批准的撤销). 这里'from'地址必须是该联系人的纯JID, 因为指定一个全JID则等同于出席信息泄露XMPP‑CORE中所说的.
CS: <presence from='mercutio@example.com' id='xv291f38' to='juliet@example.com' type='unsubscribed'/>
- 无论如何, 如果一个服务器从一个该服务器本身的已配置的域或其他信任的服务收到一个出席信息探测, 它可以提供关于该用户或那个实体的出席信息.
- 2. 或者, 如果该联系人已经临时移动或永久移动到另一个地址, 那么该服务器应该返回一个类型为"error"的出席信息节并带上一个节错误条件<redirect/> (临时的) 或 <gone/> (永久的) 并包含该联系人的新地址.
CS: <presence from='mercutio@example.com' id='xv291f38' to='juliet@example.com' type='error'> <error type='modify'> <gone xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'> xmpp:la-mer@example.com </gone> </error> </presence>
- 3. 或者, 如果该联系人没有可用的资源, 那么该服务器应该通过发送给该用户一个类型为"unavailable"的出席信息节来应答该出席信息探测(尽管在这里更倾向于发送unavailable出席信息,因为它能导致对该探测的确定的回答; 但它不是强制性的,因为它极大地增加了该联系人的服务器生成的出席信息通知的数量). 这里的'from'地址是纯JID,因为没有该联系人相关的可用资源. 如果适当地遵循本地安全策略,该出席信息通知可以包含该服务器从该联系人收到的最近的unavailable出席信息节的全XML(包括原始节的'id'), 但是如果没有,那么该出席信息通知应该简单地表示该联系人是unavailable而没有任何原始提供的细节. 在任何情况下, 返回给该探测实体的出席信息节应该包含最近一次unavailable出席信息节被生成的时间信息(格式采用XMPP延迟递送扩展DELAY).
CS: <presence from='mercutio@example.com' id='xv291f38' to='juliet@example.com' type='unavailable'> <delay xmlns='urn:xmpp:delay' stamp='2002-09-10T23:41:07Z'/> </presence>
- 4. 或者, 如果该联系人有至少一个可用资源, 那么该服务器必须通过向该用户发送该服务器从该联系人的每个可用资源收到的最近的没有'to'属性的出席信息节的全XML. 这里的'from'地址是每个可用资源的全JIDs.
CS: <presence from='romeo@example.net/foo' id='hzf1v27k' to='juliet@example.com'/> CS: <presence from='romeo@example.net/bar' id='ps6t1fu3' to='juliet@example.com'> <show>away</show> </presence>
- 实现备注: 通过"full XML"意味着从开始的<presence>标签到关闭的</presence>标签之前的完整的节, 包括所有的元素和属性,要么由内容命名空间限定要么由扩展命名空间限定; 无论如何, 根据XMPP‑CORE,如果通过服务器-服务器流发送完整的节,该联系人的服务器将需要把内容命名空间从'jabber:client'转换成'jabber:server'.
如果该联系人的服务器接收到一个地址指向该联系人的某个全JID的出席信息探测, 该服务器不能(MUST NOT)返回任何除了该探测的'to'地址之外的资源的出席信息. 规则 #1 和 #2 中对一个纯JID的探测同样适用于一个全JID探测的情况. 如果有一个匹配的全JID并且探测的实体通过一个出席信息订阅从而有权限察看该联系人的出席信息, 那么该服务器必须返回一个available出席信息通知, 它应该确切地只和那个available的资源通讯(没有详细信息,如<show/>, <status/>, <priority/>, 或出席信息扩展).
CS: <presence from='romeo@example.net/bar' to='lobby@chat.example.com'/>
- 实现备注: 关于前述处理定向出席信息的补充请参阅4.6.
处理'id'属性
和出席信息探测相关的'id'属性的处理在RFC 3921中没有定义. 尽管"发送一个探测并接收到一个应答"的模式可能看起来像一个类似XMPP <iq/>节的 请求-应答 协议, 事实上它不是,因为对一个探测的应答可以包含多个出席信息节(分别对应该联系人当前活跃的每一个可用的资源). 基于这个原因, 如果该联系人当前有可用资源,那么该联系人的服务器应该保留当发送那些出席信息通知给探测实体的时候该联系人的原始出席信息节的'id'属性(如果有的话). 相比之下, 如果该联系人当前没有可用资源, 探测实体未被授权(通过出席信息订阅)察看该联系人的出席信息, 或发生了和该探测相关的错误, 那么该联系人的服务器在应答探测实体的时候应该镜像该用户的出席信息探测的'id'.
以下示例展现这些不同之处.
在第一个场景中, Juliet发送从她的"chamber"资源出席信息探测.
CC: <presence from='juliet@example.com/chamber' id='pres1'> <show>dnd</show> <status>busy!</status> </presence>
她也从她的"balcony"资源发送出席信息.
CC: <presence from='juliet@example.com/balcony' id='pres2'> <show>away</show> <status>stepped away</status> </presence>
Romeo的服务器接着发送一个探测给Juliet.
US: <presence from='romeo@example.net' id='probe1' type='probe'/>
Juliet的服务器接着同时发送她的两个出席信息通知给Romeo, 保留她的客户端发送的节中的'id'属性.
CS: <presence from='juliet@example.com/chamber' id='pres1'> <show>dnd</show> <status>busy!</status> </presence> CS: <presence from='juliet@example.com/balcony' id='pres2'> <show>away</show> <status>stepped away</status> </presence>
在第二个场景中, 当Romeo的服务器发送探测的时候Juliet离线了.
US: <presence from='romeo@example.net' id='probe2' type='probe'/>
Juliet的服务器应答一个unavailable通知, 镜像Rome的出席信息探测的'id',因为从她的客户端已经发送的available通知的'id'被保留.
CS: <presence from='juliet@example.com' id='probe2' type='unavailable'/>
随后的出席信息广播
客户端生成随后的出席信息广播
在发送初始化出席信息之后, 在它的会话期间的任何时候该用户的客户端可以更新它的可用性并通过发送一个不带'to'地址并且没有'type'属性的出席信息节来广播.
UC: <presence> <show>away</show> </presence>
该出席信息广播可以包含<priority/>元素, <show/>元素, 和一个或多个<status/>元素实例,同样的包括扩展内容; 详情参见4.7.
无论如何, 一个用户应该发送一个出席信息更新只广播该用户的通讯可用性或该资源的通讯能力相关的信息. 和这些无关但是可能该用户的联系人同样感兴趣的信息应该通过其他方式来发送, 例如XEP‑0163所述的"发布-订阅"方法.
服务器处理随后的出站出席信息
在接收到一个表达更新的可用性的出席信息节后, 该用户的服务器必须广播那个出席信息节的完整XML给该用户名册中并且订阅类型为"from"或"both"的联系人们.
- 互操作性备注: RFC 3921定义在发送随后的出席信息通知之前该用户的服务器将检查并确认没有从该联系人收到一个出席信息错误. 那个规则已经被移除,因为本协议使用类型为"unsubscribe"(而不是"error")的出席信息节来解决订阅同步问题, 部分也因为这类节使得该联系人在该用户的名册中的订阅状态变成了"none"或"to"(见3.3和附录A), 所以没有了对错误检查的需要.
- 互操作性备注: 如果订阅类型为"both", 一些现有的服务器实现根据该用户的服务器仅当该联系人在线时才发送随后的出席信息通知给一个联系人(就是说, 如果该用户的服务器在它发给该联系人的出席信息探测的应答中从未收到过该联系人在线的肯定的标示, 该用户的服务器不会从该用户向该联系人发送随后的出席信息通知). 这一行为被理解为节省带宽, 因为大部分出席信息订阅是双向的并且很多联系人在任何特定的时间都不在线.
US: <presence from='juliet@example.com/balcony' to='romeo@example.net'> <show>away</show> </presence> US: <presence from='juliet@example.com/balcony' to='benvolio@example.net'> <show>away</show> </presence> US: <presence from='juliet@example.com/balcony' to='mercutio@example.com'> <show>away</show> </presence>
- 实现备注: 关于上述处理用于定向出席信息的补充规则参见4.6.
该用户的服务器也必须发送该出席信息节给该用户的所有可用资源(包括首先生成该出席信息的那个资源).
US: <presence from='juliet@example.com/balcony' to='juliet@example.com/chamber'> <show>away</show> </presence> US: <presence from='juliet@example.com/balcony' to='juliet@example.com/balcony'> <show>away</show> </presence>
服务器处理随后的入站出席信息
在从用户那接收到出席信息之后, 该联系人的服务器必须递送该用户的出席信息节到该联系人的所有可用资源.
[ ... to resource1 ... ] CS: <presence from='juliet@example.com/balcony' to='romeo@example.net'> <show>away</show> </presence> [ ... to resource2 ... ] CS: <presence from='juliet@example.com/balcony' to='romeo@example.net'> <show>away</show> </presence>
客户端处理随后的出席信息
从联系人的客户端的视角来看, 初始化出席信息广播和随后的出席信息没有明显的不同, 所以联系人的客户端遵循4.4.3定义的入站出席信息处理的规则.
离线出席信息
客户端生成离线出席信息
在结束和一个服务器的出席信息会话之前, 用户的客户端应该通过发送"unavailable 出席信息"优雅地变更为离线, 即, 一个没有'to'属性并且有一个值为"unavailable"的'type'属性的出席信息节.
UC: <presence type='unavailable'/>
可选的, 该离线出席信息节可以包含一个或多个<status/>元素来定义为什么该用户不再在线的原因.
UC: <presence type='unavailable'> <status>going on vacation</status> </presence>
无论如何, 该离线出席信息节不能(MUST NOT)包含<priority/>元素或<show/>元素, 因为这些元素只适用于可用资源.
服务器处理出站离线出席信息
该用户的服务器不能(MUST NOT)依赖于从一个可用的资源接收到的unavailable出席信息, 因为该资源可能不优雅地离线了(例如, 如XMPP‑CORE所述,该资源的XML流因为任何原因带或未带流错误地被关闭了).
如果一个可用的资源因为任何原因离线了(优雅或不优雅的), 该用户的服务器必须广播unavailable出席信息给所有在该用户的名册中的订阅状态为"from"或"both"的联系人.
- 互操作性备注: RFC 3921 定义了该用户的服务器将检查并确认在发送unavailable出席信息通知之前它没有从该联系人收到一个出席信息错误. 那个规则已经被移除,因为本协议使用类型为"unsubscribe"(而不是"error")的出席信息节来解决订阅同步问题, 部分也因为这类节使得该联系人在该用户的名册中的订阅状态变成了"none"或"to"(见3.3和附录A), 所以没有了对错误检查的需要.
- 互操作性备注: 即使该用户的服务器不广播该用户随后的出席信息通知给离线的联系人们(如4.4.2所述), 它必须广播该用户的unavailable出席信息通知; 如果它不这么做, 由该联系人的服务器最后收到的出席信息将是该用户用于这个出席信息会话的初始化出席信息, 结果是该联系人将认为该用户在线.
- 实现备注: 关于上述处理用于定向出席信息的补充规则参见4.6.
如果该unavailable通知优雅地被从该客户端接收到, 那么该服务器必须广播该出席信息节的完整XML.
US: <presence from='juliet@example.com/balcony' to='romeo@example.net' type='unavailable'> <status>going on vacation</status> </presence> US: <presence from='juliet@example.com/balcony' to='benvolio@example.net' type='unavailable'> <status>going on vacation</status> </presence> US: <presence from='juliet@example.com/balcony' to='mercutio@example.com' type='unavailable'> <status>going on vacation</status> </presence>
该用户的服务器也必须发送该unavailable通知给该用户的所有可用资源(以及最初生成该unavailable出席信息的那个资源).
US: <presence from='juliet@example.com/balcony' to='juliet@example.com/chamber' type='unavailable'> <status>going on vacation</status> </presence>
如果该服务器探测到那该用户已经不优雅地离线了, 那么该服务器必须代表该用户生成该unavailable出席信息广播.
- 实现备注: 在该服务器广播或生成一个unavailable出席信息通知之后,该客户端发送的任何没有'type'属性且没有'to'属性的出席信息节,必须被该用户的服务器路由或递送到所有的订阅者(即, 必须等同于为一个新的出席信息会话出发了初始化出席信息).
服务器处理入站离线出席信息
在从用户接收到一个unavailable通知之后, 该联系人的服务器必须递送该用户的出席信息节给该联系人的所有可用资源.
[ ... to resource1 ... ] CS: <presence from='juliet@example.com/balcony' to='romeo@example.net' type='unavailable'> <status>going on vacation</status> </presence> [ ... to resource2 ... ] CS: <presence from='juliet@example.com/balcony' to='romeo@example.net' type='unavailable'> <status>going on vacation</status> </presence>
- 实现备注: 如果该联系人的服务器没有广播随后的出席信息通知给离线的用户们(如4.4.2), 它也必须注意到该用户是离线的并更新它的关于哪些实体在线的内部存储.
客户端处理离线出席信息
从联系人的客户端的视角来看, 在线出席信息广播和离线出席信息广播没有明显的差别, 所以通常联系人的客户端遵循定义的处理入站出席信息的规则4.4.3.
无论如何, 如果该联系人从该用户的纯JID(而不是某个可用资源的全JID)接收到一个unavailable通知, 该联系人的客户端应该视为该unavailable通知适用于所有资源.
定向出席信息
本章是客户端和服务器处理出席信息通知和出席信息探测的规则的补充, 但是仅用于定向出席信息的特定场景.
总则
通常, 当一个客户端希望分享可用性信息给一个没有订阅它的出席信息的实体的时候,它会发送定向出席信息, 典型的是在一个临时基础上. 定向出席信息的常规使用包括5.1所述的临时的一对一会话和XEP-0045所述的多用户聊天室.
通过分享定向出席信息给另一个实体而建立的临时的关系比起通过出席信息订阅建立的永久关系来说是次要的. 所以, 建立, 修改, 或取消一个出席信息订阅的动作,必须优先于接下来的子章节定义的规则. 例如, 如果一个用户分享了定向出席信息给一个联系人,但是接着通过完成出席信息订阅"握手"来把该联系人加入到该用户的名册中, 那么该用户的服务器必须把该联系人视为如第三章所述的任何一个普通的订阅者来处理, 例如, 发送随后的出席信息广播给该联系人. 另一个例子是, 如果该用户接着取消了该联系人对该用户的出席信息的订阅, 该用户的服务器必须如3.2所述处理这个取消动作, 这包括发送unavailable出席信息给该联系人, 即使该用户曾经发送了定向出席信息给该联系人.
XMPP服务器典型的实现定向出席信息的办法是通过保持一个用户在该用户的给定资源(全JID)的当前会话期间曾经对它们发送了定向出席信息的实体的列表(纯JIDs或全JIDs), 然后在该用户下线的时候清除这个列表(例如, 发送一个类型为"unavailable"的广播出席信息节). 该服务器必须从定向出席信息列表(或它的功能等价物)移除任何该用户曾经对它们发送过定向unavailable出席信息的实体并且应该移除任何发送了unavailable出席信息给该用户的实体.
客户端生成定向出席信息
大家知道, 定向出席信息是一个客户端生成的presence节,这个节的'to'属性值是另一个实体的纯JID或全JID,并且这个节要么没有'type'属性(表示可用性)要么'type'属性值为"unavailable".
服务器处理出站定向出席信息
当用户的服务器接收到一个定向出席信息节, 它应该根据以下规则处理它.
- 如果该用户在已经发送初始化出席信息之后并且在发送unavailable出席信息广播之前(即, 在该用户的出席信息会话期间), 发送定向的可用或不可用出席信息给一个在该用户的名册中并且订阅状态为"from"或"both"的联系人, 该用户的服务器必须本地递送或远程路由那个出席信息节的完整XML给那个实体,但是应该不(SHOULD NOT)基于出席信息广播来修改该联系人的状态(即, 它应该在任何随后由该用户初始化的的出席信息广播中包含该联系人的JID).
- 如果该用户在已经发送初始化出席信息之后并且在发送unavailable出席信息广播之前(即, 在该用户的出席信息会话期间), 发送定向的可用或不可用出席信息给一个不在该用户的名册中并且订阅状态为"from"或"both"的联系人, 该用户的服务器必须本地递送或远程路由那个出席信息节的完整XML给那个实体,但是不能(MUST NOT)基于available出席信息广播来修改该联系人的状态(即, 它不能(MUST NOT)在任何随后由该用户初始化的available出席信息广播中包含该联系人的JID); 无论如何, 如果发送定向出席信息的用户的可用资源离线了, 该用户的服务器必须路由那个离线出席信息给该实体(如果该用户还未发送定向unavailable出席信息给那个实体).
- 如果该用户从未发送过初始化出席信息或在已经发送unavailable出席信息广播之后(即, 该资源已连接但不可用)发送定向出席信息, 该用户的服务器必须把该实体当作上面第二种情况下该用户发送定向出席信息的实体.
服务器处理入站定向出席信息
从联系人的服务器的视角来看, 在出席信息广播和定向出席信息之间没有显著差别, 所以联系人的服务器遵循4.3.2,4.4.3和4.5.3定义的处理入站出席信息的规则.
客户端处理入站定向出席信息
从联系人的客户端的视角来看,在出席信息广播和定向出席信息之间没有显著差别, 所以联系人的客户端遵循定义的处理入站出席信息的规则4.4.3(译者注:原文如此,疑为4.4.4之误).
服务器处理出席信息探测
如果一个用户的客户端发送了定向出席信息给另一个实体(例如, 一个一对一聊天伙伴或一个多用户聊天室), 在一段时间之后该实体或它的服务器可能想知道该用户是否仍然在线. 这个场景在多用户聊天室中特别常见, 该用户可能在很长一段时间内在该聊天室中是一个参加者. 如果该用户的客户端下线而没有(通过客户端或该客户端的服务器)通知该聊天室, 该用户的在该房间里的表现可能成为一个"幽灵", 看起来是参加者但实际上已经不在该房间了. 为了查明这类"幽灵", 一些聊天室实现会发送出席信息探测给这些已经加入房间的用户.
在定向出席信息的案例中, 探测的实体应该从接收到定向出席信息的那个JID(一个全JID或纯JID)发送探测. 该探测应该被发送到那个用户的全JID, 而不是那个用户的不带资源的纯JID, 因为伴随定向出席信息的临时"授权"是基于曾经发送定向出席信息给该探测实体的那个用户的全JID. 当该用户的服务器接收到一个探测, 它必须首先应用任何和出席信息订阅相关的逻辑,如4.3.2所述. 如果该探测实体未订阅该用户的出席信息, 那么该服务器必须检查该用户是否曾经在当前会话中发送过定向出席信息给该实体; 如果是, 该服务器应该仅以纯粹的"available"或"unavailable"类型(即, 不包含子元素)应答这个探测并且只用于那个全JID(即, 不为任何可能与当前该用户的纯JID相关的其他资源).
出席信息语法
Type属性
缺少'type'属性代表相关实体是可以通讯的(见4.2和4.4).
'type'属性值为"unavailable"代表相关实体不可通讯(见4.5).
XMPP出席信息节也用于协商和管理对其他实体的出席信息的订阅. 这些任务通过第三章所述的类型为"subscribe", "unsubscribe", "subscribed", 和"unsubscribed"的出席信息节来完成.
如果一个用户和联系人在不同的XMPP服务器上发生关系, 那些服务器为了确定对端服务器上的那个实体的可用性,也使用一个类型为"probe"的特殊的出席信息节; 详见4.3. 客户端不应(SHOULD NOT)发送类型为"probe"的出席信息节.
'type'属性的值可以归纳如下:
- error -- error发生在处理之前发送的出席信息节的时候; 如果出席信息节的类型为"error", 它必须包含一个<error/>子元素(参考 XMPP‑CORE).
- probe -- 请求一个实体当前的出席信息; 应该只由一个服务器代表一个用户生成.
- subscribe -- 发送者希望订阅接收者的出席信息.
- subscribed -- 发送者已经允许接收者接收他们的出席信息.
- unavailable -- 发送者不再可通讯.
- unsubscribe -- 发送者取消订阅接收者的出席信息.
- unsubscribed -- 订阅请求已经被拒绝或撤销之前准许的被对方订阅.
如果'type'属性的值不是前述的任何一个, 接收方或一个中间的路由服务器应该返回一个节错误<bad-request/>.
- 实现备注: <presence/>元素的'type'属性没有缺省值.
- 实现备注: <presence/>元素的'type'属性没有"available"这个值.
子元素
根据缺省命名空间声明, 一个出席信息节是由'jabber:client'或'jabber:server'命名空间限定的, 它定义了明确的出席信息节的子元素, 特别是<show/>, <status/>, 和 <priority/> 元素. 这些子元素被用于提供关于一个实体的可用性的更详细的信息. 典型情况下这些子元素只在该出席信息节没有'type'属性的时候被包含, 尽管在接下来的文本里有一些例外.
Show元素
可选的<show/>元素定义了一个实体或它的一个特定资源的特定的可用性子状态. 一个出席信息节不能(MUST NOT)包含多于一个的<show/>元素. <show/>元素没有定义属性. <show/>元素不能(MUST NOT)包含混合内容(定义于XML3.2.2节). <show/>元素的XML字符数据不意味着它是用于展示给自然人用户看的. 该XML字符数据必须是以下之一(更多的可用性状态可以通过扩展内容元素来定义):
- away -- 该实体或资源临时离开.
- chat -- 该实体或资源活跃并想聊天.
- dnd -- 该实体或资源忙(dnd = "Do Not Disturb",免打扰).
- xa -- 该实体或资源要离开相当长时间(xa = "eXtended Away",长时间离开).
如果<show/>元素未被提供, 该实体被假定在线并且可用.
任何由接收者和代理路由服务器对可用性状态的特别处理取决于该实现(例如, 把可用性状态合并到节路由和递送逻辑中).
Status元素
可选的<status/>元素包含了自然人可读的定义实体可用性的自然语言描述的XML字符数据. 通常在出席信息节不包含'type'属性的时候被用于和show元素结合一起提供某个可用性状态的细节(例如, "In a meeting","开会中").
<presence from='romeo@example.net/orchard' xml:lang='en'> <show>dnd</show> <status>Wooing Juliet</status> </presence>
<status/>元素没有定义属性, 除了从XML继承的'xml:lang'属性. <status/>元素不能(MUST NOT)包含混合内容(定义于XML3.2.2节). <status/>元素可以包含多个实例, 但是只能在每个实例的都有一个'xml:lang'属性并且属性值为唯一的语言值(或者显式地,或者从一个元素的更高的XML级别的'xml:lang'值继承, 从发送者的视角来看,可能包括XMPP‑CORE描述的XML流头).
<presence from='romeo@example.net/orchard' id='jx62vs97' xml:lang='en'> <show>dnd</show> <status>Wooing Juliet</status> <status xml:lang='cs'>Dvořím se Julii</status> </presence>
一个类型为"unavailable"的出席信息节也可以包含一个<status/>元素来提供为什么该实体下线的详细原因.
<presence from='romeo@example.net/orchard' id='oy6sb241' type='unavailable' xml:lang='en'> <status>Busy IRL</status> </presence>
<status/>子元素也可以在一个订阅相关的出席信息节中被发送(即, 类型为 "subscribe", "subscribed", "unsubscribe", 或 "unsubscribed") 以提供该动作的描述. 一个可交互的客户端可以展示这个<status/>信息给一个自然人用户(见第11章).
<presence from='romeo@example.net' id='uc51xs63' to='nurse@example.com' type='subscribe'> <status>Hi, Juliet told me to add you to my buddy list.</status> </presence>
Priority元素
可选的<priority/>元素包含定义该资源优先级的非自然人可读的XML字符数据. 它的值必须是一个处于 -128 和 +127 之间的整数. 一个出席信息不能(MUST NOT)包含多个<priority/>元素. 没有为<priority/>定义属性. <priority/>元素不能(MUST NOT)包含混合内容(定义于XML3.2.2节).
<presence xml:lang='en'> <show>dnd</show> <status>Wooing Juliet</status> <status xml:lang='cs'>Dvořím se Julii</status> <priority>1</priority> </presence>
如果没有提供优先级, 处理的服务器或客户端必须视为优先级为零("0").
该客户端的服务器可以覆写客户端提供的优先级的值(例如, 为了给正在递送给预期的帐号的纯JID的消息强加一个消息处理规则以用于该帐号的所有可用资源). 如果服务器这样做, 当它回应该客户端的出席信息给自己并发送出席信息通知给该用户的联系人们的时候,它必须以修改过的优先级来通讯(因为修改过的优先级的值典型的缺省是零, 以修改过的优先级的值来通讯可以不包含<priority/>子元素).
关于即时消息和出席信息应用中节处理过程中优先级值的语法信息, 参见第八章.
扩展内容
如XMPP‑CORE所述, 一个XML节可以包含任何由不同于缺省命名空间限定的子元素; 这也适用于出席信息节.
(以下例子中, 出席信息节包含定义于XEP‑0115的实体能力信息.)
<presence from='romeo@example.net'> <c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://psi-im.org' ver='q07IKJEyjvHSyhy//CH0CxmKi8w='/> </presence>
包含在一个出席信息节中的任何扩展内容应该展示用于通讯的实体可用性的方方面面或提供和通讯相关的能力.
交换消息
一旦一个客户端如XMPP‑CORE所述,已经在一个服务器上通过了验证并绑定了一个资源到一个XML流,一个XMPP服务器将路由XML节到和从那个客户端. 一种可交换的节是 <message/> (如果, 也就是说, 消息功能在该服务器上是允许的). 交换消息是XMPP的一个基本用途,它发生在一个用户生成一个地址指向另一个实体的消息节的时候. 如第八章所定义的, 该发送者的服务器负责递送该消息到期望的接收者(如果该接收者在同一台本地服务器上)或路由该消息到该接收者的服务器(如果该接收者在一个远端服务器上). 所以一个消息节是被用来"推送"信息给另一个实体.
一对一聊天会话
在实践中, 在自然人用户之间的即时消息活动倾向于以一个会话式的方式发生,我们称之为一个"聊天会话": 在一个比较短暂的时间内双方的多个比较快速的消息交换是有继承的.
当一个自然人用户倾向于和一个联系人进行这样一种聊天会话(而不是发送一个单独的消息给某人却不期望获得应答), 由该用户的客户端生成的这个消息的类型应该是"chat"并且该联系人的客户端应该在随后的应答中保持那个消息类型. 该用户的客户端也应该在它的初始化消息中包含一个<thread/>元素, 这样该联系人的客户端也应该在那个会话的生命周期中保持它(参见5.2.5).
该用户的客户端应该在一个聊天会话中把初始消息的地址指向该联系人的纯JID <contact@domainpart> (而不是基于任何它可能之前从联系人那收到的出席信息通知的 <show/>, <status/>, 或 <priority/> 值来尝试猜测一个适当的全JID <contact@domainpart/resourcepart> ). 直到和除非该用户的客户端从该联系人收到一个应答, 它应该发送任何更多的消息给该联系人的纯JID. 该联系人的客户端应该把它的应答的地址指向该用户的全JID <user@domainpart/resourcepart>,即初始消息中提供的'from'地址. 一旦该用户的客户端从该联系人的全JID收到一个应答, 它应该把它随后的消息指向该联系人的全JID,即在该联系人的应答中提供的'from'地址, 从而"锁定"在那个全JID. 一个客户端应该在接收到一个来自对方节点控制的任何其他资源的<message/>或<presence/>节(或一个来自已锁定的资源的出席信息节)的时候"解除锁定"; 结果是, 它应该在聊天会话的下一个消息中指向对方节点的纯JID(从而"解锁"之前的"锁定"),直到它从对方的全JIDs收到一个消息.
当双方参加一个一个聊天会话但是没有基于一个出席信息订阅来互相分享出席信息, 他们应该互相发送定向出席信息,这样双方能很容易地查询对方在该聊天会话期间是否下线. 然而, 一个客户端必须提供一个方法让一个用户能全局地禁止或只允许特定的实体进行这类出席信息分享. 更进一步的, 当出于某种原因相信该聊天会话结束了(例如, 如果, 在一段合理的时间之后, 双方没有交换随后的消息),一方应该发送定向unavailable出席信息给另一方 .
聊天会话的例子参见第7章.
消息语法
以下章节描述<message/>节的语法.
To属性
即时消息客户端通过在<message/>节中的'to'属性中提供预期的接收者的JID来为一个消息指定预期接收者.
如果该消息在现有的聊天会话或已接收的消息的上下文之外被发送, 'to'地址的值的格式应该是<localpart@domainpart>而不是<localpart@domainpart/resourcepart> (见5.1).
<message from='juliet@example.com/balcony' id='ktx72v49' to='romeo@example.net' type='chat' xml:lang='en'> <body>Art thou not Romeo, and a Montague?</body> </message>
如果该消息是作为对之前从格式<localpart@domainpart/resourcepart>收到的一个消息的应答来发送的(例如, 在一个如5.1所述的一对一聊天会话的上下文中), 该'to'地址的值的格式应该是<localpart@domainpart/resourcepart>而不是<localpart@domainpart>, 除非发送者已经知道(例如, 通过出席信息)接收者的资源不再可用.
<message from='romeo@example.net/orchard' id='sl3nx51f' to='juliet@example.com/balcony' type='chat' xml:lang='en'> <body>Neither, fair saint, if either thee dislike.</body> </message>
Type属性
在即时消息应用中消息节的常用的包括: 单条消息; 在一对一聊天会话的上下文中发送的消息; 在一个多用户聊天室中发送的消息; 警告, 通知, 或其他给不期望应答的人的信息,错误. 这些用途通过'type'属性来区分. 建议包含'type'属性. 如果包含了它, 'type'属性必须是以下的值之一:
- chat -- 该消息在一个一对一聊天会话的上下文中被发送. 典型的一个可交互的客户端将在一个界面上展示一个类型为"chat"的消息,这使得双方之间能够进行一对一聊天, 同时包括一个适当的会话历史. 关于一对一聊天会话的详细建议参见5.1.
- error -- 该消息由一个从另一个实体接收到一个消息并在处理的时候发现了错误的实体生成(关于节错误语法的详情, 参考XMPP‑CORE). 接收到一个类型为"error"的消息的客户端应该展示一个适当的界面来通知原始的发送者关于这个错误的类型.
- groupchat -- 该消息在一个多用户聊天环境的上下文中被发送(类似IRC). 典型的一个接收的客户端将在一个界面展示一个类型为"groupchat"的消息,这使得多方可以进行多对多的聊天, 同时包含一个聊天室中的参与者的名册和适当的会话历史. 关于基于XMPP的群聊的详细信息, 参见XEP‑0045.
- headline -- 该消息提供一个警告, 一个通知, 或其他给那些不期望应答的人的临时信息(例如, 新闻头条, 运动更新, 准实时的市场数据, 或整合的内容). 因为不期望对该消息的回复, 典型的一个接收客户端将在一个界面展示一个类型为"headline"的消息以适当地把它们和独立消息, 聊天消息, 以及群聊消息区分开来(例如, 不向该接收者提供应答能力). 如果'to'地址是纯JID, 该接收服务器应该递送该消息给该接收者的所有非负出席信息优先级的可用资源并且必须递送该消息给那些资源中的至少一个资源; 如果该'to'地址是一个全JID并且有一个匹配的资源, 该服务器必须递送该消息给那个资源; 否则该服务器必须要么安静地忽略该消息要么返回一个错误(见第8章).
- normal -- 该消息是一个在一对一聊天会话或群聊上下文之外的被发送的独立消息, 并且它期望接收者应答. 典型的一个接收客户端将在一个界面展示一个类型为"normal"的消息,这使得接收方能够应答, 但没有会话历史. 'type'属性的缺省值是"normal".
一个IM应用应该支持所有前述的消息类型. 如果一个应用接收了一个没有'type'属性的消息或该应用不理解被提供的'type'属性的值, 它必须认为该消息的类型为"normal" (即, "normal" 是缺省的).
不同消息类型的服务器处理指南参见第8章.
尽管'type'属性是可选的, 出于礼貌在任何对一个消息的应答之中会镜像其类型; 更进一步, 一些专门的应用(例如, 一个多用户聊天服务)可以基于他们的自主判断来强制使用特定消息类型(例如, type='groupchat').
Body元素
<body/>元素包含自然人可读的XML字符数据以指定该消息的正文; 这个子元素通常会被包含但是可选的.
<message from='juliet@example.com/balcony' id='b4vs9km4' to='romeo@example.net' type='chat' xml:lang='en'> <body>Wherefore art thou, Romeo?</body> </message>
没有为<body/>元素定义属性, 除了'xml:lang'属性例外. 在一个消息节中可以包括多个<body/>元素实例用来为同一body提供替代版本, 但是每个实例只允许拥有一个'xml:lang'属性并且其值为一个唯一性的语言(要么显性的, 要么从更高层次的XML继承'xml:lang'值, 从发送者角度看是包含在XMPP‑CORE所述的XML流头中).
<message from='juliet@example.com/balcony' id='z94nb37h' to='romeo@example.net' type='chat' xml:lang='en'> <body>Wherefore art thou, Romeo?</body> <body xml:lang='cs'> PročeŽ jsi ty, Romeo? </body> </message>
<body/>元素不能(MUST NOT)包含混合内容(定义于XML的3.2.2节).
Subject元素
<subject/>元素包含自然人可读的XM字符数据以指定该消息的标题.
<message from='juliet@example.com/balcony' id='c8xg3nf8' to='romeo@example.net' type='chat' xml:lang='en'> <subject>I implore you!</subject> <body>Wherefore art thou, Romeo?</body> </message>
没有为<subject/>元素定义属性, 从XML继承的'xml:lang'属性例外. 可以包含多个<subject/>元素实例以提供同意标题的替代版本, 但是每个实例只允许拥有一个'xml:lang'属性并且其值为一个唯一性的语言(要么显性的, 要么从更高层次的XML继承'xml:lang'值, 从发送者角度看是包含在XMPP‑CORE所述的XML流头中).
<message from='juliet@example.com/balcony' id='jk3v47gw' to='romeo@example.net' type='chat' xml:lang='en'> <subject>I implore you!</subject> <subject xml:lang='cs'> Úpěnlivě prosím! </subject> <body>Wherefore art thou, Romeo?</body> <body xml:lang='cs'> Pročež jsi ty, Romeo? </body> </message>
<subject/>元素不能(MUST NOT)包含混合内容(定义于XML的3.2.2节).
Thread元素
XMPP <thread/>元素主要用于唯一性地标识一个由类型为'chat'的<message/>节实例化的两个实体之间的交谈线索或"聊天会话". 然而, XMPP <thread/>元素也可以被用于唯一性地标识一个由类型为'headline'或'normal'的<message/>节实例化的两个实体之间, 或由类型为'groupchat'的<message/>节实例化的在一个多用户聊天室上下文中的多个实体之间的类似线索. 它也可用于和自然人无关的会话的<message/>节, 类似一个游戏会话或一个插件之间的互动. <thread/>元素不被用于标识个别的消息, 只用于交谈或消息会话.
<thread/>元素是可选的. 因为<thread/>元素标识了一个消息所属的特定交谈线索, 一个消息节不能(MUST NOT)多个<thread/>元素.
<thread/>元素可以拥有一个'parent'属性以标识另一个线索,当前线索是那个线索的分支或子线索. 该'parent'属性必须遵循<thread/>元素本身的语法并且它的值必须和包含了该'parent'属性的那个<thread/>元素的XML字符数据不同.
- 实现备注: 同时指定一个父线索和一个子线索的能力可能会因为重叠的线索而导致线索标识的冲突. 例如, 一个<thread/>元素可能包含XML字符数据"foo"以及一个值为"bar"的'parent'属性, 第二个<thread/>元素可能包含XML字符数据"bar"和一个值为"baz"的'parent'属性, 而第三个<thread/>元素可能包含XML字符数据"baz"和一个值再次为"foo"的'parent'属性. 如果处理这类重叠的线索标识之间的冲突取决于实现(例如, 要么它将线索标识符"链在一起",在一个多级用户界面中把"foo"同时显示成"baz"的一个父线索和一个孙线索, 要么它将在一次只显示依赖的一层).
<thread/>元素的值不是自然人可读的且必须被实体当成不透明的来处理; 从它那里不能获得语义, 并且对它只能做精确比较. <thread/>元素的值必须唯一地标识该交谈线索,在交谈伙伴之间或更更广泛(一个确保唯一性的办法是生成一个UUID所述的全局唯一标识符(UUID)).
- 安全警告: 一个生成ThreadID的应用必须确保它不会泄露关于这个实体的身份信息(例如, 该XMPP应用正在运行的设备的MAC地址).
<thread/>元素不能(MUST NOT)包含混合内容(定义于XML的3.2.2节).
<message from='juliet@example.com/balcony' to='romeo@example.net' type='chat' xml:lang='en'> <subject>I implore you!</subject> <subject xml:lang='cs'> Úpěnlivě prosím! </subject> <body>Wherefore art thou, Romeo?</body> <body xml:lang='cs'> Pročež jsi ty, Romeo? </body> <thread parent='e0ffe42b28561960c6b12b944a092794b9683a38'> 0e3141cd80894871a68e6fe6b1ec56fa </thread> </message>
关于使用<thread/>元素的详细建议, 参考XEP‑0201.
扩展内容
如XMPP‑CORE所述, 一个XML节可以包含任何不同于缺省命名空间的命名空间所限定的子元素; 这也适用于message节. 处理扩展内容的指南参见XMPP‑CORE的8.4节中提供的路由服务器和最终接收者部分.
(在下面的例子中, 该message节包含一个XEP‑0071定义的该消息的XHTML格式版本).
<message from='juliet@example.com/balcony' to='romeo@example.net' type='chat' xml:lang='en'> <body>Wherefore art thou, Romeo?</body> <html xmlns='http://jabber.org/protocol/xhtml-im'> <body xmlns='http://www.w3.org/1999/xhtml'> <p>Wherefore <span style='font-style: italic'>art</span> thou, <span style='color:red'>Romeo</span>?</p> </body> </html> </message>
交换IQ节
如XMPP‑CORE所述, IQ节提供一个结构化的 请求-应答 机制. 那一机制的基本语法(例如, 'id'属性是强制性的)定义于XMPP‑CORE, 特定语法需要完成限定类型为"get"或"set"的IQ节的直接子元素的扩展命名空间的所有实例中定义的用例. 'jabber:client'和'jabber:server'命名空间不定义除了<error/>元素之外的所有节类型的任何IQ节的子元素. 本文定义一个这样的扩展命名空间, 用于管理名册. 无论如何, IQ节可以包含由一个扩展命名空间限定的结构化的信息.
示例会话
本节的例子展示一个可能的即时消息和出席信息会话. 用户是<romeo@example.net>, 他有一个可用资源,其资源部分是"orchard", 并且在他的名册中有以下人员:
- <juliet@example.com> (订阅状态="both" 并且她有两个可用资源, "chamber" 和 "balcony")
- <benvolio@example.net> (订阅状态="to")
- <mercutio@example.org> (订阅状态="from")
首先, 该用户完成如XMPP‑CORE所述的前提条件(流建立, TLS和SASL协商, 以及资源绑定); 那些协议流程在这里不复述了.
接下来, 该用户请求他的名册.
示例1: 用户向服务器请求当前名册
UC: <iq from='romeo@example.net/orchard' id='hf61v3n7' type='get'> <query xmlns='jabber:iq:roster'/> </iq>
示例2: 用户从服务器接收到名册
US: <iq id='hf61v3n7' to='romeo@example.net/orchard' type='result'> <query xmlns='jabber:iq:roster'> <item jid='juliet@example.com' name='Juliet' subscription='both'> <group>Friends</group> </item> <item jid='benvolio@example.org' name='Benvolio' subscription='to'/> <item jid='mercutio@example.org' name='Mercutio' subscription='from'/> </query> </iq>
现在该用户开始一个出席信息会话.
示例3: 用户发送初始出席信息
UC: <presence/>
示例4: 用户的服务器代表该用户发送出席信息探测给订阅状态="to"和订阅状态="both"的联系人们
US: <presence from='romeo@example.net' to='juliet@example.com' type='probe'/> US: <presence from='romeo@example.net' to='benvolio@example.org' type='probe'/>
示例5: 用户的服务器代表该用户的可用资源发送初始出席信息给订阅状态="from"和订阅状态="both"的联系人们, 以及用户自己
US: <presence from='romeo@example.net/orchard' to='juliet@example.com'/> US: <presence from='romeo@example.net/orchard' to='mercutio@example.org'/> US: <presence from='romeo@example.net/orchard' to='romeo@example.net'/>
示例6: 联系人的服务器代表所有可用资源应答该出席信息探测
CS: <presence from='juliet@example.com/balcony' to='romeo@example.net' xml:lang='en'> <show>away</show> <status>be right back</status> <priority>0</priority> </presence> CS: <presence from='juliet@example.com/chamber' to='romeo@example.net'> <priority>1</priority> </presence> CS: <presence from='benvolio@example.org/pda' to='romeo@example.net' xml:lang='en'> <show>dnd</show> <status>gallivanting</status> </presence>
示例7: 联系人的服务器们递送用户的初始出席信息给所有可用资源
CS: <presence from='romeo@example.net/orchard' to='juliet@example.com'/> CS: <presence from='romeo@example.net/orchard' to='juliet@example.com'/> CS: <presence from='romeo@example.net/orchard' to='mercutio@example.org'/>
示例8: 用户发送定向出席信息给另一个不在他的名册中的用户
UC: <presence from='romeo@example.net/orchard' to='nurse@example.com' xml:lang='en'> <show>dnd</show> <status>courting Juliet</status> <priority>0</priority> </presence>
现在该用户和他的联系人之一进入一个聊天会话.
示例9: 一个带线索的交谈
CC: <message from='juliet@example.com/balcony' to='romeo@example.net' type='chat' xml:lang='en'> <body>My ears have not yet drunk a hundred words</body> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> </message> CC: <message from='juliet@example.com/balcony' to='romeo@example.net' type='chat' xml:lang='en'> <body>Of that tongue's utterance, yet I know the sound:</body> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> </message> CC: <message from='juliet@example.com/balcony' to='romeo@example.net' type='chat' xml:lang='en'> <body>Art thou not Romeo, and a Montague?</body> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> </message> UC: <message from='romeo@example.net/orchard' to='juliet@example.com/balcony' type='chat' xml:lang='en'> <body>Neither, fair saint, if either thee dislike.</body> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> </message> CC: <message from='juliet@example.com/balcony' to='romeo@example.net/orchard' type='chat' xml:lang='en'> <body>How cam'st thou hither, tell me, and wherefore?</body> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> </message>
以此类推.
该用户也能发送随后的出席信息广播.
示例10: 用户发送更新的可用信息用于广播
UC: <presence xml:lang='en'> <show>away</show> <status>I shall return!</status> <priority>1</priority> </presence>
示例11: 用户的服务器广播更新的出席信息给订阅类型为"both"或"from"的联系人(但是不包括该用户向其发送过定向出席信息的实体)
US: <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> US: <presence from='romeo@example.net/orchard' to='mercutio@example.org' xml:lang='en'> <show>away</show> <status>I shall return!</status> <priority>1</priority> </presence>
示例12: 联系人们的服务器递送更新的出席信息
CS: <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> CS: <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> CS: <presence from='romeo@example.net/orchard' to='mercutio@example.org' xml:lang='en'> <show>away</show> <status>I shall return!</status> <priority>1</priority> </presence>
示例13: 联系人们的可用资源之一广播不可用(离线)通知
CC: <presence from='juliet@example.com/chamber' type='unavailable'/>
示例14: 联系人的服务器发送不可用(离线)通知给用户
CS: <presence from='juliet@example.com/chamber' to='romeo@example.net' type='unavailable'/>
现在该用户结束他的出席信息会话.
示例15: 用户发送不可用(离线)通知
UC: <presence type='unavailable' xml:lang='en'> <status>gone home</status> </presence>
示例16: 用户的服务器广播离线通知给联系人们以及该用户曾经发送过定向出席信息的那个实体
US: <presence from='romeo@example.net/orchard' to='juliet@example.com' type='unavailable' xml:lang='en'> <status>gone home</status> </presence> US: <presence from='romeo@example.net/orchard' to='mercutio@example.org' type='unavailable' xml:lang='en'> <status>gone home</status> </presence> US: <presence from='romeo@example.net/orchard' to='nurse@example.com' type='unavailable' xml:lang='en'> <status>gone home</status> </presence>
最后该用户关闭他的流并且他的服务器以同样方式应答.
示例17: 用户关闭流
UC: </stream:stream>
示例18: 用户的服务器关闭流
US: </stream:stream>
结束
处理XML节的服务器规则
处理XML节的基本服务器规则定义于XMPP‑CORE, 读者可以参考那个协议的基础规则和安全含义. 本章定义用于XMPP即时消息和出席信息服务器的附加规则.
本章定义的一些递送规则指定了"离线存储"的使用, 即, 该服务器代表该用户存储一个消息节的行为以及当该用户下次上线的时候递送它. 关于离线消息存储的建议, 参考XEP‑0160.
常规事项
XMPP‑CORE讨论了节递送的常规事项, 特别是在 (i) 提供一个关于节递送的服务的可接受级别 和 (ii) 防止目录捕获攻击和出席信息泄露 之间的平衡. 无论如何, 目录捕获攻击的概念不适用于一个已知并被一个用户信任的联系人(因为如第2章所述该联系人在该用户的名册中). 类似的, 出席信息泄露的概念不适用于一个已被授权知道一个用户的出席信息的联系人(通过第3章所述的所谓出席信息订阅)或该用户曾经自发的发送出席信息给它的那个实体(如4.6所述的的所谓定向出席信息). 所以, 下面章节的场景中用来防止目录捕获和出席信息泄露的替代方式有 (a) 安静地忽略一个节 或 (b) 返回一个错误, 如果发起的实体在该用户的名册(当该错误将泄露该用户的帐号是否存在),或被授权从该用户接收出席信息,或已经从该用户接收到定向出席信息(当该错误将泄露一个用户的资源的出席信息),服务器应该返回一个错误.
- 安全事项: 接下来描述的所有节处理规则的定义,要理解它们将的应用,将遵循相关的隐私和安全行策略的强制检查, 类似那些XEP‑0016或XEP‑0191的部署. 接下来的章节中的一致性语言(MUST, SHOULD, 等等)不意味着覆盖任何本地服务策略.
无'to'地址
如果该节无'to'属性, 适用规则定义于XMPP‑CORE.
远端域
如果包含在一个出站节的'to'属性中的地址的域部分和该服务器本身配置的域不匹配, 则适用的规则见于XMPP‑CORE的10.4节.
- 互操作性备注: RFC 3921 定义了如何使用 _im._xmpp 和 _pres._xmpp SRV记录 IMP‑SRV 作为一个应急方法来查询是否一个远端的即时消息和出席信息服务能通过以XMPP通讯. 因为那些SRV记录未被广泛部署, 本文不再定义它们的使用, 并且不鼓励新的实现.
本地域
如果在'to'属性中包含的JID的域部分和该服务器配置的域之一匹配, 该域由该服务器本身(而不是由一个特别的本地服务)提供服务, 并且该JID的格式为<domainpart>或<domainpart/resourcepart>, 适用规则定义于XMPP‑CORE.
本地用户
如果'to'地址指定了一个纯JID <localpart@domainpart> 或全JID <localpart@domainpart/resourcepart>, 该JID的域部分和由该服务器本身提供服务的已配置的域匹配, 该服务器必需做如下处理.
无该用户
如果由'to'属性而来的该用户帐号标识符不存在, 该节如何处理取决于节类型.
- 对于一个IQ节, 该服务器必须返回一个<service-unavailable/>节错误给该发送者.
- 对于一个message节, 该服务器必需要么(a) 安静地忽略该消息 要么 (b) 返回一个<service-unavailable/>节错误给该发送者.
- 对于一个不带'type'属性或'type'属性为"unavailable"的的presence节, 该服务器必须安静地忽略该节.
- 对于一个类型为"subscribe", "subscribed", "unsubscribe", 或"unsubscribed"的presence节, 该服务器必须安静地忽略该节.
- 对于一个类型为"probe"的presence节, 该服务器必需要么(a) 安静地忽略该节 要么 (b) 返回一个类型为"unsubscribed"的presence节.
本地部分@域部分
如果'to'属性中包含的JID的格式为<localpart@domainpart>, 那么该服务器必需遵循以下规则.
可用或已连接的资源
如果至少有一个可用资源或已连接资源, 如何处理该节取决于该节的类型.
Message
对于一个类型为"normal"的消息节:
- 如果所有可用资源有一个负的出席信息优先级,那么该服务器应该要么 (a) 存储该消息为离线用于晚些时候递送 要么 (b) 返回一个节错误给该发送者, 该错误应该是<service-unavailable/>.
- 如果有一个可用资源而它是非负的出席信息优先级,那么该服务器必须递送该消息给那个资源.
- 如果有多个可用资源都有非负的出席信息优先级,那么该服务器必须要么 (a) 递送该消息给"最可用的"资源或资源们(根据服务器的实现特有的机制, 例如, 认为拥有最高出席信息优先级的资源或资源们是"最可用的") 要么 (b) 递送该消息给所有非负的资源.
对于一个类型为"chat"的消息节:
- 如果这个唯一的可用资源有一个负的出席信息优先级,那么该服务器应该要么 (a) 存储该消息为离线用于晚些时候递送 要么 (b) 返回一个节错误给该发送者, 该错误应该是<service-unavailable/>.
- 如果这个唯一的可用资源有一个非负的出席信息优先级,那么该服务器必须递送该消息给那个资源.
- 如果有多个可用资源都有非负的出席信息优先级,那么该服务器必须要么 (a) 递送该消息给"最可用的"资源或资源们(根据服务器的实现特有的机制, 例如, 认为拥有最高出席信息优先级的资源或资源们是"最可用的") 要么 (b) 递送该消息给所有曾经被选择接收了聊天消息的非负的资源.
对于类型为"groupchat"的消息节, 该服务器不能(MUST NOT)递送该节给任何可用资源,而必须返回一个节错误给该发送者, 该错误应该是<service-unavailable/>.
对于一个类型为"headline"的消息节:
- 如果这个唯一的可用资源有一个负的出席信息优先级,那么该服务器必须安静地忽略该节
- 如果这个唯一的可用资源有一个非负的出席信息优先级,那么该服务器必须递送该消息给那个资源.
- 如果有多个可用资源都有非负的出席信息优先级,那么该服务器必须递送该消息给所有非负的资源.
对于类型为"error"的消息节, 该服务器必须安静地忽略该消息.
无论如何, 对于任何消息类型,该服务器不能(MUST NOT)递送该节给任何负优先级的可用资源; 如果只有一个可用资源且优先级为负, 该服务器应该如8.5.2.2所述,视同没有可用资源或已连接资源那样处理该消息.
在所有情况下, 该服务器不能(MUST NOT)重写'to'属性(即, 它必须让它就是<localpart@domainpart>而不是把它改成<localpart@domainpart/resourcepart>).
Presence
对于一个没有类型或类型为"unavailable"的出席信息节, 该服务器必须递送它到所有可用资源.
对于一个类型为"subscribe", "subscribed", "unsubscribe", 或"unsubscribed"的出席信息节, 该服务器必须遵循第3章定义的规则和附录A的总结.
对于一个类型为"probe"的出席信息节, 该服务器必须如4.3所述直接处理它.
在所有情况下, 该服务器不能(MUST NOT)重写'to'属性(即, 它必须让它就是<localpart@domainpart>而不是把它改成<localpart@domainpart/resourcepart>).
IQ
对于一个IQ节, 该服务器本身必须代表该用户应答一个IQ result 或一个IQ error, 并且不能(MUST NOT)递送该IQ节给该用户的任何可用资源. 特别是, 如果限定的命名空间的语义定义了一个该服务器能代表该用户提供应答, 那么该服务器必须代表该用户应答该节,要么返回一个类型为"result"的IQ节要么是一个类型为"error"的适合原始载荷的IQ节; 否则, 该服务器必须应答一个<service-unavailable/>节错误.
无可用或已连接的资源
如果没有可用资源或已连接资源, 如何处理该节取决于该节的类型.
Message
对于一个类型为"normal"或"chat"的消息节, 该服务器应该要么 (a) 添加该消息到离线存储 要么 (b)返回一个节错误给该发送者, 该错误应该为<service-unavailable/>.
对于一个类型为"groupchat"的消息节, 该服务器必须返回一个节错误给该发送者, 该错误应该为<service-unavailable/>.
对于一个类型为"headline" 或 "error"的消息节, 该服务器必须安静地忽略该消息.
Presence
对于一个没有类型或类型为"unavailable"的出席信息节, 该服务器应该安静地忽略该节,不把它存下来用于晚些时候递送,也不代表该用户应答它.
对于一个类型为"subscribe", "subscribed", "unsubscribe", 或"unsubscribed"的出席信息节, 该服务器必须遵循第3章定义的规则和附录A的总结.
对于一个类型为"probe"的出席信息节, 该服务器必须如4.3所述直接处理它.
IQ
对于一个IQ节, 该服务器本身必须代表该用户应答一个IQ result 或一个IQ error, 并且不能(MUST NOT)递送该IQ节给该用户的任何可用资源. 特别是, 如果限定的命名空间的语义定义了一个该服务器能代表该用户提供应答, 那么该服务器必须代表该用户应答该节,要么返回一个类型为"result"的IQ节要么是一个类型为"error"的适合原始载荷的IQ节; 否则, 该服务器必须应答一个<service-unavailable/>节错误.
本地部分@域部分/资源部分
如果一个入站节的'to'属性中包含的JID的域部分和该服务器本身配置的域之一匹配并且包含在'to'属性中的JID的格式为<localpart@domainpart/resourcepart>, 那么该服务器必须遵循以下规则.
资源匹配
如果一个可用的资源或已连接的资源准确地匹配该全JID, 如何处理该节取决于该节的类型.
- 对于一个类型为"get"或"set"的IQ节, 如果预期的接收者未通过类型为"both"或"from"的所谓出席信息订阅或通过所谓定向出席信息来分享出席信息给请求实体, 那么该服务器不应该(SHOULD NOT)递送该IQ节,而应该返回一个<service-unavailable/>节错误给请求实体. 这个策略帮助防止出席信息泄漏(见第11章).
- 对于一个类型为"result" 或 "error"的IQ节, 该服务器必须递送该节给该资源.
- 对于一个消息节, 该服务器必须递送该节给该资源.
- 对于一个没有'type'属性或'type'属性为"unavailable"的出席信息节, 该服务器必须递送该节给该资源.
- 对于一个类型为"subscribe", "subscribed", "unsubscribe", 或 "unsubscribed"的出席信息节, 该服务器必须遵循第3章提供的指南.
- 对于一个类型为"probe"的出席信息节, 该服务器必需跟随4.3提供的指南.
无资源匹配
如果没有可用资源或已连接的资源准确匹配该全JID, 如何处理该节取决于该节的类型.
Message
对于一个类型为"normal", "groupchat", 或"headline"的消息节, 该服务器必须要么 (a) 安静地忽略该节 要么 (b) 返回一个错误节给该发送者, 该节错误应该是<service-unavailable/>.
对于一个类型为"chat"的消息节:
- 如果没有可用或已连接的资源, 该服务器必须要么 (a) 存储该消息到离线用于晚些时候递送 要么 (b) 返回一个错误节给该发送者, 该节错误应该是<service-unavailable/>.
- 如果所有可用资源都有一个负的出席信息优先级那么该服务器应该 (a) 存储该消息到离线用于晚些时候递送 或 (b) 返回一个错误节给该发送者, 该节错误应该是<service-unavailable/>.
- 如果有一个可用资源的出席信息优先级不是负的那么该服务器必须递送该消息给那个资源.
- 如果有多个资源的出席信息优先级是非负的那么该服务器必须要么 (a) 递送该消息给"最可用的"资源或资源们(根据服务器的实现特有的机制, 例如, 认为拥有最高出席信息优先级的资源或资源们是"最可用的") 要么 (b) 递送该消息给所有曾经被选择接收了聊天消息的非负的资源.
对于一个类型为"error"的消息节, 该服务器必须安静地忽略该节.
Presence
对于一个没有'type'属性或'type'属性为"unavailable"的出席信息节, 该服务器必须安静地忽略该节.
对于一个类型为"subscribe"的出席信息节, 该服务器必须遵循3.1.3提供指南.
对于一个类型为"subscribed", "unsubscribe", 或 "unsubscribed"的出席信息节, 该服务器必须忽略该节.
对于一个类型为"probe"的出席信息节, 该服务器必须遵循4.3提供的指南.
IQ
对于一个IQ节, 该服务器必须返回一个<service-unavailable/>节错误给该发送者.
消息递送规则的总结
下表总结了本章前面描述的消息(不是节)递送规则. 左边的列显示了各种条件(不存在的帐号, 没有活跃的资源, 只有一个资源并且出席信息优先级为负, 只有一个资源并且出席信息优先级为非负, 或多个资源并且每个的出席信息优先级都为非负)和'to'地址(纯JID, 匹配一个可用资源的全JID, 或没有匹配的可用资源的全JID)的组合. 随后的列列出了四个主要的消息类型(normal, chat, groupchat, 或 headline) 以及六种可能的递送选项: 存储消息到离线 (O), 以一个节错误弹回该消息 (E), 安静地忽略该消息 (S), 递送该消息到'to'地址中指定的资源 (D), 根据服务器实现特有的机制递送该消息到"最可用的"资源或资源们, 例如, 认为拥有最高出席信息优先级的资源或资源们是"最可用的" (M), 或递送该消息给所有拥有非负出席信息优先级的资源 (A -- 这里对于 chat 消息来说 "所有资源" 可能意味着一组显式地被选中接收每个chat消息的资源). '/' 字符代表"异或". 该服务器在给一个特定的消息选择哪种动作的时候,应该遵守8.1给出的规则
表1: 消息递送规则
条件 | Normal | Chat | Groupchat | Headline |
---|---|---|---|---|
帐号不存在 + 纯JID | S/E | S/E | E | S |
帐号不存在 + 全JID | S/E | S/E | S/E | S/E |
帐号存在但无活跃资源 + 纯JID | O/E | O/E | E | S |
帐号存在但无活跃资源 + 全JID(不匹配) | S/E | O/E | S/E | S/E |
多个负资源但无非负资源 + 纯JID | O/E | O/E | E | S |
多个负资源但无非负资源 + 全JID(匹配) | D | D | D | D |
多个负资源但无非负资源 + 全JID(不匹配) | S/E | O/E | S/E | S/E |
一个非负资源 + 纯JID | D | D | E | D |
一个非负资源 + 全JID(匹配) | D | D | D | D |
一个非负资源 + 全JID(不匹配) | S/E | D | S/E | S/E |
多个非负资源 + 纯JID | M/A | M/A* | E | A |
多个非负资源 + 全JID(匹配) | D | D/A* | D | D |
多个非负资源 + 全JID(不匹配) | S/E | M/A* | S/E | S/E |
- 对于类型为"chat"的消息, 一个服务器不应该(SHOULD NOT)根据选项(A)来动作,除非客户端能显式地选择接收所有的chat消息; 无论如何, 选择的方法超出了本协议的范围.
URIs的处理
在一个XMPP网络上用于通讯的XMPP实体的地址(例如, 在一个XML节中的'from'和'to'地址)不能(MUST NOT)被预设为一个统一资源定位符URIscheme.
无论如何, 一个位于XMPP本身之外的应用(例如, 一个位于Web网站的页面)可能需要把一个XMPP实体标识为一个URI或一个国际化资源标识符IRI, 并且一个XMPP客户端可能需要和一个这样的外部应用交互(例如, 一个XMPP客户端可能通过点击一个web网页上的链接而被调用). 在这类交互的上下文中, XMPP客户端被鼓励处理XMPP‑URI中指定的的被编码为"xmpp:"的URIs和IRIs的地址,更多描述参见XEP‑0147. 尽管XMPP客户端也被鼓励处理CPIM中指定的编码为"im:"的URIs地址和CPP中指定的编码为"pres:"的URIs地址, 它们可以如8.3所定义的那样,移除"im:"或"pres:"的scheme并把地址解析委托给服务器.
国际化事项
关于国际化事项, 参考XMPP‑CORE的有关章节.
安全事项
关于XMPP的核心安全事项位于XMPP‑CORE的第13章, 包括通道加密, 验证, 信息泄露, 拒绝服务攻击, 以及域间联盟的讨论.
XMPP‑CORE第13.1节大概描述了在典型的XMPP部署中客户端和服务器的架构角色, 并讨论了和那些角色相关的安全特性. 这些角色对本文描述的即时消息,出席信息订阅,和出席信息通知的安全性上有影响. 本质上, 一个XMPP用户在一个XMPP服务器上注册或已经被预分配)一个帐号, 所以在这个服务器上给予了某些级别的信任以代表该用户完成各种任务, 强制安全性策略, 等等. 因此服务器的责任是:
- 本地客户端和远端服务器之间的通讯最好强制使用通道加密.
- 验证任何希望访问该用户的帐号的客户端.
- 处理已经被验证的客户端收到和发出的XML节(特别是关于即时消息和出席信息功能, 存储该用户的名册, 处理入站和出站订阅请求和应答, 生成和处理出席信息探测, 广播出站出席信息通知, 路由出站消息, 以及递送入站消息和出席信息通知).
如XMPP‑CORE的第13.1节和第13.4讨论的, 即使该服务器履行了上述的责任, 该客户端对于节可能和另一个客户端(在同一台服务器上或在一个远端服务器上)的交换没有任何保证,它可能在XMPP通讯路径中的所有跳中被保护, 或只在该服务器本身被保护. 如果该客户端希望确保它的通讯的点对点的机密性和完整性,使用一个适当的技术来对XML节加密和签名是它的责任.
只适用于XMPP的即时消息和出席信息应用的额外事项定义于本文的很多地方; 特别是:
- 当一个服务器拥有一个类型为"probe"的入站出席信息节,且它的预期接收者是和该服务器的已配置域之一相关的一个用户, 如果该发送者是一个未被授权接收那个由出席信息订阅决定的信息的实体(见第4章),该服务器不能(MUST NOT)透露该用户的出席信息.
- 一个用户的服务器不能(MUST NOT)泄露该用户的网络可用性给未被授权知道该用户的出席信息的的实体们. 在XMPP本身, 授权的方式是一个显性的从联系人到用户的订阅(如第3章所述). 无论如何,如果在该实体和生成了出席信息的该用户之间已经存在一个信任关系(例如, 如果该组织把强制分享出席信息作为员工协议的一部分,一个XMPP的企业部署可能自动把该用户的出席信息添加到一个员工私有目录),一些XMPP部署可能认为一个实体已被授权.
- 当一个服务器处理一个没有类型或类型为"unavailable"的出站出席信息节, 它必须遵循定义于第4章的规则以确保这类出席信息不被发送给未被授权知道这类信息的实体们.
- 一个客户端可以忽略<status/>元素,当该节包含在一个类型为"subscribe", "unsubscribe", "subscribed", 或 "unsubscribed"的出席信息节中的时候; 这有助于组织"出席信息订阅垃圾".
一致性需求
本章描述了一个协议特性集合来概述本协议的一致性需求. 本特性集合适用于软件认证, 互操作性测试, 和实现报告. 对于每个特性, 本章提供了以下信息:
- 自然人可读的名称
- 描述信息
- 本文特定章节的参考,该章节规范化地定义了该特性
- 该特性是否适用于客户端角色,服务器角色,或同时适用于两者(这里 "N/A" 表示该特性不适用于该特定角色)
- 该特性是必须(MUST)还是应该(SHOULD)被实现, 这里大写的术语被视为KEYWORDS所描述的含义
这里定义的特性集合尝试坚持Larry Masinter于2005年在IETF的NEWTRK工作组中建议的概念和格式, 摘自INTEROP. 尽管该特性集合比REPORTS中说的更详细, 它为实现报告的生成提供了一个合适的基础,这些报告将被提交以支持本协议按照PROCESS从建议标准发展成草案标准.
特性: message-body 描述: 支持<message/>节的<body/>子元素. 章节: 5.2.3 角色: 客户端 必须, 服务器 N/A.
特性: message-subject 描述: 支持<message/>节的<subject/>子元素. 章节: 5.2.4 角色: 客户端 应该, 服务器 N/A.
特性: message-thread 描述: 支持<message/>节的<thread/>子元素. 章节: 5.2.5 角色: 客户端 应该, 服务器 N/A.
特性: message-type-support 描述: 支持接收类型为"normal", "chat", "groupchat", "headline", 和 "error"的消息. 章节: 5.2.2 角色: 客户端 应该, 服务器 N/A.
特性: message-type-deliver 描述: 适当地递送类型为"normal", "chat", "groupchat", "headline", 和 "error"的消息. 章节: 8 角色: 客户端 N/A, 服务器 应该.
特性: presence-notype 描述: 把没有'type'属性的出席信息节视为指示可用性(在线). 章节: 4.7.1 角色: 客户端 必须, 服务器 必须.
特性: presence-probe 描述: 发送和接收'type'属性为"probe"的出席信息节用来查询出席信息. 章节: 4.7.1 角色: 客户端 N/A, 服务器 必须.
特性: presence-sub-approval 描述: 把类型为"subscribed"的出站出席信息节视为对之前从另一个实体接收到的出席信息订阅请求的批准动作, 并且把类型为"subscribed"的入站出席信息节视为来自另一个实体的订阅批准. 章节: 3.1 角色: 客户端 必须, 服务器 必须.
特性: presence-sub-cancel 描述: 把类型为"unsubscribed"的出站出席信息节视为对之前从另一个实体接收到的出席信息订阅请求的拒绝动作或对之前授予另一个实体的订阅批准的取消动作, 并且把类型为"unsubscribed"的入站出席信息节视为来自另一个实体的订阅拒绝或取消.. 章节: 3.2 角色: 客户端 必须, 服务器 必须.
特性: presence-sub-preapproval 描述: 把类型为"subscribed"的出站出席信息节视为对之前对从另一个实体接收到的订阅请求的预批准动作的确认; 这包括支持'jabber:iq:roster'命名空间的<item/>元素的'approved'属性. 章节: 3.4 角色: 客户端 可以, 服务器 可以.
特性: presence-sub-request 描述: 把类型为"subscribe"的出站出席信息节视为请求订阅另一个实体的出席信息, 并且把类型为"subscribe"的入站出席信息节视为来自另一个实体的出席信息订阅请求. 章节: 3.1 角色: 客户端 必须, 服务器 必须.
特性: presence-sub-unsubscribe 描述: 把类型为"unsubscribe"的出站出席信息节视为从另一个实体取消订阅的动作, 并把类型为"unsubscribe"的入站出席信息视为来自另一个实体的取消订阅通知. 章节: 3.3 角色: 客户端 必须, 服务器 必须.
特性: presence-unavailable 描述: 把'type'属性为"unavailable"的出席信息节视为指示失去可用性. 章节: 4.7.1 角色: 客户端 必须, 服务器 必须.
特性: roster-get 描述: 把类型为"get"包含一个空的由'jabber:iq:roster'命名空间限定的<query/>元素的IQ节视为一个获取某个服务器上的某个帐号的名册信息的请求. 章节: 2.1.3 角色: 客户端 必须, 服务器 必须.
特性: roster-set 描述: 把类型为"set"包含一个由'jabber:iq:roster'命名空间限定的<query/>元素的IQ节视为一个添加或更新包含在该<query/>元素中的条目的请求. 章节: 2.1.5 角色: 客户端 必须, 服务器 必须.
特性: roster-push 描述: 无论何时服务器那一边名册信息出现了实质上的改变,都要发送一个名册推送给感兴趣的资源, 或在从该服务器接收到的时候处理这类推送. 章节: 2.1.6 角色: 客户端 必须, 服务器 必须.
特性: roster-version 描述: 把由'jabber:iq:roster'命名空间限定的<query/>元素中的'ver'属性视为被发送或接收的名册信息的特定版本的标识符. 章节: 2.1.1 角色: 客户端 应该, 服务器 必须.
参考
规范引用
[DELAY] Saint-Andre, P., “Delayed Delivery,” XSF XEP 0203, September 2009.
[KEYWORDS] Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels,” BCP 14, RFC 2119, March 1997 (TXT, HTML, XML).
[XML] Maler, E., Yergeau, F., Sperberg-McQueen, C., Paoli, J., and T. Bray, “Extensible Markup Language (XML) 1.0 (Fifth Edition),” World Wide Web Consortium Recommendation REC‑xml‑20081126, November 2008 (HTML).
[XML-NAMES] Bray, T., Hollander, D., and A. Layman, “Namespaces in XML,” W3C REC-xml-names, January 1999.
[XMPP-CORE] Saint-Andre, P., “Extensible Messaging and Presence Protocol (XMPP): Core,” RFC 6120, March 2011.
参考文献
[CPIM] Peterson, J., “Common Profile for Instant Messaging (CPIM),” RFC 3860, August 2004 (TXT).
[CPP] Peterson, J., “Common Profile for Presence (CPP),” RFC 3859, August 2004 (TXT).
[DOS] Handley, M., Rescorla, E., and IAB, “Internet Denial-of-Service Considerations,” RFC 4732, December 2006 (TXT).
[IMP-MODEL] Day, M., Rosenberg, J., and H. Sugano, “A Model for Presence and Instant Messaging,” RFC 2778, February 2000.
[IMP-REQS] Day, M., Aggarwal, S., and J. Vincent, “Instant Messaging / Presence Protocol Requirements,” RFC 2779, February 2000 (TXT).
[IMP-SRV] Peterson, J., “Address Resolution for Instant Messaging and Presence,” RFC 3861, August 2004 (TXT).
[INTEROP] Masinter, L., “Formalizing IETF Interoperability Reporting,” Work in Progress, October 2005.
[IRC] Kalt, C., “Internet Relay Chat: Architecture,” RFC 2810, April 2000 (TXT).
[IRI] Duerst, M. and M. Suignard, “Internationalized Resource Identifiers (IRIs),” RFC 3987, January 2005 (TXT).
[PROCESS] Bradner, S., “The Internet Standards Process -- Revision 3,” BCP 9, RFC 2026, October 1996 (TXT).
[REPORTS] Dusseault, L. and R. Sparks, “Guidance on Interoperation and Implementation Reports for Advancement to Draft Standard,” BCP 9, RFC 5657, September 2009 (TXT).
[RFC3920] Saint-Andre, P., Ed., “Extensible Messaging and Presence Protocol (XMPP): Core,” RFC 3920, October 2004 (TXT, HTML, XML).
[RFC3921] Saint-Andre, P., Ed., “Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence,” RFC 3921, October 2004 (TXT, HTML, XML).
[SASL] Melnikov, A. and K. Zeilenga, “Simple Authentication and Security Layer (SASL),” RFC 4422, June 2006 (TXT).
[SIP-PRES] Rosenberg, J., “A Presence Event Package for the Session Initiation Protocol (SIP),” RFC 3856, August 2004 (TXT).
[TLS] Dierks, T. and E. Rescorla, “The Transport Layer Security (TLS) Protocol Version 1.2,” RFC 5246, August 2008 (TXT).
[TLS-CERTS] Saint-Andre, P. and J. Hodges, “Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS),” RFC 6125, March 2011.
[UNICODE] The Unicode Consortium, “The Unicode Standard, Version 6.0,” 2010.
[URI] Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax,” STD 66, RFC 3986, January 2005 (TXT).
[UUID] Leach, P., Mealling, M., and R. Salz, “A Universally Unique IDentifier (UUID) URN Namespace,” RFC 4122, July 2005 (TXT, HTML, XML).
[XEP-0016] Millard, P. and P. Saint-Andre, “Privacy Lists,” XSF XEP 0016, February 2007.
[XEP-0045] Saint-Andre, P., “Multi-User Chat,” XSF XEP 0045, July 2008.
[XEP-0054] Saint-Andre, P., “vcard-temp,” XSF XEP 0054, July 2008.
[XEP-0071] Saint-Andre, P., “XHTML-IM,” XSF XEP 0071, September 2008.
[XEP-0115] Hildebrand, J., Saint-Andre, P., and R. Tronçon, “Entity Capabilities,” XSF XEP 0115, February 2008.
[XEP-0147] Saint-Andre, P., “XMPP URI Scheme Query Components,” XSF XEP 0147, September 2006.
[XEP-0160] Saint-Andre, P., “Best Practices for Handling Offline Messages,” XSF XEP 0160, January 2006.
[XEP-0163] Saint-Andre, P. and K. Smith, “Personal Eventing Protocol,” XSF XEP 0163, July 2010.
[XEP-0191] Saint-Andre, P., “Simple Communications Blocking,” XSF XEP 0191, February 2007.
[XEP-0201] Saint-Andre, P., Paterson, I., and K. Smith, “Best Practices for Message Threads,” XSF XEP 0201, November 2010.
[XEP-0237] Saint-Andre, P. and D. Cridland, “Roster Versioning,” XSF XEP 0237, March 2010.
[XML-DATATYPES] Biron, P. and A. Malhotra, “XML Schema Part 2: Datatypes Second Edition,” W3C REC-xmlschema-2, October 2004 (HTML).
[XML-SCHEMA] Thompson, H., Maloney, M., Mendelsohn, N., and D. Beech, “XML Schema Part 1: Structures Second Edition,” World Wide Web Consortium Recommendation REC-xmlschema-1-20041028, October 2004 (HTML).
[XMPP-ADDR] Saint-Andre, P., “Extensible Messaging and Presence Protocol (XMPP): Address Format,” RFC 6122, March 2011.
[XMPP-URI] Saint-Andre, P., “Internationalized Resource Identifiers (IRIs) and Uniform Resource Identifiers (URIs) for the Extensible Messaging and Presence Protocol (XMPP),” RFC 5122, February 2008 (TXT).
[VCARD] Dawson, F. and T. Howes, “vCard MIME Directory Profile,” RFC 2426, September 1998 (HTML, XML).
附录A. 订阅状态
本章提供关于订阅状态的详细信息和订阅相关的出席信息节(即, 类型为"subscribe", "subscribed", "unsubscribe", 和"unsubscribed"的出席信息节)的服务器处理.
A.1. 已定义的状态
有四个主要的订阅状态(这些状态是从用户而不是联系人的视角来描述的):
- None:
- 该用户没有对该联系人的出席信息的订阅, 且该联系人也没有对该用户的出席信息的订阅.
- To:
- 该用户有一个对该联系人的订阅, 但该联系人没有对该用户的出席信息的订阅.
- From:
- 该联系人有一个对该用户的出席信息的订阅, 该用户没有对该联系人的出席信息的订阅.
- Both:
- 该用户和该联系人对对方的出席信息都有订阅(即, 'from'和'to'的联合).
- 实现备注: 为了如接下来的章节所述那样处理订阅相关的出席信息节, 订阅状态"None"包括该联系人根本不在该用户的名册中的情形, 即, 一个从用户的名册的视角来看是未知的实体.
前述的状态被各种和未决的入站和出站订阅相关的子状态所补充, 所以有高达九种可能的订阅状态:
- "None" = 联系人和用户未彼此订阅, 且未曾向对方请求一个订阅; 反映在该用户的名册中就是 subscription='none'.
- "None + Pending Out" = 联系人和用户未彼此订阅, 而用户已经发送了一个订阅请求给联系人但是尚未被应答; 这反映在该用户的名册中就是 subscription='none' 且 ask='subscribe'.
- "None + Pending In" = 联系人和用户未彼此订阅, 而联系人已经发送了一个订阅请求给用户但是尚未被应答. 这个状态可以反映也可以不反映在该用户的名册中, 如下: 如果该用户曾经为该联系人创建一个名册条目那么该服务器必须维护那个名册条目并且也注意到该入站出席信息订阅请求的存在, 反之如果该用户未曾为该联系人创建一个名册条目那么该用户的服务器必须注意该入站出席信息订阅请求的存在但是不能(MUST NOT)为该联系人创建一个名册条目(反之, 在把该联系人添加到该用户的名册之前,该服务器必须等到该用户已经批准该订阅请求).
- "None + Pending Out+In" = 联系人和用户未彼此订阅, 联系人已经发送了一个订阅请求给用户但是尚未被应答, 而用户已经发送了一个订阅请求给联系人但是尚未被应答; 这反映在该用户的名册中就是 subscription='none' 且 ask='subscribe'.
- "To" = 用户已经订阅了联系人(单向); 这反映在该用户的名册中就是 subscription='to'.
- "To + Pending In" = 用户已经订阅了联系人, 而联系人已经发送了一个订阅请求给用户但是尚未被应答; 这反映在该用户的名册中就是 subscription='to'.
- "From" = 联系人已经订阅了用户(单向); 这反映在该用户的名册中就是 subscription='from'.
- "From + Pending Out" = 联系人已经订阅了用户, 而用户已经发送了一个订阅请求给联系人但是尚未被应答; 这反映在该用户的名册中就是 subscription='from' 且 ask='subscribe'.
- "Both" = 用户和联系人彼此订阅了(双向); 这反映在该用户的名册中就是 subscription='both'.
A.2. 出站出席信息订阅节的服务器处理
出站出席信息订阅节允许该用户管理他的或她对该联系人的出席信息的订阅(通过"subscribe"和"unsubscribe"类型), 并且管理联系人对该用户的出席信息的访问(通过"subscribed"和"unsubscribed"类型).
以下规则适用于该节的出站路由和该用户的名册的变更. (这些规则是从该用户而不是该联系人的视角描述的. 另外, "S.N." 代表不应该(SHOULD NOT)而"M.N." 代表不能(MUST NOT).)
A.2.1. Subscribe
表2: 出站"subscribe"节的处理
+------------------------------------------------------------------+ | EXISTING STATE | ROUTE? | NEW STATE | +------------------------------------------------------------------+ | "None" | MUST [1] | "None + Pending Out" | | "None + Pending Out" | MUST | no state change | | "None + Pending In" | MUST [1] | "None + Pending Out+In" | | "None + Pending Out+In" | MUST | no state change | | "To" | MUST | no state change | | "To + Pending In" | MUST | no state change | | "From" | MUST [1] | "From + Pending Out" | | "From + Pending Out" | MUST | no state change | | "Both" | MUST | no state change | +------------------------------------------------------------------+
- [1] 一个状态变更到"pending out"包括了在该用户的名册中把'ask'标志的值设为"subscribe".
A.2.2. Unsubscribe
表3: 出站"unsubscribe"节的处理
+-----------------------------------------------------------------+ | EXISTING STATE | ROUTE? | NEW STATE | +-----------------------------------------------------------------+ | "None" | MUST | no state change | | "None + Pending Out" | MUST | "None" | | "None + Pending In" | MUST | no state change | | "None + Pending Out+In" | MUST | "None + Pending In" | | "To" | MUST | "None" | | "To + Pending In" | MUST | "None + Pending In" | | "From" | MUST | no state change | | "From + Pending Out" | MUST | "From" | | "Both" | MUST | "From" | +-----------------------------------------------------------------+
A.2.3. Subscribed
表4: 出站"subscribed"节的处理
+-----------------------------------------------------------------+ | EXISTING STATE | ROUTE? | NEW STATE | +-----------------------------------------------------------------+ | "None" | M.N. | pre-approval [1] | | "None + Pending Out" | M.N. | pre-approval [1] | | "None + Pending In" | MUST | "From" | | "None + Pending Out+In" | MUST | "From + Pending Out" | | "To" | M.N. | pre-approval [1] | | "To + Pending In" | MUST | "Both" | | "From" | M.N. | no state change | | "From + Pending Out" | M.N. | no state change | | "Both" | M.N. | no state change | +-----------------------------------------------------------------+
- [1] 关于订阅预批准的详细信息见3.4.
A.2.4. Unsubscribed
表5: 出站"unsubscribed"节的处理
+-----------------------------------------------------------------+ | EXISTING STATE | ROUTE? | NEW STATE | +-----------------------------------------------------------------+ | "None" | S.N. | no state change [1] | | "None + Pending Out" | S.N. | no state change [1] | | "None + Pending In" | MUST | "None" | | "None + Pending Out+In" | MUST | "None + Pending Out" | | "To" | S.N. | no state change [1] | | "To + Pending In" | MUST | "To" | | "From" | MUST | "None" | | "From + Pending Out" | MUST | "None + Pending Out" | | "Both" | MUST | "To" | +-----------------------------------------------------------------+
- [1] 这一时间可能导致取消一个订阅预批准, 如3.4所述.
A.3. 入站出席信息订阅节的服务器处理
入站出席信息订阅节从该用户请求一个订阅相关的操作(通过"subscribe"类型), 向该用户通知由该联系人做的订阅相关的动作(通过"unsubscribe"类型),或允许该用户管理该联系人对该用户的的出席信息的访问(通过"subscribed"和"unsubscribed"类型).
以下规则适用于该入站节的递送和该用户的名册的变更. (这些用于服务器处理入站出席信息订阅节的规则是从该用户而不是该联系人的视角描述的. 另外, "S.N." 代表不应该(SHOULD NOT).)
A.3.1. Subscribe
表6: 入站"subscribe"节的处理
+------------------------------------------------------------------+
| EXISTING STATE | DELIVER? | NEW STATE |
+------------------------------------------------------------------+
| "None" | MUST [1] | "None + Pending In" |
| "None + Pending Out" | MUST | "None + Pending Out+In" |
| "None + Pending In" | S.N. | no state change |
| "None + Pending Out+In" | S.N. | no state change |
| "To" | MUST | "To + Pending In" |
| "To + Pending In" | S.N. | no state change |
| "From" | S.N. [2] | no state change |
| "From + Pending Out" | S.N. [2] | no state change |
| "Both" | S.N. [2] | no state change |
+------------------------------------------------------------------+
- [1] 如果该用户之前发送了类型为"subscribed"的出席信息,如附录A.2.3 和 3.4所述, 那么服务器可以以"subscribed"自动应答并把状态改为"From"而不是"None + Pending In".
- [2] 服务器应该自动应答"subscribed".
A.3.2. Unsubscribe
当该用户的服务器为该用户接收到一个来自该联系人的类型为"unsubscribe"的出席信息节, 如果从该用户视角来看该节导致一个订阅状态的变更,那么该用户的服务器必需变更该状态, 必须从该联系人递送该出席信息节到该用户, 且代表该用户应该自动应答,发送一个类型为"unsubscribed"的出席信息节给该联系人. 否则该用户的服务器不能(MUST NOT)变更该状态并且(因为没有状态变更)不应该(SHOULD NOT)递送该节. 这些规则被总结在下表中.
表7: 入站"unsubscribe"节的处理
+------------------------------------------------------------------+ | EXISTING STATE | DELIVER? | NEW STATE | +------------------------------------------------------------------+ | "None" | S.N. | no state change | | "None + Pending Out" | S.N. | no state change | | "None + Pending In" | MUST [1] | "None" | | "None + Pending Out+In" | MUST [1] | "None + Pending Out" | | "To" | S.N. | no state change | | "To + Pending In" | MUST [1] | "To" | | "From" | MUST [1] | "None" | | "From + Pending Out" | MUST [1] | "None + Pending Out" | | "Both" | MUST [1] | "To" | +------------------------------------------------------------------+
- [1] 服务器应该自动应答"unsubscribed".
A.3.3. Subscribed
当该用户的服务器为该用户接收到一个来自该联系人的类型为"subscribed"的出席信息节, 如果没有未决的访问该联系人的出席信息的出站请求, 那么它不能(MUST NOT)变更该订阅状态且(因为没有状态变更)不应该(SHOULD NOT)递送该节给该用户. 如果有一个未决的访问该联系人的出席信息的出站请求并且该类型为"subscribed"的入站出席信息节会导致一个订阅状态变更, 那么该用户的服务器必须变更该订阅状态且必须递送该节到该用户. 如果该用户已经订阅了该联系人的出席信息, 该类型为"subscribed"的入站出席信息节不会导致一个订阅状态变更; 所以该用户的服务器不能(MUST NOT)变更该订阅状态且(因为没有状态变更)不应该(SHOULD NOT)递送该节给该用户. 这些规则总结在下表中.
表8: 入站"subscribed"节的处理
+------------------------------------------------------------------+ | EXISTING STATE | DELIVER? | NEW STATE | +------------------------------------------------------------------+ | "None" | S.N. | no state change | | "None + Pending Out" | MUST | "To" | | "None + Pending In" | S.N. | no state change | | "None + Pending Out+In" | MUST | "To + Pending In" | | "To" | S.N. | no state change | | "To + Pending In" | S.N. | no state change | | "From" | S.N. | no state change | | "From + Pending Out" | MUST | "Both" | | "Both" | S.N. | no state change | +------------------------------------------------------------------+
A.3.4. Unsubscribed
当该用户的服务器为该用户接收了一个来自该联系人的类型为"unsubscribed"的出席信息节, 如果没有一个访问该联系人的出席信息的未决出站请求或如果该用户目前已经订阅了该联系人的出席信息, 那么该用户的服务器必须变更该订阅状态且必须递送该节给该用户. 否则, 该用户的服务器不能(MUST NOT)变更该订阅状态且(因为没有状态变更)不应该(SHOULD NOT)递送该节. 这些规则总结在下笔中.
表9: 入站"unsubscribed"节的处理
+------------------------------------------------------------------+ | EXISTING STATE | DELIVER? | NEW STATE | +------------------------------------------------------------------+ | "None" | S.N. | no state change | | "None + Pending Out" | MUST | "None" | | "None + Pending In" | S.N. | no state change | | "None + Pending Out+In" | MUST | "None + Pending In" | | "To" | MUST | "None" | | "To + Pending In" | MUST | "None + Pending In" | | "From" | S.N. | no state change | | "From + Pending Out" | MUST | "From" | | "Both" | MUST | "From" | +------------------------------------------------------------------+
附录B. 限制通讯
IMP‑REQS的2.3.5节和5.4.10节要求一个兼容的即使消息和出席信息技术需要允许一个用户限制来自选定用户们的通讯. 实现这些的协议定义于XEP‑0016和XEP‑0191中.
附录C. vCards
IMP‑REQS的3.1.3节和4.1.4节要求有可能获取其他联系人的带外联系人信息(例如, 电话号码或email地址). 在XMPP社区中通常使用一个RFC 2426 [VCARD]定义的vCard的XML形式来提供这类信息, 但是这超出了本协议的范围(这个协议的文档被包含在XEP‑0054).
附录D. 用于jabber:iq:roster的XML Schema
以下schema正式地定义本文中使用的'jabber:iq:roster'命名空间, 和XML‑SCHEMA一致. 因为XML流和节的确认是可选的, 本schema不是规范的并且只被提供用于描述用. 关于定义核心XMPP命名空间的schemas, 参考XMPP‑CORE.
<?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:attribute name='ver' type='xs:string' use='optional'/> </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='approved' type='xs:boolean' use='optional'/> <xs:attribute name='ask' use='optional'> <xs:simpleType> <xs:restriction base='xs:NMTOKEN'> <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' default='none'> <xs:simpleType> <xs:restriction base='xs:NMTOKEN'> <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>
附录E. 和RFC 3921的不同
在来自实现和应用实践的一致性和延续性以及正式的互操作性测试的基础上, 对RFC3921做了以下大量修改(还有很多编辑方面的修改).
- 会话建立的协议被决定是不必要的并且因而之前在RFC 3921的第3章定义的内容被移除. 无论如何, 为了向后兼容,鼓励服务器实现声明对该特性的支持, 即使会话建立是一个"no-op"(非选项).
- 为了更加无缝地修复位于不同服务器上的名册之间缺少订阅状态同步的问题, 澄清和修改了和出席信息订阅请求,出席信息探测和出席信息通知相关的错误处理.
- 为出席信息探测修改了'from'地址,现在它是纯JID, 而不是全JID.
- 基于实现和部署实践调整和澄清了节递送规则.
- 显式地定义了一个服务器被允许递送一个类型为"normal"或"chat"的消息节给所有资源,如果它有办法允许所有的资源选择这一行为.
- 允许一个服务器使用它自己的机制来确定"最可用的"资源,用来递送消息, 但是提到了来自RFC 3921的机制(基于出席信息优先级)作为可能的机制.
- 添加了可选的名册信息版本以在多个会话之间名册没有变更(或只变更了很少)的时候节省带宽; 有关的协议交互开始是在XEP‑0237中描述的.
- 添加了可选的对预批准出席信息订阅的服务器支持,通过类型为"subscribed"的出席信息节, 包括一个新的'approved'属性,该属性能被设为"true" (用于一个预批准订阅) 或 "false" (缺省值).
- 给<thread/>元素添加了可选的'parent'属性.
- 把关于通讯限制的协议(定义于RFC 3921的第10章)移回到XEP‑0016,那里是它最开始采用的地方.
- 建议在对探测的应答中返回出席信息不可用(离线).
- 澄清发送给全JIDs的出席信息探测的处理.
- 显式地定义了出席信息<priority/>元素的缺省值为零.
- 移除了对"_im"和"_pres"的SRV记录的支持的建议.
附录F. 致谢
本文衍生自自RFC 3921,是对它的更新. 没有贡献者们和评论者们的工作,本文是不可能完成的.
从RFC 3921发布以来,数百人提供了实现反馈, 错误报告, 澄清的请求, 和改善建议. 尽管本文的编辑已经努力找出所有这类反馈, 但他独自对任何其余的错误和含糊的地方负责.
一些关于名册版本的文本来自XEP‑0237,而一些关于消息线索的文本来自XEP‑0201.
特别感谢 Kevin Smith, Matthew Wild, Dave Cridland, Waqas Hussain, Philipp Hancke, Florian Zeitz, Jonas Lindberg, Jehan Pages, Tory Patnoe, 和其他在工作组最后召集期间做评论的人.
也感谢代表安全局进行复审的 Richard Barnes.
工作组主席是 Ben Campbell 和 Joe Hildebrand. 责任区域编辑是 Gonzalo Camarillo.
作者的地址
Peter Saint-Andre Cisco 1899 Wyknoop Street, Suite 600 Denver, CO 80202 USA Phone:+1-303-308-3282 EMail:psaintan@cisco.com