diff --git a/src/mod_pubsub/mod_pubsub_odbc.erl b/src/mod_pubsub/mod_pubsub_odbc.erl index 4649aac78..4ab4a869a 100644 --- a/src/mod_pubsub/mod_pubsub_odbc.erl +++ b/src/mod_pubsub/mod_pubsub_odbc.erl @@ -78,6 +78,7 @@ %% exports for console debug manual use -export([create_node/5, + create_node/7, delete_node/3, subscribe_node/5, unsubscribe_node/5, @@ -87,7 +88,6 @@ get_items/2, get_item/3, get_cached_item/2, - broadcast_stanza/9, get_configure/5, set_configure/5, tree_action/3, @@ -1574,10 +1574,8 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> {result, true} -> case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of {ok, NodeId} -> - ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, Owner]), - SubsByDepth = [{Depth, [{N, get_node_subs(N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree], case node_call(Type, create_node, [NodeId, Owner]) of - {result, Result} -> {result, {NodeId, SubsByDepth, Result}}; + {result, Result} -> {result, {NodeId, Result}}; Error -> Error end; {error, {virtual, NodeId}} -> @@ -1596,15 +1594,15 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> [{xmlelement, "create", nodeAttr(Node), []}]}], case transaction(Host, CreateNode, transaction) of - {result, {NodeId, SubsByDepth, {Result, broadcast}}} -> - broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth), + {result, {NodeId, {Result, broadcast}}} -> + broadcast_created_node(Host, Node, NodeId, Type, NodeOptions), case Result of default -> {result, Reply}; _ -> {result, Result} end; - {result, {_NodeId, _SubsByDepth, default}} -> + {result, {_NodeId, default}} -> {result, Reply}; - {result, {_NodeId, _SubsByDepth, Result}} -> + {result, {_NodeId, Result}} -> {result, Result}; Error -> %% in case we change transaction to sync_dirty... @@ -1636,11 +1634,9 @@ delete_node(Host, Node, Owner) -> Action = fun(#pubsub_node{type = Type, id = NodeId}) -> case node_call(Type, get_affiliation, [NodeId, Owner]) of {result, owner} -> - ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]), - SubsByDepth = [{Depth, [{N, get_node_subs(N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree], Removed = tree_call(Host, delete_node, [Host, Node]), case node_call(Type, delete_node, [Removed]) of - {result, Res} -> {result, {SubsByDepth, Res}}; + {result, Res} -> {result, Res}; Error -> Error end; _ -> @@ -1650,26 +1646,26 @@ delete_node(Host, Node, Owner) -> end, Reply = [], case transaction(Host, Node, Action, transaction) of - {result, {_, {SubsByDepth, {Result, broadcast, Removed}}}} -> + {result, {_, {Result, broadcast, Removed}}} -> lists:foreach(fun({RNode, _RSubscriptions}) -> {RH, RN} = RNode#pubsub_node.nodeid, NodeId = RNode#pubsub_node.id, Type = RNode#pubsub_node.type, Options = RNode#pubsub_node.options, - broadcast_removed_node(RH, RN, NodeId, Type, Options, SubsByDepth) + broadcast_removed_node(RH, RN, NodeId, Type, Options) end, Removed), case Result of default -> {result, Reply}; _ -> {result, Result} end; - {result, {_, {_, {Result, _Removed}}}} -> + {result, {_, {Result, _Removed}}} -> case Result of default -> {result, Reply}; _ -> {result, Result} end; - {result, {_, {_, default}}} -> + {result, {_, default}} -> {result, Reply}; - {result, {_, {_, Result}}} -> + {result, {_, Result}} -> {result, Result}; Error -> Error @@ -1877,12 +1873,11 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> NodeId = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, - BroadcastPayload = case Broadcast of - default -> Payload; + BrPayload = case Broadcast of broadcast -> Payload; PluginPayload -> PluginPayload end, - broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, jlib:jid_tolower(Publisher), BroadcastPayload), + broadcast_publish_item(Host, Node, NodeId, Type, Options, ItemId, jlib:jid_tolower(Publisher), BrPayload, Removed), set_cached_item(Host, NodeId, ItemId, Publisher, Payload), case Result of default -> {result, Reply}; @@ -2708,21 +2703,20 @@ sub_to_deliver(_LJID, NotifyType, Depth, SubOptions) -> sub_option_can_deliver(NotifyType, Depth, Option) end, SubOptions). +node_to_deliver(LJID, NodeOptions) -> + presence_can_deliver(LJID, get_option(NodeOptions, presence_based_delivery)). + sub_option_can_deliver(items, _, {subscription_type, nodes}) -> false; sub_option_can_deliver(nodes, _, {subscription_type, items}) -> false; sub_option_can_deliver(_, _, {subscription_depth, all}) -> true; sub_option_can_deliver(_, Depth, {subscription_depth, D}) -> Depth =< D; -sub_option_can_deliver(_, _, {deliver, false}) -> false; -sub_option_can_deliver(_, _, {expire, When}) -> now() < When; -sub_option_can_deliver(_, _, _) -> true. - -node_to_deliver(LJID, NodeOptions) -> - PresenceDelivery = get_option(NodeOptions, presence_based_delivery), - presence_can_deliver(LJID, PresenceDelivery). +sub_option_can_deliver(_, _, {deliver, false}) -> false; +sub_option_can_deliver(_, _, {expire, When}) -> now() < When; +sub_option_can_deliver(_, _, _) -> true. presence_can_deliver(_, false) -> true; presence_can_deliver({User, Server, Resource}, true) -> - case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of + case ejabberd_sm:get_user_sessions(User, Server) of [] -> false; Ss -> lists:foldl(fun(_, true) -> true; @@ -2740,45 +2734,45 @@ state_can_deliver({U, S, R}, []) -> [{U, S, R}]; state_can_deliver({U, S, R}, SubOptions) -> %% Check SubOptions for 'show_values' case lists:keysearch('show_values', 1, SubOptions) of - %% If not in suboptions, item can be delivered, case doesn't apply - false -> [{U, S, R}]; - %% If in a suboptions ... - {_, {_, ShowValues}} -> - %% Get subscriber resources - Resources = case R of - %% If the subscriber JID is a bare one, get all its resources - [] -> user_resources(U, S); - %% If the subscriber JID is a full one, use its resource - R -> [R] - end, - %% For each resource, test if the item is allowed to be delivered - %% based on resource state - lists:foldl( - fun(Resource, Acc) -> - get_resource_state({U, S, Resource}, ShowValues, Acc) - end, [], Resources) + %% If not in suboptions, item can be delivered, case doesn't apply + false -> [{U, S, R}]; + %% If in a suboptions ... + {_, {_, ShowValues}} -> + %% Get subscriber resources + Resources = case R of + %% If the subscriber JID is a bare one, get all its resources + [] -> user_resources(U, S); + %% If the subscriber JID is a full one, use its resource + R -> [R] + end, + %% For each resource, test if the item is allowed to be delivered + %% based on resource state + lists:foldl( + fun(Resource, Acc) -> + get_resource_state({U, S, Resource}, ShowValues, Acc) + end, [], Resources) end. get_resource_state({U, S, R}, ShowValues, JIDs) -> %% Get user session PID case ejabberd_sm:get_session_pid(U, S, R) of - %% If no PID, item can be delivered - none -> lists:append([{U, S, R}], JIDs); - %% If PID ... - Pid -> - %% Get user resource state - %% TODO : add a catch clause - Show = case ejabberd_c2s:get_presence(Pid) of - {_, _, "available", _} -> "online"; - {_, _, State, _} -> State - end, - %% Is current resource state listed in 'show-values' suboption ? - case lists:member(Show, ShowValues) of %andalso Show =/= "online" of - %% If yes, item can be delivered - true -> lists:append([{U, S, R}], JIDs); - %% If no, item can't be delivered - false -> JIDs - end + %% If no PID, item can be delivered + none -> lists:append([{U, S, R}], JIDs); + %% If PID ... + Pid -> + %% Get user resource state + %% TODO : add a catch clause + Show = case ejabberd_c2s:get_presence(Pid) of + {_, _, "available", _} -> "online"; + {_, _, State, _} -> State + end, + %% Is current resource state listed in 'show-values' suboption ? + case lists:member(Show, ShowValues) of %andalso Show =/= "online" of + %% If yes, item can be delivered + true -> lists:append([{U, S, R}], JIDs); + %% If no, item can't be delivered + false -> JIDs + end end. %% @spec (Payload) -> int() @@ -2804,223 +2798,149 @@ event_stanza_withmoreels(Els, MoreEls) -> {xmlelement, "message", [], [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}], Els} | MoreEls]}. +event_stanza(Event, EvAttr) -> + event_stanza([{xmlelement, Event, EvAttr, []}]). +event_stanza(Event, EvAttr, Entries) -> + event_stanza([{xmlelement, Event, EvAttr, + [{xmlelement, Entry, EnAttr, []} || {Entry, EnAttr} <- Entries]}]). +event_stanza(Event, EvAttr, Entry, EnAttr, Payload) -> + event_stanza([{xmlelement, Event, EvAttr, [{xmlelement, Entry, EnAttr, Payload}]}]). +event_stanza(Event, EvAttr, Entry, EnAttr, Payload, Publisher) -> + Stanza = event_stanza(Event, EvAttr, Entry, EnAttr, Payload), + add_extended_headers(Stanza, extended_headers([jlib:jid_to_string(Publisher)])). + %%%%%% broadcast functions -broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, From, Payload) -> - case get_collection_subscriptions(Host, Node) of - SubsByDepth when is_list(SubsByDepth) -> - Content = case get_option(NodeOptions, deliver_payloads) of - true -> Payload; - false -> [] - end, - Stanza = event_stanza( - [{xmlelement, "items", nodeAttr(Node), - [{xmlelement, "item", itemAttr(ItemId), Content}]}]), - broadcast_stanza(Host, From, Node, NodeId, Type, - NodeOptions, SubsByDepth, items, Stanza, true), - case Removed of - [] -> - ok; - _ -> - case get_option(NodeOptions, notify_retract) of - true -> - RetractStanza = event_stanza( - [{xmlelement, "items", nodeAttr(Node), - [{xmlelement, "retract", itemAttr(RId), []} || RId <- Removed]}]), - broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubsByDepth, - items, RetractStanza, true); - _ -> - ok - end - end, - {result, true}; +broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, ItemId, Publisher, Payload, Removed) -> + Publish = case get_option(NodeOptions, deliver_payloads) of + true -> event_stanza("items", nodeAttr(Node), "item", itemAttr(ItemId), Payload, Publisher); + false -> event_stanza("items", nodeAttr(Node), "item", itemAttr(ItemId), [], Publisher) + end, + case Removed of + [] -> + {result, broadcast(Host, Node, NodeId, Type, NodeOptions, items, true, Publish, true)}; _ -> - {result, false} + Retract = event_stanza("items", nodeAttr(Node), [{"retract", itemAttr(Rid)} || Rid <- Removed]), + Stanzas = [{true, Publish, true}, {get_option(NodeOptions, notify_retract), Retract, true}], + {result, broadcast(Host, Node, NodeId, Type, NodeOptions, items, Stanzas)} end. broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds) -> - broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, false). -broadcast_retract_items(_Host, _Node, _NodeId, _Type, _NodeOptions, [], _ForceNotify) -> + broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, notify_retract). +broadcast_retract_items(_Host, _Node, _NodeId, _Type, _NodeOptions, [], _) -> {result, false}; -broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, ForceNotify) -> - case (get_option(NodeOptions, notify_retract) or ForceNotify) of - true -> - case get_collection_subscriptions(Host, Node) of - SubsByDepth when is_list(SubsByDepth) -> - Stanza = event_stanza( - [{xmlelement, "items", nodeAttr(Node), - [{xmlelement, "retract", itemAttr(ItemId), []} || ItemId <- ItemIds]}]), - broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubsByDepth, items, Stanza, true), - {result, true}; - _ -> - {result, false} - end; - _ -> - {result, false} - end. +broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, false) -> + broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, notify_retract); +broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, Notify) -> + Stanza = event_stanza("items", nodeAttr(Node), [{"retract", itemAttr(Rid)} || Rid <- ItemIds]), + {result, broadcast(Host, Node, NodeId, Type, NodeOptions, items, Notify, Stanza, true)}. broadcast_purge_node(Host, Node, NodeId, Type, NodeOptions) -> - case get_option(NodeOptions, notify_retract) of - true -> - case get_collection_subscriptions(Host, Node) of - SubsByDepth when is_list(SubsByDepth) -> - Stanza = event_stanza( - [{xmlelement, "purge", nodeAttr(Node), - []}]), - broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubsByDepth, nodes, Stanza, false), - {result, true}; - _ -> - {result, false} - end; - _ -> - {result, false} - end. + Stanza = event_stanza("purge", nodeAttr(Node)), + {result, broadcast(Host, Node, NodeId, Type, NodeOptions, nodes, notify_retract, Stanza, false)}. -broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) -> - case get_option(NodeOptions, notify_delete) of - true -> - case SubsByDepth of - [] -> - {result, false}; - _ -> - Stanza = event_stanza( - [{xmlelement, "delete", nodeAttr(Node), - []}]), - broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubsByDepth, nodes, Stanza, false), - {result, true} - end; - _ -> - {result, false} - end. +broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions) -> + Stanza = event_stanza("delete", nodeAttr(Node)), + {result, broadcast(Host, Node, NodeId, Type, NodeOptions, nodes, notify_delete, Stanza, false)}. -broadcast_created_node(_, _, _, _, _, []) -> - {result, false}; -broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) -> - Stanza = event_stanza([{xmlelement, "create", nodeAttr(Node), []}]), - broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, nodes, Stanza, true), - {result, true}. +broadcast_created_node(Host, Node, NodeId, Type, NodeOptions) -> + Stanza = event_stanza("create", nodeAttr(Node)), + {result, broadcast(Host, Node, NodeId, Type, NodeOptions, nodes, true, Stanza, true)}. broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) -> - case get_option(NodeOptions, notify_config) of + Stanza = case get_option(NodeOptions, deliver_payloads) of true -> - case get_collection_subscriptions(Host, Node) of - SubsByDepth when is_list(SubsByDepth) -> - Content = case get_option(NodeOptions, deliver_payloads) of - true -> - [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "result"}], - get_configure_xfields(Type, NodeOptions, Lang, [])}]; - false -> - [] - end, - Stanza = event_stanza( - [{xmlelement, "configuration", nodeAttr(Node), Content}]), - broadcast_stanza(Host, Node, NodeId, Type, - NodeOptions, SubsByDepth, nodes, Stanza, false), - {result, true}; - _ -> - {result, false} - end; - _ -> - {result, false} - end. - -get_collection_subscriptions(Host, Node) -> - Action = fun() -> - {result, lists:map(fun({Depth, Nodes}) -> - {Depth, [{N, get_node_subs(N)} || N <- Nodes]} - end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))} + event_stanza("configuration", nodeAttr(Node), + "x", [{"xmlns", ?NS_XDATA}, {"type", "result"}], + get_configure_xfields(Type, NodeOptions, Lang, [])); + false -> + event_stanza("configuration", nodeAttr(Node)) end, - case transaction(Host, Action, sync_dirty) of - {result, CollSubs} -> CollSubs; - _ -> [] - end. + {result, broadcast(Host, Node, NodeId, Type, NodeOptions, nodes, notify_config, Stanza, false)}. -get_node_subs(#pubsub_node{type = Type, - id = NodeID}) -> - case node_call(Type, get_node_subscriptions, [NodeID]) of - {result, Subs} -> get_options_for_subs(NodeID, Subs); - Other -> Other - end. +broadcast(Host, Node, NodeId, Type, NodeOptions, Notify, Stanzas) -> + Subs = node_subscriptions(Host, Node, NodeId, Type, NodeOptions, Notify), + Result = [broadcast(Host, Node, NodeId, Type, NodeOptions, Subs, Stanza, SHIM) || + {Cond, Stanza, SHIM} <- Stanzas, Cond =:= true], + lists:member(true, Result). +broadcast(Host, Node, NodeId, Type, NodeOptions, Notify, true, Stanza, SHIM) -> + Subs = node_subscriptions(Host, Node, NodeId, Type, NodeOptions, Notify), + broadcast(Host, Node, NodeId, Type, NodeOptions, Subs, Stanza, SHIM); +broadcast(_Host, _Node, _NodeId, _Type, _NodeOptions, _Notify, false, _Stanza, _SHIM) -> + false; +broadcast(Host, Node, NodeId, Type, NodeOptions, Notify, Condition, Stanza, SHIM) -> + broadcast(Host, Node, NodeId, Type, NodeOptions, Notify, get_option(NodeOptions, Condition), Stanza, SHIM). -get_options_for_subs(NodeID, Subs) -> - lists:foldl(fun({JID, subscribed, SubID}, Acc) -> - case pubsub_subscription_odbc:get_subscription(JID, NodeID, SubID) of - {error, notfound} -> [{JID, SubID, []} | Acc]; - {result, #pubsub_subscription{options = Options}} -> [{JID, SubID, Options} | Acc]; - _ -> Acc - end; - (_, Acc) -> - Acc - end, [], Subs). - -broadcast_stanza(Host, _Node, _NodeId, _Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) -> - NotificationType = get_option(NodeOptions, notification_type, headline), - BroadcastAll = get_option(NodeOptions, broadcast_all_resources), %% XXX this is not standard, but usefull - From = service_jid(Host), - Stanza = case NotificationType of - normal -> BaseStanza; - MsgType -> add_message_type(BaseStanza, atom_to_list(MsgType)) - end, - %% Handles explicit subscriptions - SubIDsByJID = subscribed_nodes_by_jid(NotifyType, SubsByDepth), - lists:foreach(fun ({LJID, NodeName, SubIDs}) -> - LJIDs = case BroadcastAll of - true -> - {U, S, _} = LJID, - [{U, S, R} || R <- user_resources(U, S)]; - false -> - [LJID] - end, - %% Determine if the stanza should have SHIM ('SubID' and 'name') headers - StanzaToSend = case {SHIM, SubIDs} of - {false, _} -> - Stanza; - %% If there's only one SubID, don't add it - {true, [_]} -> - add_shim_headers(Stanza, collection_shim(NodeName)); - {true, SubIDs} -> - add_shim_headers(Stanza, lists:append(collection_shim(NodeName), subid_shim(SubIDs))) - end, - lists:foreach(fun(To) -> - ejabberd_router:route(From, jlib:make_jid(To), StanzaToSend) - end, LJIDs) - end, SubIDsByJID). - -broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) -> - broadcast_stanza({LUser, LServer, LResource}, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM), - %% Handles implicit presence subscriptions - SenderResource = case LResource of - [] -> - case user_resources(LUser, LServer) of - [Resource|_] -> Resource; - _ -> "" - end; - _ -> - LResource - end, - case ejabberd_sm:get_session_pid(LUser, LServer, SenderResource) of +broadcast({U, S, R}, Node, NodeId, Type, NodeOptions, Subscriptions, Stanza, SHIM) -> + broadcast(S, Node, NodeId, Type, NodeOptions, Subscriptions, Stanza, SHIM) + or case ejabberd_sm:get_session_pid(U, S, user_resource(U, S, R)) of C2SPid when is_pid(C2SPid) -> - Stanza = case get_option(NodeOptions, notification_type, headline) of - normal -> BaseStanza; - MsgType -> add_message_type(BaseStanza, atom_to_list(MsgType)) - end, %% 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 - ejabberd_c2s:broadcast(C2SPid, - {pep_message, binary_to_list(Node)++"+notify"}, - _Sender = jlib:make_jid(LUser, LServer, ""), - _StanzaToSend = add_extended_headers(Stanza, - _ReplyTo = extended_headers([jlib:jid_to_string(Publisher)]))); + Event = {pep_message, binary_to_list(Node)++"+notify"}, + Message = case get_option(NodeOptions, notification_type, headline) of + normal -> Stanza; + MsgType -> add_message_type(Stanza, atom_to_list(MsgType)) + end, + ejabberd_c2s:broadcast(C2SPid, Event, jlib:make_jid(U, S, ""), Message), + true; _ -> - ?DEBUG("~p@~p has no session; can't deliver ~p to contacts", [LUser, LServer, BaseStanza]) + ?DEBUG("~p@~p has no session; can't deliver stanza: ~p", [U, S, Stanza]), + false end; -broadcast_stanza(Host, _Publisher, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) -> - broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM). +broadcast(_Host, _Node, _NodeId, _Type, _NodeOptions, [], _Stanza, _SHIM) -> + false; +broadcast(Host, _Node, _NodeId, _Type, NodeOptions, Subscriptions, Stanza, SHIM) -> + From = service_jid(Host), + Message = case get_option(NodeOptions, notification_type, headline) of + normal -> Stanza; + MsgType -> add_message_type(Stanza, atom_to_list(MsgType)) + end, + lists:foreach(fun({LJID, NodeName, SubIds}) -> + Send = case {SHIM, SubIds} of + {false, _} -> Message; + {true, [_]} -> add_shim_headers(Message, collection_shim(NodeName)); + {true, _} -> add_shim_headers(Message, lists:append(collection_shim(NodeName), subid_shim(SubIds))) + end, + ejabberd_router:route(From, jlib:make_jid(LJID), Send) + end, Subscriptions), + true. + +node_subscriptions(Host, Node, NodeId, Type, _NodeOptions, Notify) -> + % TODO temporary dirty condition, should be improved using plugin or node options + case Type of + "flat" -> node_subscriptions_bare(Host, Node, NodeId, Type); + "pep" -> node_subscriptions_bare(Host, Node, NodeId, Type); + _ -> node_subscriptions_full(Host, Node, Notify) + end. + +node_subscriptions_bare(Host, Node, NodeId, Type) -> + case node_action(Host, Type, get_node_subscriptions, [NodeId]) of + {result, Subs} -> + SubsByJid = lists:foldl( + fun({JID, subscribed, SubId}, Acc) -> + case dict:is_key(JID, Acc) of + true -> dict:append(JID, SubId, Acc); + false -> dict:store(JID, [SubId], Acc) + end; + (_, Acc) -> + Acc + end, dict:new(), Subs), + [{J, Node, S} || {J, S} <- dict:to_list(SubsByJid)]; + _ -> + [] + end. + +node_subscriptions_full(Host, Node, NotifyType) -> + Action = fun() -> + Collection = tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]), + {result, [{Depth, [{N, sub_with_options(N)} || N <- Nodes]} || {Depth, Nodes} <- Collection]} + end, + case transaction(Host, Action, sync_dirty) of + {result, CollSubs} -> subscribed_nodes_by_jid(NotifyType, CollSubs); + _ -> [] + end. subscribed_nodes_by_jid(NotifyType, SubsByDepth) -> NodesToDeliver = fun(Depth, Node, Subs, Acc) -> @@ -3031,7 +2951,7 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) -> NodeOptions = Node#pubsub_node.options, lists:foldl(fun({LJID, SubID, SubOptions}, {JIDs, Recipients}) -> case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of - true -> + true -> %% If is to deliver : case state_can_deliver(LJID, SubOptions) of [] -> {JIDs, Recipients}; @@ -3040,13 +2960,13 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) -> fun(JIDToDeliver, {JIDsAcc, RecipientsAcc}) -> case lists:member(JIDToDeliver, JIDs) of %% check if the JIDs co-accumulator contains the Subscription Jid, - false -> + false -> %% - if not, %% - add the Jid to JIDs list co-accumulator ; %% - create a tuple of the Jid, NodeId, and SubID (as list), %% and add the tuple to the Recipients list co-accumulator {[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]}; - true -> + true -> %% - if the JIDs co-accumulator contains the Jid %% get the tuple containing the Jid from the Recipient list co-accumulator {_, {JIDToDeliver, NodeName1, SubIDs}} = lists:keysearch(JIDToDeliver, 1, RecipientsAcc), @@ -3075,9 +2995,33 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) -> {_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth), JIDSubs. +sub_with_options(#pubsub_node{type = Type, id = NodeId}) -> + case node_call(Type, get_node_subscriptions, [NodeId]) of + {result, Subs} -> + lists:foldl( + fun({JID, subscribed, SubId}, Acc) -> [sub_with_options(JID, NodeId, SubId) | Acc]; + (_, Acc) -> Acc + end, [], Subs); + _ -> + [] + end. +sub_with_options(JID, NodeId, SubId) -> + case pubsub_subscription_odbc:get_subscription(JID, NodeId, SubId) of + {result, #pubsub_subscription{options = Options}} -> {JID, SubId, Options}; + _ -> {JID, SubId, []} + end. + user_resources(User, Server) -> ejabberd_sm:get_user_resources(User, Server). +user_resource(User, Server, []) -> + case user_resources(User, Server) of + [R|_] -> R; + _ -> [] + end; +user_resource(_, _, Resource) -> + Resource. + %%%%%%% Configuration handling %%

There are several reasons why the default node configuration options request might fail:

@@ -3736,7 +3680,7 @@ extended_headers(Jids) -> on_user_offline(_, JID, _) -> {User, Server, Resource} = jlib:jid_tolower(JID), - case ejabberd_sm:get_user_resources(User, Server) of + case user_resources(User, Server) of [] -> purge_offline({User, Server, Resource}); _ -> true end. diff --git a/src/mod_pubsub/pubsub_odbc.patch b/src/mod_pubsub/pubsub_odbc.patch index 998132f74..3ba5f7c1a 100644 --- a/src/mod_pubsub/pubsub_odbc.patch +++ b/src/mod_pubsub/pubsub_odbc.patch @@ -1,5 +1,5 @@ ---- mod_pubsub.erl 2010-12-07 13:54:26.000000000 +0100 -+++ mod_pubsub_odbc.erl 2010-12-07 13:59:56.000000000 +0100 +--- mod_pubsub.erl 2011-02-08 16:50:27.000000000 +0100 ++++ mod_pubsub_odbc.erl 2011-02-08 18:27:16.000000000 +0100 @@ -42,7 +42,7 @@ %%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see %%% XEP-0060 section 12.18. @@ -496,16 +496,16 @@ {result, Subscriptions} = node_call(Type, get_subscriptions, [NodeId, Subscriber]), if not IsApprover -> -@@ -1780,7 +1595,7 @@ +@@ -1778,7 +1593,7 @@ Reply = [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], [{xmlelement, "create", nodeAttr(Node), []}]}], - case transaction(CreateNode, transaction) of + case transaction(Host, CreateNode, transaction) of - {result, {NodeId, SubsByDepth, {Result, broadcast}}} -> - broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth), + {result, {NodeId, {Result, broadcast}}} -> + broadcast_created_node(Host, Node, NodeId, Type, NodeOptions), case Result of -@@ -1883,7 +1698,7 @@ +@@ -1879,7 +1694,7 @@ %%
  • The node does not exist.
  • %% subscribe_node(Host, Node, From, JID, Configuration) -> @@ -514,7 +514,7 @@ {result, GoodSubOpts} -> GoodSubOpts; _ -> invalid end, -@@ -1891,7 +1706,7 @@ +@@ -1887,7 +1702,7 @@ error -> {"", "", ""}; J -> jlib:jid_tolower(J) end, @@ -523,7 +523,7 @@ Features = features(Type), SubscribeFeature = lists:member("subscribe", Features), OptionsFeature = lists:member("subscription-options", Features), -@@ -1900,6 +1715,7 @@ +@@ -1896,6 +1711,7 @@ AccessModel = get_option(Options, access_model), SendLast = get_option(Options, send_last_published_item), AllowedGroups = get_option(Options, roster_groups_allowed, []), @@ -531,7 +531,7 @@ {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups), if not SubscribeFeature -> -@@ -2231,7 +2047,7 @@ +@@ -2226,7 +2042,7 @@ %%

    The permission are not checked in this function.

    %% @todo We probably need to check that the user doing the query has the right %% to read the items. @@ -540,7 +540,7 @@ MaxItems = if SMaxItems == "" -> get_max_items_node(Host); -@@ -2245,12 +2061,13 @@ +@@ -2240,12 +2056,13 @@ {error, Error} -> {error, Error}; _ -> @@ -555,7 +555,7 @@ {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), if not RetreiveFeature -> -@@ -2263,11 +2080,11 @@ +@@ -2258,11 +2075,11 @@ node_call(Type, get_items, [NodeId, From, AccessModel, PresenceSubscription, RosterGroup, @@ -569,7 +569,7 @@ SendItems = case ItemIDs of [] -> Items; -@@ -2280,7 +2097,8 @@ +@@ -2275,7 +2092,8 @@ %% number of items sent to MaxItems: {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], [{xmlelement, "items", nodeAttr(Node), @@ -579,7 +579,7 @@ Error -> Error end -@@ -2302,10 +2120,15 @@ +@@ -2297,10 +2115,15 @@ Error -> Error end. get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) -> @@ -596,7 +596,7 @@ %% @spec (Host, Node, NodeId, Type, LJID, Number) -> any() -@@ -2318,16 +2141,27 @@ +@@ -2313,16 +2136,27 @@ %% @doc

    Resend the items of a node to the user.

    %% @todo use cache-last-item feature send_items(Host, Node, NodeId, Type, LJID, last) -> @@ -630,7 +630,7 @@ send_items(Host, Node, NodeId, Type, LJID, Number) -> ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of {result, []} -> -@@ -2453,7 +2287,8 @@ +@@ -2448,7 +2282,8 @@ error -> {error, ?ERR_BAD_REQUEST}; _ -> @@ -640,7 +640,7 @@ case lists:member(Owner, Owners) of true -> OwnerJID = jlib:make_jid(Owner), -@@ -2463,24 +2298,7 @@ +@@ -2458,24 +2293,7 @@ end, lists:foreach( fun({JID, Affiliation}) -> @@ -666,7 +666,7 @@ end, FilteredEntities), {result, []}; _ -> -@@ -2533,11 +2351,11 @@ +@@ -2528,11 +2346,11 @@ end. read_sub(Subscriber, Node, NodeID, SubID, Lang) -> @@ -680,7 +680,7 @@ OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)}, {"subid", SubID}|nodeAttr(Node)], [XdataEl]}, -@@ -2563,7 +2381,7 @@ +@@ -2558,7 +2376,7 @@ end. set_options_helper(Configuration, JID, NodeID, SubID, Type) -> @@ -689,7 +689,7 @@ {result, GoodSubOpts} -> GoodSubOpts; _ -> invalid end, -@@ -2592,7 +2410,7 @@ +@@ -2587,7 +2405,7 @@ write_sub(_Subscriber, _NodeID, _SubID, invalid) -> {error, extended_error(?ERR_BAD_REQUEST, "invalid-options")}; write_sub(Subscriber, NodeID, SubID, Options) -> @@ -698,7 +698,7 @@ {error, notfound} -> {error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; {result, _} -> -@@ -2760,8 +2578,8 @@ +@@ -2755,8 +2573,8 @@ {"subscription", subscription_to_string(Sub)} | nodeAttr(Node)], []}]}]}, ejabberd_router:route(service_jid(Host), jlib:make_jid(JID), Stanza) end, @@ -709,28 +709,27 @@ true -> Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) -> -@@ -3116,7 +2934,7 @@ - {Depth, [{N, get_node_subs(N)} || N <- Nodes]} - end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))} +@@ -3119,7 +2937,7 @@ + Collection = tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]), + {result, [{Depth, [{N, sub_with_options(N)} || N <- Nodes]} || {Depth, Nodes} <- Collection]} end, - case transaction(Action, sync_dirty) of + case transaction(Host, Action, sync_dirty) of - {result, CollSubs} -> CollSubs; + {result, CollSubs} -> subscribed_nodes_by_jid(NotifyType, CollSubs); _ -> [] end. -@@ -3130,9 +2948,9 @@ +@@ -3188,8 +3006,8 @@ + [] + end. + sub_with_options(JID, NodeId, SubId) -> +- case pubsub_subscription:read_subscription(JID, NodeId, SubId) of +- #pubsub_subscription{options = Options} -> {JID, SubId, Options}; ++ case pubsub_subscription_odbc:get_subscription(JID, NodeId, SubId) of ++ {result, #pubsub_subscription{options = Options}} -> {JID, SubId, Options}; + _ -> {JID, SubId, []} + end. - get_options_for_subs(NodeID, Subs) -> - lists:foldl(fun({JID, subscribed, SubID}, Acc) -> -- case pubsub_subscription:read_subscription(JID, NodeID, SubID) of -+ case pubsub_subscription_odbc:get_subscription(JID, NodeID, SubID) of - {error, notfound} -> [{JID, SubID, []} | Acc]; -- #pubsub_subscription{options = Options} -> [{JID, SubID, Options} | Acc]; -+ {result, #pubsub_subscription{options = Options}} -> [{JID, SubID, Options} | Acc]; - _ -> Acc - end; - (_, Acc) -> -@@ -3321,6 +3139,30 @@ +@@ -3265,6 +3083,30 @@ Result end. @@ -761,7 +760,7 @@ %% @spec (Host, Options) -> MaxItems %% Host = host() %% Options = [Option] -@@ -3717,7 +3559,13 @@ +@@ -3661,7 +3503,13 @@ tree_action(Host, Function, Args) -> ?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]), Fun = fun() -> tree_call(Host, Function, Args) end, @@ -776,7 +775,7 @@ %% @doc

    node plugin call.

    node_call(Type, Function, Args) -> -@@ -3737,13 +3585,13 @@ +@@ -3681,13 +3529,13 @@ node_action(Host, Type, Function, Args) -> ?DEBUG("node_action ~p ~p ~p ~p",[Host,Type,Function,Args]), @@ -792,7 +791,7 @@ case tree_call(Host, get_node, [Host, Node]) of N when is_record(N, pubsub_node) -> case Action(N) of -@@ -3755,13 +3603,19 @@ +@@ -3699,13 +3547,19 @@ Error end end, Trans). @@ -816,7 +815,7 @@ {result, Result} -> {result, Result}; {error, Error} -> {error, Error}; {atomic, {result, Result}} -> {result, Result}; -@@ -3769,6 +3623,15 @@ +@@ -3713,6 +3567,15 @@ {aborted, Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]), {error, ?ERR_INTERNAL_SERVER_ERROR}; @@ -832,7 +831,7 @@ {'EXIT', Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]), {error, ?ERR_INTERNAL_SERVER_ERROR}; -@@ -3777,6 +3640,17 @@ +@@ -3721,6 +3584,17 @@ {error, ?ERR_INTERNAL_SERVER_ERROR} end.