xmpp ejabberd - 查询用户存在
xmpp ejabberd - query for user presence
假设用户的订阅类型是 'both',有没有办法查询 XMPP 中的用户状态?
因为我是为移动平台构建的,所以我已经使用隐私列表阻止了所有传入的状态节。在我的用例中,一个用户至少会订阅 500 个用户,处理这么多的状态节会给移动设备带来很大压力。
因此,我不想处理所有用户节,而是希望仅在查询时获取用户的在线状态。
目前在 ejabberd 中没有这样的功能,但这绝对是您可以作为插件开发的功能。您可以编写一个插件,该插件将使用 HTTP 网络服务器处理 http 请求,并在回答用户存在之前执行您想要的任何处理和安全检查。
为了将来参考,我设法将一些代码整合在一起(感谢 mod_last.erl)并构建了一个模块,让您可以查询用户状态。建议和反馈将不胜感激。
-module(mod_query_presence).
-behaviour(gen_mod).
-export([start/2, stop/1,
process_sm_iq/3
]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("logger.hrl").
-include("mod_privacy.hrl").
-define(NS_QUERY_PRESENCE, <<"jabber:iq:qpresence">>).
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
one_queue),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_QUERY_PRESENCE, ?MODULE, process_sm_iq, IQDisc),
?INFO_MSG("Loading module 'mod_iqtest' v.01", []).
stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_QUERY_PRESENCE),
?INFO_MSG("Stoping module 'mod_iqtest' ", []).
process_sm_iq(From, To,
#iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
User = To#jid.luser,
Server = To#jid.lserver,
Resource = xml:get_tag_attr_s(list_to_binary("resource"), SubEl),
{Subscription, _Groups} =
ejabberd_hooks:run_fold(roster_get_jid_info, Server,
{none, []}, [User, Server, From]),
if (Subscription == both) or (Subscription == from) or
(From#jid.luser == To#jid.luser) and
(From#jid.lserver == To#jid.lserver) ->
UserListRecord =
ejabberd_hooks:run_fold(privacy_get_user_list, Server,
#userlist{}, [User, Server]),
case ejabberd_hooks:run_fold(privacy_check_packet,
Server, allow,
[User, Server, UserListRecord,
{To, From,
#xmlel{name = <<"presence">>,
attrs = [],
children = []}},
out])
of
allow -> get_presence(IQ, SubEl, User, Server, Resource);
deny ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}
end;
true ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}
end
end.
get_presence(IQ, SubEl, LUser, LServer, LResource) ->
case ejabberd_sm:get_session_pid(LUser, LServer, LResource) of
none ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
Pid ->
{_U, _Resource, Status, StatusText} = ejabberd_c2s:get_presence(Pid),
IQ#iq{type = result,
sub_el =
[#xmlel{name = <<"query">>,
attrs =
[{<<"xmlns">>, ?NS_QUERY_PRESENCE},
{<<"status">>, Status},
{<<"StatusText">>, StatusText}],
children = []}]}
end.
IQ 请求格式
<iq id='id' to='56876c654366178e0e75a8cd@192.168.1.150' type='get'>
<query xmlns='jabber:iq:qpresence' resource='Smack'/>
</iq>
用户在线时的 IQ 回复格式
<iq from='56876c654366178e0e75a8cd@192.168.1.150' to='56876c654366178e0e75a8cd@192.168.1.150/Smack' id='last1' type='result'>
<query xmlns='jabber:iq:qpresence' status='dnd' StatusText='YO'/>
</iq>
如果用户不在线,您将收到服务不可用错误。
假设用户的订阅类型是 'both',有没有办法查询 XMPP 中的用户状态?
因为我是为移动平台构建的,所以我已经使用隐私列表阻止了所有传入的状态节。在我的用例中,一个用户至少会订阅 500 个用户,处理这么多的状态节会给移动设备带来很大压力。
因此,我不想处理所有用户节,而是希望仅在查询时获取用户的在线状态。
目前在 ejabberd 中没有这样的功能,但这绝对是您可以作为插件开发的功能。您可以编写一个插件,该插件将使用 HTTP 网络服务器处理 http 请求,并在回答用户存在之前执行您想要的任何处理和安全检查。
为了将来参考,我设法将一些代码整合在一起(感谢 mod_last.erl)并构建了一个模块,让您可以查询用户状态。建议和反馈将不胜感激。
-module(mod_query_presence).
-behaviour(gen_mod).
-export([start/2, stop/1,
process_sm_iq/3
]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("logger.hrl").
-include("mod_privacy.hrl").
-define(NS_QUERY_PRESENCE, <<"jabber:iq:qpresence">>).
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
one_queue),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_QUERY_PRESENCE, ?MODULE, process_sm_iq, IQDisc),
?INFO_MSG("Loading module 'mod_iqtest' v.01", []).
stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_QUERY_PRESENCE),
?INFO_MSG("Stoping module 'mod_iqtest' ", []).
process_sm_iq(From, To,
#iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
User = To#jid.luser,
Server = To#jid.lserver,
Resource = xml:get_tag_attr_s(list_to_binary("resource"), SubEl),
{Subscription, _Groups} =
ejabberd_hooks:run_fold(roster_get_jid_info, Server,
{none, []}, [User, Server, From]),
if (Subscription == both) or (Subscription == from) or
(From#jid.luser == To#jid.luser) and
(From#jid.lserver == To#jid.lserver) ->
UserListRecord =
ejabberd_hooks:run_fold(privacy_get_user_list, Server,
#userlist{}, [User, Server]),
case ejabberd_hooks:run_fold(privacy_check_packet,
Server, allow,
[User, Server, UserListRecord,
{To, From,
#xmlel{name = <<"presence">>,
attrs = [],
children = []}},
out])
of
allow -> get_presence(IQ, SubEl, User, Server, Resource);
deny ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}
end;
true ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}
end
end.
get_presence(IQ, SubEl, LUser, LServer, LResource) ->
case ejabberd_sm:get_session_pid(LUser, LServer, LResource) of
none ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
Pid ->
{_U, _Resource, Status, StatusText} = ejabberd_c2s:get_presence(Pid),
IQ#iq{type = result,
sub_el =
[#xmlel{name = <<"query">>,
attrs =
[{<<"xmlns">>, ?NS_QUERY_PRESENCE},
{<<"status">>, Status},
{<<"StatusText">>, StatusText}],
children = []}]}
end.
IQ 请求格式
<iq id='id' to='56876c654366178e0e75a8cd@192.168.1.150' type='get'>
<query xmlns='jabber:iq:qpresence' resource='Smack'/>
</iq>
用户在线时的 IQ 回复格式
<iq from='56876c654366178e0e75a8cd@192.168.1.150' to='56876c654366178e0e75a8cd@192.168.1.150/Smack' id='last1' type='result'>
<query xmlns='jabber:iq:qpresence' status='dnd' StatusText='YO'/>
</iq>
如果用户不在线,您将收到服务不可用错误。