fix dializer warnings (thanks to Karim Gemayel)

This commit is contained in:
Christophe Romain 2010-10-04 17:41:59 +02:00
parent 349c44fcc0
commit ee861e650d
3 changed files with 651 additions and 156 deletions

View File

@ -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).

View File

@ -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 <p>Generate pubsub service JID.</p>
-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);

View File

@ -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) ->