diff options
author | Christophe Romain <christophe.romain@process-one.net> | 2009-04-10 17:21:37 +0400 |
---|---|---|
committer | Christophe Romain <christophe.romain@process-one.net> | 2009-04-10 17:21:37 +0400 |
commit | 08cbaf8ccf310d562e9fa1af37ce2130b2e17b29 (patch) | |
tree | b7cf0f7a726a07d6126cdd188026519f97766ecd | |
parent | 6b2a838e244153f49d68332c0881e9ebb66c3170 (diff) |
Better handling of presence hook and caps clean
SVN Revision: 2015
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | src/ejabberd_c2s.erl | 12 | ||||
-rw-r--r-- | src/mod_caps.erl | 42 | ||||
-rw-r--r-- | src/mod_pubsub/mod_pubsub.erl | 359 | ||||
-rw-r--r-- | src/mod_pubsub/node_flat.erl | 6 |
5 files changed, 236 insertions, 192 deletions
@@ -1,3 +1,12 @@ +2009-03-20 Christophe Romain <christophe.romain@process-one.net> + + * src/mod_caps.erl: Better handling of presence hook and caps clean + * src/ejabberd_c2s.erl: Likewise + * src/mod_pubsub/mod_pubsub.erl: Likewise + + * src/mod_pubsub/mod_pubsub.erl: Improve invalid-payload check, send + last published item to new resource only, thread message sending. + 2009-04-09 Badlop <badlop@process-one.net> * src/msgs/pl.po: Fix some translations (thanks to Andrzej Smyk) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 91c257a7c..67a55757d 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -1062,18 +1062,6 @@ handle_info({route, From, To, Packet}, StateName, StateData) -> allow -> LFrom = jlib:short_prepd_jid(From), LBFrom = jlib:short_prepd_bare_jid(From), - %% Note contact availability - Els = Packet#xmlel.children, - case exmpp_presence:get_type(Packet) of - 'unavailable' -> - %mod_caps:clear_caps(From); - % caps clear disabled cause it breaks things - ok; - _ -> - ServerString = binary_to_list(StateData#state.server), - Caps = mod_caps:read_caps(Els), - mod_caps:note_caps(ServerString, From, Caps) - end, case ?SETS:is_element( LFrom, StateData#state.pres_a) orelse ?SETS:is_element( diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 902145625..875483807 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -53,6 +53,10 @@ -include_lib("exmpp/include/exmpp.hrl"). +%% hook handlers +-export([receive_packet/3, + receive_packet/4]). + -include("ejabberd.hrl"). -define(PROCNAME, ejabberd_mod_caps). @@ -157,6 +161,35 @@ stop(Host) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), gen_server:call(Proc, stop). +receive_packet(From, To, Packet) when ?IS_PRESENCE(Packet) -> + case exmpp_presence:get_type(Packet) of + 'probe' -> + ok; + 'error' -> + ok; + 'invisible' -> + ok; + 'subscribe' -> + ok; + 'subscribed' -> + ok; + 'unsubscribe' -> + ok; + 'unsubscribed' -> + ok; + 'unavailable' -> + clear_caps(From); + _ -> + ServerString = binary_to_list(StateData#state.server), + Els = Packet#xmlel.children, + note_caps(ServerString, From, read_caps(Els)) + end; +receive_packet(_, _, _) -> + ok. + +receive_packet(_JID, From, To, Packet) -> + receive_packet(From, To, Packet). + %%==================================================================== %% gen_server callbacks %%==================================================================== @@ -173,6 +206,10 @@ init([Host, _Opts]) -> {type, bag}, {attributes, record_info(fields, user_caps_resources)}]), mnesia:delete_table(user_caps_default), + mnesia:clear_table(user_caps), + mnesia:clear_table(user_caps_resources), + ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, receive_packet, 30), + ejabberd_hooks:add(s2s_receive_packet, Host, ?MODULE, receive_packet, 30), {ok, #state{host = Host}}. maybe_get_features(#caps{node = Node, version = Version, exts = Exts}) -> @@ -349,7 +386,10 @@ handle_disco_response(From, To, IQ_Rec) -> handle_info(_Info, State) -> {noreply, State}. -terminate(_Reason, _State) -> +terminate(_Reason, State) -> + Host = State#state.host, + ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, receive_packet, 30), + ejabberd_hooks:delete(s2s_receive_packet, Host, ?MODULE, receive_packet, 30), ok. code_change(_OldVsn, State, _Extra) -> diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 952f546fe..56ff84714 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -76,6 +76,8 @@ unsubscribe_node/5, publish_item/6, delete_item/4, + send_items/4, + broadcast_stanza/6, get_configure/5, set_configure/5, get_items/3, @@ -156,6 +158,7 @@ init([ServerHost, Opts]) -> ?DEBUG("pubsub init ~p ~p",[ServerHost,Opts]), Host = gen_mod:get_opt_host(ServerHost, Opts, "pubsub.@HOST@"), Access = gen_mod:get_opt(access_createnode, Opts, all), + IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), ServerHostB = list_to_binary(ServerHost), mod_disco:register_feature(ServerHost, ?NS_PUBSUB_s), ejabberd_hooks:add(disco_sm_identity, ServerHostB, ?MODULE, disco_sm_identity, 75), @@ -164,28 +167,17 @@ init([ServerHost, Opts]) -> ejabberd_hooks:add(presence_probe_hook, ServerHostB, ?MODULE, presence_probe, 50), ejabberd_hooks:add(roster_out_subscription, ServerHostB, ?MODULE, out_subscription, 50), ejabberd_hooks:add(remove_user, ServerHostB, ?MODULE, remove_user, 50), - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - lists:foreach( - fun({NS,Mod,Fun}) -> - gen_iq_handler:add_iq_handler( - Mod, ServerHostB, NS, ?MODULE, Fun, IQDisc) - end, - [{?NS_PUBSUB, ejabberd_sm, iq_sm}, - {?NS_PUBSUB_OWNER, ejabberd_sm, iq_sm}]), + gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHostB, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHostB, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc), ejabberd_router:register_route(Host), {Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts), - case lists:member("pep", Plugins) of + case lists:member(?PEPNODE, Plugins) of true -> - ejabberd_hooks:add(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75), - ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75), - ejabberd_hooks:add(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75), - lists:foreach( - fun({NS,Mod,Fun}) -> - gen_iq_handler:add_iq_handler( - Mod, ServerHost, NS, ?MODULE, Fun, IQDisc) - end, - [{?NS_PUBSUB, ejabberd_local, iq_local}, - {?NS_PUBSUB_OWNER, ejabberd_local, iq_local}]); + ejabberd_hooks:add(disco_local_identity, ServerHostB, ?MODULE, disco_local_identity, 75), + ejabberd_hooks:add(disco_local_features, ServerHostB, ?MODULE, disco_local_features, 75), + ejabberd_hooks:add(disco_local_items, ServerHostB, ?MODULE, disco_local_items, 75), + gen_iq_handler:add_iq_handler(ejabberd_local, ServerHostB, ?NS_PUBSUB, ?MODULE, iq_local, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, ServerHostB, ?NS_PUBSUB_OWNER, ?MODULE, iq_local, IQDisc); false -> ok end, @@ -494,63 +486,69 @@ handle_call(stop, _From, State) -> handle_cast({presence, JID}, State) -> %% A new resource is available. send last published items Host = State#state.host, + LJID = jlib:short_prepd_jid(JID), %% for each node From is subscribed to %% and if the node is so configured, send the last published item to From - lists:foreach(fun(Type) -> - {result, Subscriptions} = node_action(Type, get_entity_subscriptions, [Host, JID]), - lists:foreach( - fun({Node, subscribed, SubJID}) -> - case tree_action(Host, get_node, [Host, Node, JID]) of - #pubsub_node{options = Options} -> - case get_option(Options, send_last_published_item) of - on_sub_and_presence -> - send_last_item(Host, Node, SubJID); - _ -> - ok - end; - _ -> - ok - end; + spawn(fun() -> + lists:foreach(fun(Type) -> + {result, Subscriptions} = node_action(Type, get_entity_subscriptions, [Host, JID]), + lists:foreach( + fun({Node, subscribed, _SubJID}) -> + case tree_action(Host, get_node, [Host, Node, JID]) of + #pubsub_node{options = Options} -> + case get_option(Options, send_last_published_item) of + on_sub_and_presence -> + send_items(Host, Node, LJID, last); + _ -> + ok + end; + _ -> + ok + end; (_) -> - ok - end, Subscriptions) - end, State#state.plugins), + ok + end, Subscriptions) + end, State#state.plugins) + end), {noreply, State}; handle_cast({presence, User, Server, Resources, JID}, State) -> %% A new resource is available. send last published PEP items Owner = jlib:short_prepd_bare_jid(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 = {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, []), - {OU, OS, _} = Owner, - element(2, get_roster_info(OU, OS, LJID, Grps)) - end, - if Subscribed -> - send_last_item(Owner, Node, LJID); + spawn(fun() -> + 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 = {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, []), + {OU, OS, _} = Owner, + element(2, get_roster_info(OU, OS, LJID, Grps)) + end, + if Subscribed -> + send_items(Owner, Node, LJID, last); + true -> + ok + end; + false -> ok - end; - false -> - ok - end - end, Resources); - _ -> - ok - end - end, tree_action(ServerHost, get_nodes, [Owner, JID])), + end + end, Resources); + _ -> + ok + end + end, tree_action(ServerHost, get_nodes, [Owner, JID])) + end), {noreply, State}; handle_cast({remove_user, LUser, LServer}, State) -> @@ -611,6 +609,16 @@ terminate(_Reason, #state{host = Host, terminate_plugins(Host, ServerHost, Plugins, TreePlugin), ejabberd_router:unregister_route(Host), ServerHostB = list_to_binary(ServerHost), + case lists:member(?PEPNODE, Plugins) of + true -> + ejabberd_hooks:delete(disco_local_identity, ServerHostB, ?MODULE, disco_local_identity, 75), + ejabberd_hooks:delete(disco_local_features, ServerHostB, ?MODULE, disco_local_features, 75), + ejabberd_hooks:delete(disco_local_items, ServerHostB, ?MODULE, disco_local_items, 75), + gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHostB, ?NS_PUBSUB), + gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHostB, ?NS_PUBSUB_OWNER); + false -> + ok + end, ejabberd_hooks:delete(disco_local_identity, ServerHostB, ?MODULE, disco_local_identity, 75), ejabberd_hooks:delete(disco_local_features, ServerHostB, ?MODULE, disco_local_features, 75), ejabberd_hooks:delete(disco_local_items, ServerHostB, ?MODULE, disco_local_items, 75), @@ -620,12 +628,8 @@ terminate(_Reason, #state{host = Host, ejabberd_hooks:delete(presence_probe_hook, ServerHostB, ?MODULE, presence_probe, 50), ejabberd_hooks:delete(roster_out_subscription, ServerHostB, ?MODULE, out_subscription, 50), ejabberd_hooks:delete(remove_user, ServerHostB, ?MODULE, remove_user, 50), - lists:foreach(fun({NS,Mod}) -> - gen_iq_handler:remove_iq_handler(Mod, ServerHostB, NS) - end, [{?NS_PUBSUB, ejabberd_local}, - {?NS_PUBSUB_OWNER, ejabberd_local}, - {?NS_PUBSUB, ejabberd_sm}, - {?NS_PUBSUB_OWNER, ejabberd_sm}]), + gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHostB, ?NS_PUBSUB), + gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHostB, ?NS_PUBSUB_OWNER), mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB_s), ok. @@ -851,10 +855,7 @@ iq_disco_items(Host, Item, From) -> transaction(Host, Node, Action, sync_dirty) end. -iq_local(From, To, #iq{type = Type, - payload = SubEl, - ns = XMLNS, - lang = Lang} = IQ_Rec) -> +iq_local(From, To, #iq{type = Type, payload = SubEl, ns = XMLNS, lang = Lang} = IQ_Rec) -> ServerHost = exmpp_jid:ldomain_as_list(To), FromHost = exmpp_jid:ldomain_as_list(To), %% Accept IQs to server only from our own users. @@ -1102,14 +1103,14 @@ find_authorization_response(Packet) -> %% @spec (Host, JID, Node, Subscription) -> void %% Host = mod_pubsub:host() %% JID = jlib:jid() -%% Node = string() +%% SNode = string() %% Subscription = atom() %% Plugins = [Plugin::string()] %% @doc Send a message to JID with the supplied Subscription -send_authorization_approval(Host, JID, Node, Subscription) -> +send_authorization_approval(Host, JID, SNode, Subscription) -> Stanza = event_stanza( [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'subscription', attrs = - [?XMLATTR('node', Node), + [?XMLATTR('node', SNode), ?XMLATTR('jid', exmpp_jid:jid_to_binary(JID)), ?XMLATTR('subscription', subscription_to_string(Subscription))]}]), ejabberd_router ! {route, service_jid(Host), JID, Stanza}. @@ -1462,7 +1463,7 @@ subscribe_node(Host, Node, From, JID) -> {error, Error} -> {error, Error}; {result, {Result, subscribed, send_last}} -> - send_last_item(Host, Node, Subscriber), + send_items(Host, Node, Subscriber, last), case Result of default -> {result, Reply(subscribed)}; _ -> {result, Result} @@ -1552,12 +1553,12 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> PayloadSize > PayloadMaxSize -> %% Entity attempts to publish very large payload {error, extended_error('not-acceptable', "payload-too-big")}; + PayloadCount == 0 -> + %% Publisher attempts to publish to payload node with no payload + {error, extended_error('bad-request', "payload-required")}; PayloadCount > 1 -> %% Entity attempts to publish item with multiple payload elements {error, extended_error('bad-request', "invalid-payload")}; - Payload == "" -> - %% Publisher attempts to publish to payload node with no payload - {error, extended_error('bad-request', "payload-required")}; (DeliverPayloads == 0) and (PersistItems == 0) and (PayloadSize > 0) -> %% Publisher attempts to publish to transient notification node with item {error, extended_error('bad-request', "item-forbidden")}; @@ -1773,18 +1774,9 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) -> end, %% Generate the XML response (Item list), limiting the %% number of items sent to MaxItems: - ItemsEls = lists:map( - fun(#pubsub_item{itemid = {ItemId, _}, - payload = Payload}) -> - ItemAttrs = case ItemId of - "" -> []; - _ -> [?XMLATTR('id', ItemId)] - end, - #xmlel{ns = ?NS_PUBSUB, name = 'item', attrs = ItemAttrs, children = Payload} - end, lists:sublist(SendItems, MaxItems)), {result, [#xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children = [#xmlel{ns = ?NS_PUBSUB, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = - ItemsEls}]}]} + itemsEls(lists:sublist(SendItems, MaxItems))}]}]} end end. @@ -1794,14 +1786,6 @@ get_items(Host, Node, From) -> _ -> [] end. -%% @spec (Host, Node, LJID) -> any() -%% Host = host() -%% Node = pubsubNode() -%% LJID = {U, S, []} -%% @doc <p>Resend the last item of a node to the user.</p> -send_last_item(Host, Node, LJID) -> - send_items(Host, Node, LJID, last). - %% @spec (Host, Node, LJID, Number) -> any() %% Host = host() %% Node = pubsubNode() @@ -1834,17 +1818,9 @@ send_items(Host, Node, {LU, LS, LR} = LJID, Number) -> Items end end, - ItemsEls = lists:map( - fun(#pubsub_item{itemid = {ItemId, _}, payload = Payload}) -> - ItemAttrs = case ItemId of - "" -> []; - _ -> [?XMLATTR('id', ItemId)] - end, - #xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = ItemAttrs, children = Payload} - end, ToSend), Stanza = event_stanza( [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = - ItemsEls}]), + itemsEls(ToSend)}]), ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(LU, LS, LR), Stanza}. %% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response} @@ -2229,6 +2205,7 @@ event_stanza(Els) -> %%%%%% broadcast functions broadcast_publish_item(Host, Node, ItemId, _From, Payload) -> + %broadcast(Host, Node, none, true, 'items', ItemEls) Action = fun(#pubsub_node{options = Options, type = Type}) -> case node_call(Type, get_states, [Host, Node]) of @@ -2239,15 +2216,10 @@ broadcast_publish_item(Host, Node, ItemId, _From, Payload) -> true -> Payload; false -> [] end, - ItemAttrs = case ItemId of - "" -> []; - _ -> [?XMLATTR('id', ItemId)] - end, Stanza = event_stanza( [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = - [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = ItemAttrs, children = Content}]}]), - broadcast_stanza(Host, Options, States, Stanza), - broadcast_by_caps(Host, Node, Type, Stanza), + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = itemAttr(ItemId), children = Content}]}]), + broadcast_stanza(Host, Node, Type, Options, States, Stanza), {result, true}; _ -> {result, false} @@ -2258,6 +2230,7 @@ broadcast_publish_item(Host, Node, ItemId, _From, Payload) -> broadcast_retract_items(Host, Node, ItemIds) -> broadcast_retract_items(Host, Node, ItemIds, false). broadcast_retract_items(Host, Node, ItemIds, ForceNotify) -> + %broadcast(Host, Node, notify_retract, ForceNotify, 'retract', RetractEls) Action = fun(#pubsub_node{options = Options, type = Type}) -> case (get_option(Options, notify_retract) or ForceNotify) of @@ -2266,19 +2239,11 @@ broadcast_retract_items(Host, Node, ItemIds, ForceNotify) -> {result, []} -> {result, false}; {result, States} -> - RetractEls = lists:map( - fun(ItemId) -> - ItemAttrs = case ItemId of - "" -> []; - _ -> [?XMLATTR('id', ItemId)] - end, - #xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = ItemAttrs} - end, ItemIds), + RetractEls = [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = itemAttr(ItemId)} || ItemId <- ItemIds], Stanza = event_stanza( [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = RetractEls}]), - broadcast_stanza(Host, Options, States, Stanza), - broadcast_by_caps(Host, Node, Type, Stanza), + broadcast_stanza(Host, Node, Type, Options, States, Stanza), {result, true}; _ -> {result, false} @@ -2290,6 +2255,7 @@ broadcast_retract_items(Host, Node, ItemIds, ForceNotify) -> transaction(Host, Node, Action, sync_dirty). broadcast_purge_node(Host, Node) -> + %broadcast(Host, Node, notify_retract, false, 'purge', []) Action = fun(#pubsub_node{options = Options, type = Type}) -> case get_option(Options, notify_retract) of @@ -2300,8 +2266,7 @@ broadcast_purge_node(Host, Node) -> {result, States} -> Stanza = event_stanza( [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'purge', attrs = [?XMLATTR('node', node_to_string(Node))]}]), - broadcast_stanza(Host, Options, States, Stanza), - broadcast_by_caps(Host, Node, Type, Stanza), + broadcast_stanza(Host, Node, Type, Options, States, Stanza), {result, true}; _ -> {result, false} @@ -2313,6 +2278,7 @@ broadcast_purge_node(Host, Node) -> transaction(Host, Node, Action, sync_dirty). broadcast_removed_node(Host, Node) -> + %broadcast(Host, Node, notify_delete, false, 'delete', []) Action = fun(#pubsub_node{options = Options, type = Type}) -> case get_option(Options, notify_delete) of @@ -2323,8 +2289,7 @@ broadcast_removed_node(Host, Node) -> {result, States} -> Stanza = event_stanza( [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'delete', attrs = [?XMLATTR('node', node_to_string(Node))]}]), - broadcast_stanza(Host, Options, States, Stanza), - broadcast_by_caps(Host, Node, Type, Stanza), + broadcast_stanza(Host, Node, Type, Options, States, Stanza), {result, true}; _ -> {result, false} @@ -2336,6 +2301,7 @@ broadcast_removed_node(Host, Node) -> transaction(Host, Node, Action, sync_dirty). broadcast_config_notification(Host, Node, Lang) -> + %broadcast(Host, Node, notify_config, false, 'items', ConfigEls) Action = fun(#pubsub_node{options = Options, owners = Owners, type = Type}) -> case get_option(Options, notify_config) of @@ -2355,8 +2321,7 @@ broadcast_config_notification(Host, Node, Lang) -> [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = [?XMLATTR('id', <<"configuration">>)], children = Content}]}]), - broadcast_stanza(Host, Options, States, Stanza), - broadcast_by_caps(Host, Node, Type, Stanza), + broadcast_stanza(Host, Node, Type, Options, States, Stanza), {result, true}; _ -> {result, false} @@ -2367,62 +2332,91 @@ broadcast_config_notification(Host, Node, Lang) -> end, transaction(Host, Node, Action, sync_dirty). -broadcast_stanza(Host, NodeOpts, States, Stanza) -> - PresenceDelivery = get_option(NodeOpts, presence_based_delivery), - BroadcastAll = get_option(NodeOpts, broadcast_all_resources), %% XXX this is not standard +% TODO: merge broadcast code that way +%broadcast(Host, Node, Feature, Force, ElName, SubEls) -> +% Action = +% fun(#pubsub_node{options = Options, type = Type}) -> +% case (get_option(Options, Feature) or Force) of +% true -> +% case node_call(Type, get_states, [Host, Node]) of +% {result, []} -> +% {result, false}; +% {result, States} -> +% Stanza = event_stanza([{xmlelement, ElName, [{"node", node_to_string(Node)}], SubEls}]), +% broadcast_stanza(Host, Node, Type, Options, States, Stanza), +% {result, true}; +% _ -> +% {result, false} +% end; +% _ -> +% {result, false} +% end +% end, +% transaction(Host, Node, Action, sync_dirty). + +broadcast_stanza(Host, Node, _Type, Options, States, Stanza) -> + AccessModel = get_option(Options, access_model), + PresenceDelivery = get_option(Options, presence_based_delivery), + BroadcastAll = get_option(Options, broadcast_all_resources), %% XXX this is not standard, but usefull From = service_jid(Host), + %% Handles explicit subscriptions lists:foreach(fun(#pubsub_state{stateid = {LJID, _}, subscription = Subs}) -> case is_to_deliver(LJID, Subs, PresenceDelivery) of true -> - JIDs = case BroadcastAll of - true -> ejabberd_sm:get_user_resources(element(1, LJID), element(2, LJID)); - false -> [LJID] + {U, S, R} = case BroadcastAll of + true -> jlib:short_prepd_bare_jid(LJID); + false -> LJID end, - lists:foreach(fun({U, S, R}) -> - ejabberd_router ! {route, From, exmpp_jlib:make_jid(U, S, R), Stanza} - end, JIDs); + ejabberd_router ! {route, From, exmpp_jlib:make_jid(U, S, R), Stanza}; false -> ok end - end, States). - -%% 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 = 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 - %% Also, add "replyto" if entity has presence subscription to the account owner - %% See XEP-0163 1.1 section 4.3.1 - Sender = exmpp_jid:make_jid(LUser, LServer), - %%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, _}) -> - Resources = lists:foldl(fun(R, Acc) -> - case is_caps_notify(LServer, Node, {U, S, R}) of - true -> [R | Acc]; - false -> Acc - end - end, [], user_resources(U, S)), - lists:foreach(fun(R) -> - ejabberd_router ! {route, Sender, exmpp_jid:make_jid(U, S, R), Stanza} - end, Resources) - end, Contacts); + end, States), + %% Handles implicit presence subscriptions + case Host of + {LUser, LServer, LResource} -> + SenderResource = case LResource of + [] -> + case user_resources(LUser, LServer) of + [Resource|_] -> Resource; + _ -> "" + end; _ -> - ok + LResource end, - ok; + 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 + %% Also, add "replyto" if entity has presence subscription to the account owner + %% See XEP-0163 1.1 section 4.3.1 + Sender = exmpp_jid:make_jid(LUser, LServer), + %%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, _}) -> + spawn(fun() -> + Rs = lists:foldl(fun(R, Acc) -> + case is_caps_notify(LServer, Node, {U, S, R}) of + true -> [R | Acc]; + false -> Acc + end + end, [], user_resources(U, S)), + lists:foreach(fun(R) -> + ejabberd_router ! {route, Sender, exmpp_jid:make_jid(U, S, R), Stanza} + end, Rs) + end) + end, Contacts); + _ -> + ok + end, + ok; + _ -> + ?DEBUG("~p@~p has no session; can't deliver ~p to contacts", [LUser, LServer, Stanza]), + ok + end; _ -> - ?DEBUG("~p@~p has no session; can't deliver ~p to contacts", [LUser, LServer, Stanza]), ok - end; -broadcast_by_caps(_, _, _, _) -> - ok. + end. %% If we don't know the resource, just pick first if any %% If no resource available, check if caps anyway (remote online) @@ -2877,3 +2871,16 @@ uniqid() -> {T1, T2, T3} = now(), lists:flatten(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3])). +% node attributes +nodeAttr(Node) -> %% TODO: to be used + [?XMLATTR('node', node_to_string(Node))]. + +% item attributes +itemAttr([]) -> []; +itemAttr(ItemId) -> [?XMLATTR('id', ItemId)]. + +% build item elements from item list +itemsEls(Items) -> + lists:map(fun(#pubsub_item{itemid = {ItemId, _}, payload = Payload}) -> + #xmlel{ns = ?NS_PUBSUB, name = 'item', attrs = itemAttr(ItemId), children = Payload} + end, Items). diff --git a/src/mod_pubsub/node_flat.erl b/src/mod_pubsub/node_flat.erl index 2d1f8b234..e9e983aa8 100644 --- a/src/mod_pubsub/node_flat.erl +++ b/src/mod_pubsub/node_flat.erl @@ -16,7 +16,7 @@ %%% This software is copyright 2006-2009, ProcessOne. %%% %%% @copyright 2006-2009 ProcessOne -%%% @author Christophe romain <christophe.romain@process-one.net> +%%% @author Christophe Romain <christophe.romain@process-one.net> %%% [http://www.process-one.net/] %%% @version {@vsn}, {@date} {@time} %%% @end @@ -82,7 +82,7 @@ options() -> {roster_groups_allowed, []}, {publish_model, publishers}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, - {send_last_published_item, never}, + {send_last_published_item, on_sub_and_presence}, {deliver_notifications, true}, {presence_based_delivery, false}]. @@ -168,7 +168,7 @@ get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId get_item(Host, Node, ItemId) -> node_default:get_item(Host, Node, ItemId). - + get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). |