diff --git a/ChangeLog b/ChangeLog index 47f9e6eec..964d5a689 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2009-04-30 Christophe Romain + + * src/mod_pubsub/mod_pubsub.erl: API change for major optimization + * src/mod_pubsub/pubsub.hrl: Likewise + * src/mod_pubsub/nodetree_default.erl: Likewise + * src/mod_pubsub/nodetree_virtual.erl: Likewise + * src/mod_pubsub/node_mb.erl: Likewise + * src/mod_pubsub/node_dispatch.erl: Likewise + * src/mod_pubsub/node_buddy.erl: Likewise + * src/mod_pubsub/node_private.erl: Likewise + * src/mod_pubsub/node_public.erl: Likewise + * src/mod_pubsub/node_default.erl: Likewise + * src/mod_pubsub/node_pep.erl: Likewise + * src/mod_pubsub/node_club.erl: Likewise + * src/mod_pubsub/node_flat.erl: Likewise + * src/mod_pubsub/node.template: Likewise + * src/mod_pubsub/gen_pubsub_node.erl: Likewise + * src/mod_pubsub/gen_pubsub_nodetree.erl: Likewise + + * src/mod_caps.erl: Reduce memory consumption and remove mnesia table + 2009-04-28 Badlop * src/ejabberd_hooks.erl: Support distributed hooks (EJAB-829) diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 536e103a2..3b95cfd22 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -22,6 +22,8 @@ %%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA %%% 02111-1307 USA %%% +%%% 2009, improvements from Process-One to support correct PEP handling +%%% through s2s, use less memory, and speedup global caps handling %%%---------------------------------------------------------------------- -module(mod_caps). @@ -64,7 +66,7 @@ -define(CAPS_QUERY_TIMEOUT, 60000). % 1mn without answer, consider client never answer -record(caps, {node, version, exts}). --record(caps_features, {node_pair, features}). +-record(caps_features, {node_pair, features = []}). -record(user_caps, {jid, caps}). -record(user_caps_resources, {uid, resource}). -record(state, {host, @@ -190,19 +192,32 @@ receive_packet(_, _, _) -> receive_packet(_JID, From, To, Packet) -> receive_packet(From, To, Packet). +jid_to_binary(JID) -> + list_to_binary(jlib:jid_to_string(JID)). + +caps_to_binary(#caps{node = Node, version = Version, exts = Exts}) -> + BExts = [list_to_binary(Ext) || Ext <- Exts], + #caps{node = list_to_binary(Node), version = list_to_binary(Version), exts = BExts}. + +node_to_binary(Node, SubNode) -> + {list_to_binary(Node), list_to_binary(SubNode)}. + +features_to_binary(L) -> [list_to_binary(I) || I <- L]. +binary_to_features(L) -> [binary_to_list(I) || I <- L]. + %%==================================================================== %% gen_server callbacks %%==================================================================== init([Host, _Opts]) -> mnesia:create_table(caps_features, - [{ram_copies, [node()]}, + [{disc_copies, [node()]}, {attributes, record_info(fields, caps_features)}]), mnesia:create_table(user_caps, - [{disc_copies, [node()]}, + [{ram_copies, [node()]}, {attributes, record_info(fields, user_caps)}]), mnesia:create_table(user_caps_resources, - [{disc_copies, [node()]}, + [{ram_copies, [node()]}, {type, bag}, {attributes, record_info(fields, user_caps_resources)}]), mnesia:delete_table(user_caps_default), @@ -214,26 +229,21 @@ init([Host, _Opts]) -> maybe_get_features(#caps{node = Node, version = Version, exts = Exts}) -> SubNodes = [Version | Exts], - F = fun() -> - %% Make sure that we have all nodes we need to know. - %% If a single one is missing, we wait for more disco - %% responses. - lists:foldl(fun(SubNode, Acc) -> - case Acc of - fail -> fail; - _ -> - case mnesia:read({caps_features, {Node, SubNode}}) of - [] -> fail; - [#caps_features{features = Features}] -> Features ++ Acc - end - end - end, [], SubNodes) - end, - case mnesia:transaction(F) of - {atomic, fail} -> - wait; - {atomic, Features} -> - {ok, Features} + %% Make sure that we have all nodes we need to know. + %% If a single one is missing, we wait for more disco + %% responses. + case lists:foldl(fun(SubNode, Acc) -> + case Acc of + fail -> fail; + _ -> + case mnesia:dirty_read({caps_features, {Node, SubNode}}) of + [] -> fail; + [#caps_features{features = Features}] -> Features ++ Acc %% TODO binary + end + end + end, [], SubNodes) of + fail -> wait; + Features -> {ok, Features} end. timestamp() -> @@ -243,7 +253,7 @@ timestamp() -> handle_call({get_features, Caps}, From, State) -> case maybe_get_features(Caps) of {ok, Features} -> - {reply, Features, State}; + {reply, binary_to_features(Features), State}; wait -> gen_server:cast(self(), visit_feature_queries), Timeout = timestamp() + 10, @@ -266,7 +276,7 @@ handle_cast({note_caps, From, S = exmpp_jid:ldomain(From), R = exmpp_jid:resource(From), BJID = exmpp_jid:jid_to_binary(From), - mnesia:dirty_write(#user_caps{jid = BJID, caps = Caps}), + mnesia:dirty_write(#user_caps{jid = BJID, caps = caps_to_binary(Caps)}), case ejabberd_sm:get_user_resources(U, S) of [] -> % only store resource of caps aware external contacts @@ -275,20 +285,19 @@ handle_cast({note_caps, From, _ -> ok end, - SubNodes = [Version | Exts], %% Now, find which of these are not already in the database. - Fun = fun() -> - lists:foldl(fun(SubNode, Acc) -> - case mnesia:read({caps_features, {Node, SubNode}}) of - [] -> - [SubNode | Acc]; - _ -> - Acc - end - end, [], SubNodes) - end, - case mnesia:transaction(Fun) of - {atomic, Missing} -> + SubNodes = [Version | Exts], + case lists:foldl(fun(SubNode, Acc) -> + case mnesia:dirty_read({caps_features, {Node, SubNode}}) of + [] -> + [SubNode | Acc]; + _ -> + Acc + end + end, [], SubNodes) of + [] -> + {noreply, State}; + Missing -> %% For each unknown caps "subnode", we send a disco request. NewRequests = lists:foldl( fun(SubNode, Dict) -> @@ -302,30 +311,23 @@ handle_cast({note_caps, From, ejabberd_router:route(exmpp_jid:make_jid(Host), From, Stanza), timer:send_after(?CAPS_QUERY_TIMEOUT, self(), {disco_timeout, ID}), - ?DICT:store(ID, {Node, SubNode}, Dict) + ?DICT:store(ID, node_to_binary(Node, SubNode), Dict) end, Requests, Missing), - {noreply, State#state{disco_requests = NewRequests}}; - Error -> - ?ERROR_MSG("Transaction failed: ~p", [Error]), - {noreply, State} + {noreply, State#state{disco_requests = NewRequests}} end; handle_cast({disco_response, From, _To, #iq{id = ID, type = Type, payload = Payload}}, #state{disco_requests = Requests} = State) -> case {Type, Payload} of {result, #xmlel{name = 'query', children = Els}} -> case ?DICT:find(ID, Requests) of - {ok, {Node, SubNode}} -> + {ok, BinaryNode} -> Features = lists:flatmap(fun(#xmlel{name = 'feature'} = F) -> [exmpp_xml:get_attribute_as_list(F, 'var', "")]; (_) -> [] end, Els), - mnesia:transaction( - fun() -> - mnesia:write(#caps_features{node_pair = {Node, SubNode}, - features = Features}) - end), + mnesia:dirty_write(#caps_features{node_pair = BinaryNode, features = features_to_binary(Features)}), gen_server:cast(self(), visit_feature_queries); error -> ?ERROR_MSG("ID '~s' matches no query", [ID]) @@ -333,13 +335,8 @@ handle_cast({disco_response, From, _To, #iq{id = ID, type = Type, payload = Payl {error, _} -> %% XXX: if we get error, we cache empty feature not to probe the client continuously case ?DICT:find(ID, Requests) of - {ok, {Node, SubNode}} -> - Features = [], - mnesia:transaction( - fun() -> - mnesia:write(#caps_features{node_pair = {Node, SubNode}, - features = Features}) - end), + {ok, BinaryNode} -> + mnesia:dirty_write(#caps_features{node_pair = BinaryNode}), gen_server:cast(self(), visit_feature_queries); error -> ?ERROR_MSG("ID '~s' matches no query", [ID]) diff --git a/src/mod_pubsub/gen_pubsub_node.erl b/src/mod_pubsub/gen_pubsub_node.erl index 91b8163bc..b4264f809 100644 --- a/src/mod_pubsub/gen_pubsub_node.erl +++ b/src/mod_pubsub/gen_pubsub_node.erl @@ -43,29 +43,29 @@ behaviour_info(callbacks) -> {options, 0}, {features, 0}, {create_node_permission, 6}, - {create_node, 3}, - {delete_node, 2}, - {purge_node, 3}, - {subscribe_node, 8}, - {unsubscribe_node, 5}, - {publish_item, 7}, - {delete_item, 4}, - {remove_extra_items, 4}, - {get_node_affiliations, 2}, + {create_node, 2}, + {delete_node, 1}, + {purge_node, 2}, + {subscribe_node, 7}, + {unsubscribe_node, 4}, + {publish_item, 6}, + {delete_item, 3}, + {remove_extra_items, 3}, + {get_node_affiliations, 1}, {get_entity_affiliations, 2}, - {get_affiliation, 3}, - {set_affiliation, 4}, - {get_node_subscriptions, 2}, + {get_affiliation, 2}, + {set_affiliation, 3}, + {get_node_subscriptions, 1}, {get_entity_subscriptions, 2}, - {get_subscription, 3}, - {set_subscription, 4}, - {get_states, 2}, - {get_state, 3}, + {get_subscription, 2}, + {set_subscription, 3}, + {get_states, 1}, + {get_state, 2}, {set_state, 1}, - {get_items, 7}, - {get_items, 3}, - {get_item, 8}, - {get_item, 3}, + {get_items, 6}, + {get_items, 2}, + {get_item, 7}, + {get_item, 2}, {set_item, 1}, {get_item_name, 3} ]; diff --git a/src/mod_pubsub/gen_pubsub_nodetree.erl b/src/mod_pubsub/gen_pubsub_nodetree.erl index 8bd35f51d..941748e68 100644 --- a/src/mod_pubsub/gen_pubsub_nodetree.erl +++ b/src/mod_pubsub/gen_pubsub_nodetree.erl @@ -47,7 +47,7 @@ behaviour_info(callbacks) -> {get_nodes, 2}, {get_nodes, 1}, {get_subnodes, 3}, - {get_subnodes_tree, 2}, + {get_subnodes_tree, 3}, {create_node, 5}, {delete_node, 2} ]; diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 21042fcfe..45c8212a6 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -39,7 +39,7 @@ -module(mod_pubsub). -author('christophe.romain@process-one.net'). --version('1.12-04'). +-version('1.12-05'). -behaviour(gen_server). -behaviour(gen_mod). @@ -76,13 +76,11 @@ unsubscribe_node/5, publish_item/6, delete_item/4, - send_items/4, - broadcast_stanza/6, + send_items/6, + broadcast_stanza/7, get_configure/5, set_configure/5, - get_items/3, tree_action/3, - node_action/3, node_action/4 ]). @@ -167,8 +165,9 @@ init([ServerHost, Opts]) -> PepOffline = gen_mod:get_opt(pep_sendlast_offline, Opts, false), IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), ServerHostB = list_to_binary(ServerHost), - mod_disco:register_feature(ServerHost, ?NS_PUBSUB_s), + pubsub_index:init(Host, ServerHost, Opts), {Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts), + mod_disco:register_feature(ServerHost, ?NS_PUBSUB_s), ets:new(gen_mod:get_module_proc(Host, pubsub_state), [set, named_table]), ets:insert(gen_mod:get_module_proc(Host, pubsub_state), {nodetree, NodeTree}), ets:insert(gen_mod:get_module_proc(Host, pubsub_state), {plugins, Plugins}), @@ -195,7 +194,7 @@ init([ServerHost, Opts]) -> false -> ok end, - update_database(Host), + update_database(Host, ServerHost), init_nodes(Host, ServerHost), State = #state{host = Host, server_host = ServerHost, @@ -204,7 +203,7 @@ init([ServerHost, Opts]) -> pep_sendlast_offline = PepOffline, nodetree = NodeTree, plugins = Plugins}, - SendLoop = spawn(?MODULE, send_loop, [State]), %% TODO supervise that process + SendLoop = spawn(?MODULE, send_loop, [State]), {ok, State#state{send_loop = SendLoop}}. %% @spec (Host, ServerHost, Opts) -> Plugins @@ -249,7 +248,7 @@ init_nodes(Host, ServerHost) -> create_node(Host, ServerHost, ["home", ServerHost], service_jid(Host), ?STDNODE), ok. -update_database(Host) -> +update_database(Host, ServerHost) -> mnesia:del_table_index(pubsub_node, type), mnesia:del_table_index(pubsub_node, parentid), case catch mnesia:table_info(pubsub_node, attributes) of @@ -257,14 +256,14 @@ update_database(Host) -> ?INFO_MSG("upgrade pubsub tables",[]), F = fun() -> lists:foldl( - fun({pubsub_node, NodeId, ParentId, {nodeinfo, Items, Options, Entities}}, RecList) -> + fun({pubsub_node, NodeId, ParentId, {nodeinfo, Items, Options, Entities}}, {RecList, NodeIdx}) -> ItemsList = lists:foldl( fun({item, IID, Publisher, Payload}, Acc) -> - C = {Publisher, unknown}, - M = {Publisher, now()}, + C = {unknown, Publisher}, + M = {now(), Publisher}, mnesia:write( - #pubsub_item{itemid = {IID, NodeId}, + #pubsub_item{itemid = {IID, NodeIdx}, creation = C, modification = M, payload = Payload}), @@ -282,7 +281,7 @@ update_database(Host) -> end end, [], ItemsList), mnesia:write( - #pubsub_state{stateid = {JID, NodeId}, + #pubsub_state{stateid = {JID, NodeIdx}, items = UsrItems, affiliation = Aff, subscription = Sub}), @@ -292,12 +291,13 @@ update_database(Host) -> end end, [], Entities), mnesia:delete({pubsub_node, NodeId}), - [#pubsub_node{nodeid = NodeId, - parentid = ParentId, + {[#pubsub_node{nodeid = NodeId, + id = NodeIdx, + parent = element(2, ParentId), owners = Owners, options = Options} | - RecList] - end, [], + RecList], NodeIdx + 1} + end, {[], 1}, mnesia:match_object( {pubsub_node, {Host, '_'}, '_', '_'})) end, @@ -316,10 +316,62 @@ update_database(Host) -> {aborted, Reason} -> ?ERROR_MSG("Problem updating Pubsub tables:~n~p", [Reason]) end; + [nodeid, parentid, type, owners, options] -> + F = fun({pubsub_node, NodeId, {_, Parent}, Type, Owners, Options}) -> + #pubsub_node{ + nodeid = NodeId, + id = 0, + parent = Parent, + type = Type, + owners = Owners, + options = Options} + end, + mnesia:transform_table(pubsub_node, F, [nodeid, id, parent, type, owners, options]), + FNew = fun() -> + lists:foldl(fun(#pubsub_node{nodeid = NodeId} = PubsubNode, NodeIdx) -> + mnesia:write(PubsubNode#pubsub_node{id = NodeIdx}), + lists:foreach(fun(#pubsub_state{stateid = StateId} = State) -> + {JID, _} = StateId, + mnesia:delete({pubsub_state, StateId}), + mnesia:write(State#pubsub_state{stateid = {JID, NodeIdx}}) + end, mnesia:match_object(#pubsub_state{stateid = {'_', NodeId}, _ = '_'})), + lists:foreach(fun(#pubsub_item{itemid = ItemId} = Item) -> + {IID, _} = ItemId, + {M1, M2} = Item#pubsub_item.modification, + {C1, C2} = Item#pubsub_item.creation, + mnesia:delete({pubsub_item, ItemId}), + mnesia:write(Item#pubsub_item{itemid = {IID, NodeIdx}, + modification = {M2, M1}, + creation = {C2, C1}}) + end, mnesia:match_object(#pubsub_item{itemid = {'_', NodeId}, _ = '_'})), + NodeIdx + 1 + end, 1, mnesia:match_object( + {pubsub_node, {Host, '_'}, '_', '_', '_', '_', '_'}) + ++ mnesia:match_object( + {pubsub_node, {{'_', ServerHost, '_'}, '_'}, '_', '_', '_', '_', '_'})) + end, + case mnesia:transaction(FNew) of + {atomic, Result} -> + ?INFO_MSG("Pubsub tables updated correctly: ~p", [Result]); + {aborted, Reason} -> + ?ERROR_MSG("Problem updating Pubsub tables:~n~p", [Reason]) + end; _ -> ok end. +send_queue(State, Msg) -> + Pid = State#state.send_loop, + case is_process_alive(Pid) of + true -> + Pid ! Msg, + State; + false -> + SendLoop = spawn(?MODULE, send_loop, [State]), + SendLoop ! Msg, + State#state{send_loop = SendLoop} + end. + send_loop(State) -> receive {presence, JID, Pid} -> @@ -329,16 +381,16 @@ send_loop(State) -> BJID = jlib:short_prepd_bare_jid(JID), %% for each node From is subscribed to %% and if the node is so configured, send the last published item to From - lists:foreach(fun(Type) -> - {result, Subscriptions} = node_action(Type, get_entity_subscriptions, [Host, JID]), + lists:foreach(fun(PType) -> + {result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, JID]), lists:foreach( fun({Node, subscribed, SubJID}) -> if (SubJID == LJID) or (SubJID == BJID) -> case tree_action(Host, get_node, [Host, Node, JID]) of - #pubsub_node{options = Options} -> + #pubsub_node{options = Options, type = Type, id = NodeId} -> case get_option(Options, send_last_published_item) of on_sub_and_presence -> - send_items(Host, Node, SubJID, last); + send_items(Host, Node, NodeId, Type, SubJID, last); _ -> ok end; @@ -392,7 +444,7 @@ send_loop(State) -> Owner = jlib:short_prepd_bare_jid(JID), Host = State#state.host, ServerHost = State#state.server_host, - lists:foreach(fun(#pubsub_node{nodeid = {_, Node}, options = Options}) -> + lists:foreach(fun(#pubsub_node{nodeid = {_, Node}, type = Type, id = NodeId, options = Options}) -> case get_option(Options, send_last_published_item) of on_sub_and_presence -> lists:foreach(fun(Resource) -> @@ -410,7 +462,7 @@ send_loop(State) -> element(2, get_roster_info(OU, OS, LJID, Grps)) end, if Subscribed -> - send_items(Owner, Node, LJID, last); + send_items(Owner, Node, NodeId, Type, LJID, last); true -> ok end; @@ -496,7 +548,7 @@ disco_sm_items(Acc, From, To, <<>>, _Lang) -> _ -> [] end, NodeItems = lists:map( - fun(Node) -> + fun(#pubsub_node{nodeid = {_, Node}}) -> #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs = [?XMLATTR('jid', exmpp_jid:jid_to_binary(LJID)), ?XMLATTR('node', node_to_string(Node))]} @@ -509,24 +561,32 @@ disco_sm_items(Acc, From, To, NodeB, _Lang) -> %% TODO, use iq_disco_items(Host, Node, From) Host = exmpp_jid:ldomain_as_list(To), LJID = jlib:short_prepd_bare_jid(To), - case get_items(Host, Node, From) of - [] -> - Acc; - AllItems -> - Items = case Acc of + Action = fun(#pubsub_node{type = Type, id = NodeId}) -> + case node_call(Type, get_items, [NodeId, From]) of + {result, []} -> + none; + {result, AllItems} -> + Items = case Acc of {result, I} -> I; _ -> [] end, - NodeItems = lists:map( - fun(#pubsub_item{itemid = Id}) -> + NodeItems = lists:map( + fun(#pubsub_item{itemid = {Id, _}}) -> %% "jid" is required by XEP-0030, and %% "node" is forbidden by XEP-0060. - {result, Name} = node_action(Host, Node, get_item_name, [Host, Node, Id]), + {result, Name} = node_action(Host, Node, get_item_name, [NodeId, Id]), #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs = [?XMLATTR('jid', exmpp_jid:jid_to_binary(LJID)), ?XMLATTR('name', Name)]} end, AllItems), - {result, NodeItems ++ Items} + {result, NodeItems ++ Items}; + _ -> + none + end + end, + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, Items}} -> {result, Items}; + _ -> Acc end. %% ------- @@ -602,20 +662,18 @@ handle_call(stop, _From, State) -> %% @private handle_cast({presence, JID, Pid}, State) -> %% A new resource is available. send last published items - State#state.send_loop ! {presence, JID, Pid}, - {noreply, State}; + {noreply, send_queue(State, {presence, JID, Pid})}; handle_cast({presence, User, Server, Resources, JID}, State) -> %% A new resource is available. send last published PEP items - State#state.send_loop ! {presence, User, Server, Resources, JID}, - {noreply, State}; + {noreply, send_queue(State, {presence, User, Server, Resources, JID})}; handle_cast({remove_user, LUser, LServer}, State) -> Host = State#state.host, Owner = exmpp_jid:make_jid(LUser, LServer), %% remove user's subscriptions - lists:foreach(fun(Type) -> - {result, Subscriptions} = node_action(Type, get_entity_subscriptions, [Host, Owner]), + lists:foreach(fun(PType) -> + {result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Owner]), lists:foreach(fun ({Node, subscribed, JID}) -> unsubscribe_node(Host, Node, Owner, JID, all); @@ -816,17 +874,17 @@ node_disco_features(Host, Node, From) -> node_disco_info(Host, Node, From, false, true). node_disco_info(Host, Node, From, Identity, Features) -> Action = - fun(#pubsub_node{type = Type}) -> + fun(#pubsub_node{type = Type, id = NodeId}) -> I = case Identity of false -> []; true -> Types = - case tree_call(Host, get_subnodes, [Host, Node, From]) of + case tree_call(Host, get_subnodes, [NodeId, From]) of [] -> ["leaf"]; %% No sub-nodes: it's a leaf node _ -> - case node_call(Type, get_items, [Host, Node, From]) of + case node_call(Type, get_items, [NodeId, From]) of {result, []} -> ["collection"]; {result, _} -> ["leaf", "collection"]; _ -> [] @@ -849,10 +907,17 @@ node_disco_info(Host, Node, From, Identity, Features) -> %% TODO: add meta-data info (spec section 5.4) {result, I ++ F} end, - transaction(Host, Node, Action, sync_dirty). + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, Result}} -> {result, Result}; + Other -> Other + end. iq_disco_info(Host, SNode, From, Lang) -> - Node = string_to_node(SNode), + [RealSNode|_] = case SNode of + [] -> [[]]; + _ -> string:tokens(SNode, "!") + end, + Node = string_to_node(RealSNode), case Node of [] -> {result, @@ -866,7 +931,7 @@ iq_disco_info(Host, SNode, From, Lang) -> #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = [?XMLATTR('var', ?NS_VCARD_s)]}] ++ lists:map(fun(Feature) -> #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = [?XMLATTR('var', ?NS_PUBSUB_s++"#"++Feature)]} - end, features(Host, SNode))}; + end, features(Host, Node))}; _ -> node_disco_info(Host, Node, From) end. @@ -890,10 +955,10 @@ iq_disco_items(Host, Item, From) -> %% Note: Multiple Node Discovery not supported (mask on pubsub#type) %% TODO this code is also back-compatible with pubsub v1.8 (for client issue) %% TODO make it pubsub v1.12 compliant (breaks client compatibility ?) - %% TODO That is, remove name attribute (or node?, please check) + %% TODO That is, remove name attribute (or node?, please check for 2.1) Action = - fun(#pubsub_node{type = Type}) -> - NodeItems = case node_call(Type, get_items, [Host, Node, From]) of + fun(#pubsub_node{type = Type, id = NodeId}) -> + NodeItems = case node_call(Type, get_items, [NodeId, From]) of {result, I} -> I; _ -> [] end, @@ -903,17 +968,20 @@ iq_disco_items(Host, Item, From) -> RN = lists:last(SubNode), #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs = [?XMLATTR('jid', Host), ?XMLATTR('node', SN), ?XMLATTR('name', RN)]} - end, tree_call(Host, get_subnodes, [Host, Node, From])), + end, tree_call(Host, get_subnodes, [NodeId, From])), Items = lists:map( fun(#pubsub_item{itemid = {RN, _}}) -> SN = node_to_string(Node) ++ "!" ++ RN, - {result, Name} = node_call(Type, get_item_name, [Host, Node, RN]), + {result, Name} = node_call(Type, get_item_name, [NodeId, RN]), #xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs = [?XMLATTR('jid', Host), ?XMLATTR('node', SN), ?XMLATTR('name', Name)]} end, NodeItems), {result, Nodes ++ Items} end, - transaction(Host, Node, Action, sync_dirty) + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, Result}} -> {result, Result}; + Other -> Other + end end. iq_local(From, To, #iq{type = Type, payload = SubEl, ns = XMLNS, lang = Lang} = IQ_Rec) -> @@ -1098,7 +1166,7 @@ iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) -> %%% authorization handling -send_authorization_request(Host, Node, Subscriber) -> +send_authorization_request(#pubsub_node{owners = Owners, nodeid = {Host, Node}}, Subscriber) -> Lang = "en", %% TODO fix {U, S, R} = Subscriber, Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children = @@ -1125,16 +1193,9 @@ send_authorization_request(Host, Node, Subscriber) -> ?XMLATTR('type', <<"boolean">>), ?XMLATTR('label', translate:translate(Lang, "Allow this Jabber ID to subscribe to this pubsub node?"))], children = [#xmlel{ns = ?NS_DATA_FORMS, name = 'value', children = [#xmlcdata{cdata = <<"false">>}]}]}]}]}, - case tree_action(Host, get_node, [Host, Node, Subscriber]) of - #pubsub_node{owners = Owners} -> - lists:foreach( - fun({U1, S1, R1}) -> - ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(U1, S1, R1), Stanza} - end, Owners), - ok; - _ -> - ok - end. + lists:foreach(fun(Owner) -> + ejabberd_router ! {route, service_jid(Host), jlib:make_jid(Owner), Stanza} + end, Owners). find_authorization_response(Packet) -> Els = Packet#xmlel.children, @@ -1192,11 +1253,9 @@ handle_authorization_response(Host, From, To, Packet, XFields) -> "true" -> true; _ -> false end, - Action = fun(#pubsub_node{type = Type, - %%options = Options, - owners = Owners}) -> + Action = fun(#pubsub_node{type = Type, id = NodeId, owners = Owners}) -> IsApprover = lists:member(jlib:short_prepd_bare_jid(From), Owners), - {result, Subscription} = node_call(Type, get_subscription, [Host, Node, Subscriber]), + {result, Subscription} = node_call(Type, get_subscription, [NodeId, Subscriber]), if not IsApprover -> {error, 'forbidden'}; @@ -1208,7 +1267,7 @@ handle_authorization_response(Host, From, To, Packet, XFields) -> false -> none end, send_authorization_approval(Host, Subscriber, SNode, NewSubscription), - node_call(Type, set_subscription, [Host, Node, Subscriber, NewSubscription]) + node_call(Type, set_subscription, [NodeId, Subscriber, NewSubscription]) end end, case transaction(Host, Node, Action, sync_dirty) of @@ -1216,7 +1275,7 @@ handle_authorization_response(Host, From, To, Packet, XFields) -> ejabberd_router:route( To, From, exmpp_stanza:reply_with_error(Packet, Error)); - {result, _NewSubscription} -> + {result, _} -> %% XXX: notify about subscription state change, section 12.11 ok; _ -> @@ -1297,7 +1356,6 @@ handle_authorization_response(Host, From, To, Packet, XFields) -> %% create_node(Host, ServerHost, Node, Owner, Type) -> create_node(Host, ServerHost, Node, Owner, Type, all, []). - create_node(Host, ServerHost, [], Owner, Type, Access, Configuration) -> case lists:member("instant-nodes", features(Type)) of true -> @@ -1348,13 +1406,10 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> case node_call(Type, create_node_permission, [Host, ServerHost, Node, Parent, Owner, Access]) of {result, true} -> case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions]) of - ok -> - node_call(Type, create_node, [Host, Node, Owner]); - {error, 'conflict'} -> - case proplists:get_value(virtual_tree, tree_call(Host, options, [])) of - true -> node_call(Type, create_node, [Host, Node, Owner]); - _ -> {error, 'conflict'} - end; + {ok, NodeId} -> + node_call(Type, create_node, [NodeId, Owner]); + {error, {virtual, NodeId}} -> + node_call(Type, create_node, [NodeId, Owner]); Error -> Error end; @@ -1365,12 +1420,6 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> Reply = [#xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children = [#xmlel{ns = ?NS_PUBSUB, name = 'create', attrs = [?XMLATTR('node', node_to_string(Node))]}]}], case transaction(CreateNode, transaction) of - {error, Error} -> - %% in case we change transaction to sync_dirty... - %%node_action: - %% node_call(Type, delete_node, [Host, Node]), - %% tree_call(Host, delete_node, [Host, Node]), - {error, Error}; {result, {Result, broadcast}} -> %%Lang = "en", %% TODO: fix %%OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), @@ -1385,7 +1434,12 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> {result, default} -> {result, Reply}; {result, Result} -> - {result, Result} + {result, Result}; + Error -> + %% in case we change transaction to sync_dirty... + %% node_call(Type, delete_node, [NodeId]), + %% tree_call(Host, delete_node, [NodeId]), + Error end; Error -> Error @@ -1408,11 +1462,11 @@ delete_node(_Host, [], _Owner) -> %% Node is the root {error, 'not-allowed'}; delete_node(Host, Node, Owner) -> - Action = fun(#pubsub_node{type = Type}) -> - case node_call(Type, get_affiliation, [Host, Node, Owner]) of + Action = fun(#pubsub_node{type = Type, id = NodeId}) -> + case node_call(Type, get_affiliation, [NodeId, Owner]) of {result, owner} -> - Removed = tree_call(Host, delete_node, [Host, Node]), - node_call(Type, delete_node, [Host, Removed]); + Removed = tree_call(Host, delete_node, [NodeId]), + node_call(Type, delete_node, [Removed]); _ -> %% Entity is not an owner {error, 'forbidden'} @@ -1420,28 +1474,29 @@ delete_node(Host, Node, Owner) -> end, Reply = [], case transaction(Host, Node, Action, transaction) of - {error, Error} -> - {error, Error}; - {result, {Result, broadcast, Removed}} -> - lists:foreach(fun(RNode) -> - broadcast_removed_node(Host, RNode) + {result, {_, {Result, broadcast, Removed}}} -> + lists:foreach(fun({RNode, RSubscriptions}) -> + {RH, RN} = RNode#pubsub_node.nodeid, + NodeId = RNode#pubsub_node.id, + Type = RNode#pubsub_node.type, + Options = RNode#pubsub_node.options, + broadcast_removed_node(RH, RN, NodeId, Type, Options, RSubscriptions) end, Removed), case Result of default -> {result, Reply}; _ -> {result, Result} end; - {result, {Result, Removed}} -> - lists:foreach(fun(RNode) -> - broadcast_removed_node(Host, RNode) - end, Removed), + {result, {_, {Result, _Removed}}} -> case Result of default -> {result, Reply}; _ -> {result, Result} end; - {result, default} -> + {result, {_, default}} -> {result, Reply}; - {result, Result} -> - {result, Result} + {result, {_, Result}} -> + {result, Result}; + Error -> + Error end. %% @spec (Host, Node, From, JID) -> @@ -1474,7 +1529,7 @@ subscribe_node(Host, Node, From, JID) -> {undefined, undefined, undefined} end, SubId = uniqid(), - Action = fun(#pubsub_node{options = Options, type = Type}) -> + Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> Features = features(Type), SubscribeFeature = lists:member("subscribe", Features), SubscribeConfig = get_option(Options, subscribe), @@ -1502,7 +1557,7 @@ subscribe_node(Host, Node, From, JID) -> {error, extended_error('feature-not-implemented', unsupported, "subscribe")}; true -> node_call(Type, subscribe_node, - [Host, Node, From, Subscriber, + [NodeId, From, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup]) end @@ -1521,26 +1576,28 @@ subscribe_node(Host, Node, From, JID) -> end}]}] end, case transaction(Host, Node, Action, sync_dirty) of - {error, Error} -> - {error, Error}; - {result, {Result, subscribed, send_last}} -> - send_items(Host, Node, Subscriber, last), + {result, {TNode, {Result, subscribed, send_last}}} -> + NodeId = TNode#pubsub_node.id, + Type = TNode#pubsub_node.type, + send_items(Host, Node, NodeId, Type, Subscriber, last), case Result of default -> {result, Reply(subscribed)}; _ -> {result, Result} end; - {result, {Result, Subscription}} -> + {result, {TNode, {Result, Subscription}}} -> case Subscription of - pending -> send_authorization_request(Host, Node, Subscriber); + pending -> send_authorization_request(TNode, Subscriber); _ -> ok end, case Result of default -> {result, Reply(Subscription)}; _ -> {result, Result} end; - {result, Result} -> + {result, {_, Result}} -> %% this case should never occure anyway - {result, Result} + {result, Result}; + Error -> + Error end. %% @spec (Host, Noce, From, JID, SubId) -> {error, Reason} | {result, []} @@ -1567,14 +1624,16 @@ unsubscribe_node(Host, Node, From, JID, SubId) when is_list(JID) -> end, unsubscribe_node(Host, Node, From, Subscriber, SubId); unsubscribe_node(Host, Node, From, Subscriber, SubId) -> - case node_action(Host, Node, unsubscribe_node, - [Host, Node, From, Subscriber, SubId]) of - {error, Error} -> - {error, Error}; - {result, default} -> + Action = fun(#pubsub_node{type = Type, id = NodeId}) -> + node_call(Type, unsubscribe_node, [NodeId, From, Subscriber, SubId]) + end, + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, default}} -> {result, []}; - {result, Result} -> - {result, Result} + {result, {_, Result}} -> + {result, Result}; + Error -> + Error end. %% @spec (Host::host(), ServerHost::host(), JID::jid(), Node::pubsubNode(), ItemId::string(), Payload::term()) -> @@ -1595,7 +1654,7 @@ publish_item(Host, ServerHost, Node, Publisher, "", Payload) -> %% if publisher does not specify an ItemId, the service MUST generate the ItemId publish_item(Host, ServerHost, Node, Publisher, uniqid(), Payload); publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> - Action = fun(#pubsub_node{options = Options, type = Type}) -> + Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> Features = features(Type), PublishFeature = lists:member("publish", Features), PublishModel = get_option(Options, publish_model), @@ -1627,12 +1686,37 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> %% Publisher attempts to publish to persistent node with no item {error, extended_error('bad-request', "item-required")}; true -> - node_call(Type, publish_item, [Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload]) + node_call(Type, publish_item, [NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload]) end end, ejabberd_hooks:run(pubsub_publish_item, ServerHost, [ServerHost, Node, Publisher, service_jid(Host), ItemId, Payload]), Reply = [], %% TODO EJAB-909 case transaction(Host, Node, Action, sync_dirty) of + {result, {TNode, {Result, broadcast, Removed}}} -> + NodeId = TNode#pubsub_node.id, + Type = TNode#pubsub_node.type, + Options = TNode#pubsub_node.options, + broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, jlib:short_prepd_jid(Publisher), Payload), + case Result of + default -> {result, Reply}; + _ -> {result, Result} + end; + {result, {TNode, {default, Removed}}} -> + NodeId = TNode#pubsub_node.id, + Type = TNode#pubsub_node.type, + Options = TNode#pubsub_node.options, + broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed), + {result, Reply}; + {result, {TNode, {Result, Removed}}} -> + NodeId = TNode#pubsub_node.id, + Type = TNode#pubsub_node.type, + Options = TNode#pubsub_node.options, + broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed), + {result, Result}; + {result, {_, default}} -> + {result, Reply}; + {result, {_, Result}} -> + {result, Result}; {error, 'item-not-found'} -> %% handles auto-create feature %% for automatic node creation. we'll take the default node type: @@ -1649,25 +1733,8 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> false -> {error, 'item-not-found'} end; - {error, Reason} -> - {error, Reason}; - {result, {Result, broadcast, Removed}} -> - broadcast_retract_items(Host, Node, Removed), - broadcast_publish_item(Host, Node, ItemId, jlib:short_prepd_jid(Publisher), Payload), - case Result of - default -> {result, Reply}; - _ -> {result, Result} - end; - {result, default, Removed} -> - broadcast_retract_items(Host, Node, Removed), - {result, Reply}; - {result, Result, Removed} -> - broadcast_retract_items(Host, Node, Removed), - {result, Result}; - {result, default} -> - {result, Reply}; - {result, Result} -> - {result, Result} + Error -> + Error end. %% @spec (Host::host(), JID::jid(), Node::pubsubNode(), ItemId::string()) -> @@ -1690,7 +1757,7 @@ delete_item(_, "", _, _, _) -> %% Request does not specify a node {error, extended_error('bad-request', "node-required")}; delete_item(Host, Node, Publisher, ItemId, ForceNotify) -> - Action = fun(#pubsub_node{type = Type}) -> + Action = fun(#pubsub_node{type = Type, id = NodeId}) -> Features = features(Type), PersistentFeature = lists:member("persistent-items", Features), DeleteFeature = lists:member("delete-items", Features), @@ -1705,23 +1772,26 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) -> %% Service does not support item deletion {error, extended_error('feature-not-implemented', unsupported, "delete-items")}; true -> - node_call(Type, delete_item, [Host, Node, Publisher, ItemId]) + node_call(Type, delete_item, [NodeId, Publisher, ItemId]) end end, Reply = [], case transaction(Host, Node, Action, sync_dirty) of - {error, Reason} -> - {error, Reason}; - {result, {Result, broadcast}} -> - broadcast_retract_items(Host, Node, [ItemId], ForceNotify), + {result, {TNode, {Result, broadcast}}} -> + NodeId = TNode#pubsub_node.id, + Type = TNode#pubsub_node.type, + Options = TNode#pubsub_node.options, + broadcast_retract_items(Host, Node, NodeId, Type, Options, [ItemId], ForceNotify), case Result of default -> {result, Reply}; _ -> {result, Result} end; - {result, default} -> + {result, {_, default}} -> {result, Reply}; - {result, Result} -> - {result, Result} + {result, {_, Result}} -> + {result, Result}; + Error -> + Error end. %% @spec (Host, JID, Node) -> @@ -1739,7 +1809,7 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) -> %%
  • The specified node does not exist.
  • %% purge_node(Host, Node, Owner) -> - Action = fun(#pubsub_node{type = Type, options = Options}) -> + Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> Features = features(Type), PurgeFeature = lists:member("purge-nodes", Features), PersistentFeature = lists:member("persistent-items", Features), @@ -1755,23 +1825,26 @@ purge_node(Host, Node, Owner) -> %% Node is not configured for persistent items {error, extended_error('feature-not-implemented', unsupported, "persistent-items")}; true -> - node_call(Type, purge_node, [Host, Node, Owner]) + node_call(Type, purge_node, [NodeId, Owner]) end end, Reply = [], case transaction(Host, Node, Action, sync_dirty) of - {error, Reason} -> - {error, Reason}; - {result, {Result, broadcast}} -> - broadcast_purge_node(Host, Node), + {result, {TNode, {Result, broadcast}}} -> + NodeId = TNode#pubsub_node.id, + Type = TNode#pubsub_node.type, + Options = TNode#pubsub_node.options, + broadcast_purge_node(Host, Node, NodeId, Type, Options), case Result of default -> {result, Reply}; _ -> {result, Result} end; - {result, default} -> + {result, {_, default}} -> {result, Reply}; - {result, Result} -> - {result, Result} + {result, {_, Result}} -> + {result, Result}; + Error -> + Error end. %% @doc

    Return the items of a given node.

    @@ -1793,7 +1866,7 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) -> {error, Error} -> {error, Error}; _ -> - Action = fun(#pubsub_node{options = Options, type = Type}) -> + Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> Features = features(Type), RetreiveFeature = lists:member("retrieve-items", Features), PersistentFeature = lists:member("persistent-items", Features), @@ -1816,15 +1889,13 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) -> {error, extended_error('feature-not-implemented', unsupported, "persistent-items")}; true -> node_call(Type, get_items, - [Host, Node, From, + [NodeId, From, AccessModel, PresenceSubscription, RosterGroup, SubId]) end end, case transaction(Host, Node, Action, sync_dirty) of - {error, Reason} -> - {error, Reason}; - {result, Items} -> + {result, {_, Items}} -> SendItems = case ItemIDs of [] -> Items; @@ -1837,47 +1908,46 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) -> %% number of items sent to MaxItems: {result, [#xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children = [#xmlel{ns = ?NS_PUBSUB, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = - itemsEls(lists:sublist(SendItems, MaxItems))}]}]} + itemsEls(lists:sublist(SendItems, MaxItems))}]}]}; + Error -> + Error end end. -get_items(Host, Node, From) -> - case node_action(Host, Node, get_items, [Host, Node, From]) of - {result, Items} -> Items; - _ -> [] - end. - -%% @spec (Host, Node, LJID, Number) -> any() +%% @spec (Host, Node, NodeId, Type, LJID, Number) -> any() %% Host = host() -%% Node = pubsubNode() +%% NodeId = pubsubNodeId() +%% Type = pubsubNodeType() %% LJID = {U, S, []} %% Number = last | integer() %% @doc

    Resend the items of a node to the user.

    %% @todo use cache-last-item feature -send_items(Host, Node, {LU, LS, LR} = LJID, Number) -> - ToSend = case get_items(Host, Node, LJID) of - [] -> +send_items(Host, Node, NodeId, Type, {LU, LS, LR} = LJID, Number) -> + ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of + {result, []} -> []; - Items -> + {result, Items} -> case Number of last -> %%% [lists:last(Items)] does not work on clustered table [First|Tail] = Items, [lists:foldl( fun(CurItem, LastItem) -> - {_, LTimeStamp} = LastItem#pubsub_item.creation, - {_, CTimeStamp} = CurItem#pubsub_item.creation, + {LTimeStamp, _} = LastItem#pubsub_item.creation, + {CTimeStamp, _} = CurItem#pubsub_item.creation, if CTimeStamp > LTimeStamp -> CurItem; true -> LastItem end end, First, Tail)]; N when N > 0 -> - %%% This case is buggy on clustered table due to lake of order + %%% This case is buggy on clustered table due to lack of order lists:nthtail(length(Items)-N, Items); _ -> Items - end + end; + _ -> + [] end, Stanza = event_stanza( [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = @@ -1901,7 +1971,7 @@ get_affiliations(Host, JID, Plugins) when is_list(Plugins) -> %% Service does not support retreive affiliatons {{error, extended_error('feature-not-implemented', unsupported, "retrieve-affiliations")}, Acc}; true -> - {result, Affiliations} = node_action(Type, get_entity_affiliations, [Host, JID]), + {result, Affiliations} = node_action(Host, Type, get_entity_affiliations, [Host, JID]), {Status, [Affiliations|Acc]} end end, {ok, []}, Plugins), @@ -1921,10 +1991,10 @@ get_affiliations(Host, JID, Plugins) when is_list(Plugins) -> Error end; get_affiliations(Host, Node, JID) -> - Action = fun(#pubsub_node{type = Type}) -> + Action = fun(#pubsub_node{type = Type, id = NodeId}) -> Features = features(Type), RetrieveFeature = lists:member("modify-affiliations", Features), - Affiliation = node_call(Type, get_affiliation, [Host, Node, JID]), + Affiliation = node_call(Type, get_affiliation, [NodeId, JID]), if not RetrieveFeature -> %% Service does not support modify affiliations @@ -1933,15 +2003,13 @@ get_affiliations(Host, Node, JID) -> %% Entity is not an owner {error, 'forbidden'}; true -> - node_call(Type, get_node_affiliations, [Host, Node]) + node_call(Type, get_node_affiliations, [NodeId]) end end, case transaction(Host, Node, Action, sync_dirty) of - {error, Reason} -> - {error, Reason}; - {result, []} -> + {result, {_, []}} -> {error, 'item-not-found'}; - {result, Affiliations} -> + {result, {_, Affiliations}} -> Entities = lists:flatmap( fun({_, none}) -> []; ({{AU, AS, AR}, Affiliation}) -> @@ -1951,7 +2019,9 @@ get_affiliations(Host, Node, JID) -> end, Affiliations), {result, [#xmlel{ns = ?NS_PUBSUB_OWNER, name = 'pubsub', children = [#xmlel{ns = ?NS_PUBSUB_OWNER, name = 'affiliations', attrs = [?XMLATTR('node', node_to_string(Node))], children = - Entities}]}]} + Entities}]}]}; + Error -> + Error end. set_affiliations(Host, Node, From, EntitiesEls) -> @@ -1987,12 +2057,12 @@ set_affiliations(Host, Node, From, EntitiesEls) -> error -> {error, 'bad-request'}; _ -> - Action = fun(#pubsub_node{type = Type, owners = Owners}=N) -> + Action = fun(#pubsub_node{owners = Owners, type = Type, id = NodeId}=N) -> case lists:member(Owner, Owners) of true -> lists:foreach( fun({JID, Affiliation}) -> - node_call(Type, set_affiliation, [Host, Node, JID, Affiliation]), + node_call(Type, set_affiliation, [NodeId, JID, Affiliation]), case Affiliation of owner -> NewOwner = jlib:short_prepd_bare_jid(JID), @@ -2016,7 +2086,10 @@ set_affiliations(Host, Node, From, EntitiesEls) -> {error, 'forbidden'} end end, - transaction(Host, Node, Action, sync_dirty) + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, Result}} -> {result, Result}; + Other -> Other + end end. @@ -2038,7 +2111,7 @@ get_subscriptions(Host, JID, Plugins) when is_list(Plugins) -> {{error, extended_error('feature-not-implemented', unsupported, "retrieve-subscriptions")}, Acc}; true -> Subscriber = jlib:jid_remove_resource(JID), - {result, Subscriptions} = node_action(Type, get_entity_subscriptions, [Host, Subscriber]), + {result, Subscriptions} = node_action(Host, Type, get_entity_subscriptions, [Host, Subscriber]), {Status, [Subscriptions|Acc]} end end, {ok, []}, Plugins), @@ -2064,10 +2137,10 @@ get_subscriptions(Host, JID, Plugins) when is_list(Plugins) -> Error end; get_subscriptions(Host, Node, JID) -> - Action = fun(#pubsub_node{type = Type}) -> + Action = fun(#pubsub_node{type = Type, id = NodeId}) -> Features = features(Type), RetrieveFeature = lists:member("manage-subscriptions", Features), - Affiliation = node_call(Type, get_affiliation, [Host, Node, JID]), + Affiliation = node_call(Type, get_affiliation, [NodeId, JID]), if not RetrieveFeature -> %% Service does not support manage subscriptions @@ -2076,15 +2149,13 @@ get_subscriptions(Host, Node, JID) -> %% Entity is not an owner {error, 'forbidden'}; true -> - node_call(Type, get_node_subscriptions, [Host, Node]) + node_call(Type, get_node_subscriptions, [NodeId]) end end, case transaction(Host, Node, Action, sync_dirty) of - {error, Reason} -> - {error, Reason}; - {result, []} -> + {result, {_, []}} -> {error, 'item-not-found'}; - {result, Subscriptions} -> + {result, {_, Subscriptions}} -> Entities = lists:flatmap( fun({_, none}) -> []; ({{AU, AS, AR}, Subscription}) -> @@ -2099,7 +2170,9 @@ get_subscriptions(Host, Node, JID) -> end, Subscriptions), {result, [#xmlel{ns = ?NS_PUBSUB_OWNER, name = 'pubsub', children = [#xmlel{ns = ?NS_PUBSUB_OWNER, name = 'subscriptions', attrs = [?XMLATTR('node', node_to_string(Node))], children = - Entities}]}]} + Entities}]}]}; + Error -> + Error end. set_subscriptions(Host, Node, From, EntitiesEls) -> @@ -2136,21 +2209,22 @@ set_subscriptions(Host, Node, From, EntitiesEls) -> error -> {error, 'bad-request'}; _ -> - Action = fun(#pubsub_node{type = Type, owners = Owners}) -> + Action = fun(#pubsub_node{type = Type, id = NodeId, owners = Owners}) -> case lists:member(Owner, Owners) of true -> lists:foreach( fun({JID, Subscription}) -> - node_call( - Type, set_subscription, - [Host, Node, JID, Subscription]) + node_call(Type, set_subscription, [NodeId, JID, Subscription]) end, Entities), {result, []}; _ -> {error, 'forbidden'} end end, - transaction(Host, Node, Action, sync_dirty) + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, Result}} -> {result, Result}; + Other -> Other + end end. @@ -2265,174 +2339,169 @@ event_stanza(Els) -> %%%%%% broadcast functions -broadcast_publish_item(Host, Node, ItemId, _From, Payload) -> - %broadcast(Host, Node, none, true, 'items', ItemEls) - Action = - fun(#pubsub_node{options = Options, type = Type}) -> - case node_call(Type, get_states, [Host, Node]) of +broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, _From, Payload) -> + %broadcast(Host, Node, NodeId, Options, none, true, 'items', ItemEls) + case node_action(Host, Type, get_node_subscriptions, [NodeId]) of + {result, []} -> + {result, false}; + {result, Subs} -> + Content = case get_option(Options, deliver_payloads) of + true -> Payload; + false -> [] + end, + Stanza = event_stanza( + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = itemAttr(ItemId), children = Content}]}]), + broadcast_stanza(Host, Node, NodeId, Type, Options, Subs, Stanza), + case Removed of + [] -> + ok; + _ -> + case get_option(Options, notify_retract) of + true -> + RetractStanza = event_stanza( + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = itemAttr(RId)} || RId <- Removed]}]), + broadcast_stanza(Host, Node, NodeId, Type, Options, Subs, RetractStanza); + _ -> + ok + end + end, + {result, true}; + _ -> + {result, false} + end. + +broadcast_retract_items(Host, Node, NodeId, Type, Options, ItemIds) -> + broadcast_retract_items(Host, Node, NodeId, Type, Options, ItemIds, false). +broadcast_retract_items(_Host, _Node, _NodeId, _Type, _Options, [], _ForceNotify) -> + {result, false}; +broadcast_retract_items(Host, Node, NodeId, Type, Options, ItemIds, ForceNotify) -> + %broadcast(Host, Node, NodeId, Options, notify_retract, ForceNotify, 'retract', RetractEls) + case (get_option(Options, notify_retract) or ForceNotify) of + true -> + case node_action(Host, Type, get_node_subscriptions, [NodeId]) of {result, []} -> {result, false}; - {result, States} -> - Content = case get_option(Options, deliver_payloads) of - true -> Payload; - false -> [] - end, + {result, Subs} -> Stanza = event_stanza( - [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = - [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = itemAttr(ItemId), children = Content}]}]), - broadcast_stanza(Host, Node, Type, Options, States, Stanza), + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = itemAttr(ItemId)} || ItemId <- ItemIds]}]), + broadcast_stanza(Host, Node, NodeId, Type, Options, Subs, Stanza), {result, true}; _ -> {result, false} - end - end, - transaction(Host, Node, Action, sync_dirty). + end; + _ -> + {result, false} + end. -broadcast_retract_items(Host, Node, ItemIds) -> - broadcast_retract_items(Host, Node, ItemIds, false). -broadcast_retract_items(Host, Node, ItemIds, ForceNotify) -> - %broadcast(Host, Node, notify_retract, ForceNotify, 'retract', RetractEls) - Action = - fun(#pubsub_node{options = Options, type = Type}) -> - case (get_option(Options, notify_retract) or ForceNotify) of - true -> - case node_call(Type, get_states, [Host, Node]) of - {result, []} -> - {result, false}; - {result, States} -> - RetractEls = [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = itemAttr(ItemId)} || ItemId <- ItemIds], - Stanza = event_stanza( - [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], - children = RetractEls}]), - broadcast_stanza(Host, Node, Type, Options, States, Stanza), - {result, true}; - _ -> - {result, false} - end; - _ -> +broadcast_purge_node(Host, Node, NodeId, Type, Options) -> + %broadcast(Host, Node, NodeId, Options, notify_retract, false, 'purge', []) + case get_option(Options, notify_retract) of + true -> + case node_action(Host, Type, get_node_subscriptions, [NodeId]) of + {result, []} -> + {result, false}; + {result, Subs} -> + Stanza = event_stanza( + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'purge', attrs = [?XMLATTR('node', node_to_string(Node))]}]), + broadcast_stanza(Host, Node, NodeId, Type, Options, Subs, Stanza), + {result, true}; + _ -> {result, false} - end - end, - transaction(Host, Node, Action, sync_dirty). + end; + _ -> + {result, false} + end. -broadcast_purge_node(Host, Node) -> - %broadcast(Host, Node, notify_retract, false, 'purge', []) - Action = - fun(#pubsub_node{options = Options, type = Type}) -> - case get_option(Options, notify_retract) of - true -> - case node_call(Type, get_states, [Host, Node]) of - {result, []} -> - {result, false}; - {result, States} -> - Stanza = event_stanza( - [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'purge', attrs = [?XMLATTR('node', node_to_string(Node))]}]), - broadcast_stanza(Host, Node, Type, Options, States, Stanza), - {result, true}; - _ -> - {result, false} - end; +broadcast_removed_node(Host, Node, NodeId, Type, Options, Subs) -> + %broadcast(Host, Node, NodeId, Options, notify_delete, false, 'delete', []) + case get_option(Options, notify_delete) of + true -> + case Subs of + [] -> + {result, false}; _ -> - {result, false} - end - end, - transaction(Host, Node, Action, sync_dirty). + Stanza = event_stanza( + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'delete', attrs = [?XMLATTR('node', node_to_string(Node))]}]), + broadcast_stanza(Host, Node, NodeId, Type, Options, Subs, Stanza), + {result, true} + end; + _ -> + {result, false} + end. -broadcast_removed_node(Host, Node) -> - %broadcast(Host, Node, notify_delete, false, 'delete', []) - Action = - fun(#pubsub_node{options = Options, type = Type}) -> - case get_option(Options, notify_delete) of - true -> - case node_call(Type, get_states, [Host, Node]) of - {result, []} -> - {result, false}; - {result, States} -> - Stanza = event_stanza( - [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'delete', attrs = [?XMLATTR('node', node_to_string(Node))]}]), - broadcast_stanza(Host, Node, Type, Options, States, Stanza), - {result, true}; - _ -> - {result, false} - end; - _ -> +broadcast_config_notification(Host, Node, NodeId, Type, Options, Lang) -> + %broadcast(Host, Node, NodeId, Options, notify_config, false, 'items', ConfigEls) + case get_option(Options, notify_config) of + true -> + case node_action(Host, Type, get_node_subscriptions, [NodeId]) of + {result, []} -> + {result, false}; + {result, Subs} -> + Content = case get_option(Options, deliver_payloads) of + true -> + [#xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = [?XMLATTR('type', <<"form">>)], children = + get_configure_xfields(Type, Options, Lang, [])}]; + false -> + [] + end, + Stanza = event_stanza( + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = + [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = [?XMLATTR('id', <<"configuration">>)], children = + Content}]}]), + broadcast_stanza(Host, Node, NodeId, Type, Options, Subs, Stanza), + {result, true}; + _ -> {result, false} - end - end, - transaction(Host, Node, Action, sync_dirty). + end; + _ -> + {result, false} + end. -broadcast_config_notification(Host, Node, Lang) -> - %broadcast(Host, Node, notify_config, false, 'items', ConfigEls) - Action = - fun(#pubsub_node{options = Options, owners = Owners, type = Type}) -> - case get_option(Options, notify_config) of - true -> - case node_call(Type, get_states, [Host, Node]) of - {result, []} -> - {result, false}; - {result, States} -> - Content = case get_option(Options, deliver_payloads) of - true -> - [#xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = [?XMLATTR('type', <<"form">>)], children = - get_configure_xfields(Type, Options, Lang, Owners, [])}]; - false -> - [] - end, - Stanza = event_stanza( - [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [?XMLATTR('node', node_to_string(Node))], children = - [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = [?XMLATTR('id', <<"configuration">>)], children = - Content}]}]), - broadcast_stanza(Host, Node, Type, Options, States, Stanza), - {result, true}; - _ -> - {result, false} - end; - _ -> - {result, false} - end - end, - transaction(Host, Node, Action, sync_dirty). % TODO: merge broadcast code that way -%broadcast(Host, Node, Feature, Force, ElName, SubEls) -> -% Action = -% fun(#pubsub_node{options = Options, type = Type}) -> -% case (get_option(Options, Feature) or Force) of -% true -> -% case node_call(Type, get_states, [Host, Node]) of -% {result, []} -> -% {result, false}; -% {result, States} -> -% Stanza = event_stanza([{xmlelement, ElName, [{"node", node_to_string(Node)}], SubEls}]), -% broadcast_stanza(Host, Node, Type, Options, States, Stanza), -% {result, true}; -% _ -> -% {result, false} -% end; -% _ -> -% {result, false} -% end -% end, -% transaction(Host, Node, Action, sync_dirty). +%broadcast(Host, Node, NodeId, Type, Options, Feature, Force, ElName, SubEls) -> +% case (get_option(Options, Feature) or Force) of +% true -> +% case node_action(Host, Type, get_node_subscriptions, [NodeId]) of +% {result, []} -> +% {result, false}; +% {result, Subs} -> +% Stanza = event_stanza([{xmlelement, ElName, [{"node", node_to_string(Node)}], SubEls}]), +% broadcast_stanza(Host, Node, Type, Options, Subs, Stanza), +% {result, true}; +% _ -> +% {result, false} +% end; +% _ -> +% {result, false} +% end -broadcast_stanza(Host, Node, _Type, Options, States, Stanza) -> - AccessModel = get_option(Options, access_model), +broadcast_stanza(Host, Node, _NodeId, _Type, Options, Subscriptions, Stanza) -> + %AccessModel = get_option(Options, access_model), PresenceDelivery = get_option(Options, presence_based_delivery), BroadcastAll = get_option(Options, broadcast_all_resources), %% XXX this is not standard, but usefull From = service_jid(Host), %% Handles explicit subscriptions - lists:foreach(fun(#pubsub_state{stateid = {LJID, _}, subscription = Subs}) -> - case is_to_deliver(LJID, Subs, PresenceDelivery) of + lists:foreach(fun({LJID, Subscription}) -> + case is_to_deliver(LJID, Subscription, PresenceDelivery) of true -> - {U, S, R} = case BroadcastAll of - true -> jlib:short_prepd_bare_jid(LJID); - false -> LJID + LJIDs = case BroadcastAll of + true -> + {U, S, _} = LJID, + [{U, S, R} || R <- user_resources(U, S)]; + false -> + [LJID] end, - ejabberd_router ! {route, From, exmpp_jlib:make_jid(U, S, R), Stanza}; + lists:foreach(fun(To) -> + ejabberd_router ! {route, From, jlib:make_jid(To), Stanza} + end, LJIDs); false -> ok end - end, States), + end, Subscriptions), %% Handles implicit presence subscriptions case Host of {LUser, LServer, LResource} -> @@ -2507,8 +2576,8 @@ is_caps_notify(Host, Node, LJID) -> %% get_configure(Host, ServerHost, Node, From, Lang) -> Action = - fun(#pubsub_node{options = Options, owners = Owners, type = Type}) -> - case node_call(Type, get_affiliation, [Host, Node, From]) of + fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> + case node_call(Type, get_affiliation, [NodeId, From]) of {result, owner} -> Groups = ejabberd_hooks:run_fold(roster_groups, ServerHost, [], [ServerHost]), {result, @@ -2517,13 +2586,16 @@ get_configure(Host, ServerHost, Node, From, Lang) -> [?XMLATTR('node', node_to_string(Node))], children = [#xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = [?XMLATTR('type', <<"form">>)], children = - get_configure_xfields(Type, Options, Lang, Owners, Groups) + get_configure_xfields(Type, Options, Lang, Groups) }]}]}]}; _ -> {error, 'forbidden'} end end, - transaction(Host, Node, Action, sync_dirty). + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, Result}} -> {result, Result}; + Other -> Other + end. get_default(Host, Node, _From, Lang) -> Type = select_type(Host, Host, Node), @@ -2531,7 +2603,7 @@ get_default(Host, Node, _From, Lang) -> {result, [#xmlel{ns = ?NS_PUBSUB_OWNER, name = 'pubsub', children = [#xmlel{ns = ?NS_PUBSUB_OWNER, name = 'default', children = [#xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = [?XMLATTR('type', <<"form">>)], children = - get_configure_xfields(Type, Options, Lang, [], []) + get_configure_xfields(Type, Options, Lang, []) }]}]}]}. %% Get node option @@ -2607,7 +2679,7 @@ max_items(Options) -> ?LISTMXFIELD(Label, "pubsub#" ++ atom_to_list(Var), get_option(Options, Var), Opts)). -get_configure_xfields(Type, Options, Lang, _Owners, Groups) -> +get_configure_xfields(Type, Options, Lang, Groups) -> [?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NODE_CONFIG_s), ?BOOL_CONFIG_FIELD("Deliver payloads with event notifications", deliver_payloads), ?BOOL_CONFIG_FIELD("Deliver event notifications", deliver_notifications), @@ -2646,9 +2718,8 @@ set_configure(Host, Node, From, Els, Lang) -> {result, []}; "submit" -> Action = - fun(#pubsub_node{options = Options, type = Type}=N) -> - case node_call(Type, get_affiliation, - [Host, Node, From]) of + fun(#pubsub_node{options = Options, type = Type, id = NodeId} = N) -> + case node_call(Type, get_affiliation, [NodeId, From]) of {result, owner} -> case jlib:parse_xdata_submit(XEl) of invalid -> @@ -2660,8 +2731,7 @@ set_configure(Host, Node, From, Els, Lang) -> end, case set_xoption(XData, OldOpts) of NewOpts when is_list(NewOpts) -> - tree_call(Host, set_node, - [N#pubsub_node{options = NewOpts}]), + tree_call(Host, set_node, [N#pubsub_node{options = NewOpts}]), {result, ok}; Err -> Err @@ -2672,8 +2742,11 @@ set_configure(Host, Node, From, Els, Lang) -> end end, case transaction(Host, Node, Action, transaction) of - {result, ok} -> - broadcast_config_notification(Host, Node, Lang), + {result, {TNode, ok}} -> + NodeId = TNode#pubsub_node.id, + Type = TNode#pubsub_node.type, + Options = TNode#pubsub_node.options, + broadcast_config_notification(Host, Node, NodeId, Type, Options, Lang), {result, []}; Other -> Other @@ -2845,13 +2918,17 @@ features(Host, []) -> Acc ++ features(Plugin) end, [], plugins(Host))); features(Host, Node) -> - {result, Features} = node_action(Host, Node, features, []), - lists:usort(features() ++ Features). + Action = fun(#pubsub_node{type = Type}) -> {result, features(Type)} end, + case transaction(Host, Node, Action, sync_dirty) of + {result, Features} -> lists:usort(features() ++ Features); + _ -> features() + end. %% @doc

    node tree plugin call.

    tree_call({_User, Server, _Resource}, Function, Args) -> tree_call(Server, Function, Args); tree_call(Host, Function, Args) -> + ?DEBUG("tree_call ~p ~p ~p",[Host, Function, Args]), Module = case ets:lookup(gen_mod:get_module_proc(Host, pubsub_state), nodetree) of [{nodetree, N}] -> N; _ -> list_to_atom(?TREE_PREFIX ++ ?STDTREE) @@ -2863,6 +2940,7 @@ tree_action(Host, Function, Args) -> %% @doc

    node plugin call.

    node_call(Type, Function, Args) -> + ?DEBUG("node_call ~p ~p ~p",[Type, Function, Args]), Module = list_to_atom(?PLUGIN_PREFIX++Type), case catch apply(Module, Function, Args) of {result, Result} -> {result, Result}; @@ -2876,24 +2954,23 @@ node_call(Type, Function, Args) -> Result -> {result, Result} %% any other return value is forced as result end. -node_action(Type, Function, Args) -> +node_action(_Host, Type, Function, Args) -> transaction(fun() -> node_call(Type, Function, Args) end, sync_dirty). -node_action(Host, Node, Function, Args) -> - transaction(fun() -> - case tree_call(Host, get_node, [Host, Node]) of - #pubsub_node{type=Type} -> node_call(Type, Function, Args); - Other -> Other - end - end, sync_dirty). %% @doc

    plugin transaction handling.

    transaction(Host, Node, Action, Trans) -> transaction(fun() -> case tree_call(Host, get_node, [Host, Node]) of - Record when is_record(Record, pubsub_node) -> Action(Record); - Other -> Other + N when is_record(N, pubsub_node) -> + case Action(N) of + {result, Result} -> {result, {N, Result}}; + {atomic, {result, Result}} -> {result, {N, Result}}; + Other -> Other + end; + Error -> + Error end end, Trans). diff --git a/src/mod_pubsub/node.template b/src/mod_pubsub/node.template index a9f45e1ef..d4fffc474 100644 --- a/src/mod_pubsub/node.template +++ b/src/mod_pubsub/node.template @@ -41,29 +41,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1 ]). @@ -87,7 +87,7 @@ options() -> {roster_groups_allowed, []}, {publish_model, publishers}, {max_payload_size, ?MAX_PAYLOAD_SIZE}, - {send_last_published_item, never}, + {send_last_published_item, on_sub_and_presence}, {deliver_notifications, true}, {presence_based_delivery, false}]. @@ -111,74 +111,74 @@ features() -> create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_default:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). -create_node(Host, Node, Owner) -> - node_default:create_node(Host, Node, Owner). +create_node(NodeId, Owner) -> + node_default:create_node(NodeId, Owner). -delete_node(Host, Removed) -> - node_default:delete_node(Host, Removed). +delete_node(Removed) -> + node_default:delete_node(Removed). -subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> - node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> + node_default:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). -unsubscribe_node(Host, Node, Sender, Subscriber, SubID) -> - node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID). +unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> + node_default:unsubscribe_node(NodeId, Sender, Subscriber, SubID). -publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> - node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload). +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> + node_default:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). -remove_extra_items(Host, Node, MaxItems, ItemIds) -> - node_default:remove_extra_items(Host, Node, MaxItems, ItemIds). +remove_extra_items(NodeId, MaxItems, ItemIds) -> + node_default:remove_extra_items(NodeId, MaxItems, ItemIds). -delete_item(Host, Node, JID, ItemId) -> - node_default:delete_item(Host, Node, JID, ItemId). +delete_item(NodeId, JID, ItemId) -> + node_default:delete_item(NodeId, JID, ItemId). -purge_node(Host, Node, Owner) -> - node_default:purge_node(Host, Node, Owner). +purge_node(NodeId, Owner) -> + node_default:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> node_default:get_entity_affiliations(Host, Owner). -get_node_affiliations(Host, Node) -> - node_default:get_node_affiliations(Host, Node). +get_node_affiliations(NodeId) -> + node_default:get_node_affiliations(NodeId). -get_affiliation(Host, Node, Owner) -> - node_default:get_affiliation(Host, Node, Owner). +get_affiliation(NodeId, Owner) -> + node_default:get_affiliation(NodeId, Owner). -set_affiliation(Host, Node, Owner, Affiliation) -> - node_default:set_affiliation(Host, Node, Owner, Affiliation). +set_affiliation(NodeId, Owner, Affiliation) -> + node_default:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_default:get_entity_subscriptions(Host, Owner). -get_node_subscriptions(Host, Node) -> - node_default:get_node_subscriptions(Host, Node). +get_node_subscriptions(NodeId) -> + node_default:get_node_subscriptions(NodeId). -get_subscription(Host, Node, Owner) -> - node_default:get_subscription(Host, Node, Owner). +get_subscription(NodeId, Owner) -> + node_default:get_subscription(NodeId, Owner). -set_subscription(Host, Node, Owner, Subscription) -> - node_default:set_subscription(Host, Node, Owner, Subscription). +set_subscription(NodeId, Owner, Subscription) -> + node_default:set_subscription(NodeId, Owner, Subscription). -get_states(Host, Node) -> - node_default:get_states(Host, Node). +get_states(NodeId) -> + node_default:get_states(NodeId). -get_state(Host, Node, JID) -> - node_default:get_state(Host, Node, JID). +get_state(NodeId, JID) -> + node_default:get_state(NodeId, JID). set_state(State) -> node_default:set_state(State). -get_items(Host, Node, From) -> - node_default:get_items(Host, Node, From). +get_items(NodeId, From) -> + node_default:get_items(NodeId, From). -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) - node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) + node_default:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(Host, Node, ItemId) -> - node_default:get_item(Host, Node, ItemId). +get_item(NodeId, ItemId) -> + node_default:get_item(NodeId, ItemId). -get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_default:set_item(Item). diff --git a/src/mod_pubsub/node_buddy.erl b/src/mod_pubsub/node_buddy.erl index 6d9e1b549..b963fba20 100644 --- a/src/mod_pubsub/node_buddy.erl +++ b/src/mod_pubsub/node_buddy.erl @@ -44,29 +44,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1, get_item_name/3 ]). @@ -116,74 +116,74 @@ features() -> create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_default:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). -create_node(Host, Node, Owner) -> - node_default:create_node(Host, Node, Owner). +create_node(NodeId, Owner) -> + node_default:create_node(NodeId, Owner). -delete_node(Host, Removed) -> - node_default:delete_node(Host, Removed). +delete_node(Removed) -> + node_default:delete_node(Removed). -subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> - node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> + node_default:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). -unsubscribe_node(Host, Node, Sender, Subscriber, SubID) -> - node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID). +unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> + node_default:unsubscribe_node(NodeId, Sender, Subscriber, SubID). -publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> - node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload). +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> + node_default:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). -remove_extra_items(Host, Node, MaxItems, ItemIds) -> - node_default:remove_extra_items(Host, Node, MaxItems, ItemIds). +remove_extra_items(NodeId, MaxItems, ItemIds) -> + node_default:remove_extra_items(NodeId, MaxItems, ItemIds). -delete_item(Host, Node, JID, ItemId) -> - node_default:delete_item(Host, Node, JID, ItemId). +delete_item(NodeId, JID, ItemId) -> + node_default:delete_item(NodeId, JID, ItemId). -purge_node(Host, Node, Owner) -> - node_default:purge_node(Host, Node, Owner). +purge_node(NodeId, Owner) -> + node_default:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> node_default:get_entity_affiliations(Host, Owner). -get_node_affiliations(Host, Node) -> - node_default:get_node_affiliations(Host, Node). +get_node_affiliations(NodeId) -> + node_default:get_node_affiliations(NodeId). -get_affiliation(Host, Node, Owner) -> - node_default:get_affiliation(Host, Node, Owner). +get_affiliation(NodeId, Owner) -> + node_default:get_affiliation(NodeId, Owner). -set_affiliation(Host, Node, Owner, Affiliation) -> - node_default:set_affiliation(Host, Node, Owner, Affiliation). +set_affiliation(NodeId, Owner, Affiliation) -> + node_default:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_default:get_entity_subscriptions(Host, Owner). -get_node_subscriptions(Host, Node) -> - node_default:get_node_subscriptions(Host, Node). +get_node_subscriptions(NodeId) -> + node_default:get_node_subscriptions(NodeId). -get_subscription(Host, Node, Owner) -> - node_default:get_subscription(Host, Node, Owner). +get_subscription(NodeId, Owner) -> + node_default:get_subscription(NodeId, Owner). -set_subscription(Host, Node, Owner, Subscription) -> - node_default:set_subscription(Host, Node, Owner, Subscription). +set_subscription(NodeId, Owner, Subscription) -> + node_default:set_subscription(NodeId, Owner, Subscription). -get_states(Host, Node) -> - node_default:get_states(Host, Node). +get_states(NodeId) -> + node_default:get_states(NodeId). -get_state(Host, Node, JID) -> - node_default:get_state(Host, Node, JID). +get_state(NodeId, JID) -> + node_default:get_state(NodeId, JID). set_state(State) -> node_default:set_state(State). -get_items(Host, Node, From) -> - node_default:get_items(Host, Node, From). +get_items(NodeId, From) -> + node_default:get_items(NodeId, From). -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(Host, Node, ItemId) -> - node_default:get_item(Host, Node, ItemId). +get_item(NodeId, ItemId) -> + node_default:get_item(NodeId, ItemId). -get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_default:set_item(Item). diff --git a/src/mod_pubsub/node_club.erl b/src/mod_pubsub/node_club.erl index b5a04beda..1e3900cc0 100644 --- a/src/mod_pubsub/node_club.erl +++ b/src/mod_pubsub/node_club.erl @@ -44,29 +44,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1, get_item_name/3 ]). @@ -115,74 +115,74 @@ features() -> create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_default:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). -create_node(Host, Node, Owner) -> - node_default:create_node(Host, Node, Owner). +create_node(NodeId, Owner) -> + node_default:create_node(NodeId, Owner). -delete_node(Host, Removed) -> - node_default:delete_node(Host, Removed). +delete_node(Removed) -> + node_default:delete_node(Removed). -subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> - node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> + node_default:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). -unsubscribe_node(Host, Node, Sender, Subscriber, SubID) -> - node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID). +unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> + node_default:unsubscribe_node(NodeId, Sender, Subscriber, SubID). -publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> - node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload). +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> + node_default:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). -remove_extra_items(Host, Node, MaxItems, ItemIds) -> - node_default:remove_extra_items(Host, Node, MaxItems, ItemIds). +remove_extra_items(NodeId, MaxItems, ItemIds) -> + node_default:remove_extra_items(NodeId, MaxItems, ItemIds). -delete_item(Host, Node, JID, ItemId) -> - node_default:delete_item(Host, Node, JID, ItemId). +delete_item(NodeId, JID, ItemId) -> + node_default:delete_item(NodeId, JID, ItemId). -purge_node(Host, Node, Owner) -> - node_default:purge_node(Host, Node, Owner). +purge_node(NodeId, Owner) -> + node_default:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> node_default:get_entity_affiliations(Host, Owner). -get_node_affiliations(Host, Node) -> - node_default:get_node_affiliations(Host, Node). +get_node_affiliations(NodeId) -> + node_default:get_node_affiliations(NodeId). -get_affiliation(Host, Node, Owner) -> - node_default:get_affiliation(Host, Node, Owner). +get_affiliation(NodeId, Owner) -> + node_default:get_affiliation(NodeId, Owner). -set_affiliation(Host, Node, Owner, Affiliation) -> - node_default:set_affiliation(Host, Node, Owner, Affiliation). +set_affiliation(NodeId, Owner, Affiliation) -> + node_default:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_default:get_entity_subscriptions(Host, Owner). -get_node_subscriptions(Host, Node) -> - node_default:get_node_subscriptions(Host, Node). +get_node_subscriptions(NodeId) -> + node_default:get_node_subscriptions(NodeId). -get_subscription(Host, Node, Owner) -> - node_default:get_subscription(Host, Node, Owner). +get_subscription(NodeId, Owner) -> + node_default:get_subscription(NodeId, Owner). -set_subscription(Host, Node, Owner, Subscription) -> - node_default:set_subscription(Host, Node, Owner, Subscription). +set_subscription(NodeId, Owner, Subscription) -> + node_default:set_subscription(NodeId, Owner, Subscription). -get_states(Host, Node) -> - node_default:get_states(Host, Node). +get_states(NodeId) -> + node_default:get_states(NodeId). -get_state(Host, Node, JID) -> - node_default:get_state(Host, Node, JID). +get_state(NodeId, JID) -> + node_default:get_state(NodeId, JID). set_state(State) -> node_default:set_state(State). -get_items(Host, Node, From) -> - node_default:get_items(Host, Node, From). +get_items(NodeId, From) -> + node_default:get_items(NodeId, From). -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(Host, Node, ItemId) -> - node_default:get_item(Host, Node, ItemId). +get_item(NodeId, ItemId) -> + node_default:get_item(NodeId, ItemId). -get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_default:set_item(Item). diff --git a/src/mod_pubsub/node_default.erl b/src/mod_pubsub/node_default.erl index ae0707fcb..be4f3ebfa 100644 --- a/src/mod_pubsub/node_default.erl +++ b/src/mod_pubsub/node_default.erl @@ -51,29 +51,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1, get_item_name/3 ]). @@ -193,8 +193,8 @@ features() -> %%

    PubSub plugins can redefine the PubSub node creation rights as they %% which. They can simply delegate this check to the {@link node_default} %% module by implementing this function like this: -%% ```check_create_user_permission(Host, Node, Owner, Access) -> -%% node_default:check_create_user_permission(Host, Node, Owner, Access).'''

    +%% ```check_create_user_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> +%% node_default:check_create_user_permission(Host, ServerHost, Node, ParentNode, Owner, Access).'''

    create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) -> LOwner = jlib:short_prepd_jid(Owner), {User, Server, _Resource} = LOwner, @@ -215,35 +215,36 @@ create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) -> end, {result, Allowed}. -%% @spec (Host, Node, Owner) -> +%% @spec (NodeId, Owner) -> %% {result, Result} | exit -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% NodeId = mod_pubsub:pubsubNodeId() %% Owner = mod_pubsub:jid() %% @doc

    -create_node(Host, Node, Owner) -> +create_node(NodeId, Owner) -> OwnerKey = jlib:short_prepd_bare_jid(Owner), - set_state(#pubsub_state{stateid = {OwnerKey, {Host, Node}}, affiliation = owner}), + set_state(#pubsub_state{stateid = {OwnerKey, NodeId}, affiliation = owner}), {result, {default, broadcast}}. -%% @spec (Host, Removed) -> ok -%% Host = mod_pubsub:host() +%% @spec (Removed) -> ok %% Removed = [mod_pubsub:pubsubNode()] %% @doc

    purge items of deleted nodes after effective deletion.

    -delete_node(Host, Removed) -> - lists:foreach( - fun(Node) -> - lists:foreach( - fun(#pubsub_state{stateid = StateId, items = Items}) -> - del_items(Host, Node, Items), - del_state(StateId) - end, - mnesia:match_object( - #pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'})) - end, Removed), - {result, {default, broadcast, Removed}}. +delete_node(Removed) -> + Tr = fun(#pubsub_state{stateid = {J, _}, subscription = S}) -> + {J, S} + 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:map(Tr, States)} + end, Removed), + {result, {default, broadcast, Reply}}. -%% @spec (Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> +%% @spec (NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> %% {error, Reason} | {result, Result} %% @doc

    Accepts or rejects subcription requests on a PubSub node.

    %%

    The mechanism works as follow: @@ -276,17 +277,18 @@ delete_node(Host, Removed) -> %% to completly disable persistance. %%

    %%

    In the default plugin module, the record is unchanged.

    -subscribe_node(Host, Node, Sender, Subscriber, AccessModel, +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> SubKey = jlib:short_prepd_jid(Subscriber), GenKey = jlib:short_prepd_bare_jid(SubKey), Authorized = (jlib:short_prepd_bare_jid(Sender) == GenKey), - GenState = get_state(Host, Node, GenKey), + GenState = get_state(NodeId, GenKey), SubState = case SubKey of GenKey -> GenState; - _ -> get_state(Host, Node, SubKey) + _ -> get_state(NodeId, SubKey) end, Affiliation = GenState#pubsub_state.affiliation, + Subscription = SubState#pubsub_state.subscription, Whitelisted = lists:member(Affiliation, [member, publisher, owner]), if not Authorized -> @@ -295,7 +297,7 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel, Affiliation == outcast -> %% Requesting entity is blocked {error, 'forbidden'}; - SubState#pubsub_state.subscription == pending -> + Subscription == pending -> %% Requesting entity has pending subscription {error, ?ERR_EXTENDED('not-authorized', "pending-subscription")}; (AccessModel == presence) and (not PresenceSubscription) -> @@ -338,23 +340,22 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel, end end. -%% @spec (Host, Node, Sender, Subscriber, SubID) -> +%% @spec (NodeId, Sender, Subscriber, SubID) -> %% {error, Reason} | {result, []} -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% NodeId = mod_pubsub:pubsubNodeId() %% Sender = mod_pubsub:jid() %% Subscriber = mod_pubsub:jid() %% SubID = string() %% Reason = mod_pubsub:stanzaError() %% @doc

    Unsubscribe the Subscriber from the Node.

    -unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) -> +unsubscribe_node(NodeId, Sender, Subscriber, _SubId) -> SubKey = jlib:short_prepd_jid(Subscriber), GenKey = jlib:short_prepd_bare_jid(SubKey), Authorized = (jlib:short_prepd_bare_jid(Sender) == GenKey), - GenState = get_state(Host, Node, GenKey), + GenState = get_state(NodeId, GenKey), SubState = case SubKey of GenKey -> GenState; - _ -> get_state(Host, Node, SubKey) + _ -> get_state(NodeId, SubKey) end, if %% Requesting entity is prohibited from unsubscribing entity @@ -371,17 +372,16 @@ unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) -> {error, ?ERR_EXTENDED('unexpected-request', "not-subscribed")}; %% Was just subscriber, remove the record SubState#pubsub_state.affiliation == none -> - del_state(SubState#pubsub_state.stateid), + del_state(NodeId, SubKey), {result, default}; true -> set_state(SubState#pubsub_state{subscription = none}), {result, default} end. -%% @spec (Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) -> +%% @spec (NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload) -> %% {true, PubsubItem} | {result, Reply} -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% NodeId = mod_pubsub:pubsubNodeId() %% Publisher = mod_pubsub:jid() %% PublishModel = atom() %% MaxItems = integer() @@ -417,13 +417,13 @@ unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) -> %% to completly disable persistance. %%

    %%

    In the default plugin module, the record is unchanged.

    -publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) -> +publish_item(NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload) -> SubKey = jlib:short_prepd_jid(Publisher), GenKey = jlib:short_prepd_bare_jid(SubKey), - GenState = get_state(Host, Node, GenKey), + GenState = get_state(NodeId, GenKey), SubState = case SubKey of GenKey -> GenState; - _ -> get_state(Host, Node, SubKey) + _ -> get_state(NodeId, SubKey) end, Affiliation = GenState#pubsub_state.affiliation, Subscription = SubState#pubsub_state.subscription, @@ -436,31 +436,31 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) -> %% Entity does not have sufficient privileges to publish to node {error, 'forbidden'}; true -> - PubId = {SubKey, now()}, %% TODO, uses {now(),PublisherKey} for sorting (EJAB-824) %% TODO: check creation, presence, roster - Item = case get_item(Host, Node, ItemId) of + if MaxItems > 0 -> + PubId = {now(), SubKey}, + Item = case get_item(NodeId, ItemId) of {result, OldItem} -> OldItem#pubsub_item{modification = PubId, payload = Payload}; _ -> - #pubsub_item{itemid = {ItemId, {Host, Node}}, - creation = {GenKey, now()}, + #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( - Host, Node, MaxItems, Items), - if MaxItems > 0 -> set_item(Item); - true -> ok - end, - set_state(GenState#pubsub_state{items = NI}), - {result, {default, broadcast, OI}} + 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 (Host, Node, MaxItems, ItemIds) -> {NewItemIds,OldItemIds} -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% @spec (NodeId, MaxItems, ItemIds) -> {NewItemIds,OldItemIds} +%% NodeId = mod_pubsub:pubsubNodeId() %% MaxItems = integer() | unlimited %% ItemIds = [ItemId::string()] %% NewItemIds = [ItemId::string()] @@ -472,34 +472,33 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) -> %% 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(Host, Node, MaxItems, ItemIds) -> -%% node_default:remove_extra_items(Host, Node, MaxItems, ItemIds).'''

    -remove_extra_items(_Host, _Node, unlimited, ItemIds) -> +%% ```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(Host, Node, MaxItems, ItemIds) -> +remove_extra_items(NodeId, MaxItems, ItemIds) -> NewItems = lists:sublist(ItemIds, MaxItems), OldItems = lists:nthtail(length(NewItems), ItemIds), %% Remove extra items: - del_items(Host, Node, OldItems), + del_items(NodeId, OldItems), %% Return the new items list: {result, {NewItems, OldItems}}. -%% @spec (Host, Node, JID, ItemId) -> +%% @spec (NodeId, JID, ItemId) -> %% {error, Reason::stanzaError()} | %% {result, []} -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% NodeId = mod_pubsub:pubsubNodeId() %% JID = mod_pubsub:jid() %% ItemId = string() %% @doc

    Triggers item deletion.

    %%

    Default plugin: The user performing the deletion must be the node owner %% or a publisher.

    -delete_item(Host, Node, Publisher, ItemId) -> +delete_item(NodeId, Publisher, ItemId) -> GenKey = jlib:short_prepd_bare_jid(Publisher), - GenState = get_state(Host, Node, GenKey), + GenState = get_state(NodeId, GenKey), #pubsub_state{affiliation = Affiliation, items = Items} = GenState, Allowed = (Affiliation == publisher) orelse (Affiliation == owner) - orelse case get_item(Host, Node, ItemId) of + orelse case get_item(NodeId, ItemId) of {result, #pubsub_item{creation = {GenKey, _}}} -> true; _ -> false end, @@ -508,30 +507,30 @@ delete_item(Host, Node, Publisher, ItemId) -> %% Requesting entity does not have sufficient privileges {error, 'forbidden'}; true -> - case get_item(Host, Node, ItemId) of - {result, _} -> - del_item(Host, Node, ItemId), + case lists:member(ItemId, Items) of + true -> + del_item(NodeId, ItemId), NewItems = lists:delete(ItemId, Items), set_state(GenState#pubsub_state{items = NewItems}), {result, {default, broadcast}}; - _ -> + false -> %% Non-existent node or item {error, 'item-not-found'} end end. -%% @spec (Host, Node, Owner) -> +%% @spec (NodeId, Owner) -> %% {error, Reason::stanzaError()} | %% {result, {default, broadcast}} -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% NodeId = mod_pubsub:pubsubNodeId() %% Owner = mod_pubsub:jid() -purge_node(Host, Node, Owner) -> +purge_node(NodeId, Owner) -> GenKey = jlib:short_prepd_bare_jid(Owner), - GenState = get_state(Host, Node, GenKey), + GenState = get_state(NodeId, GenKey), case GenState of #pubsub_state{items = Items, affiliation = owner} -> - del_items(Host, Node, Items), + del_items(NodeId, Items), + set_state(GenState#pubsub_state{items = []}), {result, {default, broadcast}}; _ -> %% Entity is not owner @@ -548,42 +547,40 @@ purge_node(Host, Node, Owner) -> %% 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) -> +get_entity_affiliations(_Host, Owner) -> GenKey = jlib:short_prepd_bare_jid(Owner), States = mnesia:match_object( - #pubsub_state{stateid = {GenKey, {Host, '_'}}, _ = '_'}), - Tr = fun(#pubsub_state{stateid = {_, {_, N}}, affiliation = A}) -> + #pubsub_state{stateid = {GenKey, '_'}, _ = '_'}), + Tr = fun(#pubsub_state{stateid = {_, N}, affiliation = A}) -> {N, A} end, {result, lists:map(Tr, States)}. -get_node_affiliations(Host, Node) -> - {result, States} = get_states(Host, Node), +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(Host, Node, Owner) -> +get_affiliation(NodeId, Owner) -> GenKey = jlib:short_prepd_bare_jid(Owner), - GenState = get_state(Host, Node, GenKey), + GenState = get_state(NodeId, GenKey), {result, GenState#pubsub_state.affiliation}. -set_affiliation(Host, Node, Owner, Affiliation) -> +set_affiliation(NodeId, Owner, Affiliation) -> GenKey = jlib:short_prepd_bare_jid(Owner), - GenState = get_state(Host, Node, GenKey), + GenState = get_state(NodeId, GenKey), case {Affiliation, GenState#pubsub_state.subscription} of {none, none} -> - del_state(GenState#pubsub_state.stateid); + del_state(NodeId, GenKey); _ -> set_state(GenState#pubsub_state{affiliation = Affiliation}) - end, - ok. + end. %% @spec (Host, Owner) -> [{Node,Subscription}] %% Host = host() %% Owner = mod_pubsub:jid() -%% Node = mod_pubsub:pubsubNode() %% @doc

    Return 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 @@ -591,49 +588,47 @@ set_affiliation(Host, Node, Owner, Affiliation) -> %% 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) -> +get_entity_subscriptions(_Host, Owner) -> {U, D, _} = SubKey = jlib:short_prepd_jid(Owner), GenKey = jlib:short_prepd_bare_jid(SubKey), States = case SubKey of GenKey -> mnesia:match_object( - #pubsub_state{stateid = {{U, D, '_'}, {Host, '_'}}, _ = '_'}); + #pubsub_state{stateid = {{U, D, '_'}, '_'}, _ = '_'}); _ -> mnesia:match_object( - #pubsub_state{stateid = {GenKey, {Host, '_'}}, _ = '_'}) + #pubsub_state{stateid = {GenKey, '_'}, _ = '_'}) ++ mnesia:match_object( - #pubsub_state{stateid = {SubKey, {Host, '_'}}, _ = '_'}) + #pubsub_state{stateid = {SubKey, '_'}, _ = '_'}) end, - Tr = fun(#pubsub_state{stateid = {J, {_, N}}, subscription = S}) -> + Tr = fun(#pubsub_state{stateid = {J, N}, subscription = S}) -> {N, S, J} end, {result, lists:map(Tr, States)}. -get_node_subscriptions(Host, Node) -> - {result, States} = get_states(Host, Node), - Tr = fun(#pubsub_state{stateid = {J, {_, _}}, subscription = S}) -> +get_node_subscriptions(NodeId) -> + {result, States} = get_states(NodeId), + Tr = fun(#pubsub_state{stateid = {J, _}, subscription = S}) -> {J, S} end, {result, lists:map(Tr, States)}. -get_subscription(Host, Node, Owner) -> +get_subscription(NodeId, Owner) -> SubKey = jlib:short_prepd_jid(Owner), - SubState = get_state(Host, Node, SubKey), + SubState = get_state(NodeId, SubKey), {result, SubState#pubsub_state.subscription}. -set_subscription(Host, Node, Owner, Subscription) -> +set_subscription(NodeId, Owner, Subscription) -> SubKey = jlib:short_prepd_jid(Owner), - SubState = get_state(Host, Node, SubKey), + SubState = get_state(NodeId, SubKey), case {Subscription, SubState#pubsub_state.affiliation} of {none, none} -> - del_state(SubState#pubsub_state.stateid); + del_state(NodeId, SubKey); _ -> set_state(SubState#pubsub_state{subscription = Subscription}) end, ok. -%% @spec (Host, Node) -> [States] | [] -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() -%% Item = mod_pubsub:pubsubItems() +%% @spec (NodeId) -> [States] | [] +%% NodeId = mod_pubsub:pubsubNodeId() %% @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 @@ -642,21 +637,20 @@ set_subscription(Host, Node, Owner, Subscription) -> %% 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(Host, Node) -> -%% node_default:get_states(Host, Node).'''

    -get_states(Host, Node) -> +%% ```get_states(NodeId) -> +%% node_default:get_states(NodeId).'''

    +get_states(NodeId) -> States = mnesia:match_object( - #pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}), + #pubsub_state{stateid = {'_', NodeId}, _ = '_'}), {result, States}. -%% @spec (JID, Host, Node) -> [State] | [] -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% @spec (NodeId, JID) -> [State] | [] +%% NodeId = mod_pubsub:pubsubNodeId() %% JID = mod_pubsub:jid() %% State = mod_pubsub:pubsubItems() %% @doc

    Returns a state (one state list), given its reference.

    -get_state(Host, Node, JID) -> - StateId = {JID, {Host, Node}}, +get_state(NodeId, JID) -> + StateId = {JID, NodeId}, case mnesia:read({pubsub_state, StateId}) of [State] when is_record(State, pubsub_state) -> State; _ -> #pubsub_state{stateid=StateId} @@ -673,12 +667,11 @@ set_state(_) -> %% @spec (StateId) -> ok | {error, Reason::stanzaError()} %% StateId = mod_pubsub:pubsubStateId() %% @doc

    Delete a state from database.

    -del_state(StateId) -> - mnesia:delete({pubsub_state, StateId}). +del_state(NodeId, JID) -> + mnesia:delete({pubsub_state, {JID, NodeId}}). -%% @spec (Host, Node, From) -> [Items] | [] -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% @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.

    @@ -688,15 +681,15 @@ del_state(StateId) -> %% 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(Host, Node, From) -> -%% node_default:get_items(Host, Node, From).'''

    -get_items(Host, Node, _From) -> +%% ```get_items(NodeId, From) -> +%% node_default:get_items(NodeId, From).'''

    +get_items(NodeId, _From) -> Items = mnesia:match_object( - #pubsub_item{itemid = {'_', {Host, Node}}, _ = '_'}), + #pubsub_item{itemid = {'_', NodeId}, _ = '_'}), {result, Items}. -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> GenKey = jlib:short_prepd_bare_jid(JID), - GenState = get_state(Host, Node, GenKey), + GenState = get_state(NodeId, GenKey), Affiliation = GenState#pubsub_state.affiliation, Subscription = GenState#pubsub_state.subscription, Whitelisted = can_fetch_item(Affiliation, Subscription), @@ -726,25 +719,24 @@ get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubI %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; true -> - get_items(Host, Node, JID) + get_items(NodeId, JID) end. -%% @spec (Host, Node, ItemId) -> [Item] | [] -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% @spec (NodeId, ItemId) -> [Item] | [] +%% NodeId = mod_pubsub:pubsubNodeId() %% ItemId = string() %% Item = mod_pubsub:pubsubItems() %% @doc

    Returns an item (one item list), given its reference.

    -get_item(Host, Node, ItemId) -> - case mnesia:read({pubsub_item, {ItemId, {Host, Node}}}) of +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(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> GenKey = jlib:short_prepd_bare_jid(JID), - GenState = get_state(Host, Node, GenKey), + GenState = get_state(NodeId, GenKey), Affiliation = GenState#pubsub_state.affiliation, Subscription = GenState#pubsub_state.subscription, Whitelisted = can_fetch_item(Affiliation, Subscription), @@ -774,7 +766,7 @@ get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; true -> - get_item(Host, Node, ItemId) + get_item(NodeId, ItemId) end. %% @spec (Item) -> ok | {error, Reason::stanzaError()} @@ -785,21 +777,20 @@ set_item(Item) when is_record(Item, pubsub_item) -> set_item(_) -> {error, 'internal-server-error'}. -%% @spec (Host, Node, ItemId) -> ok | {error, Reason::stanzaError()} -%% Host = mod_pubsub:host() -%% Node = mod_pubsub:pubsubNode() +%% @spec (NodeId, ItemId) -> ok | {error, Reason::stanzaError()} +%% NodeId = mod_pubsub:pubsubNodeId() %% ItemId = string() %% @doc

    Delete an item from database.

    -del_item(Host, Node, ItemId) -> - mnesia:delete({pubsub_item, {ItemId, {Host, Node}}}). -del_items(Host, Node, ItemIds) -> +del_item(NodeId, ItemId) -> + mnesia:delete({pubsub_item, {ItemId, NodeId}}). +del_items(NodeId, ItemIds) -> lists:foreach(fun(ItemId) -> - del_item(Host, Node, ItemId) + del_item(NodeId, ItemId) end, ItemIds). %% @doc

    Return the name of the node if known: Default is to return %% node id.

    -get_item_name(_Host, _Node, Id) -> +get_item_name(_host, _Node, Id) -> Id. %% @spec (Affiliation, Subscription) -> true | false diff --git a/src/mod_pubsub/node_dispatch.erl b/src/mod_pubsub/node_dispatch.erl index 2d9e245e9..d5b379844 100644 --- a/src/mod_pubsub/node_dispatch.erl +++ b/src/mod_pubsub/node_dispatch.erl @@ -42,29 +42,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1, get_item_name/3 ]). @@ -112,79 +112,81 @@ features() -> create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_default:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). -create_node(Host, Node, Owner) -> - node_default:create_node(Host, Node, Owner). +create_node(NodeId, Owner) -> + node_default:create_node(NodeId, Owner). -delete_node(Host, Removed) -> - node_default:delete_node(Host, Removed). +delete_node(Removed) -> + node_default:delete_node(Removed). -subscribe_node(_Host, _Node, _Sender, _Subscriber, _AccessModel, +subscribe_node(_NodeId, _Sender, _Subscriber, _AccessModel, _SendLast, _PresenceSubscription, _RosterGroup) -> {error, 'forbidden'}. -unsubscribe_node(_Host, _Node, _Sender, _Subscriber, _SubID) -> +unsubscribe_node(_NodeId, _Sender, _Subscriber, _SubID) -> {error, 'forbidden'}. -publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> lists:foreach(fun(SubNode) -> node_default:publish_item( - Host, SubNode, Publisher, Model, + SubNode#pubsub_node.id, Publisher, Model, MaxItems, ItemId, Payload) - end, nodetree_default:get_subnodes(Host, Node, Publisher)). + end, nodetree_default:get_subnodes(NodeId, Publisher)). -remove_extra_items(_Host, _Node, _MaxItems, ItemIds) -> +remove_extra_items(_NodeId, _MaxItems, ItemIds) -> {result, {ItemIds, []}}. -delete_item(_Host, _Node, _JID, _ItemId) -> +delete_item(_NodeId, _JID, _ItemId) -> {error, 'item-not-found'}. -purge_node(_Host, _Node, _Owner) -> +purge_node(_NodeId, _Owner) -> {error, 'forbidden'}. get_entity_affiliations(_Host, _Owner) -> {result, []}. -get_node_affiliations(_Host, _Node) -> +get_node_affiliations(_NodeId) -> {result, []}. -get_affiliation(_Host, _Node, _Owner) -> +get_affiliation(_NodeId, _Owner) -> {result, []}. -set_affiliation(Host, Node, Owner, Affiliation) -> - node_default:set_affiliation(Host, Node, Owner, Affiliation). +set_affiliation(NodeId, Owner, Affiliation) -> + node_default:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(_Host, _Owner) -> {result, []}. -get_node_subscriptions(_Host, _Node) -> +get_node_subscriptions(NodeId) -> + %% note: get_node_subscriptions is used for broadcasting + %% DO NOT REMOVE + node_default:get_node_subscriptions(NodeId). + +get_subscription(_NodeId, _Owner) -> {result, []}. -get_subscription(_Host, _Node, _Owner) -> - {result, []}. +set_subscription(NodeId, Owner, Subscription) -> + node_default:set_subscription(NodeId, Owner, Subscription). -set_subscription(Host, Node, Owner, Subscription) -> - node_default:set_subscription(Host, Node, Owner, Subscription). +get_states(NodeId) -> + node_default:get_states(NodeId). -get_states(Host, Node) -> - node_default:get_states(Host, Node). - -get_state(Host, Node, JID) -> - node_default:get_state(Host, Node, JID). +get_state(NodeId, JID) -> + node_default:get_state(NodeId, JID). set_state(State) -> node_default:set_state(State). -get_items(Host, Node, From) -> - node_default:get_items(Host, Node, From). +get_items(NodeId, From) -> + node_default:get_items(NodeId, From). -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(Host, Node, ItemId) -> - node_default:get_item(Host, Node, ItemId). +get_item(NodeId, ItemId) -> + node_default:get_item(NodeId, ItemId). -get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_default:set_item(Item). diff --git a/src/mod_pubsub/node_flat.erl b/src/mod_pubsub/node_flat.erl index e9e983aa8..8fb33e447 100644 --- a/src/mod_pubsub/node_flat.erl +++ b/src/mod_pubsub/node_flat.erl @@ -35,29 +35,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1, get_item_name/3 ]). @@ -103,74 +103,74 @@ create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> end, {result, Allowed}. -create_node(Host, Node, Owner) -> - node_default:create_node(Host, Node, Owner). +create_node(NodeId, Owner) -> + node_default:create_node(NodeId, Owner). -delete_node(Host, Removed) -> - node_default:delete_node(Host, Removed). +delete_node(Removed) -> + node_default:delete_node(Removed). -subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> - node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> + node_default:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). -unsubscribe_node(Host, Node, Sender, Subscriber, SubID) -> - node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID). +unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> + node_default:unsubscribe_node(NodeId, Sender, Subscriber, SubID). -publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> - node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload). +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> + node_default:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). -remove_extra_items(Host, Node, MaxItems, ItemIds) -> - node_default:remove_extra_items(Host, Node, MaxItems, ItemIds). +remove_extra_items(NodeId, MaxItems, ItemIds) -> + node_default:remove_extra_items(NodeId, MaxItems, ItemIds). -delete_item(Host, Node, JID, ItemId) -> - node_default:delete_item(Host, Node, JID, ItemId). +delete_item(NodeId, JID, ItemId) -> + node_default:delete_item(NodeId, JID, ItemId). -purge_node(Host, Node, Owner) -> - node_default:purge_node(Host, Node, Owner). +purge_node(NodeId, Owner) -> + node_default:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> node_default:get_entity_affiliations(Host, Owner). -get_node_affiliations(Host, Node) -> - node_default:get_node_affiliations(Host, Node). +get_node_affiliations(NodeId) -> + node_default:get_node_affiliations(NodeId). -get_affiliation(Host, Node, Owner) -> - node_default:get_affiliation(Host, Node, Owner). +get_affiliation(NodeId, Owner) -> + node_default:get_affiliation(NodeId, Owner). -set_affiliation(Host, Node, Owner, Affiliation) -> - node_default:set_affiliation(Host, Node, Owner, Affiliation). +set_affiliation(NodeId, Owner, Affiliation) -> + node_default:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_default:get_entity_subscriptions(Host, Owner). -get_node_subscriptions(Host, Node) -> - node_default:get_node_subscriptions(Host, Node). +get_node_subscriptions(NodeId) -> + node_default:get_node_subscriptions(NodeId). -get_subscription(Host, Node, Owner) -> - node_default:get_subscription(Host, Node, Owner). +get_subscription(NodeId, Owner) -> + node_default:get_subscription(NodeId, Owner). -set_subscription(Host, Node, Owner, Subscription) -> - node_default:set_subscription(Host, Node, Owner, Subscription). +set_subscription(NodeId, Owner, Subscription) -> + node_default:set_subscription(NodeId, Owner, Subscription). -get_states(Host, Node) -> - node_default:get_states(Host, Node). +get_states(NodeId) -> + node_default:get_states(NodeId). -get_state(Host, Node, JID) -> - node_default:get_state(Host, Node, JID). +get_state(NodeId, JID) -> + node_default:get_state(NodeId, JID). set_state(State) -> node_default:set_state(State). -get_items(Host, Node, From) -> - node_default:get_items(Host, Node, From). +get_items(NodeId, From) -> + node_default:get_items(NodeId, From). -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(Host, Node, ItemId) -> - node_default:get_item(Host, Node, ItemId). +get_item(NodeId, ItemId) -> + node_default:get_item(NodeId, ItemId). -get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_default:set_item(Item). diff --git a/src/mod_pubsub/node_mb.erl b/src/mod_pubsub/node_mb.erl index 90a5cdad2..83413cd39 100644 --- a/src/mod_pubsub/node_mb.erl +++ b/src/mod_pubsub/node_mb.erl @@ -47,29 +47,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1, get_item_name/3 ]). @@ -120,77 +120,77 @@ features() -> create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_pep:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). -create_node(Host, Node, Owner) -> - node_pep:create_node(Host, Node, Owner). +create_node(NodeId, Owner) -> + node_pep:create_node(NodeId, Owner). -delete_node(Host, Removed) -> - node_pep:delete_node(Host, Removed). +delete_node(Removed) -> + node_pep:delete_node(Removed). -subscribe_node(Host, Node, Sender, Subscriber, AccessModel, +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> node_pep:subscribe_node( - Host, Node, Sender, Subscriber, AccessModel, SendLast, + NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). -unsubscribe_node(Host, Node, Sender, Subscriber, SubID) -> - node_pep:unsubscribe_node(Host, Node, Sender, Subscriber, SubID). +unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> + node_pep:unsubscribe_node(NodeId, Sender, Subscriber, SubID). -publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> - node_pep:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload). +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> + node_pep:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). -remove_extra_items(Host, Node, MaxItems, ItemIds) -> - node_pep:remove_extra_items(Host, Node, MaxItems, ItemIds). +remove_extra_items(NodeId, MaxItems, ItemIds) -> + node_pep:remove_extra_items(NodeId, MaxItems, ItemIds). -delete_item(Host, Node, JID, ItemId) -> - node_pep:delete_item(Host, Node, JID, ItemId). +delete_item(NodeId, JID, ItemId) -> + node_pep:delete_item(NodeId, JID, ItemId). -purge_node(Host, Node, Owner) -> - node_pep:purge_node(Host, Node, Owner). +purge_node(NodeId, Owner) -> + node_pep:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> node_pep:get_entity_affiliations(Host, Owner). -get_node_affiliations(Host, Node) -> - node_pep:get_node_affiliations(Host, Node). +get_node_affiliations(NodeId) -> + node_pep:get_node_affiliations(NodeId). -get_affiliation(Host, Node, Owner) -> - node_pep:get_affiliation(Host, Node, Owner). +get_affiliation(NodeId, Owner) -> + node_pep:get_affiliation(NodeId, Owner). -set_affiliation(Host, Node, Owner, Affiliation) -> - node_pep:set_affiliation(Host, Node, Owner, Affiliation). +set_affiliation(NodeId, Owner, Affiliation) -> + node_pep:set_affiliation(NodeId, Owner, Affiliation). -get_entity_subscriptions(Host,Owner) -> +get_entity_subscriptions(Host, Owner) -> node_pep:get_entity_subscriptions(Host, Owner). -get_node_subscriptions(Host, Node) -> - node_pep:get_node_subscriptions(Host, Node). +get_node_subscriptions(NodeId) -> + node_pep:get_node_subscriptions(NodeId). -get_subscription(Host,Node,Owner) -> - node_pep:get_subscription(Host,Node,Owner). +get_subscription(NodeId, Owner) -> + node_pep:get_subscription(NodeId, Owner). -set_subscription(Host, Node, Owner, Subscription) -> - node_pep:set_subscription(Host, Node, Owner, Subscription). +set_subscription(NodeId, Owner, Subscription) -> + node_pep:set_subscription(NodeId, Owner, Subscription). -get_states(Host, Node) -> - node_pep:get_states(Host, Node). +get_states(NodeId) -> + node_pep:get_states(NodeId). -get_state(Host, Node, JID) -> - node_pep:get_state(Host, Node, JID). +get_state(NodeId, JID) -> + node_pep:get_state(NodeId, JID). set_state(State) -> node_pep:set_state(State). -get_items(Host, Node, From) -> - node_pep:get_items(Host, Node, From). +get_items(NodeId, From) -> + node_pep:get_items(NodeId, From). -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_pep:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_pep:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(Host, Node, ItemId) -> - node_pep:get_item(Host, Node, ItemId). +get_item(NodeId, ItemId) -> + node_pep:get_item(NodeId, ItemId). -get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_pep:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_pep:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_pep:set_item(Item). diff --git a/src/mod_pubsub/node_pep.erl b/src/mod_pubsub/node_pep.erl index bf13d3520..6c3f83d21 100644 --- a/src/mod_pubsub/node_pep.erl +++ b/src/mod_pubsub/node_pep.erl @@ -40,29 +40,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1, get_item_name/3 ]). @@ -133,92 +133,92 @@ create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> end, {result, Allowed}. -create_node(Host, Node, Owner) -> - case node_default:create_node(Host, Node, Owner) of +create_node(NodeId, Owner) -> + case node_default:create_node(NodeId, Owner) of {result, _} -> {result, []}; Error -> Error end. -delete_node(Host, Removed) -> - case node_default:delete_node(Host, Removed) of +delete_node(Removed) -> + case node_default:delete_node(Removed) of {result, {_, _, Removed}} -> {result, {[], Removed}}; Error -> Error end. -subscribe_node(Host, Node, Sender, Subscriber, AccessModel, +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> node_default:subscribe_node( - Host, Node, Sender, Subscriber, AccessModel, SendLast, + NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). -unsubscribe_node(Host, Node, Sender, Subscriber, SubID) -> - case node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID) of +unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> + case node_default:unsubscribe_node(NodeId, Sender, Subscriber, SubID) of {error, Error} -> {error, Error}; {result, _} -> {result, []} end. -publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> - node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload). +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> + node_default:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). -remove_extra_items(Host, Node, MaxItems, ItemIds) -> - node_default:remove_extra_items(Host, Node, MaxItems, ItemIds). +remove_extra_items(NodeId, MaxItems, ItemIds) -> + node_default:remove_extra_items(NodeId, MaxItems, ItemIds). -delete_item(Host, Node, JID, ItemId) -> - node_default:delete_item(Host, Node, JID, ItemId). +delete_item(NodeId, JID, ItemId) -> + node_default:delete_item(NodeId, JID, ItemId). -purge_node(Host, Node, Owner) -> - node_default:purge_node(Host, Node, Owner). +purge_node(NodeId, Owner) -> + node_default:purge_node(NodeId, Owner). get_entity_affiliations(_Host, Owner) -> OwnerKey = jlib:short_prepd_bare_jid(Owner), node_default:get_entity_affiliations(OwnerKey, Owner). -get_node_affiliations(Host, Node) -> - OwnerKey = jlib:short_bare_jid(Host), - node_default:get_node_affiliations(OwnerKey, Node). +get_node_affiliations(NodeId) -> + node_default:get_node_affiliations(NodeId). -get_affiliation(_Host, Node, Owner) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - node_default:get_affiliation(OwnerKey, Node, Owner). +get_affiliation(NodeId, Owner) -> + node_default:get_affiliation(NodeId, Owner). -set_affiliation(_Host, Node, Owner, Affiliation) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - State = get_state(OwnerKey, Node, OwnerKey), - set_state(State#pubsub_state{affiliation = Affiliation}), - ok. +set_affiliation(NodeId, Owner, Affiliation) -> + node_default:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(_Host, _Owner) -> {result, []}. -get_node_subscriptions(_Host, _Node) -> - {result, []}. +get_node_subscriptions(NodeId) -> + %% note: get_node_subscriptions is used for broadcasting + %% there should not have any subscriptions + %% but that call returns also all subscription to none + %% and this is required for broadcast to occurs + %% DO NOT REMOVE + node_default:get_node_subscriptions(NodeId). -get_subscription(_Host, _Node, _Owner) -> +get_subscription(_NodeId, _Owner) -> {result, none}. -set_subscription(_Host, _Node, _Owner, _Subscription) -> +set_subscription(_NodeId, _Owner, _Subscription) -> ok. -get_states(Host, Node) -> - node_default:get_states(Host, Node). +get_states(NodeId) -> + node_default:get_states(NodeId). -get_state(Host, Node, JID) -> - node_default:get_state(Host, Node, JID). +get_state(NodeId, JID) -> + node_default:get_state(NodeId, JID). set_state(State) -> node_default:set_state(State). -get_items(Host, Node, From) -> - node_default:get_items(Host, Node, From). +get_items(NodeId, From) -> + node_default:get_items(NodeId, From). -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(Host, Node, ItemId) -> - node_default:get_item(Host, Node, ItemId). +get_item(NodeId, ItemId) -> + node_default:get_item(NodeId, ItemId). -get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_default:set_item(Item). diff --git a/src/mod_pubsub/node_private.erl b/src/mod_pubsub/node_private.erl index 32f86fbb8..8458d50dc 100644 --- a/src/mod_pubsub/node_private.erl +++ b/src/mod_pubsub/node_private.erl @@ -44,29 +44,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1, get_item_name/3 ]). @@ -116,76 +116,76 @@ create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_default:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). -create_node(Host, Node, Owner) -> - node_default:create_node(Host, Node, Owner). +create_node(NodeId, Owner) -> + node_default:create_node(NodeId, Owner). -delete_node(Host, Removed) -> - node_default:delete_node(Host, Removed). +delete_node(Removed) -> + node_default:delete_node(Removed). -subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> - node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, + node_default:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). -unsubscribe_node(Host, Node, Sender, Subscriber, SubID) -> - node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID). +unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> + node_default:unsubscribe_node(NodeId, Sender, Subscriber, SubID). -publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> - node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload). +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> + node_default:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). -remove_extra_items(Host, Node, MaxItems, ItemIds) -> - node_default:remove_extra_items(Host, Node, MaxItems, ItemIds). +remove_extra_items(NodeId, MaxItems, ItemIds) -> + node_default:remove_extra_items(NodeId, MaxItems, ItemIds). -delete_item(Host, Node, JID, ItemId) -> - node_default:delete_item(Host, Node, JID, ItemId). +delete_item(NodeId, JID, ItemId) -> + node_default:delete_item(NodeId, JID, ItemId). -purge_node(Host, Node, Owner) -> - node_default:purge_node(Host, Node, Owner). +purge_node(NodeId, Owner) -> + node_default:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> node_default:get_entity_affiliations(Host, Owner). -get_node_affiliations(Host, Node) -> - node_default:get_node_affiliations(Host, Node). +get_node_affiliations(NodeId) -> + node_default:get_node_affiliations(NodeId). -get_affiliation(Host, Node, Owner) -> - node_default:get_affiliation(Host, Node, Owner). +get_affiliation(NodeId, Owner) -> + node_default:get_affiliation(NodeId, Owner). -set_affiliation(Host, Node, Owner, Affiliation) -> - node_default:set_affiliation(Host, Node, Owner, Affiliation). +set_affiliation(NodeId, Owner, Affiliation) -> + node_default:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_default:get_entity_subscriptions(Host, Owner). -get_node_subscriptions(Host, Node) -> - node_default:get_node_subscriptions(Host, Node). +get_node_subscriptions(NodeId) -> + node_default:get_node_subscriptions(NodeId). -get_subscription(Host, Node, Owner) -> - node_default:get_subscription(Host, Node, Owner). +get_subscription(NodeId, Owner) -> + node_default:get_subscription(NodeId, Owner). -set_subscription(Host, Node, Owner, Subscription) -> - node_default:set_subscription(Host, Node, Owner, Subscription). +set_subscription(NodeId, Owner, Subscription) -> + node_default:set_subscription(NodeId, Owner, Subscription). -get_states(Host, Node) -> - node_default:get_states(Host, Node). +get_states(NodeId) -> + node_default:get_states(NodeId). -get_state(Host, Node, JID) -> - node_default:get_state(Host, Node, JID). +get_state(NodeId, JID) -> + node_default:get_state(NodeId, JID). set_state(State) -> node_default:set_state(State). -get_items(Host, Node, From) -> - node_default:get_items(Host, Node, From). +get_items(NodeId, From) -> + node_default:get_items(NodeId, From). -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(Host, Node, ItemId) -> - node_default:get_item(Host, Node, ItemId). +get_item(NodeId, ItemId) -> + node_default:get_item(NodeId, ItemId). -get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_default:set_item(Item). diff --git a/src/mod_pubsub/node_public.erl b/src/mod_pubsub/node_public.erl index c8fa085ab..2c8d702be 100644 --- a/src/mod_pubsub/node_public.erl +++ b/src/mod_pubsub/node_public.erl @@ -44,29 +44,29 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, - create_node/3, - delete_node/2, - purge_node/3, - subscribe_node/8, - unsubscribe_node/5, - publish_item/7, - delete_item/4, - remove_extra_items/4, + create_node/2, + delete_node/1, + purge_node/2, + subscribe_node/7, + unsubscribe_node/4, + publish_item/6, + delete_item/3, + remove_extra_items/3, get_entity_affiliations/2, - get_node_affiliations/2, - get_affiliation/3, - set_affiliation/4, + get_node_affiliations/1, + get_affiliation/2, + set_affiliation/3, get_entity_subscriptions/2, - get_node_subscriptions/2, - get_subscription/3, - set_subscription/4, - get_states/2, - get_state/3, + get_node_subscriptions/1, + get_subscription/2, + set_subscription/3, + get_states/1, + get_state/2, set_state/1, - get_items/7, - get_items/3, - get_item/8, - get_item/3, + get_items/6, + get_items/2, + get_item/7, + get_item/2, set_item/1, get_item_name/3 ]). @@ -115,74 +115,74 @@ features() -> create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) -> node_default:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access). -create_node(Host, Node, Owner) -> - node_default:create_node(Host, Node, Owner). +create_node(NodeId, Owner) -> + node_default:create_node(NodeId, Owner). -delete_node(Host, Removed) -> - node_default:delete_node(Host, Removed). +delete_node(Removed) -> + node_default:delete_node(Removed). -subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> - node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). +subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> + node_default:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup). -unsubscribe_node(Host, Node, Sender, Subscriber, SubID) -> - node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID). +unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> + node_default:unsubscribe_node(NodeId, Sender, Subscriber, SubID). -publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) -> - node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload). +publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) -> + node_default:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload). -remove_extra_items(Host, Node, MaxItems, ItemIds) -> - node_default:remove_extra_items(Host, Node, MaxItems, ItemIds). +remove_extra_items(NodeId, MaxItems, ItemIds) -> + node_default:remove_extra_items(NodeId, MaxItems, ItemIds). -delete_item(Host, Node, JID, ItemId) -> - node_default:delete_item(Host, Node, JID, ItemId). +delete_item(NodeId, JID, ItemId) -> + node_default:delete_item(NodeId, JID, ItemId). -purge_node(Host, Node, Owner) -> - node_default:purge_node(Host, Node, Owner). +purge_node(NodeId, Owner) -> + node_default:purge_node(NodeId, Owner). get_entity_affiliations(Host, Owner) -> node_default:get_entity_affiliations(Host, Owner). -get_node_affiliations(Host, Node) -> - node_default:get_node_affiliations(Host, Node). +get_node_affiliations(NodeId) -> + node_default:get_node_affiliations(NodeId). -get_affiliation(Host, Node, Owner) -> - node_default:get_affiliation(Host, Node, Owner). +get_affiliation(NodeId, Owner) -> + node_default:get_affiliation(NodeId, Owner). -set_affiliation(Host, Node, Owner, Affiliation) -> - node_default:set_affiliation(Host, Node, Owner, Affiliation). +set_affiliation(NodeId, Owner, Affiliation) -> + node_default:set_affiliation(NodeId, Owner, Affiliation). get_entity_subscriptions(Host, Owner) -> node_default:get_entity_subscriptions(Host, Owner). -get_node_subscriptions(Host, Node) -> - node_default:get_node_subscriptions(Host, Node). +get_node_subscriptions(NodeId) -> + node_default:get_node_subscriptions(NodeId). -get_subscription(Host, Node, Owner) -> - node_default:get_subscription(Host, Node, Owner). +get_subscription(NodeId, Owner) -> + node_default:get_subscription(NodeId, Owner). -set_subscription(Host, Node, Owner, Subscription) -> - node_default:set_subscription(Host, Node, Owner, Subscription). +set_subscription(NodeId, Owner, Subscription) -> + node_default:set_subscription(NodeId, Owner, Subscription). -get_states(Host, Node) -> - node_default:get_states(Host, Node). +get_states(NodeId) -> + node_default:get_states(NodeId). -get_state(Host, Node, JID) -> - node_default:get_state(Host, Node, JID). +get_state(NodeId, JID) -> + node_default:get_state(NodeId, JID). set_state(State) -> node_default:set_state(State). -get_items(Host, Node, From) -> - node_default:get_items(Host, Node, From). +get_items(NodeId, From) -> + node_default:get_items(NodeId, From). -get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). -get_item(Host, Node, ItemId) -> - node_default:get_item(Host, Node, ItemId). +get_item(NodeId, ItemId) -> + node_default:get_item(NodeId, ItemId). -get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> - node_default:get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). +get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> + node_default:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId). set_item(Item) -> node_default:set_item(Item). diff --git a/src/mod_pubsub/nodetree_default.erl b/src/mod_pubsub/nodetree_default.erl index b8a6b6f26..d529849ff 100644 --- a/src/mod_pubsub/nodetree_default.erl +++ b/src/mod_pubsub/nodetree_default.erl @@ -51,7 +51,7 @@ get_nodes/2, get_nodes/1, get_subnodes/3, - get_subnodes_tree/2, + get_subnodes_tree/3, create_node/5, delete_node/2 ]). @@ -76,9 +76,10 @@ init(_Host, _ServerHost, _Opts) -> {attributes, record_info(fields, pubsub_node)}]), NodesFields = record_info(fields, pubsub_node), case mnesia:table_info(pubsub_node, attributes) of - [host_node, host_parent, info] -> ok; % old schema, updated later by pubsub NodesFields -> ok; - _ -> mnesia:transform_table(pubsub_node, ignore, NodesFields) + _ -> + ok + %% mnesia:transform_table(pubsub_state, ignore, StatesFields) end, ok. terminate(_Host, _ServerHost) -> @@ -97,12 +98,11 @@ set_node(Record) when is_record(Record, pubsub_node) -> set_node(_) -> {error, 'internal-server-error'}. -get_node(Host, Node, _From) -> - get_node(Host, Node). - %% @spec (Host, Node) -> pubsubNode() | {error, Reason} %% Host = mod_pubsub:host() %% Node = mod_pubsub:pubsubNode() +get_node(Host, Node, _From) -> + get_node(Host, Node). get_node(Host, Node) -> case catch mnesia:read({pubsub_node, {Host, Node}}) of [Record] when is_record(Record, pubsub_node) -> Record; @@ -110,44 +110,48 @@ get_node(Host, Node) -> Error -> Error end. -get_nodes(Key, _From) -> - get_nodes(Key). - -%% @spec (Key) -> [pubsubNode()] | {error, Reason} -%% Key = mod_pubsub:host() | mod_pubsub:jid() -get_nodes(Key) -> - mnesia:match_object(#pubsub_node{nodeid = {Key, '_'}, _ = '_'}). +%% @spec (Host) -> [pubsubNode()] | {error, Reason} +%% Host = mod_pubsub:host() | mod_pubsub:jid() +get_nodes(Host, _From) -> + get_nodes(Host). +get_nodes(Host) -> + mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}). %% @spec (Host, Node, From) -> [pubsubNode()] | {error, Reason} %% Host = mod_pubsub:host() %% Node = mod_pubsub:pubsubNode() %% From = mod_pubsub:jid() get_subnodes(Host, Node, _From) -> - mnesia:match_object(#pubsub_node{parentid = {Host, Node}, _ = '_'}). + get_subnodes(Host, Node). +get_subnodes(Host, Node) -> + mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, parent = Node, _ = '_'}). %% @spec (Host, Index) -> [pubsubNode()] | {error, Reason} %% Host = mod_pubsub:host() %% Node = mod_pubsub:pubsubNode() +%% From = mod_pubsub:jid() +get_subnodes_tree(Host, Node, _From) -> + get_subnodes_tree(Host, Node). get_subnodes_tree(Host, Node) -> - mnesia:foldl(fun(#pubsub_node{nodeid = {H, N}}, Acc) -> + mnesia:foldl(fun(#pubsub_node{nodeid = {H, N} = R}, Acc) -> case lists:prefix(Node, N) and (H == Host) of - true -> [N | Acc]; + true -> [R | Acc]; _ -> Acc end end, [], pubsub_node). -%% @spec (Key, Node, Type, Owner, Options) -> ok | {error, Reason} -%% Key = mod_pubsub:host() | mod_pubsub:jid() +%% @spec (Host, Node, Type, Owner, Options) -> ok | {error, Reason} +%% Host = mod_pubsub:host() | mod_pubsub:jid() %% Node = mod_pubsub:pubsubNode() %% NodeType = mod_pubsub:nodeType() %% Owner = mod_pubsub:jid() %% Options = list() -create_node(Key, Node, Type, Owner, Options) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - case mnesia:read({pubsub_node, {Key, Node}}) of +create_node(Host, Node, Type, Owner, Options) -> + BJID = jlib:short_prepd_bare_jid(Owner), + case mnesia:read({pubsub_node, {Host, Node}}) of [] -> {ParentNode, ParentExists} = - case Key of + case Host of {_U, _S, _R} -> %% This is special case for PEP handling %% PEP does not uses hierarchy @@ -158,7 +162,7 @@ create_node(Key, Node, Type, Owner, Options) -> [] -> {[], true}; _ -> - case mnesia:read({pubsub_node, {Key, Parent}}) of + case mnesia:read({pubsub_node, {Host, Parent}}) of [] -> {Parent, false}; _ -> {Parent, true} end @@ -166,13 +170,14 @@ create_node(Key, Node, Type, Owner, Options) -> end, case ParentExists of true -> - %% Service requires registration - %%{error, ?ERR_REGISTRATION_REQUIRED}; - mnesia:write(#pubsub_node{nodeid = {Key, Node}, - parentid = {Key, ParentNode}, + NodeId = pubsub_index:new(node), + mnesia:write(#pubsub_node{nodeid = {Host, Node}, + id = NodeId, + parent = ParentNode, type = Type, - owners = [OwnerKey], - options = Options}); + owners = [BJID], + options = Options}), + {ok, NodeId}; false -> %% Requesting entity is prohibited from creating nodes {error, 'forbidden'} @@ -182,12 +187,13 @@ create_node(Key, Node, Type, Owner, Options) -> {error, 'conflict'} end. -%% @spec (Key, Node) -> [mod_pubsub:node()] -%% Key = mod_pubsub:host() | mod_pubsub:jid() +%% @spec (Host, Node) -> [mod_pubsub:node()] +%% Host = mod_pubsub:host() | mod_pubsub:jid() %% Node = mod_pubsub:pubsubNode() -delete_node(Key, Node) -> - Removed = get_subnodes_tree(Key, Node), - lists:foreach(fun(N) -> - mnesia:delete({pubsub_node, {Key, N}}) - end, Removed), +delete_node(Host, Node) -> + Removed = get_subnodes_tree(Host, Node), + lists:foreach(fun(#pubsub_node{nodeid = {_, N}, id = I}) -> + pubsub_index:free(node, I), + mnesia:delete({pubsub_node, {Host, N}}) + end, Removed), Removed. diff --git a/src/mod_pubsub/nodetree_virtual.erl b/src/mod_pubsub/nodetree_virtual.erl index a850f26e6..5fb77e217 100644 --- a/src/mod_pubsub/nodetree_virtual.erl +++ b/src/mod_pubsub/nodetree_virtual.erl @@ -49,7 +49,7 @@ get_nodes/2, get_nodes/1, get_subnodes/3, - get_subnodes_tree/2, + get_subnodes_tree/3, create_node/5, delete_node/2 ]). @@ -95,14 +95,13 @@ get_node(Host, Node, _From) -> get_node(Host, Node) -> #pubsub_node{nodeid = {Host, Node}}. -get_nodes(Key, _From) -> - get_nodes(Key). - -%% @spec (Key) -> [pubsubNode()] +%% @spec (Host) -> [pubsubNode()] %% Host = mod_pubsub:host() | mod_pubsub:jid() %% @doc

    Virtual node tree does not handle a node database. Any node is considered %% as existing. Nodes list can not be determined.

    -get_nodes(_Key) -> +get_nodes(Host, _From) -> + get_nodes(Host). +get_nodes(_Host) -> []. %% @spec (Host, Node, From) -> [pubsubNode()] @@ -110,13 +109,17 @@ get_nodes(_Key) -> %% Node = mod_pubsub:pubsubNode() %% From = mod_pubsub:jid() %% @doc

    Virtual node tree does not handle parent/child. Child list is empty.

    -get_subnodes(_Host, _Node, _From) -> +get_subnodes(Host, Node, _From) -> + get_subnodes(Host, Node). +get_subnodes(_Host, _Node) -> []. %% @spec (Host, Index) -> [pubsubNode()] %% Host = mod_pubsub:host() %% Node = mod_pubsub:pubsubNode() %% @doc

    Virtual node tree does not handle parent/child. Child list is empty.

    +get_subnodes_tree(Host, Node, _From) -> + get_subnodes_tree(Host, Node). get_subnodes_tree(_Host, _Node) -> []. @@ -129,11 +132,11 @@ get_subnodes_tree(_Host, _Node) -> %% @doc

    No node record is stored on database. Any valid node %% is considered as already created.

    %%

    default allowed nodes: /home/host/user/any/node/name

    -create_node(_Host, Node, _Type, Owner, _Options) -> +create_node(Host, Node, _Type, Owner, _Options) -> UserName = exmpp_jid:lnode_as_list(Owner), UserHost = exmpp_jid:ldomain_as_list(Owner), case Node of - ["home", UserHost, UserName | _] -> {error, 'conflict'}; + ["home", UserHost, UserName | _] -> {error, {virtual, {Host, Node}}}; _ -> {error, 'not-allowed'} end. @@ -142,5 +145,5 @@ create_node(_Host, Node, _Type, Owner, _Options) -> %% Node = mod_pubsub:pubsubNode() %% @doc

    Virtual node tree does not handle parent/child. %% node deletion just affects the corresponding node.

    -delete_node(_Host, Node) -> - [Node]. +delete_node(Host, Node) -> + [get_node(Host, Node)]. diff --git a/src/mod_pubsub/pubsub.hrl b/src/mod_pubsub/pubsub.hrl index 46cbe9b95..87088eb6a 100644 --- a/src/mod_pubsub/pubsub.hrl +++ b/src/mod_pubsub/pubsub.hrl @@ -77,24 +77,28 @@ %%% @type affiliation() = none | owner | publisher | outcast. %%% @type subscription() = none | pending | unconfigured | subscribed. +%%% internal pubsub index table +-record(pubsub_index, {index, last, free}). + %%% @type pubsubNode() = #pubsub_node{ %%% nodeid = {Host::host(), Node::pubsubNode()}, %%% parentid = {Host::host(), Node::pubsubNode()}, +%%% nodeidx = int(). %%% type = nodeType(), -%%% owners = [ljid()], -%%% options = [nodeOption()]}. +%%% options = [nodeOption()]} %%%

    This is the format of the nodes table. The type of the table %%% is: set,ram/disc.

    %%%

    The parentid and type fields are indexed.

    -record(pubsub_node, {nodeid, - parentid = {}, - type = "", + id, + parent, + type = "default", owners = [], options = [] }). %%% @type pubsubState() = #pubsub_state{ -%%% stateid = {ljid(), {Host::host(), Node::pubsubNode()}}, +%%% stateid = {ljid(), pubsubNodeId()}}, %%% items = [ItemId::string()], %%% affiliation = affiliation(), %%% subscription = subscription()}. @@ -106,11 +110,11 @@ subscription = none }). -%% @type pubsubItem() = #pubsub_item{ -%% itemid = {ItemId::string(), {Host::host(),Node::pubsubNode()}}, -%% creation = {ljid(), now()}, -%% modification = {ljid(), now()}, -%% payload = XMLContent::string()}. +%%% @type pubsubItem() = #pubsub_item{ +%%% itemid = {ItemId::string(), pubsubNodeId()}}, +%%% creation = {ljid(), now()}, +%%% modification = {ljid(), now()}, +%%% payload = XMLContent::string()}. %%%

    This is the format of the published items table. The type of the %%% table is: set,disc,fragmented.

    -record(pubsub_item, {itemid, diff --git a/src/mod_pubsub/pubsub_index.erl b/src/mod_pubsub/pubsub_index.erl new file mode 100644 index 000000000..2ba5d4b20 --- /dev/null +++ b/src/mod_pubsub/pubsub_index.erl @@ -0,0 +1,65 @@ +%%% ==================================================================== +%%% ``The contents of this file are subject to the Erlang Public License, +%%% Version 1.1, (the "License"); you may not use this file except in +%%% compliance with the License. You should have received a copy of the +%%% Erlang Public License along with this software. If not, it can be +%%% retrieved via the world wide web at http://www.erlang.org/. +%%% +%%% Software distributed under the License is distributed on an "AS IS" +%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%%% the License for the specific language governing rights and limitations +%%% under the License. +%%% +%%% The Initial Developer of the Original Code is ProcessOne. +%%% Portions created by ProcessOne are Copyright 2006-2009, ProcessOne +%%% All Rights Reserved.'' +%%% This software is copyright 2006-2009, ProcessOne. +%%% +%%% +%%% @copyright 2006-2009 ProcessOne +%%% @author Christophe Romain +%%% [http://www.process-one.net/] +%%% @version {@vsn}, {@date} {@time} +%%% @end +%%% ==================================================================== + +%% important note: +%% new/1 and free/2 MUST be called inside a transaction bloc + +-module(pubsub_index). +-author('christophe.romain@process-one.net'). + +-include("pubsub.hrl"). + +-export([init/3, new/1, free/2]). + +init(_Host, _ServerHost, _Opts) -> + mnesia:create_table(pubsub_index, + [{disc_copies, [node()]}, + {attributes, record_info(fields, pubsub_index)}]). + +new(Index) -> + case mnesia:read({pubsub_index, Index}) of + [I] -> + case I#pubsub_index.free of + [] -> + Id = I#pubsub_index.last + 1, + mnesia:write(I#pubsub_index{last = Id}), + Id; + [Id|Free] -> + mnesia:write(I#pubsub_index{free = Free}), + Id + end; + _ -> + mnesia:write(#pubsub_index{index = Index, last = 1, free = []}), + 1 + end. + +free(Index, Id) -> + case mnesia:read({pubsub_index, Index}) of + [I] -> + Free = I#pubsub_index.free, + mnesia:write(I#pubsub_index{free = [Id|Free]}); + _ -> + ok + end.