diff --git a/ChangeLog b/ChangeLog index e0676e15b..3fab5bf73 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2009-03-03 Christophe Romain + + * src/mod_pubsub/mod_pubsub.erl: Add roster subscriptions handling + so on_sub_and_presence if fully supported. + + * src/mod_pubsub/mod_pubsub.erl: Allow to send PEP events to all + connected ressources, even via s2s. + * src/mod_caps.erl: Likewise + 2009-02-27 Badlop * src/web/ejabberd_http.erl: Added a workaround for inet:peername diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 8e43c02d5..1eca49aab 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -35,7 +35,7 @@ note_caps/3, clear_caps/1, get_features/2, - get_user_resource/2, + get_user_resources/2, handle_disco_response/3]). %% gen_mod callbacks @@ -61,7 +61,7 @@ -record(caps, {node, version, exts}). -record(caps_features, {node_pair, features}). -record(user_caps, {jid, caps}). --record(user_caps_default, {uid, resource}). +-record(user_caps_resources, {uid, resource}). -record(state, {host, disco_requests = ?DICT:new(), feature_queries = []}). @@ -109,21 +109,17 @@ clear_caps(JID) -> BJID = list_to_binary(jlib:jid_to_string(JID)), BUID = list_to_binary(jlib:jid_to_string({U, S, []})), catch mnesia:dirty_delete({user_caps, BJID}), - case catch mnesia:dirty_read({user_caps_default, BUID}) of - [#user_caps_default{resource=R}] -> - catch mnesia:dirty_delete({user_caps_default, BUID}); - _ -> - ok - end. + catch mnesia:dirty_delete_object(#user_caps_resources{uid = BUID, resource = list_to_binary(R)}), + ok. %% give default user resource -get_user_resource(LUser, LServer) -> +get_user_resources(LUser, LServer) -> BUID = list_to_binary(jlib:jid_to_string({LUser, LServer, []})), - case catch mnesia:dirty_read({user_caps_default, BUID}) of - [#user_caps_default{resource=R}] -> - R; - _ -> - [] + case catch mnesia:dirty_read({user_caps_resources, BUID}) of + {'EXIT', _} -> + []; + Resources -> + lists:map(fun(#user_caps_resources{resource=R}) -> binary_to_list(R) end, Resources) end. %% note_caps should be called to make the module request disco @@ -180,9 +176,11 @@ init([Host, _Opts]) -> mnesia:create_table(user_caps, [{disc_copies, [node()]}, {attributes, record_info(fields, user_caps)}]), - mnesia:create_table(user_caps_default, + mnesia:create_table(user_caps_resources, [{disc_copies, [node()]}, - {attributes, record_info(fields, user_caps_default)}]), + {type, bag}, + {attributes, record_info(fields, user_caps_resources)}]), + mnesia:delete_table(user_caps_default), {ok, #state{host = Host}}. maybe_get_features(#caps{node = Node, version = Version, exts = Exts}) -> @@ -239,11 +237,11 @@ handle_cast({note_caps, From, mnesia:dirty_write(#user_caps{jid = BJID, caps = Caps}), case ejabberd_sm:get_user_resources(U, S) of [] -> - ok; - _ -> - % only store default resource of external contacts + % only store resources of caps aware external contacts BUID = list_to_binary(jlib:jid_to_string(jlib:jid_remove_resource(From))), - mnesia:dirty_write(#user_caps_default{uid = BUID, resource = R}) + mnesia:dirty_write(#user_caps_resources{uid = BUID, resource = list_to_binary(R)}); + _ -> + ok end, SubNodes = [Version | Exts], %% Now, find which of these are not already in the database. diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 0721ae65f..00079b26b 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -41,7 +41,7 @@ -module(mod_pubsub). -author('christophe.romain@process-one.net'). --version('1.12-01'). +-version('1.12-02'). -behaviour(gen_server). -behaviour(gen_mod). @@ -56,6 +56,7 @@ %% exports for hooks -export([presence_probe/3, + in_subscription/6, remove_user/2, disco_local_identity/5, disco_local_features/5, @@ -163,6 +164,7 @@ init([ServerHost, Opts]) -> ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), ejabberd_hooks:add(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50), + ejabberd_hooks:add(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50), IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), lists:foreach( @@ -417,6 +419,16 @@ presence_probe(#jid{lserver = Host} = JID, JID, Pid) -> presence_probe(_, _, _) -> ok. +%% ------- +%% subscription hooks handling functions +%% +in_subscription(Acc, User, Server, JID, subscribed, _) -> + Proc = gen_mod:get_module_proc(Server, ?PROCNAME), + gen_server:cast(Proc, {subscribed, User, Server, JID}), + Acc; +in_subscription(Acc, _, _, _, _, _) -> + Acc. + %% ------- %% user remove hook handling function %% @@ -518,6 +530,42 @@ handle_cast({presence, JID, Pid}, State) -> end, {noreply, State}; +handle_cast({subscribed, User, Server, JID}, State) -> + %% and send last PEP events published by JID + Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)), + Host = State#state.host, + ServerHost = State#state.server_host, + lists:foreach(fun(#pubsub_node{nodeid = {_, Node}, options = Options}) -> + case get_option(Options, send_last_published_item) of + on_sub_and_presence -> + lists:foreach(fun(Resource) -> + LJID = jlib:jid_tolower(jlib:make_jid(User, Server, Resource)), + case is_caps_notify(ServerHost, Node, LJID) of + true -> + Subscribed = case get_option(Options, access_model) of + open -> true; + presence -> true; + whitelist -> false; % subscribers are added manually + authorize -> false; % likewise + roster -> + Grps = get_option(Options, roster_groups_allowed, []), + element(2, get_roster_info(User, Server, LJID, Grps)) + end, + if Subscribed -> + send_last_item(Owner, Node, LJID); + true -> + ok + end; + false -> + ok + end + end, user_resources(User, Server)); + _ -> + ok + end + end, tree_action(Host, get_nodes, [Owner])), + {noreply, State}; + handle_cast({remove_user, LUser, LServer}, State) -> Host = State#state.host, Owner = jlib:make_jid(LUser, LServer, ""), @@ -582,6 +630,7 @@ terminate(_Reason, #state{host = Host, ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), ejabberd_hooks:delete(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50), + ejabberd_hooks:delete(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50), lists:foreach(fun({NS,Mod}) -> gen_iq_handler:remove_iq_handler(Mod, ServerHost, NS) @@ -2301,7 +2350,10 @@ broadcast_stanza(Host, NodeOpts, States, Stanza) -> %% broadcast Stanza to all contacts of the user that are advertising %% interest in this kind of Node. broadcast_by_caps({LUser, LServer, LResource}, Node, _Type, Stanza) -> - SenderResource = user_resource(LUser, LServer, LResource), + SenderResource = case LResource of + [] -> hd(user_resources(LUser, LServer)); + _ -> LResource + end, case ejabberd_sm:get_session_pid(LUser, LServer, SenderResource) of C2SPid when is_pid(C2SPid) -> %% set the from address on the notification to the bare JID of the account owner @@ -2311,14 +2363,17 @@ broadcast_by_caps({LUser, LServer, LResource}, Node, _Type, Stanza) -> %%ReplyTo = jlib:make_jid(LUser, LServer, SenderResource), % This has to be used case catch ejabberd_c2s:get_subscribed(C2SPid) of Contacts when is_list(Contacts) -> - lists:foreach(fun({U, S, R}) -> - LJID = {U, S, user_resource(U, S, R)}, - case is_caps_notify(LServer, Node, LJID) of - true -> - ejabberd_router ! {route, Sender, jlib:make_jid(LJID), Stanza}; - false -> - ok - end + lists:foreach(fun({U, S, _}) -> + JIDs = lists:foldl(fun(R, Acc) -> + LJID = {U, S, R}, + case is_caps_notify(LServer, Node, LJID) of + true -> [LJID | Acc]; + false -> Acc + end + end, [], user_resources(U, S)), + lists:foreach(fun(JID) -> + ejabberd_router ! {route, Sender, jlib:make_jid(JID), Stanza} + end, JIDs) end, Contacts); _ -> ok @@ -2333,15 +2388,11 @@ broadcast_by_caps(_, _, _, _) -> %% If we don't know the resource, just pick first if any %% If no resource available, check if caps anyway (remote online) -user_resource(LUser, LServer, []) -> - case ejabberd_sm:get_user_resources(LUser, LServer) of - [R|_] -> - R; - [] -> - mod_caps:get_user_resource(LUser, LServer) - end; -user_resource(_, _, LResource) -> - LResource. +user_resources(User, Server) -> + case ejabberd_sm:get_user_resources(User, Server) of + [] -> mod_caps:get_user_resources(User, Server); + Rs -> Rs + end. is_caps_notify(Host, Node, LJID) -> case mod_caps:get_caps(LJID) of