From 8ce1e790ac788b8a0c3a2cab352dbb25f4b374f4 Mon Sep 17 00:00:00 2001
From: Christophe Romain
Date: Tue, 20 Oct 2009 15:03:07 +0000
Subject: [PATCH] does not use slash as default separator in nodename
(EJAB-667)
SVN Revision: 2687
---
src/mod_pubsub/gen_pubsub_node.erl | 4 +-
src/mod_pubsub/gen_pubsub_nodetree.erl | 2 +-
src/mod_pubsub/mod_pubsub.erl | 140 ++++++++++++-------------
src/mod_pubsub/mod_pubsub_odbc.erl | 119 +++++++++------------
src/mod_pubsub/node_buddy.erl | 11 +-
src/mod_pubsub/node_club.erl | 11 +-
src/mod_pubsub/node_dag.erl | 11 +-
src/mod_pubsub/node_dispatch.erl | 11 +-
src/mod_pubsub/node_flat.erl | 17 ++-
src/mod_pubsub/node_flat_odbc.erl | 17 ++-
src/mod_pubsub/node_hometree.erl | 15 ++-
src/mod_pubsub/node_hometree_odbc.erl | 14 ++-
src/mod_pubsub/node_mb.erl | 10 +-
src/mod_pubsub/node_pep.erl | 10 +-
src/mod_pubsub/node_pep_odbc.erl | 9 +-
src/mod_pubsub/node_private.erl | 11 +-
src/mod_pubsub/node_public.erl | 11 +-
src/mod_pubsub/nodetree_dag.erl | 9 +-
src/mod_pubsub/nodetree_tree.erl | 60 ++++++-----
src/mod_pubsub/nodetree_tree_odbc.erl | 59 +++++++----
src/mod_pubsub/nodetree_virtual.erl | 4 +-
src/mod_pubsub/pubsub_odbc.patch | 135 +++++++++++++++---------
22 files changed, 423 insertions(+), 267 deletions(-)
diff --git a/src/mod_pubsub/gen_pubsub_node.erl b/src/mod_pubsub/gen_pubsub_node.erl
index 3762d4a44..64464bcac 100644
--- a/src/mod_pubsub/gen_pubsub_node.erl
+++ b/src/mod_pubsub/gen_pubsub_node.erl
@@ -68,7 +68,9 @@ behaviour_info(callbacks) ->
{get_item, 7},
{get_item, 2},
{set_item, 1},
- {get_item_name, 3}
+ {get_item_name, 3},
+ {node_to_path, 1},
+ {path_to_node, 1}
];
behaviour_info(_Other) ->
undefined.
diff --git a/src/mod_pubsub/gen_pubsub_nodetree.erl b/src/mod_pubsub/gen_pubsub_nodetree.erl
index f55694500..84cf8f61b 100644
--- a/src/mod_pubsub/gen_pubsub_nodetree.erl
+++ b/src/mod_pubsub/gen_pubsub_nodetree.erl
@@ -51,7 +51,7 @@ behaviour_info(callbacks) ->
{get_parentnodes_tree, 3},
{get_subnodes, 3},
{get_subnodes_tree, 3},
- {create_node, 5},
+ {create_node, 6},
{delete_node, 2}
];
behaviour_info(_Other) ->
diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl
index a449644f8..d7d1fffe3 100644
--- a/src/mod_pubsub/mod_pubsub.erl
+++ b/src/mod_pubsub/mod_pubsub.erl
@@ -268,11 +268,11 @@ terminate_plugins(Host, ServerHost, Plugins, TreePlugin) ->
ok.
init_nodes(Host, ServerHost, _NodeTree, Plugins) ->
- %% TODO, this call should be done PLugin side
+ %% TODO, this call should be done plugin side
case lists:member("hometree", Plugins) of
true ->
- create_node(Host, ServerHost, ["home"], service_jid(Host), "hometree"),
- create_node(Host, ServerHost, ["home", ServerHost], service_jid(Host), "hometree");
+ create_node(Host, ServerHost, string_to_node("/home"), service_jid(Host), "hometree"),
+ create_node(Host, ServerHost, string_to_node("/home/"++ServerHost), service_jid(Host), "hometree");
false ->
ok
end.
@@ -403,7 +403,28 @@ update_node_database(Host, ServerHost) ->
rename_default_nodeplugin();
_ ->
ok
- end.
+ end,
+ mnesia:transaction(fun() ->
+ case catch mnesia:first(pubsub_node) of
+ {_, L} when is_list(L) ->
+ lists:foreach(
+ fun({H, N}) when is_list(N) ->
+ [Node] = mnesia:read({pubsub_node, {H, N}}),
+ Type = Node#pubsub_node.type,
+ BN = element(2, node_call(Type, path_to_node, [N])),
+ BP = case [element(2, node_call(Type, path_to_node, [P])) || P <- Node#pubsub_node.parents] of
+ [<<>>] -> [];
+ Parents -> Parents
+ end,
+ mnesia:write(Node#pubsub_node{nodeid={H, BN}, parents=BP}),
+ mnesia:delete({pubsub_node, {H, N}});
+ (_) ->
+ ok
+ end, mnesia:all_keys(pubsub_node));
+ _ ->
+ ok
+ end
+ end).
rename_default_nodeplugin() ->
lists:foreach(fun(Node) ->
@@ -548,7 +569,7 @@ send_loop(State) ->
on_sub_and_presence ->
lists:foreach(fun({Resource, Caps}) ->
CapsNotify = case catch mod_caps:get_features(ServerHost, Caps) of
- Features when is_list(Features) -> lists:member(Node ++ "+notify", Features);
+ Features when is_list(Features) -> lists:member(node_to_string(Node) ++ "+notify", Features);
_ -> false
end,
case CapsNotify of
@@ -644,7 +665,7 @@ disco_sm_features(Acc, From, To, Node, _Lang) ->
disco_sm_items(Acc, From, To, [], _Lang) ->
Host = To#jid.lserver,
- case tree_action(Host, get_subnodes, [Host, [], From]) of
+ case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
[] ->
Acc;
Nodes ->
@@ -662,8 +683,9 @@ disco_sm_items(Acc, From, To, [], _Lang) ->
{result, NodeItems ++ Items}
end;
-disco_sm_items(Acc, From, To, Node, _Lang) ->
+disco_sm_items(Acc, From, To, SNode, _Lang) ->
Host = To#jid.lserver,
+ Node = string_to_node(SNode),
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
% TODO call get_items/6 instead for access control (EJAB-1033)
case node_call(Type, get_items, [NodeId, From]) of
@@ -677,13 +699,8 @@ disco_sm_items(Acc, From, To, Node, _Lang) ->
end,
NodeItems = lists:map(
fun(#pubsub_item{itemid = {Id, _}}) ->
- %% "jid" is required by XEP-0030, and
- %% "node" is forbidden by XEP-0060.
{result, Name} = node_call(Type, get_item_name, [Host, Node, Id]),
- {xmlelement, "item",
- [{"jid", SBJID},
- {"name", Name}],
- []}
+ {xmlelement, "item", [{"jid", SBJID}, {"name", Name}], []}
end, AllItems),
{result, NodeItems ++ Items};
_ ->
@@ -1066,7 +1083,7 @@ iq_disco_info(Host, SNode, From, Lang) ->
end,
Node = string_to_node(RealSNode),
case Node of
- [] ->
+ <<>> ->
{result,
[{xmlelement, "identity",
[{"category", "pubsub"},
@@ -1085,24 +1102,17 @@ iq_disco_info(Host, SNode, From, Lang) ->
iq_disco_items(Host, [], From) ->
{result, lists:map(
- fun(#pubsub_node{nodeid = {_, SubNode}}) ->
- SN = node_to_string(SubNode),
- RN = lists:last(SubNode),
- %% remove name attribute
- {xmlelement, "item", [{"jid", Host},
- {"node", SN},
- {"name", RN}], []}
- end, tree_action(Host, get_subnodes, [Host, [], From]))};
+ fun(#pubsub_node{nodeid = {_, SubNode}, type = Type}) ->
+ {result, Path} = node_call(Type, node_to_path, [SubNode]),
+ [Name|_] = lists:reverse(Path),
+ {xmlelement, "item", [{"jid", Host}, {"name", Name}|nodeAttr(SubNode)], []}
+ end, tree_action(Host, get_subnodes, [Host, <<>>, From]))};
iq_disco_items(Host, Item, From) ->
case string:tokens(Item, "!") of
[_SNode, _ItemID] ->
{result, []};
[SNode] ->
Node = string_to_node(SNode),
- %% 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 for 2.1)
Action =
fun(#pubsub_node{type = Type, id = NodeId}) ->
% TODO call get_items/6 instead for access control (EJAB-1033)
@@ -1112,17 +1122,12 @@ iq_disco_items(Host, Item, From) ->
end,
Nodes = lists:map(
fun(#pubsub_node{nodeid = {_, SubNode}}) ->
- SN = node_to_string(SubNode),
- RN = lists:last(SubNode),
- {xmlelement, "item", [{"jid", Host}, {"node", SN},
- {"name", RN}], []}
+ {xmlelement, "item", [{"jid", Host}|nodeAttr(SubNode)], []}
end, tree_call(Host, get_subnodes, [Host, Node, 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]),
- {xmlelement, "item", [{"jid", Host}, {"node", SN},
- {"name", Name}], []}
+ {result, Name} = node_call(Type, get_item_name, [Host, Node, RN]),
+ {xmlelement, "item", [{"jid", Host}, {"name", Name}], []}
end, NodeItems),
{result, Nodes ++ Items}
end,
@@ -1178,10 +1183,7 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
{xmlelement, _, _, SubEls} = SubEl,
case xml:remove_cdata(SubEls) of
[{xmlelement, Name, Attrs, Els} | Rest] ->
- Node = case Host of
- {_, _, _} -> xml:get_attr_s("node", Attrs);
- _ -> string_to_node(xml:get_attr_s("node", Attrs))
- end,
+ Node = string_to_node(xml:get_attr_s("node", Attrs)),
case {IQType, Name} of
{set, "create"} ->
Config = case Rest of
@@ -1282,10 +1284,7 @@ iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
Action = xml:remove_cdata(SubEls),
case Action of
[{xmlelement, Name, Attrs, Els}] ->
- Node = case Host of
- {_, _, _} -> xml:get_attr_s("node", Attrs);
- _ -> string_to_node(xml:get_attr_s("node", Attrs))
- end,
+ Node = string_to_node(xml:get_attr_s("node", Attrs)),
case {IQType, Name} of
{get, "configure"} ->
get_configure(Host, ServerHost, Node, From, Lang);
@@ -1521,7 +1520,7 @@ send_authorization_approval(Host, JID, SNode, Subscription) ->
end,
Stanza = event_stanza(
[{xmlelement, "subscription",
- [{"node", SNode}, {"jid", jlib:jid_to_string(JID)}] ++ SubAttrs,
+ [{"jid", jlib:jid_to_string(JID)}|nodeAttr(SNode)] ++ SubAttrs,
[]}]),
ejabberd_router ! {route, service_jid(Host), JID, Stanza}.
@@ -1531,10 +1530,7 @@ handle_authorization_response(Host, From, To, Packet, XFields) ->
lists:keysearch("pubsub#allow", 1, XFields)} of
{{value, {_, [SNode]}}, {value, {_, [SSubscriber]}},
{value, {_, [SAllow]}}} ->
- Node = case Host of
- {_, _, _} -> [SNode];
- _ -> string:tokens(SNode, "/")
- end,
+ Node = string_to_node(SNode),
Subscriber = jlib:string_to_jid(SSubscriber),
Allow = case SAllow of
"1" -> true;
@@ -1664,21 +1660,18 @@ update_auth(Host, Node, Type, NodeId, Subscriber,
%%
create_node(Host, ServerHost, Node, Owner, Type) ->
create_node(Host, ServerHost, Node, Owner, Type, all, []).
-create_node(Host, ServerHost, [], Owner, Type, Access, Configuration) ->
+create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
case lists:member("instant-nodes", features(Type)) of
true ->
- {LOU, LOS, _} = jlib:jid_tolower(Owner),
- HomeNode = ["home", LOS, LOU],
- create_node(Host, ServerHost,
- HomeNode, Owner, Type, Access, Configuration),
- NewNode = HomeNode ++ [randoms:get_string()],
+ NewNode = string_to_node(randoms:get_string()),
case create_node(Host, ServerHost,
NewNode, Owner, Type, Access, Configuration) of
{result, []} ->
{result,
[{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
[{xmlelement, "create", nodeAttr(NewNode), []}]}]};
- Error -> Error
+ Error ->
+ Error
end;
false ->
%% Service does not support instant nodes
@@ -1686,7 +1679,6 @@ create_node(Host, ServerHost, [], Owner, Type, Access, Configuration) ->
end;
create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
Type = select_type(ServerHost, Host, Node, GivenType),
- Parent = lists:sublist(Node, length(Node) - 1),
%% TODO, check/set node_type = Type
ParseOptions = case xml:remove_cdata(Configuration) of
[] ->
@@ -1711,9 +1703,18 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
{result, NodeOptions} ->
CreateNode =
fun() ->
+ SNode = node_to_string(Node),
+ Parent = case node_call(Type, node_to_path, [Node]) of
+ {result, [SNode]} -> <<>>;
+ {result, Path} -> element(2, node_call(Type, path_to_node, [lists:sublist(Path, length(Path)-1)]))
+ end,
+ Parents = case Parent of
+ <<>> -> [];
+ _ -> [Parent]
+ end,
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
+ case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of
{ok, NodeId} ->
node_call(Type, create_node, [NodeId, Owner]);
{error, {virtual, NodeId}} ->
@@ -2499,9 +2500,8 @@ read_sub(Subscriber, Node, NodeID, SubID, Lang) ->
{error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
{result, #pubsub_subscription{options = Options}} ->
{result, XdataEl} = pubsub_subscription:get_options_xform(Lang, Options),
- OptionsEl = {xmlelement, "options", [{"node", node_to_string(Node)},
- {"jid", jlib:jid_to_string(Subscriber)},
- {"subid", SubID}],
+ OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)},
+ {"subid", SubID}|nodeAttr(Node)],
[XdataEl]},
PubsubEl = {xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], [OptionsEl]},
{result, PubsubEl}
@@ -2591,7 +2591,7 @@ get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
[];
({#pubsub_node{nodeid = {_, SubsNode}}, Subscription}) ->
case Node of
- [] ->
+ <<>> ->
[{xmlelement, "subscription",
[{"subscription", subscription_to_string(Subscription)}|nodeAttr(SubsNode)],
[]}];
@@ -2606,7 +2606,7 @@ get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
[];
({#pubsub_node{nodeid = {_, SubsNode}}, Subscription, SubID, SubJID}) ->
case Node of
- [] ->
+ <<>> ->
[{xmlelement, "subscription",
[{"jid", jlib:jid_to_string(SubJID)},
{"subid", SubID},
@@ -2623,7 +2623,7 @@ get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
end;
({#pubsub_node{nodeid = {_, SubsNode}}, Subscription, SubJID}) ->
case Node of
- [] ->
+ <<>> ->
[{xmlelement, "subscription",
[{"jid", jlib:jid_to_string(SubJID)},
{"subscription", subscription_to_string(Subscription)}|nodeAttr(SubsNode)],
@@ -2805,14 +2805,8 @@ subscription_to_string(_) -> "none".
%% Node = pubsubNode()
%% NodeStr = string()
%% @doc Convert a node type from pubsubNode to string.
-node_to_string([]) -> "/";
-node_to_string(Node) ->
- case Node of
- [[_ | _] | _] -> string:strip(lists:flatten(["/", lists:map(fun(S) -> [S, "/"] end, Node)]), right, $/);
- [Head | _] when is_integer(Head) -> Node
- end.
-string_to_node(SNode) ->
- string:tokens(SNode, "/").
+node_to_string(Node) -> binary_to_list(Node).
+string_to_node(SNode) -> list_to_binary(SNode).
%% @spec (Host) -> jid()
%% Host = host()
@@ -3044,7 +3038,7 @@ get_options_for_subs(NodeID, Subs) ->
% {result, []} ->
% {result, false};
% {result, Subs} ->
-% Stanza = event_stanza([{xmlelement, ElName, [{"node", node_to_string(Node)}], SubEls}]),
+% Stanza = event_stanza([{xmlelement, ElName, nodeAttr(Node), SubEls}]),
% broadcast_stanza(Host, Node, Type, NodeOptions, SubOpts, Stanza),
% {result, true};
% _ ->
@@ -3161,7 +3155,7 @@ is_caps_notify(Host, Node, LJID) ->
false;
Caps ->
case catch mod_caps:get_features(Host, Caps) of
- Features when is_list(Features) -> lists:member(Node ++ "+notify", Features);
+ Features when is_list(Features) -> lists:member(node_to_string(Node) ++ "+notify", Features);
_ -> false
end
end.
@@ -3689,6 +3683,8 @@ uniqid() ->
lists:flatten(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3])).
% node attributes
+nodeAttr(Node) when is_list(Node) ->
+ [{"node", Node}];
nodeAttr(Node) ->
[{"node", node_to_string(Node)}].
diff --git a/src/mod_pubsub/mod_pubsub_odbc.erl b/src/mod_pubsub/mod_pubsub_odbc.erl
index ff06097f1..8b3d38f4a 100644
--- a/src/mod_pubsub/mod_pubsub_odbc.erl
+++ b/src/mod_pubsub/mod_pubsub_odbc.erl
@@ -266,11 +266,11 @@ terminate_plugins(Host, ServerHost, Plugins, TreePlugin) ->
ok.
init_nodes(Host, ServerHost, _NodeTree, Plugins) ->
- %% TODO, this call should be done PLugin side
- case lists:member("hometree", Plugins) of
+ %% TODO, this call should be done plugin side
+ case lists:member("hometree_odbc", Plugins) of
true ->
- create_node(Host, ServerHost, ["home"], service_jid(Host), "hometree"),
- create_node(Host, ServerHost, ["home", ServerHost], service_jid(Host), "hometree");
+ create_node(Host, ServerHost, string_to_node("/home"), service_jid(Host), "hometree_odbc"),
+ create_node(Host, ServerHost, string_to_node("/home/"++ServerHost), service_jid(Host), "hometree_odbc");
false ->
ok
end.
@@ -372,7 +372,7 @@ send_loop(State) ->
on_sub_and_presence ->
lists:foreach(fun({Resource, Caps}) ->
CapsNotify = case catch mod_caps:get_features(ServerHost, Caps) of
- Features when is_list(Features) -> lists:member(Node ++ "+notify", Features);
+ Features when is_list(Features) -> lists:member(node_to_string(Node) ++ "+notify", Features);
_ -> false
end,
case CapsNotify of
@@ -468,7 +468,7 @@ disco_sm_features(Acc, From, To, Node, _Lang) ->
disco_sm_items(Acc, From, To, [], _Lang) ->
Host = To#jid.lserver,
- case tree_action(Host, get_subnodes, [Host, [], From]) of
+ case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
[] ->
Acc;
Nodes ->
@@ -486,8 +486,9 @@ disco_sm_items(Acc, From, To, [], _Lang) ->
{result, NodeItems ++ Items}
end;
-disco_sm_items(Acc, From, To, Node, _Lang) ->
+disco_sm_items(Acc, From, To, SNode, _Lang) ->
Host = To#jid.lserver,
+ Node = string_to_node(SNode),
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
% TODO call get_items/6 instead for access control (EJAB-1033)
case node_call(Type, get_items, [NodeId, From]) of
@@ -501,13 +502,8 @@ disco_sm_items(Acc, From, To, Node, _Lang) ->
end,
NodeItems = lists:map(
fun(#pubsub_item{itemid = {Id, _}}) ->
- %% "jid" is required by XEP-0030, and
- %% "node" is forbidden by XEP-0060.
{result, Name} = node_call(Type, get_item_name, [Host, Node, Id]),
- {xmlelement, "item",
- [{"jid", SBJID},
- {"name", Name}],
- []}
+ {xmlelement, "item", [{"jid", SBJID}, {"name", Name}], []}
end, AllItems),
{result, NodeItems ++ Items};
_ ->
@@ -892,7 +888,7 @@ iq_disco_info(Host, SNode, From, Lang) ->
end,
Node = string_to_node(RealSNode),
case Node of
- [] ->
+ <<>> ->
{result,
[{xmlelement, "identity",
[{"category", "pubsub"},
@@ -912,24 +908,17 @@ iq_disco_info(Host, SNode, From, Lang) ->
iq_disco_items(Host, [], From, _RSM) ->
{result, lists:map(
- fun(#pubsub_node{nodeid = {_, SubNode}}) ->
- SN = node_to_string(SubNode),
- RN = lists:last(SubNode),
- %% remove name attribute
- {xmlelement, "item", [{"jid", Host},
- {"node", SN},
- {"name", RN}], []}
- end, tree_action(Host, get_subnodes, [Host, [], From]))};
+ fun(#pubsub_node{nodeid = {_, SubNode}, type = Type}) ->
+ {result, Path} = node_call(Type, node_to_path, [SubNode]),
+ [Name|_] = lists:reverse(Path),
+ {xmlelement, "item", [{"jid", Host}, {"name", Name}|nodeAttr(SubNode)], []}
+ end, tree_action(Host, get_subnodes, [Host, <<>>, From]))};
iq_disco_items(Host, Item, From, RSM) ->
case string:tokens(Item, "!") of
[_SNode, _ItemID] ->
{result, []};
[SNode] ->
Node = string_to_node(SNode),
- %% 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 for 2.1)
Action =
fun(#pubsub_node{type = Type, id = NodeId}) ->
%% TODO call get_items/6 instead for access control (EJAB-1033)
@@ -939,17 +928,12 @@ iq_disco_items(Host, Item, From, RSM) ->
end,
Nodes = lists:map(
fun(#pubsub_node{nodeid = {_, SubNode}}) ->
- SN = node_to_string(SubNode),
- RN = lists:last(SubNode),
- {xmlelement, "item", [{"jid", Host}, {"node", SN},
- {"name", RN}], []}
+ {xmlelement, "item", [{"jid", Host}|nodeAttr(SubNode)], []}
end, tree_call(Host, get_subnodes, [Host, Node, 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]),
- {xmlelement, "item", [{"jid", Host}, {"node", SN},
- {"name", Name}], []}
+ {result, Name} = node_call(Type, get_item_name, [Host, Node, RN]),
+ {xmlelement, "item", [{"jid", Host}, {"name", Name}], []}
end, NodeItems),
{result, Nodes ++ Items ++ jlib:rsm_encode(RsmOut)}
end,
@@ -1005,10 +989,7 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
{xmlelement, _, _, SubEls} = SubEl,
case xml:remove_cdata(SubEls) of
[{xmlelement, Name, Attrs, Els} | Rest] ->
- Node = case Host of
- {_, _, _} -> xml:get_attr_s("node", Attrs);
- _ -> string_to_node(xml:get_attr_s("node", Attrs))
- end,
+ Node = string_to_node(xml:get_attr_s("node", Attrs)),
case {IQType, Name} of
{set, "create"} ->
Config = case Rest of
@@ -1112,10 +1093,7 @@ iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
end, xml:remove_cdata(SubEls)),
case Action of
[{xmlelement, Name, Attrs, Els}] ->
- Node = case Host of
- {_, _, _} -> xml:get_attr_s("node", Attrs);
- _ -> string_to_node(xml:get_attr_s("node", Attrs))
- end,
+ Node = string_to_node(xml:get_attr_s("node", Attrs)),
case {IQType, Name} of
{get, "configure"} ->
get_configure(Host, ServerHost, Node, From, Lang);
@@ -1352,7 +1330,7 @@ send_authorization_approval(Host, JID, SNode, Subscription) ->
end,
Stanza = event_stanza(
[{xmlelement, "subscription",
- [{"node", SNode}, {"jid", jlib:jid_to_string(JID)}] ++ SubAttrs,
+ [{"jid", jlib:jid_to_string(JID)}|nodeAttr(SNode)] ++ SubAttrs,
[]}]),
ejabberd_router ! {route, service_jid(Host), JID, Stanza}.
@@ -1362,10 +1340,7 @@ handle_authorization_response(Host, From, To, Packet, XFields) ->
lists:keysearch("pubsub#allow", 1, XFields)} of
{{value, {_, [SNode]}}, {value, {_, [SSubscriber]}},
{value, {_, [SAllow]}}} ->
- Node = case Host of
- {_, _, _} -> [SNode];
- _ -> string:tokens(SNode, "/")
- end,
+ Node = string_to_node(SNode),
Subscriber = jlib:string_to_jid(SSubscriber),
Allow = case SAllow of
"1" -> true;
@@ -1495,21 +1470,18 @@ update_auth(Host, Node, Type, NodeId, Subscriber,
%%
create_node(Host, ServerHost, Node, Owner, Type) ->
create_node(Host, ServerHost, Node, Owner, Type, all, []).
-create_node(Host, ServerHost, [], Owner, Type, Access, Configuration) ->
+create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
case lists:member("instant-nodes", features(Type)) of
true ->
- {LOU, LOS, _} = jlib:jid_tolower(Owner),
- HomeNode = ["home", LOS, LOU],
- create_node(Host, ServerHost,
- HomeNode, Owner, Type, Access, Configuration),
- NewNode = HomeNode ++ [randoms:get_string()],
+ NewNode = string_to_node(randoms:get_string()),
case create_node(Host, ServerHost,
NewNode, Owner, Type, Access, Configuration) of
{result, []} ->
{result,
[{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
[{xmlelement, "create", nodeAttr(NewNode), []}]}]};
- Error -> Error
+ Error ->
+ Error
end;
false ->
%% Service does not support instant nodes
@@ -1517,7 +1489,6 @@ create_node(Host, ServerHost, [], Owner, Type, Access, Configuration) ->
end;
create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
Type = select_type(ServerHost, Host, Node, GivenType),
- Parent = lists:sublist(Node, length(Node) - 1),
%% TODO, check/set node_type = Type
ParseOptions = case xml:remove_cdata(Configuration) of
[] ->
@@ -1542,9 +1513,18 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
{result, NodeOptions} ->
CreateNode =
fun() ->
+ SNode = node_to_string(Node),
+ Parent = case node_call(Type, node_to_path, [Node]) of
+ {result, [SNode]} -> <<>>;
+ {result, Path} -> element(2, node_call(Type, path_to_node, [lists:sublist(Path, length(Path)-1)]))
+ end,
+ Parents = case Parent of
+ <<>> -> [];
+ _ -> [Parent]
+ end,
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
+ case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of
{ok, NodeId} ->
node_call(Type, create_node, [NodeId, Owner]);
{error, {virtual, NodeId}} ->
@@ -2329,9 +2309,8 @@ read_sub(Subscriber, Node, NodeID, SubID, Lang) ->
{error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
{result, #pubsub_subscription{options = Options}} ->
{result, XdataEl} = pubsub_subscription_odbc:get_options_xform(Lang, Options),
- OptionsEl = {xmlelement, "options", [{"node", node_to_string(Node)},
- {"jid", jlib:jid_to_string(Subscriber)},
- {"subid", SubID}],
+ OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)},
+ {"subid", SubID}|nodeAttr(Node)],
[XdataEl]},
PubsubEl = {xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], [OptionsEl]},
{result, PubsubEl}
@@ -2421,7 +2400,7 @@ get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
[];
({#pubsub_node{nodeid = {_, SubsNode}}, Subscription}) ->
case Node of
- [] ->
+ <<>> ->
[{xmlelement, "subscription",
[{"subscription", subscription_to_string(Subscription)}|nodeAttr(SubsNode)],
[]}];
@@ -2436,7 +2415,7 @@ get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
[];
({#pubsub_node{nodeid = {_, SubsNode}}, Subscription, SubID, SubJID}) ->
case Node of
- [] ->
+ <<>> ->
[{xmlelement, "subscription",
[{"jid", jlib:jid_to_string(SubJID)},
{"subid", SubID},
@@ -2453,7 +2432,7 @@ get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
end;
({#pubsub_node{nodeid = {_, SubsNode}}, Subscription, SubJID}) ->
case Node of
- [] ->
+ <<>> ->
[{xmlelement, "subscription",
[{"jid", jlib:jid_to_string(SubJID)},
{"subscription", subscription_to_string(Subscription)}|nodeAttr(SubsNode)],
@@ -2635,14 +2614,8 @@ subscription_to_string(_) -> "none".
%% Node = pubsubNode()
%% NodeStr = string()
%% @doc Convert a node type from pubsubNode to string.
-node_to_string([]) -> "/";
-node_to_string(Node) ->
- case Node of
- [[_ | _] | _] -> string:strip(lists:flatten(["/", lists:map(fun(S) -> [S, "/"] end, Node)]), right, $/);
- [Head | _] when is_integer(Head) -> Node
- end.
-string_to_node(SNode) ->
- string:tokens(SNode, "/").
+node_to_string(Node) -> binary_to_list(Node).
+string_to_node(SNode) -> list_to_binary(SNode).
%% @spec (Host) -> jid()
%% Host = host()
@@ -2874,7 +2847,7 @@ get_options_for_subs(NodeID, Subs) ->
% {result, []} ->
% {result, false};
% {result, Subs} ->
-% Stanza = event_stanza([{xmlelement, ElName, [{"node", node_to_string(Node)}], SubEls}]),
+% Stanza = event_stanza([{xmlelement, ElName, nodeAttr(Node), SubEls}]),
% broadcast_stanza(Host, Node, Type, NodeOptions, SubOpts, Stanza),
% {result, true};
% _ ->
@@ -2991,7 +2964,7 @@ is_caps_notify(Host, Node, LJID) ->
false;
Caps ->
case catch mod_caps:get_features(Host, Caps) of
- Features when is_list(Features) -> lists:member(Node ++ "+notify", Features);
+ Features when is_list(Features) -> lists:member(node_to_string(Node) ++ "+notify", Features);
_ -> false
end
end.
@@ -3575,6 +3548,8 @@ uniqid() ->
lists:flatten(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3])).
% node attributes
+nodeAttr(Node) when is_list(Node) ->
+ [{"node", Node}];
nodeAttr(Node) ->
[{"node", node_to_string(Node)}].
diff --git a/src/mod_pubsub/node_buddy.erl b/src/mod_pubsub/node_buddy.erl
index 20b679ca7..c952252bd 100644
--- a/src/mod_pubsub/node_buddy.erl
+++ b/src/mod_pubsub/node_buddy.erl
@@ -68,7 +68,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1
]).
@@ -193,3 +195,10 @@ set_item(Item) ->
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
+
+node_to_path(Node) ->
+ node_flat:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_flat:path_to_node(Path).
+
diff --git a/src/mod_pubsub/node_club.erl b/src/mod_pubsub/node_club.erl
index 3c1596cd6..d77cde702 100644
--- a/src/mod_pubsub/node_club.erl
+++ b/src/mod_pubsub/node_club.erl
@@ -68,7 +68,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1
]).
@@ -192,3 +194,10 @@ set_item(Item) ->
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
+
+node_to_path(Node) ->
+ node_flat:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_flat:path_to_node(Path).
+
diff --git a/src/mod_pubsub/node_dag.erl b/src/mod_pubsub/node_dag.erl
index f28c338d8..b70169460 100644
--- a/src/mod_pubsub/node_dag.erl
+++ b/src/mod_pubsub/node_dag.erl
@@ -52,7 +52,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3]).
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1]).
init(Host, ServerHost, Opts) ->
@@ -173,3 +175,10 @@ set_item(Item) ->
get_item_name(Host, Node, ID) ->
node_hometree:get_item_name(Host, Node, ID).
+
+node_to_path(Node) ->
+ node_hometree:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_hometree:path_to_node(Path).
+
diff --git a/src/mod_pubsub/node_dispatch.erl b/src/mod_pubsub/node_dispatch.erl
index 8c2c7d0f4..d81848fe3 100644
--- a/src/mod_pubsub/node_dispatch.erl
+++ b/src/mod_pubsub/node_dispatch.erl
@@ -66,7 +66,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1
]).
@@ -196,3 +198,10 @@ set_item(Item) ->
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
+
+node_to_path(Node) ->
+ node_flat:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_flat:path_to_node(Path).
+
diff --git a/src/mod_pubsub/node_flat.erl b/src/mod_pubsub/node_flat.erl
index 67b95973a..854e64321 100644
--- a/src/mod_pubsub/node_flat.erl
+++ b/src/mod_pubsub/node_flat.erl
@@ -59,7 +59,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1
]).
@@ -179,3 +181,16 @@ set_item(Item) ->
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
+
+node_to_path(Node) ->
+ [binary_to_list(Node)].
+
+path_to_node(Path) ->
+ case Path of
+ % default slot
+ [Node] -> list_to_binary(Node);
+ % handle old possible entries, used when migrating database content to new format
+ [Node|_] when is_list(Node) -> list_to_binary(string:join([""|Path], "/"));
+ % default case (used by PEP for example)
+ _ -> list_to_binary(Path)
+ end.
diff --git a/src/mod_pubsub/node_flat_odbc.erl b/src/mod_pubsub/node_flat_odbc.erl
index f073c70a8..0e6c8faa1 100644
--- a/src/mod_pubsub/node_flat_odbc.erl
+++ b/src/mod_pubsub/node_flat_odbc.erl
@@ -63,7 +63,9 @@
get_item/2,
set_item/1,
get_item_name/3,
- get_last_items/3
+ get_last_items/3,
+ node_to_path/1,
+ path_to_node/1
]).
@@ -196,3 +198,16 @@ get_item_name(Host, Node, Id) ->
get_last_items(NodeId, From, Count) ->
node_hometree_odbc:get_last_items(NodeId, From, Count).
+node_to_path(Node) ->
+ [binary_to_list(Node)].
+
+path_to_node(Path) ->
+ case Path of
+ % default slot
+ [Node] -> list_to_binary(Node);
+ % handle old possible entries, used when migrating database content to new format
+ [Node|_] when is_list(Node) -> list_to_binary(string:join([""|Path], "/"));
+ % default case (used by PEP for example)
+ _ -> list_to_binary(Path)
+ end.
+
diff --git a/src/mod_pubsub/node_hometree.erl b/src/mod_pubsub/node_hometree.erl
index efb14c922..5ce7b9724 100644
--- a/src/mod_pubsub/node_hometree.erl
+++ b/src/mod_pubsub/node_hometree.erl
@@ -75,7 +75,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1
]).
%% ================
@@ -203,7 +205,7 @@ create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) ->
_ ->
case acl:match_rule(ServerHost, Access, LOwner) of
allow ->
- case Node of
+ case node_to_path(Node) of
["home", Server, User | _] -> true;
_ -> false
end;
@@ -989,6 +991,14 @@ del_items(NodeId, ItemIds) ->
get_item_name(_Host, _Node, Id) ->
Id.
+node_to_path(Node) ->
+ string:tokens(binary_to_list(Node), "/").
+
+path_to_node([]) ->
+ <<>>;
+path_to_node(Path) ->
+ list_to_binary(string:join([""|Path], "/")).
+
%% @spec (Affiliation, Subscription) -> true | false
%% Affiliation = owner | member | publisher | outcast | none
%% Subscription = subscribed | none
@@ -1014,3 +1024,4 @@ first_in_list(Pred, [H | T]) ->
true -> {value, H};
_ -> first_in_list(Pred, T)
end.
+
diff --git a/src/mod_pubsub/node_hometree_odbc.erl b/src/mod_pubsub/node_hometree_odbc.erl
index 1401fb0ce..141d3275c 100644
--- a/src/mod_pubsub/node_hometree_odbc.erl
+++ b/src/mod_pubsub/node_hometree_odbc.erl
@@ -81,7 +81,9 @@
get_item/2,
set_item/1,
get_item_name/3,
- get_last_items/3
+ get_last_items/3,
+ path_to_node/1,
+ node_to_path/1
]).
-export([
@@ -210,7 +212,7 @@ create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) ->
_ ->
case acl:match_rule(ServerHost, Access, LOwner) of
allow ->
- case Node of
+ case node_to_path(Node) of
["home", Server, User | _] -> true;
_ -> false
end;
@@ -1209,6 +1211,14 @@ del_items(NodeId, ItemIds) ->
get_item_name(_Host, _Node, Id) ->
Id.
+node_to_path(Node) ->
+ string:tokens(binary_to_list(Node), "/").
+
+path_to_node([]) ->
+ <<>>;
+path_to_node(Path) ->
+ list_to_binary(string:join([""|Path], "/")).
+
%% @spec (Affiliation, Subscription) -> true | false
%% Affiliation = owner | member | publisher | outcast | none
%% Subscription = subscribed | none
diff --git a/src/mod_pubsub/node_mb.erl b/src/mod_pubsub/node_mb.erl
index c1accca3c..a497c17ba 100644
--- a/src/mod_pubsub/node_mb.erl
+++ b/src/mod_pubsub/node_mb.erl
@@ -71,7 +71,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1
]).
init(Host, ServerHost, Opts) ->
@@ -201,3 +203,9 @@ set_item(Item) ->
get_item_name(Host, Node, Id) ->
node_pep:get_item_name(Host, Node, Id).
+node_to_path(Node) ->
+ node_pep:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_pep:path_to_node(Path).
+
diff --git a/src/mod_pubsub/node_pep.erl b/src/mod_pubsub/node_pep.erl
index 754312b83..5bdb4915f 100644
--- a/src/mod_pubsub/node_pep.erl
+++ b/src/mod_pubsub/node_pep.erl
@@ -64,7 +64,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1
]).
init(Host, ServerHost, Opts) ->
@@ -268,6 +270,12 @@ set_item(Item) ->
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
+node_to_path(Node) ->
+ node_flat:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_flat:path_to_node(Path).
+
%%%
%%% Internal
diff --git a/src/mod_pubsub/node_pep_odbc.erl b/src/mod_pubsub/node_pep_odbc.erl
index 95c737a6a..e24c35a14 100644
--- a/src/mod_pubsub/node_pep_odbc.erl
+++ b/src/mod_pubsub/node_pep_odbc.erl
@@ -70,7 +70,9 @@
get_item/2,
set_item/1,
get_item_name/3,
- get_last_items/3
+ get_last_items/3,
+ node_to_path/1,
+ path_to_node/1
]).
init(Host, ServerHost, Opts) ->
@@ -305,6 +307,11 @@ set_item(Item) ->
get_item_name(Host, Node, Id) ->
node_hometree_odbc:get_item_name(Host, Node, Id).
+node_to_path(Node) ->
+ node_flat_odbc:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_flat_odbc:path_to_node(Path).
%%%
%%% Internal
diff --git a/src/mod_pubsub/node_private.erl b/src/mod_pubsub/node_private.erl
index 054485ef5..10f019305 100644
--- a/src/mod_pubsub/node_private.erl
+++ b/src/mod_pubsub/node_private.erl
@@ -68,7 +68,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1
]).
@@ -196,3 +198,10 @@ set_item(Item) ->
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
+
+node_to_path(Node) ->
+ node_flat:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_flat:path_to_node(Path).
+
diff --git a/src/mod_pubsub/node_public.erl b/src/mod_pubsub/node_public.erl
index ee440f933..5e26d8273 100644
--- a/src/mod_pubsub/node_public.erl
+++ b/src/mod_pubsub/node_public.erl
@@ -68,7 +68,9 @@
get_item/7,
get_item/2,
set_item/1,
- get_item_name/3
+ get_item_name/3,
+ node_to_path/1,
+ path_to_node/1
]).
@@ -194,3 +196,10 @@ set_item(Item) ->
%% node id.
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).
+
+node_to_path(Node) ->
+ node_flat:node_to_path(Node).
+
+path_to_node(Path) ->
+ node_flat:path_to_node(Path).
+
diff --git a/src/mod_pubsub/nodetree_dag.erl b/src/mod_pubsub/nodetree_dag.erl
index 7ddcaf18a..d7094e3e0 100644
--- a/src/mod_pubsub/nodetree_dag.erl
+++ b/src/mod_pubsub/nodetree_dag.erl
@@ -32,7 +32,7 @@
get_parentnodes_tree/3,
get_subnodes/3,
get_subnodes_tree/3,
- create_node/5,
+ create_node/6,
delete_node/2]).
-include_lib("stdlib/include/qlc.hrl").
@@ -53,14 +53,12 @@
%% API
%%====================================================================
init(Host, ServerHost, Opts) ->
- nodetree_tree:init(Host, ServerHost, Opts),
- mnesia:transaction(fun create_node/5,
- [Host, [], "default", service_jid(ServerHost), []]).
+ nodetree_tree:init(Host, ServerHost, Opts).
terminate(Host, ServerHost) ->
nodetree_tree:terminate(Host, ServerHost).
-create_node(Key, NodeID, Type, Owner, Options) ->
+create_node(Key, NodeID, Type, Owner, Options, Parents) ->
OwnerJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
case find_node(Key, NodeID) of
false ->
@@ -68,6 +66,7 @@ create_node(Key, NodeID, Type, Owner, Options) ->
N = #pubsub_node{nodeid = oid(Key, NodeID),
id = ID,
type = Type,
+ parents = Parents,
owners = [OwnerJID],
options = Options},
case set_node(N) of
diff --git a/src/mod_pubsub/nodetree_tree.erl b/src/mod_pubsub/nodetree_tree.erl
index ba8168f52..39160e7b5 100644
--- a/src/mod_pubsub/nodetree_tree.erl
+++ b/src/mod_pubsub/nodetree_tree.erl
@@ -56,7 +56,7 @@
get_parentnodes_tree/3,
get_subnodes/3,
get_subnodes_tree/3,
- create_node/5,
+ create_node/6,
delete_node/2
]).
@@ -160,13 +160,13 @@ get_parentnodes_tree(Host, Node, From) ->
%% From = mod_pubsub:jid()
get_subnodes(Host, Node, _From) ->
get_subnodes(Host, Node).
+get_subnodes(Host, <<>>) ->
+ Q = qlc:q([N || #pubsub_node{nodeid = {NHost, _},
+ parents = Parents} = N <- mnesia:table(pubsub_node),
+ Host == NHost,
+ Parents == []]),
+ qlc:e(Q);
get_subnodes(Host, Node) ->
-% mnesia:foldl(fun(#pubsub_node{nodeid = {H, _}, parents = Parents} = N, Acc) ->
-% case lists:member(Node, Parents) and (Host == H) of
-% true -> [N | Acc];
-% false -> Acc
-% end
-% end, [], pubsub_node).
Q = qlc:q([N || #pubsub_node{nodeid = {NHost, _},
parents = Parents} = N <- mnesia:table(pubsub_node),
Host == NHost,
@@ -181,12 +181,21 @@ get_subnodes_tree(Host, Node, _From) ->
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
get_subnodes_tree(Host, Node) ->
- mnesia:foldl(fun(#pubsub_node{nodeid = {H, N}} = R, Acc) ->
- case lists:prefix(Node, N) and (H == Host) of
- true -> [R | Acc];
- false -> Acc
- end
- end, [], pubsub_node).
+ case get_node(Host, Node) of
+ {error, _} ->
+ [];
+ Rec ->
+ BasePlugin = list_to_atom("node_"++Rec#pubsub_node.type),
+ BasePath = BasePlugin:node_to_path(Node),
+ mnesia:foldl(fun(#pubsub_node{nodeid = {H, N}} = R, Acc) ->
+ Plugin = list_to_atom("node_"++R#pubsub_node.type),
+ Path = Plugin:node_to_path(N),
+ case lists:prefix(BasePath, Path) and (H == Host) of
+ true -> [R | Acc];
+ false -> Acc
+ end
+ end, [], pubsub_node)
+ end.
%% @spec (Host, Node, Type, Owner, Options) -> ok | {error, Reason}
%% Host = mod_pubsub:host() | mod_pubsub:jid()
@@ -194,26 +203,27 @@ get_subnodes_tree(Host, Node) ->
%% NodeType = mod_pubsub:nodeType()
%% Owner = mod_pubsub:jid()
%% Options = list()
-create_node(Host, Node, Type, Owner, Options) ->
+create_node(Host, Node, Type, Owner, Options, Parents) ->
BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
case catch mnesia:read({pubsub_node, {Host, Node}}) of
[] ->
- {ParentNode, ParentExists} =
+ ParentExists =
case Host of
{_U, _S, _R} ->
%% This is special case for PEP handling
%% PEP does not uses hierarchy
- {[], true};
+ true;
_ ->
- case lists:sublist(Node, length(Node) - 1) of
- [] ->
- {[], true};
- Parent ->
+ case Parents of
+ [] -> true;
+ [Parent|_] ->
case catch mnesia:read({pubsub_node, {Host, Parent}}) of
- [#pubsub_node{owners = [{[], Host, []}]}] -> {Parent, true};
- [#pubsub_node{owners = Owners}] -> {Parent, lists:member(BJID, Owners)};
- _ -> {Parent, false}
- end
+ [#pubsub_node{owners = [{[], Host, []}]}] -> true;
+ [#pubsub_node{owners = Owners}] -> lists:member(BJID, Owners);
+ _ -> false
+ end;
+ _ ->
+ false
end
end,
case ParentExists of
@@ -221,7 +231,7 @@ create_node(Host, Node, Type, Owner, Options) ->
NodeId = pubsub_index:new(node),
mnesia:write(#pubsub_node{nodeid = {Host, Node},
id = NodeId,
- parents = [ParentNode],
+ parents = Parents,
type = Type,
owners = [BJID],
options = Options}),
diff --git a/src/mod_pubsub/nodetree_tree_odbc.erl b/src/mod_pubsub/nodetree_tree_odbc.erl
index ab7525c60..d8ff30832 100644
--- a/src/mod_pubsub/nodetree_tree_odbc.erl
+++ b/src/mod_pubsub/nodetree_tree_odbc.erl
@@ -57,7 +57,7 @@
get_parentnodes_tree/3,
get_subnodes/3,
get_subnodes_tree/3,
- create_node/5,
+ create_node/6,
delete_node/2
]).
@@ -209,30 +209,39 @@ get_subnodes_tree(Host, Node) ->
%% NodeType = mod_pubsub:nodeType()
%% Owner = mod_pubsub:jid()
%% Options = list()
-create_node(Host, Node, Type, _Owner, Options) ->
+%% Parents = list()
+create_node(Host, Node, Type, Owner, Options, Parents) ->
+ BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
case nodeid(Host, Node) of
{error, ?ERR_ITEM_NOT_FOUND} ->
- {ParentNode, ParentExists} = case Host of
- {_U, _S, _R} ->
- %% This is special case for PEP handling
- %% PEP does not uses hierarchy
- {[], true};
- _ ->
- case lists:sublist(Node, length(Node) - 1) of
- [] ->
- {[], true};
- Parent ->
- case nodeid(Host, Parent) of
- {result, _} -> {Parent, true};
- _ -> {Parent, false}
+ ParentExists =
+ case Host of
+ {_U, _S, _R} ->
+ %% This is special case for PEP handling
+ %% PEP does not uses hierarchy
+ true;
+ _ ->
+ case Parents of
+ [] -> true;
+ [Parent|_] ->
+ case nodeid(Host, Parent) of
+ {result, PNodeId} ->
+ case nodeowners(PNodeId) of
+ [{[], Host, []}] -> true;
+ Owners -> lists:member(BJID, Owners)
+ end;
+ _ ->
+ false
+ end;
+ _ ->
+ false
end
- end
end,
case ParentExists of
true ->
case set_node(#pubsub_node{
nodeid={Host, Node},
- parents=[ParentNode],
+ parents=Parents,
type=Type,
options=Options}) of
{result, NodeId} -> {ok, NodeId};
@@ -285,8 +294,8 @@ raw_to_node(Host, {Node, Parent, Type, NodeId}) ->
[]
end,
#pubsub_node{
- nodeid = {Host, string_to_node(Host, Node)},
- parents = [string_to_node(Host, Parent)],
+ nodeid = {Host, ?PUBSUB:string_to_node(Node)},
+ parents = [?PUBSUB:string_to_node(Parent)],
id = NodeId,
type = Type,
options = Options}.
@@ -295,7 +304,10 @@ raw_to_node(Host, {Node, Parent, Type, NodeId}) ->
%% Record = mod_pubsub:pubsub_node()
set_node(Record) ->
{Host, Node} = Record#pubsub_node.nodeid,
- [Parent] = Record#pubsub_node.parents,
+ Parent = case Record#pubsub_node.parents of
+ [] -> <<>>;
+ [First|_] -> First
+ end,
Type = Record#pubsub_node.type,
H = ?PUBSUB:escape(Host),
N = ?PUBSUB:escape(?PUBSUB:node_to_string(Node)),
@@ -352,5 +364,8 @@ nodeid(Host, Node) ->
{error, ?ERR_ITEM_NOT_FOUND}
end.
-string_to_node({_, _, _}, Node) -> Node;
-string_to_node(_, Node) -> ?PUBSUB:string_to_node(Node).
+nodeowners(NodeId) ->
+ {result, Res} = node_hometree_odbc:get_node_affiliations(NodeId),
+ lists:foldl(fun({LJID, owner}, Acc) -> [LJID|Acc];
+ (_, Acc) -> Acc
+ end, [], Res).
diff --git a/src/mod_pubsub/nodetree_virtual.erl b/src/mod_pubsub/nodetree_virtual.erl
index 24330e506..ec35b91d9 100644
--- a/src/mod_pubsub/nodetree_virtual.erl
+++ b/src/mod_pubsub/nodetree_virtual.erl
@@ -51,7 +51,7 @@
get_parentnodes_tree/3,
get_subnodes/3,
get_subnodes_tree/3,
- create_node/5,
+ create_node/6,
delete_node/2
]).
@@ -157,7 +157,7 @@ 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, _Parents) ->
{error, {virtual, {Host, Node}}}.
%% @spec (Host, Node) -> [mod_pubsub:node()]
diff --git a/src/mod_pubsub/pubsub_odbc.patch b/src/mod_pubsub/pubsub_odbc.patch
index 99c0a7809..a23147bc5 100644
--- a/src/mod_pubsub/pubsub_odbc.patch
+++ b/src/mod_pubsub/pubsub_odbc.patch
@@ -1,5 +1,5 @@
---- mod_pubsub.erl 2009-10-13 18:30:32.000000000 +0200
-+++ mod_pubsub_odbc.erl 2009-10-13 18:30:47.000000000 +0200
+--- mod_pubsub.erl 2009-10-20 16:33:47.000000000 +0200
++++ mod_pubsub_odbc.erl 2009-10-20 16:33:26.000000000 +0200
@@ -42,7 +42,7 @@
%%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see
%%% XEP-0060 section 12.18.
@@ -49,7 +49,18 @@
init_nodes(Host, ServerHost, NodeTree, Plugins),
State = #state{host = Host,
server_host = ServerHost,
-@@ -277,178 +275,6 @@
+@@ -269,207 +267,14 @@
+
+ init_nodes(Host, ServerHost, _NodeTree, Plugins) ->
+ %% TODO, this call should be done plugin side
+- case lists:member("hometree", Plugins) of
++ case lists:member("hometree_odbc", Plugins) of
+ true ->
+- create_node(Host, ServerHost, string_to_node("/home"), service_jid(Host), "hometree"),
+- create_node(Host, ServerHost, string_to_node("/home/"++ServerHost), service_jid(Host), "hometree");
++ create_node(Host, ServerHost, string_to_node("/home"), service_jid(Host), "hometree_odbc"),
++ create_node(Host, ServerHost, string_to_node("/home/"++ServerHost), service_jid(Host), "hometree_odbc");
+ false ->
ok
end.
@@ -179,7 +190,28 @@
- rename_default_nodeplugin();
- _ ->
- ok
-- end.
+- end,
+- mnesia:transaction(fun() ->
+- case catch mnesia:first(pubsub_node) of
+- {_, L} when is_list(L) ->
+- lists:foreach(
+- fun({H, N}) when is_list(N) ->
+- [Node] = mnesia:read({pubsub_node, {H, N}}),
+- Type = Node#pubsub_node.type,
+- BN = element(2, node_call(Type, path_to_node, [N])),
+- BP = case [element(2, node_call(Type, path_to_node, [P])) || P <- Node#pubsub_node.parents] of
+- [<<>>] -> [];
+- Parents -> Parents
+- end,
+- mnesia:write(Node#pubsub_node{nodeid={H, BN}, parents=BP}),
+- mnesia:delete({pubsub_node, {H, N}});
+- (_) ->
+- ok
+- end, mnesia:all_keys(pubsub_node));
+- _ ->
+- ok
+- end
+- end).
-
-rename_default_nodeplugin() ->
- lists:foreach(fun(Node) ->
@@ -228,7 +260,7 @@
send_queue(State, Msg) ->
Pid = State#state.send_loop,
case is_process_alive(Pid) of
-@@ -471,17 +297,15 @@
+@@ -492,17 +297,15 @@
%% for each node From is subscribed to
%% and if the node is so configured, send the last published item to From
lists:foreach(fun(PType) ->
@@ -252,7 +284,7 @@
true ->
% resource not concerned about that subscription
ok
-@@ -808,10 +632,10 @@
+@@ -825,10 +628,10 @@
{result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Subscriber]),
lists:foreach(fun
({Node, subscribed, _, JID}) ->
@@ -265,7 +297,7 @@
true ->
node_action(Host, Type, unsubscribe_node, [NodeId, Subscriber, JID, all]);
false ->
-@@ -926,7 +750,8 @@
+@@ -943,7 +746,8 @@
sub_el = SubEl} = IQ ->
{xmlelement, _, QAttrs, _} = SubEl,
Node = xml:get_attr_s("node", QAttrs),
@@ -275,7 +307,7 @@
{result, IQRes} ->
jlib:iq_to_xml(
IQ#iq{type = result,
-@@ -1031,7 +856,7 @@
+@@ -1048,7 +852,7 @@
[] ->
["leaf"]; %% No sub-nodes: it's a leaf node
_ ->
@@ -284,7 +316,7 @@
{result, []} -> ["collection"];
{result, _} -> ["leaf", "collection"];
_ -> []
-@@ -1047,8 +872,9 @@
+@@ -1064,8 +868,9 @@
[];
true ->
[{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []} |
@@ -296,7 +328,7 @@
end, features(Type))]
end,
%% TODO: add meta-data info (spec section 5.4)
-@@ -1076,14 +902,15 @@
+@@ -1093,21 +898,22 @@
{xmlelement, "feature", [{"var", ?NS_DISCO_ITEMS}], []},
{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []},
{xmlelement, "feature", [{"var", ?NS_VCARD}], []}] ++
@@ -313,19 +345,18 @@
-iq_disco_items(Host, [], From) ->
+iq_disco_items(Host, [], From, _RSM) ->
{result, lists:map(
- fun(#pubsub_node{nodeid = {_, SubNode}}) ->
- SN = node_to_string(SubNode),
-@@ -1093,7 +920,7 @@
- {"node", SN},
- {"name", RN}], []}
- end, tree_action(Host, get_subnodes, [Host, [], From]))};
+ fun(#pubsub_node{nodeid = {_, SubNode}, type = Type}) ->
+ {result, Path} = node_call(Type, node_to_path, [SubNode]),
+ [Name|_] = lists:reverse(Path),
+ {xmlelement, "item", [{"jid", Host}, {"name", Name}|nodeAttr(SubNode)], []}
+ end, tree_action(Host, get_subnodes, [Host, <<>>, From]))};
-iq_disco_items(Host, Item, From) ->
+iq_disco_items(Host, Item, From, RSM) ->
case string:tokens(Item, "!") of
[_SNode, _ItemID] ->
{result, []};
-@@ -1105,10 +932,10 @@
- %% TODO That is, remove name attribute (or node?, please check for 2.1)
+@@ -1115,10 +921,10 @@
+ Node = string_to_node(SNode),
Action =
fun(#pubsub_node{type = Type, id = NodeId}) ->
- % TODO call get_items/6 instead for access control (EJAB-1033)
@@ -338,16 +369,16 @@
end,
Nodes = lists:map(
fun(#pubsub_node{nodeid = {_, SubNode}}) ->
-@@ -1124,7 +951,7 @@
- {xmlelement, "item", [{"jid", Host}, {"node", SN},
- {"name", Name}], []}
+@@ -1129,7 +935,7 @@
+ {result, Name} = node_call(Type, get_item_name, [Host, Node, RN]),
+ {xmlelement, "item", [{"jid", Host}, {"name", Name}], []}
end, NodeItems),
- {result, Nodes ++ Items}
+ {result, Nodes ++ Items ++ jlib:rsm_encode(RsmOut)}
end,
case transaction(Host, Node, Action, sync_dirty) of
{result, {_, Result}} -> {result, Result};
-@@ -1256,7 +1083,8 @@
+@@ -1258,7 +1064,8 @@
(_, Acc) ->
Acc
end, [], xml:remove_cdata(Els)),
@@ -357,7 +388,7 @@
{get, "subscriptions"} ->
get_subscriptions(Host, Node, From, Plugins);
{get, "affiliations"} ->
-@@ -1279,7 +1107,9 @@
+@@ -1281,7 +1088,9 @@
iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
{xmlelement, _, _, SubEls} = SubEl,
@@ -367,8 +398,8 @@
+ end, xml:remove_cdata(SubEls)),
case Action of
[{xmlelement, Name, Attrs, Els}] ->
- Node = case Host of
-@@ -1405,7 +1235,8 @@
+ Node = string_to_node(xml:get_attr_s("node", Attrs)),
+@@ -1404,7 +1213,8 @@
_ -> []
end
end,
@@ -378,7 +409,7 @@
sync_dirty) of
{result, Res} -> Res;
Err -> Err
-@@ -1445,7 +1276,7 @@
+@@ -1444,7 +1254,7 @@
%%% authorization handling
@@ -387,7 +418,7 @@
Lang = "en", %% TODO fix
Stanza = {xmlelement, "message",
[],
-@@ -1474,7 +1305,7 @@
+@@ -1473,7 +1283,7 @@
[{xmlelement, "value", [], [{xmlcdata, "false"}]}]}]}]},
lists:foreach(fun(Owner) ->
ejabberd_router ! {route, service_jid(Host), jlib:make_jid(Owner), Stanza}
@@ -396,7 +427,7 @@
find_authorization_response(Packet) ->
{xmlelement, _Name, _Attrs, Els} = Packet,
-@@ -1541,8 +1372,8 @@
+@@ -1537,8 +1347,8 @@
"true" -> true;
_ -> false
end,
@@ -407,7 +438,7 @@
{result, Subscriptions} = node_call(Type, get_subscriptions, [NodeId, Subscriber]),
if
not IsApprover ->
-@@ -1728,7 +1559,7 @@
+@@ -1729,7 +1539,7 @@
Reply = [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
[{xmlelement, "create", nodeAttr(Node),
[]}]}],
@@ -416,7 +447,7 @@
{result, {Result, broadcast}} ->
%%Lang = "en", %% TODO: fix
%%OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-@@ -1836,7 +1667,7 @@
+@@ -1837,7 +1647,7 @@
%%The node does not exist.
%%
subscribe_node(Host, Node, From, JID, Configuration) ->
@@ -425,7 +456,7 @@
{result, GoodSubOpts} -> GoodSubOpts;
_ -> invalid
end,
-@@ -1844,7 +1675,7 @@
+@@ -1845,7 +1655,7 @@
error -> {"", "", ""};
J -> jlib:jid_tolower(J)
end,
@@ -434,7 +465,7 @@
Features = features(Type),
SubscribeFeature = lists:member("subscribe", Features),
OptionsFeature = lists:member("subscription-options", Features),
-@@ -1863,9 +1694,13 @@
+@@ -1864,9 +1674,13 @@
{"", "", ""} ->
{false, false};
_ ->
@@ -451,7 +482,7 @@
end
end,
if
-@@ -2196,7 +2031,7 @@
+@@ -2197,7 +2011,7 @@
%% The permission are not checked in this function.
%% @todo We probably need to check that the user doing the query has the right
%% to read the items.
@@ -460,7 +491,7 @@
MaxItems =
if
SMaxItems == "" -> get_max_items_node(Host);
-@@ -2235,11 +2070,11 @@
+@@ -2236,11 +2050,11 @@
node_call(Type, get_items,
[NodeId, From,
AccessModel, PresenceSubscription, RosterGroup,
@@ -474,7 +505,7 @@
SendItems = case ItemIDs of
[] ->
Items;
-@@ -2252,7 +2087,8 @@
+@@ -2253,7 +2067,8 @@
%% number of items sent to MaxItems:
{result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
[{xmlelement, "items", nodeAttr(Node),
@@ -484,7 +515,7 @@
Error ->
Error
end
-@@ -2284,16 +2120,27 @@
+@@ -2285,16 +2100,27 @@
%% @doc Resend the items of a node to the user.
%% @todo use cache-last-item feature
send_items(Host, Node, NodeId, Type, LJID, last) ->
@@ -518,7 +549,7 @@
send_items(Host, Node, NodeId, Type, LJID, Number) ->
ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of
{result, []} ->
-@@ -2419,29 +2266,12 @@
+@@ -2420,29 +2246,12 @@
error ->
{error, ?ERR_BAD_REQUEST};
_ ->
@@ -551,7 +582,7 @@
end, Entities),
{result, []};
_ ->
-@@ -2494,11 +2324,11 @@
+@@ -2495,11 +2304,11 @@
end.
read_sub(Subscriber, Node, NodeID, SubID, Lang) ->
@@ -562,10 +593,10 @@
{result, #pubsub_subscription{options = Options}} ->
- {result, XdataEl} = pubsub_subscription:get_options_xform(Lang, Options),
+ {result, XdataEl} = pubsub_subscription_odbc:get_options_xform(Lang, Options),
- OptionsEl = {xmlelement, "options", [{"node", node_to_string(Node)},
- {"jid", jlib:jid_to_string(Subscriber)},
- {"subid", SubID}],
-@@ -2525,7 +2355,7 @@
+ OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)},
+ {"subid", SubID}|nodeAttr(Node)],
+ [XdataEl]},
+@@ -2525,7 +2334,7 @@
end.
set_options_helper(Configuration, JID, NodeID, SubID, Type) ->
@@ -574,7 +605,7 @@
{result, GoodSubOpts} -> GoodSubOpts;
_ -> invalid
end,
-@@ -2554,7 +2384,7 @@
+@@ -2554,7 +2363,7 @@
write_sub(_Subscriber, _NodeID, _SubID, invalid) ->
{error, extended_error(?ERR_BAD_REQUEST, "invalid-options")};
write_sub(Subscriber, NodeID, SubID, Options) ->
@@ -583,7 +614,7 @@
{error, notfound} ->
{error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
{result, _} ->
-@@ -2722,8 +2552,8 @@
+@@ -2722,8 +2531,8 @@
{"subscription", subscription_to_string(Sub)} | nodeAttr(Node)], []}]}]},
ejabberd_router ! {route, service_jid(Host), jlib:make_jid(JID), Stanza}
end,
@@ -594,7 +625,7 @@
true ->
Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) ->
-@@ -3013,7 +2843,7 @@
+@@ -3007,7 +2816,7 @@
{Depth, [{N, get_node_subs(N)} || N <- Nodes]}
end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))}
end,
@@ -603,7 +634,7 @@
{result, CollSubs} -> CollSubs;
_ -> []
end.
-@@ -3027,9 +2857,9 @@
+@@ -3021,9 +2830,9 @@
get_options_for_subs(NodeID, Subs) ->
lists:foldl(fun({JID, subscribed, SubID}, Acc) ->
@@ -615,7 +646,7 @@
_ -> Acc
end;
(_, Acc) ->
-@@ -3227,6 +3057,30 @@
+@@ -3221,6 +3030,30 @@
Result
end.
@@ -646,7 +677,7 @@
%% @spec (Host, Options) -> MaxItems
%% Host = host()
%% Options = [Option]
-@@ -3613,7 +3467,13 @@
+@@ -3607,7 +3440,13 @@
tree_action(Host, Function, Args) ->
?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]),
Fun = fun() -> tree_call(Host, Function, Args) end,
@@ -661,7 +692,7 @@
%% @doc node plugin call.
node_call(Type, Function, Args) ->
-@@ -3633,13 +3493,13 @@
+@@ -3627,13 +3466,13 @@
node_action(Host, Type, Function, Args) ->
?DEBUG("node_action ~p ~p ~p ~p",[Host,Type,Function,Args]),
@@ -677,7 +708,7 @@
case tree_call(Host, get_node, [Host, Node]) of
N when is_record(N, pubsub_node) ->
case Action(N) of
-@@ -3652,8 +3512,14 @@
+@@ -3646,8 +3485,14 @@
end
end, Trans).
@@ -694,7 +725,7 @@
{result, Result} -> {result, Result};
{error, Error} -> {error, Error};
{atomic, {result, Result}} -> {result, Result};
-@@ -3661,6 +3527,15 @@
+@@ -3655,6 +3500,15 @@
{aborted, Reason} ->
?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
{error, ?ERR_INTERNAL_SERVER_ERROR};
@@ -710,7 +741,7 @@
{'EXIT', Reason} ->
?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]),
{error, ?ERR_INTERNAL_SERVER_ERROR};
-@@ -3669,6 +3544,17 @@
+@@ -3663,6 +3517,17 @@
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.