模块ejabberd_c2s中,状态为session_established2。
用户发送iq set 消息到服务器
<iq id="M8QF3-52" type="set"><query xmlns="jabber:iq:roster"><item jid="1555215557@kinglong" name="1555215557"><group>Friends</group></item></query></iq>
服务器端匹配到
- case Name of
- ......
- To = xml:get_attr_s("to", Attrs),
- ToJID = case To of
- "" ->
- jlib:make_jid(User, Server, "");
- _ ->
- jlib:string_to_jid(To)
- end,
- ......
- "iq" ->
- case jlib:iq_query_info(NewEl) of
- #iq{xmlns = Xmlns} = IQ
- ....
- _ ->
- ejabberd_hooks:run(
- user_send_packet,
- Server,
- [FromJID, ToJID, NewEl]),
- check_privacy_route(FromJID, StateData, FromJID, ToJID, NewEl),
上面这段代码需要注意ToJID的解析,发送给服务器的消息一般是默认不带to 属性的,这种情况下会自动将to 补上(这关系到包的路由)。
ejabberd_hooks:run(user_send_packet 会记录用户发送的消息到日志。
check_privacy_route 中对权限进行效验,通过后调用ejabberd_router:route(FromRoute, To, Packet) 进行路由。
在ejabberd_router:route中调用do_route/3:
- ejabberd_hooks:run_fold(filter_packet,
- case mnesia:dirty_read(route, LDstDomain) of
注:此处会以LDstDomain = To#jid.lserver, 去router表查找是交由本地处理,还是交由其他server 处理。
在模块ejabberd_local 中使用ejabberd_router:register_route(Host, {apply, ?MODULE, route}) 对当前domain进行了登记,所以会路由到此模块,并调用相应的route函数。
- mnesia:write(#route{domain = LDomain,
- pid = Pid,
- local_hint = LocalHint})
- {apply, Module, Function} ->
- Module:Function(From, To, Packet);
客户端发送的iq消息初始to为空,但系统已自动补上ToJID(参见前面的说明),资源也为空的 iq,所以会路由到ejabberd_sm:route/3
- if
- To#jid.luser /= "" ->
- ejabberd_sm:route(From, To, Packet);
也就是说对于那些在session_established 之后发送的消息都会交由ejabberd_sm去路由。
在ejabberd_sm中由do_route/3 来处理:
- do_route(From, To, Packet) ->
- case LResource of
- "" ->
- case Name of
- ......
- "iq" ->
- process_iq(From, To, Packet);
ejabberd_sm 是一个gen_server 当调用register_iq_handler 接口时会向sm_iqtable 插入记录。
而这些命名空间处理模块的登记是由gen_iq_handler 模块的add_iq_handler/6 来进行的。
- -module(gen_iq_handler)
- add_iq_handler(Component, Host, NS, Module, Function, Type) ->
- case Type of
- no_queue ->
- Component:register_iq_handler(Host, NS, Module, Function, no_queue);
在mod_roster:start/2 中有如下代码:
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER,
- ?MODULE, process_iq, IQDisc).
可以看出mod_roster模块将自身的process_iq 函数在ejabberd_sm中注册为了jabber:iq:roster 命名空间的处理方法。
process_iq中针对发送到本地的iq调用process_local_iq/3:
- process_local_iq(From, To, #iq{type = Type} = IQ) ->
- case Type of
- set ->
- process_iq_set(From, To, IQ);
- get ->
- process_iq_get(From, To, IQ)
- end.
这里会对针对iq 的 type 进行不同处理,我们目前跟踪的是 <iq id="M8QF3-52" type="set">
- process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
- {xmlelement, _Name, _Attrs, Els} = SubEl,
- lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els),
- IQ#iq{type = result, sub_el = []}.
- process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
- JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
- #jid{user = User, luser = LUser, lserver = LServer} = From,
- case JID1 of
- error ->
- ok;
- _ ->
- JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
- LJID = jlib:jid_tolower(JID1),
- F = fun() ->
- Res = mnesia:read({roster, {LUser, LServer, LJID}}),
- Item = case Res of
- [] ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer},
- jid = JID};
- [I] ->
- I#roster{jid = JID,
- name = "",
- groups = [],
- xs = []}
- end,
- Item1 = process_item_attrs(Item, Attrs),
- Item2 = process_item_els(Item1, Els),
- case Item2#roster.subscription of <pre name="code" class="java"> %% 如果是删除
%%否则添加
_ ->mnesia:write(Item2)end,
%% If the item exist in shared roster, take the%% subscription information from there:
Item3 = ejabberd_hooks:run_fold(roster_process_item,LServer, Item2, [LServer]),
case roster_version_on_db(LServer)
oftrue -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha(term_to_binary(now()))});
false -> okend,{Item, Item3}end,
case mnesia:transaction(F)
of{atomic, {OldItem, Item}} -> push_item(User, LServer, To, Item),
case Item#roster.subscription
ofremove -> send_unsubscribing_presence(From, OldItem),
ok;_ -> ok end;
E -> ?DEBUG("ROSTER: roster item set error: ~p~n", [E]), ok end end;
以上就是一条添加/订阅 好友的iq 消息在ejabberd 中从接收到最终写入到数据库所经过的路径。