XEP-0060
(→包含SHIM头) |
(→PubSub URIs) |
||
第5,958行: | 第5,958行: | ||
'''例子 225. XMPP URI 用于节点''' | '''例子 225. XMPP URI 用于节点''' | ||
− | <source lang=" | + | <source lang="html4strict"> |
xmpp:pubsub.shakespeare.lit?;node=princely_musings | xmpp:pubsub.shakespeare.lit?;node=princely_musings | ||
− | </source> | + | </source> |
+ | |||
+ | 以下URI识别位于一个节点的一个特定条目. | ||
− | + | '''例子 226. XMPP URI 用于一个pubsub条目''' | |
− | + | ||
− | + | ||
+ | <source lang="html4strict"> | ||
xmpp:pubsub.shakespeare.lit?;node=princely_musings;item=ae890ac52d0df67ed7cfdf51b644e901 | xmpp:pubsub.shakespeare.lit?;node=princely_musings;item=ae890ac52d0df67ed7cfdf51b644e901 | ||
+ | </source> | ||
+ | 以下URI定义如何订阅一个节点(详见本文的[[XEP-0060#URI查询类型|URI查询类型]]). | ||
− | + | '''例子 227. URI 用于订阅一个pubsub节点''' | |
− | + | ||
− | + | ||
+ | <source lang="html4strict"> | ||
xmpp:pubsub.shakespeare.lit?pubsub;action=subscribe;node=princely_musings | xmpp:pubsub.shakespeare.lit?pubsub;action=subscribe;node=princely_musings | ||
+ | </source> | ||
+ | 以下URI定义如何从一个节点获取一个特定的条目. | ||
− | + | '''例子 228. URI 用于获取一个pubsub条目''' | |
− | + | ||
− | + | ||
+ | <source lang="html4strict"> | ||
xmpp:pubsub.shakespeare.lit?pubsub;action=retrieve;node=princely_musings;item=ae890ac52d0df67ed7cfdf51b644e901 | xmpp:pubsub.shakespeare.lit?pubsub;action=retrieve;node=princely_musings;item=ae890ac52d0df67ed7cfdf51b644e901 | ||
+ | </source> |
2013年8月8日 (四) 17:56的版本
本文的英文原文来自XEP-0060
XEP-0060: 发布-订阅
摘要: 本文定义了一个XMPP协议扩展来实现实现通用的 发布-订阅 功能。这个协议使 XMPP实体能在一个pubsub服务创建节点(主题)并发布信息到那些节点上;然后一个事件通知(携带或未带载荷)被广播到所有订阅了该节点的实体. PubSub因此坚持了经典的观察者设计模式,并可以作为广泛应用的服务基础,包括新闻提要,内容整合,富出席信息,地理位置,工作流系统,网络管理系统,以及任何其他需要事件通知的应用。
作者: Peter Millard, Peter Saint-Andre, Ralph Meijer
版权: © 1999 - 2013 XMPP标准化基金会(XSF). 参见法律通告.
状态: 草案
类型: 标准跟踪
版本: 1.13
最后更新日期: 2010-07-12
注意: 这里定义的协议是XMPP标准化基金会的一个草案标准.对本协议的执行是被鼓励的,也适于布署到生产系统,但是在它成为最终标准之前可能还会有一些变动.
绪论
概览
本文定义的XMPP 发布-订阅 扩展提供了一个框架用于广泛的应用, 包括新闻摘要, 内容整合, 扩展的出席信息, 地理位置, 头像管理, 共享的标签, 拍卖和贸易系统, 工作流系统, 网络管理系统, NNTP网关, 资料管理, 以及任何其他需要事件通知的应用.
这个技术使用了经典的 "发布-订阅" 或曰 "观察者" 设计模式: 一个人或应用发布信息, 同时一个事件通知 (包含或不包含有效载荷) 被广播到所有授权的订阅者. 通常, 发布者和订阅者之间的联系是由一个服务来调节的,这个服务接收发布请求,广播事件通知到订阅者, 并允许特许实体能管理被授权发布或订阅的人员或应用的列表. 对于发布和订阅的焦点是一个节点 "node" ,它是发布者发送数据的目的地,也是订阅者接收通知的目的地. 节点也维护一个事件历史并提供其他服务以补充纯的 pubsub 模式.
本文定义一个通用的协议,所有 pubsub 应用都能使用. 兼容的实现不需要实现这里定义的所有特性 (参见 特性汇总.) 其他协议可以定义 发布-订阅 的子集 "subsets" 或范本 "profiles" 用于特定的场合, 但是这些范本超出了本文的范围.
它如何工作
尽管本协议很大,因为它定义了多方面的用例和可能的错误流程, 但其基本的思路是简单的:
- 一个实体发布信息到一个 发布-订阅 服务上的一个节点.
- pubsub服务推送一个事件通知到所有被授权可以得知该发布信息的实体.
可能最流行的类似 发布-订阅 功能的应用是内容整合, 它常见于和博客,新闻网站,以及其他互联网可用的经常更新的信息相关的 RSS 和 Atom (RFC 4287 1) 种子. 设想一个<hamlet@denmark.lit>的博客发布例子. 当 Hamlet 写下一篇新博文, 他的博客软件发布该文到一个位于<pubsub.shakespeare.lit>的pubsub节点:
例子 1. 发布者发布一篇新博文
<iq type='set' from='hamlet@denmark.lit/blogbot' to='pubsub.shakespeare.lit' id='pub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <publish node='princely_musings'> <item> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </publish> </pubsub> </iq>
所以那就是 发布-订阅 pubsub 的发布 "pub" 部分.
现在 pubsub 服务通知所有订阅者新博文:
例子 2. 服务通知订阅者
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> [ ... ENTRY ... ] </item> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='bernardo@denmark.lit' id='bar'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> [ ... ENTRY ... ] </item> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='horatio@denmark.lit' id='baz'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> [ ... ENTRY ... ] </item> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='bard@shakespeare.lit' id='fez'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> [ ... ENTRY ... ] </item> </items> </event> </message>
这里是一个甚至更简单的例子: 一个临时的节点只发送通知而不带有效载荷:
例子 3. 一个临时通知
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='elsinore/doorbell'/> </event> </message>
自然, 涉及的实体为了得到完整的 pubsub 功能可能需要完成其他用例 -- 例如, 发布者可能需要建立一个节点 (见 创建节点) 并且订阅者可能需要为通知报名(见 订阅节点). 这些用例在本文的其余部分有完整的描述. (关于哪些特性是必需的,哪些是推荐的或可选的信息, 参考 特性汇总.)
术语表
以下术语用于本文中涉及的pubsub服务上下文中的元素,对象,或动作.(注意: 本文的一些术语在正文中有更详细的解释.)
- Authorize Access Model 授权访问模式
- 一种节点访问模式,一个实体只能在节点所有者批准了订阅申请之后才能订阅(订阅申请被接受但是只是临时的),并且只有订阅者可以获取项目.
- Address 地址
- Collection Node 集合节点
- 一个节点类型,它包含很多节点 和/或 其他集合,但是不发布条目。集合使得展示节点间更复杂的关系成为可能。集合节点定义于PubSub集合节点 4
- |Entity 实体
- 一个以JID为地址的Jabber实体 (客户端,服务,应用等).
- Event 事件
- 一个节点状态的一次变更
- Instant Node 即时节点
- 一个节点,它的NodeID由pubsub服务自动生成
- Item 条目
- 一个XML片段,它由一个节点发布, 从而生成一个事件
- ItemID 条目ID
- 在一个特定的节点上下文中一个条目的唯一标识符
- Leaf Node 叶子节点
- 一个节点类型,它仅包含已发布的条目. 它不是其他节点的一个容器
- Node 节点
- 一个虚拟的位置,信息可以被发布到那,并且从它那能接收到事件通知 和/或 有效载荷(在其他pubsub系统中,它可能被称为“topic”(主题))
- NodeID 节点ID
- 在一个特定的pubsub服务上下文中一个节点的唯一标识符,节点ID要么由节点创建者提供,要么由pubsub服务生成(如果节点创建者请求一个即时节点)。节点ID可以有语义(例如,在一些系统或在类似PEP的pubsub范本中,节点ID可以是一个 用于相关的载荷的XML命名空间)但是这类含义是可选的.如果一个文档定义了在某个给定的节点ID在XMPP pubsub系统的领域中是独特的, 它必须指定相关载荷的XML命名空间.
- Notification 通知
- 一个发送给订阅者的消息通知他们一个事件
- Outcast 被排斥者
- 一个不被允许订阅或发布到某个节点的实体
- Owner 所有者
- 一个节点的管理者,可能不止一个; 经常(但不是必要的)是节点创建者
- Open Access Model 开放访问模式
- 一种节点访问模式,任何实体可以订阅和获取条目而不需要批准.
- Payload 载荷
- 包含在一个发布请求或一个事件通知的<item/>元素中的XML数据。一个给定的载荷由一个XML命名空间和相关的schema来定义。一个定义某个载荷的格式的文档应该指定该载荷是否只用于具有相同的XML命名空间的节点ID,或是否能被用于任何条目ID。这样一个文档也应该对于被发布载荷的那个节点是否单个节点的最佳配置提出建议.
- Personal Eventing 个人事件
- 发布-订阅的一个简化的子集,用于即时消息和出席信息应用程序场景,每个IM用户的JID是一个虚拟的pubsub服务; 细节请看基于Pubsub的个人事件 5.
- Presence Access Model 出席信息访问模式
- Publisher 发布者
- 一个实体,被允许发布条目到一个节点并且自动订阅到该节点
- Publish-Only 仅发布
- 一个实体,被允许发布条目到一个节点但不被允许接收通知. (当自动化实体需要代表所有者生成通知的时候,这个岗位在没有开放访问模式的上下文中是有用的.)
- Pubsub Service 发布订阅服务
- 一个遵循这里定义的协议的 XMPP 服务器或组件
- Roster Access Model 名册访问模式
- 一种节点访问模式,任何订阅了所有者的出席信息并且在指定的名册组中的实体订阅该节点并从该节点获取条目;这个访问模式主要适用于即时消息系统
- Subscriber 订阅者
- 订阅了某个节点的一个实体
- Whitelist Access Model 白名单访问模式
- 一种节点访问模式,一个实体只有在显式地被所有者允许的情况下才可以订阅和接收条目(从未授权的实体发出的订阅请求会被拒绝).
需求
pubsub服务的需求可能是由终端用户的需要驱动的,也包括其他可能使用这项服务的组件或服务器。首先,一个用Jabber实现的pubsub服务必须提供基本的特性来实现一个纯的 发布-订阅 模式:
- 一个实体必须(MUST)能够发布事件到一个服务,这样所有某个节点的订阅者能接收到事件通知.参见发布条目到节点.
- 一个实体必需能订阅一个节点(或被告知不允许订阅). 见订阅节点.
- 一个实体必须被允许从属于一个节点。被允许的从属关系包括member(成员)、无(none)、outcast(被排斥者)、所有者(owner)、仅发布者(publish-only)、以及发布者(publisher)。实现必须支持无和所有者的从属关系,可以支持成员、被排斥者、发布者、和仅发布者的从属关系. 见从属关系.
- 一个实体必须被允许查询一个pubsub服务(或一个指定的节点)以确定该服务(或节点)实现提供了本文定义的哪些可选的特性。这个查询必须使用服务查询(disco#info)协议. 见查询节点信息.
一些基于Jabber的pubsub服务可能需要使用其他特性,但是这些特性是可选的(OPTIONAL)所以不是强制要求和本协议兼容的。无论如何,如果以下这些特性被实现了,它们必须(MUST)按照此处本协议的规定来保证兼容性。这些特性包括:
- 一个服务可以对一个节点缓存最近一次发布的条目(即使"persistent-items"选项未被设置成true);如果它确实把缺省的 "cache-last-item" 设置成 true,它应该(SHOULD)按照"send_last_published_item"参数的配置发送最近发布的条目(或关于它的通知)给订阅的实体。
- 一个节点所有者应该能指定谁可以订阅该节点.
- 一个节点所有者应该能指定谁可以发布项目到该节点.
- 一个节点可以被配置成在事件通知内递送已发布的有效载荷.
- 一个节点可以被配置成持久化已发布条目到一些持久存储机构.
- 一个节点可以被配置成只持久化有限次数的已发布条目.
- 一个服务可以支持集合.如XEP-0248所述.
- 一个服务或节点可以支持扩展的服务查询信息(meta-data).
准备工作
从属关系
为了管理许可,此处定义的协议使用了一个层次从属关系,类似多用户聊天 7中所采用的.
所有的从属关系必需基于一个纯JID (<localpart@domain.tld> 或 <domain.tld>) 而不是全JID (<localpart@domain.tld/resource> 或 <domain.tld/resource>).
对于 "owner" 和 "none" 的从属关系的支持是必需的。对于所有其他的从属关系的支持是推荐的(RECOMMENDED)。对于由一个实现支持的每个非必需的从属关系, 它应该返回一个服务查询特性 "name-affiliation" , 这里 "name" 是从属关系的名称, 类似 "member", "outcast", 或 "publisher" (见特性总结). 特殊种类的pubsub服务可以强制额外的需求(例如, 要求支持给定的非必需从属关系或所有从属关系).
表 1: 从属关系和他们的权限
从属关系 | 订阅 | 获取条目 | 发布条目 | 删除单个条目 | 清理节点 | 配置节点 | 删除节点 |
---|---|---|---|---|---|---|---|
所有者 | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
发布者 | Yes | Yes | Yes | Yes * | Yes * | No | No |
仅发布 | No | No | Yes | Yes * | No * | No | No |
成员 | Yes | Yes | No | No | No | No | No |
无 | Yes | No | No | No | No | No | No |
被排斥者 | No | No | No | No | No | No | No |
- 注意: 一旦一个发布者已经发布条目到了那个节点, 服务可以允许任何发布者删除/清理任何条目而不只是允许原始的发布者移除它。对于 仅发布 从属关系,这种行为是不推荐的,该 仅发布 实体将仅被允许删除它曾经发布的条目。
实体变更它和节点的从属关系的方法是很明确的。典型来讲,一个从属关系状态的改变需要所有者的动作。从属关系变更和它们的触发动作定义在下表中。
表 2: 从属关系状态图
被排斥者 | -- | 所有者移除禁令 | 所有者添加实体到成员列表 | 所有者添加实体到发布者列表 | 所有者添加实体到所有者列表 |
无 | 所有者禁止实体 | -- | 所有者添加实体到成员列表 | 所有者添加实体到发布者列表 | 所有者添加实体到所有者列表 |
成员 | 所有者禁止实体 | 所有者从成员列表中移除实体 | -- | 所有者添加实体到发布者列表 | 所有者添加实体到所有者列表 |
发布者 | 所有者禁止实体 | 所有者从发布者列表中移除实体 | n/a | -- | 所有者添加实体到所有者列表 |
所有者 | n/a | 所有者辞职 | n/a | n/a | -- |
订阅状态
订阅某个节点可以有很多状态
表 3: 订阅状态
订阅状态 | 描述 |
---|---|
无 | 节点不能(MUST NOT)发送事件通知或有效载荷给实体. |
未决的 | 一个实体已经申请订阅一个节点,但是这个申请还没被节点所有者批准。处于这个状态时节点不能(MUST NOT)发送事件通知或有效载荷给实体。 |
未配置的 | 一个实体已经订阅但是它的订阅选项还没有配置。在这个状态时节点可以发送事件通知或有效载荷给实体。 服务可以(MAY)让未配置的订阅超时(作废)。 |
已订阅的 | 一个实体已经订阅了一个节点。在这个状态下节点必须发送所有事件通知(和有效载荷,如果配置了的话)给实体(按照订阅者配置和内容过滤)。 |
事件类型
发布-订阅协议需要两个主要的尺度来使我们能够衡量事件:持久性和临时性,纯的通知和包含有效载荷的通知。实现应该让所有者能够从两个尺度都可以配置一个节点。
无论一个节点是否配置用于持久性或者临时性的事件,一个服务可以缓存最后发布到节点的条目,在这种情况下,它应该根据"send_last_published_item"选项(参见本文的 条目缓存)的配置发送那个条目给订阅者; 如果该服务支持 "http://jabber.org/protocol/pubsub#last-published" 特性,那么这个选项的值的缺省值必须是 "on_sub_and_presence" (尽管该服务应该允许该节点所有者覆盖该缺省值).
注意: "on_sub_and_presence" 设置和订阅者的出席信息相关, 而不是和发布者的出席信息相关.
一个pubsub服务必须确认发布请求在这两个方面都满足节点的配置。(参见本文的 发布条目到节点 章节了解相关的出错条件)。
节点配置和期望的事件类型决定了是否一个条目必须由发布者提供,该条目是否在发布请求或事件通知中包含一个载荷,以及一个条目的I是由发布者提供还是由pubsub服务生成。我们在下表中总结相关的规则:
表 4: 条目,载荷,以及条目 ID
持久性节点 ** | 发布者必须包含一个 <item/> 元素,它可以是空的或者包含一个载荷;即使发布请求包含一个载荷,pubsub服务也不能(MUST NOT)在事件通知中包含载荷;如果发布请求不包含条目ID,pubsub服务必须生成条目ID | 发布请求必须包含一个 <item/> 元素,其中应该包含一个载荷;如果发布请求包含一个载荷,事件通知必需包含该载荷;如果发布请求不包含条目ID,pubsub服务必须生成条目ID |
临时性节点 ** | 发布请求不能(MUST NOT)包含一个 <item/> 元素;载荷不被包含在发布请求或事件通知中,尽管事件通知必须包含一个空的<items/>元素;条目ID既不会由发布请求提供也不会由pubsub服务生成 | 发布请求必须包含一个 <item/> 元素,其中包含一个载荷;如果发布请求包含一个载荷,事件通知必需包含该载荷;pubsub服务可以生成一个条目ID |
- 注意: 节点通知是仅通知的还是包含载荷, 由 "pubsub#deliver_payloads" 配置项来决定.
- 注意: 该节点是持久的还是临时的,由 "pubsub#persist_items" 配置项来决定.
节点类型
有两种节点类型:
表 5: 节点类型
节点类型 | 描述 |
---|---|
叶节点 | 一个仅包含发布的条目的节点。它不包含任何其他节点。这是最常见的节点类型。 |
集合节点 | 一个包含节点和/或其他集合但是不包含发布项目的节点。集合使得节点之间更复杂的关系成为可能。详见 XEP-0248. |
节点访问模式
为了使节点创建对客户端更加简单,我们定义了以下节点访问模式(为了公开性):
表 6: 节点访问模式
访问模式 | 描述 |
---|---|
开放的 | 任何实体可以订阅这个节点(即, 订阅不需要批准) ,并且任何实体可以从这个节点获取条目(即,不需要被订阅);这应该是一般pubsub服务的缺省访问模式。 |
出席信息 | 任何拥有"from"或"both"类型的订阅的实体可以向节点订阅并获取条目;这个访问模式主要应用于即时消息系统 (参见 RFC 3921 ). |
名册 | 任何处于指定名册组中的实体可以向节点订阅和获取条目;这种访问模式主要用于即时消息系统 (参见 RFC 3921 ). |
授权的 | 节点所有者必须批准所有订阅请求,并且只有订阅者可以从节点获取条目. |
白名单 | 一个实体仅仅在被节点所有者加入白名单的时候才可以订阅和获取条目。节点所有者必须自动进入白名单。为了添加实体到白名单,节点所有者应该y使用本文的 管理从属关系 中定义的协议,特别是把从属关系设为"member". |
一个通用的发布-订阅实现应该支持所有已定义的访问模式, 尽管专门的 发布-订阅 实现可以只支持访问模式的一个子集. 在一个特定的部署中提供哪个访问模式是一个服务供应的问题(比如,一些受限的部署可能希望锁定许可从而只提供“授权的”和"白名单"的访问模式,或甚至只提供"白名单"的访问模式).
一个节点创建者或所有者可以覆盖缺省的访问模式,通过给'pubsub#access_model'配置项指定一个适当的值(参见本文的 以缺省值创建节点 和 配置节点).
寻址
如果一个 pubsub 节点是可设定地址的,它必须(MUST)被设定为一个JID或一个JID和一个节点的组合。7
JID
如果一个 pubsub 节点的地址被设定成一个JID,节点ID必须(MUST)是资源标识符,并且节点ID不能(MUST NOT)是JID中的"user"(节点标识符)部分(如"domain.tld/NodeID" 和 "user@domain.tld/NodeID" 是被允许的; "NodeID@domain.tld" 是不允许的9)。JID 寻址应该在使用一个不支持节点属性的协议和一个 pubsub 节点交互的时候使用. 例如,当一个服务允许实体通过出席信息订阅节点, 它会把节点地址设成JID。如果一个 pubsub 节点可以被设置成 JID ,pubsub 服务必须确保节点ID符合 RFC 3920 所述的Stringprep中的Resourceprep范本。
考虑以下例子,pubsub服务定位于主机 pubsub.shakespeare.lit.
例子 4. 节点地址设定为 domain.tld/NodeID
<iq to='pubsub.shakespeare.lit/news announcements'> ... </iq>
现在再考虑以下例子, pubsub 服务定位于 pubsub@shakespeare.lit.
例子 5. 节点地址设定为 user@domain.tld/NodeID
<iq to='pubsub@shakespeare.lit/news announcements'> ... </iq>
JID+NodeID
如果一个pubsub节点地址可以被设成一个JID加节点,这个节点ID必须既是服务查询的'node'属性又是pubsub服务的'node'属性;即,用于查询时,一个pubsub节点等同于一个服务查询节点。如果一个pubsub节点可以被设置成 JID 加 node,这个pubsub服务应该节点ID符合 RFC 3920 所述的Stringprep中的Resourceprep范本。
考虑以下例子,(虚拟的)pubsub服务位于 hamlet@denmark.lit.
例子 6. 节点地址设为 JID+NodeID
<iq to='hamlet@denmark.lit'> <query node='princely_musings'/> </iq>
实体用例
本章定义了使用案例,用于任何希望和一个发布-订阅服务交互的实体使用的任何协议,主要集中于服务查询的用例。
查询特性
一个服务必须回应由'http://jabber.org/protocol/disco#info'命名空间限定的服务查询信息请求。由一个pubsub服务返回的"disco#info"结果必须表明服务的标识符以及支持哪些pubsub特性。
案例子 7. 实体查询 Pubsub 服务支持的特性
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='feature1'> <query xmlns='http://jabber.org/protocol/disco#info'/> </iq>
例子 8. Pubsub 服务返回支持的特性组
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='feature1'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='pubsub' type='service'/> <feature var='http://jabber.org/protocol/pubsub'/> </query> </iq>
可能的 pubsub 特性在本文中到处都是,并且它们都已经被注册了,如本文的 XMPP注册处事项 所述。想了解哪个特性是必需的,建议的,可选的,参见本文的 特性总结 。
查询节点
如果一个服务实现了一个多层次节点(也就是 集合节点),它必须(MUST)也要让实体能够通过 服务查询 协议在这些层级中查询节点,在结果集很大的时候遵照 XEP-0030 的建议(这时候应该使用Jabber search] 10 或一些其他协议).以下例子展示在一个多层次 pubsub 服务中如何使用 服务查询 来查询可用的节点.
注意: 节点层次和集合节点是可选的. 详细情况参照本文的 节点ID语义 和 集合节点.
在第一个例子中,一个实体向一个根节点(即服务本身)发送一个服务查询条目("disco#items")请求,它是一个集合节点:
例子 9. 实体请求所有一级节点
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='nodes1'> <query xmlns='http://jabber.org/protocol/disco#items'/> </iq>
例子 10. 服务返回所有一级节点
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='nodes1'> <query xmlns='http://jabber.org/protocol/disco#items'> <item jid='pubsub.shakespeare.lit' node='blogs' name='Weblog updates'/> <item jid='pubsub.shakespeare.lit' node='news' name='News and announcements'/> </query> </iq>
第二个例子中,一个实体发送一个 disco#items 请求给其中一个一级节点,它也是一个集合节点:
'例子 11. 实体请求二级节点
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='nodes2'> <query xmlns='http://jabber.org/protocol/disco#items' node='blogs'/> </iq>
例子 12. 服务返回二级节点
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='nodes2'> <query xmlns='http://jabber.org/protocol/disco#items' node='blogs'> <item jid='pubsub.shakespeare.lit' node='princely_musings'/> <item jid='pubsub.shakespeare.lit' node='kingly_ravings'/> <item jid='pubsub.shakespeare.lit' node='starcrossed_stories'/> <item jid='pubsub.shakespeare.lit' node='moorish_meanderings'/> </query> </iq>
如果一个节点是叶子节点而不是一个集合节点并且有条目被发布到该节点,这个服务可以为每一个已发布的条目返回一个 <item/> 元素,如本文的查询节点条目所述,无论如何这类条目不能(MUST NOT)包含一个'node'属性(因为它们是已发布的条目,不是节点).
查询节点节点信息
一个 pubsub 服务必须允许实体去查询每个单独的节点来获得该节点的相关信息。必须(MUST)使用服务查询协议来查询这些信息。"disco#info" 结果必须包含一个标识符,其类别是“pubsub”,类型是“leaf”或“collection”。
注意: 如果一个节点的标识符的类型为“leaf”,它不能(MUST NOT)包含其它的节点或集合(只有条目);如果一个节点的ID类型是“collection”,它不能(MUST NOT)包含条目(只有其它节点或集合)。
'例子 13. 实体查询集合节点的信息
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='info2'> <query xmlns='http://jabber.org/protocol/disco#info' node='blogs'/> </iq>
例子 14. 服务应答pubsub/collection标识符
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='meta1'> <query xmlns='http://jabber.org/protocol/disco#info' node='blogs'> <identity category='pubsub' type='collection'/> </query> </iq>
例子 15. 实体查询叶子节点信息
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='info1'> <query xmlns='http://jabber.org/protocol/disco#info' node='princely_musings'/> </iq>
例子 16. 服务应答 pubsub/leaf 标识符
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='info1'> <query xmlns='http://jabber.org/protocol/disco#info' node='princely_musings'> ... <identity category='pubsub' type='leaf'/> ... </query> </iq>
查询节点元数据
"disco#info" 结果可以包含关于节点的详细元数据,封装在服务发现扩展|服务发现扩展 12 定义的数据表格 11 格式中,这里的数据表格上下文通过包含一个 "http://jabber.org/protocol/pubsub#meta-data" 中定义的 FORM_TYPE 来指定的,并遵循 数据表格的字段标准化 13。如果元数据被提供了,它应该包含所有已配置的选项值,像"automatic" 信息一样,比如节点创建日期,发布者列表以及类似的信息。
例子 17. 实体查询节点的信息
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='meta1'> <query xmlns='http://jabber.org/protocol/disco#info' node='princely_musings'/> </iq>
例子 18. 服务应答信息和元数据
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='meta1'> <query xmlns='http://jabber.org/protocol/disco#info' node='princely_musings'> <identity category='pubsub' type='leaf'/> <feature var='http://jabber.org/protocol/pubsub'/> <x xmlns='jabber:x:data' type='result'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#meta-data</value> </field> <field var='pubsub#type' label='Payload type' type='text-single'> <value>http://www.w3.org/2005/Atom</value> </field> <field var='pubsub#creator' label='Node creator' type='jid-single'> <value>hamlet@denmark.lit</value> </field> <field var='pubsub#creation_date' label='Creation date' type='text-single'> <value>2003-07-29T22:56Z</value> </field> <field var='pubsub#title' label='A short name for the node' type='text-single'> <value>Princely Musings (Atom)</value> </field> <field var='pubsub#description' label='A description of the node' type='text-single'> <value>Updates for Hamlet's Princely Musings weblog.</value> </field> <field var='pubsub#language' label='Default language' type='list-single'> <value>en</value> </field> <field var='pubsub#contact' label='People to contact with questions' type='jid-multi'> <value>bard@shakespeare.lit</value> </field> <field var='pubsub#owner' label='Node owners' type='jid-multi'> <value>hamlet@denmark.lit</value> </field> <field var='pubsub#publisher' label='Publishers to this node' type='jid-multi'> <value>hamlet@denmark.lit</value> </field> <field var='pubsub#num_subscribers' label='Number of subscribers to this node' type='text-single'> <value>1066</value> </field> </x> </query> </iq>
注意: 节点元数据可以用多种方法设置。一些是基于节点配置(如所有者的JID),也有的是动态的(如订阅者的编号)。任何在节点元数据中提供的静态信息应该(SHOULD)在节点配置表单中以字段形式提供。
注意: pubsub#language 字段应该是单列的,这样pubsub服务能展示一个适当的语言和语言代码列表.
查询节点条目
为了查询服务中特定节点的已发布条目,一个实体可以发送"disco#items"请求给节点本身,服务可以通过服务查询的<item/>元素返回每个条目。每个服务查询条目的'name'属性必须包含它的ItemID,并且该条目不能(MUST NOT)有'node'属性。这个ItemID可以用于获取条目,使用本文中从节点获取条目定义的协议.
'例子 19. 实体请求一个节点的所有条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items1'> <query xmlns='http://jabber.org/protocol/disco#items' node='princely_musings'/> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <query xmlns='http://jabber.org/protocol/disco#items' node='princely_musings'> <item jid='pubsub.shakespeare.lit' name='368866411b877c30064a5f62b917cffe'/> <item jid='pubsub.shakespeare.lit' name='3300659945416e274474e469a1f0154c'/> <item jid='pubsub.shakespeare.lit' name='4e30f35051b7b8b42abe083742187228'/> <item jid='pubsub.shakespeare.lit' name='ae890ac52d0df67ed7cfdf51b644e901'/> </query> </iq>
找回订阅
一个实体可能想查询服务以找回它对服务中所有节点的订阅。推荐支持这个特性("retrieve-subscriptions").
为了做出这些查询,请求的实体必须发送一个 IQ-get 节,这个节的 <pubsub/> 子元素包含一个没有属性的空<subscriptions/>元素。
例子 20. 实体请求所有当前的订阅
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='subscriptions1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions/> </pubsub> </iq>
如果该服务返回一个订阅列表,它必须(MUST)返回所有和请求中'from'属性中的纯JID(<localpart@domain.tld> or <domain.tld>)部分匹配的JIDs的订阅.
对于每个订阅, 返回一个 <subscription/> 元素以指明 NodeID, 以及这个节点ID所从属的JID(可以包含一个资源,取决于实体如何订阅的),以及当前的订阅状态。如果服务支持订阅标识符,'subid' 属性也必须出现.
例子 21. 服务返回所有当前订阅
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='subscriptions1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions> <subscription node='node1' jid='francisco@denmark.lit' subscription='subscribed'/> <subscription node='node2' jid='francisco@denmark.lit' subscription='subscribed'/> <subscription node='node5' jid='francisco@denmark.lit' subscription='unconfigured'/> <subscription node='node6' jid='francisco@denmark.lit' subscription='subscribed' subid='123-abc'/> <subscription node='node6' jid='francisco@denmark.lit' subscription='subscribed' subid='004-yyy'/> </subscriptions> </pubsub> </iq>
如果请求实体没有订阅,pubsub 服务必须返回一个空的 <subscriptions/> 元素.
例子 22. 没有订阅
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='subscriptions1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions/> </pubsub> </iq>
如果服务不支持订阅找回,该服务必须应答一个<feature-not-implemented/> 错误, 指定一个pubsub特有的错误条件 <unsupported/> 以及特性 "retrieve-subscriptions".
例子 23. 不支持订阅找回
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='subscriptions1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-subscriptions'/> </error> </iq>
一个实体也可以在一个特定节点请求它的所有订阅(例如, 如果它的订阅有多个SubIDs(子ID)),通过在<subscriptions/>元素包含一个'node'属性.
例子 24. 实体从一个特定节点请求当前所有订阅
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='subscriptions2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions node='princely_musings'/> </pubsub> </iq>
服务以后将只返回该实体的订阅到那个特定的节点; 这就像在该实体的订阅上做了个过滤器.
例子 25. 服务返回所有当前订阅到一个特定节点
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='subscriptions2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions node='node6'> <subscription node='node6' jid='francisco@denmark.lit' subscription='subscribed' subid='123-abc'/> <subscription node='node6' jid='francisco@denmark.lit' subscription='subscribed' subid='004-yyy'/> </subscriptions> </pubsub> </iq>
找回从属关系
一个实体可能希望查询服务以找回它在该服务上的所有节点的从属关系,或查询一个特定的节点以找回在那个节点的从属关系。推荐支持这个特性("retrieve-affiliations").
为了向服务做出这些请求,请求实体包含一个没有属性的空 <affiliations/> 元素。
例子 26. 实体请求所有当前从属关系
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='affil1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations/> </pubsub> </iq>
如果服务返回一个从属关系列表,它必须返回所有和该请求的'from'属性的纯JID(<localpart@domain.tld> or <domain.tld>)部分相匹配的JID的从属关系。
对于每个从属关系, 返回一个 <affiliation/> 元素,包含 NodeID 和从属关系状态(所有者, 发布者, 仅发布,成员,或 被排斥者).
例子 27. 服务应答所有当前从属关系
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='affil1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations> <affiliation node='node1' affiliation='owner'/> <affiliation node='node2' affiliation='publisher'/> <affiliation node='node5' affiliation='outcast'/> <affiliation node='node6' affiliation='owner'/> </affiliations> </pubsub> </iq>
如果请求实体没有从属关系, pubsub服务必须返回一个空的 <affiliations/> 元素.
例子 28. 没有从属关系
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='affil1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations/> </pubsub> </iq>
如果该服务不支持从属关系找回, 服务必须应答一个 <feature-not-implemented/> 错误, 指明一个pubsub特有的错误条件 <unsupported/> 和 一个 "retrieve-affiliations" 的特性.
例子 29. 不支持从属关系找回
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='affil1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-affiliations'/> </error> </iq>
为了执行一个对特定节点的从属关系请求, 请求实体包含一个带'node'属性的空<affiliations/>元素.
例子 30. 实体请求特定节点的从属关系
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='affil2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations node='node6'/> </pubsub> </iq>
例子 31. 服务返回当前从属关系
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='affil2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations> <affiliation node='node6' affiliation='owner'/> </affiliations> </pubsub> </iq>
订阅者用例
本节定义了潜在和实际的订阅者使用协议的用例。(注意:本文的实现备注描述了一个pubsub服务必须遵守的许多重要的因素和商业规则.另外,所有例子都假定独立的pubsub组件存在并包含任何服务器或网络标记的相关'from'地址).
订阅节点
请求
当一个Jabber实体希望订阅一个节点,它向pubsub服务发送订阅请求。订阅请求是一个 IQ-set 消息,其<pubsub/> 元素包含并且仅包含一个<subscribe/>元素.这个<subscribe/>元素应该拥有一个'node' 属性指明实体希望订阅的节点.这个<subscribe/>元素也必须拥有一个'jid' 属性指明用于订阅的JID确切的XMPP地址--通常是一个纯JID(<localpart@domain.tld> 或 <domain.tld>) 或全JID(<localpart@domain.tld/resource> or <domain.tld/resource>).
这里是一个订阅请求的例子.
例子 32. 实体订阅节点
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscribe node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq>
成功场景
如果订阅请求被成功地处理, 该服务器必须通知该请求实体它已经被订阅了(它可以包含一个服务器生成的SubID).
例子 33. 服务返回成功
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3' subscription='subscribed'/> </pubsub> </iq>
错误场景
有很多原因可能导致订阅请求失败:
- JIDs的纯JID部分不匹配.
- 节点有一个"presence"访问模式并且请求实体没有订阅所有者的出席信息。
- 节点有一个"roster"的访问模式并且请求实体不在授权名册组中。
- 节点有一个"whitelist"的访问模式并且请求实体不在白名单中。
- 订阅节点的时候服务需要付费。
- 请求实体是匿名的并且服务不允许匿名实体订阅。
- 请求实体有一个未决的订阅。
- 请求实体被禁止订阅(例如,因为从属关系是被排斥者)。
- 请求实体尝试建立太多订阅.
- 节点不支持订阅。
- 节点已经被移走了。
- 节点不存在。
更完整的错误案例在以下章节.
JIDs不匹配
如果指定的JID是一个纯JID或全JID, 该服务必需最低程度检查纯JID部分和接收到的IQ请求的'from'属性的纯JID部分以确保请求实体和正在请求加入订阅者列表的JID是同一个实体.
如果JIDs的纯JID部分不符合以上描述并且请求实体没有被实现定义某种管理或代理特权, 该服务必须返回一个<bad-request/>错误, 它应该也包含一个pubsub特有的错误条件<invalid-jid/>.
例子 34. JIDs不匹配
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
注意: 实现可以允许该服务的管理员配置一个实体列表免于这种检查; 那些实体可以被认为是"可信的代理",被允许代表其他实体订阅. 同样的办法, 实现可以允许实体黑名单,不被允许执行特定的操作(类似订阅或创建节点).
要求订阅出席信息
对于拥有"presence"访问模式的节点,如果请求实体没有订阅所有者的出席信息,那么pubsub服务必须返回一个 <not-authorized/> 错误,它也应该包含一个pubsub特有的<presence-subscription-required/>错误条件。
例子 35. 实体没有被授权创建一个订阅(需要出席信息订阅)
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <presence-subscription-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不在名册组中
对于拥有"roster"访问模式的节点,如果请求实体不在授权的名册组中,pubsub服务必须返回一个 <not-authorized/> 错误,它也应该也包含一个 pubsub特有的<not-in-roster-group/>错误条件。
例子 36. 实体没有被授权创建一个订阅(不在名册组中)
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-in-roster-group xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不在白名单中
对于拥有"白名单"节点访问模式的节点,如果请求实体不在白名单中,pubsub服务必须返回一个 <not-allowed/> 错误,指定一个pubsub特有的<closed-node/>错误条件。
例子 37. 节点有白名单访问模式
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='cancel'> <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <closed-node xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
要求付款
商业部署可能希望把订阅者连接到一个付费客户数据库。如果订阅者需要付费才能订阅该节点(例如,如果订阅者不在客户数据库中或客户还未付帐),该服务应该返回一个 <payment-required/> 错误给订阅者。
例子 38. 订阅需要付费
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <payment-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
不允许匿名订阅
一些 XMPP 服务器可能允许使用SASL ANONYMOUS验证;无论如何,因为这会导致实体不稳定(分配的JID可能不是同一个负责人持续拥有的),一个服务可以阻止匿名实体订阅一个节点并且应该使用服务查询来决定是否有一个实体的标识符是 "account/anonymous".如果一个请求实体是匿名的但是服务器不允许匿名实体订阅,该服务应该返回一个< forbidden/>错误给订阅者.
例子 39. 请求实体是匿名用户
<iq type='error' from='pubsub.shakespeare.lit' to='anonymous@denmark.lit/foo' id='sub1'> <error type='cancel'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
订阅未决
如果请求实体有一个未决的订阅, 服务必须返回一个<not-authorized/>错误给订阅者,表明一个发生了pubsub特有的<pending-subscription/>错误条件.
例子 40. 请求实体有一个未决的订阅
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <pending-subscription xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
被屏蔽
如果请求实体被屏蔽订阅(例如, 因为它有一个被排斥者的从属关系), 服务器必须返回一个<forbidden/>错误给订阅者.
例子 41. 请求实体被屏蔽
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
太多订阅
如果请求实体尝试建立太多订阅("太多"的定义是本地服务策略的问题), 该服务必须返回一个<policy-violation/>错误给订阅者, 指定一个pubsub特有的错误条件<too-many-subscriptions/>.
例子 42. 请求实体超出了订阅数量限制
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='wait'> <policy-violation xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <too-many-subscriptions xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
该服务可能匹配订阅实体的纯JID或全JID来确定是否一个实体请求了太多订阅.
不支持订阅
如果节点不允许实体订阅, 服务应该返回一个<feature-not-implemented/>错误给订阅者,指定一个pubsub特有的<unsupported/>错误条件和一个"subscribe"特性.
例子 43. 订阅不支持
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='subscribe'/> </error> </iq>
节点被移走了
如果该节点被移走了, 该服务应该返回一个<gone/>错误(如果该节点被永久性的移走了)或一个<redirect/>错误(如果该节点被临时移走了).
例子 44. 节点被移走了
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='modify'> <gone xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'> xmpp:pubsub.shakespeare.lit?;node=some-other-node </gone> </error> </iq>
节点不存在
如果节点不存在, 服务应该返回一个<item-not-found/>错误给订阅者.
例子 45. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
要求批准
对于拥有"authorize"访问模式的节点, 订阅请求必须由节点所有者批准, 除非服务策略允许非"none"实体自动订阅(例如,成员和发布者可能被允许自动订阅); 所以pubsub服务发送一个消息给节点所有者请求授权(参见本文的 管理订阅请求). 因为订阅请求可能被批准也可能不被批准, 服务必须返回一个未决通知给订阅者.
例子 46. 服务应答未决
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='pending'/> </pubsub> </iq>
要求配置
如果实体在接收事件通知之前必须配置它的订阅选项(见本文 配置订阅选项),服务必须通知实体这件事. 它应该返回一个IQ-result给请求实体一个记号表示需要订阅配置。
例子 47. 服务应答成功并指示要求订阅配置
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='unconfigured'> <subscribe-options> <required/> </subscribe-options> </subscription> </pubsub> </iq>
注意: 只有在订阅者必须在收到任何事件通知之前配置订阅,节点才将包含这个<required/>子元素. 如果配置是必需的而配置请求没有在合理的时间内提交,一个服务可以判定订阅请求超时(取决于服务或节点的配置).
替代的方案是, 如果没有同步做配置就不能创建这个订阅, 服务可以返回一个<not-acceptable/> 错误, 指定一个pubsub特有的<configuration-required/>错误条件.
例子 48. 服务返回错误指出要求订阅配置
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscribe node='princely_musings' jid='francisco@denmark.lit'/> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </options> </pubsub> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <configuration-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
如果没有包含<required/>元素也没有错误返回, 订阅立刻生效并且实体可以在任何时间配置这个订阅(服务可以通过在IQ-result中包含一个空的<subscribe- options/>元素指出支持订阅选项, 如下案例所示).
例子 49. 服务应答成功并指示订阅选项被支持但不是必需的
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='unconfigured'> <subscribe-options/> </subscription> </pubsub> </iq>
多重订阅
一个实体可能希望使用不同的订阅选项来订阅, 这样它能多次订阅同一个节点. 对这个特性("multi-subscribe")的支持是可选的.
如果允许相同的JID的多重订阅, 该服务必需使用'subid'属性来区分相同实体的多个订阅(所以对于每个node+JID组合来说SubID必须是唯一的并且SubID被发送给订阅者的任何时候都是存在的). 不推荐客户端生成SubIDs, 因为可能导致冲突; 所以一个服务应该代表该订阅者生成SubID并且如果订阅者已经提供了SubID服务可以覆盖它. 如果该服务不允许相同实体多重订阅并且它接收到了额外的订阅请求, 该服务必须返回当前的订阅状态(好像订阅刚被批准).
当pubsub服务生成事件通知, 它应该只发送一个事件通知给一个有多重订阅的实体, 而不是为每个订阅发送一个事件通知. 这里"实体"意味着指定订阅的JID, 要么是纯JID要么是全JID; 无论如何, 如果相同的纯JID有多个订阅但是那些订阅的全JIDs不同(例如, 一个订阅是用于 user@domain.tld./foo 而另一个订阅是用于 user@domain.tld/bar), 该服务必须把它们视为不同的 JIDs 用来生成事件通知.
获取最近发布的条目
当一个订阅请求被成功处理, 服务可以发送最近发布的条目给新订阅者. 包含这个条目的消息应该被标为由'urn:xmpp:delay'命名空间限定的扩展信息(见延迟递送 14) 以表明它是被延迟递送的. (注意在本例子中该事件通知被发送到纯JID,因为它就是订阅JID.)
例子 50. 服务发送最近发布的条目
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </event> <delay xmlns='urn:xmpp:delay' stamp='2003-12-13T23:58:37Z'/> </message>
如果该服务缺省的配置是对所有节点都发送最近发布的条目(根据节点配置覆盖的), 它必须在它对 disco#info 请求的应答中返回一个特性 "http://jabber.org/protocol/pubsub#last-published" .
从节点取消订阅
请求
为了从一个节点取消订阅, 订阅者发送一个 IQ-set, 它的 <pubsub/> 子元素包含一个<unsubscribe/>元素,指明节点和已订阅的 JID.
例子 51. 实体从一个节点取消订阅
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='unsub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <unsubscribe node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq>
成功场景
如果请求可以被成功处理,服务必须返回一个 IQ result.
例子 52. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'/>
错误场景
有很多原因可能导致取消订阅失败:
- 请求实体对该节点有多个订阅但是没有指定一个订阅ID.
- 请求未指定一个已存在的订阅者
- 请求实体没有足够的权限取消指定JID的订阅.
- 节点不存在.
- 请求指定的订阅ID不合法或不是当前的.
错误场景的更完整的描述如下.
无订阅ID
如果请求实体有多个订阅指向该节点但是没有指定一个订阅ID, 服务必须返回一个<bad-request/>错误, 它也应该也包含一个 <subid-required/>的pubsub特有的错误条件.
例子 53. 实体未指定SubID
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <subid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
无该订阅者
如果'jid'属性的值未指定一个已存在的订阅者, pubsub服务必须返回一个错误节, 这个节应该是<unexpected-request/>并且也应该包含一个 <not-subscribed/>的pubsub特有的错误条件.
例子 54. 请求实体不是一个订阅者
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='cancel'> <unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
权限不足
如果请求实体被禁止取消指定JID的订阅, 服务必须返回一个<forbidden/>错误. 服务必须检查这个做出请求的实体是否被授权可以取消订阅. 如果订阅者的JID格式是<localpart@domain.tld/resource> 或 <domain.tld/resource>, 服务必须通过比较两个JID的<localpart@domain.tld> 或 <domain.tld> 部分来执行这个检查以确保它们是匹配的. 如果这些JID的纯JID部分不匹配并且请求实体没有被授权取消这个JID的订阅(例如, 因为它不是一个服务管理员或被授权的代理), 服务必须返回一个<forbidden/>错误.
例子 55. 请求实体被禁止取消订阅实体
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
节点不存在
如果节点不存在, pubsub服务必须返回一个 <item-not-found/> 错误.
例子 56. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
坏的订阅ID
如果一个订阅标识符关联于某个订阅项, 取消订阅请求必须包含一个适当的'subid'属性. 如果这个取消订阅请求包含一个 SubID 但是该节点不支持 SubIDs (或该订阅者第一次并没有使用 SubID 来订阅), 服务应该忽略这个 SubID 并简单地取消订阅这个实体. 如果订阅者以前使用一个 SubID 来订阅但是取消订阅申请包含一个不合法或非当前订阅者的 SubID , 服务必须返回一个<not-acceptable/>错误, 它应该也包含一个<invalid-subid/>的pubsub特有的错误条件.
例子 57. 非法的订阅标识符
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
配置订阅选项
实现可以允许订阅者们配置订阅选项. 实现应该使用 数据表单 (Data Forms)协议来实现这个配置(无论如何, 一个带外机制如web界面也可能被提供).
声明支持
如果一个服务支持订阅选项, 它必须在它对"disco#info"查询的应答中声明(在应答中包含一个feature,其'var'属性为"pubsub#subscription-options").
例子 58. Pubsub服务指示支持订阅选项
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='feature1'> <query xmlns='http://jabber.org/protocol/disco#info'> ... <feature var='http://jabber.org/protocol/pubsub#subscription-options'/> ... </query> </iq>
请求
一个订阅者通过在IQ-get节中包含一个<options/>元素来请求订阅选项.
例子 59. 订阅者请求订阅选项表单
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='options1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq>
成功场景
如果请求被成功的处理, 服务必须应答选项.
例子 60. 服务应答选项表单
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='form'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver' type='boolean' label='Enable delivery?'> <value>1</value> </field> <field var='pubsub#digest' type='boolean' label='Receive digest notifications (approx. one per day)?'> <value>0</value> </field> <field var='pubsub#include_body' type='boolean' label='Receive message body in addition to payload?'> <value>false</value> </field> <field var='pubsub#show-values' type='list-multi' label='Select the presence types which are allowed to receive event notifications'> <option label='Want to Chat'><value>chat</value></option> <option label='Available'><value>online</value></option> <option label='Away'><value>away</value></option> <option label='Extended Away'><value>xa</value></option> <option label='Do Not Disturb'><value>dnd</value></option> <value>chat</value> <value>online</value> </field> </x> </options> </pubsub> </iq>
注意: 前述的例子展示了一些(但不是所有)的可以(MAY)被提供的可能的配置选项. 如果一个实现使用 数据表单 (Data Forms)协议提供了这些选项, 它必须使用那些在和 'http://jabber.org/protocol/pubsub' 命名空间相关的XMPP注册处中注册了的字段(以上初步展示了那些字段, 并且在本文的 pubsub#subscribe_options FORM_TYPE 描述了它们, 但是不能(MUST NOT)被当作规范, 因为 XMPP注册处以后还可以在不改变本文的情况下标准化更多的字段).
注意: 很多相关的数据表单字段有一个 "boolean" 类型并且必须被有效处理. 15
失败场景
很多原因可以导致选项请求失败:
- 请求实体没有足够的权限来修改指定的JID的订阅选项.
- 请求实体(或指定的订阅者)未曾订阅.
- 请求没有同时指定 NodeID 和订阅者的 JID.
- 请求没有指定一个订阅ID但是它是必需的.
- 请求指定了一个订阅项ID但不是合法的或当前的.
- 订阅选项不支持.
- 节点不存在.
更完整的错误场景描述如下.
权限不足
请求订阅选项的时候, 订阅者必须指定向节点订阅的 JID 并且应该指定一个节点(如果没有指定节点, 即 通过 node="", 服务必须认为请求实体希望为它的订阅项向根集合节点请求订阅选项; 详见 XEP-0248 ).
服务必须验证提出请求的实体已经被授权为订阅的实体设置订阅选项. 如果订阅者的JID的格式是(<localpart@domain.tld/resource> 或 <domain.tld/resource>), 服务必须比较两个JID的(<localpart@domain.tld> 或 <domain.tld>)部分以确保他们是匹配的. 如果两个JID的纯JID部分不匹配并且请求实体没有被授权修改这个JID的订阅选项(例如, 因为它不是一个服务范围内的管理员或授权代理), 服务必须返回一个<forbidden/>错误.
例子 61. 请求实体没有足够的权限修改订阅选项
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
无该订阅者
如果请求实体(或指定的订阅者, 如果不同的话) 未曾订阅, 服务必须返回一个 <unexpected-request/> 错误, 它也应该包括一个 <not-subscribed/> 的 pubsub特有的错误条件.
例子 62. 没有这个订阅者
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <error type='modify'> <unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
要求订阅者JID
如果订阅者没有指定一个订阅者JID, 服务必须返回一个<bad-request/>错误, 它也应该包含一个<jid-required/>的pubsub特有的错误条件.
例子 63. 订阅者JID未指定
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <jid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
要求订阅ID
如果一个订阅标识符关联于某个订阅, 为了服务能够区分来自同一实体的订阅,在订阅请求中必须带上'subid'属性. 如果'subid'是必需的但未被提供, 服务必须返回一个<bad-request/>错误, 它也应该包含一个<subid-required/>的pubsub特有的错误条件.
例子 64. 要求SubID
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <subid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
错误的订阅ID
如果一个订阅标识符关联于某订阅, 但请求包含的 SubID 不合法或不是当前订阅者的, 服务必须返回一个<not-acceptable/>错误, 它也应该包含一个<invalid-subid/>的pubsub特有的错误条件.
例子 65. 非法的订阅标识符
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不支持订阅选项
如果节点或服务不支持订阅选项, 服务必须应答一个<feature-not-implemented/>错误, 指定一个<unsupported/>的pubsub特有的错误条件和一个"subscription-options"特性.
例子 66. 不支持订阅选项
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='subscription-options'/> </error> </iq>
节点不存在
如果节点不存在, pubsub服务必须返回一个<item-not-found/>错误.
例子 67. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
表单提交
接收了配置表单之后, 请求实体应该提交这个表单来更新这个实体对于那个节点的订阅选项.
例子 68. 订阅者提交完成的选项表单
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='options2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </options> </pubsub> </iq>
表单处理
成功
如果服务能成功处理提交的表单, 它必须应答成功.
例子 69. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options2'/>
失败
如果订阅者尝试设置一组非法的选项, 服务必须应答一个<bad-request/>错误.
例子 70. 服务对于请求非法选项应答错误
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options2'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-options xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
其他适用于获取订阅选项时发生的错误也同样适用于设置订阅选项的情形.
订阅并配置
大家直到, 如果一个服务支持订阅选项, 一个实体可以在同一个节中订阅和提供订阅选项.
注意: <options/>元素必须跟随在<subscribe/>元素之后并且不能(MUST NOT)拥有一个'node'属性或'jid'属性, 因为<subscribe/>元素的'node'属性值指明了期望的NodeID而<subscribe/>元素的'jid'属性值指明了订阅者的JID; 如果这些值中的任何一个违规了, 服务必须(MUST)返回一个<bad-request/>错误.
例子 71. 实体订阅节点并设置选项
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscribe node='princely_musings' jid='francisco@denmark.lit'/> <options> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </options> </pubsub> </iq>
当服务通知客户端成功时, 它应该包含一个类型为"result"的数据表单来通知客户端配置选项的结果.
例子 72. 服务应答成功(包含配置选项)
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3' subscription='subscribed'/> <options> <x xmlns='jabber:x:data' type='result'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </options> </pubsub> </iq>
请求缺省订阅配置选项
一个实体可能想请求关于缺省订阅配置的信息. 对此特性的支持是可选的.
请求
要获得一个节点的缺省订阅选项, 实体必须发送一个空的<default/>元素到该节点; 在应答里面, 该节点应该返回缺省的订阅选项.
例子 73. 实体请求缺省订阅配置选项
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='def1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <default node='princely_musings'/> </pubsub> </iq>
注意: 这里的命名空间是 'http://jabber.org/protocol/pubsub' (不是用于获取缺省节点配置选项的 'http://jabber.org/protocol/pubsub#owner' ).
服务本身也可以有缺省订阅配置选项. 要获得一个服务的缺省订阅配置选项的所有(叶子)节点, 实体必须发送一个空的<default/>元素但是不指定节点; 在应答中, 该服务应该返回缺省订阅选项.
例子 74. 实体请求缺省订阅配置选项
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='def2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <default/> </pubsub> </iq>
获取集合节点的缺省订阅配置选项的过程参见 XEP-0248 .
成功场景
如果没有发生错误, 该节点必须返回缺省的订阅配置选项.
例子 75. 服务应答缺省订阅配置选项
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='def1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <default node='princely_musings'> <x xmlns='jabber:x:data' type='result'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </default> </pubsub> </iq>
错误场景
有很多原因使得请求缺省订阅配置选项失败:
- 服务不支持订阅配置.
- 服务不支持获取缺省订阅选项.
对这些错误的完整描述见接下来的章节.
不支持节点配置
如果节点不支持订阅配置, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个 "subscription-options" 特性.
例子 76. 服务不支持订阅配置
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='def1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='subscription-options'/> </error> </iq>
不支持获取缺省订阅配置
如果节点不支持或缺缺省明月配置选项, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "retrieve-default-sub" .
例子 77. 服务不支持获取缺省订阅配置选项
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='def1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-default-sub'/> </error> </iq>
从节点获取条目
选择持久化条目的pubsub实现可以允许实体从一个节点请求已有的条目(例如, 一个实体可能希望在成功订阅之后这样做以接收这个节点历史上发布的所有条目).
许可
服务必须遵循节点访问模式来决定是否向请求它们的实体返回这些条目. 具体来说:
- 如果访问模式是"open", 服务应该允许任何实体(无论是否订阅)获取条目.
- 如果访问模式是"presence", 服务应该允许任何已订阅这个所有者的出席信息的实体获取条目.
- 如果访问模式是"roster", 服务应该允许任何已订阅这个所有者的出席信息并处于适当的名册组中的实体获取条目.
- 如果访问模式是"authorize"或"whitelist", 服务必须只允许已订阅的实体来获取条目.
(In addition, a service MUST always allow the node owner to retrieve items from a node and SHOULD always allow a publisher to do so.)
对于将来的访问模式的需求, 唯一的例外应该是强制本地隐私和安全策略, 更全面的描述见本文的安全事项. (另外, 一个服务必须总是允许节点所有者从一个节点获取条目并且应该总是允许一个发布者这样做.)
请求所有条目
订阅者可以通过仅仅不加限制地指明节点ID来请求所有的条目.
例子 78. 订阅者请求所有条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'/> </pubsub> </iq>
返回所有条目
服务然后应该返回所有发布到这个节点的条目, 尽管它可以截取结果(如果已发行的条目数量太多的话,参见下一节),很自然的它不能返回那些已删除的,过期的,等等条目.
例子 79. 服务返回所有条目
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='368866411b877c30064a5f62b917cffe'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>The Uses of This World</title> <summary> O, that this too too solid flesh would melt Thaw and resolve itself into a dew! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T17:47:23Z</published> <updated>2003-12-12T17:47:23Z</updated> </entry> </item> <item id='3300659945416e274474e469a1f0154c'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Ghostly Encounters</title> <summary> O all you host of heaven! O earth! what else? And shall I couple hell? O, fie! Hold, hold, my heart; And you, my sinews, grow not instant old, But bear me stiffly up. Remember thee! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T23:21:34Z</published> <updated>2003-12-12T23:21:34Z</updated> </entry> </item> <item id='4e30f35051b7b8b42abe083742187228'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Alone</title> <summary> Now I am alone. O, what a rogue and peasant slave am I! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-13T11:09:53Z</published> <updated>2003-12-13T11:09:53Z</updated> </entry> </item> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </pubsub> </iq>
返回一些条目
一个节点可能有非常多的条目,在这种情况下在应答一个条目请求时返回所有条目是一个问题. 这种情况下,服务应该返回一些条目并提示条目列表已经被截断了,包含一个结果集管理 16 记号.
例子 80. 服务通过结果集管理返回一些条目
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='368866411b877c30064a5f62b917cffe'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>The Uses of This World</title> <summary> O, that this too too solid flesh would melt Thaw and resolve itself into a dew! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T17:47:23Z</published> <updated>2003-12-12T17:47:23Z</updated> </entry> </item> <item id='3300659945416e274474e469a1f0154c'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Ghostly Encounters</title> <summary> O all you host of heaven! O earth! what else? And shall I couple hell? O, fie! Hold, hold, my heart; And you, my sinews, grow not instant old, But bear me stiffly up. Remember thee! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T23:21:34Z</published> <updated>2003-12-12T23:21:34Z</updated> </entry> </item> <item id='4e30f35051b7b8b42abe083742187228'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Alone</title> <summary> Now I am alone. O, what a rogue and peasant slave am I! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-13T11:09:53Z</published> <updated>2003-12-13T11:09:53Z</updated> </entry> </item> </items> <set xmlns='http://jabber.org/protocol/rsm'> <first index='0'>368866411b877c30064a5f62b917cffe</first> <last>4e30f35051b7b8b42abe083742187228</last> <count>19</count> </set> </pubsub> </iq>
返回最近发布的条目
即使服务或节点不支持持久化条目, 它可以返回最近发布的条目.
例子 81. 服务返回最近发布的条目
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </pubsub> </iq>
只返回通知
一个服务可以返回事件通知而不包含载荷(例如, 为了节省带宽). 如果这样, 该客户端可以请求一个特定的条目(使用 ItemID)以获取载荷. 当一个实体通过ItemID请求条目, 实现必须允许在该请求中指定多个条目.
例子 82. 订阅者通过ItemID请求特定条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items3'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='368866411b877c30064a5f62b917cffe'/> <item id='4e30f35051b7b8b42abe083742187228'/> </items> </pubsub> </iq>
例子 83. 服务发送被请求的条目(们)
<iq type='result' from='pubsub.shakespeare.lit' id='items3'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='368866411b877c30064a5f62b917cffe'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>The Uses of This World</title> <summary> O, that this too too solid flesh would melt Thaw and resolve itself into a dew! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T17:47:23Z</published> <updated>2003-12-12T17:47:23Z</updated> </entry> </item> <item id='4e30f35051b7b8b42abe083742187228'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Alone</title> <summary> Now I am alone. O, what a rogue and peasant slave am I! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-13T11:09:53Z</published> <updated>2003-12-13T11:09:53Z</updated> </entry> </item> </items> </pubsub> </iq>
请求最近的条目
一个服务可以允许实体使用 'max_items' 属性请求最近的N个条目 . 当使用了 max_items , 实现应该返回N个最近的(和N个最旧的相反)条目. (注意: 本协议的未来版本可能推荐使用 XEP-0059 取代 'max_items' 属性.)
例子 84. 订阅者请求两个最近的条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings' max_items='2'/> </pubsub> </iq>
例子 85. 服务返回两个最近的条目
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='4e30f35051b7b8b42abe083742187228'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Alone</title> <summary> Now I am alone. O, what a rogue and peasant slave am I! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-13T11:09:53Z</published> <updated>2003-12-13T11:09:53Z</updated> </entry> </item> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </pubsub> </iq>
请求一个特定条目
订阅者可以通过指定节点ID和适当的ItemID请求一个特定条目.
例子 86. 订阅者请求一个特定条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items3'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </pubsub> </iq>
该服务接着将返回那个特定的条目,如果可用的话.
错误场景
有很多种原因可能导致条目接收请求失败:
- 请求实体对同一个节点有多个订阅但是没有指定一个订阅ID.
- 请求实体订阅了但是指定了一个非法的订阅项ID.
- 节点不返回条目给未订阅的实体而请求实体确实未曾订阅.
- 服务或节点不支持持久化条目并且不返回最后发布的条目.
- 服务或节点不支持条目获取.
- 节点有一个"presence"访问模式而请求实体没有订阅所有者的出席信息.
- 节点有一个"roster"访问模式而请求实体不在授权的名册组之一.
- 节点有一个"whitelist"访问模式而请求实体不在白名单中.
- 服务或节点需要付费才允许获取条目.
- 请求实体被禁止从节点获取条目(例如, 因为有一个排斥者的从属关系).
- 节点不存在.
这些错误的完整描述如下.
要求订阅ID
如果请求实体对同一个节点有多个订阅但是没有指定一个订阅ID, 服务必须返回一个<bad-request/>错误给订阅者, 它也应该包含一个<subid-required/>的pubsub特有的错误条件.
例子 87. 实体未指定SubID
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <subid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
非法的订阅ID
如果请求实体订阅了但是指定了一个非法的订阅ID, 服务必须返回一个<not-acceptable/>错误给订阅者, 它也应该包含一个<invalid-subid/>的pubsub特有的错误条件.
例子 88. 实体指定了非法的SubID
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
实体未订阅
如果节点未返回条目给未订阅的实体并且请求实体未曾订阅(包括已有一个未决的订阅的情形), 服务必须返回一个<not-authorized/>错误给订阅者, 它也应该包含一个<not-subscribed/>的pubsub特有的错误条件.
例子 89. 实体未订阅
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不支持持久化条目
如果服务或节点不支持持久化条目且没有返回最后发行的条目, 服务必须返回一个<feature-not-implemented/>错误给订阅者, 指定一个pubsub特有的错误条件<unsupported/>以及一个特性"persistent-items".
例子 90. 不支持持久化条目
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/> </error> </iq>
不支持获取条目
如果服务或节点不支持获取条目(例如, 因为节点是一个如 XEP-0248 所述的集合节点), 服务必须返回一个<feature-not-implemented/>错误给订阅者, 指明一个pubsub特有的错误条件<unsupported/>以及一个特性"retrieve-items".
例子 91. 不支持获取条目
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-items'/> </error> </iq>
要求订阅出席信息
对于访问模式为"presence"的节点, 如果请求实体没有订阅所有者的出席信息那么pubsub服务必须应答一个<not-authorized/>错误, 它还应该包含一个pubsub特有的错误条件<presence-subscription-required/>.
例子 92. 实体没有被授权获取条目(要求订阅出席信息)
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <presence-subscription-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不在名册组
对于访问模式为"roster"的节点, 如果请求实体不在授权名册组中那么pubsub服务必须应答一个<not-authorized/>错误, 它也应该包含一个pubsub特有的错误条件<not-in-roster-group/>.
例子 93. 实体没有被授权获取条目(不在名册组中)
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-in-roster-group xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不在白名单
对于访问模式为"whitelist"的节点, 如果请求实体不在白名单中那么服务必须返回一个<not-allowed/>错误, 指明一个pubsub特有的错误条件<closed-node/>.
例子 94. 节点为白名单模式
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='cancel'> <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <closed-node xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
要求付费
商业部署可能希望把订阅者链接到一个付费客户数据库. 如果订阅者需要付费才能从那个节点获取条目(例如, 如果订阅者不在客户数据库或客户的帐目没有付清), 服务应该返回一个<payment-required/>错误给订阅者.
例子 95. 需要付费才能获取条目
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <payment-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
被禁止
如果请求实体被禁止获取条目(例如, 因为有一个被排斥者的从属关系), 服务必须返回一个<forbidden/>错误给订阅者.
例子 96. 请求实体被禁止
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
节点不存在
如果节点不存在, 服务应该返回一个<item-not-found/>错误给订阅者.
例子 97. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
无此条目
如果该节点没有条目或请求的那个条目不存在, 服务应该返回一个类型为"result"并带有一个空的<items/>元素的IQ节.
例子 98. 无此条目
<iq from='pubsub.shakespeare.lit' id='items1' to='francisco@denmark.lit/barracks' type='result'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'/> </pubsub> </iq>
发布者用例
发布条目到节点
请求
一个被允许向一个节点发布条目的实体 (即.,一个发布者或一个所有者) 可以在任何时候通过发送一个包含一个pubsub子元素<publish/>的 IQ-set 节给服务来做到这一点.
语法如下:
例子如下.
例子 99. 发布者带ItemID发布一个条目
<iq type='set' from='hamlet@denmark.lit/blogbot' to='pubsub.shakespeare.lit' id='publish1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <publish node='princely_musings'> <item id='bnd81g37d61f49fgn581'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </publish> </pubsub> </iq>
成功场景
如果pubsub服务能成功处理请求, 它必须通知发布者成功了. 如果发布请求不包含一个ItemID, IQ-result 应该包含一个空的<item/>元素来指定已发布的条目的ItemID.
例子 100. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/blogbot' id='publish1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <publish node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </publish> </pubsub> </iq>
注意: 如果发布者之前用同一个ItemID发布了一个条目, 该请求的成功处理意味着服务必须以新条目覆盖那个旧条目并做如下处理.
pubsub服务接着必须发送一个事件通知给每个按标准可以接收事件通知的实体(典型的是对于每个已批准的订阅者, 尽管在其他上下文中实体也可以接收事件通知,如通知触发器总结的那样). 每个由pubsub服务生成的<message/>节应该拥有一个值为唯一的'id'属性这样该服务能正确地追踪任何和通知相关的可能发生的错误(见本文的处理通知相关的错误). 根据节点配置, 事件通知要么包含要么不包含在和, 如下所示.
注意: 为了做到如本文从节点删除条目所述的条目删除的授权, 支持持久化条目的实现应该存储该条目(如果该节点是如此配置的)并维护一个发布者的记录.
注意: 如果该服务或节点已被配置,那么该节点上缓存的条目数有一个最大值,如果当一个条目被发布时缓存数量已经达到了最大值,该服务必须删除现有的条目之一. 推荐服务遵循 "先进先出" 规则并删除最旧的条目. 根据节点配置, 删除一个现有的条目可能(MAY)导致发送一个删除通知给订阅者.
带载荷的通知
如果节点被配置成包含载荷, 订阅者将随事件通知接收到载荷.
例子 101. 订阅者接收带载荷的事件通知
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='bernardo@denmark.lit' id='bar'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='horatio@denmark.lit' id='baz'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='bard@shakespeare.lit' id='fez'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </event> </message>
不带载荷的通知
如果节点被配置成不包含载荷, 订阅者将只接收事件通知. (如果不包含载荷, 订阅者可以通过本文从节点接收条目定义的协议请求已发布的条目.)
例子 102. 订阅者只接收事件通知
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='bernardo@denmark.lit' id='bar'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='horatio@denmark.lit' id='baz'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='bard@shakespeare.lit' id='fez'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </event> </message>
条目发布者
如果配置成这样, 服务在生成事件通知的时候可以包含该条目的发布者.
例子 103. 服务通知订阅者
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901' publisher='hamlet@denmark.lit'> [ ... ENTRY ... ] </item> </items> </event> </message>
'publisher'属性的值必须由服务生成, 服务不接受在发布条目中带它, 因为允许发布者断言它的JID可能导致欺诈.
由服务标记的JID要么是 (1) 从用来发布该条目的 IQ-set 的 'from'属性获得的发布者的全JID <localpart@domain.tld/resource> ,要么是 (2) 在发布者白名单的显式列表中的一个正式的从属关系中衍生出的发布者的纯JID <localpart@domain.tld>.
包含订阅ID
如果一个单一的实体多次向一个节点订阅, 服务应该以符号标记事件通知这样该实体能确定哪个订阅标识符生成了该事件. 如果包含了这些符号, 它们必须使用Stanza Headers and Internet Metadata 19 格式并且应该在事件通知信息之后被包含(即, 作为<message/>节的最后的子元素).
例子 104. 订阅者接收带标记的事件通知
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </event> <headers xmlns='http://jabber.org/protocol/shim'> <header name='SubID'>123-abc</header> <header name='SubID'>004-yyy</header> </headers> </message>
错误场景
有很多原因可能导致发布请求失败:
- 请求实体没有足够的权限发布.
- 节点不支持条目发布.
- 节点不存在.
- 载荷大小超出服务器定义的限制.
- 条目包含多个载荷元素或根载荷元素的命名空间和为该节点配置的命名空间不匹配.
- 请求和该节点配置不匹配.
这写错误场景的完整描述见下面章节.
注意: 如果一个发布者以一个Item ID发布一个条目并且该ItemID和现有的条目匹配, pubsub服务不能(MUST NOT)让这次发布失败而是必须覆盖现有条目并生成一个新的事件通知(即, 重发布等同于修改).
权限不足
如果请求实体没有足够的权限发布, 服务必须返回一个<forbidden/>错误.
例子 105. 实体没有足够权限发布到节点
<iq type='error' from='pubsub.shakespeare.lit' id='publish1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
不支持发布条目
如果该节点不支持条目发布(例如, 因为它是一个如 XEP-0248 所述的集合节点), 服务必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "publish" .
例子 106. 节点不支持条目发布
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='publish1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='publish'/> </error> </iq>
节点不存在
如果请求实体尝试发布条目到一个不存在的节点并且服务不支持"自动创建"特性 (见自动创建节点), 该服务必须返回一个<item-not-found/> 错误.
例子 107. 实体尝试发布到不存在的节点
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='publish1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
载荷过大
如果载荷的大小超出了服务定义的限制, 服务必须返回一个<not-acceptable/>错误, 它也将包含一个pubsub特有的错误条件<payload-too-big/>.
例子 108. 实体尝试发布非常大的载荷
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='publish1'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <payload-too-big xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
坏的载荷
如果<item/>元素包含多个载荷元素或根载荷元素的命名空间和为该节点配置的命名空间不匹配, 服务必须弹回该请求一个<bad-request/>错误, 它也应该包含一个pubsub特有的错误条件<invalid-payload/>.
例子 109. 实体尝试发布条目时带多个载荷元素或命名空间不匹配
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='publish1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-payload xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
请求和配置不匹配
如果请求未遵循该节点配置的事件类型, 服务可以弹回该请求一个<bad-request/>错误, 它也应该包含一个pubsub特有的错误条件. 采用以下规则:
- 如果事件类型是持久化(事件通知或载荷)并且发布者未指定一个ItemID, 服务必须生成该ItemID且不能(MUST NOT)弹回发布请求.
- 如果事件类型是持久化(事件通知或载荷)并且发布者未包含一个条目, 服务必须弹回发布请求一个<bad-request/>错误以及一个pubsub特有的错误条件<item-required/>.
- 如果事件类型是载荷(持久化或临时的)并且发布者未包含一个载荷, 服务应该弹回发布请求一个<bad-request/>错误和一个pubsub特有的错误条件<payload-required/>.
- 如果事件类型是 通知 + 临时的 并且发布者提供了一个条目, 服务必须弹回发布请求一个<bad-request/>错误和一个pubsub特有的错误条件<item-forbidden/>.
这些错误的例子如下所示.
例子 110. 发布者尝试不带条目发布到持久化节点
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='publish1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <item-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
例子 111. 发布者尝试不带载荷发布到载荷节点
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='publish1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <payload-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
例子 112. 发布者尝试带条目发布到临时通知节点
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='publish1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <item-forbidden xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
自动创建节点
一个pubsub服务可以在它接收到一个发送到不存在的节点的发布请求的时候自动创建一个节点而不是返回一个<item-not-found/>错误). 当这样做的时候, 该服务应该应用缺省节点配置. 如果一个服务支持这个功能, 它必须声明那个事实,在它的 disco#info 应答中包含一个特性 "http://jabber.org/protocol/pubsub#auto-create" .
发布选项
一个pubsub服务可以支持为一个发布请求指定选项的能力(如果这样, 它必须声明支持 "http://jabber.org/protocol/pubsub#publish-options" 特性). 这是例子:
例子 113. 带选项发布
<iq type='set' from='hamlet@denmark.lit/blogbot' to='pubsub.shakespeare.lit' id='pub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <publish node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </publish> <publish-options> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#publish-options</value> </field> <field var='pubsub#access_model'> <value>presence</value> </field> </x> </publish-options> </pubsub> </iq>
<publish-options/>元素应该包含一个数据表单(见 XEP-0004 ), 它的 FORM_TYPE 应该是 "http://jabber.org/protocol/pubsub#publish-options" (见 XEP-0068 ).
字段如何被处理取决于该pubsub服务, 它在 XEP-0004 的功能语境中是作为一个 表单处理 实体.
例如, 服务可以把字段视为一个前提条件, 在那种情况下服务将做如下处理:
- 如果该节点存在但前提条件不吻合, 那么发布将以一个<conflict/>错误条件和一个pubsub特有的条件<precondition-not-met/>失败.
- 如果该节点存在且前提条件吻合, 那么发布成功.
- 如果该节点不存在且该服务支持"auto-create"特性, 那么该服务将以除了哪些在前提条件中指定的配置之外的缺省配置自动创建该节点, 并且发布成功.
- 如果该节点不存在且该服务不支持"auto-create"特性, 那么发布将失败.
从节点删除条目
一个发布者可能想在它已经发布一个条目到一个支持持久化条目的节点之后删除那个条目. 对此特性("delete-items")的支持是推荐的.
请求
要删除一个条目, 发布者发送一个如下例所示的retract请求. <retract/>元素必须拥有一个'node'属性, 可以拥有一个'notify'属性, 并且必须包含一个<item/>元素; 该<item/>元素必须是空的并且必须拥有一个'id'属性.
例子 114. 实体从节点删除一个条目
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='retract1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <retract node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </retract> </pubsub> </iq>
成功场景
如果没发生错误, 服务必须删除该条目.
例子 115. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='retract1'/>
删除并通知
如果没发生错误且<retract/>元素包含一个值为 "true" 或 "1" 的 'notify' 属性 20, 那么服务必须删除该条目并且必须如下所示通知所有的订阅者. 语法除了替换了一个<item/>元素和事件通知完全一样, 这里的<items/>元素包含了一个<retract/>元素.
例子 116. 订阅者被通知删除
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <retract id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='bernardo@denmark.lit' id='bar'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <retract id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </event> </message>
包含订阅ID
如果一个单独的实体多次订阅了某节点, 服务应该标记该节点删除,这样实体能确定哪个订阅标识符生成了这个通知. 如上, 如果这些标记被包含, 它们必需使用 Stanza Headers and Internet Metadata (SHIM) 协议并且应该在通知数据之后被包含(即, 作为<message/>节的最后的子元素).
例子 117. 订阅者接收带标记的事件通知
<message from='pubsub.shakespeare.lit' to='bernardo@denmark.lit' id='bar'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <retract id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </event> <headers xmlns='http://jabber.org/protocol/shim'> <header name='SubID'>123-abc</header> <header name='SubID'>004-yyy</header> </headers> </message>
错误场景
有很多原因可能导致条目撤销请求失败:
- 发布者没有足够权限删除请求的条目.
- 节点或条目不存在.
- 请求没有指定节点.
- 请求没有包含一个<item/>元素或<item/>元素未指定一个ItemID.
- 节点不支持持久化条目.
- 服务不支持条目删除.
这些错误的完整描述见下面章节.
权限不足
如果请求的实体没有足够的权限删除该条目, 服务必须返回一个<forbidden/>错误.
例子 118. 请求实体没有足够权限
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='retract1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
节点不存在
如果该节点或条目不存在, 服务必须返回一个<item-not-found/>条目.
例子 119. 不存在的节点或条目
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='retract1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
要求NodeID
如果请求未指定一个节点, 服务必须返回一个<bad-request/>错误, 它也应该包含一个pubsub特有的错误条件<nodeid-required/>.
例子 120. 请求未指定一个节点
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='retract1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
要求Item或ItemID
如果请求未包含一个<item/>元素或<item/>元素未指定一个ItemID, 服务必须返回一个<bad-request/>错误, 它也应该包含一个pubsub特有的错误条件<item-required/>.
例子 121. 请求未指定一个条目
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='retract1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <item-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不支持持久化条目
如果该节点不支持持久化条目(例如, 因为它是一个集合节点或一个临时节点所以不递送载荷), 服务必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "persistent-items" .
例子 122. 节点不支持持久化条目
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='retract1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/> </error> </iq>
不支持条目删除
如果该服务不支持条目删除, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "delete-items" .
例子 123. 服务不支持条目删除
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='retract1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='delete-items'/> </error> </iq>
所有者用例
创建节点
常规事项
一个实体可能想创建一个新节点. 推荐支持这个特性("create-nodes"). 无论如何, 一个服务可以基于请求实体的身份不允许创建一个节点, 或可以完全不允许创建节点(例如, 为服务器范围的管理员保留这个权限).
有两种办法创建一个节点:
- 以缺省配置和指定的节点类型创建一个节点.
- 同时创建并配置一个节点.
例子 124. 请求创建一个节点
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='create1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <create node='princely_musings'/> </pubsub> </iq>
这些方法, 以及相应的方法特有的错误条件的完整解释在下面章节中.
在方法特有的错误条件之外, 有很多常规的原因导致节点创建请求失败:
- 服务不支持节点创建.
- 只有在该服务上注册了的实体被允许创建节点,未注册的请求实体不允许.
- 请求实体没有足够权限创建节点.
- 请求的NodeID已存在.
- 请求未包含一个NodeID且不支持"instant nodes".
常规错误场景的完整描述如下.
如果服务不支持节点创建, 它必须应答一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "create-nodes" .
例子 125. 服务不支持节点创建
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='create1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='create-nodes'/> </error> </iq>
如果只有在服务上注册了的实体可以创建节点但是请求实体还没有注册, 服务必须应答一个<registration-required/>错误.
例子 126. 服务要求注册
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='create1'> <error type='auth'> <registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
如果请求实体没有足够权限创建节点, 服务必须应答一个<forbidden/>错误.
例子 127. 请求实体被禁止创建节点
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='create1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
如果请求的NodeID已存在, 服务必须应答一个<conflict/>错误.
例子 128. NodeID已存在
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='create1'> <error type='cancel'> <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
如果节点创建者未指定一个NodeID但是服务不支持即时节点, 服务必须返回一个<not-acceptable/>错误, 指定一个pubsub特有的错误条件<nodeid-required/>.
例子 129. 服务不支持即时节点
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='create2'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
如果节点创建者未指定一个NodeID但是服务支持即时节点, 服务应该代表节点创建者生成一个在该服务上下文中唯一的NodeID.
例子 130. 实体请求一个即时节点
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='create2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <create/> </pubsub> </iq>
如果没有发生错误, pubsub服务应该创建该节点, 生成一个那个服务上下文中唯一的NodeID, 并通知该用户成功了(应答中包含该NodeID).
例子 131. 服务应答成功和生成的NodeID
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='create2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <create node='25e3d37dabbab9541f7523321421edc5bfeb2dae'/> </pubsub> </iq>
注意: 当一个服务代表请求实体成功创建一个节点, 它必须返回一个 IQ result (根据 XMPP Core). 如果该节点创建请求未指定一个NodeID且服务支持创建即时节点, 该服务必须在该 IQ result 中指定已创建的NodeID. 类似的, 如果该节点创建请求指定了一个NodeID但是服务在创建该节点之前修改了该NodeID, 该服务必须也在 IQ result 中指定已修改的节点. 在所有其他情况下, 该服务可以在该 IQ result 中指定NodeID但是节点创建者不能(MUST NOT)依赖从服务接收到它(因为节点创建者能通过跟踪 IQ-set 指定的'id'属性来确定哪个节点被创建了).
以缺省配置创建一个节点
如上文解释的, 每个节点有它自己的缺省配置. 通过以缺省配置向服务请求创建一个节点, 该节点创建者接受缺省配置. 如果服务允许节点配置, 所有者可以在创建节点之后重新配置该节点(如本文的配置节点所述). 另外, 一个服务可以允许实体在创建节点之前为一个给定的节点类型决定缺省配置(如本文的请求缺省配置所述).
为了以缺省配置创建一个节点, 节点创建者可以简单的包含一个空的<create/>子元素.
在以下例子中, 节点创建者以一个公开访问模式(假定是该服务的缺省类型)请求一个叶子节点(缺省类型).
例子 132. 实体以(缺省的)公开访问模式请求叶子节点
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='create1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <create node='princely_musings'/> </pubsub> </iq>
注意: 'pubsub#node_type' 配置字段的的缺省设置是"leaf".
为了向服务请求一个不同于缺省的访问模式, 该节点创建者必须在节点创建请求中包含一个数据表单来为 'pubsub#access_model' 字段指定一个非缺省的值.
例子 133. 实体以非缺省访问模式请求叶子节点
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='create2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <create node='princely_musings'/> <configure> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#node_config</value> </field> <field var='pubsub#access_model'><value>whitelist</value></field> </x> </configure> </pubsub> </iq>
如果该访问模式是被支持的并且没有发生常规或方法特有的错误, 服务应该创建该节点并通知请求实体成功.
例子 134. 服务通知请求实体成功
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='create1'/>
如果服务不支持指定的访问模式, 它必须返回一个<not-acceptable/>错误, 指定一个pubsub特有的错误条件<unsupported-access-model/>.
例子 135. 服务不支持指定的访问模式
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='create2'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported-access-model xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
(对于如果服务不支持指定的节点类型的错误处理, 参考 XEP-0248 .)
创建并配置节点
如果一个实现允许节点配置(见本文的配置节点), 它应该允许创建请求在节点创建请求中包含期望的节点配置.
注意: <configure/>元素必须跟随<create/>元素且不能(MUST NOT)拥有 'node' 属性, 因为<create/>元素的 'node' 属性的值指定了期望的NodeID; 如果这些规则中的任何一个规则被违反了, 该服务必须返回一个<bad-request/>错误.
例子 136. 实体以非缺省配置请求一个新节点
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='create1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <create node='princely_musings'/> <configure> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#node_config</value> </field> <field var='pubsub#title'><value>Princely Musings (Atom)</value></field> <field var='pubsub#deliver_notifications'><value>1</value></field> <field var='pubsub#deliver_payloads'><value>1</value></field> <field var='pubsub#persist_items'><value>1</value></field> <field var='pubsub#max_items'><value>10</value></field> <field var='pubsub#item_expire'><value>604800</value></field> <field var='pubsub#access_model'><value>open</value></field> <field var='pubsub#publish_model'><value>publishers</value></field> <field var='pubsub#purge_offline'><value>0</value></field> <field var='pubsub#send_last_published_item'><value>never</value></field> <field var='pubsub#presence_based_delivery'><value>false</value></field> <field var='pubsub#notification_type'><value>headline</value></field> <field var='pubsub#notify_config'><value>0</value></field> <field var='pubsub#notify_delete'><value>0</value></field> <field var='pubsub#notify_retract'><value>0</value></field> <field var='pubsub#notify_sub'><value>0</value></field> <field var='pubsub#max_payload_size'><value>1028</value></field> <field var='pubsub#type'><value>http://www.w3.org/2005/Atom</value></field> <field var='pubsub#body_xslt'> <value>http://jabxslt.jabberstudio.org/atom_body.xslt</value> </field> </x> </configure> </pubsub> </iq>
例子 137. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='create1'/>
如果一个服务支持这个 "create-and-configure" 特性, 它必须在对服务查询信息请求的应答中通过返回一个特性 "http://jabber.org/protocol/pubsub#create-and-configure" 来声明那一事实. 如果 create-and-configure 选项不被支持但是请求实体还是发送了这样一个请求, 服务应该忽略该请求的配置部分而当成没有包含这些配置一样处理.
配置节点
在创建了一个新节点之后, 该节点所有者可能想修改该节点的配置. 推荐支持这个特性.
请求
例子 138. 所有者请求配置表单
<iq type='get' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='config1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <configure node='princely_musings'/> </pubsub> </iq>
成功场景
如果没有发生错误, 服务器必须返回一个配置表单给该节点所有者, 它应该包含当前的节点配置作为缺省值.
注意: 接下来的例子展示了一些可以提供的可能的配置选项. 如果一个实现使用 数据表单 协议实现了这些特性, 那个实现必须使用已经注册到XMPP注册处的和 'http://jabber.org/protocol/pubsub' 命名空间相关的字段(那些字段变量的初步表述见下文以及本文的pubsub#node_config FORM_TYPE, 但是不能(MUST NOT)被解释为标准, 因为XMPP注册处可以晚些时候标准化额外的字段而不用改变本文). 一个实现可以选择指定不同的标签, 值, 以及甚至字段类型, 但必须遵循已定义的变量名scheme.
例子 139. 服务应答配置表单
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='config1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <configure node='princely_musings'> <x xmlns='jabber:x:data' type='form'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#node_config</value> </field> <field var='pubsub#title' type='text-single' label='A friendly name for the node'/> <field var='pubsub#deliver_notifications' type='boolean' label='Whether to deliver event notifications'> <value>true</value> </field> <field var='pubsub#deliver_payloads' type='boolean' label='Whether to deliver payloads with event notifications'> <value>true</value> </field> <field var='pubsub#notify_config' type='boolean' label='Notify subscribers when the node configuration changes'> <value>0</value> </field> <field var='pubsub#notify_delete' type='boolean' label='Notify subscribers when the node is deleted'> <value>false</value> </field> <field var='pubsub#notify_retract' type='boolean' label='Notify subscribers when items are removed from the node'> <value>false</value> </field> <field var='pubsub#notify_sub' type='boolean' label='Notify owners about new subscribers and unsubscribes'> <value>0</value> </field> <field var='pubsub#persist_items' type='boolean' label='Persist items to storage'> <value>1</value> </field> <field var='pubsub#max_items' type='text-single' label='Max # of items to persist'> <value>10</value> </field> <field var='pubsub#item_expire' type='text-single' label='Time after which to automatically purge items'> <value>604800</value> </field> <field var='pubsub#subscribe' type='boolean' label='Whether to allow subscriptions'> <value>1</value> </field> <field var='pubsub#access_model' type='list-single' label='Specify the subscriber model'> <option><value>authorize</value></option> <option><value>open</value></option> <option><value>presence</value></option> <option><value>roster</value></option> <option><value>whitelist</value></option> <value>open</value> </field> <field var='pubsub#roster_groups_allowed' type='list-multi' label='Roster groups allowed to subscribe'> <option><value>friends</value></option> <option><value>courtiers</value></option> <option><value>servants</value></option> <option><value>enemies</value></option> </field> <field var='pubsub#publish_model' type='list-single' label='Specify the publisher model'> <option><value>publishers</value></option> <option><value>subscribers</value></option> <option><value>open</value></option> <value>publishers</value> </field> <field var='pubsub#purge_offline' type='boolean' label='Purge all items when the relevant publisher goes offline?'> <value>0</value> </field> <field var='pubsub#max_payload_size' type='text-single' label='Max Payload size in bytes'> <value>1028</value> </field> <field var='pubsub#send_last_published_item' type='list-single' label='When to send the last published item'> <option label='Never'><value>never</value></option> <option label='When a new subscription is processed'><value>on_sub</value></option> <option label='When a new subscription is processed and whenever a subscriber comes online'> <value>on_sub_and_presence</value> </option> <value>never</value> </field> <field var='pubsub#presence_based_delivery' type='boolean' label='Deliver event notifications only to available users'> <value>0</value> </field> <field var='pubsub#notification_type' type='list-single' label='Specify the delivery style for event notifications'> <option><value>normal</value></option> <option><value>headline</value></option> <value>headline</value> </field> <field var='pubsub#type' type='text-single' label='Specify the type of payload data to be provided at this node'> <value>http://www.w3.org/2005/Atom</value> </field> <field var='pubsub#dataform_xslt' type='text-single' label='Payload XSLT'/> </x> </configure> </pubsub> </iq>
错误场景
有很多原因可能导致节点配置请求失败:
- 服务不支持节点配置.
- 请求实体没有足够权限配置该节点.
- 请求未指定一个节点.
- 节点没有配置选项.
- 指定的节点不存在.
这些错误场景的完整描述在下面章节.
不支持节点配置
如果服务不支持节点配置, 该服务必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "config-node" .
例子 140. 服务不支持节点配置
<iq type='error' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='config1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='config-node'/> </error> </iq>
权限不足
如果请求实体没有足够权限去配置该节点, 服务必须应答一个<forbidden/>错误.
例子 141. 请求实体被禁止配置该节点
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='config1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
要求NodeID
如果请求未指定一个节点, 服务应该返回一个<bad-request/>错误. 有可能不包含一个NodeID是因为请求实体向根节点请求配置; 无论如何, 如果请求实体不是一个服务器范围的管理员, 最好返回<bad-request/>而不是<forbidden/>.
例子 142. 请求未指定一个节点
<iq type='error' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='config1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
无配置选项
如果没有配置选项可用(例如, 因为节点配置"被封止了"), 该服务必须返回一个<not-allowed/>错误给所有者.
例子 143. 节点无配置选项
<iq type='error' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='config1'> <error type='cancel'> <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
节点不存在
如果节点不存在, 服务必须返回一个<item-not-found/>错误.
例子 144. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='config1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
表单提交
在接收到配置表单之后, 所有者应该提交一个完成的配置表单.
例子 145. 所有者提交节点配置表单
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='config2'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <configure node='princely_musings'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#node_config</value> </field> <field var='pubsub#title'><value>Princely Musings (Atom)</value></field> <field var='pubsub#deliver_notifications'><value>1</value></field> <field var='pubsub#deliver_payloads'><value>1</value></field> <field var='pubsub#persist_items'><value>1</value></field> <field var='pubsub#max_items'><value>10</value></field> <field var='pubsub#item_expire'><value>604800</value></field> <field var='pubsub#access_model'><value>roster</value></field> <field var='pubsub#roster_groups_allowed'> <value>friends</value> <value>servants</value> <value>courtiers</value> </field> <field var='pubsub#publish_model'><value>publishers</value></field> <field var='pubsub#purge_offline'><value>0</value></field> <field var='pubsub#send_last_published_item'><value>never</value></field> <field var='pubsub#presence_based_delivery'><value>false</value></field> <field var='pubsub#notification_type'><value>headline</value></field> <field var='pubsub#notify_config'><value>0</value></field> <field var='pubsub#notify_delete'><value>0</value></field> <field var='pubsub#notify_retract'><value>0</value></field> <field var='pubsub#notify_sub'><value>0</value></field> <field var='pubsub#max_payload_size'><value>1028</value></field> <field var='pubsub#type'><value>http://www.w3.org/2005/Atom</value></field> <field var='pubsub#body_xslt'> <value>http://jabxslt.jabberstudio.org/atom_body.xslt</value> </field> </x> </configure> </pubsub> </iq>
替代的, 所有者可以取消配置过程, 在这种情况下必须应用现有的配置.
例子 146. 所有者取消配置过程
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='config2'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <configure node='princely_musings'> <x xmlns='jabber:x:data' type='cancel'/> </configure> </pubsub> </iq>
表单处理
成功
如果表单能被成功处理, 服务必须返回一个 IQ-result.
例子 147. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='config2'/>
注意: 如果该节点类型从叶子改成集合且有条目和该节点相关, 服务必须清除节点的所有条目(通知或不通知订阅者).
失败
如果请求的节点配置变更不能被处理(例如, 因为节点所有者尝试修改配置使得它没有节点所有者), 服务必须返回一个<not-acceptable/>错误给所有者.
例子 148. 配置变更不能被处理
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='config2'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
成功并通知
如果 "pubsub#notify_config" 选项被设为true, 服务必须通知订阅者配置变更. (服务应该为叶子节点支持这个选项,必须为集合节点支持这个选项,如 XEP-0248 所述.) 如果节点配置被设为仅通知, 该通知必须包含一个空的<configuration/>元素,它的'node'属性被设为该节点的NodeID; 如果该节点配置被设为全载荷, <configuration/>元素必须额外包含该节点配置并通过 数据表单 协议展示它.
例子 149. 服务发送配置变更通知(仅事件通知)
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <configuration node='princely_musings'/> </event> </message>
例子 150. 服务发送配置变更通知(带载荷)
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <configuration node='princely_musings'> <x xmlns='jabber:x:data' type='result'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#node_config</value> </field> <field var='pubsub#title'><value>Princely Musings (Atom)</value></field> <field var='pubsub#deliver_notifications'><value>1</value></field> <field var='pubsub#deliver_payloads'><value>1</value></field> <field var='pubsub#notify_config'><value>0</value></field> <field var='pubsub#notify_delete'><value>0</value></field> <field var='pubsub#notify_retract'><value>0</value></field> <field var='pubsub#notify_sub'><value>0</value></field> <field var='pubsub#persist_items'><value>1</value></field> <field var='pubsub#max_items'><value>10</value></field> <field var='pubsub#item_expire'><value>604800</value></field> <field var='pubsub#subscribe'><value>1</value></field> <field var='pubsub#access_model'><value>open</value></field> <field var='pubsub#publish_model'><value>publishers</value></field> <field var='pubsub#purge_offline'><value>0</value></field> <field var='pubsub#max_payload_size'><value>9216</value></field> <field var='pubsub#send_last_published_item'><value>never</value></field> <field var='pubsub#presence_based_delivery'><value>0</value></field> <field var='pubsub#notification_type'><value>headline</value></field> <field var='pubsub#type'><value>http://www.w3.org/2005/Atom</value></field> <field var='pubsub#body_xslt'> <value>http://jabxslt.jabberstudio.org/atom_body.xslt</value> </field> </x> </configuration> </event> </message>
请求缺省节点配置选项
一个实体可能希望请求关于缺省节点配置的信息, 例如 为了确定是否执行前文所述的 创建并配置 . 对这个特性的支持是可选的.
请求
要获得节点选项, 该实体必须不带NodeID发送一个空的<default/>元素给服务; 在应答中, 该服务应该返回缺省节点选项.
例子 151. 实体请求缺省节点配置选项
<iq type='get' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='def1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <default/> </pubsub> </iq>
成功场景
如果没有发生错误, 服务必须返回缺省节点配置选项.
例子 152. 服务应答缺省节点配置选项
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='def1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <default> <x xmlns='jabber:x:data' type='form'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#node_config</value> </field> <field var='pubsub#title' type='text-single' label='A friendly name for the node'/> <field var='pubsub#deliver_notifications' type='boolean' label='Deliver event notifications'> <value>true</value> </field> <field var='pubsub#deliver_payloads' type='boolean' label='Deliver payloads with event notifications'> <value>1</value> </field> <field var='pubsub#description' type='text-single' label='A description of the node'/> <field var='pubsub#notify_config' type='boolean' label='Notify subscribers when the node configuration changes'> <value>0</value> </field> <field var='pubsub#notify_delete' type='boolean' label='Notify subscribers when the node is deleted'> <value>0</value> </field> <field var='pubsub#notify_retract' type='boolean' label='Notify subscribers when items are removed from the node'> <value>0</value> </field> <field var='pubsub#notify_sub' type='boolean' label='Notify owners about new subscribers and unsubscribes'> <value>0</value> </field> <field var='pubsub#persist_items' type='boolean' label='Persist items to storage'> <value>1</value> </field> <field var='pubsub#max_items' type='text-single' label='Max # of items to persist'> <value>10</value> </field> <field var='pubsub#item_expire' type='text-single' label='Time after which to automatically purge items'> <value>604800</value> </field> <field var='pubsub#subscribe' type='boolean' label='Whether to allow subscriptions'> <value>1</value> </field> <field var='pubsub#access_model' type='list-single' label='Specify the subscriber model'> <option><value>authorize</value></option> <option><value>open</value></option> <option><value>presence</value></option> <option><value>roster</value></option> <option><value>whitelist</value></option> <value>open</value> </field> <field var='pubsub#roster_groups_allowed' type='list-multi' label='Roster groups allowed to subscribe'> <option><value>friends</value></option> <option><value>courtiers</value></option> <option><value>servants</value></option> <option><value>enemies</value></option> </field> <field var='pubsub#publish_model' type='list-single' label='Specify the publisher model'> <option><value>publishers</value></option> <option><value>subscribers</value></option> <option><value>open</value></option> <value>publishers</value> </field> <field var='pubsub#purge_offline' type='boolean' label='Purge all items when the relevant publisher goes offline?'> <value>0</value> </field> <field var='pubsub#max_payload_size' type='text-single' label='Max payload size in bytes'> <value>9216</value> </field> <field var='pubsub#send_last_published_item' type='list-single' label='When to send the last published item'> <option label='Never'><value>never</value></option> <option label='When a new subscription is processed'><value>on_sub</value></option> <option label='When a new subscription is processed and whenever a subscriber comes online'> <value>on_sub_and_presence</value> </option> <value>never</value> </field> <field var='pubsub#presence_based_delivery' type='boolean' label='Deliver notifications only to available users'> <value>0</value> </field> <field var='pubsub#notification_type' type='list-single' label='Specify the delivery style for notifications'> <option><value>normal</value></option> <option><value>headline</value></option> <value>headline</value> </field> </x> </default> </pubsub> </iq>
错误场景
有很多原因可能导致缺省节点配置选项请求失败:
- 服务不支持节点配置.
- 服务不支持获取缺省节点配置.
这些错误场景的完整描述见下面章节.
不支持节点配置
如果服务不支持节点配置, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "config-node" .
例子 153. 服务不支持节点配置
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='def1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='config-node'/> </error> </iq>
不支持缺省节点配置获取
如果服务不支持获取缺省节点配置选项, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "retrieve-default" .
例子 154. 服务不支持获取缺省节点配置选项
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='def1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-default'/> </error> </iq>
删除节点
如果一个服务支持节点创建, 它必须支持节点删除. 如果一个实现持久化了条目, 它必须在节点被删除之前从持久化存储删除所有条目.
请求
为了删除一个节点, 一个节点所有者必须发送一个节点删除请求, 包含一个<delete/>元素, 其 'node' 属性指定要被删除的节点的NodeID.
例子 155. 所有者删除一个节点
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='delete1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <delete node='princely_musings'/> </pubsub> </iq>
该删除请求可以包含包含一个替换节点的URI,请求可以被重定向到那. 典型的这是一个 XMPP URI 或 IRI ,如 PubSub URIs所述, 但是它也可以是一个 HTTP URI 或任何其他形式.
例子 156. 所有者删除一个节点并重定向
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='delete1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <delete node='princely_musings'> <redirect uri='xmpp:hamlet@denmark.lit?;node=blog'/> </delete> </pubsub> </iq>
对重定向的支持在pubsub服务的部分中是可选的.
成功场景
如果没发生错误, 服务必须通知所有者成功.
例子 157. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' id='delete1'/>
另外, 该服务也必须发送节点删除的通知给所有订阅者(它应该包含未决和未配置的订阅).
例子 158. 订阅者被通知节点删除了
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <delete node='princely_musings'> <redirect uri='xmpp:hamlet@denmark.lit?;node=blog'/> </delete> </event> </message> <message from='pubsub.shakespeare.lit' to='bernardo@denmark.lit' id='bar'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <delete node='princely_musings'> <redirect uri='xmpp:hamlet@denmark.lit?;node=blog'/> </delete> </event> </message>
错误场景
有很多原因可能导致节点删除失败:
- 请求实体没有足够权限删除该节点.
- 该节点是根集合节点, 它不能被删除(见 XEP-0248 ).
- 指定的节点不存在.
这些错误场景的完整描述见下面章节.
权限不足
如果请求实体没有足够权限删除该节点(例如, 不是一个所有者), 该服务必须返回一个<forbidden/>错误.
例子 159. 实体不是一个所有者
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='delete1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
节点不存在
如果请求实体尝试删除一个不存在的节点, 服务必须返回一个<item-not-found/>错误.
例子 160. 所有者尝试删除一个不存在的节点
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='delete1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
清除所有节点条目
如果一个服务持久化了发布的条目, 一个节点所有者可能想清除该节点的所有已发布条目(也就是从持久化存储中移除所有条目, 除了最后发布的条目以外, 它可以被缓存). 实现这个特性对于一个服务是可选的.
请求
为了清除一个节点的所有条目, 一个节点所有者发送一个节点清除请求,包含一个<purge/>元素,它的 'node' 属性指定要被清理的节点的NodeID.
例子 161. 所有者从一个节点清除所有条目
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='purge1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <purge node='princely_musings'/> </pubsub> </iq>
成功场景
如果没发生错误, 服务必须清理该节点并通知所有者成功了.
例子 162. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' id='purge1'/>
如果该节点或服务已经被配置成通知订阅者条目删除了, 一个清除请求不能(MUST NOT)导致发送和删除条目时一样的通知(因为清理一个节点的很多持久化条目可能导致大量的通知); 取而代之的是, 该节点必须发送一个单独的通知给每个订阅者, 包含一个空的<purge/>子元素.
例子 163. 订阅者被通知节点清理了
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <purge node='princely_musings'/> </event> </message> <message from='pubsub.shakespeare.lit' to='bernardo@denmark.lit' id='bar'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <purge node='princely_musings'/> </event> </message>
错误场景
有很躲原因可能导致节点清理请求失败:
- 该节点或服务不支持节点清理.
- 请求实体没有足够权限清理该节点.
- 该节点未被配置成持久化条目.
- 指定的节点不存在.
这些错误的完整描述在下面章节.
不支持节点清理
如果该节点或服务不支持节点清理, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "purge-nodes" .
例子 164. 服务不支持节点清理
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='purge1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='purge-nodes'/> </error> </iq>
权限不足
如果请求实体没有足够权限清理该节点(例如, 因为它不是节点所有者), 该服务必须返回一个<forbidden/>错误.
例子 165. 实体不是一个所有者
<iq type='error' from='pubsub.shakespeare.lit' id='purge1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
节点未持久化条目
如果该服务或节点未持久化条目(例如, 因为该节点是一个集合节点, 如 XEP-0248 所述), 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "persistent-items" .
例子 166. 节点未被配置成持久化条目
<iq type='error' from='pubsub.shakespeare.lit' id='purge1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/> </error> </iq>
节点不存在
如果该节点不存在, 服务必须返回一个<item-not-found/>错误.
例子 167. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' id='purge1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
管理订阅请求
任何时候一个服务可以发送订阅批准请求给该节点的所有者. 一个订阅批准求情包含一个message节,这个节包含了一个由 "http://jabber.org/protocol/pubsub#subscribe_authorization" FORM_TYPE 限定的数据表单. 这个格式必须包含一个布尔值字段,其 'var' 属性的值为 "pubsub#allow", 这个字段指出是否允许订阅请求. 这个表单应该包含字段来指定节点标识符和未决订阅者的JID. 该message可以包含一个<body/>元素以包含自然语言文本来解释该消息所包含的一个未决订阅表单
例子 168. 服务发送授权请求给节点所有者
<message to='hamlet@denmark.lit' from='pubsub.shakespeare.lit' id='approve1'> <x xmlns='jabber:x:data' type='form'> <title>PubSub subscriber request</title> <instructions> To approve this entity's subscription request, click the OK button. To deny the request, click the cancel button. </instructions> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_authorization</value> </field> <field var='pubsub#subid' type='hidden'><value>123-abc</value></field> <field var='pubsub#node' type='text-single' label='Node ID'> <value>princely_musings</value> </field> <field var='pusub#subscriber_jid' type='jid-single' label='Subscriber Address'> <value>horatio@denmark.lit</value> </field> <field var='pubsub#allow' type='boolean' label='Allow this JID to subscribe to this pubsub node?'> <value>false</value> </field> </x> </message>
为了批准请求, 所有者将提交该表单并把 "pubsub#allow" 字段的值设为 "1" 或 "true"; 为了追踪的目的该message必须反映最初提供的 'id' 属性.
例子 169. 所有者批准订阅请求
<message from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='approve1'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_authorization</value> </field> <field var='pubsub#subid'> <value>123-abc</value> </field> <field var='pubsub#node'> <value>princely_musings</value> </field> <field var='pubsub#subscriber_jid'> <value>horatio@denmark.lit</value> </field> <field var='pubsub#allow'> <value>true</value> </field> </x> </message>
然后服务应该通知被批准的订阅者(见本文的 订阅状态变更通知).
例子 170. 订阅批准通知
<message from='pubsub.shakespeare.lit' to='horatio@denmark.lit' id='approvalnotify1'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <subscription node='princely_musings' jid='horatio@denmark.lit' subscription='subscribed'/> </event> </message>
为了禁止请求, 所有者将提交该表单并把 "pubsub#allow" 字段的值设为 "0" 或 "false"; 如上, 该message必须反映最初提供的 'id' .
例子 171. 所有者禁止订阅请求
<message from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='approve1'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_authorization</value> </field> <field var='pubsub#subid'> <value>123-abc</value> </field> <field var='pubsub#node'> <value>princely_musings</value> </field> <field var='pubsub#subscriber_jid'> <value>horatio@denmark.lit</value> </field> <field var='pubsub#allow'> <value>false</value> </field> </x> </message>
然后服务应该通知被禁止的订阅者(见本文的 订阅状态变更通知).
例子 172. 订阅 取消/禁止 通知
<message from='pubsub.shakespeare.lit' to='horatio@denmark.lit' id='unsubnotify1'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <subscription node='princely_musings' jid='horatio@denmark.lit' subscription='none'/> </event> </message>
为了取消该表单的提交, 所有者将把表单的 'type' 属性设为 "cancel" 来应答.
例子 173. 所有者取消表单提交
<message from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='approve1'> <x xmlns='jabber:x:data' type='cancel'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_authorization</value> </field> </x> </message>
服务必须检查 "pubsub#allow" 字段来看是否订阅应该被允许或禁止. 如果所有者取消数据表单, 那么该订阅请求必须保持在未决状态.
处理未决订阅请求
一个节点所有者可能想在一个服务上获取它们的节点上的所有未决订阅请求. 对于一个服务来说实现该特性是可选的.
这个特性必须使用Ad-Hoc Commands 21协议来实现, 这里的命令名称(command元素的 'node' 属性)的值必须为 "http://jabber.org/protocol/pubsub#get-pending".
请求
例子 174. 所有者请求未决的请求
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='pending1'> <command xmlns='http://jabber.org/protocol/commands' node='http://jabber.org/protocol/pubsub#get-pending' action='execute'/> </iq>
成功场景
如果没发生错误, 服务应该为管理的订阅请求返回一个数据表单, 它必须包含一个单独的字段,其 'var' 属性值为 "pubsub#node" ,其<option/>元素指定请求实体有批准权限的节点们(为了优化, 服务可以只指定那些有未决订阅请求的节点们).
例子 175. 服务以所在的数据表单应答
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='pending1'> <command xmlns='http://jabber.org/protocol/commands' sessionid='pubsub-get-pending:20031021T150901Z-600' node='http://jabber.org/protocol/pubsub#get-pending' status='executing' action='execute'> <x xmlns='jabber:x:data' type='form'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_authorization</value> </field> <field type='list-single' var='pubsub#node'> <option><value>princely_musings</value></option> <option><value>news_from_elsinore</value></option> </field> </x> </command> </iq>
错误场景
有很多原因可能导致获取未决的订阅批准请求失败:
- 服务不支持 ad-hoc commands 协议.
- 服务支持 ad-hoc commands 但是不支持 "get-pending" 特性.
- 请求实体没有足够权限批准订阅请求.
- 指定的节点不存在.
这些错误场景的完整描述见下面章节.
不支持Ad-Hoc Commands
如果服务不支持ad-hoc commands协议, 它必须应答一个<service-unavailable/>错误.
例子 176. 服务以未找到节点应答
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='pending1'> <error type='cancel'> <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
不支持Get-Pending
如果服务不支持 "get-pending" 特性, 它必须应该一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "get-pending".
例子 177. 服务以未找到节点应答
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='pending1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='get-pending'/> </error> </iq>
权限不足
如果请求实体没有足够权限批准订阅请求, 服务必须返回一个<forbidden/>错误.
例子 178. 实体没有足够权限批准订阅请求
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='pending1'> <error type='cancel'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
节点不存在
如果请求的节点不存在, 服务必须应答一个<item-not-found/>错误.
例子 179. 服务以未找到节点应答
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='pending1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
按节点请求
在接收到管理的订阅请求的数据表单之后, 接着所有者可以获取给定节点的未决订阅批准请求.
例子 180. 所有者获取某个节点的所有未决订阅请求
<iq type='set' to='pubsub.shakespeare.lit' id='pending2'> <command xmlns='http://jabber.org/protocol/commands' sessionid='pubsub-get-pending:20031021T150901Z-600' node='http://jabber.org/protocol/pubsub#get-pending' action='execute'> <x xmlns='jabber:x:data' type='submit'> <field var='pubsub#node'> <value>princely_musings</value> </field> </x> </command> </iq>
如果没发生错误, 服务将应答成功.
例子 181. 服务应答成功
<iq from='pubsub.shakespeare.lit' id='pending2' to='hamlet@denmark.lit/elsinore' type='result'> <command xmlns='http://jabber.org/protocol/commands' sessionid='pubsub-get-pending:20031021T150901Z-600' node='http://jabber.org/protocol/pubsub#get-pending' action='completed'/> </iq>
服务接着将为每个未决的订阅请求发送一个订阅批准消息, 如上文所述的每个单独的未决订阅请求.
注意: 一个服务应该遵循它的从属关系策略来维护一个未决订阅的列表. 特别是, 如果某个带有一个未决订阅的实体的从属关系被修改成所有者或发布者, 服务应该自动批准该订阅请求并把该实体从未决列表中移除之前的请求. 类似的, 如果某个带有一个未决订阅的实体的从属关系被修改成被排斥者, 服务应该自动拒绝该订阅请求并从未决列表中移除该实体之前的请求. (如果一个实体的订阅请求被禁止, 服务应该发送一个<message/>给该实体, 这个message遵循本文的订阅状态变更通知所述的格式.)
管理订阅
一个节点所有者可能想编辑和一个给定节点相关的订阅的列表. 对这个特性("pubsub#manage-subscriptions")的支持是可选的.
获取订阅列表
首先所有者获取订阅列表.
请求
为了请求一个所有订阅的列表, 节点所有者必须发送一个订阅请求, 包含一个<subscriptions/>元素,它的 'node' 属性指定相关节点的NodeID.
例子 182. 所有者请求所有订阅
<iq type='get' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='subman1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <subscriptions node='princely_musings'/> </pubsub> </iq>
成功场景
如果没发生错误, 服务必须返回那些订阅状态为 "subscribed" 或 "unconfigured" 的实体的订阅列表(它不能(MUST NOT)返回订阅状态为 "none" 的实体,不应该(SHOULD NOT)返回订阅状态为 "pending" 的实体). 结果可以为同一个实体(JID)指定多个<subscription/>元素, 但是每个元素必须拥有一个值为唯一的 'subid' 属性(如下所述).
例子 183. 服务返回订阅列表
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='subman1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <subscriptions node='princely_musings'> <subscription jid='hamlet@denmark.lit' subscription='subscribed'/> <subscription jid='polonius@denmark.lit' subscription='unconfigured'/> <subscription jid='bernardo@denmark.lit' subscription='subscribed' subid='123-abc'/> <subscription jid='bernardo@denmark.lit' subscription='subscribed' subid='004-yyy'/> </subscriptions> </pubsub> </iq>
错误场景
有很多原因可能导致管理订阅请求失败:
- 服务不支持订阅管理.
- 请求实体没有足够权限管理订阅.
- 指定的节点不存在.
这些错误场景的完整描述见下面章节.
如果一个实现不支持订阅管理, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "manage-subscriptions".
例子 184. 节点或服务不支持订阅管理
<iq type='error' from='pubsub.shakespeare.lit' id='purge1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='manage-subscriptions'/> </error> </iq>
如果请求实体不是一个节点所有者, 服务必须返回一个<forbidden/>错误.
例子 185. 实体不是一个所有者
<iq type='error' from='pubsub.shakespeare.lit' id='subman1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
如果该节点不存在, 服务必须返回一个<item-not-found/>错误.
例子 186. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' id='purge1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
修改订阅
请求
在接收到订阅列表之后, 节点所有者可以修改订阅状态. 该所有者必须只发送修改过的订阅状态(即, 一个 "delta"), 而不是完整的列表. (注意: 如果在一个修改请求中未指定 'subscription' 属性, 那么该值不能(MUST NOT)被变更.)
例子 187. 所有者修改订阅
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='subman2'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <subscriptions node='princely_musings'> <subscription jid='bard@shakespeare.lit' subscription='subscribed'/> </subscriptions> </pubsub> </iq>
成功场景
例子 188. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' id='subman2'/>
错误场景
有很多原因可能导致修改订阅请求失败:
- 服务不支持订阅管理.
- 请求实体没有足够权限管理订阅.
- 指定的节点不存在.
这些错误场景的完整描述见下面章节.
如果一个实现不支持订阅管理, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "manage-subscriptions".
’‘’例子 189. 节点或服务不支持订阅管理
<iq type='error' from='pubsub.shakespeare.lit' id='subman2'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='manage-subscriptions'/> </error> </iq>
如果请求实体不是个节点所有者, 该服务必须返回一个<forbidden/>错误.
例子 190. 实体不是一个所有者
<iq type='error' from='pubsub.shakespeare.lit' id='subman1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
如果该节点不存在, 服务必须返回一个<item-not-found/>错误.
例子 191. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' id='subman2'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
多重修改
所有者可以在一个请求中变更多个订阅. 如果该实体元素中的一个是非法的, 该服务必须返回一个带着错误条目的 IQ 错误(它应该是<not-acceptable/>), 这里返回的是原始的, 未改变过的订阅.
例子 192. 所有者为多个实体设置订阅
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='subman3'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <subscriptions node='princely_musings'> <subscription jid='polonius@denmark.lit' subscription='none'/> <subscription jid='bard@shakespeare.lit' subscription='subscribed'/> </subscriptions> </pubsub> </iq>
例子 193. 服务应答一个错误
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='subman3'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <subscriptions node='princely_musings'> <subscription jid='bard@shakespeare.lit' subscription='subscribed'/> </subscriptions> </pubsub> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
如果在一个对多个实体修改的请求中发生错误, pubsub服务必须返回任何导致该错误的<subscription/>元素(们). 返回的修改失败的实体必须包含现有的 'subscription' 属性. 任何未在一个 IQ 错误场景中返回的实体元素必须被视为已经成功修改. 所有者可以为相同实体指定多个<subscription/>元素, 但是每个元素必须拥有一个值为唯一的 'subid' 属性.
删除订阅者
为了从订阅列表移除一个实体, 所有者必须把 'subscription' 属性的值设为 "none" 且服务必须从订阅列表中移除那个实体并在未来的列表请求中不再返回它.
通知订阅者
一个实现应该通知一个实体它的订阅已经改变(见本文的订阅状态变更通知).
例子 194. 服务发送订阅变更通知
<message from='pubsub.shakespeare.lit' to='polonius@denmark.lit'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <subscription node='princely_musings' jid='polonius@denmark.lit' subscription='none'/> </event> </message>
管理从属关系
一个节点可能想管理和一个给定节点相关的实体的从属关系并为新实体设置从属关系. 对此特性("pubsub#modify-affiliations")的支持是可选的.
获取从属关系列表
首先所有者获取从属关系列表.
请求
为了请求一个所有从属的实体的列表, 一个节点所有者必须发送一个从属关系请求, 包含一个<affiliations/>元素,它的 'node' 属性指定相关节点的NodeID.
例子 195. 所有者请求所有从属的实体
<iq type='get' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='ent1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <affiliations node='princely_musings'/> </pubsub> </iq>
成功场景
如果未发生错误, 该服务必须返回从属关系为 "owner", "member", "publisher", "publish-only", 或 "outcast" 的实体的列表(它不能(MUST NOT)返回从属关系为 "none" 的实体).
例子 196. 服务返回从属的实体的列表
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='ent1'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <affiliations node='princely_musings'> <affiliation jid='hamlet@denmark.lit' affiliation='owner'/> <affiliation jid='polonius@denmark.lit' affiliation='outcast'/> </affiliations> </pubsub> </iq>
错误场景
有很多原因可能导致从属实体请求失败:
- 服务不支持从属关系修改.
- 请求实体没有足够权限修改从属关系.
- 指定的节点不存在.
这些错误场景的完整描述见下面章节.
如果一个实现不支持从属关系修改, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "modify-affiliations".
例子 197. 节点或服务不支持从属关系管理
<iq type='error' from='pubsub.shakespeare.lit' id='ent1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='modify-affiliations'/> </error> </iq>
如果请求实体不是一个节点所有者, 服务必须返回一个<forbidden/>错误.
例子 198. 实体不是一个所有者
<iq type='error' from='pubsub.shakespeare.lit' id='ent1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
如果该节点不存在, 该服务必须返回一个<item-not-found/>错误.
例子 199. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' id='ent1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
修改从属关系
一个节点所有者可能想编辑和一个给定节点相关的一个实体的从属关系或为一个新实体设置从属关系.
请求
为了修改一个从属关系, 一个节点所有者必须发送一个 IQ set , 包含要修改的从属关系或从属关系们. 该所有者必须只发送要修改的从属关系(即, 一个 "delta"), 而不是完整的列表 (注意: 如果在一个修改请求中未指定 'affiliation' 属性, 那么其值不能(MUST NOT)被修改.)
例子 200. 所有者修改从属关系
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='ent2'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <affiliations node='princely_musings'> <affiliation jid='bard@shakespeare.lit' affiliation='publisher'/> </affiliations> </pubsub> </iq>
成功场景
例子 201. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' id='ent2'/>
错误场景
有很多原因可能导致修改从属关系请求失败:
- 节点或服务不支持请求的从属关系.
- 服务不支持从属关系修改.
- 请求实体没有足够权限来修改从属关系.
- 指定的节点不存在.
这些错误场景的完整描述在下面章节.
如果一个实现不支持从属关系修改, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "modify-affiliations".
例子 202. 节点或服务不支持从属关系管理
<iq type='error' from='pubsub.shakespeare.lit' id='ent1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='modify-affiliations'/> </error> </iq>
如果节点或服务不支持请求的从属关系, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和特性 "member-affiliation", "outcast-affiliation", "publisher-affiliation", 或 "publish-only-affiliation" 之一的适当特性.
例子 203. 节点或服务不支持请求的从属关系
<iq type='error' from='pubsub.shakespeare.lit' id='ent1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='member-affiliation'/> </error> </iq>
如果请求实体不是节点所有者, 服务必须返回一个<forbidden/>错误.
例子 204. 实体不是一个所有者
<iq type='error' from='pubsub.shakespeare.lit' id='ent1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
如果该节点不存在, 服务必须返回一个<item-not-found/>错误.
例子 205. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' id='ent1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
多重修改
所有者可以在一个单独的请求中变更多个从属关系. 如果指定的实体元素之一是非法的, 服务必须返回一个 IQ error (它应该是<not-acceptable/>) 并携带该非法的条目, 这里返回的从属关系是原始的, 未改变的从属关系.
下面的例子展示一个实体尝试让一些从属关系为 "owner" 的实体变成其他从属关系, 如果只有一个所有者那么这个动作是不能(MUST NOT)被允许的.
例子 206. 所有者为多个实体设置从属关系
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='ent3'> <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> <affiliations node='princely_musings'> <affiliation jid='hamlet@denmark.lit' affiliation='none'/> <affiliation jid='polonius@denmark.lit' affiliation='none'/> <affiliation jid='bard@shakespeare.lit' affiliation='publisher'/> </affiliations> </pubsub> </iq>
例子 207. 服务应答一个错误
<iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' id='ent3'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
在本文开始的状态图是一组 必须实现 的规则,用于检查可能的状态变迁. 实现可以强制其他(更严格的)规则. 如果在一个为多个实体做修改的请求期间发生错误, pubsub错误必须返回任何导致错误的<affiliation/>元素(们). 返回修改失败的实体必须包含现有的 'affiliation' 属性. 在一个 IQ error 中未返回的任何实体元素必须被视为成功地修改了. 所有者不能(MUST NOT)为相同实体指定多个<affiliation/>元素; 否则服务必须返回一个<bad-request/>错误.
删除实体
为了从从属关系列表中删除一个实体, 所有者必须把 'affiliation' 属性的值设为 "none" 并且服务必须从从属关系列表中移除那个实体且在将来的列表请求中不应答它.
通知实体
一个实现可以发送一个事件通知给一个从属关系改变了的实体, 它可以包含一个<body/>元素来指定关于从属关系变更的自然语言文本且它应该包含修改过的从属关系数据.
例子 208. 服务发送从属关系变更通知
<message from='pubsub.shakespeare.lit' to='polonius@denmark.lit'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations node='princely_musings'> <affilation jid='polonius@denmark.lit' affiliation='none'/> </affiliations> </pubsub> </message>
IM帐号集成
发布-订阅 功能能被集成到现有的即时消息和出席信息服务中(见 RFC 3921), 这样每个注册的帐号相当于一个虚拟的pubsub服务(有时候称为 "pubsub-on-a-JID"). 在这类部署中, 每个pubsub服务的根pubsub节点有和该帐号的纯JID(<localpart@domain.tld> 或 <domain.tld>)相同的地址, 它典型地和一个IM用户(例如, <hamlet@denmark.lit>)关联. 因为一个IM用户典型地有一个名册 "好友" 并分享出席信息给那些好友, 该虚拟pubsub服务能使用名册和出席信息来为订阅者提供一些有用的快捷方式, 特别是本章所描述的自动订阅和过滤通知特性.
注意: PEP把接收者的PEP通知捆绑到订阅者的出席信息, 但没把PEP通知的生成捆绑到发布者的出席信息. 如果发布者希望在结束它的出席信息会话之前停止生成PEP事件(或生成一个 "空" 事件, 好像某些PEP载荷能做的那样), 该发布者必须直接引导它的客户端去这样做而不能(MUST NOT)依赖于当PEP服务从发布者接收到unavailable出席信息时PEP服务自动 "清零" 它的PEP信息.
如果一个即时消息和出席信息帐号也是一个虚拟pubsub服务, 来自该帐号的纯JID对服务查询信息("disco#info")的应答必须包含一个特性 "http:/jabber.org/protocol/pubsub#pubsub-on-a-jid":
例子 209. IM服务器代表IM帐号返回支持的特性
<iq from='hamlet@denmark.lit' id='bvg194j7' to='francisco@denmark.lit/barracks' type='result'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='account' type='registered'/> <feature var='http://jabber.org/protocol/pubsub#pubsub-on-a-jid'/> </query> </iq>
注意: 因为对于一个客户端生成的任何节来说,帐号所有者的纯JID是缺省目的地址, 客户端经常在这类节中省略 "to" 属性; 在这一点上, 参见 RFC 6120 和 (关于名册) RFC 6121.
自动订阅
当一个联系人通过分享XMPP出席信息从属于帐号所有者, "auto-subscribe" 特性极大地简化了订阅过程. 特别是, 对 "auto-subscribe" 的支持有以下的内涵:
帐号所有者
因为帐号所有者本身隐性地订阅了它自己的XMPP出席信息(例如, 每个XMPP资源从该帐号所有者的所有资源接收出席信息), 一个服务必须认为该帐号所有者有一个对该帐号所有者的根集合节点的pubsub订阅,该订阅的订阅类型为 "items" 且订阅层次为 "all". 这一点对所有访问模式都是对的.
出席信息订阅者
在节点处于以下几种访问模式下,如果一个实体(即, 一个IM帐号)对某个帐号所有者的纯JID(<localpart@domain.tld> 或 <domain.tld>)有一个XMPP出席信息订阅, 服务必须认为该联系人有一个对该帐号所有者的根集合节点的订阅类型为 "items" 且订阅层次为 "all" 的pubsub订阅:
- 该节点的访问模式为 "open".
- 该节点的访问模式为 "presence".
- 该节点的访问模式为 "roster" 且联系人在指定的名册组.
如果该联系人没有权限从任何帐号所有者的根集合节点之下的特定节点接收信息(例如, 因为一个特定节点的访问模式是 "roster" 但是该联系人不在指定的名册组中), 该服务不能(MUST NOT)发送关于那个节点的通知给该联系人并且也不能(MUST NOT)返回任何关于那个节点存在一个潜在的订阅的错误给该联系人(例如, 当联系人发送出席信息给该帐号所有者的时候,服务不能(MUST NOT)返回一个pubsub订阅错误给该联系人).
注意: 当一个IM联系人对帐号所有者有一个订阅, 自动的pubsub订阅必须基于包含在该出席信息订阅请求的 'from' 地址的纯JID, 它对于一个IM联系人将是一个纯JID (<localpart@domain.tld> 或 <domain.tld>).
出席信息分享者
如果该节点有一个开放访问模式, 如果某实体在没有出席信息订阅的情况下曾经发送了出席信息给该帐号所有者,pubsub服务应该也认为该实体临时或隐性地订阅了该节点. 在这种情况下, 该订阅应该基于该出席信息节的 'from' 地址, 它将是一个全JID (<localpart@domain.tld/resource> 或 <domain.tld/resource>). 当该服务从该全JID接收到unavailable出席信息, 它必须取消该临时订阅.
过滤通知
一个联系人可能不想接收某个用户的虚拟pubsub服务上所有节点的通知. 一个联系人应该标记它的参数给该帐号所有者的服务器,通过包含 XEP-0115 信息,指定该联系人希望接收到通知的节点(如果有的话)的NodeIDs. 这个信息被一个pubsub服务用于支持 "filtered-notifications" 特性,只从那些和订阅者的参数匹配的NodeIDs发送通知.
为了让这变为可能, 所有可能的NodeIDs能被加上字符串 "+notify" 以指示该联系人希望从该指定的NodeID接收到通知. 因而如果Romeo想接收到的定位数据的通知(User Geolocation 22) 和 歌曲数据 (User Tune 23)而不是活动数据(User Activity 24), 它的客户端在它发送的对 disco#info 的应答结果中将声明支持如下字符串: 25
这组字符串接着将被声明,通过把它们包含到 标识符+特性 哈希封装到 'ver' 属性里,如 XEP-0115 所述.
例子 210. 联系人携带caps发送出席信息
<presence from='romeo@montague.lit/orchard'> <c xmlns='http://jabber.org/protocol/caps' node='http://www.chatopus.com/#2.2' ver='AFBT0mPr29zQE5aGtCJp97CIS6E='/> </presence>
缓存 XEP-0115 信息是该帐号所有者的服务器的责任. 当该服务器从一个联系人接收出席信息, 它必须检查那个出席信息的实体能力数据并为该联系人的客户端把那个数据和期望的NodeIDs关联起来. 该服务器不能(MUST NOT)发送和该联系人未曾通过 "NodeID+notify" 的disco#info特性请求的任何NodeIDs相关的通知. 这使得一个客户端能关闭所有通知(例如, 因为带宽限制)并容易地接收所有期望的数据格式,只要简单地在它的 disco#info 结果中增加适当的 "NodeID+notify" 支持和客户端能力的支持. 无论如何, 它也暗示一个客户端只能在一个全局基础上请求通知而不能请求, 比如说, 只从在该用户的名册中的特定联系人请求心情信息. 社区一致统一这是一个可接受的折衷. 而且, 注意这只在如果帐号所有者对联系人有一个出席信息订阅且该联系人也有对该帐号所有者有一个出席信息订阅的时候发生.
一些例子可能有助于演示通知过滤的概念. 这里我们显示由以上列出的联系人中的两个生成的出席信息(benvolio@montague.lit 和 juliet@capulet.lit 之间没有任何订阅和被订阅出席信息,所以没有被纳入这些协议流程).
例子 211. 带caps的出席信息
<presence from='nurse@capulet.lit/chamber'> <c xmlns='http://jabber.org/protocol/caps' node='http://exodus.jabberstudio.org/#0.9.1' ver='wXj6c5xhT9frdqhvTSjkdejUUP8='/> </presence> <presence from='romeo@montague.lit/orchard'> <c xmlns='http://jabber.org/protocol/caps' node='http://www.chatopus.com/#2.2' ver='1FDrLLbYMpzvcI95jgSHABSWDRY='/> </presence>
我们假定Juliet的服务器不知道任何这些能力, 所以它代表Juliet本身发送服务查询信息请求给每个客户端(实际上, capulet.lit服务器将很快建立一个客户端能力的缓存, 有了这个结果,它将不需要发送这些服务查询请求):
例子 212. 帐号服务器查询联系人
<iq from='juliet@capulet.lit' to='nurse@capulet.lit/chamber' type='get' id='disco123'> <query xmlns='http://jabber.org/protocol/disco#info'/> </iq> <iq from='nurse@capulet.lit/chamber' to='juliet@capulet.lit' type='result' id='disco123'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='client' type='pc'/> <feature var='http://jabber.org/protocol/activity'/> <feature var='http://jabber.org/protocol/activity+notify'/> <feature var='http://jabber.org/protocol/geoloc'/> <feature var='http://jabber.org/protocol/geoloc+notify'/> <feature var='http://jabber.org/protocol/muc'/> <feature var='http://jabber.org/protocol/tune'/> <feature var='http://jabber.org/protocol/tune+notify'/> </query> </iq>
该服务器也将查询为<romeo@montague.lit> identity+features :
例子 213. 帐号服务器查询联系人
<iq from='juliet@capulet.lit' to='romeo@montague.lit/orchard' type='get' id='disco234'> <query xmlns='http://jabber.org/protocol/disco#info'/> </iq> <iq from='romeo@montague.lit/orchard' to='juliet@capulet.lit' type='result' id='disco234'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='client' type='pda'/> <feature var='http://jabber.org/protocol/geoloc'/> <feature var='http://jabber.org/protocol/geoloc+notify'/> <feature var='http://jabber.org/protocol/tune'/> <feature var='http://jabber.org/protocol/tune+notify'/> </query> </iq>
(如 XEP-0115 提到的, 该服务器必须使用生成的方法来检查 'ver' 属性提供的哈希值以确保未中毒.)
现在我们回到帐号所有者的发布和通知的服务器生成, 因为服务器有caps信息,过滤被激活了:
- 如果Juliet发布一个tune条目给 presence-access "http://jabber.org/protocol/tune" 节点, 她的服务器将发送通知给<nurse@capulet.lit/chamber>和<romeo@montague.lit/orchard>(全JIDs).
- 如果Juliet发布一个activity条目给 presence-access "http://jabber.org/protocol/activity" 节点, 她的服务器将只发送通知给<nurse@capulet.lit/chamber>.
- 如果Juliet发布一个geolocation条目给 roster-access "http://jabber.org/protocol/geoloc" 节点,并带着 "pubsub#roster_groups_allowed" 变量,其值为 "Friends", 她的服务器将只发送通知给<romeo@montague.lit/orchard>,因为nurse不在那个名册组中.
特性汇总
本章汇总这里描述的特性, 为每个特性指定了适当的要求(REQUIRED, RECOMMENDED, 或 OPTIONAL), 并给描述的每个特性和本文的章节提供了横向参考.
注意: 特性名称的格式都是 "http://jabber.org/protocol/pubsub#name", 这里 "name" 是在第一行下面指定的文本.
表 7: 服务查询特性
名称 | 描述 | 支持 | 章节 |
---|---|---|---|
access-authorize | 缺省访问模式为 "authorize" (授权). | 可选 | 节点访问模式 |
access-open | 缺省访问模式为 "open" (公开). | 可选 | 节点访问模式 |
access-presence | 缺省访问模式为 "presence" (出席信息). | 可选 | 节点访问模式 |
access-roster | 缺省访问模式为 "roster" (名册). | 可选 | 节点访问模式 |
access-whitelist | 缺省访问模式为 "whitelist" . | 可选 | 节点访问模式 |
auto-create | 服务支持在对不存在的节点发布的时候自动创建节点 | 可选 | 自动创建节点 |
auto-subscribe | 服务支持基于出席信息订阅自动订阅到一个节点. | 推荐 | 自动订阅 |
collections | 支持集合节点. | 可选 | 参考 XEP-0248 |
config-node | 支持节点选项配置. | 推荐 | 配置节点 |
create-and-configure | 支持同时创建和配置节点. | 推荐 | 创建并配置节点 |
create-nodes | 支持i节点创建. | 推荐 | 创建节点 |
delete-items | 支持删除条目. | 推荐 | 从节点删除条目 |
delete-nodes | 支持删除节点. | 推荐 | 删除节点 |
filtered-notifications | 基于实体能力数据过滤通知. | 推荐 | 过滤通知 |
get-pending | 支持获取未决的订阅批准. | 可选 | 管理订阅请求 |
instant-nodes | 支持创建即时节点. | 推荐 | 创建节点 |
item-ids | 发布者可以指定条目标识符. | 推荐 | |
last-published | 缺省的,最后发布的条目会被发送给新订阅者和现有的订阅者中有可用的出席信息的接收者. | 推荐 | 事件类型 |
leased-subscription | 支持基于时间的订阅. | 可选 | 基于时间的订阅(租用) |
manage-subscriptions | 节点所有者可以管理订阅. | 可选 | 管理订阅 |
member-affiliation | 支持成员从属关系. | 推荐 | 从属关系 |
meta-data | 支持节点元数据. | 推荐 | |
modify-affiliations | 节点所有者可以修改从属关系. | 可选 | 管理从属关系 |
multi-collection | 一个的难度的叶子节点可以被关联到多个集合. | 可选 | 参考 XEP-0248 |
multi-subscribe | 一个单独实体可以多次订阅一个节点. | 可选 | 多重订阅 |
outcast-affiliation | 支持被排斥者从属关系. | 推荐 | 从属关系 |
persistent-items | 支持持久化条目. | 推荐 | |
presence-notifications | 支持基于出席信息递送事件通知. | 可选 | |
presence-subscribe | 授权的联系人自动订阅到一个用户的虚拟pubsub服务. | 推荐 | 自动订阅 |
publish | 支持发布条目. | 必需 | 发布条目到节点 |
publish-options | 支持带选项发布一个条目. | 可选 | 发布选项 |
publish-only-affiliation | 支持 仅发布 从属关系. | 可选 | 从属关系 |
publisher-affiliation | 支持发布者从属关系. | 推荐 | 从属关系 |
purge-nodes | 支持清理节点. | 可选 | 清除所有节点条目 |
retract-items | 支持条目收回. | 可选 | 从节点删除条目 |
retrieve-affiliations | 支持获取当前从属关系. | 推荐 | 获取从属关系 |
retrieve-default | 支持获取缺省节点配置. | 推荐 | 请求缺省节点配置选项 |
retrieve-default-sub | 支持获取缺省订阅配置. | 可选 | 请求缺省订阅配置选项 |
retrieve-items | 支持获取条目. | 推荐 | 从节点获取条目 |
retrieve-subscriptions | 支持获取当前订阅. | 推荐 | 获取订阅 |
subscribe | 支持订阅和取消订阅. | 必需 | 订阅节点 和 从节点取消订阅 |
subscription-options | 支持订阅选项的配置. | 可选 | 配置订阅选项 |
subscription-notifications | 支持订阅状态变更通知. | 可选 | 订阅状态变更通知 |
错误条件
表 8: 错误条件和典型原因
条件 | 描述 |
---|---|
<conflict/> | 节点已存在. |
<feature-not-implemented/> | 对一个节点(或系统)尝试操作失败,因为服务或节点不支持该操作; 该错误也应该指定不支持哪个特性. |
<forbidden/> | 一个实体没有足够权限来执行该动作, 请求对另一个Jabber ID的操作(例如, francisco@denmark.lit 尝试订阅 bernardo@denmark.lit 到一个节点), 或请求实体的从属关系为被排斥者 "outcast". |
<item-not-found/> | 一些操作所指定的节点或条目不存在. |
<not-allowed/> | 一个实体尝试执行一个服务实现的动作; 无论如何服务范围的管理员或节点所有者已经禁止那个服务或节点的这个动作. |
<not-authorized/> | 一个实体尝试从一个节点订阅或获取条目但是未被授权查看该帐号所有者的出席信息, 不在适当的名册组中, 或不在订阅白名单上. |
<payment-required/> | 订阅和条目获取是基于某种付费服务的. 付费将在带外完成,使用某些事先商定的方法(不在此定义). |
<registration-required/> | 实体在被允许创建节点之前被要求注册. |
实现备注
通知触发
有很多中可能的触发器用来为当前发布的条目或最后发布的条目发送一个事件通知给一个实体, 总结如下:
- 实体显式地从节点请求一个或多个条目并被授权获取条目们; 当服务接收到这样一个请求, 它发送条目们到该实体.
- 实体是某节点的授权订阅者(显式地通过订阅或隐性地基于一个所有者或发布者角色); 当发布者发送一个发布请求, 服务发送当前发布的条目给该实体(遵循适当的出席信息检查和通知过滤).
- 实体未订阅但是可以选择这么做,即曾经发送包含适当实体能力数据的出席信息给一个支持过滤通知的服务(基于一个对通知感兴趣的表达有效地建立一个 "临时订阅" ); 当服务首次接收到这样的出席信息, 它发送最后发布的条目给该实体(只在第一次接收到这样的出席信息之后发送它, 在收到随后的包含同样的通知兴趣的出席信息更新时不再发送).
- 实体未订阅但是可以选择这么做,即曾经发送包含适当实体能力数据的出席信息给一个支持过滤通知的服务(有效建立一个 "临时订阅"); 当发布者发送一个发布一个和该实体表达的通知兴趣匹配的请求, 该服务发送当前发布的条目给该实体.
- 实体获得对该节点的访问,因为节点访问模式的改变; 结果是, 该服务发送最后发布的条目给该实体.
- 实体被加入到和节点访问模式 "roster" 关联的名册组; 结果是, 该服务发送最后发布的条目给该实体.
期望的通知接收者
当一个pubsub服务生成了通知, 它必须遵循隐含在每个订阅者的订阅选项配置中的递送规则. 特别是, 'to' 地址应该只能是订阅的JID. 服务不应(SHOULD NOT)尝试猜测和订阅JID关联的最可用的资源(例如, 在即时消息系统上下文中).
通知相关的错误处理
如上面提到的, 一个pubsub服务应该确保它为每个事件通知生成的<message/>节拥有一个有值的 'id' 属性. (这个通知ID不能和节点ID或条目ID混淆.) 这个ID在该pubsub服务上下文中必须是唯一的以确保跟踪任何递送相关的错误.
服务如何准确地处理递送相关的错误将是一个实现的问题. 通常, 这类处理的效果类似其他消息递送系统执行的弹回操作, 类似邮件传送代理和邮件列表软件. 接下来是一些关于处理和pubsub事件通知相关的XMPP特有错误条件的建议指南(见 RFC 3920 和 XEP-0086 中关于XMPP错误条件的语义):
- 如果该XMPP错误的类型是 "cancel" (例如, <item-not-found/>), 或该错误条件是<gone/>, pubsub服务应该终止该实体对那个节点的订阅并可以终止那个实体对所有位于该服务上的节点的订阅.
- 如果该XMPP错误的类型是 "auth" (例如, <registration-required/>) 或 "wait" (例如, <remote-server-timeout/>), 或错误条件是<bad-request/>, <redirect/>, 或 <not-acceptable/>, 该pubsub服务应该为那个实体增加一个弹回计数并可以在一个配置的时间段之后重新发送该事件通知. 该服务可以终止该实体对那个节点的订阅,如果弹回计数达到一个配置的限制.
临时订阅
一个实现可以让一个实体能临时订阅到一个节点, 即, 只在订阅者在线的这个当前出席信息会话中订阅. 要临时订阅, 订阅者必须把 "pubsub#expire" 订阅配置选项设为一个 "presence" 的文字值.
例子 214. 一个临时订阅
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='lease3'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> ... <field var='pubsub#expire'><value>presence</value></field> ... </x> </options> </pubsub> </iq>
该服务接着将在它从订阅者那里接收到类型为 "unavailable" 的出席信息的时候自动取消订阅.
一个实现可以让节点所有者能强制所有订阅都是临时的, 它对于那些也被配置成使用基于出席信息递送的节点是有用的. 这个设定使用 "pubsub#tempsub" 节点配置选项,值设为true.
例子 215. 所有者把所有订阅设为临时
<iq type='set' from='hamlet@denmark.lit/elsinore' to='pubsub.shakespeare.lit' id='configtemp'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <configure node='princely_musings'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#node_config</value> </field> <field var='pubsub#tempsub'><value>true</value></field> </x> </configure> </pubsub> </iq>
事件基于出席信息递送
pubsub实现可以只在订阅者在线的时候递送事件通知. 在这些情况下, 选项可以是一个上面例子中展示的节点配置选项. 为了便于实现这个目的, 该pubsub服务需要订阅该订阅者的出席信息并在发送任何事件通知之前检查订阅者的当前出席信息(如 RFC 3921 所述). 出席信息订阅必须基于该订阅的JID.
不路由事件到离线存储
发送事件给现有的Jabber服务器的用户可以强制事件通知被路由到离线存储中用于晚些时候递送(如离线消息处理最佳实践] 27). 这可能不总是必要的. 阻止这个行为的可能的方法包括:
包含消息主体
如果一个服务理解一个特定载荷类型的语义且一个实体的订阅是配置成这样的(把 "pubsub#include_body" 订阅选项设为true), 该服务应该在它为给定节点发送的事件通知的载荷中包含一个适当的 XMPP <body/> 子元素, 这里 body 的XML字符串数据汇总或展现了包含在该载荷中的信息(这使那些不理解该载荷格式的客户端能展示适当的信息给一个终端用户). 例如, Atom <summary/> 元素(见 RFC 4287 )可以被映射成 XMPP <body/> 元素. 一个服务不能(MUST NOT)为一个节点提供 "pubsub#include_body" 订阅选项,如果它没有一个已定义的方法来把部分或全部载荷格式转换成一个合理的消息主体. 一个节点所有者可以为把载荷格式转换成一个消息主体定义一个XSLT, 通过 "pubsub#body_xslt" 节点配置选项. 这个XSLT由pubsub服务在接收到一个发布请求之后和发送适当的通知之前应用, 不是由客户端在发送一个发布请求之前应用.
如果服务不理解一个特定载荷类型的语义,因而不能把该载荷转换成一个自然人可读的消息主体, 它不应(SHOULD NOT)包含一个<body/>元素.
如果一个订阅者对同一个节点有多个订阅, 其中的一些 SubIDs 把 include_body 设为 true 而其他一些则把 include_body 设为 false, 该服务应该在所有通知中包含一个主体.
节点ID和条目ID唯一性
在一个特定的pubsub服务上下文中, NodeIDs必须被视为唯一的标识符.
如果使用了条目标识符, 在该节点的范围内它们必须被视为唯一的. NodeID + ItemID 的组合在一个给定的服务中必须是唯一的, 且必须在一个单独的节点指定一个单独的已发布条目.
如果一个发布者发布了一个条目且ItemID和一个现有的条目吻合, 该pubsub服务必须覆写现有的条目并生成新的事件通知.
因为有可能一个节点的配置被改变了之后要求使用ItemIDs(例如, 从临时的改成持久的), 服务应该为了内部跟踪的目的使用ItemIDs, 即使它在配置变更之前生成的通知没有包含ItemIDs.
条目缓存
一个服务可以缓存最后发布到节点的条目, 即使该节点被配置成用于临时发布(即, 配置成非持久化条目). 最后发布的条目应该在成功处理订阅请求或由一个节点批准之后被发送给最新的订阅者.
注意: 通用的 发布-订阅 协议的特定范本可以对于 "cache-last-item" 特性定义更严格的要求.
批处理
一个发布者可以在一个发布请求中包含多个<item/>元素以及在一个收回请求中包含多个<item/>元素. 这就是发布或收回的 "批处理" .
如果服务不能处理发布或收回的条目中的任何一个, 整个批处理必须失败且服务不能(MUST NOT)发布或收回任何条目.
如果一个批量发布包含如此多的条目以至于所有条目的发布将超出该节点的最大数量限制, 该服务必须返回一个<not-allowed/>错误, 它应该也包含一个pubsub特有的错误条件<max-items-exceeded/>.
注意: 批量发布造成了 "最后发布的条目" 概念的问题; 所以, 如果需要信息一致性, 一个发布者应该在分开的请求中发布条目而不是用批处理模式.
自动订阅的所有者和发布者
一个服务必须允许所有者和发布者订阅一个节点, 以及从一个节点获取条目,即使他们没有订阅. 服务可以自动订阅所有者和发布者,如果他们未曾订阅, 在这种情况下如果对订阅是必要的话它应该生成一个订阅ID,并且应该发送一个成功订阅的通知,如本文的订阅状态变更通知所述.
授权订阅请求(未决订阅者)
订阅请求如何被发送给节点所有者是一个实现的问题. 可能包括:
- 发送请求给全部所有者(这些可以被放到离线存储中,如 XEP-0160 所述)并且第一个批准的人做主.
- 服务将订阅所有者的出席信息, 且只发送给在线的所有者.
- 全部所有者投票决定新订阅者.
- 允许任何所有者否决该订阅者.
一个实现可以使用这些方法中的任何一个, 或一些这里没定义的方法.
订阅状态变更通知
各种动作和事件可以导致一个订阅状态的变更:
- 批准或拒绝一个订阅请求,如管理订阅请求用例所述
- 撤销一个现有的订阅, 它有很多可能的触发器 "triggers" :
- 实体简单地取消订阅该节点
- 节点类型是 "presence" 且潜在的出席信息订阅被撤销
- 节点类型是 "roster" 且该实体被移动到未授权的名册组
当发生了一个订阅状态变更, 一个服务应该发送一个消息给这个(新的, 之前的, 或拒绝的)订阅者以向它通知这个变更, 这里该消息包含一个<event/>元素,并带有一个单独的<subscription/>子元素来指定该节点, JID, 和订阅状态. 该通知可以包含一个<body/>元素来指定关于该订阅变更的自然语言文本. 服务对其发送通知的那个JID的地址就是该订阅请求中的 'jid' 属性所设置的那个.
例子 216. 订阅批准通知
<message from='pubsub.shakespeare.lit' to='horatio@denmark.lit' id='approvalnotify1'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <subscription node='princely_musings' jid='horatio@denmark.lit' subscription='subscribed'/> </event> </message>
例子 217. 订阅 撤销/拒绝 通知
<message from='pubsub.shakespeare.lit' to='horatio@denmark.lit' id='unsubnotify1'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <subscription node='princely_musings' jid='horatio@denmark.lit' subscription='none'/> </event> </message>
如果服务有该订阅者(之前的或拒绝的)的出席信息的知识, 它应该发送消息给该订阅者的所有资源; 如果没有, 它必须发送该消息给该订阅者的从属的JID.
如果一个服务或节点支持这个特性, 它必须在它对服务查询信息请求的应答中返回一个特性 "subscription-notifications" .
NodeID语义
NodeIDs在pubsub的特定的范本,实现,或部署中可以有语义. 无论如何, 强烈建议这类语义不被用于封装节点的层次结构; 反之, 节点层次应该使用集合节点和它们的关联子节点来封装,如 XEP-0248 所述.
包含SHIM头
当使用了SubIDs的时候, 节头和互联网元数据(SHIM) 头被包含在里面以区分被发送的关联到特定订阅的不同通知. 相关用例和场景为:
SHIM头是由订阅者订阅了的那个节点生成的, 它可以是一个叶子节点或一个集合节点(参考 XEP-0248 ).
当内容不是基于订阅ID来区分的时候,SHIM头不会被包含, 例如, 当一个节点发送配置变更通知, 该节点已经被清理的通知, 或该节点被删除的通知给该节点本身.
和生成实体关联的事件和载荷
一个实现可以让节点配置能指定事件通知和实体之间的用于发布信息的关联, 但是这样一个特性是可选的. 这里是一些可能的例子:
因此我们定义了 "itemreply" 节点配置选项, 有两个可能的值:
- "owner" (即, 节点所有者或节点所有者的别名)
- "publisher" (即, 该条目的发布者)
一个节点所有者不能(MUST NOT)定义多于一个这类选项.
一个例子如下.
例子 218. 带发布者JID的事件通知
<message from='pubsub.shakespeare.lit' to='bassanio@merchantofvenice.lit'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='n48ad4fj78zn38st734'> <item id='i1s2d3f4g5h6bjeh936' publisher='portia@merchantofvenice.lit'> <geoloc xmlns='http://jabber.org/protocol/geoloc'> <description>Venice</description> <lat>45.44</lat> <lon>12.33</lon> </geoloc> </item> </items> </event> </message>
作为一种选择, 如果一个服务实现了本协议的个人事件子集, 该虚拟pubsub服务是该帐号所有者的纯JID且通知被从那个JID发送; 细节参考 XEP-0163 .
链接
单词 "chaining" 指的是订阅一个节点到另一个节点的实践. 比如, 考虑一个场景,节点<pubsub@example.net/NewsBroadcaster>想分发信息从<pubsub.example.com>上的节点 "NewsFeed" 收到的信息. 因为理论上对于<pubsub@example.net/NewsBroadcaster>来说直接订阅NewsFeed节点是有可能的(因为之前的节点被直接寻址为一个JID), 实现不能(MUST NOT)以这种方式链接节点. 反之, 实现必须从该pubsub服务而不是该节点的地址来订阅(在这里展示的例子中, 该订阅将被从<pubsub@example.net>而不是<pubsub@example.net/NewsBroadcaster>)发送.
基于时间的订阅(租用)
在一些系统中可能想要提供一个订阅 "leasing" 特性以让旧的或不新鲜的订阅超时失效. 租用能用可配置的订阅选项来实现; 具体来说, 当一个实体订阅了, 该服务将要求订阅选项配置且配置格式将包含一个字段 "pubsub#expire". 这个字段必须包含一个dateTime(定义于XMPP日期和事件范本 30).
租用过程如下所示.
例子 219. 租用过程
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='lease1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscribe node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='lease1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='unconfigured'> <subscribe-options> <required/> </subscribe-options> </subscription> </pubsub> </iq> <iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='lease2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='lease2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='form'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> ... <field var='pubsub#expire' type='text-single' label='Requested lease period'/> ... </x> </options> </pubsub> </iq> <iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='lease3'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> ... <field var='pubsub#expire'><value>2006-02-28T11:59Z</value></field> ... </x> </options> </pubsub> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='lease3'/>
当该租用快要结束的时候(例如, 在该租约结束之前24小时),该服务可以发送一个消息给该订阅者. 要做到这必须发送一个包含了一个由 'http://jabber.org/protocol/pubsub#event' 命名空间限定的<subscription/>元素的<message/>,这个<subscription/>元素必须包含一个 'expiry' 属性.
例子 220. 服务通知订阅者租约快到期了
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <subscription expiry='2006-02-28T23:59Z' jid='francisco@denmark.lit' node='princely_musings' subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3' subscription='subscribed'/> </event> </message>
当该订阅者想更新租用, 它将获得当前的订阅选项, 修改 "pubsub#expire" 字段的值, 并提交该新订阅选项给回服务. 如果新的过期值超过了订阅租用允许的最大值, 该服务必须把该字段的值改为当前的 date/time 加上最大允许租用时间.
例子 221. 更新一个租用
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='renew1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='renew1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='form'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> ... <field var='pubsub#expire' type='text-single' label='Requested lease period'/> ... </x> </options> </pubsub> </iq> <iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='renew2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> ... <field var='pubsub#expire'><value>2006-03-31T23:59Z</value></field> ... </x> </options> </pubsub> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='renew2'/>
基于内容的Pubsub系统
一个服务可以让实体能订阅节点并应用一个过滤器到通知(例如, 关键字匹配,类似 "从Slashdot发送所有匹配术语 'Jabber' 的新闻条目给我"). 这样一个基于内容的服务应该允许一个实体多次订阅相同的节点并且, 如果是这样, 必须用订阅标识符(SubIDs)来区分多个订阅. 为了防止冲突, 一个支持基于内容订阅的使用SubIDs的服务应该代表订阅者生成SubIDs而不是允许订阅者设置他们自己的SubIDs. 31
基于内容的服务应该使用订阅选项来指定被应用的过滤器(们). 因为有很多可能的过滤机制(其中一些可以是应用特有的), 本文不订阅任何这类放大. 无论如何, 过滤机制可以被定义在独立的协议中.
下面是一个用于基于内容的pubsub的订阅选项配置过程的虚构的例子.
例子 222. 一个基于内容的订阅
<iq type='set' from='bard@shakespeare.lit/globe' to='pubsub.shakespeare.lit' id='filter1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscribe node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='bard@shakespeare.lit/globe' id='filter1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='bard@shakespeare.lit' subid='991d7fd1616fd041015064133cd097a10030819e' subscription='unconfigured'> <subscribe-options> <required/> </subscribe-options> </subscription> </pubsub> </iq> <iq type='get' from='bard@shakespeare.lit/globe' to='pubsub.shakespeare.lit' id='filter2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='bard@shakespeare.lit' subid='991d7fd1616fd041015064133cd097a10030819e'/> </pubsub> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='bard@shakespeare.lit/globe' id='filter2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='bard@shakespeare.lit' subid='991d7fd1616fd041015064133cd097a10030819e'> <x xmlns='jabber:x:data' type='form'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> ... <field var='x-http://shakespeare.lit/search#keyword' type='text-single' label='Keyword to match'/> ... </x> </options> </pubsub> </iq> <iq type='set' from='bard@shakespeare.lit/globe' to='pubsub.shakespeare.lit' id='filter3'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='bard@shakespeare.lit' subid='991d7fd1616fd041015064133cd097a10030819e'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> ... <field var='x-http://shakespeare.lit/search#keyword'><value>peasant</value></field> ... </x> </options> </pubsub> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='bard@shakespeare.lit/globe' id='filter3'/>
该订阅者接着将被通知匹配该关键字的事件.
例子 223. 为匹配的关键字的事件通知
<message from='pubsub.shakespeare.lit' to='bard@shakespeare.lit'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='4e30f35051b7b8b42abe083742187228'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Alone</title> <summary> Now I am alone. O, what a rogue and peasant slave am I! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-13T11:09:53Z</published> <updated>2003-12-13T11:09:53Z</updated> </entry> </item> </items> </event> <headers xmlns='http://jabber.org/protocol/shim'> <header name='SubID'>991d7fd1616fd041015064133cd097a10030819e</header> </headers> </message>
单件节点
对于一些节点, 期望在任何时间至少有一个和该节点关联的条目存在(例如, 一个客户端可能想使用一个由那个客户端控制的命名空间命名的节点名来存储它的参数). 当想用这个模式的时候, 推荐该发布者指定一个 "current" ItemID来确保一个新条目的发布将覆盖现有的条目.
例子 224. 发布到一个单件节点
<iq type='set' from='horatio@denmark.lit/mobile' to='pubsub.shakespeare.lit' id='single1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <publish node='elsinore-doorbell'> <item id='current'/> </publish> </pubsub> </iq>
自然的, 该节点所有者能通过把 max_items 配置选项设为 "1" 来强制该单件节点模式.
PubSub URIs
一个 XMPP URI (见 RFC 5122 32)能被用于识别或交互的目的. 一些例子如下.
以下URI只是标识一个pubsub节点.
例子 225. XMPP URI 用于节点
xmpp:pubsub.shakespeare.lit?;node=princely_musings
以下URI识别位于一个节点的一个特定条目.
例子 226. XMPP URI 用于一个pubsub条目
xmpp:pubsub.shakespeare.lit?;node=princely_musings;item=ae890ac52d0df67ed7cfdf51b644e901
以下URI定义如何订阅一个节点(详见本文的URI查询类型).
例子 227. URI 用于订阅一个pubsub节点
xmpp:pubsub.shakespeare.lit?pubsub;action=subscribe;node=princely_musings
以下URI定义如何从一个节点获取一个特定的条目.
例子 228. URI 用于获取一个pubsub条目
xmpp:pubsub.shakespeare.lit?pubsub;action=retrieve;node=princely_musings;item=ae890ac52d0df67ed7cfdf51b644e901