From 690857e0d419911e63dc1329b86e7301c3f11b2b Mon Sep 17 00:00:00 2001 From: Christophe Romain Date: Wed, 4 Aug 2010 18:30:22 +0200 Subject: [PATCH] enforce disco features results (thanks to Karim)(EJAB-1033, EJAB-1228, EJAB-1238) --- src/mod_pubsub/mod_pubsub.erl | 302 +++++++++++++-------------- src/mod_pubsub/mod_pubsub_odbc.erl | 324 ++++++++++++++--------------- src/mod_pubsub/pubsub_odbc.patch | 231 ++++++++++++++------ 3 files changed, 470 insertions(+), 387 deletions(-) diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 28ad99a91..9ece1fe53 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -74,8 +74,7 @@ disco_sm_items/5 ]). %% exported iq handlers --export([iq_local/3, - iq_sm/3 +-export([iq_sm/3 ]). %% exports for console debug manual use @@ -201,25 +200,22 @@ init([ServerHost, Opts]) -> ets:insert(gen_mod:get_module_proc(ServerHost, config), {ignore_pep_from_offline, PepOffline}), ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}), ejabberd_hooks:add(sm_remove_connection_hook, ServerHostB, ?MODULE, on_user_offline, 75), - ejabberd_hooks:add(disco_sm_identity, ServerHostB, ?MODULE, disco_sm_identity, 75), - ejabberd_hooks:add(disco_sm_features, ServerHostB, ?MODULE, disco_sm_features, 75), - ejabberd_hooks:add(disco_sm_items, ServerHostB, ?MODULE, disco_sm_items, 75), + 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), ejabberd_hooks:add(presence_probe_hook, ServerHostB, ?MODULE, presence_probe, 80), ejabberd_hooks:add(roster_in_subscription, ServerHostB, ?MODULE, in_subscription, 50), ejabberd_hooks:add(roster_out_subscription, ServerHostB, ?MODULE, out_subscription, 50), ejabberd_hooks:add(remove_user, ServerHostB, ?MODULE, remove_user, 50), ejabberd_hooks:add(anonymous_purge_hook, ServerHostB, ?MODULE, remove_user, 50), - 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), case lists:member(?PEPNODE, Plugins) of true -> ejabberd_hooks:add(feature_check_packet, ServerHostB, ?MODULE, feature_check_packet, 75), - 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); + ejabberd_hooks:add(disco_sm_identity, ServerHostB, ?MODULE, disco_sm_identity, 75), + ejabberd_hooks:add(disco_sm_features, ServerHostB, ?MODULE, disco_sm_features, 75), + ejabberd_hooks:add(disco_sm_items, ServerHostB, ?MODULE, disco_sm_items, 75), + 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); false -> ok end, @@ -585,15 +581,14 @@ send_loop(State) -> %% disco hooks handling functions %% -identity(Host) -> - Identity = case lists:member(?PEPNODE, plugins(Host)) of - true -> [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"pep">>)]; - false -> [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"service">>)] - end, - #xmlel{ns = ?NS_DISCO_INFO, name = 'identity', attrs = Identity}. - disco_local_identity(Acc, _From, To, <<>>, _Lang) -> - Acc ++ [identity(exmpp_jid:prep_domain_as_list(To))]; + case lists:member(?PEPNODE, plugins(exmpp_jid:prep_domain_as_list(To))) of + true -> + [#xmlel{name = 'identity', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"pep">>)]} + | Acc]; + false -> Acc + end; disco_local_identity(Acc, _From, _To, _Node, _Lang) -> Acc. @@ -614,81 +609,99 @@ disco_local_items(Acc, _From, _To, <<>>, _Lang) -> disco_local_items(Acc, _From, _To, _Node, _Lang) -> Acc. -disco_sm_identity(Acc, _From, To, <<>>, _Lang) -> - Acc ++ [identity(exmpp_jid:prep_domain_as_list(To))]; disco_sm_identity(Acc, From, To, Node, _Lang) -> - LOwner = jlib:short_prepd_bare_jid(To), - Acc ++ case node_disco_identity(LOwner, From, Node) of - {result, I} -> I; - _ -> [] - end. + disco_identity(To, Node, From) ++ Acc. -disco_sm_features(Acc, _From, _To, [], _Lang) -> - Acc; -disco_sm_features(Acc, From, To, Node, _Lang) -> - LOwner = jlib:short_prepd_bare_jid(To), - Features = node_disco_features(LOwner, From, Node), - case {Acc, Features} of - {{result, AccFeatures}, {result, AddFeatures}} -> - {result, AccFeatures++AddFeatures}; - {_, {result, AddFeatures}} -> - {result, AddFeatures}; - {_, _} -> - Acc +disco_identity(_Host, <<>>, _From) -> + [#xmlel{name = 'identity', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"pep">>)]}]; +disco_identity(Host, Node, From) -> + Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) -> + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, _} -> + {result, + [#xmlel{name = 'identity', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"pep">>)]}, + #xmlel{name = 'identity', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"leaf">>) + | case get_option(Options, title) of + false -> []; + Title -> [?XMLATTR('name', Title)] + end + ]}]}; + {error, _} -> {result, []} + end + end, + case transaction(exmpp_jid:to_lower(Host), Node, Action, sync_dirty) of + {result, {_, Result}} -> Result; + _ -> [] end. -disco_sm_items(Acc, From, To, <<>>, _Lang) -> - Host = exmpp_jid:prep_domain_as_list(To), - case tree_action(Host, get_subnodes, [Host, <<>>, From]) of - [] -> - Acc; - Nodes -> - SBJID = exmpp_jid:to_binary(exmpp_jid:bare(To)), - Items = case Acc of - {result, I} -> I; - _ -> [] - end, - NodeItems = lists:map( - fun(#pubsub_node{nodeid = {_, Node}}) -> - #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs = - [?XMLATTR('jid', SBJID) | nodeAttr(Node)]} - end, Nodes), - {result, NodeItems ++ Items} + +disco_sm_features(empty, From, To, Node, Lang) -> + disco_sm_features({result, []}, From, To, Node, Lang); +disco_sm_features({result, OtherFeatures}, From, To, Node, _Lang) -> + {result, disco_features(To, Node, From) ++ OtherFeatures}. + +disco_features(_Host, <<>>, _From) -> + [?NS_PUBSUB_s + | [?NS_PUBSUB_s++"#"++Feature || Feature <- features("pep")]]; +disco_features(Host, Node, From) -> + Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) -> + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, _} -> + {result, [?NS_PUBSUB_s + | [?NS_PUBSUB_s ++ "#" ++ Feature || Feature <- features("pep")]]}; + _ -> {result, []} + end + end, + case transaction(exmpp_jid:to_lower(Host), Node, Action, sync_dirty) of + {result, {_, Result}} -> Result; + _ -> [] + end. + +disco_sm_items(empty, From, To, Node, Lang) -> + disco_sm_items({result, []}, From, To, Node, Lang); +disco_sm_items({result, OtherItems}, From, To, Node, _Lang) -> + {result, disco_items(To, Node, From) ++ OtherItems}. + +disco_items(Host, <<>>, From) -> + Action = fun(#pubsub_node{nodeid ={_, NodeID}, options = Options, type = Type, id = Idx, owners = Owners}, Acc) -> + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, _} -> + [#xmlel{name = 'item', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('jid', exmpp_jid:to_binary(Host)), + ?XMLATTR('node', NodeID) | + case get_option(Options, title) of + false -> []; + [Title] -> [?XMLATTR('title', Title)] + end]} + | Acc]; + _ -> Acc + end + end, + case transaction(exmpp_jid:to_lower(Host), Action, sync_dirty) of + {result, Result} -> Result; + _ -> [] end; -disco_sm_items(Acc, From, To, NodeB, _Lang) -> - SNode = binary_to_list(NodeB), - Node = string_to_node(SNode), - %% TODO, use iq_disco_items(Host, Node, From) - Host = exmpp_jid:prep_domain_as_list(To), - SBJID = exmpp_jid:to_binary(exmpp_jid:bare(To)), - Action = fun(#pubsub_node{type = Type, id = NodeId}) -> - % TODO call get_items/6 instead for access control (EJAB-1033) - case node_call(Type, get_items, [NodeId, From]) of - {result, []} -> - none; - {result, AllItems} -> - Items = case Acc of - {result, I} -> I; - _ -> [] - end, - NodeItems = lists:map( - fun(#pubsub_item{itemid = {Id, _}}) -> - {result, Name} = node_call(Type, get_item_name, [Host, Node, Id]), - #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs = - [?XMLATTR('jid', SBJID), - ?XMLATTR('name', Name)]} - end, AllItems), - {result, NodeItems ++ Items}; - _ -> - none - end - end, - case transaction(Host, Node, Action, sync_dirty) of - {result, {_, Items}} -> {result, Items}; - _ -> Acc - end. - +disco_items(Host, Node, From) -> + Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) -> + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, Items} -> + {result, + [#xmlel{name = 'item', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('jid', exmpp_jid:to_binary(Host)), + ?XMLATTR('name', ItemID)]} + || #pubsub_item{itemid = {ItemID,_}} <- Items]}; + _ -> {result, []} + end + end, + case transaction(exmpp_jid:to_lower(Host), Node, Action, sync_dirty) of + {result, {_, Result}} -> Result; + _ -> [] + end. + %% ------- %% presence hooks handling functions %% @@ -874,11 +887,11 @@ terminate(_Reason, #state{host = Host, case lists:member(?PEPNODE, Plugins) of true -> ejabberd_hooks:delete(feature_check_packet, ServerHostB, ?MODULE, feature_check_packet, 75), - 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); + ejabberd_hooks:delete(disco_sm_identity, ServerHostB, ?MODULE, disco_sm_identity, 75), + ejabberd_hooks:delete(disco_sm_features, ServerHostB, ?MODULE, disco_sm_features, 75), + ejabberd_hooks:delete(disco_sm_items, ServerHostB, ?MODULE, disco_sm_items, 75), + gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHostB, ?NS_PUBSUB), + gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHostB, ?NS_PUBSUB_OWNER); false -> ok end, @@ -886,9 +899,6 @@ terminate(_Reason, #state{host = Host, 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), - ejabberd_hooks:delete(disco_sm_identity, ServerHostB, ?MODULE, disco_sm_identity, 75), - ejabberd_hooks:delete(disco_sm_features, ServerHostB, ?MODULE, disco_sm_features, 75), - ejabberd_hooks:delete(disco_sm_items, ServerHostB, ?MODULE, disco_sm_items, 75), ejabberd_hooks:delete(presence_probe_hook, ServerHostB, ?MODULE, presence_probe, 80), ejabberd_hooks:delete(roster_in_subscription, ServerHostB, ?MODULE, in_subscription, 50), ejabberd_hooks:delete(roster_out_subscription, ServerHostB, ?MODULE, out_subscription, 50), @@ -1067,10 +1077,6 @@ command_disco_info(_Host, ?NS_PUBSUB_GET_PENDING_b, _From) -> node_disco_info(Host, Node, From) -> node_disco_info(Host, Node, From, true, true). -node_disco_identity(Host, Node, From) -> - node_disco_info(Host, Node, From, true, false). -node_disco_features(Host, Node, From) -> - node_disco_info(Host, Node, From, false, true). node_disco_info(Host, Node, From, Identity, Features) -> Action = fun(#pubsub_node{type = Type, id = NodeId}) -> @@ -1175,17 +1181,15 @@ iq_disco_items(Host, Item, From) -> {result, []}; [SNode] -> Node = string_to_node(SNode), - Action = - fun(#pubsub_node{type = Type, id = NodeId}) -> - % TODO call get_items/6 instead for access control (EJAB-1033) - NodeItems = case node_call(Type, get_items, [NodeId, From]) of - {result, I} -> I; - _ -> [] - end, + Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) -> + NodeItems = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, R} -> R; + _ -> [] + end, Nodes = lists:map( - fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) -> + fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) -> Attrs = - case get_option(Options, title) of + case get_option(SubOptions, title) of false -> [?XMLATTR('jid', Host) | nodeAttr(SubNode)]; Title -> @@ -1206,24 +1210,23 @@ iq_disco_items(Host, Item, From) -> end end. -iq_local(From, To, #iq{type = Type, payload = SubEl, ns = XMLNS, lang = Lang} = IQ_Rec) -> - ServerHost = exmpp_jid:prep_domain_as_list(To), - FromHost = exmpp_jid:prep_domain_as_list(To), - %% Accept IQs to server only from our own users. - if - FromHost /= ServerHost -> - exmpp_iq:error(IQ_Rec, 'forbidden'); - true -> - LOwner = jlib:short_prepd_bare_jid(From), - Res = case XMLNS of - ?NS_PUBSUB -> iq_pubsub(LOwner, ServerHost, From, Type, SubEl, Lang); - ?NS_PUBSUB_OWNER -> iq_pubsub_owner(LOwner, ServerHost, From, Type, SubEl, Lang) - end, - case Res of - {result, []} -> exmpp_iq:result(IQ_Rec); - {result, [IQRes]} -> exmpp_iq:result(IQ_Rec, IQRes); - {error, Error} -> exmpp_iq:error(IQ_Rec, Error) - end +get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) -> + AccessModel = get_option(Options, access_model), + AllowedGroups = get_option(Options, roster_groups_allowed, []), + {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), + node_call(Type, get_items, [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined]). + +get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) -> + if (AccessModel == presence) or (AccessModel == roster) -> + case Host of + {User, Server, _} -> + get_roster_info(User, Server, From, AllowedGroups); + _ -> + [{OUser, OServer, _}|_] = Owners, + get_roster_info(OUser, OServer, From, AllowedGroups) + end; + true -> + {true, true} end. iq_sm(From, To, #iq{type = Type, payload = SubEl, ns = XMLNS, lang = Lang} = IQ_Rec) -> @@ -1934,7 +1937,7 @@ subscribe_node(Host, Node, From, JID, Configuration) -> _:_ -> {undefined, undefined, undefined} end, - Action = fun(#pubsub_node{options = Options, owners = [Owner|_], type = Type, id = NodeId}) -> + Action = fun(#pubsub_node{options = Options, owners = Owners, type = Type, id = NodeId}) -> Features = features(Type), SubscribeFeature = lists:member("subscribe", Features), OptionsFeature = lists:member("subscription-options", Features), @@ -1943,21 +1946,7 @@ subscribe_node(Host, Node, From, JID, Configuration) -> AccessModel = get_option(Options, access_model), SendLast = get_option(Options, send_last_published_item), AllowedGroups = get_option(Options, roster_groups_allowed, []), - {PresenceSubscription, RosterGroup} = - case Host of - {OUser, OServer, _} -> - get_roster_info(OUser, OServer, - Subscriber, AllowedGroups); - _ -> - case Subscriber of - {undefined, undefined, undefined} -> - {false, false}; - _ -> - {OU, OS, _} = Owner, - get_roster_info(OU, OS, - Subscriber, AllowedGroups) - end - end, + {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups), if not SubscribeFeature -> %% Node does not support subscriptions @@ -2315,20 +2304,13 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) -> {error, Error} -> {error, Error}; _ -> - Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> + Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId, owners = Owners}) -> Features = features(Type), RetreiveFeature = lists:member("retrieve-items", Features), PersistentFeature = lists:member("persistent-items", Features), AccessModel = get_option(Options, access_model), AllowedGroups = get_option(Options, roster_groups_allowed, []), - {PresenceSubscription, RosterGroup} = - case Host of - {OUser, OServer, _} -> - get_roster_info(OUser, OServer, - jlib:short_prepd_jid(From), AllowedGroups); - _ -> - {true, true} - end, + {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), if not RetreiveFeature -> %% Item Retrieval Not Supported @@ -2866,7 +2848,8 @@ set_subscriptions(Host, Node, From, EntitiesEls) -> end end. - +get_roster_info(_, _, {undefined, undefined, _}, _) -> + {false, false}; %% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups) %% -> {PresenceSubscription, RosterGroup} get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) -> @@ -2880,7 +2863,9 @@ get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, A RosterGroup = lists:any(fun(Group) -> lists:member(Group, AllowedGroups) end, Groups), - {PresenceSubscription, RosterGroup}. + {PresenceSubscription, RosterGroup}; +get_roster_info(OwnerUser, OwnerServer, JID, AllowedGroups) -> + get_roster_info(OwnerUser, OwnerServer, exmpp_jid:to_lower(JID), AllowedGroups). %% @spec (AffiliationStr) -> Affiliation %% AffiliationStr = string() @@ -3869,6 +3854,11 @@ transaction(Host, Node, Action, Trans) -> end end, Trans). +transaction(Host, Action, Trans) -> + transaction(fun() -> + {result, lists:foldl(Action, [], tree_call(Host, get_nodes, [Host]))} + end, Trans). + transaction(Fun, Trans) -> case catch mnesia:Trans(Fun) of {result, Result} -> {result, Result}; diff --git a/src/mod_pubsub/mod_pubsub_odbc.erl b/src/mod_pubsub/mod_pubsub_odbc.erl index 2733c0379..e49971c03 100644 --- a/src/mod_pubsub/mod_pubsub_odbc.erl +++ b/src/mod_pubsub/mod_pubsub_odbc.erl @@ -74,8 +74,7 @@ disco_sm_items/5 ]). %% exported iq handlers --export([iq_local/3, - iq_sm/3 +-export([iq_sm/3 ]). %% exports for console debug manual use @@ -201,25 +200,22 @@ init([ServerHost, Opts]) -> ets:insert(gen_mod:get_module_proc(ServerHost, config), {ignore_pep_from_offline, PepOffline}), ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}), ejabberd_hooks:add(sm_remove_connection_hook, ServerHostB, ?MODULE, on_user_offline, 75), - ejabberd_hooks:add(disco_sm_identity, ServerHostB, ?MODULE, disco_sm_identity, 75), - ejabberd_hooks:add(disco_sm_features, ServerHostB, ?MODULE, disco_sm_features, 75), - ejabberd_hooks:add(disco_sm_items, ServerHostB, ?MODULE, disco_sm_items, 75), + 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), ejabberd_hooks:add(presence_probe_hook, ServerHostB, ?MODULE, presence_probe, 80), ejabberd_hooks:add(roster_in_subscription, ServerHostB, ?MODULE, in_subscription, 50), ejabberd_hooks:add(roster_out_subscription, ServerHostB, ?MODULE, out_subscription, 50), ejabberd_hooks:add(remove_user, ServerHostB, ?MODULE, remove_user, 50), ejabberd_hooks:add(anonymous_purge_hook, ServerHostB, ?MODULE, remove_user, 50), - 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), case lists:member(?PEPNODE, Plugins) of true -> ejabberd_hooks:add(feature_check_packet, ServerHostB, ?MODULE, feature_check_packet, 75), - 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); + ejabberd_hooks:add(disco_sm_identity, ServerHostB, ?MODULE, disco_sm_identity, 75), + ejabberd_hooks:add(disco_sm_features, ServerHostB, ?MODULE, disco_sm_features, 75), + ejabberd_hooks:add(disco_sm_items, ServerHostB, ?MODULE, disco_sm_items, 75), + 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); false -> ok end, @@ -395,15 +391,14 @@ send_loop(State) -> %% disco hooks handling functions %% -identity(Host) -> - Identity = case lists:member(?PEPNODE, plugins(Host)) of - true -> [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"pep">>)]; - false -> [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"service">>)] - end, - #xmlel{ns = ?NS_DISCO_INFO, name = 'identity', attrs = Identity}. - disco_local_identity(Acc, _From, To, <<>>, _Lang) -> - Acc ++ [identity(exmpp_jid:prep_domain_as_list(To))]; + case lists:member(?PEPNODE, plugins(exmpp_jid:prep_domain_as_list(To))) of + true -> + [#xmlel{name = 'identity', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"pep">>)]} + | Acc]; + false -> Acc + end; disco_local_identity(Acc, _From, _To, _Node, _Lang) -> Acc. @@ -424,81 +419,103 @@ disco_local_items(Acc, _From, _To, <<>>, _Lang) -> disco_local_items(Acc, _From, _To, _Node, _Lang) -> Acc. -disco_sm_identity(Acc, _From, To, <<>>, _Lang) -> - Acc ++ [identity(exmpp_jid:prep_domain_as_list(To))]; disco_sm_identity(Acc, From, To, Node, _Lang) -> - LOwner = jlib:short_prepd_bare_jid(To), - Acc ++ case node_disco_identity(LOwner, From, Node) of - {result, I} -> I; - _ -> [] - end. + disco_identity(To, Node, From) ++ Acc. -disco_sm_features(Acc, _From, _To, [], _Lang) -> - Acc; -disco_sm_features(Acc, From, To, Node, _Lang) -> - LOwner = jlib:short_prepd_bare_jid(To), - Features = node_disco_features(LOwner, From, Node), - case {Acc, Features} of - {{result, AccFeatures}, {result, AddFeatures}} -> - {result, AccFeatures++AddFeatures}; - {_, {result, AddFeatures}} -> - {result, AddFeatures}; - {_, _} -> - Acc +disco_identity(_Host, <<>>, _From) -> + [#xmlel{name = 'identity', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"pep">>)]}]; +disco_identity(Host, Node, From) -> + Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) -> + Owners = node_owners_call(Type, Idx), + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, _} -> + {result, + [#xmlel{name = 'identity', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"pep">>)]}, + #xmlel{name = 'identity', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"leaf">>) + | case get_option(Options, title) of + false -> []; + Title -> [?XMLATTR('name', Title)] + end + ]}]}; + {error, _} -> {result, []} + end + end, + case transaction(exmpp_jid:to_lower(Host), Node, Action, sync_dirty) of + {result, {_, Result}} -> Result; + _ -> [] end. -disco_sm_items(Acc, From, To, <<>>, _Lang) -> - Host = exmpp_jid:prep_domain_as_list(To), - case tree_action(Host, get_subnodes, [Host, <<>>, From]) of - [] -> - Acc; - Nodes -> - SBJID = exmpp_jid:to_binary(exmpp_jid:bare(To)), - Items = case Acc of - {result, I} -> I; - _ -> [] - end, - NodeItems = lists:map( - fun(#pubsub_node{nodeid = {_, Node}}) -> - #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs = - [?XMLATTR('jid', SBJID) | nodeAttr(Node)]} - end, Nodes), - {result, NodeItems ++ Items} + +disco_sm_features(empty, From, To, Node, Lang) -> + disco_sm_features({result, []}, From, To, Node, Lang); +disco_sm_features({result, OtherFeatures}, From, To, Node, _Lang) -> + {result, disco_features(To, Node, From) ++ OtherFeatures}. + +disco_features(_Host, <<>>, _From) -> + [?NS_PUBSUB_s + | [?NS_PUBSUB_s++"#"++Feature || Feature <- features("pep")]]; +disco_features(Host, Node, From) -> + Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) -> + Owners = node_owners_call(Type, Idx), + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, _} -> + {result, [?NS_PUBSUB_s + | [?NS_PUBSUB_s ++ "#" ++ Feature || Feature <- features("pep")]]}; + _ -> {result, []} + end + end, + case transaction(exmpp_jid:to_lower(Host), Node, Action, sync_dirty) of + {result, {_, Result}} -> Result; + _ -> [] + end. + +disco_sm_items(empty, From, To, Node, Lang) -> + disco_sm_items({result, []}, From, To, Node, Lang); +disco_sm_items({result, OtherItems}, From, To, Node, _Lang) -> + {result, disco_items(To, Node, From) ++ OtherItems}. + +disco_items(Host, <<>>, From) -> + Action = fun(#pubsub_node{nodeid ={_, NodeID}, options = Options, type = Type, id = Idx}, Acc) -> + Owners = node_owners_call(Type, Idx), + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, _} -> + [#xmlel{name = 'item', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('jid', exmpp_jid:to_binary(Host)), + ?XMLATTR('node', NodeID) | + case get_option(Options, title) of + false -> []; + [Title] -> [?XMLATTR('title', Title)] + end]} + | Acc]; + _ -> Acc + end + end, + case transaction_on_nodes(exmpp_jid:to_lower(Host), Action, sync_dirty) of + {result, Result} -> Result; + _ -> [] end; -disco_sm_items(Acc, From, To, NodeB, _Lang) -> - SNode = binary_to_list(NodeB), - Node = string_to_node(SNode), - %% TODO, use iq_disco_items(Host, Node, From) - Host = exmpp_jid:prep_domain_as_list(To), - SBJID = exmpp_jid:to_binary(exmpp_jid:bare(To)), - Action = fun(#pubsub_node{type = Type, id = NodeId}) -> - % TODO call get_items/6 instead for access control (EJAB-1033) - case node_call(Type, get_items, [NodeId, From]) of - {result, []} -> - none; - {result, AllItems} -> - Items = case Acc of - {result, I} -> I; - _ -> [] - end, - NodeItems = lists:map( - fun(#pubsub_item{itemid = {Id, _}}) -> - {result, Name} = node_call(Type, get_item_name, [Host, Node, Id]), - #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs = - [?XMLATTR('jid', SBJID), - ?XMLATTR('name', Name)]} - end, AllItems), - {result, NodeItems ++ Items}; - _ -> - none - end - end, - case transaction(Host, Node, Action, sync_dirty) of - {result, {_, Items}} -> {result, Items}; - _ -> Acc - end. - +disco_items(Host, Node, From) -> + Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) -> + Owners = node_owners_call(Type, Idx), + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, Items} -> + {result, + [#xmlel{name = 'item', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('jid', exmpp_jid:to_binary(Host)), + ?XMLATTR('name', ItemID)]} + || #pubsub_item{itemid = {ItemID,_}} <- Items]}; + _ -> {result, []} + end + end, + case transaction(exmpp_jid:to_lower(Host), Node, Action, sync_dirty) of + {result, {_, Result}} -> Result; + _ -> [] + end. + %% ------- %% presence hooks handling functions %% @@ -684,11 +701,11 @@ terminate(_Reason, #state{host = Host, case lists:member(?PEPNODE, Plugins) of true -> ejabberd_hooks:delete(feature_check_packet, ServerHostB, ?MODULE, feature_check_packet, 75), - 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); + ejabberd_hooks:delete(disco_sm_identity, ServerHostB, ?MODULE, disco_sm_identity, 75), + ejabberd_hooks:delete(disco_sm_features, ServerHostB, ?MODULE, disco_sm_features, 75), + ejabberd_hooks:delete(disco_sm_items, ServerHostB, ?MODULE, disco_sm_items, 75), + gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHostB, ?NS_PUBSUB), + gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHostB, ?NS_PUBSUB_OWNER); false -> ok end, @@ -696,9 +713,6 @@ terminate(_Reason, #state{host = Host, 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), - ejabberd_hooks:delete(disco_sm_identity, ServerHostB, ?MODULE, disco_sm_identity, 75), - ejabberd_hooks:delete(disco_sm_features, ServerHostB, ?MODULE, disco_sm_features, 75), - ejabberd_hooks:delete(disco_sm_items, ServerHostB, ?MODULE, disco_sm_items, 75), ejabberd_hooks:delete(presence_probe_hook, ServerHostB, ?MODULE, presence_probe, 80), ejabberd_hooks:delete(roster_in_subscription, ServerHostB, ?MODULE, in_subscription, 50), ejabberd_hooks:delete(roster_out_subscription, ServerHostB, ?MODULE, out_subscription, 50), @@ -878,10 +892,6 @@ command_disco_info(_Host, ?NS_PUBSUB_GET_PENDING_b, _From) -> node_disco_info(Host, Node, From) -> node_disco_info(Host, Node, From, true, true). -node_disco_identity(Host, Node, From) -> - node_disco_info(Host, Node, From, true, false). -node_disco_features(Host, Node, From) -> - node_disco_info(Host, Node, From, false, true). node_disco_info(Host, Node, From, Identity, Features) -> Action = fun(#pubsub_node{type = Type, id = NodeId}) -> @@ -988,17 +998,16 @@ iq_disco_items(Host, Item, From, RSM) -> {result, []}; [SNode] -> Node = string_to_node(SNode), - Action = - fun(#pubsub_node{type = Type, id = NodeId}) -> - % TODO call get_items/6 instead for access control (EJAB-1033) - {NodeItems, RsmOut} = case node_call(Type, get_items, [NodeId, From, RSM]) of - {result, I} -> I; - _ -> {[], none} - end, + Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) -> + Owners = node_owners_call(Type, Idx), + {NodeItems, RsmOut} = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners, RSM) of + {result, R} -> R; + _ -> {[], none} + end, Nodes = lists:map( - fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) -> + fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) -> Attrs = - case get_option(Options, title) of + case get_option(SubOptions, title) of false -> [?XMLATTR('jid', Host) | nodeAttr(SubNode)]; Title -> @@ -1019,24 +1028,17 @@ iq_disco_items(Host, Item, From, RSM) -> end end. -iq_local(From, To, #iq{type = Type, payload = SubEl, ns = XMLNS, lang = Lang} = IQ_Rec) -> - ServerHost = exmpp_jid:prep_domain_as_list(To), - FromHost = exmpp_jid:prep_domain_as_list(To), - %% Accept IQs to server only from our own users. - if - FromHost /= ServerHost -> - exmpp_iq:error(IQ_Rec, 'forbidden'); - true -> - LOwner = jlib:short_prepd_bare_jid(From), - Res = case XMLNS of - ?NS_PUBSUB -> iq_pubsub(LOwner, ServerHost, From, Type, SubEl, Lang); - ?NS_PUBSUB_OWNER -> iq_pubsub_owner(LOwner, ServerHost, From, Type, SubEl, Lang) - end, - case Res of - {result, []} -> exmpp_iq:result(IQ_Rec); - {result, [IQRes]} -> exmpp_iq:result(IQ_Rec, IQRes); - {error, Error} -> exmpp_iq:error(IQ_Rec, Error) - end +get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) -> + if (AccessModel == presence) or (AccessModel == roster) -> + case Host of + {User, Server, _} -> + get_roster_info(User, Server, From, AllowedGroups); + _ -> + [{OUser, OServer, _}|_] = Owners, + get_roster_info(OUser, OServer, From, AllowedGroups) + end; + true -> + {true, true} end. iq_sm(From, To, #iq{type = Type, payload = SubEl, ns = XMLNS, lang = Lang} = IQ_Rec) -> @@ -1758,24 +1760,8 @@ subscribe_node(Host, Node, From, JID, Configuration) -> AccessModel = get_option(Options, access_model), SendLast = get_option(Options, send_last_published_item), AllowedGroups = get_option(Options, roster_groups_allowed, []), - {PresenceSubscription, RosterGroup} = - case Host of - {OUser, OServer, _} -> - get_roster_info(OUser, OServer, - Subscriber, AllowedGroups); - _ -> - case Subscriber of - {undefined, undefined, undefined} -> - {false, false}; - _ -> - case node_owners_call(Type, NodeId) of - [{OU, OS, _} | _] -> - get_roster_info(OU, OS, Subscriber, AllowedGroups); - _ -> - {false, false} - end - end - end, + Owners = node_owners_call(Type, NodeId), + {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups), if not SubscribeFeature -> %% Node does not support subscriptions @@ -2139,14 +2125,8 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs, RSM) -> PersistentFeature = lists:member("persistent-items", Features), AccessModel = get_option(Options, access_model), AllowedGroups = get_option(Options, roster_groups_allowed, []), - {PresenceSubscription, RosterGroup} = - case Host of - {OUser, OServer, _} -> - get_roster_info(OUser, OServer, - jlib:short_prepd_jid(From), AllowedGroups); - _ -> - {true, true} - end, + Owners = node_owners_call(Type, NodeId), + {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), if not RetreiveFeature -> %% Item Retrieval Not Supported @@ -2196,6 +2176,17 @@ get_item(Host, Node, ItemId) -> {result, {_, Items}} -> Items; Error -> Error end. +get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) -> + case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, none) of + {result, {I, _}} -> {result, I}; + Error -> Error + end. +get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, RSM) -> + AccessModel = get_option(Options, access_model), + AllowedGroups = get_option(Options, roster_groups_allowed, []), + {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), + node_call(Type, get_items, [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined, RSM]). + %% @spec (Host, Node, NodeId, Type, LJID, Number) -> any() %% Host = pubsubHost() @@ -2459,7 +2450,7 @@ set_options_helper(Configuration, JID, NodeID, SubID, Type) -> _ -> invalid end, Subscriber = try exmpp_jid:parse(JID) of - J -> jlib:short_jid(J) + J -> J catch _ -> exmpp_jid:make("", "", "") %% TODO, check if use <<>> instead of "" end, @@ -2586,8 +2577,9 @@ get_subscriptions(Host, Node, JID) -> end end, case transaction(Host, Node, Action, sync_dirty) of - {result, {_, []}} -> - {error, 'item-not-found'}; +%% Fix bug when node owner retrieve an empty subscriptions list +% {result, {_, []}} -> +% {error, 'item-not-found'}; {result, {_, Subscriptions}} -> Entities = lists:flatmap( fun({_, none}) -> []; @@ -2681,7 +2673,8 @@ set_subscriptions(Host, Node, From, EntitiesEls) -> end end. - +get_roster_info(_, _, {undefined, undefined, _}, _) -> + {false, false}; %% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups) %% -> {PresenceSubscription, RosterGroup} get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) -> @@ -2695,7 +2688,9 @@ get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, A RosterGroup = lists:any(fun(Group) -> lists:member(Group, AllowedGroups) end, Groups), - {PresenceSubscription, RosterGroup}. + {PresenceSubscription, RosterGroup}; +get_roster_info(OwnerUser, OwnerServer, JID, AllowedGroups) -> + get_roster_info(OwnerUser, OwnerServer, exmpp_jid:to_lower(JID), AllowedGroups). %% @spec (AffiliationStr) -> Affiliation %% AffiliationStr = string() @@ -3714,6 +3709,11 @@ transaction(Host, Node, Action, Trans) -> end end, Trans). +transaction_on_nodes(Host, Action, Trans) -> + transaction(Host, fun() -> + {result, lists:foldl(Action, [], tree_call(Host, get_nodes, [Host]))} + end, Trans). + transaction(Host, Fun, Trans) -> transaction_retry(Host, Fun, Trans, 2). @@ -3865,8 +3865,8 @@ is_feature_supported(_, []) -> true; is_feature_supported(#xmlel{name = 'presence', children = Els}, Feature) -> case mod_caps:read_caps(Els) of - nothing -> false; - Caps -> lists:member(Feature ++ "+notify", mod_caps:get_features(Caps)) + nothing -> false; + Caps -> lists:member(Feature ++ "+notify", mod_caps:get_features(Caps)) end. on_user_offline(_, JID, _) -> diff --git a/src/mod_pubsub/pubsub_odbc.patch b/src/mod_pubsub/pubsub_odbc.patch index b910e09a9..4ca79a9bd 100644 --- a/src/mod_pubsub/pubsub_odbc.patch +++ b/src/mod_pubsub/pubsub_odbc.patch @@ -1,5 +1,5 @@ ---- mod_pubsub.erl 2010-07-01 11:15:55.000000000 +0200 -+++ mod_pubsub_odbc.erl 2010-07-01 11:18:12.000000000 +0200 +--- mod_pubsub.erl 2010-08-04 18:28:18.000000000 +0200 ++++ mod_pubsub_odbc.erl 2010-08-04 18:29:41.000000000 +0200 @@ -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. @@ -22,7 +22,7 @@ %% exports for hooks -export([presence_probe/3, -@@ -105,7 +105,7 @@ +@@ -104,7 +104,7 @@ string_to_affiliation/1, extended_error/2, extended_error/3, @@ -31,7 +31,7 @@ ]). %% API and gen_server callbacks -@@ -124,7 +124,7 @@ +@@ -123,7 +123,7 @@ -export([send_loop/1 ]). @@ -40,7 +40,7 @@ -define(LOOPNAME, ejabberd_mod_pubsub_loop). -define(PLUGIN_PREFIX, "node_"). -define(TREE_PREFIX, "nodetree_"). -@@ -224,8 +224,6 @@ +@@ -220,8 +220,6 @@ ok end, ejabberd_router:register_route(Host), @@ -49,7 +49,7 @@ init_nodes(Host, ServerHost, NodeTree, Plugins), State = #state{host = Host, server_host = ServerHost, -@@ -284,206 +282,15 @@ +@@ -280,206 +278,15 @@ init_nodes(Host, ServerHost, _NodeTree, Plugins) -> %% TODO, this call should be done plugin side @@ -260,7 +260,7 @@ send_loop(State) -> receive -@@ -495,7 +302,10 @@ +@@ -491,7 +298,10 @@ %% for each node From is subscribed to %% and if the node is so configured, send the last published item to From lists:foreach(fun(PType) -> @@ -272,7 +272,54 @@ lists:foreach( fun({Node, subscribed, _, SubJID}) -> if (SubJID == LJID) or (SubJID == BJID) -> -@@ -758,10 +568,10 @@ +@@ -616,7 +426,8 @@ + [#xmlel{name = 'identity', ns = ?NS_DISCO_INFO, + attrs = [?XMLATTR('category', <<"pubsub">>), ?XMLATTR('type', <<"pep">>)]}]; + disco_identity(Host, Node, From) -> +- Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) -> ++ Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) -> ++ Owners = node_owners_call(Type, Idx), + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, _} -> + {result, +@@ -647,7 +458,8 @@ + [?NS_PUBSUB_s + | [?NS_PUBSUB_s++"#"++Feature || Feature <- features("pep")]]; + disco_features(Host, Node, From) -> +- Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) -> ++ Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) -> ++ Owners = node_owners_call(Type, Idx), + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, _} -> + {result, [?NS_PUBSUB_s +@@ -666,7 +478,8 @@ + {result, disco_items(To, Node, From) ++ OtherItems}. + + disco_items(Host, <<>>, From) -> +- Action = fun(#pubsub_node{nodeid ={_, NodeID}, options = Options, type = Type, id = Idx, owners = Owners}, Acc) -> ++ Action = fun(#pubsub_node{nodeid ={_, NodeID}, options = Options, type = Type, id = Idx}, Acc) -> ++ Owners = node_owners_call(Type, Idx), + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, _} -> + [#xmlel{name = 'item', ns = ?NS_DISCO_INFO, +@@ -680,13 +493,14 @@ + _ -> Acc + end + end, +- case transaction(exmpp_jid:to_lower(Host), Action, sync_dirty) of ++ case transaction_on_nodes(exmpp_jid:to_lower(Host), Action, sync_dirty) of + {result, Result} -> Result; + _ -> [] + end; + + disco_items(Host, Node, From) -> +- Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) -> ++ Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) -> ++ Owners = node_owners_call(Type, Idx), + case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of + {result, Items} -> + {result, +@@ -771,10 +585,10 @@ lists:foreach(fun(PType) -> {result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Entity]), lists:foreach(fun @@ -285,7 +332,7 @@ true -> node_action(Host, PType, unsubscribe_node, [NodeId, Entity, JID, all]); false -> -@@ -939,10 +749,11 @@ +@@ -949,10 +763,11 @@ end, ejabberd_router:route(To, From, Res); #iq{type = get, ns = ?NS_DISCO_ITEMS, @@ -299,7 +346,7 @@ {result, IQRes} -> Result = #xmlel{ns = ?NS_DISCO_ITEMS, name = 'query', attrs = QAttrs, -@@ -1083,7 +894,7 @@ +@@ -1089,7 +904,7 @@ [] -> ["leaf"]; %% No sub-nodes: it's a leaf node _ -> @@ -308,7 +355,7 @@ {result, []} -> ["collection"]; {result, _} -> ["leaf", "collection"]; _ -> [] -@@ -1099,8 +910,9 @@ +@@ -1105,8 +920,9 @@ []; true -> [#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = [?XMLATTR('var', ?NS_PUBSUB_s)]} | @@ -320,7 +367,7 @@ end, features(Type))] end, %% TODO: add meta-data info (spec section 5.4) -@@ -1129,8 +941,9 @@ +@@ -1135,8 +951,9 @@ #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = [?XMLATTR('var', ?NS_PUBSUB_s)]}, #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = [?XMLATTR('var', ?NS_ADHOC_s)]}, #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = [?XMLATTR('var', ?NS_VCARD_s)]}] ++ @@ -332,7 +379,7 @@ end, features(Host, Node))}; ?NS_ADHOC_b -> command_disco_info(Host, Node, From); -@@ -1140,7 +953,7 @@ +@@ -1146,7 +963,7 @@ node_disco_info(Host, Node, From) end. @@ -341,7 +388,7 @@ case tree_action(Host, get_subnodes, [Host, <<>>, From]) of Nodes when is_list(Nodes) -> {result, lists:map( -@@ -1157,7 +970,7 @@ +@@ -1163,7 +980,7 @@ Other -> Other end; @@ -350,7 +397,7 @@ %% TODO: support localization of this string CommandItems = [ #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', -@@ -1166,10 +979,10 @@ +@@ -1172,19 +989,20 @@ ?XMLATTR('name', "Get Pending") ]}], {result, CommandItems}; @@ -363,19 +410,20 @@ case string:tokens(Item, "!") of [_SNode, _ItemID] -> {result, []}; -@@ -1178,9 +991,9 @@ - Action = - fun(#pubsub_node{type = Type, id = NodeId}) -> - % TODO call get_items/6 instead for access control (EJAB-1033) -- NodeItems = case node_call(Type, get_items, [NodeId, From]) of -+ {NodeItems, RsmOut} = case node_call(Type, get_items, [NodeId, From, RSM]) of - {result, I} -> I; -- _ -> [] -+ _ -> {[], none} - end, + [SNode] -> + Node = string_to_node(SNode), +- Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) -> +- NodeItems = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of ++ Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) -> ++ Owners = node_owners_call(Type, Idx), ++ {NodeItems, RsmOut} = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners, RSM) of + {result, R} -> R; +- _ -> [] ++ _ -> {[], none} + end, Nodes = lists:map( - fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) -> -@@ -1198,7 +1011,7 @@ + fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) -> +@@ -1202,7 +1020,7 @@ {result, Name} = node_call(Type, get_item_name, [Host, Node, RN]), #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs = [?XMLATTR('jid', Host), ?XMLATTR('name', Name)]} end, NodeItems), @@ -384,7 +432,20 @@ end, case transaction(Host, Node, Action, sync_dirty) of {result, {_, Result}} -> {result, Result}; -@@ -1330,7 +1143,8 @@ +@@ -1210,12 +1028,6 @@ + end + end. + +-get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) -> +- AccessModel = get_option(Options, access_model), +- AllowedGroups = get_option(Options, roster_groups_allowed, []), +- {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), +- node_call(Type, get_items, [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined]). +- + get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) -> + if (AccessModel == presence) or (AccessModel == roster) -> + case Host of +@@ -1333,7 +1145,8 @@ (_, Acc) -> Acc end, [], exmpp_xml:remove_cdata_from_list(Els)), @@ -394,7 +455,7 @@ {get, 'subscriptions'} -> get_subscriptions(Host, Node, From, Plugins); {get, 'affiliations'} -> -@@ -1487,7 +1301,8 @@ +@@ -1490,7 +1303,8 @@ _ -> [] end end, @@ -404,7 +465,7 @@ sync_dirty) of {result, Res} -> Res; Err -> Err -@@ -1531,7 +1346,7 @@ +@@ -1534,7 +1348,7 @@ %%% authorization handling @@ -413,7 +474,7 @@ Lang = <<"en">>, %% TODO fix {U, S, R} = Subscriber, Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children = -@@ -1561,7 +1376,7 @@ +@@ -1564,7 +1378,7 @@ lists:foreach(fun(Owner) -> {U, S, R} = Owner, ejabberd_router:route(service_jid(Host), exmpp_jid:make(U, S, R), Stanza) @@ -422,7 +483,7 @@ find_authorization_response(Packet) -> Els = Packet#xmlel.children, -@@ -1620,8 +1435,8 @@ +@@ -1623,8 +1437,8 @@ "true" -> true; _ -> false end, @@ -433,7 +494,7 @@ {result, Subscriptions} = node_call(Type, get_subscriptions, [NodeId, Subscriber]), if not IsApprover -> -@@ -1820,7 +1635,7 @@ +@@ -1823,7 +1637,7 @@ end, Reply = #xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children = [#xmlel{ns = ?NS_PUBSUB, name = 'create', attrs = nodeAttr(Node)}]}, @@ -442,7 +503,7 @@ {result, {NodeId, SubsByDepth, {Result, broadcast}}} -> broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth), case Result of -@@ -1924,7 +1739,7 @@ +@@ -1927,7 +1741,7 @@ %%
  • The node does not exist.
  • %% subscribe_node(Host, Node, From, JID, Configuration) -> @@ -451,32 +512,24 @@ {result, GoodSubOpts} -> GoodSubOpts; _ -> invalid end, -@@ -1934,7 +1749,7 @@ +@@ -1937,7 +1751,7 @@ _:_ -> {undefined, undefined, undefined} end, -- Action = fun(#pubsub_node{options = Options, owners = [Owner|_], type = Type, id = NodeId}) -> +- Action = fun(#pubsub_node{options = Options, owners = Owners, type = Type, id = NodeId}) -> + Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> Features = features(Type), SubscribeFeature = lists:member("subscribe", Features), OptionsFeature = lists:member("subscription-options", Features), -@@ -1953,9 +1768,12 @@ - {undefined, undefined, undefined} -> - {false, false}; - _ -> -- {OU, OS, _} = Owner, -- get_roster_info(OU, OS, -- Subscriber, AllowedGroups) -+ case node_owners_call(Type, NodeId) of -+ [{OU, OS, _} | _] -> -+ get_roster_info(OU, OS, Subscriber, AllowedGroups); -+ _ -> -+ {false, false} -+ end - end - end, +@@ -1946,6 +1760,7 @@ + AccessModel = get_option(Options, access_model), + SendLast = get_option(Options, send_last_published_item), + AllowedGroups = get_option(Options, roster_groups_allowed, []), ++ Owners = node_owners_call(Type, NodeId), + {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups), if -@@ -2301,7 +2119,7 @@ + not SubscribeFeature -> +@@ -2290,7 +2105,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. @@ -485,7 +538,22 @@ MaxItems = if SMaxItems == "" -> get_max_items_node(Host); -@@ -2340,11 +2158,11 @@ +@@ -2304,12 +2119,13 @@ + {error, Error} -> + {error, Error}; + _ -> +- Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId, owners = Owners}) -> ++ Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> + Features = features(Type), + RetreiveFeature = lists:member("retrieve-items", Features), + PersistentFeature = lists:member("persistent-items", Features), + AccessModel = get_option(Options, access_model), + AllowedGroups = get_option(Options, roster_groups_allowed, []), ++ Owners = node_owners_call(Type, NodeId), + {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), + if + not RetreiveFeature -> +@@ -2322,11 +2138,11 @@ node_call(Type, get_items, [NodeId, From, AccessModel, PresenceSubscription, RosterGroup, @@ -499,7 +567,7 @@ SendItems = case ItemIDs of [] -> Items; -@@ -2357,7 +2175,7 @@ +@@ -2339,7 +2155,7 @@ %% number of items sent to MaxItems: {result, #xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children = [#xmlel{ns = ?NS_PUBSUB, name = 'items', attrs = nodeAttr(Node), children = @@ -508,7 +576,25 @@ Error -> Error end -@@ -2389,16 +2207,29 @@ +@@ -2360,6 +2176,17 @@ + {result, {_, Items}} -> Items; + Error -> Error + end. ++get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) -> ++ case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, none) of ++ {result, {I, _}} -> {result, I}; ++ Error -> Error ++ end. ++get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, RSM) -> ++ AccessModel = get_option(Options, access_model), ++ AllowedGroups = get_option(Options, roster_groups_allowed, []), ++ {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), ++ node_call(Type, get_items, [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined, RSM]). ++ + + %% @spec (Host, Node, NodeId, Type, LJID, Number) -> any() + %% Host = pubsubHost() +@@ -2371,16 +2198,29 @@ %% @doc

    Resend the items of a node to the user.

    %% @todo use cache-last-item feature send_items(Host, Node, NodeId, Type, LJID, last) -> @@ -544,7 +630,7 @@ send_items(Host, Node, NodeId, Type, {LU, LS, LR} = LJID, Number) -> ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of {result, []} -> -@@ -2525,7 +2356,8 @@ +@@ -2507,7 +2347,8 @@ error -> {error, 'bad-request'}; _ -> @@ -554,7 +640,7 @@ case lists:member(Owner, Owners) of true -> OwnerJID = exmpp_jid:make(Owner), -@@ -2535,24 +2367,8 @@ +@@ -2517,24 +2358,8 @@ end, lists:foreach( fun({JID, Affiliation}) -> @@ -581,7 +667,7 @@ end, FilteredEntities), {result, []}; _ -> -@@ -2607,11 +2423,11 @@ +@@ -2589,11 +2414,11 @@ end. read_sub(Subscriber, Node, NodeID, SubID, Lang) -> @@ -595,7 +681,7 @@ OptionsEl = #xmlel{ns = ?NS_PUBSUB, name = 'options', attrs = [ ?XMLATTR('jid', exmpp_jid:to_binary(Subscriber)), ?XMLATTR('subid', SubID) | nodeAttr(Node)], -@@ -2638,7 +2454,7 @@ +@@ -2620,7 +2445,7 @@ end. set_options_helper(Configuration, JID, NodeID, SubID, Type) -> @@ -604,7 +690,7 @@ {result, GoodSubOpts} -> GoodSubOpts; _ -> invalid end, -@@ -2668,7 +2484,7 @@ +@@ -2650,7 +2475,7 @@ write_sub(_Subscriber, _NodeID, _SubID, invalid) -> {error, extended_error('bad-request', "invalid-options")}; write_sub(Subscriber, NodeID, SubID, Options) -> @@ -613,7 +699,7 @@ {error, notfound} -> {error, extended_error('not-acceptable', "invalid-subid")}; {result, _} -> -@@ -2841,8 +2657,8 @@ +@@ -2824,8 +2649,8 @@ ?XMLATTR('subsription', subscription_to_string(Sub)) | nodeAttr(Node)]}]}]}, ejabberd_router:route(service_jid(Host), JID, Stanza) end, @@ -624,7 +710,7 @@ true -> Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) -> -@@ -3188,7 +3004,7 @@ +@@ -3174,7 +2999,7 @@ {Depth, [{N, get_node_subs(N)} || N <- Nodes]} end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))} end, @@ -633,7 +719,7 @@ {result, CollSubs} -> CollSubs; _ -> [] end. -@@ -3202,9 +3018,9 @@ +@@ -3188,9 +3013,9 @@ get_options_for_subs(NodeID, Subs) -> lists:foldl(fun({JID, subscribed, SubID}, Acc) -> @@ -645,7 +731,7 @@ _ -> Acc end; (_, Acc) -> -@@ -3426,6 +3242,30 @@ +@@ -3412,6 +3237,30 @@ Result end. @@ -676,7 +762,7 @@ %% @spec (Host, Options) -> MaxItems %% Host = host() %% Options = [Option] -@@ -3829,7 +3669,13 @@ +@@ -3815,7 +3664,13 @@ tree_action(Host, Function, Args) -> ?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]), Fun = fun() -> tree_call(Host, Function, Args) end, @@ -691,7 +777,7 @@ %% @doc

    node plugin call.

    node_call(Type, Function, Args) -> -@@ -3849,13 +3695,13 @@ +@@ -3835,13 +3690,13 @@ node_action(Host, Type, Function, Args) -> ?DEBUG("node_action ~p ~p ~p ~p",[Host,Type,Function,Args]), @@ -707,10 +793,17 @@ case tree_call(Host, get_node, [Host, Node]) of N when is_record(N, pubsub_node) -> case Action(N) of -@@ -3868,8 +3714,15 @@ +@@ -3854,13 +3709,20 @@ end end, Trans). +-transaction(Host, Action, Trans) -> +- transaction(fun() -> ++transaction_on_nodes(Host, Action, Trans) -> ++ transaction(Host, fun() -> + {result, lists:foldl(Action, [], tree_call(Host, get_nodes, [Host]))} + end, Trans). + -transaction(Fun, Trans) -> - case catch mnesia:Trans(Fun) of +transaction(Host, Fun, Trans) -> @@ -725,7 +818,7 @@ {result, Result} -> {result, Result}; {error, Error} -> {error, Error}; {atomic, {result, Result}} -> {result, Result}; -@@ -3877,6 +3730,15 @@ +@@ -3868,6 +3730,15 @@ {aborted, Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]), {error, 'internal-server-error'}; @@ -741,7 +834,7 @@ {'EXIT', Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]), {error, 'internal-server-error'}; -@@ -3885,6 +3747,16 @@ +@@ -3876,6 +3747,16 @@ {error, 'internal-server-error'} end.