MUC基础
概念
MUC(Multi User Chat),XMPP在其XEP-0045扩展中定义的一个用于多用户文本会议(群聊)的协议,类似于互联网中继聊天(IRC),提供通道或房间让大家能互相交流信息,并查看用户在线情况。
特征
1 每个参与者都可以分享消息(不包含游客Visitor)
2 每个参与者都可以获取聊天室的联系人名单
3 参与者通过昵称标识而不是真实的JabberID
4 聊天室内分享所有参与者的出席情况
5 参与者不局限于人(例:智能机器人)
名词
房间:房间的JID标识 <room@service>(例如:teaparty@conference. ejabberd.org),这里 “room” 是房间的名称而 “service” 是多用户聊天服务运行所在的主机名
房客:房客的JID标识<room@service/nick>,nick是房客在房间的昵称
岗位:表达了用户和房间的长期关系(永久),所有者(owner)-必须、管理者(admin)-推荐、成员(member)-推荐、排斥者(outcast)-推荐,岗位被授予,撤销,和维护都是基于用户的纯JID
角色:表达了用户和房间的临时联系,它只存在与一次访问期间(暂时),主持人(moderator)-必须、与会者(paticipant)-必须、游客(visitor)-推荐,角色的授予,撤销,和维护是基于房客的房间昵称或全JID,而不是纯JID
岗位角色权限
1 owener:包含admin所有功能以及指定admin及销毁聊天室
2 admin:能查看会话内容、发言、踢出参与者和游客并禁止他们进入聊天室、控制他人发言的权利, 查看成员真实的JID、指定member和moderator、重新配置聊天室信息等
3 member:可加入聊天室、查看会话内容、发言(member与participant的区别在于是否注册, member是已注册的用户)
4 outcast:无法进入聊天室,被某个聊天室禁止的用户
5 moderator:能查看会话内容、发言、踢出参与者和游客、控制他人发言的权利
6 participant:即可查看会话内容又可发言
7 visitor:能进入聊天室,查看会话内容但是无法发言
房间类型
1 Hidden Room(隐藏房间) – 一个无法被任何用户以普通方法如搜索和服务查询来发现的房间; 反义词: 公开(public)房间
2 Public Room(公开房间) – 用户可以通过普通方法如搜索和服务查询来发现的房间; 反义词: 隐藏房间
3 Members-Only Room(仅限会员的房间) – 如果一个用户不在成员列表中则无法加入的一个房间; 反义词: 开放(open)房间
4 Open Room(开放房间) – 任何人可以加入而不需要在成员列表中的房间; 反义词: 仅限会员的房间
5 Moderated Room(被主持的房间) – 只有有”发言权”的用户才可以发送消息给所有房客的房间; 反义词: 非主持的(Unmoderated)房间
6 Unmoderated Room(非主持的房间) – 任何房客都被允许发送消息给所有房客的房间; 反义词: 被主持的房间
7 Non-Anonymous Room(非匿名房间) – 一个房客的全JID会暴露给所有其他房客的房间, 尽管房客可以选择任何期望的房间昵称; 相对的是半匿名(Semi-Anonymous)房间
8 Semi-Anonymous Room(半匿名房间) – 一个房客的全JID只能被房间管理员发现的房间; 相对的是非匿名(Non-Anonymous)房间
9 Password-Protected Room(密码保护房间) – 一个用户必须提供正确密码才能加入的房间; 反义词: 非保密房间
10 Unsecured Room(非保密房间) – 任何人不需要提供密码就可以进入的房间; 反义词: 密码保护房间
11 Persistent Room(持久房间) – 如果最后一个房客退出也不会被销毁的房间; 反义词: 临时房间
12 Temporary Room(临时房间) – 如果最后一个房客退出就会被销毁的房间; 反义词: 持久房间
XML架构协议及使用范围
1 http://jabber.org/protocol/muc
用户加入MUC房间,向房间服务发送出席信息
用户新建MUC房间,向房间服务发送出席信息
2 http://jabber.org/protocol/muc#user
用户与房间服务交互(例如:简介邀请,更新出席信息发送,消息通知)
3 http://jabber.org/protocol/muc#admin
房间管理层操作(例如:主持人用例,管理员用例,所有者用例)
4 http://jabber.org/protocol/muc#owner
房间所有者特有权限操作(例如:新建房间,修改房间配置,销毁房间)
5 http://jabber.org/protocol/muc#unique
申请唯一房间名
MUC协议分析
新建房间
若用户试图新建房间被拒绝访问, 服务则返回<not-allowed/> 错误(注:新建房间的权限可能限制在特定用户群或服务级别的管理员,)
<presence
from='darkcave@chat.shakespeare.lit/thirdwitch'
to='hag66@shakespeare.lit/pda'
type='error'>
<x xmlns='http://jabber.org/protocol/muc'/>
<error type='cancel'>
<not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</presence>
若访问不受限制,则按以下步骤新建群:
1 用户通过发送一个包含满足http://jabber.org/protocol/muc名字空间的空<x/> 子元素的出席信息到<room@service/nick> 声明其对MUC协议的支持
2 如果用户被允许新建房间且房间不存在, 服务根据缺省配置新建此房间, 指定请求的用户作为初始房间拥有者, 并增加这个拥有者到该房间但不允许任何别的用户进入该房间,锁定房间(locking)。从房间向所有者发送包含用户状态指定信息和房间已建立(状态码201标识)的出席信息节,然后等待配置
3 根据新建房间类型分为两种情况
a)如果初始的房间所有者想新建一个持久房间,则房间所有者发送类型为“get”包含一个遵循http://jabber.org/protocol/muc#owner名字空间的<query/>元素的IQ节给该房间请求房间配置 ,然后执行第4和第5步
b)如果初始的房间所有者想新建一个临时房间,则房间所有者发送类型为“set”遵循http://jabber.org/protocol/muc#owner名字空间并包含一个满足 ‘jabber:x:data’ 名字空间,类型为 “submit”的空的<x/> 元素的<query/>元素给该房间,然后跳到第6步
4 如果房间所有者请求了一个配置表格, 服务则发送一个包含配置表格并遵循 ‘jabber:x:data’名字空间的IQ给房间拥有者,如果没有配置选项可用, 房间则返回一个空的<query/>元素给房间所有者
5 初始的房间所有者填写配置表格(或接受缺省配置),通过发送“set”类型并包含完整配置表格的IQ完成配置(设置一个初始化配置的超时值, 如果房间所有者再给定的超时时间内未完成房间配置,房间所有者就被假定已经接受了缺省得配置或取消了配置过程)
6 若服务从初始房间所有者接收到完整准确的配置表格(或接收到了一个临时房间的请求),服务则解锁房间(unlock),允许其他用户进去,并发送“result”类型的IQ给房间所有者;若接收的配置表格违反一个或多个服务策略导致房间创建失败(例:密码保护房间,密码为空),服务返回<not-acceptable/>错误;若服务接收到了取消(指令),则销毁房间
以下展示新建房间协议流程:
首先,Jabber用户发送一个包含满足http://jabber.org/protocol/muc名字空间的空<x/> 子元素的出席信息到房间(寻求进入一个房间时也发送和这同样的节)
<presence
from='crone1@shakespeare.lit/desktop'
to='darkcave@chat.shakespeare.lit/firstwitch'>
<x xmlns='http://jabber.org/protocol/muc'/>
</presence>
如果该房间不存在,服务则新建这个房间(取决于关于新建房间的本地策略),指定发出请求的用户的纯JID成为所有者,添加这个所有者到房间,并通过发送以下格式的出席信息节承认房间新建成功
<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'/>
<status code='201'/>
</x>
</presence>
分支a,新建临时房间:发送类型为“set”遵循http://jabber.org/protocol/muc#owner名字空间并包含一个满足 ‘jabber:x:data’ 名字空间,类型为 “submit”的空的<x/> 元素的<query/>元素给该房间
<iq from='crone1@shakespeare.lit/desktop'
id='create1'
to='darkcave@chat.shakespeare.lit'
type='set'>
<query xmlns='http://jabber.org/protocol/muc#owner'>
<x xmlns='jabber:x:data' type='submit'/>
</query>
</iq>
分支b,新建持久房间:发送类型为“get”包含一个遵循http://jabber.org/protocol/muc#owner名字空间的<query/>元素的IQ节给该房间请求房间配置
<iq from='crone1@shakespeare.lit/desktop'
id='create1'
to='darkcave@chat.shakespeare.lit'
type='get'>
<query xmlns='http://jabber.org/protocol/muc#owner'/>
</iq>
若房间不存在, 服务则返回一个初始的房间配置表单给该用户(以下是个典型实例)
<iq from='darkcave@chat.shakespeare.lit'
id='create1'
to='crone1@shakespeare.lit/desktop'
type='result'>
<query xmlns='http://jabber.org/protocol/muc#owner'>
<x xmlns='jabber:x:data' type='form'>
<title>Configuration for "darkcave" Room</title>
<instructions>
Your room darkcave@macbeth has been created!
The default configuration is as follows:
- No logging
- No moderation
- Up to 20 occupants
- No password required
- No invitation required
- Room is not persistent
- Only admins may change the subject
- Presence broadcasted for all users
To accept the default configuration, click OK. To
select a different configuration, please complete
this form.
</instructions>
<field
type='hidden'
var='FORM_TYPE'>
<value>http://jabber.org/protocol/muc#roomconfig</value>
</field>
<field
label='Natural-Language Room Name'
type='text-single'
var='muc#roomconfig_roomname'/>
<field
label='Short Description of Room'
type='text-single'
var='muc#roomconfig_roomdesc'/>
<field
label='Natural Language for Room Discussions'
type='text-single'
var='muc#roomconfig_lang'/>
<field
label='Enable Public Logging?'
type='boolean'
var='muc#roomconfig_enablelogging'>
<value>0</value>
</field>
<field
label='Allow Occupants to Change Subject?'
type='boolean'
var='muc#roomconfig_changesubject'>
<value>0</value>
</field>
<field
label='Allow Occupants to Invite Others?'
type='boolean'
var='muc#roomconfig_allowinvites'>
<value>0</value>
</field>
<field
label='Maximum Number of Occupants'
type='list-single'
var='muc#roomconfig_maxusers'>
<value>20</value>
<option label='10'><value>10</value></option>
<option label='20'><value>20</value></option>
<option label='30'><value>30</value></option>
<option label='50'><value>50</value></option>
<option label='100'><value>100</value></option>
<option label='None'><value>none</value></option>
</field>
<field
label='Roles for which Presence is Broadcast'
type='list-multi'
var='muc#roomconfig_presencebroadcast'>
<value>moderator</value>
<value>participant</value>
<value>visitor</value>
<option label='Moderator'><value>moderator</value></option>
<option label='Participant'><value>participant</value></option>
<option label='Visitor'><value>visitor</value></option>
</field>
<field
label='Roles and Affiliations that May Retrieve Member List'
type='list-multi'
var='muc#roomconfig_getmemberlist'>
<value>moderator</value>
<value>participant</value>
<value>visitor</value>
<option label='Moderator'><value>moderator</value></option>
<option label='Participant'><value>participant</value></option>
<option label='Visitor'><value>visitor</value></option>
</field>
<field
label='Make Room Publicly Searchable?'
type='boolean'
var='muc#roomconfig_publicroom'>
<value>1</value>
</field>
<field
label='Make Room Persistent?'
type='boolean'
var='muc#roomconfig_persistentroom'>
<value>0</value>
</field>
<field
label='Make Room Moderated?'
type='boolean'
var='muc#roomconfig_moderatedroom'>
<value>0</value>
</field>
<field
label='Make Room Members-Only?'
type='boolean'
var='muc#roomconfig_membersonly'>
<value>0</value>
</field>
<field
label='Password Required to Enter?'
type='boolean'
var='muc#roomconfig_passwordprotectedroom'>
<value>0</value>
</field>
<field type='fixed'>
<value>
If a password is required to enter this room,
you must specify the password below.
</value>
</field>
<field
label='Password'
type='text-private'
var='muc#roomconfig_roomsecret'/>
<field
label='Who May Discover Real JIDs?'
type='list-single'
var='muc#roomconfig_whois'>
<option label='Moderators Only'>
<value>moderators</value>
</option>
<option label='Anyone'>
<value>anyone</value>
</option>
</field>
<field type='fixed'>
<value>
You may specify additional people who have
administrative privileges in the room. Please
provide one Jabber ID per line.
</value>
</field>
<field
label='Room Admins'
type='jid-multi'
var='muc#roomconfig_roomadmins'/>
<field type='fixed'>
<value>
You may specify additional owners for this
room. Please provide one Jabber ID per line.
</value>
</field>
<field
label='Room Owners'
type='jid-multi'
var='muc#roomconfig_roomowners'/>
</x>
</query>
</iq>
注意:_whois 配置选项指定该房间是非匿名的(值为“anyone”),半匿名的(值为“moderators”),还是全匿名的(值为“none”,不显示在这)
若没有配置选项,则服务返回空的<query/>元素给房间所有者
<iq from='darkcave@chat.shakespeare.lit'
id='create1'
to='crone1@shakespeare.lit/desktop'
type='result'>
<query xmlns='http://jabber.org/protocol/muc#owner'/>
</iq>
房间所有者填写完成表单返回提交给服务
<iq from='crone1@shakespeare.lit/desktop'
id='create2'
to='darkcave@chat.shakespeare.lit'
type='set'>
<query xmlns='http://jabber.org/protocol/muc#owner'>
<x xmlns='jabber:x:data' type='submit'>
<field var='FORM_TYPE'>
<value>http://jabber.org/protocol/muc#roomconfig</value>
</field>
<field var='muc#roomconfig_roomname'>
<value>A Dark Cave</value>
</field>
<field var='muc#roomconfig_roomdesc'>
<value>The place for all good witches!</value>
</field>
<field var='muc#roomconfig_enablelogging'>
<value>0</value>
</field>
<field var='muc#roomconfig_changesubject'>
<value>1</value>
</field>
<field var='muc#roomconfig_allowinvites'>
<value>0</value>
</field>
<field var='muc#roomconfig_maxusers'>
<value>10</value>
</field>
<field var='muc#roomconfig_publicroom'>
<value>0</value>
</field>
<field var='muc#roomconfig_persistentroom'>
<value>0</value>
</field>
<field var='muc#roomconfig_moderatedroom'>
<value>0</value>
</field>
<field var='muc#roomconfig_membersonly'>
<value>0</value>
</field>
<field var='muc#roomconfig_passwordprotectedroom'>
<value>1</value>
</field>
<field var='muc#roomconfig_roomsecret'>
<value>cauldronburn</value>
</field>
<field var='muc#roomconfig_whois'>
<value>moderators