3
votes

Is there a way to query for user presence in XMPP, given that the user's subscription type is 'both'?

Since i am building for mobile platform, i have blocked all incoming presence stanzas using privacy list. In my use case, a user would be at least be subscribed to 500 users and processing these many presence stanzas would put a lot of stress on the mobile device.

So instead of processing all the user stanzas, i would like to get the presence for a user only when i query for it.

2

2 Answers

0
votes

There is no such feature at the moment inside ejabberd, but that's definitely something you can develop as a plugin. You can write a plugin that will be handling http requests using HTTP webserver and do whatever processing and security check you want before answering with the user presence.

0
votes

For future reference, i have managed to pull together some code(thanks to mod_last.erl) and build a module that lets you query for user presence. Suggestions & feedbacks will be highly appreciated.

-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 request format

<iq id='id' to='[email protected]' type='get'>
    <query xmlns='jabber:iq:qpresence' resource='Smack'/>
</iq>

IQ reply format if user is online

<iq from='[email protected]' to='[email protected]/Smack' id='last1' type='result'>
    <query xmlns='jabber:iq:qpresence' status='dnd' StatusText='YO'/>
</iq>

If the user is not online, you will get an service-unavailable error.