From f6e4d95c6f510dc4e539e8c7d9908b0a6181ff78 Mon Sep 17 00:00:00 2001
From: Christophe Romain
The modules are initialized in alphetical order and the list is checked %% and sorted to ensure that each module is initialized only once.
-%%See {@link node_hometree:init/1} for an example implementation.
+%%See {@link node_flat:init/1} for an example implementation.
init_plugins(Host, ServerHost, Opts) -> TreePlugin = list_to_atom(?TREE_PREFIX ++ gen_mod:get_opt(nodetree, Opts, ?STDTREE)), @@ -1912,7 +1912,7 @@ delete_node(Host, Node, Owner) -> %% Node = pubsubNode() %% From = jid() %% JID = jid() -%% @see node_hometree:subscribe_node/5 +%% @see node_flat:subscribe_node/5 %% @docAccepts or rejects subcription requests on a PubSub node.
%%There are several reasons why the subscription request might fail:
%%The modules are initialized in alphetical order and the list is checked %% and sorted to ensure that each module is initialized only once.
-%%See {@link node_hometree:init/1} for an example implementation.
+%%See {@link node_flat:init/1} for an example implementation.
init_plugins(Host, ServerHost, Opts) -> TreePlugin = list_to_atom(?TREE_PREFIX ++ gen_mod:get_opt(nodetree, Opts, ?STDTREE)), @@ -1727,7 +1727,7 @@ delete_node(Host, Node, Owner) -> %% Node = pubsubNode() %% From = jid() %% JID = jid() -%% @see node_hometree:subscribe_node/5 +%% @see node_flat:subscribe_node/5 %% @docAccepts or rejects subcription requests on a PubSub node.
%%There are several reasons why the subscription request might fail:
%%It is used as a default for all unknown PubSub node type. It can serve +%%% as a developer basis and reference to build its own custom pubsub node +%%% types.
+%%%PubSub plugin nodes are using the {@link gen_node} behaviour.
+%%%The API isn't stabilized yet. The pubsub plugin +%%% development is still a work in progress. However, the system is already +%%% useable and useful as is. Please, send us comments, feedback and +%%% improvements.
+ -module(node_flat). -author('christophe.romain@process-one.net'). @@ -64,13 +80,60 @@ path_to_node/1 ]). +%% ================ +%% API definition +%% ================ -init(Host, ServerHost, Opts) -> - node_hometree:init(Host, ServerHost, Opts). +%% @spec (Host, ServerHost, Opts) -> any() +%% Host = mod_pubsub:host() +%% ServerHost = mod_pubsub:host() +%% Opts = list() +%% @docCalled during pubsub modules initialisation. Any pubsub plugin must +%% implement this function. It can return anything.
+%%This function is mainly used to trigger the setup task necessary for the +%% plugin. It can be used for example by the developer to create the specific +%% module database schema if it does not exists yet.
+init(_Host, _ServerHost, _Opts) -> + pubsub_subscription:init(), + mnesia:create_table(pubsub_state, + [{disc_copies, [node()]}, + {attributes, record_info(fields, pubsub_state)}]), + mnesia:create_table(pubsub_item, + [{disc_only_copies, [node()]}, + {attributes, record_info(fields, pubsub_item)}]), + ItemsFields = record_info(fields, pubsub_item), + case mnesia:table_info(pubsub_item, attributes) of + ItemsFields -> ok; + _ -> + mnesia:transform_table(pubsub_item, ignore, ItemsFields) + end, + ok. -terminate(Host, ServerHost) -> - node_hometree:terminate(Host, ServerHost). +%% @spec (Host, ServerHost) -> any() +%% Host = mod_pubsub:host() +%% ServerHost = host() +%% @docCalled during pubsub modules termination. Any pubsub plugin must +%% implement this function. It can return anything.
+terminate(_Host, _ServerHost) -> + ok. +%% @spec () -> [Option] +%% Option = mod_pubsub:nodeOption() +%% @doc Returns the default pubsub node options. +%%Example of function return value:
+%% ``` +%% [{deliver_payloads, true}, +%% {notify_config, false}, +%% {notify_delete, false}, +%% {notify_retract, true}, +%% {persist_items, true}, +%% {max_items, 10}, +%% {subscribe, true}, +%% {access_model, open}, +%% {publish_model, publishers}, +%% {max_payload_size, 100000}, +%% {send_last_published_item, never}, +%% {presence_based_delivery, false}]''' options() -> [{deliver_payloads, true}, {notify_config, false}, @@ -90,10 +153,33 @@ options() -> {deliver_notifications, true}, {presence_based_delivery, false}]. +%% @spec () -> [] +%% @doc Returns the node features features() -> - node_hometree:features(). + ["create-nodes", + "auto-create", + "access-authorize", + "delete-nodes", + "delete-items", + "get-pending", + "instant-nodes", + "manage-subscriptions", + "modify-affiliations", + "multi-subscribe", + "outcast-affiliation", + "persistent-items", + "publish", + "purge-nodes", + "retract-items", + "retrieve-affiliations", + "retrieve-items", + "retrieve-subscriptions", + "subscribe", + "subscription-notifications", + "subscription-options" + ]. -%% use same code as node_hometree, but do not limite node to +%% use same code as node_flat, but do not limite node to %% the home/localhost/user/... hierarchy %% any node is allowed create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> @@ -107,93 +193,805 @@ create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> end, {result, Allowed}. +%% @spec (NodeId, Owner) -> +%% {result, Result} | exit +%% NodeId = mod_pubsub:pubsubNodeId() +%% Owner = mod_pubsub:jid() +%% @doc create_node(NodeId, Owner) -> - node_hometree:create_node(NodeId, Owner). + OwnerKey = jlib:short_prepd_bare_jid(Owner), + set_state(#pubsub_state{stateid = {OwnerKey, NodeId}, affiliation = owner}), + {result, {default, broadcast}}. +%% @spec (Removed) -> ok +%% Removed = [mod_pubsub:pubsubNode()] +%% @docpurge items of deleted nodes after effective deletion.
delete_node(Removed) -> - node_hometree:delete_node(Removed). + Tr = fun(#pubsub_state{stateid = {J, _}, subscriptions = Ss}) -> + lists:map(fun(S) -> + {J, S} + end, Ss) + end, + Reply = lists:map( + fun(#pubsub_node{id = NodeId} = PubsubNode) -> + {result, States} = get_states(NodeId), + lists:foreach( + fun(#pubsub_state{stateid = {LJID, _}, items = Items}) -> + del_items(NodeId, Items), + del_state(NodeId, LJID) + end, States), + {PubsubNode, lists:flatmap(Tr, States)} + end, Removed), + {result, {default, broadcast, Reply}}. -subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> - node_hometree:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). +%% @spec (NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> +%% {error, Reason} | {result, Result} +%% @docAccepts or rejects subcription requests on a PubSub node.
+%%The mechanism works as follow: +%%
The selected behaviour depends on the return parameter: +%%
In the default plugin module, the record is unchanged.
+subscribe_node(NodeId, Sender, {U,S,R} = Subscriber, AccessModel, + SendLast, PresenceSubscription, RosterGroup, Options) -> + SubKey = {U, S, R}, + GenKey = {U, S, undefined}, + Authorized = (jlib:short_prepd_bare_jid(Sender) == GenKey), + GenState = get_state(NodeId, GenKey), + SubState = case SubKey of + GenKey -> GenState; + _ -> get_state(NodeId, SubKey) + end, + Affiliation = GenState#pubsub_state.affiliation, + Subscriptions = SubState#pubsub_state.subscriptions, + Whitelisted = lists:member(Affiliation, [member, publisher, owner]), + PendingSubscription = lists:any(fun({pending, _}) -> true; + (_) -> false + end, Subscriptions), + if + not Authorized -> + %% JIDs do not match + {error, ?ERR_EXTENDED('bad-request', "invalid-jid")}; + Affiliation == outcast -> + %% Requesting entity is blocked + {error, 'forbidden'}; + PendingSubscription -> + %% Requesting entity has pending subscription + {error, ?ERR_EXTENDED('not-authorized', "pending-subscription")}; + (AccessModel == presence) and (not PresenceSubscription) -> + %% Entity is not authorized to create a subscription (presence subscription required) + {error, ?ERR_EXTENDED('not-authorized', "presence-subscription-required")}; + (AccessModel == roster) and (not RosterGroup) -> + %% Entity is not authorized to create a subscription (not in roster group) + {error, ?ERR_EXTENDED('not-authorized', "not-in-roster-group")}; + (AccessModel == whitelist) and (not Whitelisted) -> + %% Node has whitelist access model and entity lacks required affiliation + {error, ?ERR_EXTENDED('not-allowed', "closed-node")}; + %%MustPay -> + %% % Payment is required for a subscription + %% {error, ?ERR_PAYMENT_REQUIRED}; + %%ForbiddenAnonymous -> + %% % Requesting entity is anonymous + %% {error, 'forbidden'}; + true -> + case pubsub_subscription:subscribe_node(Subscriber, NodeId, Options) of + {result, SubId} -> + NewSub = case AccessModel of + authorize -> pending; + _ -> subscribed + end, + set_state(SubState#pubsub_state{subscriptions = [{NewSub, SubId} | Subscriptions]}), + case {NewSub, SendLast} of + {subscribed, never} -> + {result, {default, subscribed, SubId}}; + {subscribed, _} -> + {result, {default, subscribed, SubId, send_last}}; + {_, _} -> + {result, {default, pending, SubId}} + end; + _ -> + {error, 'internal-server-error'} + end + end. -unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> - node_hometree:unsubscribe_node(NodeId, Sender, Subscriber, SubID). +%% @spec (NodeId, Sender, Subscriber, SubId) -> +%% {error, Reason} | {result, []} +%% NodeId = mod_pubsub:pubsubNodeId() +%% Sender = mod_pubsub:jid() +%% Subscriber = mod_pubsub:jid() +%% SubId = mod_pubsub:subid() +%% Reason = mod_pubsub:stanzaError() +%% @docUnsubscribe the Subscriber from the Node.
+unsubscribe_node(NodeId, Sender, {U, S, R} = Subscriber, SubId) -> + SubKey = {U, S, R}, + GenKey = {U, S, undefined}, + Authorized = (jlib:short_prepd_bare_jid(Sender) == GenKey), + GenState = get_state(NodeId, GenKey), + SubState = case SubKey of + GenKey -> GenState; + _ -> get_state(NodeId, SubKey) + end, + Subscriptions = lists:filter(fun({_Sub, _SubId}) -> true; + (_SubId) -> false + end, SubState#pubsub_state.subscriptions), + SubIdExists = case SubId of + [] -> false; + List when is_list(List) -> true; + _ -> false + end, + if + %% Requesting entity is prohibited from unsubscribing entity + not Authorized -> + {error, 'forbidden'}; + %% Entity did not specify SubId + %%SubId == "", ?? -> + %% {error, ?ERR_EXTENDED('bad-request', "subid-required")}; + %% Invalid subscription identifier + %%InvalidSubId -> + %% {error, ?ERR_EXTENDED('not-acceptable', "invalid-subid")}; + %% Requesting entity is not a subscriber + Subscriptions == [] -> + {error, ?ERR_EXTENDED('unexpected-request', "not-subscribed")}; + %% Subid supplied, so use that. + SubIdExists -> + Sub = first_in_list(fun({_, Id}) when Id == SubId -> true; + (_) -> false + end, Subscriptions), + case Sub of + {value, Subscribed} -> + delete_subscriptions(Subscriber, NodeId, [Subscribed], SubState), + {result, default}; + false -> + {error, ?ERR_EXTENDED('unexpected-request', "not-subscribed")} + end; + %% Asking to remove all subscriptions to the given node + SubId == all -> + delete_subscriptions(SubKey, NodeId, Subscriptions, SubState), + {result, default}; + %% No subid supplied, but there's only one matching + %% subscription, so use that. + length(Subscriptions) == 1 -> + delete_subscriptions(SubKey, NodeId, Subscriptions, SubState), + {result, default}; + true -> + {error, ?ERR_EXTENDED('bad-request', "subid-required")} + end. -publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> - node_hometree:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). +delete_subscriptions(SubKey, NodeId, Subscriptions, SubState) -> + NewSubs = lists:foldl(fun({Subscription, SubId}, Acc) -> + pubsub_subscription:delete_subscription(SubKey, NodeId, SubId), + Acc -- [{Subscription, SubId}] + end, SubState#pubsub_state.subscriptions, Subscriptions), + case {SubState#pubsub_state.affiliation, NewSubs} of + {none, []} -> + % Just a regular subscriber, and this is final item, so + % delete the state. + del_state(NodeId, SubKey); + _ -> + set_state(SubState#pubsub_state{subscriptions = NewSubs}) + end. + +%% @spec (NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload) -> +%% {true, PubsubItem} | {result, Reply} +%% NodeId = mod_pubsub:pubsubNodeId() +%% Publisher = mod_pubsub:jid() +%% PublishModel = atom() +%% MaxItems = integer() +%% ItemId = string() +%% Payload = term() +%% @docPublishes the item passed as parameter.
+%%The mechanism works as follow: +%%
The selected behaviour depends on the return parameter: +%%
In the default plugin module, the record is unchanged.
+publish_item(NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload) -> + SubKey = jlib:short_prepd_jid(Publisher), + GenKey = jlib:short_prepd_bare_jid(Publisher), + GenState = get_state(NodeId, GenKey), + SubState = case SubKey of + GenKey -> GenState; + _ -> get_state(NodeId, SubKey) + end, + Affiliation = GenState#pubsub_state.affiliation, + Subscribed = case PublishModel of + subscribers -> is_subscribed(SubState#pubsub_state.subscriptions); + _ -> undefined + end, + if + not ((PublishModel == open) + or ((PublishModel == publishers) + and ((Affiliation == owner) or (Affiliation == publisher))) + or (Subscribed == true)) -> + %% Entity does not have sufficient privileges to publish to node + {error, 'forbidden'}; + true -> + %% TODO: check creation, presence, roster + if MaxItems > 0 -> + Now = now(), + PubId = {Now, SubKey}, + Item = case get_item(NodeId, ItemId) of + {result, OldItem} -> + OldItem#pubsub_item{modification = PubId, + payload = Payload}; + _ -> + #pubsub_item{itemid = {ItemId, NodeId}, + creation = {Now, GenKey}, + modification = PubId, + payload = Payload} + end, + Items = [ItemId | GenState#pubsub_state.items--[ItemId]], + {result, {NI, OI}} = remove_extra_items(NodeId, MaxItems, Items), + set_item(Item), + set_state(GenState#pubsub_state{items = NI}), + {result, {default, broadcast, OI}}; + true -> + {result, {default, broadcast, []}} + end + end. + +%% @spec (NodeId, MaxItems, ItemIds) -> {NewItemIds,OldItemIds} +%% NodeId = mod_pubsub:pubsubNodeId() +%% MaxItems = integer() | unlimited +%% ItemIds = [ItemId::string()] +%% NewItemIds = [ItemId::string()] +%% @docThis function is used to remove extra items, most notably when the +%% maximum number of items has been reached.
+%%This function is used internally by the core PubSub module, as no +%% permission check is performed.
+%%In the default plugin module, the oldest items are removed, but other +%% rules can be used.
+%%If another PubSub plugin wants to delegate the item removal (and if the +%% plugin is using the default pubsub storage), it can implements this function like this: +%% ```remove_extra_items(NodeId, MaxItems, ItemIds) -> +%% node_default:remove_extra_items(NodeId, MaxItems, ItemIds).'''
+remove_extra_items(_NodeId, unlimited, ItemIds) -> + {result, {ItemIds, []}}; remove_extra_items(NodeId, MaxItems, ItemIds) -> - node_hometree:remove_extra_items(NodeId, MaxItems, ItemIds). + NewItems = lists:sublist(ItemIds, MaxItems), + OldItems = lists:nthtail(length(NewItems), ItemIds), + %% Remove extra items: + del_items(NodeId, OldItems), + %% Return the new items list: + {result, {NewItems, OldItems}}. +%% @spec (NodeId, Publisher, PublishModel, ItemId) -> +%% {error, Reason::stanzaError()} | +%% {result, []} +%% NodeId = mod_pubsub:pubsubNodeId() +%% Publisher = mod_pubsub:jid() +%% PublishModel = atom() +%% ItemId = string() +%% @docTriggers item deletion.
+%%Default plugin: The user performing the deletion must be the node owner +%% or a publisher, or PublishModel being open.
delete_item(NodeId, Publisher, PublishModel, ItemId) -> - node_hometree:delete_item(NodeId, Publisher, PublishModel, ItemId). + GenKey = jlib:short_prepd_bare_jid(Publisher), + GenState = get_state(NodeId, GenKey), + #pubsub_state{affiliation = Affiliation, items = Items} = GenState, + Allowed = (Affiliation == publisher) orelse (Affiliation == owner) + orelse (PublishModel == open) + orelse case get_item(NodeId, ItemId) of + {result, #pubsub_item{creation = {_, GenKey}}} -> true; + _ -> false + end, + if + not Allowed -> + %% Requesting entity does not have sufficient privileges + {error, 'forbidden'}; + true -> + case lists:member(ItemId, Items) of + true -> + del_item(NodeId, ItemId), + set_state(GenState#pubsub_state{items = lists:delete(ItemId, Items)}), + {result, {default, broadcast}}; + false -> + case Affiliation of + owner -> + %% Owner can delete other publishers items as well + {result, States} = get_states(NodeId), + lists:foldl( + fun(#pubsub_state{items = PI, affiliation = publisher} = S, Res) -> + case lists:member(ItemId, PI) of + true -> + del_item(NodeId, ItemId), + set_state(S#pubsub_state{items = lists:delete(ItemId, PI)}), + {result, {default, broadcast}}; + false -> + Res + end; + (_, Res) -> + Res + end, {error, 'item-not-found'}, States); + _ -> + %% Non-existent node or item + {error, 'item-not-found'} + end + end + end. +%% @spec (NodeId, Owner) -> +%% {error, Reason::stanzaError()} | +%% {result, {default, broadcast}} +%% NodeId = mod_pubsub:pubsubNodeId() +%% Owner = mod_pubsub:jid() purge_node(NodeId, Owner) -> - node_hometree:purge_node(NodeId, Owner). + GenKey = jlib:short_prepd_bare_jid(Owner), + GenState = get_state(NodeId, GenKey), + case GenState of + #pubsub_state{affiliation = owner} -> + {result, States} = get_states(NodeId), + lists:foreach( + fun(#pubsub_state{items = []}) -> + ok; + (#pubsub_state{items = Items} = S) -> + del_items(NodeId, Items), + set_state(S#pubsub_state{items = []}) + end, States), + {result, {default, broadcast}}; + _ -> + %% Entity is not owner + {error, 'forbidden'} + end. +%% @spec (Host, JID) -> [{Node,Affiliation}] +%% Host = host() +%% JID = mod_pubsub:jid() +%% @docReturn the current affiliations for the given user
+%%The default module reads affiliations in the main Mnesia +%% pubsub_state table. If a plugin stores its data in the same +%% table, it should return an empty list, as the affiliation will be read by +%% the default PubSub module. Otherwise, it should return its own affiliation, +%% that will be added to the affiliation stored in the main +%% pubsub_state table.
get_entity_affiliations(Host, Owner) -> - node_hometree:get_entity_affiliations(Host, Owner). + GenKey = jlib:short_prepd_bare_jid(Owner), + States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}), + NodeTree = case catch ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of + [{nodetree, N}] -> N; + _ -> nodetree_tree + end, + Reply = lists:foldl(fun(#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) -> + case NodeTree:get_node(N) of + #pubsub_node{nodeid = {Host, _}} = Node -> [{Node, A}|Acc]; + _ -> Acc + end + end, [], States), + {result, Reply}. get_node_affiliations(NodeId) -> - node_hometree:get_node_affiliations(NodeId). + {result, States} = get_states(NodeId), + Tr = fun(#pubsub_state{stateid = {J, _}, affiliation = A}) -> + {J, A} + end, + {result, lists:map(Tr, States)}. get_affiliation(NodeId, Owner) -> - node_hometree:get_affiliation(NodeId, Owner). + GenKey = jlib:short_prepd_bare_jid(Owner), + GenState = get_state(NodeId, GenKey), + {result, GenState#pubsub_state.affiliation}. -set_affiliation(NodeId, Owner, Affiliation) -> - node_hometree:set_affiliation(NodeId, Owner, Affiliation). +set_affiliation(NodeId, Owner, Affiliation) when ?IS_JID(Owner)-> + GenKey = jlib:short_prepd_bare_jid(Owner), + GenState = get_state(NodeId, GenKey), + case {Affiliation, GenState#pubsub_state.subscriptions} of + {none, none} -> + del_state(NodeId, GenKey); + _ -> + set_state(GenState#pubsub_state{affiliation = Affiliation}) + end. +%% @spec (Host, Owner) -> [{Node,Subscription}] +%% Host = host() +%% Owner = mod_pubsub:jid() +%% @docReturn the current subscriptions for the given user
+%%The default module reads subscriptions in the main Mnesia +%% pubsub_state table. If a plugin stores its data in the same +%% table, it should return an empty list, as the affiliation will be read by +%% the default PubSub module. Otherwise, it should return its own affiliation, +%% that will be added to the affiliation stored in the main +%% pubsub_state table.
get_entity_subscriptions(Host, Owner) -> - node_hometree:get_entity_subscriptions(Host, Owner). + {U, D, _} = SubKey = jlib:short_prepd_jid(Owner), + GenKey = jlib:short_prepd_bare_jid(Owner), + States = case SubKey of + GenKey -> mnesia:match_object( + #pubsub_state{stateid = {{U, D, '_'}, '_'}, _ = '_'}); + _ -> mnesia:match_object( + #pubsub_state{stateid = {GenKey, '_'}, _ = '_'}) + ++ mnesia:match_object( + #pubsub_state{stateid = {SubKey, '_'}, _ = '_'}) + end, + NodeTree = case catch ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of + [{nodetree, N}] -> N; + _ -> nodetree_tree + end, + Reply = lists:foldl(fun(#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) -> + case NodeTree:get_node(N) of + #pubsub_node{nodeid = {Host, _}} = Node -> + lists:foldl(fun({Sub, SubId}, Acc2) -> + [{Node, Sub, SubId, J} | Acc2]; + (S, Acc2) -> + [{Node, S, J} | Acc2] + end, Acc, Ss); + _ -> Acc + end + end, [], States), + {result, Reply}. get_node_subscriptions(NodeId) -> - node_hometree:get_node_subscriptions(NodeId). + {result, States} = get_states(NodeId), + Tr = fun(#pubsub_state{stateid = {J, _}, subscriptions = Subscriptions}) -> + %% TODO: get rid of cases to handle non-list subscriptions + case Subscriptions of + [_|_] -> + lists:foldl(fun({S, SubId}, Acc) -> + [{J, S, SubId} | Acc]; + (S, Acc) -> + [{J, S} | Acc] + end, [], Subscriptions); + [] -> + []; + _ -> + [{J, none}] + end + end, + {result, lists:flatmap(Tr, States)}. get_subscriptions(NodeId, Owner) -> - node_hometree:get_subscriptions(NodeId, Owner). + SubKey = jlib:short_prepd_jid(Owner), + SubState = get_state(NodeId, SubKey), + {result, SubState#pubsub_state.subscriptions}. set_subscriptions(NodeId, Owner, Subscription, SubId) -> - node_hometree:set_subscriptions(NodeId, Owner, Subscription, SubId). + SubKey = jlib:short_prepd_jid(Owner), + SubState = get_state(NodeId, SubKey), + case {SubId, SubState#pubsub_state.subscriptions} of + {_, []} -> + case Subscription of + none -> {error, ?ERR_EXTENDED('bad_request', "not-subscribed")}; + _ -> new_subscription(NodeId, Owner, Subscription, SubState) + end; + {"", [{_, SID}]} -> + case Subscription of + none -> unsub_with_subid(NodeId, SID, SubState); + _ -> replace_subscription({Subscription, SID}, SubState) + end; + {"", [_|_]} -> + {error, ?ERR_EXTENDED('bad_request', "subid-required")}; + _ -> + case Subscription of + none -> unsub_with_subid(NodeId, SubId, SubState); + _ -> replace_subscription({Subscription, SubId}, SubState) + end + end. +replace_subscription(NewSub, SubState) -> + NewSubs = replace_subscription(NewSub, + SubState#pubsub_state.subscriptions, []), + set_state(SubState#pubsub_state{subscriptions = NewSubs}). + +replace_subscription(_, [], Acc) -> + Acc; +replace_subscription({Sub, SubId}, [{_, SubID} | T], Acc) -> + replace_subscription({Sub, SubId}, T, [{Sub, SubID} | Acc]). + +new_subscription(NodeId, Owner, Subscription, SubState) -> + SubId = pubsub_subscription:add_subscription(Owner, NodeId, []), + Subscriptions = SubState#pubsub_state.subscriptions, + set_state(SubState#pubsub_state{subscriptions = [{Subscription, SubId} | Subscriptions]}), + {Subscription, SubId}. + +unsub_with_subid(NodeId, SubId, SubState) -> + pubsub_subscription:delete_subscription(SubState#pubsub_state.stateid, + NodeId, SubId), + NewSubs = lists:filter(fun ({_, SID}) -> SubId =/= SID end, + SubState#pubsub_state.subscriptions), + case {NewSubs, SubState#pubsub_state.affiliation} of + {[], none} -> + del_state(NodeId, element(1, SubState#pubsub_state.stateid)); + _ -> + set_state(SubState#pubsub_state{subscriptions = NewSubs}) + end. +%% @spec (Host, Owner) -> {result, [Node]} | {error, Reason} +%% Host = host() +%% Owner = jid() +%% Node = pubsubNode() +%% @docReturns a list of Owner's nodes on Host with pending +%% subscriptions.
get_pending_nodes(Host, Owner) -> - node_hometree:get_pending_nodes(Host, Owner). + GenKey = jlib:jid_remove_resource(jlib:jid_tolower(Owner)), + States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, + affiliation = owner, + _ = '_'}), + NodeIDs = [ID || #pubsub_state{stateid = {_, ID}} <- States], + NodeTree = case catch ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of + [{nodetree, N}] -> N; + _ -> nodetree_tree + end, + Reply = mnesia:foldl(fun(#pubsub_state{stateid = {_, NID}} = S, Acc) -> + case lists:member(NID, NodeIDs) of + true -> + case get_nodes_helper(NodeTree, S) of + {value, Node} -> [Node | Acc]; + false -> Acc + end; + false -> + Acc + end + end, [], pubsub_state), + {result, Reply}. +get_nodes_helper(NodeTree, + #pubsub_state{stateid = {_, N}, subscriptions = Subs}) -> + HasPending = fun ({pending, _}) -> true; + (pending) -> true; + (_) -> false + end, + case lists:any(HasPending, Subs) of + true -> + case NodeTree:get_node(N) of + #pubsub_node{nodeid = {_, Node}} -> + {value, Node}; + _ -> + false + end; + false -> + false + end. + +%% @spec (NodeId) -> {result, [State] | []} +%% NodeId = mod_pubsub:pubsubNodeId() +%% State = mod_pubsub:pubsubState() +%% @doc Returns the list of stored states for a given node. +%%For the default PubSub module, states are stored in Mnesia database.
+%%We can consider that the pubsub_state table have been created by the main +%% mod_pubsub module.
+%%PubSub plugins can store the states where they wants (for example in a +%% relational database).
+%%If a PubSub plugin wants to delegate the states storage to the default node, +%% they can implement this function like this: +%% ```get_states(NodeId) -> +%% node_default:get_states(NodeId).'''
get_states(NodeId) -> - node_hometree:get_states(NodeId). + States = case catch mnesia:match_object( + #pubsub_state{stateid = {'_', NodeId}, _ = '_'}) of + List when is_list(List) -> List; + _ -> [] + end, + {result, States}. +%% @spec (NodeId, JID) -> [State] | [] +%% NodeId = mod_pubsub:pubsubNodeId() +%% JID = mod_pubsub:jid() +%% State = mod_pubsub:pubsubItems() +%% @docReturns a state (one state list), given its reference.
get_state(NodeId, JID) -> - node_hometree:get_state(NodeId, JID). + StateId = {JID, NodeId}, + case catch mnesia:read({pubsub_state, StateId}) of + [State] when is_record(State, pubsub_state) -> State; + _ -> #pubsub_state{stateid=StateId} + end. -set_state(State) -> - node_hometree:set_state(State). +%% @spec (State) -> ok | {error, Reason::stanzaError()} +%% State = mod_pubsub:pubsubStates() +%% @docWrite a state into database.
+set_state(State) when is_record(State, pubsub_state) -> + mnesia:write(State); +set_state(_) -> + {error, 'internal-server-error'}. -get_items(NodeId, From) -> - node_hometree:get_items(NodeId, From). +%% @spec (NodeId, JID) -> ok | {error, Reason::stanzaError()} +%% NodeId = mod_pubsub:pubsubNodeId() +%% @docDelete a state from database.
+del_state(NodeId, JID) -> + mnesia:delete({pubsub_state, {JID, NodeId}}). -get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +%% @spec (NodeId, From) -> [Items] | [] +%% NodeId = mod_pubsub:pubsubNodeId() +%% Items = mod_pubsub:pubsubItems() +%% @doc Returns the list of stored items for a given node. +%%For the default PubSub module, items are stored in Mnesia database.
+%%We can consider that the pubsub_item table have been created by the main +%% mod_pubsub module.
+%%PubSub plugins can store the items where they wants (for example in a +%% relational database), or they can even decide not to persist any items.
+%%If a PubSub plugin wants to delegate the item storage to the default node, +%% they can implement this function like this: +%% ```get_items(NodeId, From) -> +%% node_default:get_items(NodeId, From).'''
+get_items(NodeId, _From) -> + Items = mnesia:match_object(#pubsub_item{itemid = {'_', NodeId}, _ = '_'}), + {result, lists:reverse(lists:keysort(#pubsub_item.modification, Items))}. +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> + SubKey = jlib:short_prepd_jid(JID), + GenKey = jlib:short_prepd_bare_jid(JID), + GenState = get_state(NodeId, GenKey), + SubState = get_state(NodeId, SubKey), + Affiliation = GenState#pubsub_state.affiliation, + Subscriptions = SubState#pubsub_state.subscriptions, + Whitelisted = can_fetch_item(Affiliation, Subscriptions), + if + %%SubId == "", ?? -> + %% Entity has multiple subscriptions to the node but does not specify a subscription ID + %{error, ?ERR_EXTENDED('bad-request', "subid-required")}; + %%InvalidSubId -> + %% Entity is subscribed but specifies an invalid subscription ID + %{error, ?ERR_EXTENDED('not-acceptable', "invalid-subid")}; + Affiliation == outcast -> + %% Requesting entity is blocked + {error, 'forbidden'}; + (AccessModel == presence) and (not PresenceSubscription) -> + %% Entity is not authorized to create a subscription (presence subscription required) + {error, ?ERR_EXTENDED('not-authorized', "presence-subscription-required")}; + (AccessModel == roster) and (not RosterGroup) -> + %% Entity is not authorized to create a subscription (not in roster group) + {error, ?ERR_EXTENDED('not-authorized', "not-in-roster-group")}; + (AccessModel == whitelist) and (not Whitelisted) -> + %% Node has whitelist access model and entity lacks required affiliation + {error, ?ERR_EXTENDED('not-allowed', "closed-node")}; + (AccessModel == authorize) and (not Whitelisted) -> + %% Node has authorize access model + {error, 'forbidden'}; + %%MustPay -> + %% % Payment is required for a subscription + %% {error, ?ERR_PAYMENT_REQUIRED}; + true -> + get_items(NodeId, JID) + end. +%% @spec (NodeId, ItemId) -> [Item] | [] +%% NodeId = mod_pubsub:pubsubNodeId() +%% ItemId = string() +%% Item = mod_pubsub:pubsubItems() +%% @docReturns an item (one item list), given its reference.
get_item(NodeId, ItemId) -> - node_hometree:get_item(NodeId, ItemId). + case mnesia:read({pubsub_item, {ItemId, NodeId}}) of + [Item] when is_record(Item, pubsub_item) -> + {result, Item}; + _ -> + {error, 'item-not-found'} + end. +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> + GenKey = jlib:short_prepd_bare_jid(JID), + GenState = get_state(NodeId, GenKey), + Affiliation = GenState#pubsub_state.affiliation, + Subscriptions = GenState#pubsub_state.subscriptions, + Whitelisted = can_fetch_item(Affiliation, Subscriptions), + if + %%SubId == "", ?? -> + %% Entity has multiple subscriptions to the node but does not specify a subscription ID + %{error, ?ERR_EXTENDED('bad-request', "subid-required")}; + %%InvalidSubID -> + %% Entity is subscribed but specifies an invalid subscription ID + %{error, ?ERR_EXTENDED('not-acceptable', "invalid-subid")}; + Affiliation == outcast -> + %% Requesting entity is blocked + {error, 'forbidden'}; + (AccessModel == presence) and (not PresenceSubscription) -> + %% Entity is not authorized to create a subscription (presence subscription required) + {error, ?ERR_EXTENDED('not-authorized', "presence-subscription-required")}; + (AccessModel == roster) and (not RosterGroup) -> + %% Entity is not authorized to create a subscription (not in roster group) + {error, ?ERR_EXTENDED('not-authorized', "not-in-roster-group")}; + (AccessModel == whitelist) and (not Whitelisted) -> + %% Node has whitelist access model and entity lacks required affiliation + {error, ?ERR_EXTENDED('not-allowed', "closed-node")}; + (AccessModel == authorize) and (not Whitelisted) -> + %% Node has authorize access model + {error, 'forbidden'}; + %%MustPay -> + %% % Payment is required for a subscription + %% {error, ?ERR_PAYMENT_REQUIRED}; + true -> + get_item(NodeId, ItemId) + end. -get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +%% @spec (Item) -> ok | {error, Reason::stanzaError()} +%% Item = mod_pubsub:pubsubItems() +%% @docWrite an item into database.
+set_item(Item) when is_record(Item, pubsub_item) -> + mnesia:write(Item); +set_item(_) -> + {error, 'internal-server-error'}. -set_item(Item) -> - node_hometree:set_item(Item). +%% @spec (NodeId, ItemId) -> ok | {error, Reason::stanzaError()} +%% NodeId = mod_pubsub:pubsubNodeId() +%% ItemId = string() +%% @docDelete an item from database.
+del_item(NodeId, ItemId) -> + mnesia:delete({pubsub_item, {ItemId, NodeId}}). +del_items(NodeId, ItemIds) -> + lists:foreach(fun(ItemId) -> + del_item(NodeId, ItemId) + end, ItemIds). -get_item_name(Host, Node, Id) -> - node_hometree:get_item_name(Host, Node, Id). +%% @docReturn the name of the node if known: Default is to return +%% node id.
+get_item_name(_Host, _Node, Id) -> + Id. node_to_path(Node) -> - [Node]. + [list_to_binary(Item) || Item <- string:tokens(binary_to_list(Node), "/")]. +path_to_node([]) -> + <<>>; path_to_node(Path) -> - case Path of - % default slot - [Node] -> Node; - % handle old possible entries, used when migrating database content to new format - [Node|_] when is_list(Node) -> list_to_binary(string:join([""|Path], "/")); - % default case (used by PEP for example) - _ -> list_to_binary(Path) + list_to_binary(string:join([""|[binary_to_list(Item) || Item <- Path]], "/")). + +%% @spec (Affiliation, Subscription) -> true | false +%% Affiliation = owner | member | publisher | outcast | none +%% Subscription = subscribed | none +%% @doc Determines if the combination of Affiliation and Subscribed +%% are allowed to get items from a node. +can_fetch_item(owner, _) -> true; +can_fetch_item(member, _) -> true; +can_fetch_item(publisher, _) -> true; +can_fetch_item(outcast, _) -> false; +can_fetch_item(none, Subscriptions) -> is_subscribed(Subscriptions); +can_fetch_item(_Affiliation, _Subscription) -> false. + +is_subscribed(Subscriptions) -> + lists:any(fun ({subscribed, _SubId}) -> true; + (_) -> false + end, Subscriptions). + +%% Returns the first item where Pred() is true in List +first_in_list(_Pred, []) -> + false; +first_in_list(Pred, [H | T]) -> + case Pred(H) of + true -> {value, H}; + _ -> first_in_list(Pred, T) end. + diff --git a/src/mod_pubsub/node_flat_odbc.erl b/src/mod_pubsub/node_flat_odbc.erl index d30acbb0f..087103d7b 100644 --- a/src/mod_pubsub/node_flat_odbc.erl +++ b/src/mod_pubsub/node_flat_odbc.erl @@ -67,10 +67,10 @@ init(Host, ServerHost, Opts) -> - node_hometree_odbc:init(Host, ServerHost, Opts). + node_flat_odbc:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> - node_hometree_odbc:terminate(Host, ServerHost). + node_flat_odbc:terminate(Host, ServerHost). options() -> [{deliver_payloads, true}, @@ -94,9 +94,9 @@ options() -> {rsm, true}]. features() -> - node_hometree_odbc:features(). + node_flat_odbc:features(). -%% use same code as node_hometree_odbc, but do not limite node to +%% use same code as node_flat_odbc, but do not limite node to %% the home/localhost/user/... hierarchy %% any node is allowed create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> @@ -111,85 +111,85 @@ create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> {result, Allowed}. create_node(NodeId, Owner) -> - node_hometree_odbc:create_node(NodeId, Owner). + node_flat_odbc:create_node(NodeId, Owner). delete_node(Removed) -> - node_hometree_odbc:delete_node(Removed). + node_flat_odbc:delete_node(Removed). subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> - node_hometree_odbc:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). + node_flat_odbc:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> - node_hometree_odbc:unsubscribe_node(NodeId, Sender, Subscriber, SubID). + node_flat_odbc:unsubscribe_node(NodeId, Sender, Subscriber, SubID). publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> - node_hometree_odbc:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). + node_flat_odbc:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). remove_extra_items(NodeId, MaxItems, ItemIds) -> - node_hometree_odbc:remove_extra_items(NodeId, MaxItems, ItemIds). + node_flat_odbc:remove_extra_items(NodeId, MaxItems, ItemIds). delete_item(NodeId, Publisher, PublishModel, ItemId) -> - node_hometree_odbc:delete_item(NodeId, Publisher, PublishModel, ItemId). + node_flat_odbc:delete_item(NodeId, Publisher, PublishModel, ItemId). purge_node(NodeId, Owner) -> - node_hometree_odbc:purge_node(NodeId, Owner). + node_flat_odbc:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> - node_hometree_odbc:get_entity_affiliations(Host, Owner). + node_flat_odbc:get_entity_affiliations(Host, Owner). get_node_affiliations(NodeId) -> - node_hometree_odbc:get_node_affiliations(NodeId). + node_flat_odbc:get_node_affiliations(NodeId). get_affiliation(NodeId, Owner) -> - node_hometree_odbc:get_affiliation(NodeId, Owner). + node_flat_odbc:get_affiliation(NodeId, Owner). set_affiliation(NodeId, Owner, Affiliation) -> - node_hometree_odbc:set_affiliation(NodeId, Owner, Affiliation). + node_flat_odbc:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> - node_hometree_odbc:get_entity_subscriptions(Host, Owner). + node_flat_odbc:get_entity_subscriptions(Host, Owner). get_node_subscriptions(NodeId) -> - node_hometree_odbc:get_node_subscriptions(NodeId). + node_flat_odbc:get_node_subscriptions(NodeId). get_subscriptions(NodeId, Owner) -> - node_hometree_odbc:get_subscriptions(NodeId, Owner). + node_flat_odbc:get_subscriptions(NodeId, Owner). set_subscriptions(NodeId, Owner, Subscription, SubId) -> - node_hometree_odbc:set_subscriptions(NodeId, Owner, Subscription, SubId). + node_flat_odbc:set_subscriptions(NodeId, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> - node_hometree_odbc:get_pending_nodes(Host, Owner). + node_flat_odbc:get_pending_nodes(Host, Owner). get_states(NodeId) -> - node_hometree_odbc:get_states(NodeId). + node_flat_odbc:get_states(NodeId). get_state(NodeId, JID) -> - node_hometree_odbc:get_state(NodeId, JID). + node_flat_odbc:get_state(NodeId, JID). set_state(State) -> - node_hometree_odbc:set_state(State). + node_flat_odbc:set_state(State). get_items(NodeId, From) -> - node_hometree_odbc:get_items(NodeId, From). + node_flat_odbc:get_items(NodeId, From). get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree_odbc:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + node_flat_odbc:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). get_item(NodeId, ItemId) -> - node_hometree_odbc:get_item(NodeId, ItemId). + node_flat_odbc:get_item(NodeId, ItemId). get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree_odbc:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + node_flat_odbc:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> - node_hometree_odbc:set_item(Item). + node_flat_odbc:set_item(Item). get_item_name(Host, Node, Id) -> - node_hometree_odbc:get_item_name(Host, Node, Id). + node_flat_odbc:get_item_name(Host, Node, Id). get_last_items(NodeId, From, Count) -> - node_hometree_odbc:get_last_items(NodeId, From, Count). + node_flat_odbc:get_last_items(NodeId, From, Count). node_to_path(Node) -> node_flat:node_to_path(Node). diff --git a/src/mod_pubsub/node_hometree.erl b/src/mod_pubsub/node_hometree.erl index c45448787..a5bf4da01 100644 --- a/src/mod_pubsub/node_hometree.erl +++ b/src/mod_pubsub/node_hometree.erl @@ -15,7 +15,6 @@ %%% All Rights Reserved.'' %%% This software is copyright 2006-2010, ProcessOne. %%% -%%% %%% @copyright 2006-2010 ProcessOne %%% @author Christophe RomainIt is used as a default for all unknown PubSub node type. It can serve -%%% as a developer basis and reference to build its own custom pubsub node -%%% types.
-%%%PubSub plugin nodes are using the {@link gen_node} behaviour.
-%%%The API isn't stabilized yet. The pubsub plugin -%%% development is still a work in progress. However, the system is already -%%% useable and useful as is. Please, send us comments, feedback and -%%% improvements.
- -module(node_hometree). -author('christophe.romain@process-one.net'). @@ -80,60 +64,13 @@ path_to_node/1 ]). -%% ================ -%% API definition -%% ================ -%% @spec (Host, ServerHost, Opts) -> any() -%% Host = mod_pubsub:host() -%% ServerHost = mod_pubsub:host() -%% Opts = list() -%% @docCalled during pubsub modules initialisation. Any pubsub plugin must -%% implement this function. It can return anything.
-%%This function is mainly used to trigger the setup task necessary for the -%% plugin. It can be used for example by the developer to create the specific -%% module database schema if it does not exists yet.
-init(_Host, _ServerHost, _Opts) -> - pubsub_subscription:init(), - mnesia:create_table(pubsub_state, - [{disc_copies, [node()]}, - {attributes, record_info(fields, pubsub_state)}]), - mnesia:create_table(pubsub_item, - [{disc_only_copies, [node()]}, - {attributes, record_info(fields, pubsub_item)}]), - ItemsFields = record_info(fields, pubsub_item), - case mnesia:table_info(pubsub_item, attributes) of - ItemsFields -> ok; - _ -> - mnesia:transform_table(pubsub_item, ignore, ItemsFields) - end, - ok. +init(Host, ServerHost, Opts) -> + node_flat:init(Host, ServerHost, Opts). -%% @spec (Host, ServerHost) -> any() -%% Host = mod_pubsub:host() -%% ServerHost = host() -%% @docCalled during pubsub modules termination. Any pubsub plugin must -%% implement this function. It can return anything.
-terminate(_Host, _ServerHost) -> - ok. +terminate(Host, ServerHost) -> + node_flat:terminate(Host, ServerHost). -%% @spec () -> [Option] -%% Option = mod_pubsub:nodeOption() -%% @doc Returns the default pubsub node options. -%%Example of function return value:
-%% ``` -%% [{deliver_payloads, true}, -%% {notify_config, false}, -%% {notify_delete, false}, -%% {notify_retract, true}, -%% {persist_items, true}, -%% {max_items, 10}, -%% {subscribe, true}, -%% {access_model, open}, -%% {publish_model, publishers}, -%% {max_payload_size, 100000}, -%% {send_last_published_item, never}, -%% {presence_based_delivery, false}]''' options() -> [{deliver_payloads, true}, {notify_config, false}, @@ -153,31 +90,8 @@ options() -> {deliver_notifications, true}, {presence_based_delivery, false}]. -%% @spec () -> [] -%% @doc Returns the node features features() -> - ["create-nodes", - "auto-create", - "access-authorize", - "delete-nodes", - "delete-items", - "get-pending", - "instant-nodes", - "manage-subscriptions", - "modify-affiliations", - "multi-subscribe", - "outcast-affiliation", - "persistent-items", - "publish", - "purge-nodes", - "retract-items", - "retrieve-affiliations", - "retrieve-items", - "retrieve-subscriptions", - "subscribe", - "subscription-notifications", - "subscription-options" - ]. + node_flat:features(). %% @spec (Host, ServerHost, Node, ParentNode, Owner, Access) -> bool() %% Host = mod_pubsub:host() @@ -217,805 +131,93 @@ create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) -> end, {result, Allowed}. -%% @spec (NodeId, Owner) -> -%% {result, Result} | exit -%% NodeId = mod_pubsub:pubsubNodeId() -%% Owner = mod_pubsub:jid() -%% @doc create_node(NodeId, Owner) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - set_state(#pubsub_state{stateid = {OwnerKey, NodeId}, affiliation = owner}), - {result, {default, broadcast}}. + node_flat:create_node(NodeId, Owner). -%% @spec (Removed) -> ok -%% Removed = [mod_pubsub:pubsubNode()] -%% @docpurge items of deleted nodes after effective deletion.
delete_node(Removed) -> - Tr = fun(#pubsub_state{stateid = {J, _}, subscriptions = Ss}) -> - lists:map(fun(S) -> - {J, S} - end, Ss) - end, - Reply = lists:map( - fun(#pubsub_node{id = NodeId} = PubsubNode) -> - {result, States} = get_states(NodeId), - lists:foreach( - fun(#pubsub_state{stateid = {LJID, _}, items = Items}) -> - del_items(NodeId, Items), - del_state(NodeId, LJID) - end, States), - {PubsubNode, lists:flatmap(Tr, States)} - end, Removed), - {result, {default, broadcast, Reply}}. + node_flat:delete_node(Removed). -%% @spec (NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> -%% {error, Reason} | {result, Result} -%% @docAccepts or rejects subcription requests on a PubSub node.
-%%The mechanism works as follow: -%%
The selected behaviour depends on the return parameter: -%%
In the default plugin module, the record is unchanged.
-subscribe_node(NodeId, Sender, {U,S,R} = Subscriber, AccessModel, - SendLast, PresenceSubscription, RosterGroup, Options) -> - SubKey = {U, S, R}, - GenKey = {U, S, undefined}, - Authorized = (jlib:short_prepd_bare_jid(Sender) == GenKey), - GenState = get_state(NodeId, GenKey), - SubState = case SubKey of - GenKey -> GenState; - _ -> get_state(NodeId, SubKey) - end, - Affiliation = GenState#pubsub_state.affiliation, - Subscriptions = SubState#pubsub_state.subscriptions, - Whitelisted = lists:member(Affiliation, [member, publisher, owner]), - PendingSubscription = lists:any(fun({pending, _}) -> true; - (_) -> false - end, Subscriptions), - if - not Authorized -> - %% JIDs do not match - {error, ?ERR_EXTENDED('bad-request', "invalid-jid")}; - Affiliation == outcast -> - %% Requesting entity is blocked - {error, 'forbidden'}; - PendingSubscription -> - %% Requesting entity has pending subscription - {error, ?ERR_EXTENDED('not-authorized', "pending-subscription")}; - (AccessModel == presence) and (not PresenceSubscription) -> - %% Entity is not authorized to create a subscription (presence subscription required) - {error, ?ERR_EXTENDED('not-authorized', "presence-subscription-required")}; - (AccessModel == roster) and (not RosterGroup) -> - %% Entity is not authorized to create a subscription (not in roster group) - {error, ?ERR_EXTENDED('not-authorized', "not-in-roster-group")}; - (AccessModel == whitelist) and (not Whitelisted) -> - %% Node has whitelist access model and entity lacks required affiliation - {error, ?ERR_EXTENDED('not-allowed', "closed-node")}; - %%MustPay -> - %% % Payment is required for a subscription - %% {error, ?ERR_PAYMENT_REQUIRED}; - %%ForbiddenAnonymous -> - %% % Requesting entity is anonymous - %% {error, 'forbidden'}; - true -> - case pubsub_subscription:subscribe_node(Subscriber, NodeId, Options) of - {result, SubId} -> - NewSub = case AccessModel of - authorize -> pending; - _ -> subscribed - end, - set_state(SubState#pubsub_state{subscriptions = [{NewSub, SubId} | Subscriptions]}), - case {NewSub, SendLast} of - {subscribed, never} -> - {result, {default, subscribed, SubId}}; - {subscribed, _} -> - {result, {default, subscribed, SubId, send_last}}; - {_, _} -> - {result, {default, pending, SubId}} - end; - _ -> - {error, 'internal-server-error'} - end - end. +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> + node_flat:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). -%% @spec (NodeId, Sender, Subscriber, SubId) -> -%% {error, Reason} | {result, []} -%% NodeId = mod_pubsub:pubsubNodeId() -%% Sender = mod_pubsub:jid() -%% Subscriber = mod_pubsub:jid() -%% SubId = mod_pubsub:subid() -%% Reason = mod_pubsub:stanzaError() -%% @docUnsubscribe the Subscriber from the Node.
-unsubscribe_node(NodeId, Sender, {U, S, R} = Subscriber, SubId) -> - SubKey = {U, S, R}, - GenKey = {U, S, undefined}, - Authorized = (jlib:short_prepd_bare_jid(Sender) == GenKey), - GenState = get_state(NodeId, GenKey), - SubState = case SubKey of - GenKey -> GenState; - _ -> get_state(NodeId, SubKey) - end, - Subscriptions = lists:filter(fun({_Sub, _SubId}) -> true; - (_SubId) -> false - end, SubState#pubsub_state.subscriptions), - SubIdExists = case SubId of - [] -> false; - List when is_list(List) -> true; - _ -> false - end, - if - %% Requesting entity is prohibited from unsubscribing entity - not Authorized -> - {error, 'forbidden'}; - %% Entity did not specify SubId - %%SubId == "", ?? -> - %% {error, ?ERR_EXTENDED('bad-request', "subid-required")}; - %% Invalid subscription identifier - %%InvalidSubId -> - %% {error, ?ERR_EXTENDED('not-acceptable', "invalid-subid")}; - %% Requesting entity is not a subscriber - Subscriptions == [] -> - {error, ?ERR_EXTENDED('unexpected-request', "not-subscribed")}; - %% Subid supplied, so use that. - SubIdExists -> - Sub = first_in_list(fun({_, Id}) when Id == SubId -> true; - (_) -> false - end, Subscriptions), - case Sub of - {value, Subscribed} -> - delete_subscriptions(Subscriber, NodeId, [Subscribed], SubState), - {result, default}; - false -> - {error, ?ERR_EXTENDED('unexpected-request', "not-subscribed")} - end; - %% Asking to remove all subscriptions to the given node - SubId == all -> - delete_subscriptions(SubKey, NodeId, Subscriptions, SubState), - {result, default}; - %% No subid supplied, but there's only one matching - %% subscription, so use that. - length(Subscriptions) == 1 -> - delete_subscriptions(SubKey, NodeId, Subscriptions, SubState), - {result, default}; - true -> - {error, ?ERR_EXTENDED('bad-request', "subid-required")} - end. +unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> + node_flat:unsubscribe_node(NodeId, Sender, Subscriber, SubID). -delete_subscriptions(SubKey, NodeId, Subscriptions, SubState) -> - NewSubs = lists:foldl(fun({Subscription, SubId}, Acc) -> - pubsub_subscription:delete_subscription(SubKey, NodeId, SubId), - Acc -- [{Subscription, SubId}] - end, SubState#pubsub_state.subscriptions, Subscriptions), - case {SubState#pubsub_state.affiliation, NewSubs} of - {none, []} -> - % Just a regular subscriber, and this is final item, so - % delete the state. - del_state(NodeId, SubKey); - _ -> - set_state(SubState#pubsub_state{subscriptions = NewSubs}) - end. +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> + node_flat:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). - -%% @spec (NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload) -> -%% {true, PubsubItem} | {result, Reply} -%% NodeId = mod_pubsub:pubsubNodeId() -%% Publisher = mod_pubsub:jid() -%% PublishModel = atom() -%% MaxItems = integer() -%% ItemId = string() -%% Payload = term() -%% @docPublishes the item passed as parameter.
-%%The mechanism works as follow: -%%
The selected behaviour depends on the return parameter: -%%
In the default plugin module, the record is unchanged.
-publish_item(NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload) -> - SubKey = jlib:short_prepd_jid(Publisher), - GenKey = jlib:short_prepd_bare_jid(Publisher), - GenState = get_state(NodeId, GenKey), - SubState = case SubKey of - GenKey -> GenState; - _ -> get_state(NodeId, SubKey) - end, - Affiliation = GenState#pubsub_state.affiliation, - Subscribed = case PublishModel of - subscribers -> is_subscribed(SubState#pubsub_state.subscriptions); - _ -> undefined - end, - if - not ((PublishModel == open) - or ((PublishModel == publishers) - and ((Affiliation == owner) or (Affiliation == publisher))) - or (Subscribed == true)) -> - %% Entity does not have sufficient privileges to publish to node - {error, 'forbidden'}; - true -> - %% TODO: check creation, presence, roster - if MaxItems > 0 -> - Now = now(), - PubId = {Now, SubKey}, - Item = case get_item(NodeId, ItemId) of - {result, OldItem} -> - OldItem#pubsub_item{modification = PubId, - payload = Payload}; - _ -> - #pubsub_item{itemid = {ItemId, NodeId}, - creation = {Now, GenKey}, - modification = PubId, - payload = Payload} - end, - Items = [ItemId | GenState#pubsub_state.items--[ItemId]], - {result, {NI, OI}} = remove_extra_items(NodeId, MaxItems, Items), - set_item(Item), - set_state(GenState#pubsub_state{items = NI}), - {result, {default, broadcast, OI}}; - true -> - {result, {default, broadcast, []}} - end - end. - -%% @spec (NodeId, MaxItems, ItemIds) -> {NewItemIds,OldItemIds} -%% NodeId = mod_pubsub:pubsubNodeId() -%% MaxItems = integer() | unlimited -%% ItemIds = [ItemId::string()] -%% NewItemIds = [ItemId::string()] -%% @docThis function is used to remove extra items, most notably when the -%% maximum number of items has been reached.
-%%This function is used internally by the core PubSub module, as no -%% permission check is performed.
-%%In the default plugin module, the oldest items are removed, but other -%% rules can be used.
-%%If another PubSub plugin wants to delegate the item removal (and if the -%% plugin is using the default pubsub storage), it can implements this function like this: -%% ```remove_extra_items(NodeId, MaxItems, ItemIds) -> -%% node_default:remove_extra_items(NodeId, MaxItems, ItemIds).'''
-remove_extra_items(_NodeId, unlimited, ItemIds) -> - {result, {ItemIds, []}}; remove_extra_items(NodeId, MaxItems, ItemIds) -> - NewItems = lists:sublist(ItemIds, MaxItems), - OldItems = lists:nthtail(length(NewItems), ItemIds), - %% Remove extra items: - del_items(NodeId, OldItems), - %% Return the new items list: - {result, {NewItems, OldItems}}. + node_flat:remove_extra_items(NodeId, MaxItems, ItemIds). -%% @spec (NodeId, Publisher, PublishModel, ItemId) -> -%% {error, Reason::stanzaError()} | -%% {result, []} -%% NodeId = mod_pubsub:pubsubNodeId() -%% Publisher = mod_pubsub:jid() -%% PublishModel = atom() -%% ItemId = string() -%% @docTriggers item deletion.
-%%Default plugin: The user performing the deletion must be the node owner -%% or a publisher, or PublishModel being open.
delete_item(NodeId, Publisher, PublishModel, ItemId) -> - GenKey = jlib:short_prepd_bare_jid(Publisher), - GenState = get_state(NodeId, GenKey), - #pubsub_state{affiliation = Affiliation, items = Items} = GenState, - Allowed = (Affiliation == publisher) orelse (Affiliation == owner) - orelse (PublishModel == open) - orelse case get_item(NodeId, ItemId) of - {result, #pubsub_item{creation = {_, GenKey}}} -> true; - _ -> false - end, - if - not Allowed -> - %% Requesting entity does not have sufficient privileges - {error, 'forbidden'}; - true -> - case lists:member(ItemId, Items) of - true -> - del_item(NodeId, ItemId), - set_state(GenState#pubsub_state{items = lists:delete(ItemId, Items)}), - {result, {default, broadcast}}; - false -> - case Affiliation of - owner -> - %% Owner can delete other publishers items as well - {result, States} = get_states(NodeId), - lists:foldl( - fun(#pubsub_state{items = PI, affiliation = publisher} = S, Res) -> - case lists:member(ItemId, PI) of - true -> - del_item(NodeId, ItemId), - set_state(S#pubsub_state{items = lists:delete(ItemId, PI)}), - {result, {default, broadcast}}; - false -> - Res - end; - (_, Res) -> - Res - end, {error, 'item-not-found'}, States); - _ -> - %% Non-existent node or item - {error, 'item-not-found'} - end - end - end. + node_flat:delete_item(NodeId, Publisher, PublishModel, ItemId). -%% @spec (NodeId, Owner) -> -%% {error, Reason::stanzaError()} | -%% {result, {default, broadcast}} -%% NodeId = mod_pubsub:pubsubNodeId() -%% Owner = mod_pubsub:jid() purge_node(NodeId, Owner) -> - GenKey = jlib:short_prepd_bare_jid(Owner), - GenState = get_state(NodeId, GenKey), - case GenState of - #pubsub_state{affiliation = owner} -> - {result, States} = get_states(NodeId), - lists:foreach( - fun(#pubsub_state{items = []}) -> - ok; - (#pubsub_state{items = Items} = S) -> - del_items(NodeId, Items), - set_state(S#pubsub_state{items = []}) - end, States), - {result, {default, broadcast}}; - _ -> - %% Entity is not owner - {error, 'forbidden'} - end. + node_flat:purge_node(NodeId, Owner). -%% @spec (Host, JID) -> [{Node,Affiliation}] -%% Host = host() -%% JID = mod_pubsub:jid() -%% @docReturn the current affiliations for the given user
-%%The default module reads affiliations in the main Mnesia -%% pubsub_state table. If a plugin stores its data in the same -%% table, it should return an empty list, as the affiliation will be read by -%% the default PubSub module. Otherwise, it should return its own affiliation, -%% that will be added to the affiliation stored in the main -%% pubsub_state table.
get_entity_affiliations(Host, Owner) -> - GenKey = jlib:short_prepd_bare_jid(Owner), - States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}), - NodeTree = case catch ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of - [{nodetree, N}] -> N; - _ -> nodetree_tree - end, - Reply = lists:foldl(fun(#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) -> - case NodeTree:get_node(N) of - #pubsub_node{nodeid = {Host, _}} = Node -> [{Node, A}|Acc]; - _ -> Acc - end - end, [], States), - {result, Reply}. + node_flat:get_entity_affiliations(Host, Owner). get_node_affiliations(NodeId) -> - {result, States} = get_states(NodeId), - Tr = fun(#pubsub_state{stateid = {J, _}, affiliation = A}) -> - {J, A} - end, - {result, lists:map(Tr, States)}. + node_flat:get_node_affiliations(NodeId). get_affiliation(NodeId, Owner) -> - GenKey = jlib:short_prepd_bare_jid(Owner), - GenState = get_state(NodeId, GenKey), - {result, GenState#pubsub_state.affiliation}. + node_flat:get_affiliation(NodeId, Owner). -set_affiliation(NodeId, Owner, Affiliation) when ?IS_JID(Owner)-> - GenKey = jlib:short_prepd_bare_jid(Owner), - GenState = get_state(NodeId, GenKey), - case {Affiliation, GenState#pubsub_state.subscriptions} of - {none, none} -> - del_state(NodeId, GenKey); - _ -> - set_state(GenState#pubsub_state{affiliation = Affiliation}) - end. +set_affiliation(NodeId, Owner, Affiliation) -> + node_flat:set_affiliation(NodeId, Owner, Affiliation). -%% @spec (Host, Owner) -> [{Node,Subscription}] -%% Host = host() -%% Owner = mod_pubsub:jid() -%% @docReturn the current subscriptions for the given user
-%%The default module reads subscriptions in the main Mnesia -%% pubsub_state table. If a plugin stores its data in the same -%% table, it should return an empty list, as the affiliation will be read by -%% the default PubSub module. Otherwise, it should return its own affiliation, -%% that will be added to the affiliation stored in the main -%% pubsub_state table.
get_entity_subscriptions(Host, Owner) -> - {U, D, _} = SubKey = jlib:short_prepd_jid(Owner), - GenKey = jlib:short_prepd_bare_jid(Owner), - States = case SubKey of - GenKey -> mnesia:match_object( - #pubsub_state{stateid = {{U, D, '_'}, '_'}, _ = '_'}); - _ -> mnesia:match_object( - #pubsub_state{stateid = {GenKey, '_'}, _ = '_'}) - ++ mnesia:match_object( - #pubsub_state{stateid = {SubKey, '_'}, _ = '_'}) - end, - NodeTree = case catch ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of - [{nodetree, N}] -> N; - _ -> nodetree_tree - end, - Reply = lists:foldl(fun(#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) -> - case NodeTree:get_node(N) of - #pubsub_node{nodeid = {Host, _}} = Node -> - lists:foldl(fun({Sub, SubId}, Acc2) -> - [{Node, Sub, SubId, J} | Acc2]; - (S, Acc2) -> - [{Node, S, J} | Acc2] - end, Acc, Ss); - _ -> Acc - end - end, [], States), - {result, Reply}. + node_flat:get_entity_subscriptions(Host, Owner). get_node_subscriptions(NodeId) -> - {result, States} = get_states(NodeId), - Tr = fun(#pubsub_state{stateid = {J, _}, subscriptions = Subscriptions}) -> - %% TODO: get rid of cases to handle non-list subscriptions - case Subscriptions of - [_|_] -> - lists:foldl(fun({S, SubId}, Acc) -> - [{J, S, SubId} | Acc]; - (S, Acc) -> - [{J, S} | Acc] - end, [], Subscriptions); - [] -> - []; - _ -> - [{J, none}] - end - end, - {result, lists:flatmap(Tr, States)}. + node_flat:get_node_subscriptions(NodeId). get_subscriptions(NodeId, Owner) -> - SubKey = jlib:short_prepd_jid(Owner), - SubState = get_state(NodeId, SubKey), - {result, SubState#pubsub_state.subscriptions}. + node_flat:get_subscriptions(NodeId, Owner). set_subscriptions(NodeId, Owner, Subscription, SubId) -> - SubKey = jlib:short_prepd_jid(Owner), - SubState = get_state(NodeId, SubKey), - case {SubId, SubState#pubsub_state.subscriptions} of - {_, []} -> - case Subscription of - none -> {error, ?ERR_EXTENDED('bad_request', "not-subscribed")}; - _ -> new_subscription(NodeId, Owner, Subscription, SubState) - end; - {"", [{_, SID}]} -> - case Subscription of - none -> unsub_with_subid(NodeId, SID, SubState); - _ -> replace_subscription({Subscription, SID}, SubState) - end; - {"", [_|_]} -> - {error, ?ERR_EXTENDED('bad_request', "subid-required")}; - _ -> - case Subscription of - none -> unsub_with_subid(NodeId, SubId, SubState); - _ -> replace_subscription({Subscription, SubId}, SubState) - end - end. + node_flat:set_subscriptions(NodeId, Owner, Subscription, SubId). -replace_subscription(NewSub, SubState) -> - NewSubs = replace_subscription(NewSub, - SubState#pubsub_state.subscriptions, []), - set_state(SubState#pubsub_state{subscriptions = NewSubs}). - -replace_subscription(_, [], Acc) -> - Acc; -replace_subscription({Sub, SubId}, [{_, SubID} | T], Acc) -> - replace_subscription({Sub, SubId}, T, [{Sub, SubID} | Acc]). - -new_subscription(NodeId, Owner, Subscription, SubState) -> - SubId = pubsub_subscription:add_subscription(Owner, NodeId, []), - Subscriptions = SubState#pubsub_state.subscriptions, - set_state(SubState#pubsub_state{subscriptions = [{Subscription, SubId} | Subscriptions]}), - {Subscription, SubId}. - -unsub_with_subid(NodeId, SubId, SubState) -> - pubsub_subscription:delete_subscription(SubState#pubsub_state.stateid, - NodeId, SubId), - NewSubs = lists:filter(fun ({_, SID}) -> SubId =/= SID end, - SubState#pubsub_state.subscriptions), - case {NewSubs, SubState#pubsub_state.affiliation} of - {[], none} -> - del_state(NodeId, element(1, SubState#pubsub_state.stateid)); - _ -> - set_state(SubState#pubsub_state{subscriptions = NewSubs}) - end. -%% @spec (Host, Owner) -> {result, [Node]} | {error, Reason} -%% Host = host() -%% Owner = jid() -%% Node = pubsubNode() -%% @docReturns a list of Owner's nodes on Host with pending -%% subscriptions.
get_pending_nodes(Host, Owner) -> - GenKey = jlib:jid_remove_resource(jlib:jid_tolower(Owner)), - States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, - affiliation = owner, - _ = '_'}), - NodeIDs = [ID || #pubsub_state{stateid = {_, ID}} <- States], - NodeTree = case catch ets:lookup(gen_mod:get_module_proc(Host, config), nodetree) of - [{nodetree, N}] -> N; - _ -> nodetree_tree - end, - Reply = mnesia:foldl(fun(#pubsub_state{stateid = {_, NID}} = S, Acc) -> - case lists:member(NID, NodeIDs) of - true -> - case get_nodes_helper(NodeTree, S) of - {value, Node} -> [Node | Acc]; - false -> Acc - end; - false -> - Acc - end - end, [], pubsub_state), - {result, Reply}. + node_flat:get_pending_nodes(Host, Owner). -get_nodes_helper(NodeTree, - #pubsub_state{stateid = {_, N}, subscriptions = Subs}) -> - HasPending = fun ({pending, _}) -> true; - (pending) -> true; - (_) -> false - end, - case lists:any(HasPending, Subs) of - true -> - case NodeTree:get_node(N) of - #pubsub_node{nodeid = {_, Node}} -> - {value, Node}; - _ -> - false - end; - false -> - false - end. - -%% @spec (NodeId) -> {result, [State] | []} -%% NodeId = mod_pubsub:pubsubNodeId() -%% State = mod_pubsub:pubsubState() -%% @doc Returns the list of stored states for a given node. -%%For the default PubSub module, states are stored in Mnesia database.
-%%We can consider that the pubsub_state table have been created by the main -%% mod_pubsub module.
-%%PubSub plugins can store the states where they wants (for example in a -%% relational database).
-%%If a PubSub plugin wants to delegate the states storage to the default node, -%% they can implement this function like this: -%% ```get_states(NodeId) -> -%% node_default:get_states(NodeId).'''
get_states(NodeId) -> - States = case catch mnesia:match_object( - #pubsub_state{stateid = {'_', NodeId}, _ = '_'}) of - List when is_list(List) -> List; - _ -> [] - end, - {result, States}. + node_flat:get_states(NodeId). -%% @spec (NodeId, JID) -> [State] | [] -%% NodeId = mod_pubsub:pubsubNodeId() -%% JID = mod_pubsub:jid() -%% State = mod_pubsub:pubsubItems() -%% @docReturns a state (one state list), given its reference.
get_state(NodeId, JID) -> - StateId = {JID, NodeId}, - case catch mnesia:read({pubsub_state, StateId}) of - [State] when is_record(State, pubsub_state) -> State; - _ -> #pubsub_state{stateid=StateId} - end. + node_flat:get_state(NodeId, JID). -%% @spec (State) -> ok | {error, Reason::stanzaError()} -%% State = mod_pubsub:pubsubStates() -%% @docWrite a state into database.
-set_state(State) when is_record(State, pubsub_state) -> - mnesia:write(State); -set_state(_) -> - {error, 'internal-server-error'}. +set_state(State) -> + node_flat:set_state(State). -%% @spec (NodeId, JID) -> ok | {error, Reason::stanzaError()} -%% NodeId = mod_pubsub:pubsubNodeId() -%% @docDelete a state from database.
-del_state(NodeId, JID) -> - mnesia:delete({pubsub_state, {JID, NodeId}}). +get_items(NodeId, From) -> + node_flat:get_items(NodeId, From). -%% @spec (NodeId, From) -> [Items] | [] -%% NodeId = mod_pubsub:pubsubNodeId() -%% Items = mod_pubsub:pubsubItems() -%% @doc Returns the list of stored items for a given node. -%%For the default PubSub module, items are stored in Mnesia database.
-%%We can consider that the pubsub_item table have been created by the main -%% mod_pubsub module.
-%%PubSub plugins can store the items where they wants (for example in a -%% relational database), or they can even decide not to persist any items.
-%%If a PubSub plugin wants to delegate the item storage to the default node, -%% they can implement this function like this: -%% ```get_items(NodeId, From) -> -%% node_default:get_items(NodeId, From).'''
-get_items(NodeId, _From) -> - Items = mnesia:match_object(#pubsub_item{itemid = {'_', NodeId}, _ = '_'}), - {result, lists:reverse(lists:keysort(#pubsub_item.modification, Items))}. -get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> - SubKey = jlib:short_prepd_jid(JID), - GenKey = jlib:short_prepd_bare_jid(JID), - GenState = get_state(NodeId, GenKey), - SubState = get_state(NodeId, SubKey), - Affiliation = GenState#pubsub_state.affiliation, - Subscriptions = SubState#pubsub_state.subscriptions, - Whitelisted = can_fetch_item(Affiliation, Subscriptions), - if - %%SubId == "", ?? -> - %% Entity has multiple subscriptions to the node but does not specify a subscription ID - %{error, ?ERR_EXTENDED('bad-request', "subid-required")}; - %%InvalidSubId -> - %% Entity is subscribed but specifies an invalid subscription ID - %{error, ?ERR_EXTENDED('not-acceptable', "invalid-subid")}; - Affiliation == outcast -> - %% Requesting entity is blocked - {error, 'forbidden'}; - (AccessModel == presence) and (not PresenceSubscription) -> - %% Entity is not authorized to create a subscription (presence subscription required) - {error, ?ERR_EXTENDED('not-authorized', "presence-subscription-required")}; - (AccessModel == roster) and (not RosterGroup) -> - %% Entity is not authorized to create a subscription (not in roster group) - {error, ?ERR_EXTENDED('not-authorized', "not-in-roster-group")}; - (AccessModel == whitelist) and (not Whitelisted) -> - %% Node has whitelist access model and entity lacks required affiliation - {error, ?ERR_EXTENDED('not-allowed', "closed-node")}; - (AccessModel == authorize) and (not Whitelisted) -> - %% Node has authorize access model - {error, 'forbidden'}; - %%MustPay -> - %% % Payment is required for a subscription - %% {error, ?ERR_PAYMENT_REQUIRED}; - true -> - get_items(NodeId, JID) - end. +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_flat:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -%% @spec (NodeId, ItemId) -> [Item] | [] -%% NodeId = mod_pubsub:pubsubNodeId() -%% ItemId = string() -%% Item = mod_pubsub:pubsubItems() -%% @docReturns an item (one item list), given its reference.
get_item(NodeId, ItemId) -> - case mnesia:read({pubsub_item, {ItemId, NodeId}}) of - [Item] when is_record(Item, pubsub_item) -> - {result, Item}; - _ -> - {error, 'item-not-found'} - end. -get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> - GenKey = jlib:short_prepd_bare_jid(JID), - GenState = get_state(NodeId, GenKey), - Affiliation = GenState#pubsub_state.affiliation, - Subscriptions = GenState#pubsub_state.subscriptions, - Whitelisted = can_fetch_item(Affiliation, Subscriptions), - if - %%SubId == "", ?? -> - %% Entity has multiple subscriptions to the node but does not specify a subscription ID - %{error, ?ERR_EXTENDED('bad-request', "subid-required")}; - %%InvalidSubID -> - %% Entity is subscribed but specifies an invalid subscription ID - %{error, ?ERR_EXTENDED('not-acceptable', "invalid-subid")}; - Affiliation == outcast -> - %% Requesting entity is blocked - {error, 'forbidden'}; - (AccessModel == presence) and (not PresenceSubscription) -> - %% Entity is not authorized to create a subscription (presence subscription required) - {error, ?ERR_EXTENDED('not-authorized', "presence-subscription-required")}; - (AccessModel == roster) and (not RosterGroup) -> - %% Entity is not authorized to create a subscription (not in roster group) - {error, ?ERR_EXTENDED('not-authorized', "not-in-roster-group")}; - (AccessModel == whitelist) and (not Whitelisted) -> - %% Node has whitelist access model and entity lacks required affiliation - {error, ?ERR_EXTENDED('not-allowed', "closed-node")}; - (AccessModel == authorize) and (not Whitelisted) -> - %% Node has authorize access model - {error, 'forbidden'}; - %%MustPay -> - %% % Payment is required for a subscription - %% {error, ?ERR_PAYMENT_REQUIRED}; - true -> - get_item(NodeId, ItemId) - end. + node_flat:get_item(NodeId, ItemId). -%% @spec (Item) -> ok | {error, Reason::stanzaError()} -%% Item = mod_pubsub:pubsubItems() -%% @docWrite an item into database.
-set_item(Item) when is_record(Item, pubsub_item) -> - mnesia:write(Item); -set_item(_) -> - {error, 'internal-server-error'}. +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_flat:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -%% @spec (NodeId, ItemId) -> ok | {error, Reason::stanzaError()} -%% NodeId = mod_pubsub:pubsubNodeId() -%% ItemId = string() -%% @docDelete an item from database.
-del_item(NodeId, ItemId) -> - mnesia:delete({pubsub_item, {ItemId, NodeId}}). -del_items(NodeId, ItemIds) -> - lists:foreach(fun(ItemId) -> - del_item(NodeId, ItemId) - end, ItemIds). +set_item(Item) -> + node_flat:set_item(Item). -%% @docReturn the name of the node if known: Default is to return -%% node id.
-get_item_name(_Host, _Node, Id) -> - Id. +get_item_name(Host, Node, Id) -> + node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> - [list_to_binary(Item) || Item <- string:tokens(binary_to_list(Node), "/")]. + [Node]. -path_to_node([]) -> - <<>>; path_to_node(Path) -> - list_to_binary(string:join([""|[binary_to_list(Item) || Item <- Path]], "/")). - -%% @spec (Affiliation, Subscription) -> true | false -%% Affiliation = owner | member | publisher | outcast | none -%% Subscription = subscribed | none -%% @doc Determines if the combination of Affiliation and Subscribed -%% are allowed to get items from a node. -can_fetch_item(owner, _) -> true; -can_fetch_item(member, _) -> true; -can_fetch_item(publisher, _) -> true; -can_fetch_item(outcast, _) -> false; -can_fetch_item(none, Subscriptions) -> is_subscribed(Subscriptions); -can_fetch_item(_Affiliation, _Subscription) -> false. - -is_subscribed(Subscriptions) -> - lists:any(fun ({subscribed, _SubId}) -> true; - (_) -> false - end, Subscriptions). - -%% Returns the first item where Pred() is true in List -first_in_list(_Pred, []) -> - false; -first_in_list(Pred, [H | T]) -> - case Pred(H) of - true -> {value, H}; - _ -> first_in_list(Pred, T) + case Path of + % default slot + [Node] -> Node; + % handle old possible entries, used when migrating database content to new format + [Node|_] when is_list(Node) -> list_to_binary(string:join([""|Path], "/")); + % default case (used by PEP for example) + _ -> list_to_binary(Path) end. - diff --git a/src/mod_pubsub/node_hometree_odbc.erl b/src/mod_pubsub/node_hometree_odbc.erl index c07d089b7..6c4a56483 100644 --- a/src/mod_pubsub/node_hometree_odbc.erl +++ b/src/mod_pubsub/node_hometree_odbc.erl @@ -239,7 +239,7 @@ create_node(NodeId, Owner) -> %% Removed = [mod_pubsub:pubsubNode()] %% @docpurge items of deleted nodes after effective deletion.
delete_node(Removed) -> -%% pablo: TODO, this is present on trunk/node_hometree but not on trunk/node_hometree_odbc. +%% pablo: TODO, this is present on trunk/node_flat but not on trunk/node_flat_odbc. %% check what is the desired behaviour %% Tr = fun(#pubsub_state{stateid = {J, _}, subscriptions = Ss}) -> %% lists:map(fun(S) -> @@ -1382,7 +1382,7 @@ i2l(L, N) when is_list(L) -> end. node_to_path(Node) -> - node_hometree:node_to_path(Node). + node_flat:node_to_path(Node). path_to_node(Path) -> - node_hometree:path_to_node(Path). + node_flat:path_to_node(Path). diff --git a/src/mod_pubsub/node_mb.erl b/src/mod_pubsub/node_mb.erl index 38af49bff..57d155558 100644 --- a/src/mod_pubsub/node_mb.erl +++ b/src/mod_pubsub/node_mb.erl @@ -177,7 +177,7 @@ set_subscriptions(NodeId, Owner, Subscription, SubId) -> node_pep:set_subscriptions(NodeId, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> - node_hometree:get_pending_nodes(Host, Owner). + node_flat:get_pending_nodes(Host, Owner). get_states(NodeId) -> node_pep:get_states(NodeId). diff --git a/src/mod_pubsub/node_pep.erl b/src/mod_pubsub/node_pep.erl index c552bc238..af862e1de 100644 --- a/src/mod_pubsub/node_pep.erl +++ b/src/mod_pubsub/node_pep.erl @@ -71,12 +71,12 @@ ]). init(Host, ServerHost, Opts) -> - node_hometree:init(Host, ServerHost, Opts), + node_flat:init(Host, ServerHost, Opts), complain_if_modcaps_disabled(ServerHost), ok. terminate(Host, ServerHost) -> - node_hometree:terminate(Host, ServerHost), + node_flat:terminate(Host, ServerHost), ok. options() -> @@ -139,40 +139,40 @@ create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> {result, Allowed}. create_node(NodeId, Owner) -> - case node_hometree:create_node(NodeId, Owner) of + case node_flat:create_node(NodeId, Owner) of {result, _} -> {result, []}; Error -> Error end. delete_node(Removed) -> - case node_hometree:delete_node(Removed) of + case node_flat:delete_node(Removed) of {result, {_, _, Removed}} -> {result, {[], Removed}}; Error -> Error end. subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> - node_hometree:subscribe_node( + node_flat:subscribe_node( NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> - case node_hometree:unsubscribe_node(NodeId, Sender, Subscriber, SubID) of + case node_flat:unsubscribe_node(NodeId, Sender, Subscriber, SubID) of {error, Error} -> {error, Error}; {result, _} -> {result, []} end. publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> - node_hometree:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). + node_flat:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). remove_extra_items(NodeId, MaxItems, ItemIds) -> - node_hometree:remove_extra_items(NodeId, MaxItems, ItemIds). + node_flat:remove_extra_items(NodeId, MaxItems, ItemIds). delete_item(NodeId, Publisher, PublishModel, ItemId) -> - node_hometree:delete_item(NodeId, Publisher, PublishModel, ItemId). + node_flat:delete_item(NodeId, Publisher, PublishModel, ItemId). purge_node(NodeId, Owner) -> - node_hometree:purge_node(NodeId, Owner). + node_flat:purge_node(NodeId, Owner). get_entity_affiliations(_Host, Owner) -> {_, D, _} = SubKey = jlib:jid_tolower(Owner), @@ -192,13 +192,13 @@ get_entity_affiliations(_Host, Owner) -> {result, Reply}. get_node_affiliations(NodeId) -> - node_hometree:get_node_affiliations(NodeId). + node_flat:get_node_affiliations(NodeId). get_affiliation(NodeId, Owner) -> - node_hometree:get_affiliation(NodeId, Owner). + node_flat:get_affiliation(NodeId, Owner). set_affiliation(NodeId, Owner, Affiliation) -> - node_hometree:set_affiliation(NodeId, Owner, Affiliation). + node_flat:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(_Host, Owner) -> {U, D, _} = SubKey = jlib:jid_tolower(Owner), @@ -236,43 +236,43 @@ get_node_subscriptions(NodeId) -> %% but that call returns also all subscription to none %% and this is required for broadcast to occurs %% DO NOT REMOVE - node_hometree:get_node_subscriptions(NodeId). + node_flat:get_node_subscriptions(NodeId). get_subscriptions(NodeId, Owner) -> - node_hometree:get_subscriptions(NodeId, Owner). + node_flat:get_subscriptions(NodeId, Owner). set_subscriptions(NodeId, Owner, Subscription, SubId) -> - node_hometree:set_subscriptions(NodeId, Owner, Subscription, SubId). + node_flat:set_subscriptions(NodeId, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> - node_hometree:get_pending_nodes(Host, Owner). + node_flat:get_pending_nodes(Host, Owner). get_states(NodeId) -> - node_hometree:get_states(NodeId). + node_flat:get_states(NodeId). get_state(NodeId, JID) -> - node_hometree:get_state(NodeId, JID). + node_flat:get_state(NodeId, JID). set_state(State) -> - node_hometree:set_state(State). + node_flat:set_state(State). get_items(NodeId, From) -> - node_hometree:get_items(NodeId, From). + node_flat:get_items(NodeId, From). get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + node_flat:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). get_item(NodeId, ItemId) -> - node_hometree:get_item(NodeId, ItemId). + node_flat:get_item(NodeId, ItemId). get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + node_flat:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> - node_hometree:set_item(Item). + node_flat:set_item(Item). get_item_name(Host, Node, Id) -> - node_hometree:get_item_name(Host, Node, Id). + node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat:node_to_path(Node). diff --git a/src/mod_pubsub/node_pep_odbc.erl b/src/mod_pubsub/node_pep_odbc.erl index ee10ed964..4155ba318 100644 --- a/src/mod_pubsub/node_pep_odbc.erl +++ b/src/mod_pubsub/node_pep_odbc.erl @@ -76,12 +76,12 @@ ]). init(Host, ServerHost, Opts) -> - node_hometree_odbc:init(Host, ServerHost, Opts), + node_flat_odbc:init(Host, ServerHost, Opts), complain_if_modcaps_disabled(ServerHost), ok. terminate(Host, ServerHost) -> - node_hometree_odbc:terminate(Host, ServerHost), + node_flat_odbc:terminate(Host, ServerHost), ok. options() -> @@ -146,60 +146,60 @@ create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> {result, Allowed}. create_node(NodeId, Owner) -> - case node_hometree_odbc:create_node(NodeId, Owner) of + case node_flat_odbc:create_node(NodeId, Owner) of {result, _} -> {result, []}; Error -> Error end. delete_node(Removed) -> - case node_hometree_odbc:delete_node(Removed) of + case node_flat_odbc:delete_node(Removed) of {result, {_, _, Removed}} -> {result, {[], Removed}}; Error -> Error end. subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> - node_hometree_odbc:subscribe_node( + node_flat_odbc:subscribe_node( NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> - case node_hometree_odbc:unsubscribe_node(NodeId, Sender, Subscriber, SubID) of + case node_flat_odbc:unsubscribe_node(NodeId, Sender, Subscriber, SubID) of {error, Error} -> {error, Error}; {result, _} -> {result, []} end. publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> - node_hometree_odbc:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). + node_flat_odbc:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). remove_extra_items(NodeId, MaxItems, ItemIds) -> - node_hometree_odbc:remove_extra_items(NodeId, MaxItems, ItemIds). + node_flat_odbc:remove_extra_items(NodeId, MaxItems, ItemIds). delete_item(NodeId, Publisher, PublishModel, ItemId) -> - node_hometree_odbc:delete_item(NodeId, Publisher, PublishModel, ItemId). + node_flat_odbc:delete_item(NodeId, Publisher, PublishModel, ItemId). purge_node(NodeId, Owner) -> - node_hometree_odbc:purge_node(NodeId, Owner). + node_flat_odbc:purge_node(NodeId, Owner). get_entity_affiliations(_Host, Owner) -> OwnerKey = jlib:short_prepd_bare_jid(Owner), - node_hometree_odbc:get_entity_affiliations(OwnerKey, Owner). + node_flat_odbc:get_entity_affiliations(OwnerKey, Owner). get_node_affiliations(NodeId) -> - node_hometree_odbc:get_node_affiliations(NodeId). + node_flat_odbc:get_node_affiliations(NodeId). get_affiliation(NodeId, Owner) -> - node_hometree_odbc:get_affiliation(NodeId, Owner). + node_flat_odbc:get_affiliation(NodeId, Owner). set_affiliation(NodeId, Owner, Affiliation) -> - node_hometree_odbc:set_affiliation(NodeId, Owner, Affiliation). + node_flat_odbc:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(_Host, Owner) -> SubKey = jlib:short_prepd_jid(Owner), GenKey = jlib:short_prepd_bare_jid(SubKey), Host = ?PUBSUB:escape(element(2, SubKey)), - SJ = node_hometree_odbc:encode_jid(SubKey), - GJ = node_hometree_odbc:encode_jid(GenKey), + SJ = node_flat_odbc:encode_jid(SubKey), + GJ = node_flat_odbc:encode_jid(GenKey), Query = case SubKey of GenKey -> ["select host, node, type, i.nodeid, jid, subscription " @@ -217,9 +217,9 @@ get_entity_subscriptions(_Host, Owner) -> Reply = case catch ejabberd_odbc:sql_query_t(Query) of {selected, ["host", "node", "type", "nodeid", "jid", "subscription"], RItems} -> lists:map(fun({H, N, T, I, J, S}) -> - O = node_hometree_odbc:decode_jid(H), + O = node_flat_odbc:decode_jid(H), Node = nodetree_odbc:raw_to_node(O, {N, "", T, I}), - {Node, node_hometree_odbc:decode_subscription(S), node_hometree_odbc:decode_jid(J)} + {Node, node_flat_odbc:decode_subscription(S), node_flat_odbc:decode_jid(J)} end, RItems); _ -> [] @@ -229,8 +229,8 @@ get_entity_subscriptions_for_send_last(_Host, Owner) -> SubKey = jlib:short_prepd_jid(Owner), GenKey = jlib:short_prepd_bare_jid(SubKey), Host = ?PUBSUB:escape(element(2, SubKey)), - SJ = node_hometree_odbc:encode_jid(SubKey), - GJ = node_hometree_odbc:encode_jid(GenKey), + SJ = node_flat_odbc:encode_jid(SubKey), + GJ = node_flat_odbc:encode_jid(GenKey), Query = case SubKey of GenKey -> ["select host, node, type, i.nodeid, jid, subscription " @@ -250,9 +250,9 @@ get_entity_subscriptions_for_send_last(_Host, Owner) -> Reply = case catch ejabberd_odbc:sql_query_t(Query) of {selected, ["host", "node", "type", "nodeid", "jid", "subscription"], RItems} -> lists:map(fun({H, N, T, I, J, S}) -> - O = node_hometree_odbc:decode_jid(H), + O = node_flat_odbc:decode_jid(H), Node = nodetree_odbc:raw_to_node(O, {N, "", T, I}), - {Node, node_hometree_odbc:decode_subscription(S), node_hometree_odbc:decode_jid(J)} + {Node, node_flat_odbc:decode_subscription(S), node_flat_odbc:decode_jid(J)} end, RItems); _ -> [] @@ -266,49 +266,49 @@ get_node_subscriptions(NodeId) -> %% but that call returns also all subscription to none %% and this is required for broadcast to occurs %% DO NOT REMOVE - node_hometree_odbc:get_node_subscriptions(NodeId). + node_flat_odbc:get_node_subscriptions(NodeId). get_subscriptions(NodeId, Owner) -> - node_hometree_odbc:get_subscriptions(NodeId, Owner). + node_flat_odbc:get_subscriptions(NodeId, Owner). set_subscriptions(NodeId, Owner, Subscription, SubId) -> - node_hometree_odbc:set_subscriptions(NodeId, Owner, Subscription, SubId). + node_flat_odbc:set_subscriptions(NodeId, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> - node_hometree_odbc:get_pending_nodes(Host, Owner). + node_flat_odbc:get_pending_nodes(Host, Owner). get_states(NodeId) -> - node_hometree_odbc:get_states(NodeId). + node_flat_odbc:get_states(NodeId). get_state(NodeId, JID) -> - node_hometree_odbc:get_state(NodeId, JID). + node_flat_odbc:get_state(NodeId, JID). set_state(State) -> - node_hometree_odbc:set_state(State). + node_flat_odbc:set_state(State). get_items(NodeId, From) -> - node_hometree_odbc:get_items(NodeId, From). + node_flat_odbc:get_items(NodeId, From). get_items(NodeId, From, RSM) -> - node_hometree_odbc:get_items(NodeId, From, RSM). + node_flat_odbc:get_items(NodeId, From, RSM). get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, none). get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) -> - node_hometree_odbc:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). + node_flat_odbc:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM). get_last_items(NodeId, JID, Count) -> - node_hometree_odbc:get_last_items(NodeId, JID, Count). + node_flat_odbc:get_last_items(NodeId, JID, Count). get_item(NodeId, ItemId) -> - node_hometree_odbc:get_item(NodeId, ItemId). + node_flat_odbc:get_item(NodeId, ItemId). get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree_odbc:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + node_flat_odbc:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> - node_hometree_odbc:set_item(Item). + node_flat_odbc:set_item(Item). get_item_name(Host, Node, Id) -> - node_hometree_odbc:get_item_name(Host, Node, Id). + node_flat_odbc:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat_odbc:node_to_path(Node). diff --git a/src/mod_pubsub/node_private.erl b/src/mod_pubsub/node_private.erl index 5cbc22604..cbde859e1 100644 --- a/src/mod_pubsub/node_private.erl +++ b/src/mod_pubsub/node_private.erl @@ -37,7 +37,7 @@ %% it's possible not to define some function at all %% in that case, warning will be generated at compilation %% and function call will fail, -%% then mod_pubsub will call function from node_hometree +%% then mod_pubsub will call function from node_flat %% (this makes code cleaner, but execution a little bit longer) %% API definition @@ -76,10 +76,10 @@ init(Host, ServerHost, Opts) -> - node_hometree:init(Host, ServerHost, Opts). + node_flat:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> - node_hometree:terminate(Host, ServerHost). + node_flat:terminate(Host, ServerHost). options() -> [{deliver_payloads, true}, @@ -118,89 +118,89 @@ features() -> ]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> - node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, + node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(NodeId, Owner) -> - node_hometree:create_node(NodeId, Owner). + node_flat:create_node(NodeId, Owner). delete_node(Removed) -> - node_hometree:delete_node(Removed). + node_flat:delete_node(Removed). subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> - node_hometree:subscribe_node(NodeId, Sender, Subscriber, AccessModel, + node_flat:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> - node_hometree:unsubscribe_node(NodeId, Sender, Subscriber, SubID). + node_flat:unsubscribe_node(NodeId, Sender, Subscriber, SubID). publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> - node_hometree:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). + node_flat:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). remove_extra_items(NodeId, MaxItems, ItemIds) -> - node_hometree:remove_extra_items(NodeId, MaxItems, ItemIds). + node_flat:remove_extra_items(NodeId, MaxItems, ItemIds). delete_item(NodeId, Publisher, PublishModel, ItemId) -> - node_hometree:delete_item(NodeId, Publisher, PublishModel, ItemId). + node_flat:delete_item(NodeId, Publisher, PublishModel, ItemId). purge_node(NodeId, Owner) -> - node_hometree:purge_node(NodeId, Owner). + node_flat:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> - node_hometree:get_entity_affiliations(Host, Owner). + node_flat:get_entity_affiliations(Host, Owner). get_node_affiliations(NodeId) -> - node_hometree:get_node_affiliations(NodeId). + node_flat:get_node_affiliations(NodeId). get_affiliation(NodeId, Owner) -> - node_hometree:get_affiliation(NodeId, Owner). + node_flat:get_affiliation(NodeId, Owner). set_affiliation(NodeId, Owner, Affiliation) -> - node_hometree:set_affiliation(NodeId, Owner, Affiliation). + node_flat:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> - node_hometree:get_entity_subscriptions(Host, Owner). + node_flat:get_entity_subscriptions(Host, Owner). get_node_subscriptions(NodeId) -> - node_hometree:get_node_subscriptions(NodeId). + node_flat:get_node_subscriptions(NodeId). get_subscriptions(NodeId, Owner) -> - node_hometree:get_subscriptions(NodeId, Owner). + node_flat:get_subscriptions(NodeId, Owner). set_subscriptions(NodeId, Owner, Subscription, SubId) -> - node_hometree:set_subscriptions(NodeId, Owner, Subscription, SubId). + node_flat:set_subscriptions(NodeId, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> - node_hometree:get_pending_nodes(Host, Owner). + node_flat:get_pending_nodes(Host, Owner). get_states(NodeId) -> - node_hometree:get_states(NodeId). + node_flat:get_states(NodeId). get_state(NodeId, JID) -> - node_hometree:get_state(NodeId, JID). + node_flat:get_state(NodeId, JID). set_state(State) -> - node_hometree:set_state(State). + node_flat:set_state(State). get_items(NodeId, From) -> - node_hometree:get_items(NodeId, From). + node_flat:get_items(NodeId, From). get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + node_flat:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). get_item(NodeId, ItemId) -> - node_hometree:get_item(NodeId, ItemId). + node_flat:get_item(NodeId, ItemId). get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + node_flat:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> - node_hometree:set_item(Item). + node_flat:set_item(Item). get_item_name(Host, Node, Id) -> - node_hometree:get_item_name(Host, Node, Id). + node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat:node_to_path(Node). diff --git a/src/mod_pubsub/node_public.erl b/src/mod_pubsub/node_public.erl index ec75c67d8..6063bc17d 100644 --- a/src/mod_pubsub/node_public.erl +++ b/src/mod_pubsub/node_public.erl @@ -37,7 +37,7 @@ %% it's possible not to define some function at all %% in that case, warning will be generated at compilation %% and function call will fail, -%% then mod_pubsub will call function from node_hometree +%% then mod_pubsub will call function from node_flat %% (this makes code cleaner, but execution a little bit longer) %% API definition @@ -76,10 +76,10 @@ init(Host, ServerHost, Opts) -> - node_hometree:init(Host, ServerHost, Opts). + node_flat:init(Host, ServerHost, Opts). terminate(Host, ServerHost) -> - node_hometree:terminate(Host, ServerHost). + node_flat:terminate(Host, ServerHost). options() -> [{node_type, public}, @@ -119,87 +119,87 @@ features() -> ]. create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> - node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). + node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). create_node(NodeId, Owner) -> - node_hometree:create_node(NodeId, Owner). + node_flat:create_node(NodeId, Owner). delete_node(Removed) -> - node_hometree:delete_node(Removed). + node_flat:delete_node(Removed). subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> - node_hometree:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). + node_flat:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options). unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> - node_hometree:unsubscribe_node(NodeId, Sender, Subscriber, SubID). + node_flat:unsubscribe_node(NodeId, Sender, Subscriber, SubID). publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> - node_hometree:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). + node_flat:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). remove_extra_items(NodeId, MaxItems, ItemIds) -> - node_hometree:remove_extra_items(NodeId, MaxItems, ItemIds). + node_flat:remove_extra_items(NodeId, MaxItems, ItemIds). delete_item(NodeId, Publisher, PublishModel, ItemId) -> - node_hometree:delete_item(NodeId, Publisher, PublishModel, ItemId). + node_flat:delete_item(NodeId, Publisher, PublishModel, ItemId). purge_node(NodeId, Owner) -> - node_hometree:purge_node(NodeId, Owner). + node_flat:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> - node_hometree:get_entity_affiliations(Host, Owner). + node_flat:get_entity_affiliations(Host, Owner). get_node_affiliations(NodeId) -> - node_hometree:get_node_affiliations(NodeId). + node_flat:get_node_affiliations(NodeId). get_affiliation(NodeId, Owner) -> - node_hometree:get_affiliation(NodeId, Owner). + node_flat:get_affiliation(NodeId, Owner). set_affiliation(NodeId, Owner, Affiliation) -> - node_hometree:set_affiliation(NodeId, Owner, Affiliation). + node_flat:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> - node_hometree:get_entity_subscriptions(Host, Owner). + node_flat:get_entity_subscriptions(Host, Owner). get_node_subscriptions(NodeId) -> - node_hometree:get_node_subscriptions(NodeId). + node_flat:get_node_subscriptions(NodeId). get_subscriptions(NodeId, Owner) -> - node_hometree:get_subscriptions(NodeId, Owner). + node_flat:get_subscriptions(NodeId, Owner). set_subscriptions(NodeId, Owner, Subscription, SubId) -> - node_hometree:set_subscription(NodeId, Owner, Subscription, SubId). + node_flat:set_subscription(NodeId, Owner, Subscription, SubId). get_pending_nodes(Host, Owner) -> - node_hometree:get_pending_nodes(Host, Owner). + node_flat:get_pending_nodes(Host, Owner). get_states(NodeId) -> - node_hometree:get_states(NodeId). + node_flat:get_states(NodeId). get_state(NodeId, JID) -> - node_hometree:get_state(NodeId, JID). + node_flat:get_state(NodeId, JID). set_state(State) -> - node_hometree:set_state(State). + node_flat:set_state(State). get_items(NodeId, From) -> - node_hometree:get_items(NodeId, From). + node_flat:get_items(NodeId, From). get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + node_flat:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). get_item(NodeId, ItemId) -> - node_hometree:get_item(NodeId, ItemId). + node_flat:get_item(NodeId, ItemId). get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). + node_flat:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> - node_hometree:set_item(Item). + node_flat:set_item(Item). %% @docReturn the name of the node if known: Default is to return %% node id.
get_item_name(Host, Node, Id) -> - node_hometree:get_item_name(Host, Node, Id). + node_flat:get_item_name(Host, Node, Id). node_to_path(Node) -> node_flat:node_to_path(Node). diff --git a/src/mod_pubsub/nodetree_tree_odbc.erl b/src/mod_pubsub/nodetree_tree_odbc.erl index 6f037c2c1..55c611345 100644 --- a/src/mod_pubsub/nodetree_tree_odbc.erl +++ b/src/mod_pubsub/nodetree_tree_odbc.erl @@ -366,7 +366,7 @@ nodeid(Host, Node) -> end. nodeowners(NodeId) -> - {result, Res} = node_hometree_odbc:get_node_affiliations(NodeId), + {result, Res} = node_flat_odbc:get_node_affiliations(NodeId), lists:foldl(fun({LJID, owner}, Acc) -> [LJID|Acc]; (_, Acc) -> Acc end, [], Res).