From ee861e650d9e2c242d0d4027fbae77bedaa50e9f Mon Sep 17 00:00:00 2001 From: Christophe Romain Date: Mon, 4 Oct 2010 17:41:59 +0200 Subject: [PATCH] fix dializer warnings (thanks to Karim Gemayel) --- src/mod_pubsub/node_dag.erl | 407 ++++++++++++++++++++++++++----- src/mod_pubsub/nodetree_dag.erl | 398 ++++++++++++++++++++++-------- src/mod_pubsub/nodetree_tree.erl | 2 +- 3 files changed, 651 insertions(+), 156 deletions(-) diff --git a/src/mod_pubsub/node_dag.erl b/src/mod_pubsub/node_dag.erl index 29db20103..7f92cbc57 100644 --- a/src/mod_pubsub/node_dag.erl +++ b/src/mod_pubsub/node_dag.erl @@ -53,123 +53,412 @@ get_item/2, set_item/1, get_item_name/3, - node_to_path/1, - path_to_node/1]). + node_to_path/1, + path_to_node/1]). +-spec(init/3 :: + ( + Host :: string(), + ServerHost :: string(), + Opts :: [{Key::atom(), Value::term()}]) + -> 'ok' + ). + init(Host, ServerHost, Opts) -> node_flat:init(Host, ServerHost, Opts). + +-spec(terminate/2 :: + ( + Host :: string(), + ServerHost :: string()) + -> 'ok' + ). + terminate(Host, ServerHost) -> node_flat:terminate(Host, ServerHost). + +-spec(options/0 :: () -> [nodeOption()]). + options() -> - [{node_type, leaf} | node_flat:options()]. + [{'node_type', 'leaf'} | node_flat:options()]. + + +-spec(features/0 :: () -> [Feature::string()]). features() -> ["multi-collection" | node_flat:features()]. -create_node_permission(_Host, _ServerHost, _Node, _ParentNode, - _Owner, _Access) -> + +-spec(create_node_permission/6 :: + ( + Host :: hostPubsub(), + ServerHost :: string(), + NodeId :: nodeId(), + ParentNodeId :: nodeId(), + JID :: jidEntity(), + Access :: atom()) + -> {'result', 'true'} + ). + +create_node_permission(_Host, _ServerHost, _NodeId, _ParentNodeId, + _JID, _Access) -> {result, true}. -create_node(NodeId, Owner) -> - node_flat:create_node(NodeId, Owner). -delete_node(Removed) -> - node_flat:delete_node(Removed). +-spec(create_node/2 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity()) + -> {'result', {'default', 'broadcast'}} + ). -subscribe_node(NodeId, Sender, Subscriber, AccessModel, +create_node(NodeIdx, JID) -> + node_flat:create_node(NodeIdx, JID). + + +-spec(delete_node/1 :: + ( + Nodes :: [Node::pubsubNode()]) + -> {result, {'default', 'broadcast', + Reply :: [{Node :: pubsubNode(), + [{Owner :: bareUsr(), + Subscriptions :: [{Subscription :: subscription(), + SubId :: subId()}]}]}]}} + ). + +delete_node(Nodes) -> + node_flat:delete_node(Nodes). + + +-spec(subscribe_node/8 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity(), + Subscriber :: jidEntity(), + AccessModel :: accessModel(), + SendLast :: atom(), + PresenceSubscription :: boolean(), + RosterGroup :: boolean(), + Options :: [nodeOption()]) + -> {'result', {'default', + Subscription :: 'subscribed', + SubId :: subId()}} + | {'result', {'default', + Subscription :: 'subscribed', + SubId :: subId(), + SendLast ::' send_last'}} + | {'result', {'default', + Subscription :: 'pending', + SubId :: subId()}} + | {'error', _} %% TODO add all error cases + ). + +subscribe_node(NodeIdx, JID, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> - node_flat:subscribe_node(NodeId, Sender, Subscriber, AccessModel, + node_flat:subscribe_node(NodeIdx, JID, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). -unsubscribe_node(NodeId, Sender, Subscriber, SubId) -> - node_flat:unsubscribe_node(NodeId, Sender, Subscriber, SubId). -publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> +-spec(unsubscribe_node/4 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity(), + Subscriber :: jidEntity(), + SubId :: subId()) + -> {'result', 'default'} | {'error', _} %% TODO : add all error cases + ). + +unsubscribe_node(NodeIdx, JID, Subscriber, SubId) -> + node_flat:unsubscribe_node(NodeIdx, JID, Subscriber, SubId). + + +-spec(publish_item/6 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity(), + PublishModel :: atom(), %% TODO : make a generic publishMod() type + MaxItems :: 'unlimited' | integer(), + ItemId :: itemId(), + Payload :: payload()) + -> {'result', {'default', 'broadcast', ItemIds :: [] | [itemId()]}} + | {'error', _} + ). + +publish_item(NodeIdx, JID, PublishModel, MaxItems, ItemId, Payload) -> %% TODO: should look up the NodeTree plugin here. There's no %% access to the Host of the request at this level, so for now we %% just use nodetree_dag. - case nodetree_dag:get_node(NodeId) of + case nodetree_dag:get_node(NodeIdx) of #pubsub_node{options = Options} -> - case find_opt(node_type, Options) of - collection -> + case find_opt('node_type', Options) of + 'collection' -> {error, mod_pubsub:extended_error('not-allowed', "publish")}; _ -> - node_flat:publish_item(NodeId, Publisher, Model, + node_flat:publish_item(NodeIdx, JID, PublishModel, MaxItems, ItemId, Payload) end; - Err -> - Err + Error -> + Error end. -find_opt(_, []) -> false; -find_opt(Option, [{Option, Value} | _]) -> Value; -find_opt(Option, [_ | T]) -> find_opt(Option, T). -remove_extra_items(NodeId, MaxItems, ItemIds) -> - node_flat:remove_extra_items(NodeId, MaxItems, ItemIds). +-spec(find_opt/2 :: + ( + Key :: atom(), + Options :: [] | [Option::nodeOption()]) + -> Value :: 'false' | term() + ). -delete_item(NodeId, Publisher, PublishModel, ItemId) -> - node_flat:delete_item(NodeId, Publisher, PublishModel, ItemId). +find_opt(_, []) -> false; +find_opt(Key, [{Key, Value} | _]) -> Value; +find_opt(Key, [_ | Options]) -> find_opt(Key, Options). -purge_node(NodeId, Owner) -> - node_flat:purge_node(NodeId, Owner). -get_entity_affiliations(Host, Owner) -> - node_flat:get_entity_affiliations(Host, Owner). +-spec(remove_extra_items/3 :: + ( + NodeIdx :: nodeIdx(), + MaxItems :: 'unlimited' | integer(), + ItemsIds :: [ItemId::itemId()]) + -> {'result', + {OldItems :: [] | [ItemId::itemId()], + NewItems :: [] | [ItemId::itemId()]}} + ). -get_node_affiliations(NodeId) -> - node_flat:get_node_affiliations(NodeId). +remove_extra_items(NodeIdx, MaxItems, ItemIds) -> + node_flat:remove_extra_items(NodeIdx, MaxItems, ItemIds). -get_affiliation(NodeId, Owner) -> - node_flat:get_affiliation(NodeId, Owner). -set_affiliation(NodeId, Owner, Affiliation) -> - node_flat:set_affiliation(NodeId, Owner, Affiliation). +-spec(delete_item/4 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity(), + PublishModel :: atom(), + ItemId :: itemId()) + -> {'result', {'default', 'broadcast'}} | {'error', _} + ). -get_entity_subscriptions(Host, Owner) -> - node_flat:get_entity_subscriptions(Host, Owner). +delete_item(NodeIdx, JID, PublishModel, ItemId) -> + node_flat:delete_item(NodeIdx, JID, PublishModel, ItemId). -get_node_subscriptions(NodeId) -> - node_flat:get_node_subscriptions(NodeId). -get_subscriptions(NodeId, Owner) -> - node_flat:get_subscriptions(NodeId, Owner). +-spec(purge_node/2 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity()) + -> {'result', {'default', 'broadcast'}} | {'error', 'forbidden'} + ). -set_subscriptions(NodeId, Owner, Subscription, SubId) -> - node_flat:set_subscriptions(NodeId, Owner, Subscription, SubId). +purge_node(NodeIdx, JID) -> + node_flat:purge_node(NodeIdx, JID). -get_pending_nodes(Host, Owner) -> - node_flat:get_pending_nodes(Host, Owner). -get_states(NodeId) -> - node_flat:get_states(NodeId). +-spec(get_entity_affiliations/2 :: + ( + Host :: binary(), + JID :: jidEntity()) + -> {'result', Reply :: [] | [{Node::pubsubNode(), Affiliation::affiliation()}]} + ). -get_state(NodeId, JID) -> - node_flat:get_state(NodeId, JID). +get_entity_affiliations(Host, JID) -> + node_flat:get_entity_affiliations(Host, JID). + + +-spec(get_node_affiliations/1 :: + ( + NodeIdx :: nodeIdx()) + -> {'result', [] | [{Entity::fullUsr(), Affiliation::affiliation()}]} + ). + +get_node_affiliations(NodeIdx) -> + node_flat:get_node_affiliations(NodeIdx). + + +-spec(get_affiliation/2 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity()) + -> {'result', Affiliation::affiliation()} + ). + +get_affiliation(NodeIdx, JID) -> + node_flat:get_affiliation(NodeIdx, JID). + + +-spec(set_affiliation/3 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity(), + Affiliation :: affiliation()) + -> 'ok' | {error, 'internal-server-error'} + ). + +set_affiliation(NodeIdx, JID, Affiliation) -> + node_flat:set_affiliation(NodeIdx, JID, Affiliation). + + +-spec(get_entity_subscriptions/2 :: + ( + Host :: hostPubsub(), + JID :: jidEntity()) + -> {'result', [] + | [{Node :: pubsubNode(), + Subscription :: subscription(), + SubId :: subId(), + Entity :: fullUsr()}]} + ). + +get_entity_subscriptions(Host, JID) -> + node_flat:get_entity_subscriptions(Host, JID). + + +-spec(get_node_subscriptions/1 :: + ( + NodeIdx :: nodeIdx()) + -> {'result', [] + | [{Entity::fullUsr(), 'none'}] + | [{Entity::fullUsr(), Subscription::subscription(), SubId::subId()}]} + ). + +get_node_subscriptions(NodeIdx) -> + node_flat:get_node_subscriptions(NodeIdx). + + +-spec(get_subscriptions/2 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity()) + -> {'result', Subscriptions :: [] | [{Subscription::subscription(), SubId::subId()}]} + ). + +get_subscriptions(NodeIdx, JID) -> + node_flat:get_subscriptions(NodeIdx, JID). + + +-spec(set_subscriptions/4 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity(), + Subscription :: subscription(), + SubId :: subId()) + -> 'ok' + | {Subscription::subscription(), SubId::subId()} + | {error, _} + ). + +set_subscriptions(NodeIdx, JID, Subscription, SubId) -> + node_flat:set_subscriptions(NodeIdx, JID, Subscription, SubId). + + +-spec(get_pending_nodes/2 :: + ( + Host :: hostPubsub(), + JID :: jidEntity()) + -> 'false' | {'value', NodeId::nodeId()} + ). + +get_pending_nodes(Host, JID) -> + node_flat:get_pending_nodes(Host, JID). + + +-spec(get_states/1 :: + ( + NodeIdx :: nodeIdx()) + -> {'result', States :: [] | [State::pubsubState()]} + ). + +get_states(NodeIdx) -> + node_flat:get_states(NodeIdx). + + +-spec(get_state/2 :: + ( + NodeIdx :: nodeIdx(), + Entity :: fullUsr()) + -> State::pubsubState() + ). + +get_state(NodeIdx, Entity) -> + node_flat:get_state(NodeIdx, Entity). + + +-spec(set_state/1 :: + ( + State :: pubsubState()) + -> 'ok' | {error, 'internal-server-error'} + ). set_state(State) -> node_flat:set_state(State). -get_items(NodeId, From) -> - node_flat:get_items(NodeId, From). -get_items(NodeId, JID, AccessModel, PresenceSubscription, +-spec(get_items/2 :: + ( + NodeIdx :: nodeIdx(), + Entity :: fullUsr()) + -> {'result', Items :: [] | [Item::pubsubItem()]} + ). + +get_items(NodeIdx, Entity) -> + node_flat:get_items(NodeIdx, Entity). + + +-spec(get_items/6 :: + ( + NodeIdx :: nodeIdx(), + JID :: jidEntity(), + AccessModel :: accessModel(), + PresenceSubscription :: boolean(), + RosterGroup :: boolean(), + SubId :: subId()) + -> {'result', Items :: [] | [Item::pubsubItem()]} + | {'error', _} + ). + +get_items(NodeIdx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_flat:get_items(NodeId, JID, AccessModel, PresenceSubscription, + node_flat:get_items(NodeIdx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(NodeId, ItemId) -> - node_flat:get_item(NodeId, ItemId). -get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, +-spec(get_item/2 :: + ( + NodeIdx :: nodeIdx(), + ItemId :: itemId()) + -> {'result', Item::pubsubItem()} | {'error', 'item-not-found'} + ). + +get_item(NodeIdx, ItemId) -> + node_flat:get_item(NodeIdx, ItemId). + + +-spec(get_item/7 :: + ( + NodeIdx :: nodeIdx(), + ItemId :: itemId(), + JID :: jidEntity(), + AccessModel :: accessModel(), + PresenceSubscription :: boolean(), + RosterGroup :: boolean(), + SubId :: subId()) + -> {'result', Item::pubsubItem()} | {'error', 'item-not-found'} + ). + +get_item(NodeIdx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_flat:get_item(NodeId, ItemId, JID, AccessModel, + node_flat:get_item(NodeIdx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + +-spec(set_item/1 :: + ( + Item :: pubsubItem()) + -> 'ok' | {error, 'internal-server-error'} + ). + set_item(Item) -> node_flat:set_item(Item). diff --git a/src/mod_pubsub/nodetree_dag.erl b/src/mod_pubsub/nodetree_dag.erl index 722aaee51..890934557 100644 --- a/src/mod_pubsub/nodetree_dag.erl +++ b/src/mod_pubsub/nodetree_dag.erl @@ -51,128 +51,265 @@ %%==================================================================== %% API %%==================================================================== +-spec(init/3 :: + ( + Host :: string(), + ServerHost :: string(), + Opts :: [{Key::atom(), Value::term()}]) + -> 'ok' + ). + init(Host, ServerHost, Opts) -> nodetree_tree:init(Host, ServerHost, Opts). + +-spec(terminate/2 :: + ( + Host :: string(), + ServerHost :: string()) + -> 'ok' + ). + terminate(Host, ServerHost) -> nodetree_tree:terminate(Host, ServerHost). -create_node(Key, Node, Type, Owner, Options, Parents) -> - OwnerJID = jlib:short_prepd_bare_jid(Owner), - case find_node(Key, Node) of + +-spec(create_node/6 :: + ( + Host :: hostPubsub(), + NodeId :: nodeId(), + Type :: nodeType(), + JID :: jidEntity(), + Options :: [nodeOption()], + ParentNodeIds :: [] | [nodeId()]) + -> {'ok', NodeIdx::nodeIdx()} | {'error', _} + ). + +create_node(Host, NodeId, Type, #jid{node = U, domain = S} = _JID, Options, ParentNodeIds) -> + case find_node(Host, NodeId) of false -> - Nidx = pubsub_index:new(node), - N = #pubsub_node{id = oid(Key, Node), - idx = Nidx, + NodeIdx = pubsub_index:new(node), + Node = #pubsub_node{id = {Host, NodeId}, + idx = NodeIdx, type = Type, - parents = Parents, - owners = [OwnerJID], + parents = ParentNodeIds, + owners = [_Owner = {U,S,undefined}], options = Options}, - case set_node(N) of - ok -> {ok, Nidx}; + case set_node(Node) of + ok -> {ok, NodeIdx}; Other -> Other end; _ -> {error, exmpp_stanza:error(?NS_JABBER_CLIENT, 'conflict')} end. -set_node(#pubsub_node{id = {Key, _}, + +-spec(set_node/1 :: + ( + Node :: pubsubNode()) + -> 'ok' | {'error', _} + ). + +set_node(#pubsub_node{id = {Host, _}, owners = Owners, options = Options} = Node) -> - Parents = find_opt(collection, ?DEFAULT_PARENTS, Options), - case validate_parentage(Key, Owners, Parents) of + ParentNodeIds = find_opt('collection', ?DEFAULT_PARENTS, Options), + case validate_parentage(Host, Owners, ParentNodeIds) of true -> %% Update parents whenever the config changes. - mnesia:write(Node#pubsub_node{parents = Parents}); + mnesia:write(Node#pubsub_node{parents = ParentNodeIds}); Other -> Other end. -delete_node(Key, Node) -> - case find_node(Key, Node) of + +-spec(delete_node/2 :: + ( + Host :: hostPubsub(), + NodeId :: nodeId()) + -> [pubsubNode()] | {'error', _} + ). + +delete_node(Host, NodeId) -> + case find_node(Host, NodeId) of false -> {error, exmpp_stanza:error(?NS_JABBER_CLIENT, 'item-not-found')}; - Record -> + ParentNode -> %% Find all of N's children, update their configs to %% remove N from the collection setting. - lists:foreach(fun (#pubsub_node{options = Opts} = Child) -> - NewOpts = remove_config_parent(Node, Opts), - Parents = find_opt(collection, ?DEFAULT_PARENTS, NewOpts), + lists:foreach(fun (#pubsub_node{options = Options} = Node) -> + NewOptions = remove_config_parent(NodeId, Options), + Parents = find_opt('collection', ?DEFAULT_PARENTS, NewOptions), ok = mnesia:write(pubsub_node, - Child#pubsub_node{ + Node#pubsub_node{ parents = Parents, - options = NewOpts}, + options = NewOptions}, write) - end, get_subnodes(Key, Node)), + end, get_subnodes(Host, NodeId)), %% Remove and return the requested node. - pubsub_index:free(node, Record#pubsub_node.idx), - mnesia:delete_object(pubsub_node, Record, write), - [Record] + pubsub_index:free(node, ParentNode#pubsub_node.idx), + mnesia:delete_object(pubsub_node, ParentNode, write), + [ParentNode] end. -options() -> - nodetree_tree:options(). -get_node(Host, Node, _From) -> - get_node(Host, Node). +-spec(options/0 :: () -> Options::[Option::nodeOption()]). + %Options::[{'virtual_tree', 'false'}]) -get_node(Host, Node) -> - case find_node(Host, Node) of +options() -> nodetree_tree:options(). + + +-spec(get_node/3 :: + ( + Host :: hostPubsub(), + NodeId :: nodeId(), + JID :: jidEntity()) + -> pubsubNode() | {'error', _} + ). + +get_node(Host, NodeId, _JID) -> + get_node(Host, NodeId). + + +-spec(get_node/2 :: + ( + Host :: hostPubsub(), + NodeId :: nodeId()) + -> pubsubNode() | {'error', _} + ). + +get_node(Host, NodeId) -> + case find_node(Host, NodeId) of false -> {error, exmpp_stanza:error(?NS_JABBER_CLIENT, 'item-not-found')}; - Record -> Record + Node -> Node end. -get_node(Node) -> - nodetree_tree:get_node(Node). -get_nodes(Key, From) -> - nodetree_tree:get_nodes(Key, From). +-spec(get_node/1 :: + ( + NodeIdx :: nodeIdx()) + -> pubsubNode() | {'error', 'item-not-found'} + ). -get_nodes(Key) -> - nodetree_tree:get_nodes(Key). +get_node(NodeIdx) -> + nodetree_tree:get_node(NodeIdx). -get_parentnodes(Host, Node, _From) -> - case find_node(Host, Node) of + +-spec(get_nodes/2 :: + ( + Host :: hostPubsub(), + JID :: jidEntity()) + -> Nodes :: [] | [Node::pubsubNode()] + ). + +get_nodes(Host, JID) -> + nodetree_tree:get_nodes(Host, JID). + + +-spec(get_nodes/1 :: + ( + Host :: hostPubsub()) + -> Nodes :: [] | [Node::pubsubNode()] + ). + +get_nodes(Host) -> + nodetree_tree:get_nodes(Host). + + +-spec(get_parentnodes/3 :: + ( + Host :: hostPubsub(), + NodeId :: nodeId(), + JID :: jidEntity()) + -> ParentNodes :: [] | [ParentNode::pubsubNode()] + ). + +get_parentnodes(Host, NodeId, _JID) -> + case find_node(Host, NodeId) of false -> {error, exmpp_stanza:error(?NS_JABBER_CLIENT, 'item-not-found')}; - #pubsub_node{parents = Parents} -> - Q = qlc:q([N || #pubsub_node{id = {NHost, NNode}} = N <- mnesia:table(pubsub_node), - Parent <- Parents, + #pubsub_node{parents = ParentNodeIds} -> + Q = qlc:q([Node || #pubsub_node{id = {NHost, NNodeId}} = Node + <- mnesia:table(pubsub_node), + ParentNodeId <- ParentNodeIds, Host == NHost, - Parent == NNode]), + ParentNodeId == NNodeId]), qlc:e(Q) end. -get_parentnodes_tree(Host, Node, _From) -> + +-spec(get_parentnodes_tree/3 :: + ( + Host :: hostPubsub(), + NodeId :: nodeId(), + JID :: jidEntity()) + -> [] | [{Depth::integer(), Nodes :: [] | [Node::pubsubNode()]}] + ). + +get_parentnodes_tree(Host, NodeId, _JID) -> Pred = fun (Name, #pubsub_node{id = {_, NodeName}}) -> Name == NodeName end, Tr = fun (#pubsub_node{parents = Parents}) -> Parents end, - traversal_helper(Pred, Tr, Host, [Node]). + traversal_helper(Pred, Tr, Host, [NodeId]). -get_subnodes(Host, Node, _From) -> + +-spec(get_subnodes/3 :: + ( + ParentNodeHost :: hostPubsub(), + ParentNodeId :: nodeId(), + JID :: jidEntity()) + -> [] | [Node::pubsubNode()] | {'error', _} + ). + +get_subnodes(Host, Node, _JID) -> get_subnodes(Host, Node). -get_subnodes(Host, <<>>) -> - get_subnodes_helper(Host, <<>>); -get_subnodes(Host, Node) -> - case find_node(Host, Node) of + +-spec(get_subnodes/2 :: + ( + ParentNodeHost :: hostPubsub(), + ParentNodeId :: nodeId()) + -> [] | [Node::pubsubNode()] | {'error', _} + ). + +get_subnodes(ParentNodeHost, <<>>) -> + get_subnodes_helper(ParentNodeHost, <<>>); +get_subnodes(ParentNodeHost, ParentNodeId) -> + case find_node(ParentNodeHost, ParentNodeId) of false -> {error, exmpp_stanza:error(?NS_JABBER_CLIENT, 'item-not-found')}; - _ -> get_subnodes_helper(Host, Node) + _ -> get_subnodes_helper(ParentNodeHost, ParentNodeId) end. -get_subnodes_helper(Host, Node) -> - Q = qlc:q([Record || #pubsub_node{id = {NHost, _}, - parents = Parents} = Record <- mnesia:table(pubsub_node), - Host == NHost, - lists:member(Node, Parents)]), + +-spec(get_subnodes_helper/2 :: + ( + ParentNodeHost :: hostPubsub(), + ParentNodeId :: nodeId()) + -> SubNodes :: [] | [Node::pubsubNode()] + ). + +get_subnodes_helper(ParentNodeHost, ParentNodeId) -> + Q = qlc:q([Node || #pubsub_node{id = {Host, _}, + parents = ParentNodeIds} = Node <- mnesia:table(pubsub_node), + ParentNodeHost == Host, + lists:member(ParentNodeId, ParentNodeIds)]), qlc:e(Q). -get_subnodes_tree(Host, Node, From) -> - Pred = fun (N, #pubsub_node{parents = Parents}) -> - lists:member(N, Parents) + +-spec(get_subnodes_tree/3 :: + ( + ParentNodeHost :: hostPubsub(), + ParentNodeId :: nodeId(), + JID :: jidEntity()) + -> [] | [{Depth::integer(), Nodes :: [] | [Node::pubsubNode()]}] + ). + +get_subnodes_tree(Host, ParentNodeId, JID) -> + Pred = fun(NodeId, #pubsub_node{parents = ParentNodeIds}) -> + lists:member(NodeId, ParentNodeIds) end, - Tr = fun (#pubsub_node{id = {_, N}}) -> [N] end, - traversal_helper(Pred, Tr, 1, Host, [Node], - [{0, [get_node(Host, Node, From)]}]). + Tr = fun (#pubsub_node{id = {_, NodeId}}) -> [NodeId] end, + traversal_helper(Pred, Tr, 1, Host, [ParentNodeId], + [{0, [get_node(Host, ParentNodeId, JID)]}]). %%==================================================================== %% Internal functions @@ -181,8 +318,15 @@ oid(Key, Name) -> {Key, Name}. %% Key = jlib:jid() | host() %% Node = string() -find_node(Key, Node) -> - case mnesia:read(pubsub_node, oid(Key, Node), read) of +-spec(find_node/2 :: + ( + Host :: hostPubsub(), + NodeId :: nodeId()) + -> pubsubNode() | 'false' + ). + +find_node(Host, NodeId) -> + case mnesia:read({pubsub_node, {Host, NodeId}}) of [] -> false; [Node] -> Node end. @@ -190,53 +334,109 @@ find_node(Key, Node) -> %% Key = jlib:jid() | host() %% Default = term() %% Options = [{Key = atom(), Value = term()}] +-spec(find_opt/3 :: + ( + Key :: atom(), + Default :: term(), + Options :: [Option::nodeOption()]) + -> Value::term() + ). + find_opt(Key, Default, Options) -> case lists:keysearch(Key, 1, Options) of - {value, {Key, Val}} -> Val; + {value, {Key, Value}} -> Value; _ -> Default end. -traversal_helper(Pred, Tr, Host, Nodes) -> - traversal_helper(Pred, Tr, 0, Host, Nodes, []). + +-spec(traversal_helper/4 :: + ( + Pred :: fun(), + Tr :: fun(), + ParentNodeHost :: hostPubsub(), + ParentNodeIds :: [] | [ParentNodeId::nodeId()]) + -> [] | [{Depth::integer(), Nodes :: [] | [Node::pubsubNode()]}] + ). + +traversal_helper(Pred, Tr, ParentNodeHost, ParentNodeIds) -> + traversal_helper(Pred, Tr, 0, ParentNodeHost, ParentNodeIds, []). + + +-spec(traversal_helper/6 :: + ( + Pred :: fun(), + Tr :: fun(), + Depth :: integer(), + ParentNodeHost :: hostPubsub(), + ParentNodeIds :: [] | [ParentNodeId::nodeId()], + Acc :: [] | [{Depth::integer(), Nodes :: [] | [Node::pubsubNode()]}]) + -> [] | [{Depth::integer(), Nodes :: [] | [Node::pubsubNode()]}] + ). traversal_helper(_Pred, _Tr, _Depth, _Host, [], Acc) -> Acc; -traversal_helper(Pred, Tr, Depth, Host, Nodes, Acc) -> - Q = qlc:q([Record || #pubsub_node{id = {NHost, _}} = Record <- mnesia:table(pubsub_node), - Node <- Nodes, - Host == NHost, - Pred(Node, Node)]), +traversal_helper(Pred, Tr, Depth, ParentNodeHost, ParentNodeIds, Acc) -> + Q = qlc:q([Node || #pubsub_node{id = {Host, _}} = Node <- mnesia:table(pubsub_node), + ParentNodeId <- ParentNodeIds, + ParentNodeHost == Host, + Pred(ParentNodeId, Node)]), Nodes = qlc:e(Q), - Names = lists:flatmap(Tr, Nodes), - traversal_helper(Pred, Tr, Depth + 1, Host, Names, [{Depth, Nodes} | Acc]). + Ids = lists:flatmap(Tr, Nodes), + traversal_helper(Pred, Tr, Depth + 1, ParentNodeHost, Ids, [{Depth, Nodes} | Acc]). -remove_config_parent(Node, Options) -> - remove_config_parent(Node, Options, []). -remove_config_parent(_Node, [], Acc) -> +-spec(remove_config_parent/2 :: + ( + NodeId :: nodeId(), + Options :: [Option::nodeOption()]) + -> [Option::nodeOption()] + ). + +remove_config_parent(NodeId, Options) -> + remove_config_parent(NodeId, Options, []). + + +-spec(remove_config_parent/3 :: + ( + NodeId :: nodeId(), + Options :: [] | [Option::nodeOption()], + Acc :: [Option::nodeOption()]) + -> [Option::nodeOption()] + ). + +remove_config_parent(_NodeId, [], Acc) -> lists:reverse(Acc); -remove_config_parent(Node, [{collection, Parents} | T], Acc) -> - remove_config_parent(Node, T, - [{collection, lists:delete(Node, Parents)} | Acc]); -remove_config_parent(Node, [H | T], Acc) -> - remove_config_parent(Node, T, [H | Acc]). +remove_config_parent(NodeId, [{'collection', ParentNodeIds} | Options], Acc) -> + remove_config_parent(NodeId, Options, + [{'collection', lists:delete(NodeId, ParentNodeIds)} | Acc]); +remove_config_parent(NodeId, [Option | Options], Acc) -> + remove_config_parent(NodeId, Options, [Option | Acc]). -validate_parentage(_Key, _Owners, []) -> + +-spec(validate_parentage/3 :: + ( + Host :: hostPubsub(), + Owners :: [Owner::bareUsr()], + ParentNodeIds :: [] | [ParentNodeId::nodeId()] | [ParentNodeId :: nodeId() | []]) + -> 'true' | {'error', _} + ). + +validate_parentage(_Host, _Owners, []) -> true; -validate_parentage(Key, Owners, [[] | T]) -> - validate_parentage(Key, Owners, T); -validate_parentage(Key, Owners, [<<>> | T]) -> - validate_parentage(Key, Owners, T); -validate_parentage(Key, Owners, [ParentId | T]) -> - case find_node(Key, ParentId) of +validate_parentage(Host, Owners, [[] | ParentNodeIds]) -> + validate_parentage(Host, Owners, ParentNodeIds); +validate_parentage(Host, Owners, [<<>> | ParentNodeIds]) -> + validate_parentage(Host, Owners, ParentNodeIds); +validate_parentage(Host, Owners, [ParentNodeId | ParentNodeIds]) -> + case find_node(Host, ParentNodeId) of false -> {error, exmpp_stanza:error(?NS_JABBER_CLIENT, 'item_not_found')}; - #pubsub_node{owners = POwners, options = POptions} -> - NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions), - MutualOwners = [O || O <- Owners, PO <- POwners, - O == PO], + #pubsub_node{owners = ParentNodeOwners, options = Options} -> + NodeType = find_opt('node_type', ?DEFAULT_NODETYPE, Options), + MutualOwners = [Owner || Owner <- Owners, ParentNodeOwner <- ParentNodeOwners, + Owner == ParentNodeOwner], case {MutualOwners, NodeType} of {[], _} -> {error, exmpp_stanza:error(?NS_JABBER_CLIENT, 'forbidden')}; - {_, collection} -> validate_parentage(Key, Owners, T); + {_, 'collection'} -> validate_parentage(Host, Owners, ParentNodeIds); {_, _} -> {error, exmpp_stanza:error(?NS_JABBER_CLIENT, 'not-allowed')} end end. @@ -244,6 +444,12 @@ validate_parentage(Key, Owners, [ParentId | T]) -> %% @spec (Host) -> jid() %% Host = host() %% @doc

Generate pubsub service JID.

+-spec(service_jid/1 :: + ( + Host :: hostPubsub()) + -> ServiceJID :: jidContact() | jidComponent() %% should only return jidContact() + ). + service_jid(Host) -> case Host of {U,S,_} -> exmpp_jid:make(U, S); diff --git a/src/mod_pubsub/nodetree_tree.erl b/src/mod_pubsub/nodetree_tree.erl index 5d0f1142b..263b89d66 100644 --- a/src/mod_pubsub/nodetree_tree.erl +++ b/src/mod_pubsub/nodetree_tree.erl @@ -157,7 +157,7 @@ get_node(Host, NodeId) -> -spec(get_node/1 :: ( NodeIdx :: nodeIdx()) - -> pubsubNode() | {error, 'item-not-found'} | any() + -> pubsubNode() | {'error', 'item-not-found'} ). get_node(NodeIdx) ->