diff options
Diffstat (limited to 'src/mod_pubsub/mod_pubsub_odbc.erl')
-rw-r--r-- | src/mod_pubsub/mod_pubsub_odbc.erl | 331 |
1 files changed, 179 insertions, 152 deletions
diff --git a/src/mod_pubsub/mod_pubsub_odbc.erl b/src/mod_pubsub/mod_pubsub_odbc.erl index 6150141e7..3507091f2 100644 --- a/src/mod_pubsub/mod_pubsub_odbc.erl +++ b/src/mod_pubsub/mod_pubsub_odbc.erl @@ -73,8 +73,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 @@ -198,24 +197,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, ServerHost, ?MODULE, on_user_offline, 75), - ejabberd_hooks:add(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75), - ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), - ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), + ejabberd_hooks:add(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), ejabberd_hooks:add(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80), ejabberd_hooks:add(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), ejabberd_hooks:add(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50), ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:add(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc), case lists:member(?PEPNODE, Plugins) of true -> ejabberd_hooks:add(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75), - 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), - gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB, ?MODULE, iq_local, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_local, IQDisc); + ejabberd_hooks:add(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75), + ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), + ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), + gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc); false -> ok end, @@ -387,15 +384,12 @@ send_loop(State) -> %% disco hooks handling functions %% -identity(Host) -> - Identity = case lists:member(?PEPNODE, plugins(Host)) of - true -> [{"category", "pubsub"}, {"type", "pep"}]; - false -> [{"category", "pubsub"}, {"type", "service"}] - end, - {xmlelement, "identity", Identity, []}. - disco_local_identity(Acc, _From, To, [], _Lang) -> - Acc ++ [identity(To#jid.lserver)]; + case lists:member(?PEPNODE, plugins(To#jid.lserver)) of + true -> + [{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []} | Acc]; + false -> Acc + end; disco_local_identity(Acc, _From, _To, _Node, _Lang) -> Acc. @@ -416,76 +410,120 @@ 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(To#jid.lserver)]; +disco_sm_identity(Acc, From, To, Node, Lang) when is_list(Node) -> + disco_sm_identity(Acc, From, To, list_to_binary(Node), Lang); +disco_sm_identity(empty, From, To, Node, Lang) -> + disco_sm_identity([], From, To, Node, Lang); disco_sm_identity(Acc, From, To, Node, _Lang) -> - LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)), - Acc ++ case node_disco_identity(LOwner, From, Node) of - {result, I} -> I; - _ -> [] - end. + disco_identity(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From) ++ Acc. + +disco_identity(_Host, <<>>, _From) -> + [{xmlelement, "identity", [{"category", "pubsub"}, {"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, [{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []}, + {xmlelement, "identity", + [{"category", "pubsub"}, + {"type", "leaf"} + | case get_option(Options, title) of + false -> []; + [Title] -> [{"name", Title}] + end], + []}]}; + _ -> {result, []} + end + end, + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, Result}} -> Result; + _ -> [] + end. -disco_sm_features(Acc, _From, _To, [], _Lang) -> - Acc; -disco_sm_features(Acc, From, To, Node, _Lang) -> - LOwner = jlib:jid_tolower(jlib:jid_remove_resource(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_sm_features(Acc, From, To, Node, Lang) when is_list(Node) -> + disco_sm_features(Acc, From, To, list_to_binary(Node), Lang); +disco_sm_features(empty, From, To, Node, Lang) -> + disco_sm_features({result, []}, From, To, Node, Lang); +disco_sm_features({result, OtherFeatures} = _Acc, From, To, Node, _Lang) -> + {result, + OtherFeatures ++ + disco_features(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From)}; +disco_sm_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + +disco_features(_Host, <<>>, _From) -> + [?NS_PUBSUB + | [?NS_PUBSUB++"#"++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 + | [?NS_PUBSUB ++ "#" ++ Feature || Feature <- features("pep")]]}; + _ -> {result, []} + end + end, + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, Result}} -> Result; + _ -> [] end. -disco_sm_items(Acc, From, To, [], _Lang) -> - Host = To#jid.lserver, - case tree_action(Host, get_subnodes, [Host, <<>>, From]) of - [] -> - Acc; - Nodes -> - SBJID = jlib:jid_to_string(jlib:jid_remove_resource(To)), - Items = case Acc of - {result, I} -> I; - _ -> [] - end, - NodeItems = lists:map( - fun(#pubsub_node{nodeid = {_, Node}}) -> - {xmlelement, "item", - [{"jid", SBJID}|nodeAttr(Node)], - []} - end, Nodes), - {result, NodeItems ++ Items} +disco_sm_items(Acc, From, To, Node, Lang) when is_list(Node) -> + disco_sm_items(Acc, From, To, list_to_binary(Node), Lang); +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, + lists:usort(OtherItems ++ + disco_items(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From))}; +disco_sm_items(Acc, _From, _To, _Node, _Lang) -> + Acc. + +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, _} -> + [{xmlelement, "item", + [{"node", binary_to_list(NodeID)}, + {"jid", case Host of + {_,_,_} -> jlib:jid_to_string(Host); + _Host -> Host + end} + | case get_option(Options, title) of + false -> []; + [Title] -> [{"name", Title}] + end], + []} + | Acc]; + _ -> Acc + end + end, + case transaction_on_nodes(Host, Action, sync_dirty) of + {result, Items} -> Items; + _ -> [] end; -disco_sm_items(Acc, From, To, SNode, _Lang) -> - Host = To#jid.lserver, - Node = string_to_node(SNode), - 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} -> - SBJID = jlib:jid_to_string(jlib:jid_remove_resource(To)), - 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]), - {xmlelement, "item", [{"jid", SBJID}, {"name", Name}], []} - end, AllItems), - {result, NodeItems ++ Items}; - _ -> - none - end - 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, [{xmlelement, "item", + [{"jid", case Host of + {_,_,_} -> jlib:jid_to_string(Host); + _Host -> Host + end}, + {"name", ItemID}], []} + || #pubsub_item{itemid = {ItemID,_}} <- Items]}; + _ -> {result, []} + end + end, case transaction(Host, Node, Action, sync_dirty) of - {result, {_, Items}} -> {result, Items}; - _ -> Acc + {result, {_, Result}} -> Result; + _ -> [] end. %% ------- @@ -661,25 +699,23 @@ terminate(_Reason, #state{host = Host, case lists:member(?PEPNODE, Plugins) of true -> ejabberd_hooks:delete(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75), - ejabberd_hooks:delete(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75), - ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75), - ejabberd_hooks:delete(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75), - gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB), - gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER); + ejabberd_hooks:delete(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75), + ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), + ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), + gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB), + gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER); false -> ok end, ejabberd_hooks:delete(sm_remove_connection_hook, ServerHost, ?MODULE, on_user_offline, 75), - ejabberd_hooks:delete(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75), - ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), - ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), + ejabberd_hooks:delete(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75), + ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75), + ejabberd_hooks:delete(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75), ejabberd_hooks:delete(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80), ejabberd_hooks:delete(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), ejabberd_hooks:delete(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50), ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:delete(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB), - gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER), mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB), gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! stop, terminate_plugins(Host, ServerHost, Plugins, TreePlugin). @@ -821,10 +857,10 @@ command_disco_info(_Host, <<?NS_PUBSUB_GET_PENDING>>, _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_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}) -> @@ -926,17 +962,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 -> [{"jid", Host} |nodeAttr(SubNode)]; Title -> @@ -957,24 +992,6 @@ iq_disco_items(Host, Item, From, RSM) -> end end. -iq_local(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) -> - ServerHost = To#jid.lserver, - %% Accept IQs to server only from our own users. - if - From#jid.lserver /= ServerHost -> - IQ#iq{type = error, sub_el = [?ERR_FORBIDDEN, SubEl]}; - true -> - LOwner = jlib:jid_tolower(jlib:jid_remove_resource(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, IQRes} -> IQ#iq{type = result, sub_el = IQRes}; - {error, Error} -> IQ#iq{type = error, sub_el = [Error, SubEl]} - end - end. - iq_sm(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) -> ServerHost = To#jid.lserver, LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)), @@ -1687,25 +1704,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 - {"", "", ""} -> - {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 @@ -2054,14 +2054,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:jid_tolower(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 @@ -2112,6 +2106,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() @@ -2584,8 +2589,24 @@ set_subscriptions(Host, Node, From, EntitiesEls) -> end 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. + %% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups) %% -> {PresenceSubscription, RosterGroup} +get_roster_info(_, _, {"", "", _}, _) -> + {false, false}; get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) -> {Subscription, Groups} = ejabberd_hooks:run_fold( @@ -2597,7 +2618,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, jlib:jid_tolower(JID), AllowedGroups). %% @spec (AffiliationStr) -> Affiliation %% AffiliationStr = string() @@ -3582,6 +3605,10 @@ transaction(Host, Node, Action, Trans) -> Error 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). |