查看源代码
来自Jabber/XMPP中文翻译计划
XEP-0045
的源代码
跳转到:
导航
,
搜索
根据下列原因,你没有权限编辑本页:
您刚才请求的操作只有这个用户组中的用户才能使用:
用户
您可以查看并复制此页面的源代码:
[[Category:XMPP扩展]] [[Category:翻译中]] '''本文的英文原文来自[http://www.xmpp.org/extensions/xep-0045.html XEP-0045]''' '''XEP-0045: 多用户聊天''' 摘要: 本文定义了一个XMPP协议扩展用于多用户文本会议.即多个XMPP可以在一个房间或频道互相交流信息, 类似互联网中继聊天系统(IRC).还有标准聊天室功能如聊天室的主题和邀请,本协议定义了一个强有力的房间控制模型,包括能够踢和禁止用户,任命主持人和管理员,要求会员或密码才能加入房间,等等。 作者: Peter Saint-Andre XMPP扩展协议的版权(1999-2008)归XMPP标准化基金会(XSF)所有 版权: © 1999 - 2010 XMPP标准化基金会(XSF). 参见[[XEP-0045#法律通告|法律通告]]. 状态: 草案 类型: 标准跟踪 版本: 1.24 最后更新日期: 2008-07-16 注意: 这里定义的协议是XMPP标准化基金会的一个草案标准.对本协议的执行是被鼓励的,也适于部署到生产系统,但是在它成为最终标准之前可能还会有一些变动. ==绪论== 传统上, 即时消息被视为由一对一的聊天构成而不是多对多聊天(即所谓"群聊"或"文本会议"). 群聊功能常见于一些系统如 Internet Relay Chat (IRC) 和 流行的IM服务所提供的聊天室功能. Jabber社区早在1999年开发和实施了一个基本的群聊协议. 这个 "groupchat 1.0" 协议为聊天室提供了一个最小功能集但是范围很有限. 本协议(多用户聊天或简称MUC)建立在向后兼容旧的"groupchat 1.0"协议的基础上但是提供高级功能如邀请, 房间主持和管理, 以及专门的房间类型. ==范围== 本文着重于和配置,参与以及管理一个独立的基于文本的会议室相关的通用需求. 这里所指出的需求是应用于单个房间级别的并且是"通用的", 某种意义上它们是在Jabber社区广泛讨论的或在现有的Jabber之外的基于文本的会议环境(例如, 定义在 [http://tools.ietf.org/html/rfc1459 RFC 1459] [[XEP-0045#附录G:备注|1]]中的Internet Relay Chat 和它的继承者: [http://tools.ietf.org/html/rfc2810 RFC 2810] [[XEP-0045#附录G:备注|2]], [http://tools.ietf.org/html/rfc2811 RFC 2811] [[XEP-0045#附录G:备注|3]], [http://tools.ietf.org/html/rfc2812 RFC 2812] [[XEP-0045#附录G:备注|4]], [http://tools.ietf.org/html/rfc2813 RFC 2813] [[XEP-0045#附录G:备注|5]])中已经存在的. 本文明确地不涉及以下需求: * 房间之间的关系(例如, 房间的层次结构) * 多用户聊天服务的管理(例如, 管理跨越整个服务级别的权限或注册一个全局可用的房间昵称);这些用例定义在[http://xmpp.org/extensions/xep-0133.html Service Administration] [[XEP-0045#附录G:备注|6]] * 个别消息的主持 * 通过房间发送的消息的加密 * 高级特性, 如附加文件给一个房间, 集成白板, 以及和语音或视频聊天服务的接口 * MUC部署和外来的聊天系统(例如, 和IRC网关或现有的其他IM系统)之间的交互 * 在多个MUC部署之间进行镜像或复制 这一受限的范围并非蔑视这些都很有用的主题; 无论如何, 这意味着本文专注于讨论和介绍一个易于理解的协议能够被类似的Jabber客户端和组件开发者实现. 将来的协议当然可能涉及以上提到的这些主题. ==需求== 本文描述了由Jabber现有的多用户聊天服务提供的最小功能集. 为了向后兼容性起见, 本文使用原来的"groupchat 1.0"协议作为基本功能, 包括以下这些: * 每个房间被标识为 <room@service> (例如, <jdev@conference.jabber.org>), 这里 "room" 是房间的名称而 "service" 是多用户聊天服务运行所在的主机名. * 在一个房间里每个房客被标识为 <room@service/nick>, 这里 "nick" 是这个房客在这个房间里的昵称,定义于刚加入这个房间的时候,也可以在房客驻留改房间期间修改. * 一个用户通过发送出席信息给 <room@service/nick> 来加入一个房间(也就是成为房客). * 在多用户聊天房间里发送的消息使用特殊的类型"groupchat"并且被寻址于房间本身 (room@service), 然后反映给所有房客. * 通过发送出席信息给 <room@service/newnick>,一个房客可以改变他或她的房间昵称以及在房间中的可用性状态 . * 通过发送一个类型为"unavailable"的出席信息给当前的<room@service/nick>,一个房客可以退出房间. 本文追加的特性和功能包括以下这些: # 本地会话日志(不需要房间内的机器人) # 允许用户申请房间成员 # 在一个非匿名房间里, 允许房客可以察看(另)一个房客的全JID # 在一个半匿名房间里, 允许主持人可以察看一个房客的全JID # 允许只有主持人修改房间主题 # 允许主持人从房间里踢出与会者和游客 # 在一个被主持的房间里,主持人可以授予和撤销发言权(也就是说, 发言的权力), 并且管理发言权列表 # 允许管理员授权和取消主持人权力, 并且管理主持人列表 # 允许管理员在房间禁止用户, 并管理黑名单 # 允许管理员授予和撤销成员权力, 并且管理一个仅限成员的房间的成员列表 # 允许所有者限制房客的数量 # 允许所有者指定其他的所有者(们) # 允许所有者授予或撤销管理特权, 并管理管理员列表 # 允许所有者销毁房间 另外, 本文提供了协议元素用于支持以下房间类型: # 公共的或隐藏的 # 持久的或临时的 # 密码保护的或不安全的 # 仅限成员的或开放的 # 主持的或非主持的 # 非匿名的或半匿名的 为了实现这些需求, 本扩展协议需要满足 'http://jabber.org/protocol/muc' 名字空间(以及 在主名字空间URI加上 #owner, #admin, 和 #user 片断). ==术语== ===通用术语=== Affiliation(级别) -- 一个长期存在的和房间之间的联系或连接; 可能的级别有 "owner"(所有者), "admin"(管理者), "member"(成员), 以及 "outcast"(被排斥者) (当然也可能没有级别); 级别从角色来看是唯一的. 一个级别跨越了用户对一个房间的访问期间. Ban(禁止) -- 从一个房间移除一个用户以使这个用户不能够再进入这个房间 (直到这个禁令被废除为止). 一个被禁止的用户的级别为 "outcast"(被排斥者). Bare JID(纯JID) -- 一个用户的标识符 <user@host>, 不同于任何已有会话或资源的上下文, 与之相对的是全JID和房间JID. Full JID(全JID) -- 一个在线用户的标识符 <user@host/resource> , 不同于一个房间的上下文; 与之相对的是纯JID和房间JID. GC -- 最小的 "groupchat 1.0" 协议[7], Jabber社区于1999年开发; MUC 向后兼容GC. History(历史) -- 有限数量的消息节, 由当前讨论的上下文提供发送给一个新的房客. Invitation(邀请) -- 从一个用户发出的特殊消息给另一个用户, 邀请对方加入房间. IRC -- Internet Relay Chat. Kick(踢人) -- 临时从一个房间移除一个与会者或游客; 这个用户任何时候都可以再次进入这个房间. 一个被踢的用户的角色是"none". Logging(记录) -- 存储发生在一个房间的讨论内容用于公开发布到房间背景之外的地方. Member(成员) -- 一个用户在一个仅限会员的房间内处于"white list"(白名单)内,或已经注册到一个公开的房间. 一个成员级别是"member". Moderator(主持人) -- 一个房间角色,通常和房间的管理有关但是这个角色可以被赋予非管理员; 可以踢人, 可以授予和撤销发言权, 等等. 一个主持人的角色是"moderator". MUC -- 本文所定义的基于文本会议的多用户聊天协议. Occupant(房客) -- 一个房间里的任何Jabber用户 (这是一个 "抽象类" 并且不对应任何特定的角色). Outcast(被排斥者) -- 一个被某个房间禁止的用户. 一个被排斥者的级别是 "outcast". Participant(与会者) -- 一个没有管理权限的房客; 在一个被主持的房间里, 参与者更多地被定义为有发言权的 (与之相反的是游客). 一个与会者的角色是"participant". Private Message(私有消息) -- 从一个房客直接发给另一个房间JID的消息(不是房间本身广播给所有房客的消息). Role(角色) -- 在一个房间里的一个临时的地位或者权限级别, 对于这个房间中的用户的长期级别来说是唯一的; 可能的角色有 "moderator"(主持人), "participant"(与会者), 和 "visitor"(游客) (也可能没有预定义的角色). 一个角色仅仅存在于一个房客访问一个房间的期间. Room(房间) -- 一个虚拟的地方, Jabber用户象征性地加入它, 来和其他用户一起参与一个实时的基于文本的会议. Room Administrator(房间管理员) -- 一个由房间所有者授权的用户, 可以执行管理功能, 如禁止用户等等; 无论如何, 不允许改变定义的房间特性. 一个管理员的级别是"admin" . Room ID(房间ID) -- 一个房间JID的节点标识符部分, 它可以是不透明的因而对人类用户没有什么含义(见 语法的商业规则Business Rules for syntax); 与之相对的是房间名. Room JID(房间JID) -- 在一个房间上下文中的一个房客,以 <room@service/nick> 来标识; 与之相对的是纯JID和全JID. Room Name(房间名) -- 一个用户友好的, 自然语言的房间名字, 由房间所有者配置并在服务查询中展示; 与之相对的是房间ID. Room Nickname(房间昵称) -- 房间JID的资源标识符部分(见语法的商业规则); 这是一个房客在这个房间中所呈现的"友好的名字". Room Owner(房间所有者) -- 建立某个房间的Jabber用户或一个被房间创建者或所有者指派拥有所有者权限(如果允许的话)的Jabber用户; 它被允许改变定义好的房间特性, 也可以执行全部的管理功能. 一个所有者的级别为"owner". Room Roster(房间名册) -- 一个房间中的所有房客在一个Jabber客户端的展现. Server(服务器) -- 一个Jabber服务器,可以关联或不关联一个基于文本的会议服务. Service(服务) -- 一个主机, 提供基于文本的会议的能力; 通常但不必须是一个Jabber服务器的子域(例如, conference.jabber.org). Subject(主题) -- 一个房间的临时讨论标题. Visit(访问) -- 一个房间的一个用户的"session"(会话), 当用户进入这个房间时开始(也就是说, 成为一个房客) , 结束于用户离开房间之时. Visitor(游客) -- 在一个被主持的房间里的一个没有发言权的房客(相反则是一个与会者). 一个游客的角色是"visitor". Voice(发言权) -- 在一个被主持的房间里, 发送消息给全部房客的权限. ===房间类型=== Fully-Anonymous Room(全匿名房间) -- 一个房间的房客的全JID或纯JID不能被任何人查询到, 包括房间管理员和房间所有者; 这类房间是不推荐的(NOT RECOMMENDED)或不被MUC显式支持, 但是如果一个服务提供适当的配置选项来使用这个协议,这种情况也是有可能的; 相对的则是非匿名房间和半匿名房间. Hidden Room(隐藏房间) -- 一个无法被任何用户以普通方法如搜索和服务查询来发现的房间; 反义词: 公开(public)房间. Members-Only Room(仅限会员的房间) -- 如果一个用户不在成员列表中则无法加入的一个房间; 反义词: 开放(open)房间. Moderated Room(被主持的房间) -- 只有有"发言权"的用户才可以发送消息给所有房客的房间; 反义词: 非主持的Unmoderated房间. Non-Anonymous Room(非匿名房间) -- 一个房客的全JID会暴露给所有其他房客的房间, 尽管房客可以选择任何期望的房间昵称; 相对的是半匿名房间和全匿名房间. Open Room(开放房间) -- 任何人可以加入而不需要在成员列表中的房间; 反义词: 仅限会员的房间. Password-Protected Room(密码保护房间) -- 一个用户必须提供正确密码才能加入的房间; 反义词: 非保密房间. Persistent Room(持久房间) -- 如果最后一个房客退出也不会被销毁的房间; 反义词: 临时房间. Public Room(公开房间) -- 用户可以通过普通方法如搜索和服务查询来发现的房间; 反义词: 隐藏房间. Semi-Anonymous Room(半匿名房间) -- 一个房客的全JID只能被房间管理员发现的房间; 相对的是全匿名房间和非匿名房间. Temporary Room(临时房间) -- 如果最后一个房客退出就会被销毁的房间; 反义词: 持久房间. Unmoderated Room(非主持的房间) -- 任何房客都被允许发送消息给所有房客的房间; 反义词: 被主持的房间. Unsecured Room(非保密房间) -- 任何人不需要提供密码就可以进入的房间; 反义词: 密码保护房间. ===4.3 人物=== 本文的大部分例子使用了 the scenario of the witches' meeting held in a dark cave at the beginning of Act IV, Scene I of Shakespeare's Macbeth, 在这里代表"darkcave@macbeth.shakespeare.lit"聊天室. 人物如下: '''表1: 剧中人''' {|border="1" cellspacing="0" !房间昵称 !!全 JID !!级别 |- |firstwitch ||crone1@shakespeare.lit/desktop ||所有者 |- |secondwitch ||wiccarocks@shakespeare.lit/laptop ||管理员 |- |thirdwitch ||hag66@shakespeare.lit/pda ||无 |} ==角色和级别== 有两个尺度我们可以用来衡量一个用户的连接或在一个房间的地位. 一个是用户和一个房间的长期的级别 -- 例如, 一个用户的状态是一个所有者或一个被排斥者. 另一个是当一个用户驻留于一个聊天室的时候的角色 -- 例如, 一个房客的地位是一个主持人,有权利踢出游客和与会者. 这两个尺度各自都是唯一的, 因为一个级别是跨越访问的, 而一个角色只存在于一次访问期间. 另外, 在角色和级别之间没有一对一的对应关系; 例如, 某个不从属于某房间的人可能成为一个(临时的)主持人, 一个成员可能在一个被主持的房间中是一个与会者或游客者. 这些概念以下全面解释. ===角色=== 以下是已定义的角色: '''表2: 角色''' {|border="1" cellspacing="0" !名称 !!支持 |- | 主持人 ||必需的 |- | 无 ||缺少角色 |- | 与会者 ||必需的 |- | 游客 ||推荐的 |} 角色是临时的,它不一定要在用户对房间的访问中持久化,它可以(MAY)在一个房客访问房间期间改变. 一个实现可以(MAY)在一次访问期间持久化角色并且应该(SHOULD)在被主持的房间这样做 (因为在游客和与会者之间,唯一性对一个被被主持的房间是很关键的). 在角色和级别之间没有一对一的映射(例如, 一个成员可以是一个与会者或一个游客). 在房间会话中,一个主持人是最有权力的房客, 它能在某种程度走上管理房间的其他房客的角色. 一个与会者的权力小于一个主持人, 尽管他或她有权发言. 在一个被主持的房间会话中游客是一个更受限制的角色, 因为访问者不允许发送消息给所有房客. 角色的授予,撤销, 和维护是基于房客的房间昵称或全JID,而不是纯JID. 和这些角色相关的权限,还有角色改变触发的动作, 定义在下文中. 所有在房间中生成或反射的出席信息中关于角色的信息必须(MUST)被发送,从而发送给房客们. ====权限==== 大部分情况下, 角色存在于一个层次中. 例如, 一个与会者可以做任何游客能做的事, 而一个主持人可以做任何与会者能做的事. 每个角色拥有下一级角色所没有的权限; 这些权限定义于下表作为缺省值(一个实现可以(MAY)提供配置选项来重载这些缺省值). '''表3: 和角色相关的权限''' {|border="1" cellspacing="0" !权限 !!无 !!游客 !!与会者 !!主持人 |- |在房间中出席 ||否 ||是 ||是 ||是 |- |接收消息 ||否 ||是 ||是 ||是 |- |接收房客出席信息 ||否 ||是* ||是 ||是 |- |出席信息广播到房间 ||否 ||是* ||是 ||是 |- |改变可用性状态 ||否 ||是 ||是 ||是 |- |改变房间昵称 ||否 ||是* ||是 ||是 |- |发送私人消息 ||否 ||是* ||是 ||是 |- |邀请其他用户 ||否 ||是* ||是* ||是 |- |发送消息给所有人 ||否 ||否** ||是 ||是 |- |修改标题 ||否 ||否* ||是* ||是 |- |踢出与会者和游客 ||否 ||否 ||否 ||是 |- |授予发言权 ||否 ||否 ||否 ||是 |- |撤销发言权 ||否 ||否 ||否 ||是*** |} * 缺省; 设定配置时可以(MAY)修改这个权限. ** 一个实现可以(MAY)在非主持的房间里缺省地授予发言权给游客. *** 主持人不能(MUST NOT)从一个管理员或所有者收回发言权. ====变更角色==== 一个房客的角色变更方法是定义好的. 有时候房客自己的动作导致变更 (例如, 加入或退出房间), 反之有时候由主持人,管理员或所有者的动作导致变更. 如果一个房客的角色改变了, 一个 MUC 服务实现必须(MUST)变更这个房客的角色来反映这个变更并且传达这个变更给所有房客. 角色的变更和它们触发的动作定义于下表. '''表4: 角色状态表''' {|border="1" cellspacing="0" !> !!无 !!游客 !!与会者 !!主持人 |- |无 ||-- ||进入被主持的房间 ||进入非主持的房间 ||管理员或所有者进入房间 |- |游客 ||退出房间或被主持人踢出房间 ||-- ||主持人授予发言权 ||管理员或所有者授予主持人权限 |- |与会者 ||退出房间或被主持人踢出房间 ||主持人撤销发言权 |-- ||管理员或所有者授予主持人权限 |- |主持人 ||退出房间 ||管理员或所有者改变角色成为游客* ||管理员或所有者改变角色成为与会者或撤销主持人权限* ||-- |} * 一个主持人不能(MUST NOT)从一个级别属于等于或高于主持人的房客那里收回主持人权限. 注意: 特定的角色一般暗含特定的权限. 例如, 一个管理员或所有者自动成为一个主持人, 所以如果一个房客被授予管理员地位那么这个房客事实上将被授予主持人权限; 类似的, 当一个房客成为一个被主持的房间的成员, 这个房客自动拥有一个与会者的角色. 无论如何, 失去管理员地位并不足以意味这个房客不再是主持人 (因为只要是与会者就可能成为一个主持人). 因此, 当一个房客被授予特定的级别的时候所拥有的角色是固定的, 反之当一个房客失去一个特定的级别它的角色是不确定的并取决于(服务的)实现. 因为一个客户端无法预料是否在撤销某个级别之后这个角色成为什么, 如果它不想同时移除管理员/所有者权限和主持人角色, 那么除了级别变更之外它还必须特意请求角色变更. ===级别=== 已定义了以下级别: # 所有者 # 管理员 # 成员 # 被排斥者 # 无 (缺少级别) 必须支持"所有者"这个级别,推荐支持"管理员","成员","被排斥者"的级别.("无"表示缺少级别) 这些级别是长时间的跨越一个用户对这个房间的访问期间的并且不受房间里事件的影响. 而且, 这些级别和一个房客在房间中的角色之间没有一对一的映射关系. 级别被授予,撤销, 和维护都是基于这个用户的纯 JID. 如果一个没有已定义的级别的用户进入一个房间, 这个用户的级别被定义为"无"; 无论如何, 这个级别不能跨越(多次的)访问 (也就是说, 一个服务不会跨越访问维护一个 "无 列表"). "成员"级别为房间所有者或管理员提供了一个方法来指定一个"白名单",其中的用户被允许加入一个仅供会员的房间. 当一个成员加入了一个仅供会员的房间, 他或她的级别不会改变, 无论他或她的角色是什么. 成员级别也为用户提供一个方法来高效地注册一个开放的房间并在某种方式意义上保持和那个房间的联系(例如可能在房间里预留那个用户的昵称). 一个被排斥者就是一个被从房间踢出来并且不允许进入那个房间的用户. 关于级别的信息必须(MUST)由房间生成或反射到所有的出席信息节之中发送给房客们. ====权限==== 大部分情况下, 级别存在一个层次结构. 例如, 一个所有者可以做任何管理员能做的事情, 而一个管理员可以做任何成员能做的事情. 每个级别拥有其下一级级别所没有的权限; 这些权限定义在下表中. '''表5: 和级别相关的权限''' {|border="1" cellspacing="0" !权限 !!Outcast(被排斥者) !!None(无) !!Member(成员) !!Admin(管理员) !!Owner(所有者) |- |进入房间 ||否 ||是* ||是 ||是 ||是 |- |注册一个开放的房间 ||否 ||是 ||N/A ||N/A ||N/A |- |接收成员列表 ||否 ||否** ||是 ||是 ||是 |- |加入一个仅限会员的房间 ||否 ||否 ||是* ||是 ||是 |- |踢出成员并把用户的级别删除 ||否 ||否 ||否 ||是 ||是 |- |编辑成员列表 ||否 ||否 ||否 ||是 ||是 |- |编辑主持人列表 ||否 ||否 ||否 ||是** ||是** |- |编辑管理员列表 ||否 ||否 ||否 ||否 ||是 |- |编辑所有者列表 ||否 ||否 ||否 ||否 ||是 |- |变更房间定义 ||否 ||否 ||否 ||否 ||是 |- |销毁房间 ||否 ||否 ||否 ||否 ||是 |} * 作为缺省值, 一个无级别的用户进入一个被主持的房间的角色是一个游客, 而进入一个开放的房间的角色是一个与会者. 一个成员进入一个房间的角色是与会者. 一个管理员或所有者进入房间的角色是一个主持人. ** 一个管理员或所有者不能(MUST NOT)撤销另一个管理员或所有者的权限. ====变更级别==== 一个用户的级别变更方法已经定义得很完善. 有时用户自己的动作导致这些变更(例如, 注册为一个房间的新成员), 反之有时候一个管理员或所有者的动作导致了这些变更. 如果一个用户的级别改变了, 一个MUC服务实现必须(MUST)变更这个用户的级别来反射这一变更并通知所有房客. 级别变更和他们触发的动作定义在下表中. '''表 6: 级别状态表''' {|border="1" cellspacing="0" | ||被排斥者(Outcast) ||无(None) ||成员(Member) ||管理员(Admin) ||所有者(Owner) |- |被排斥者(Outcast) ||-- ||管理员或所有者移除屏蔽 ||管理员或所有者增加用户到成员列表 ||所有者增加用户到管理员列表 ||所有者增加用户到所有者列表 |- |无(None) ||管理员或所有者使用屏蔽 ||-- ||管理员或所有者增加用户到成员列表, 或用户注册一个成员(如果允许) ||所有者增加用户到管理员列表 ||所有者增加用户到所有者列表 |- |成员(Member) ||管理员或所有者使用屏蔽 ||管理员或所有者变更级别为"none" ||-- ||所有者增加用户到管理员列表 ||所有者增加用户到所有者列表 |- |管理员(Admin) ||所有者使用屏蔽 ||所有者变更级别为"none" ||所有者变更级别为"member" ||-- ||所有者增加用户到所有者列表 |- |所有者(Owner) ||所有者使用屏蔽 ||所有者变更级别为"none" ||所有者变更级别为"member" ||所有者变更级别为"admin" ||-- |} ==实体用例== 一个MUC实现必须(MUST)支持[http://xmpp.org/extensions/xep-0030.html 服务发现] [[XEP-0045#附录G:备注|7]]. ===MUC的发现组件支持=== 一个Jabber实体可能希望发现是否一个服务实现了多用户聊天协议; 为了达到这个目的, 它发送一个服务发现信息("disco#info")查询给这组件的JID: '''例子 1. 用户通过Disco查询聊天服务是否支持MUC''' <source lang="xml"> <iq from='hag66@shakespeare.lit/pda' id='disco1' to='macbeth.shakespeare.lit' type='get'> <query xmlns='http://jabber.org/protocol/disco#info'/> </iq> </source> 服务必须(MUST)返回它的的身份和它所支持的特性: '''例子 2. 服务返回Disco Info结果''' <source lang="xml"> <iq from='macbeth.shakespeare.lit' id='disco1' to='hag66@shakespeare.lit/pda' type='result'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='conference' name='Macbeth Chat Service' type='text'/> <feature var='http://jabber.org/protocol/muc'/> </query> </iq> </source> 注意: 因为MUC是旧的"groupchat 1.0"协议的超集, 一个MUC服务不应该(SHOULD NOT)返回一个<feature var='gc-1.0'/>条目在一个disco#info结果中. ===发现房间=== 发现服务条目("disco#items")协议使得一个用户可以向一个服务查询相关的条目列表, 在一个聊天服务中这包含这个服务所承载的所有特定房间的集合. '''例子 3. 用户向聊天服务查询房间''' <source lang="xml"> <iq from='hag66@shakespeare.lit/pda' id='disco2' to='macbeth.shakespeare.lit' type='get'> <query xmlns='http://jabber.org/protocol/disco#items'/> </iq> </source> 服务应该(SHOULD)返回它承载的所有房间的列表. '''例子 4. 服务返回Disco Item结果''' <source lang="xml"> <iq from='macbeth.shakespeare.lit' id='disco2' to='hag66@shakespeare.lit/pda' type='result'> <query xmlns='http://jabber.org/protocol/disco#items'> <item jid='heath@macbeth.shakespeare.lit' name='A Lonely Heath'/> <item jid='darkcave@macbeth.shakespeare.lit' name='A Dark Cave'/> <item jid='forres@macbeth.shakespeare.lit' name='The Palace'/> <item jid='inverness@macbeth.shakespeare.lit' name='Macbeth's Castle'/> </query> </iq> </source> 如果全部房间的列表太大(详见[XMPP文档列表/XMPP扩展/XEP-0030]), 服务可以(MAY)只返回部分的房间列表.如果这样做了, 它应该 SHOULD 包含一个 <set/> 元素 (定义在 [http://xmpp.org/extensions/xep-0059.html Result Set Management] [[XEP-0045#附录G:备注|8]]) 以表明这个列表不是全部的结果集. '''例子 5. 服务返回Disco Item结果的部分列表''' <source lang="xml"> <iq from='rooms.shakespeare.lit' id='disco-rsm-1' to='hag66@shakespeare.lit/pda' type='result'> <query xmlns='http://jabber.org/protocol/disco#items'> <item jid='alls-well-that-ends-well@rooms.shakespeare.lit'/> <item jid='as-you-like-it@rooms.shakespeare.lit'/> <item jid='cleopatra@rooms.shakespeare.lit'/> <item jid='comedy-of-errors@rooms.shakespeare.lit'/> <item jid='coriolanus@rooms.shakespeare.lit'/> <item jid='cymbeline@rooms.shakespeare.lit'/> <item jid='hamlet@rooms.shakespeare.lit'/> <item jid='henry-the-fourth-one@rooms.shakespeare.lit'/> <item jid='henry-the-fourth-two@rooms.shakespeare.lit'/> <item jid='henry-the-fifth@rooms.shakespeare.lit'/> <set xmlns='http://jabber.org/protocol/rsm'> <first index='0'>alls-well-that-ends-well@rooms.shakespeare.lit</first> <last>henry-the-fifth@rooms.shakespeare.lit</last> <count>37</count> </set> </query> </iq> </source> ===查询房间信息=== 使用 disco#info 协议, 一个用户也可以查询一个特定房间的详情. 为了在进入房间之间确定这个房间的隐私和安全配置用户应该(SHOULD)这样做(详见 [XEP-0045#安全事项|安全事项]). '''例子 6. 用户查询特定聊天室的信息''' <source lang="xml"> <iq from='hag66@shakespeare.lit/pda' id='disco3' to='darkcave@macbeth.shakespeare.lit' type='get'> <query xmlns='http://jabber.org/protocol/disco#info'/> </iq> </source> 房间必须(MUST)返回它的标识并且应该(SHOULD)返回它支持的特性: '''例子 7. 房间返回查询信息结果''' <source lang="xml"> <iq from='darkcave@macbeth.shakespeare.lit' id='disco3' to='hag66@shakespeare.lit/pda' type='result'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='conference' name='A Dark Cave' type='text'/> <feature var='http://jabber.org/protocol/muc'/> <feature var='muc_passwordprotected'/> <feature var='muc_hidden'/> <feature var='muc_temporary'/> <feature var='muc_open'/> <feature var='muc_unmoderated'/> <feature var='muc_nonanonymous'/> </query> </iq> </source> 注意: 因为 MUC 是旧的 "groupchat 1.0" 协议的超集, 一个 MUC 房间不应该(SHOULD NOT)在一个disco#info结果中返回<feature var='gc-1.0'/>条目. 房间应该(SHOULD)返回它支持的实质的有意义的特性, 例如密码保护和房间主持(这些特性被完整地列入了特性注册, 由[http://xmpp.org/registrar/ XMPP Registrar]维护; 也见于本文的[[XEP-0045#registrar|XMPP注册]] 章节). 一个聊天室可以(MAY)使用[http://xmpp.org/extensions/xep-0128.html 服务查询扩展] [[XEP-0045#附录G:备注|9]]在它的disco#info应答中返回更详细的信息, 通过包含一个隐含的FORM_TYPE属性值"http://jabber.org/protocol/muc#roominfo"来标识. 这些信息可能包括关于一个房间的更详细的描述, 当前的房间标题, 以及这个房间当前的房客数量: '''例子 8. 房间返回扩展的查询信息结果''' <source lang="xml"> <iq from='darkcave@macbeth.shakespeare.lit' id='disco3a' to='hag66@shakespeare.lit/pda' type='result'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='conference' name='A Dark Cave' type='text'/> <feature var='http://jabber.org/protocol/muc'/> <feature var='muc_passwordprotected'/> <feature var='muc_hidden'/> <feature var='muc_temporary'/> <feature var='muc_open'/> <feature var='muc_unmoderated'/> <feature var='muc_nonanonymous'/> <x xmlns='jabber:x:data' type='result'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/muc#roominfo</value> </field> <field var='muc#roominfo_description' label='Description'> <value>The place for all good witches!</value> </field> <field var='muc#roominfo_changesubject' label='Whether Occupants May Change the Subject'> <value>true</value> </field> <field var='muc#roominfo_contactjid' label='Contact Addresses'> <value>crone1@shakespeare.lit</value> </field> <field var='muc#roominfo_subject' label='Subject'> <value>Spells</value> </field> <field var='muc#roominfo_occupants' label='Number of occupants'> <value>3</value> </field> <field var='muc#roominfo_lang' label='Language of discussion'> <value>en</value> </field> <field var='muc#roominfo_logs' label='URL for discussion logs'> <value>http://www.shakespeare.lit/chatlogs/darkcave/</value> </field> <field var='muc#roominfo_pubsub' label='Associated pubsub node'> <value>xmpp:pubsub.shakespeare.lit?node=chatrooms/darkcave</value> </field> </x> </query> </iq> </source> 某些扩展的房间信息可能是动态生成的(例如, 讨论记录的URL地址, 它可能取决于服务器那一层的配置); 反之另一些信息则可能基于房间那一层的配置,任何定义在[[XEP-0045#附录G:备注|muc#roomconfig FORM_TYPE]] 里的字段都可以用于扩展服务发现的字段(如上文所示的 muc#roomconfig_changesubject 字段). 注意: 前述 'http://jabber.org/protocol/muc#roominfo' FORM_TYPE的扩展服务发现字段将来还可以扩充(通过本文的[[XEP-0045#附录G:备注|字段标准化]]章节描述的机制). ===查询房间条目=== 一个用户也可以(MAY)向一个特定的聊天室查询和它相关的条目: '''例子 9. 用户查询和一个特定聊天室相关的条目''' <source lang="xml"> <iq from='hag66@shakespeare.lit/pda' id='disco4' to='darkcave@macbeth.shakespeare.lit' type='get'> <query xmlns='http://jabber.org/protocol/disco#items'/> </iq> </source> 一个实现可以(MAY)返回现有房客的列表(如果那信息是可公开的), 或不返回列表(如果那信息是私有的). '''例子 10. 房间返回查询条目结果(条目是公开的)''' <source lang="xml"> <iq from='darkcave@macbeth.shakespeare.lit' id='disco4' to='hag66@shakespeare.lit/pda' type='result'> <query xmlns='http://jabber.org/protocol/disco#items'> <item jid='darkcave@macbeth.shakespeare.lit/firstwitch'/> <item jid='darkcave@macbeth.shakespeare.lit/secondwitch'/> </query> </iq> </source> 注意: 这些 <item/> 元素由 disco#items 名字空间限定, 而不是 muc 名字空间; 这意味着他们不能拥有 'affiliation' 或 'role' 属性, 例如. '''例子 11. 房间返回空的查询条目结果(条目是私有的)''' <source lang="xml"> <iq from='darkcave@macbeth.shakespeare.lit' id='disco4' to='hag66@shakespeare.lit/pda' type='result'> <query xmlns='http://jabber.org/protocol/disco#items'/> </iq> </source> ===查询一个房间的房客=== 如果一个非房客试图发送一个查询请求给一个<room@service/nick>类型的地址, 一个 MUC 服务应该(SHOULD)返回这个请求给这个实体并指明一个<bad-request/>错误条件. 如果一个房客发送这样一个请求, 服务可以(MAY)把它传递给指定的接收者; 详见本文的 [[XEP-0045#实施指南|实施指南]]章节. ===发现客户端对MUC的支持=== 一个 Jabber 用户可能想发现这个用户的某个联系人是否支持多用户聊天协议. 这可以使用服务发现(协议)来完成. '''例子 12. 用户查询联系人对于 MUC 的支持''' <source lang="xml"> <iq from='hag66@shakespeare.lit/pda' id='disco5' to='wiccarocks@shakespeare.lit/laptop' type='get'> <query xmlns='http://jabber.org/protocol/disco#info'/> </iq> </source> 客户端应该(SHOULD)返回它的标识和它支持的特性: '''例子 13. 联系人返回发现信息结果''' <source lang="xml"> <iq from='wiccarocks@shakespeare.lit/laptop' id='disco5' to='hag66@shakespeare.lit/pda' type='result'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='client' type='pc'/> ... <feature var='http://jabber.org/protocol/muc'/> ... </query> </iq> </source> 一个用户也可能查询一个联系人在哪个房间. 这可以通过特定服务发现节点 'http://jabber.org/protocol/muc#rooms' 查询联系人的全JID(<user@host/resource>)来完成 : '''例子 14. 用户在当前房间查询联系人''' <source lang="xml"> <iq from='hag66@shakespeare.lit/pda' id='rooms1' to='wiccarocks@shakespeare.lit/laptop' type='get'> <query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/muc#rooms'/> </iq> </source> '''例子 15. 联系人返回房间查询结果''' <source lang="xml"> <iq from='wiccarocks@shakespeare.lit/laptop' id='rooms1' to='hag66@shakespeare.lit/pda' type='result'> <query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/muc#rooms'/> <item jid='darkcave@macbeth.shakespeare.lit'/> <item jid='characters@conference.shakespeare.lit'/> </query> </iq> </source> 可选的, 联系人可以(MAY)把它的房间昵称作为'name'属性的值返回: <source lang="xml"> ... <item jid='darkcave@macbeth.shakespeare.lit' name='secondwitch'/> ... </source> ==房客用例== 在一个多用户聊天环境中主要的行为者是房客, 它可以被认为存在于一个多用户聊天室"之内"并且参与那个房间的讨论 (在本协议中, 与会者和游客"仅仅"被认为是房客, 因为他们不拥有管理员权限). 为了更加清晰起见, 本文中的协议元素中涉及到驻留者的用例分为以下三类: # 现存于 "groupchat 1.0" 协议的最小功能集 # 对于 "groupchat 1.0" 协议直接的应用, 如处理一些和新房间类型有关的错误 # 用来处理"groupchat 1.0"协议未涉及的功能的额外的协议元素(房间邀请, 房间密码, 和房间角色及级别相关的扩展出席信息); 在'http://jabber.org/protocol/muc#user'名字空间 注意: 这里所有客户端生成的例子是从服务的角度来展示的, 所以所有由服务收到的节都包含一个'from'属性来表达发送者的全JID(这个from属性是由一个通用的Jabber路由或会话管理者加入的). 另外, 通常的表示请求已被完成的 IQ 结果节(如 [[RFC 3920]] [10]中所要求的)未显示在这里. ===进入一个房间=== ====Groupchat 1.0协议==== 为了参加一个多用户聊天室的讨论, 一个Jabber用户必须(MUST)首先进入一个房间成为一个房客. 在旧的"groupchat 1.0"协议中, 这是通过发送出席信息<room@service/nick>来实现的, 这里"room"是房间的 ID, "service" 是聊天服务的主机名, "nick" 是这个用户在这房间里预期的昵称: '''例子 16. Jabber用户进入一个房间(Groupchat 1.0)''' <source lang="xml"> <presence from='hag66@shakespeare.lit/pda' to='darkcave@macbeth.shakespeare.lit/thirdwitch'/> </source> 在这个例子中, 一个全JID为"hag66@shakespeare.lit/pda"的用户请求用昵称"thirdwitch"进入位于"macbeth.shakespeare.lit"聊天服务的房间"darkcave". 如果用户未指定一个房间昵称, 服务应该(SHOULD)返回一个<jid-malformed/>错误: '''例子 17. Jabber用户进入一个房间(Groupchat 1.0)''' <source lang="xml"> <presence from='darkcave@macbeth.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='error'> <error code='400' type='modify'> <jid-malformed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence> </source> ====基本MUC协议==== 兼容的多用户聊天服务必须(MUST)接受知道"groupchat 1.0" (GC)协议或multi-user chat (MUC)协议的任何客户端发出上述请求进入会议室; 无论如何, MUC 客户端应该(SHOULD)声明他们的有能力支持 MUC 协议, 方法是在出席信息节里面包含一个空的 <x/> 元素, 满足名字空间 'http://jabber.org/protocol/muc' (注意不需要 '#user' 部分): '''例子 18. Jabber用户准备进入一个房间(Multi-User Chat)''' <source lang="xml"> <presence from="hag66@shakespeare.lit/pda" to='darkcave@macbeth.shakespeare.lit/thirdwitch'> <x xmlns='http://jabber.org/protocol/muc'/> </presence> </source> 注意: 如果发生了一个和加入房间有关的错误, 服务应该 SHOULD 返回一个包含 MUC 子元素 (i.e., <x xmlns='http://jabber.org/protocol/muc'/>) 的 <presence/> 节,其 type 为 "error". 在尝试进入房间之间, 一个兼容MUC的客户端应该(SHOULD)首先查询它的保留的房间昵称 (如果有的话), 接下来的协议本文中的 [[XEP-0045#发现保留的房间昵称|发现保留的房间昵称]] 章节对此作了定义. ====出席信息广播==== 如果服务能够添加用户到房间, 它必须(MUST)从所有现存的房客的房间JID发送出席信息给新的房客的全JID, 包括扩展的关于角色的出席信息, 一个满足 'http://jabber.org/protocol/muc#user' 名字空间的<x/> 元素并包含一个<item/>子元素, 这个子元素的'role'属性值设为"moderator", "participant", 或"visitor", 这个子元素的'affiliation'属性值设为"owner", "admin", "member", 或 "none" 中的一个: '''例子 19. 服务从现有的房客发送出席信息给新的房客''' <source lang="xml"> <presence from='darkcave@macbeth.shakespeare.lit/firstwitch' to='hag66@shakespeare.lit/pda'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='owner' role='moderator'/> </x> </presence> <presence from='darkcave@macbeth.shakespeare.lit/secondwitch' to='hag66@shakespeare.lit/pda'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='admin' role='moderator'/> </x> </presence> </source> 这个示例中, 用户已从前一个例子进入房间, 有两个人已经在房间里: 一个是昵称为"firstwitch"的(房间拥有者), 另一个是昵称为"secondwitch"的(房间管理员). 服务也必须(MUST)从新进入的房客的房间JID向所有房客的全JID发送出席信息(含新房客): '''例子 20. 服务发送新房客的出席信息给所有房客''' <source lang="xml"> <presence from='darkcave@macbeth.shakespeare.lit/thirdwitch' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='participant'/> </x> </presence> <presence from='darkcave@macbeth.shakespeare.lit/thirdwitch' to='wiccarocks@shakespeare.lit/laptop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='participant'/> </x> </presence> <presence from='darkcave@macbeth.shakespeare.lit/thirdwitch' to='hag66@shakespeare.lit/pda'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='participant'/> <status code='110'/> </x> </presence> </source> 在这个例子里, 初始的房间出席信息从新房客(thirdwitch)发送给所有房客, 包括这个新房客自己. 看看上面最后一个节, 由房间以房客的名义发送给用户自己的出席信息,应该 SHOULD 包含一个 110 状态码,这样用户就知道这个出席信息来自于作为房客的那个他自己. 服务可以 MAY 重写新房客的房间昵称 (例如, 如果房间昵称被锁定). 如果服务不接受新房客请求的房间昵称,而是分配一个新的房间昵称, 它必须 MUST 包含一个 "210" 状态码在发送给这个新房客的出席信息广播里. '''例子 21. 服务发送新房客的出席信息给新房客''' <source lang="xml"> <presence from='darkcave@macbeth.shakespeare.lit/thirdwitch' to='hag66@shakespeare.lit/pda'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='participant'/> <status code='110'/> <status code='210'/> </x> </presence> </source> 注意: 发送给新房客的出席信息的顺序是很重要的. 服务必须 MUST 首先发送现有房客的完整列表给这个新房客,然后只发送新房客自己的出席信息给新房客. 这有助于客户端知道什么时候它收到了完整的房间名册( "room roster"). 发送出席信息广播之后(并且只在这之后), 服务可以发送讨论历史, 即时消息, 出席信息更新, 以及其他房间内的流量. ====缺省角色==== 下表总结了初始缺省的角色,一个服务应该根据用户的级别来设置它们(没有和 被排斥者 "outcast" 级别相关的角色, 因为这些用户不允许进入房间). '''表7: 基于级别的初始角色''' {|border="1" cellspacing="0" !房间类型 !!无 !!成员 !!管理员 !!所有者 |- |被主持的 ||游客 ||与会者 ||主持人 ||主持人 |- |非主持的 ||与会者 ||与会者 ||主持人 ||主持人 |- |仅限会员的 ||N/A * ||与会者 ||主持人 ||主持人 |- |开放的 ||与会者 ||与会者 ||主持人 ||主持人 |} * 实体不被允许. ====非匿名房间==== 如果房间是非匿名的, 服务必须 MUST 发送新房客的全JID给所有房客,使用满足 'http://jabber.org/protocol/muc#user' 名字空间的扩展出席信息,其中带有 <x/> 元素并包含一个 <item/> 子元素,其 'jid' 属性值为这个房客的全JID: '''例子 22. 服务发送全JID给所有房客''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit/thirdwitch' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='none' jid='hag66@shakespeare.lit/pda' role='participant'/> </x> </presence> [ ... ] </source> 如果这个用户正在进入一个非匿名房间(即, 它如上所示,向所有房客通报每个房客的全JID), 服务应该 SHOULD 允许该用户加入本房间,但是必须 MUST 同时警告该用户本房间是非匿名的. 应该 SHOULD 在房间发送给这个新房客的初始出席信息种包含状态码 "100" 来实现这一点: '''例子 23. 服务发送新房客的出席信息给新房客''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit/thirdwitch' to='hag66@shakespeare.lit/pda'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='participant'/> <status code='100'/> <status code='110'/> <status code='210'/> </x> </presence> </source> 无论如何, 也可以 MAY 发送一个 "groupchat" 类型的消息给新房客来达到上述目的,这个消息应该包含一个 <x/> 子元素,并拥有 <status/> 子元素,并且其'code'属性值为"100": '''例子 24. 服务警告新房客(该房间)非匿名''' <source lang="xml"> <message from='darkcave@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='groupchat'> <body>This room is not anonymous.</body> <x xmlns='http://jabber.org/protocol/muc#user'> <status code='100'/> </x> </message> </source> 附带的状态码协助客户端展示它们自己的通知消息 (例如, 和用户所在地方有关的信息). ====半匿名房间==== 如果房间是半匿名的, 服务必须 MUST 如上文所述从新房客发送出席信息给所有房客, 但是必须 MUST 只在发给"主持人"的时候发送新房客的全JID,而非主持人则不发(全JID). (注意: 所有随后的例子中,涉及的<item/>元素都带有'jid'属性, 即使这个信息在半匿名房间里不被发送给非主持人.) ====密码保护房间==== 如果房间要求密码验证而用户不能提供(或密码错误), 服务必须 MUST 拒绝访问这个房间并且通知该用户它们是未被授权的; 具体方法是返回一个类型为"error"的出席信息节并标明 <not-authorized/> 错误: '''例子 25. 服务拒绝访问,因为(用户)未提供密码''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='error'> <x xmlns='http://jabber.org/protocol/muc'/> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence> </source> 密码应该 SHOULD 通过进入房间时发送的出席信息节来提供, 包含在满足 'http://jabber.org/protocol/muc' 名字空间的 <x/> 元素的<password/> 子元素里. 密码以明码方式发送; 目前不支持其它验证方法, 而且任何这类的验证或授权方法都将会定义在一个独立的协议里(参见本文的[[XEP-0045#安全事项|安全事项]]章节). '''例子 26. 用户进入房间时提供密码''' <source lang="xml"> <presence from='hag66@shakespeare.lit/pda' to='darkcave@chat.shakespeare.lit/thirdwitch'> <x xmlns='http://jabber.org/protocol/muc'> <password>cauldronburn</password> </x> </presence> </source> ====仅限会员房间==== 如果房间是仅限会员的,但用户不是(该房间的)成员, 服务必须 MUST 拒绝访问这个房间并通知用户它们不被允许进入房间; 具体方法是返回一个"error"类型的出席信息节,并包含一个 <registration-required/> 错误条件: '''例子 27. 服务拒绝访问,因为用户不在成员列表中''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='error'> <x xmlns='http://jabber.org/protocol/muc'/> <error type='auth'> <registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence> </source> ====被禁止的用户==== 如果用户已经被房间禁止(即, 其级别为被排斥者 "outcast"), 服务必须 MUST 拒绝访问这个房间并通知用户他(她)被禁止了; 具体方法是返回一个出席信息节,类型为"error",标明 <forbidden/> 错误条件: '''例子 28. 服务拒绝访问,因为用户被禁止了''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='error'> <x xmlns='http://jabber.org/protocol/muc'/> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence> </source> ====昵称冲突==== 如果房间里已经有别的用户使用了准备进入房间的新用户预期的昵称(或如果这个昵称被保留给另一个成员列表里面的用户), 服务必须 MUST 拒绝访问这个房间并通知用户这个冲突; 具体方法是返回一个出席信息节,类型为"error",标明 <conflict/> 错误条件: '''例子 29. 服务拒绝访问,因为昵称冲突''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='error'> <x xmlns='http://jabber.org/protocol/muc'/> <error type='cancel'> <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence> </source> 无论如何, 如果现有房客的纯 JID <localpart@domain.tld> 和准备进入房间的用户的纯 JID 相同, 那么服务应该 SHOULD 允许这个用户的进入, 所以这个用户就有两个(或更多) 房间内的会话 "sessions" 使用同一个房间昵称, 每一个对应一个资源. 如果一个服务允许相同纯JID可以同时存在多个房客并使用同一个房间的房间昵称, 它应该 SHOULD 路由房间内的消息给该用户的所有资源并允许用户的所有资源发送消息给房间; 视实现而定,服务来决定如何适当的处理从用户的资源发送的出席信息以及如何路由私有消息到所有或某个资源(基于出席信息优先级或其他机制). 如何确定昵称冲突取决于实现(例如, 该服务是否应用于一个特定的惯例, 一个 stringprep 规则如 Resourceprep 或 Nodeprep, 等等). ====最大用户数==== 如果房间达到它的最大房客数量, 服务应该 SHOULD 拒绝访问这个房间并通知该用户这个限制; 方法是返回一个出席信息节,类型为"error",标明 <service-unavailable/> 错误条件: '''例子 30. 服务通知用户该房间已达到房客数量极限''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='error'> <x xmlns='http://jabber.org/protocol/muc'/> <error type='wait'> <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence> </source> 另外, 房间可以踢出空闲用户("idle user")以腾出空间. 如果房间的房客数量已达到最大值但是一个房间管理员或所有者试图进入,该房间应该允许管理员或所有者加入,为了使得额外的房客达到一个合理的数目,该数量可以 MAY 做成可配置的。 ====锁住的房间==== 如果一个用户尝试进入一个房间而该房间是锁住的 "locked" (即, 在房间创建者提供初始的配置之前以及也就是在房间正式存在之前), 服务必须 MUST 拒绝进入并返回一个 <item-not-found/> 错误给该用户: '''例子 31. 服务拒绝访问,因为房间不存在''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='error'> <x xmlns='http://jabber.org/protocol/muc'/> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence> </source> ====不存在的房间==== 如果用户准备进入房间时,该房间已经不存在了, 服务应该 SHOULD 建立它; 无论如何, 这不是必需的, 因为一个实现或部署可以 MAY 选择限制建立房间的权限. 详见本文的[[XEP-0045#创建一个房间|创建一个房间]]章节. ====房间记录==== 如果用户进入一个房间,该房间的讨论是被记录到一个公开的存档里面(经常可以通过HTTP访问的), 服务应该 SHOULD 允许该用户加入该房间但是必须 MUST 同时警告该用户讨论已被记录. 方法是应该 SHOULD 在房间发送给该新房客的初始出席信息中包含一个状态码 "170": '''例子 32. 服务发送新房客的出席信息给新房客''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit/thirdwitch' to='hag66@shakespeare.lit/pda'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='participant'/> <status code='100'/> <status code='110'/> <status code='170'/> <status code='210'/> </x> </presence> </source> ====讨论历史==== 如上发送完初始出席信息之后, 一个房间可以 MAY 发送讨论历史给这个新房客. (在完成按照本文[[XEP-0045#出席信息广播|出席信息广播]]章节规定的发送房间出席信息之前,该房间不能 MUST NOT 发送任何讨论历史.) 是否这个历史要被发送, 以及这个历史里面包含多少条消息, 将由聊天服务实现或特定的部署来决定. '''例子 33. 讨论历史的发送''' <source lang="xml"> <message from='darkcave@chat.shakespeare.lit/firstwitch' to='hecate@shakespeare.lit/broom' type='groupchat'> <body>Thrice the brinded cat hath mew'd.</body> <delay xmlns='urn:xmpp:delay' from='crone1@shakespeare.lit/desktop' stamp='2002-10-13T23:58:37Z'/> </message> <message from='darkcave@chat.shakespeare.lit/secondwitch' to='hecate@shakespeare.lit/broom' type='groupchat'> <body>Thrice and once the hedge-pig whined.</body> <delay xmlns='urn:xmpp:delay' from='wiccarocks@shakespeare.lit/laptop' stamp='2002-10-13T23:58:43Z'/> </message> <message from='darkcave@chat.shakespeare.lit/thirdwitch' to='hecate@shakespeare.lit/broom' type='groupchat'> <body>Harpier cries 'Tis time, 'tis time.</body> <delay xmlns='urn:xmpp:delay' from='hag66@shakespeare.lit/pda' stamp='2002-10-13T23:58:49Z'/> </message> </source> 讨论历史消息必须 MUST 标为[http://xmpp.org/extensions/xep-0203.html Delayed Delivery] [[XEP-0045#附录G:备注|11]]信息,满足'urn:xmpp:delay' 名字空间,以表明它们是被延迟发送的并且标明它们最初发出的时间. (注意: 'urn:xmpp:delay' 明子空间定义在 XEP-0203 里面,取代了旧的定义在 [http://xmpp.org/extensions/xep-0091.html Legacy Delayed Delivery] [[XEP-0045#附录G:备注|12]] 里的 'jabber:x:delay' 名字空间 ; 直到XEP - 0091状态更改为已过时, 实现应该 SHOULD 包含两种日期时间(datetime)格式.). 在非匿名房间里,'from'属性应该 SHOULD 是原始发送者的全JID, 但不能 MUST NOT 在半匿名房间里(在那里'from'属性应该 SHOULD 设置为房间本身的JID). 服务应该 SHOULD 在进入该房间之后,发送任何即时("live")消息之前,发送完所有讨论历史消息. ====管理讨论历史==== 用户可能 MAY 希望管理进入房间时(由房间)提供的讨论历史(可能因为用户带宽比较低或正在使用迷你客户端). 他必须 MUST 在加入房间时发出的初始出席信息节里包含一个 <history/> 子元素. 这个元素有四个可用的属性: '''表 8: 历史管理属性''' {|border="1" cellspacing="0" !属性 !!数据类型 !!含义 |- | maxchars ||int ||限制历史中的字符总数为"X" (这里的字符数量是全部 XML 节的字符数, 不只是它们的 XML 字符数据). |- | maxstanzas ||int ||限制历史中的消息总数为"X". |- | seconds ||int ||仅发送最后 "X" 秒收到的消息. |- | since ||dateTime ||仅发送从指定日期时间 datetime 之后收到的消息 (这个datatime必须 MUST 符合[http://xmpp.org/extensions/xep-0082.html XMPP Date and Time Profiles] [[XEP-0045#附录G:备注|13]] 定义的DateTime 规则,). |} 服务必须 MUST 发送满足以上条件组合的最小数量的消息, 还要顾及服务级别和房间级别的缺省设置. 服务必须 MUST 只发送完整的消息节(即, 它不能 MUST not 按特定字符数把历史从字面上截断, 但是必须 MUST 发送最大数量的完整节,这使得字符数小于或等于 'maxchars' 属性的值). 如果客户端不希望收到历史, 它必须 MUST 把'maxchars' 属性值设为"0" (zero). 以下例子展示如何使用这个协议. '''例子 34. 用户请求在历史中限制消息数量''' <source lang="xml"> <presence from='hag66@shakespeare.lit/pda' to='darkcave@chat.shakespeare.lit/thirdwitch'> <x xmlns='http://jabber.org/protocol/muc'> <history maxstanzas='20'/> </x> </presence> </source> '''例子 35. 用户请求最后三分钟的历史''' <source lang="xml"> <presence from='hag66@shakespeare.lit/pda' to='darkcave@chat.shakespeare.lit/thirdwitch'> <x xmlns='http://jabber.org/protocol/muc'> <history seconds='180'/> </x> </presence> </source> '''例子 36. 用户请求从Unix时代到现在的所有历史''' <source lang="xml"> <presence from='hag66@shakespeare.lit/pda' to='darkcave@chat.shakespeare.lit/thirdwitch'> <x xmlns='http://jabber.org/protocol/muc'> <history since='1970-01-01T00:00:00Z'/> </x> </presence> </source> 服务绝对不应该 SHOULD NOT 返回从Unix时代开始到现在的所有消息, 而应该 SHOULD 基于服务或房间的缺省值返回适当的有限数量的历史给用户. '''例子 37. 用户请求不发送历史''' <source lang="xml"> <presence from='hag66@shakespeare.lit/pda' to='darkcave@chat.shakespeare.lit/thirdwitch'> <x xmlns='http://jabber.org/protocol/muc'> <history maxchars='0'/> </x> </presence> </source> ===退出一个房间=== 为了退出一个多用户聊天房间, 一个房客发送一个类型为"unavailable"的出席信息节给正在使用这个房间的 <room@service/nick> . '''例子 38. 房客退出一个房间''' <source lang="xml"> <presence from='hag66@shakespeare.lit/pda' to='darkcave@chat.shakespeare.lit/thirdwitch' type='unavailable'/> </source> 服务必须 MUST 接着从要离开的房客的房间JID发送"unavailable"类型的出席信息节给这个要离开的房客的全JID们以及留在房间的房客们: '''例子 39. 服务发送和离开的房客有关的出席信息''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit/thirdwitch' to='hag66@shakespeare.lit/pda' type='unavailable'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='none'/> <status code='110'/> </x> </presence> <presence from='darkcave@chat.shakespeare.lit/thirdwitch' to='crone1@shakespeare.lit/desktop' type='unavailable'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='none'/> </x> </presence> <presence from='darkcave@chat.shakespeare.lit/thirdwitch' to='wiccarocks@shakespeare.lit/laptop' type='unavailable'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='none'/> </x> </presence> </source> 由房间反射的类型为"unavailable"的出席信息节必须 MUST 包含扩展的关于角色和级别的出席信息; 'role'属性值应该 SHOULD 被设为 "none" 以表示这个人不再是一个房客了. 房客可以 MAY 在出席信息节包含一个常规的 <status/> 信息; 这使房客能在必要的情况下提供一个自定的退出消息: '''例子 40. 自定的退出消息''' <source lang="xml"> <presence from='wiccarocks@shakespeare.lit/laptop' to='darkcave@chat.shakespeare.lit/oldhag' type='unavailable'> <status>gone where the goblins go</status> </presence> </source> 常规的出席信息节生成规则定义在 [[RFC3921|XMPP IM]] [[XEP-0045#附录G;备注|14]], 所以如果用户发送一个一般的不可用出席信息节, 用户的服务器将广播那个节到 <room@service/nick> ,而该用户之前曾经发送过直接出席信息给这个<room@service/nick>. 有可能一个用户不能正常地通过直接发送不可用信息给一个房间来退出该房间. 如果该用户没有发送不可用出席信息就下线了, 用户的服务器负责代替该用户发送不可用出席信息 (依据 RFC 3921). 如果该用户的服务器下线或该用户的服务器和该用户连接的MUC服务失去连接(例如, 在联邦通信), 这个MUC服务负责监视它收到的错误信息节以确定该用户是否下线. 如果该MUC服务确定该用户已下线, 它必须 must 当成该用户自己发送了不可用信息一样地处理这个用户. 注意: 如果房间不是持久的并且该房客是最后一个退出的, 服务负责销毁这个房间. ===更改昵称=== 多用户聊天室的一个常用功能是一个房客能修改自己在房间里的昵称. 在 MUC 里这需要发送一个更新出席信息给房间, 具体来说是在相同的房间里发送出席信息给一个新的房间JID (变更的只是这个房间JID的资源). '''例子 41. 房客修改昵称''' <source lang="xml"> <presence from='hag66@shakespeare.lit/pda' to='darkcave@chat.shakespeare.lit/oldhag'/> </source> 服务接着发送两个出席信息节给每个房客的全JID(包括修改自己昵称的房客本身), 一个是类型为"unavailable"的用于旧的昵称另一个指明新昵称可用了. 这个不可用出席信息必须 MUST 在一个满足'http://jabber.org/protocol/muc#user' 名字空间的 <x/> 子元素里面包含以下扩展的出席信息 : * 新昵称(在这个例子中, nick='oldhag') * 一个状态码 303 这使接受者能从旧昵称关联到新昵称. '''例子 42. 服务更新昵称''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit/thirdwitch' to='crone1@shakespeare.lit/desktop' type='unavailable'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' jid='hag66@shakespeare.lit/pda' nick='oldhag' role='participant'/> <status code='303'/> </x> </presence> <presence from='darkcave@chat.shakespeare.lit/thirdwitch' to='wiccarocks@shakespeare.lit/laptop' type='unavailable'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' jid='hag66@shakespeare.lit/pda' nick='oldhag' role='participant'/> <status code='303'/> </x> </presence> <presence from='darkcave@chat.shakespeare.lit/thirdwitch' to='hag66@shakespeare.lit/pda' type='unavailable'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' jid='hag66@shakespeare.lit/pda' nick='oldhag' role='participant'/> <status code='303'/> <status code='110'/> </x> </presence> <presence from='darkcave@chat.shakespeare.lit/oldhag' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' jid='hag66@shakespeare.lit/pda' role='participant'/> </x> </presence> <presence from='darkcave@chat.shakespeare.lit/oldhag' to='wiccarocks@shakespeare.lit/laptop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' jid='hag66@shakespeare.lit/pda' role='participant'/> </x> </presence> <presence from='darkcave@chat.shakespeare.lit/oldhag' to='hag66@shakespeare.lit/pda'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' jid='hag66@shakespeare.lit/pda' role='participant'/> <status code='110'/> </x> </presence> </source> 如果该用户尝试修改他或她的房间昵称,但这个昵称已经被其他用户使用了 (或者这个昵称是被这房间的其他用户级别保留的, 例如, 一个成员或者所有者), 服务必须 MUST 拒绝这次昵称修改并通知该用户这一冲突; 也就是返回一个类型为 "error" 的出席信息节指明 <conflict/> 错误条件: '''例子 43. 服务拒绝昵称修改,因为昵称冲突''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='error'> <x xmlns='http://jabber.org/protocol/muc'/> <error type='cancel'> <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence> </source> 无论如何, 如果现有房客的纯JID <localpart@domain.tld> 和尝试变更昵称的房客的纯JID相同, 那么服务可以 MAY 允许昵称变更. 详见本文的[[XEP-0045#昵称冲突|昵称冲突]]章节. 如果该用户尝试变更自己的昵称但是房间昵称被锁定了("locked down"), 服务必须 MUST 拒绝这个昵称变更请求并返回一个"error"类型的出席信息节,指明一个 <not-acceptable/> 错误条件: '''例子 44. 服务拒绝昵称变更,因为房间昵称被锁定''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda' type='error'> <x xmlns='http://jabber.org/protocol/muc'/> <error type='cancel'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence> </source> 用户应该 SHOULD 接着发现它的保留昵称,如本文的 [[XEP-0045#发现保留的房间昵称|发现保留的房间昵称]]章节所述. ===更改可用性状态=== 在一个多用户聊天系统里例如IRC, 一个常用的修改某人房间昵称的行为也意味着变更某人的可用性(例如, 变更某人的房间昵称为"thirdwitch|away"). 在Jabber里面, 可用性当然是通过出席信息 (中 <show/> 和 <status/> 元素)的变更来通知的, 这能提供重要的上下文给聊天室. 一个房客通过发送更新的出席信息给它自己的<room@service/nick>来改变他在房间内的可用性状态. '''例子 45. 房客变更可用性状态''' <source lang="xml"> <presence from='wiccarocks@shakespeare.lit/laptop' to='darkcave@chat.shakespeare.lit/oldhag'> <show>xa</show> <status>gone where the goblins go</status> </presence> </source> 服务然后从该房客发送一个出席信息节来修改他或她的出席信息给每个房客的全JID, 包含扩展的出席信息,包括这个房客的角色和全JID(给那些有权知道的人): '''例子 46. 服务传递修改的出席信息给所有房客''' <source lang="xml"> <presence from='darkcave@chat.shakespeare.lit/secondwitch' to='crone1@shakespeare.lit/desktop'> <show>xa</show> <status>gone where the goblins go</status> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='admin' jid='wiccarocks@shakespeare.lit/laptop' role='moderator'/> </x> </presence> [ ... ] </source> ===邀请其他用户进入一个房间=== ====直接邀请==== 一个办法是发送一个直接的邀请(而不是由房间本身来间接邀请),定义在[http://xmpp.org/extensions/xep-0249.html Direct MUC Invitations] [XEP-0045#附录G:备注|15]]. 直接发送邀请有助于适应被邀请者那一边的通信阻塞(对方可能拒绝和和不在好友名单中的实体通信). ====间接邀请==== 邀请别的用户到一个房间成为房客是很有用的. 为了做到这一点, 一个 MUC 客户端必须 MUST 发送以下格式的 XML 给 <room@service> 本身 (原因(reason)是可选的 OPTIONAL 而消息(message)的类型必须 MUST 是显式或隐式的"normal"类型): '''例子 47. 房客通过房间发送一个邀请''' <source lang="xml"> <message from='crone1@shakespeare.lit/desktop' to='darkcave@chat.shakespeare.lit'> <x xmlns='http://jabber.org/protocol/muc#user'> <invite to='hecate@shakespeare.lit'> <reason> Hey Hecate, this is the place for all good witches! </reason> </invite> </x> </message> </source> <room@service> 本身必须 MUST 接着增加一个 'from' 地址到 <invite/> 元素,其值为邀请者的纯JID, 全JID, 或房间JID,并发送邀请给 'to' 地址所指明的被邀请者(为了旧的客户端,服务可以 MAY 包含一个消息主体"message body"解释这个邀请或包含一个原因"reason"(子元素); 另外, 房间应该 SHOULD 增加 password 如果该房间是密码保护的): '''例子 48. 房间代表邀请者发送邀请给被邀请者''' <source lang="xml"> <message from='darkcave@chat.shakespeare.lit' to='hecate@shakespeare.lit'> <x xmlns='http://jabber.org/protocol/muc#user'> <invite from='crone1@shakespeare.lit/desktop'> <reason> Hey Hecate, this is the place for all good witches! </reason> </invite> <password>cauldronburn</password> </x> </message> </source> 如果房间是仅限成员的, 服务可以 MAY 同时把这个被邀请者加入成员列表. (注意: 在仅限成员的房间里邀请的权力应该 SHOULD 由房间管理员限定; 如果一个没有权限的成员修改成员列表试图邀请别的用户, 服务应该 SHOULD 返回一个 <forbidden/> 错误给该房客; 详见本文的[[XEP-0045#修改成员列表|修改成员列表]]章节.) 如果邀请者提供了一个不存在的JID, 房间应该 SHOULD 返回一个 <item-not-found/> 错误给邀请者. 被邀请者可以 MAY 选择正式地拒绝 (反之则忽略) 邀请; 这是发送者希望看到的正式的通知. 为了拒绝这个邀请, 被邀请者必须 MUST 发送以下格式的消息给 <room@service> 本身: '''例子 49. 被邀请者谢绝邀请''' <source lang="xml"> <message from='hecate@shakespeare.lit/broom' to='darkcave@chat.shakespeare.lit'> <x xmlns='http://jabber.org/protocol/muc#user'> <decline to='crone1@shakespeare.lit'> <reason> Sorry, I'm too busy right now. </reason> </decline> </x> </message> </source> '''例子 50. 房间通知邀请者邀请被拒绝了''' <source lang="xml"> <message from='darkcave@chat.shakespeare.lit' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <decline from='hecate@shakespeare.lit'> <reason> Sorry, I'm too busy right now. </reason> </decline> </x> </message> </source> 可能(有人)想知道为什么被邀请者不直接发送拒绝消息给访问者. 主要原因是特定的实现可能 MAY 选择让邀请基于房间JIDs而不是纯JIDs (所以, 例如, 一个房客可能从一个房间邀请某人到另一个房间而不需要知道这个人的纯JID). 因而服务必须 MUST 同时处理邀请和拒绝. ===把一对一聊天转为多用户会议=== 有时候人们需要把一个一对一的聊天转成一个多用户的会议. 以下例子展示了这个流程. 首先, 两个用户开始一个一对一聊天. '''例子 51. 一个一对一聊天''' <source lang="xml"> <message from='crone1@shakespeare.lit/desktop' to='wiccarocks@shakespeare.lit/laptop' type='chat'> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> <body>Thrice the brinded cat hath mew'd.</body> </message> <message from='wiccarocks@shakespeare.lit/laptop' to='crone1@shakespeare.lit/desktop' type='chat'> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> <body>Thrice and once the hedge-pig whined.</body> </message> </source> 现在第一个用户决定加入第三个人到这个讨论, 所以她 (或, 更准确地说, 她的客户端) 做以下事情: # 新建一个多用户聊天室 # 可选地发送一对一聊天的历史到房间 # 发送一个邀请给第二个人和第三个人, 包含一个 <continue/> 元素 (可选地包含一个 'thread' 属性). 注意: 新房间应该 SHOULD 是非匿名的, 可以 MAY 是一个即时房间(定义于本文的[[XEP-0045#新建即时房间|新建即时房间]]章节), 也可以 MAY 有一个从服务接收的唯一房间名(定义于本文的[[XEP-0045#请求唯一的房间名|请求唯一的房间名]]章节. 注意: 如果这个一对一的聊天消息包含了一个 <thread/> 元素, 这个新建房间的人应该 SHOULD 在历史消息中包含这个 ThreadID, 在邀请中把这个 ThreadID 的值赋予 <continue/> 元素的 'thread' 属性, 并把这 ThreadID 包含在任何新的消息中发送到房间. ThreadIDs 的使用是推荐的 RECOMMENDED ,因为它帮助提供一对一聊天和多用户聊天的连续性. '''例子 52. 继续讨论 I: 用户新建房间''' <source lang="xml"> <presence from='crone1@shakespeare.lit/desktop' to='darkcave@chat.shakespeare.lit/firstwitch'> <x xmlns='http://jabber.org/protocol/muc'/> </presence> <presence from='darkcave@chat.shakespeare.lit/firstwitch' to='crone1@shakespeare.lit/desktop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='owner' role='moderator'/> <status code='110'/> </x> </presence> </source> '''例子 53. 继续讨论 II: 所有者发送历史到房间''' <source lang="xml"> <message from='crone1@shakespeare.lit/desktop' to='darkcave@chat.shakespeare.lit' type='groupchat'> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> <body>Thrice the brinded cat hath mew'd.</body> <delay xmlns='urn:xmpp:delay' from='crone1@shakespeare.lit/desktop' stamp='2004-09-29T01:54:37Z'/> </message> <message from='crone1@shakespeare.lit/desktop' to='darkcave@chat.shakespeare.lit' type='groupchat'> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> <body>Thrice and once the hedge-pig whined.</body> <delay xmlns='urn:xmpp:delay' from='wiccarocks@shakespeare.lit/laptop' stamp='2004-09-29T01:55:21Z'/> </message> </source> 注意: 使用 Delayed Delivery 协议使房间创建者能够从他一对一聊天历史指明每个消息的日期时间 datetime (通过 'stamp' 属性), 以及每个消息的原始发送者的 JID (通过'from' 属性). 房间创建者应该 SHOULD 在邀请额外的用户到房间之前发送完整的一对一聊天历史, 并且也应该 SHOULD 把第二个人加入该房间之前和第一个人在一对一聊天界面中出现的任何消息当成历史来发送; 如果这个一对一历史特别的大, 发送的客户端可能希望在数秒内发送这个历史而不是一次性发送所有历史(以to 避免触发频率限制). 服务不应该 SHOULD NOT 在从房间所有者接收的历史消息之前添加它自己的延迟元素"delay elements" (见本文的[[XEP-0045#讨论历史|讨论历史]]章节) . '''例子 54. 继续讨论 III: 所有者发送邀请(们), 包含 Continue 标志''' <source lang="xml"> <message from='crone1@shakespeare.lit/desktop' to='darkcave@chat.shakespeare.lit'> <x xmlns='http://jabber.org/protocol/muc#user'> <invite to='wiccarocks@shakespeare.lit/laptop'> <reason>This coven needs both wiccarocks and hag66.</reason> <continue thread='e0ffe42b28561960c6b12b944a092794b9683a38'/> </invite> <invite to='hag66@shakespeare.lit'> <reason>This coven needs both wiccarocks and hag66.</reason> <continue thread='e0ffe42b28561960c6b12b944a092794b9683a38'/> </invite> </x> </message> </source> 注意: 当邀请者的客户端一知道和它一对一聊天的那个人的全JID之后, 它就应该 SHOULD 在邀请中包含这个全JID (而不是纯JID). 邀请被递送到被邀请者: '''例子 55. 邀请被递送''' <source lang="xml"> <message from='darkcave@chat.shakespeare.lit'> to='wiccarocks@shakespeare.lit/laptop'> <x xmlns='http://jabber.org/protocol/muc#user'> <invite from='crone1@shakespeare.lit'> <reason>This coven needs both wiccarocks and hag66.</reason> <continue thread='e0ffe42b28561960c6b12b944a092794b9683a38'/> </invite> </x> </message> <message from='darkcave@chat.shakespeare.lit'> to='hag66@shakespeare.lit'> <x xmlns='http://jabber.org/protocol/muc#user'> <invite from='crone1@shakespeare.lit'> <reason>This coven needs both wiccarocks and hag66.</reason> <continue thread='e0ffe42b28561960c6b12b944a092794b9683a38'/> </invite> </x> </message> </source> 当客户端被 <wiccarocks@shakespeare.lit/laptop> 用来接收邀请, 它应该 SHOULD 自动加入或提示用户是否加入 (取决于用户的选项配置) 并且随后无缝地把现有的一对一聊天窗口转到一个多用户会议的窗口: '''例子 56. 被邀请者接受邀请, 加入房间, 并接收出席信息和历史''' <source lang="xml"> <presence from='wiccarocks@shakespeare.lit/laptop' to='darkcave@chat.shakespeare.lit/secondwitch'> <x xmlns='http://jabber.org/protocol/muc'/> </presence> <presence from='darkcave@chat.shakespeare.lit/firstwitch' to='wiccarocks@shakespeare.lit/laptop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='owner' role='moderator'/> </x> </presence> <presence from='darkcave@chat.shakespeare.lit/secondwitch' to='wiccarocks@shakespeare.lit/laptop'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='participant'/> </x> </presence> <message from='darkcave@chat.shakespeare.lit' to='wiccarocks@shakespeare.lit/laptop' type='groupchat'> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> <body>Thrice the brinded cat hath mew'd.</body> <delay xmlns='urn:xmpp:delay' from='crone1@shakespeare.lit/desktop' stamp='2004-09-29T01:54:37Z'/> </message> <message from='darkcave@chat.shakespeare.lit' to='wiccarocks@shakespeare.lit/laptop' type='groupchat'> <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread> <body>Thrice and once the hedge-pig whined.</body> <delay xmlns='urn:xmpp:delay' from='wiccarocks@shakespeare.lit/laptop' stamp='2004-09-29T01:55:21Z'/> </message> </source> 注意: 事实上,这些消息从 <room@service> 本身而不是 <room@service/nick> 发出,告诉这些接收的客户端这些消息是优先的聊天历史, 因为任何来自房客的消息的 'from' 地址应该等于发送者的房间JID. ===房客修改房间标题=== 如果房间配置允许, 一个房客可以 MAY 被允许修改一个房间的主题. 详见本文的[[XEP-0045#修改房间主题|修改房间主题]]章节. ===发送私有消息=== 因为每个房客有一个唯一的房间JID, 一个房客可以 MAY 发送一个私有消息 "private message" 给选定的房客,即通过服务发送一个消息给那房客的房间JID. 这个消息类型应该 SHOULD 是 "chat" 并且不能 MUST NOT 是 "groupchat", 但是可以 MAY 不表明 (即, 一个常规"normal"消息). 这个权力应该 SHOULD 被任何房客允许 (甚至在一个被主持的房间里的游客). '''例子 57. 房客发送私有消息''' <source lang="xml"> <message from='wiccarocks@shakespeare.lit/laptop' to='darkcave@chat.shakespeare.lit/firstwitch' type='chat'> <body>I'll give thee a wind.</body> </message> </source> 服务负责把'from'地址改为发送者的房间JID并递送这个消息到预期的接收者的全JID. '''例子 58. 接收者接收私有消息''' <source lang="xml"> <message from='darkcave@chat.shakespeare.lit/secondwitch' to='crone1@shakespeare.lit/desktop' type='chat'> <body>I'll give thee a wind.</body> </message> </source> 如果发送者尝试发送一个类型为 "groupchat" 的私有消息给特定的房客, 服务必须 MUST 拒绝递送这个消息 (因为接收者的客户端期望的房间内的消息类型为"groupchat") 并且返回一个 <bad-request/> 错误给发送者: '''例子 59. 房客尝试发送类型为"Groupchat"的私有消息给特定的房客''' <source lang="xml"> <message from='wiccarocks@shakespeare.lit/laptop' to='darkcave@chat.shakespeare.lit/firstwitch' type='groupchat'> <body>I'll give thee a wind.</body> </message> <message from='darkcave@chat.shakespeare.lit' to='wiccarocks@shakespeare.lit/laptop' type='error'> <body>I'll give thee a wind.</body> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </message> </source> 如果发送者尝试发送一个私有消息给一个不存在的房间JID, 服务必须 MUST 返回一个 <item-not-found/> 错误给发送者. 如果发送者不是预期的接收者正在访问的那个房间的房客, 服务必须 MUST 返回一个 <not-acceptable/> 错误给发送者. ===发送消息给所有房客=== 房客发送一个消息给所有房间内的房客的方法,是发送一个类型为 "groupchat" 的消息到 <room@service> 本身 (服务可以 MAY 忽略或拒绝类型不是 "groupchat" 的消息). 在一个被主持的房间, 这个权力限于角色为与会者或更高的房客拥有. '''例子 60. 房客发送一个消息给所有房客''' <source lang="xml"> <message from='hag66@shakespeare.lit/pda' to='darkcave@chat.shakespeare.lit' type='groupchat'> <body>Harpier cries: 'tis time, 'tis time.</body> </message> </source> 如果发送者在这个房间有发言权 (在被主持的房间里缺省是这样期望), 服务必须 MUST 修改发送者的 'from' 属性成为房间JID并反射这个消息到每个房客的全JID. '''例子 61. 服务反射消息给所有房客''' <source lang="xml"> <message from='darkcave@chat.shakespeare.lit/thirdwitch' to='crone1@shakespeare.lit/desktop' type='groupchat'> <body>Harpier cries: 'tis time, 'tis time.</body> </message> <message from='darkcave@chat.shakespeare.lit/thirdwitch' to='wiccarocks@shakespeare.lit/laptop' type='groupchat'> <body>Harpier cries: 'tis time, 'tis time.</body> </message> <message from='darkcave@chat.shakespeare.lit/thirdwitch' to='hag66@shakespeare.lit/pda' type='groupchat'> <body>Harpier cries: 'tis time, 'tis time.</body> </message> </source> 如果发送者是个游客 即, 在一个被主持的房间里没有发言权), 服务可以 MAY 返回一个 <forbidden/> 错误给发送者并且不能 MUST NOT 反射这个消息给所有房客. 如果发送者不是该房间的房客, 服务应该 SHOULD 返回一个 <not-acceptable/> 错误给发送者并且不应该 SHOULD NOT 反射这个消息给所有房客; 这个规则的唯一的例外是,一个实现可以 MAY 允许用户们拥有特定的权限 (例如, 一个房间拥有者, 房间管理员, 或服务级别的管理员) 发送消息到这个房间,即使那些用户不是房客. ===注册一个房间=== ===获取成员列表=== ===发现保留的房间昵称=== ===请求发言权=== '''文档信息''' 系列: [[XMPP扩展]] 编号: 0045 发行者: [XMPP文档列表/XMPP标准基金会] 状态: 草案 类型: 标准跟踪 版本: 1.24 最后更新日期: 2008-07-16 批准机构: [XMPP文档列表/XMPP理事会] 依赖于: [[XMPP Core]], [[XMPP IM]], XEP-0004, XEP-0030, XEP-0068, XEP-0082, XEP-0128 上文: 无 下文: 无 简称: muc muc名字空间的XML方案: <http://www.xmpp.org/schemas/muc.xsd> muc#admin名字空间的XML方案: <http://www.xmpp.org/schemas/muc-admin.xsd> muc#owner名字空间的XML方案: <http://www.xmpp.org/schemas/muc-owner.xsd> muc#unique名字空间的XML方案: <http://www.xmpp.org/schemas/muc-unique.xsd> muc#user名字空间的XML方案: <http://www.xmpp.org/schemas/muc-user.xsd> 注册项: <http://www.xmpp.org/registrar/muc.html> Wiki页: <http://wiki.jabber.org/index.php/Multi-User Chat (XEP-0045)> '''作者信息''' :'''Peter Saint-Andre''' :Email: [mailto:stpeter@jabber.org stpeter@jabber.org] :JabberID: [xmpp:stpeter@jabber.org stpeter@jabber.org] '''法律通告''' '''版权''' XMPP扩展协议的版权(1999-2008)归XMPP标准化基金会(XSF)所有. '''权限''' 特此授权,费用全免,对任何获得本协议副本的人,对使用本协议没有限制,包括不限制在软件程序中实现本协议,不限制在网络服务中布署本协议,不限制拷贝,修改,合并,发行,翻译,分发,转授,或销售本协议的副本,被允许使用本协议做了以上工作的人士,应接受前述的版权声明和本许可通知并且必须包含在所有的副本或实质性部分的规格中.除非单独的许可,被重新分发的修改工作,不得含有关于作者,标题,编号,或出版者的规格的误导性资料,并不得宣称修改工作是由本文的作者,作者所属的任何组织或项目,或XMPP标准基金会签注。 '''免责声明'''' 注意:本协议是提供的“原样”的基础,没有担保或任何形式的条件,明示或暗示,包括,但不限于任何担保或关于名称,非侵权性,适销性或适合作某一特定目的的条件.在任何情况XMPP标准基金会或作者不对此协议承担任何责任索赔,损害赔偿,或其他责任,无论是在一项行动的合同,侵权,或否则,所产生的,运出,或在他涉嫌与规格或执行,部署或以其它方式使用本协议. ## '''责任限制''' 在任何情况下以及没有任何法律规定时,不论是侵权行为(包括疏忽),合同或其它方面,除非根据适用法律的要求(如蓄意和有严重疏忽行为)或同意以书面形式,XMPP标准基金会或任何作者不对本协议承担所造成的损失,包括任何直接,间接,特殊,偶发,或相应的损害赔偿的任何字符利用所产生的或不能使用的规格(包括但不限于善意的损失,停止作业,电脑失灵或故障,或任何和所有其他商业损害或损失) ,即使XMPP标准基金会或作者已被告知此类损害的可能性。 '''知识产权的一致性''' XMPP扩展协议完全遵守XSF的知识产权策略(可在<http://www.xmpp.org/extensions/ipr-policy.shtml>找到副本或写信给XSF, P.O. Box 1641, Denver, CO 80201 USA). '''讨论地点''' 首选的讨论的地方是标准讨论邮件列表: <http://mail.jabber.org/mailman/listinfo/standards>. 勘误表发送到[mailto:editor@xmpp.org editor@xmpp.org] '''XMPP 相关信息''' XMPP 是由XSF(XMPP标准化基金会)按互联网标准程序贡献的,和 IETF的RFC 2026兼容的规范,包括 XMPP核心(RFC 3920)和 XMPP IM(RFC 3921).在本文中定义的任何协议,都是在互联网标准程序之外开发的,是扩展XMPP,而不是改变、发展和修改 XMPP本身. '''一致性术语''' 本文中以下关键词的含义如 RFC 2119 所述: "MUST", "SHALL", "REQUIRED"; "MUST NOT", "SHALL NOT"; "SHOULD", "RECOMMENDED"; "SHOULD NOT", "NOT RECOMMENDED"; "MAY", "OPTIONAL".
该页面使用的模板:
模板:XEP附录CDEF
(
查看源代码
) (保护)
返回到
XEP-0045
。
查看
页面
讨论
查看源代码
历史
个人工具
登录/创建账户
导航
首页
社区专页
新闻动态
最近更改
随机页面
帮助
XMPP资源
XMPP公共服务
XMPP客户端软件
XMPP服务器软件
友情链接
搜索
工具箱
链入页面
链出更改
特殊页面