Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/processone/ejabberd.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/nodetree_tree_odbc.erl')
-rw-r--r--src/nodetree_tree_odbc.erl466
1 files changed, 466 insertions, 0 deletions
diff --git a/src/nodetree_tree_odbc.erl b/src/nodetree_tree_odbc.erl
new file mode 100644
index 000000000..9756b897b
--- /dev/null
+++ b/src/nodetree_tree_odbc.erl
@@ -0,0 +1,466 @@
+%%% ====================================================================
+%%% ``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-2013, ProcessOne
+%%% All Rights Reserved.''
+%%% This software is copyright 2006-2013, ProcessOne.
+%%%
+%%%
+%%% @copyright 2006-2013 ProcessOne
+%%% @author Christophe Romain <christophe.romain@process-one.net>
+%%% [http://www.process-one.net/]
+%%% @version {@vsn}, {@date} {@time}
+%%% @end
+%%% ====================================================================
+
+%%% @doc The module <strong>{@module}</strong> is the default PubSub node tree plugin.
+%%% <p>It is used as a default for all unknown PubSub node type. It can serve
+%%% as a developer basis and reference to build its own custom pubsub node tree
+%%% types.</p>
+%%% <p>PubSub node tree plugins are using the {@link gen_nodetree} behaviour.</p>
+%%% <p><strong>The API isn't stabilized yet</strong>. The pubsub plugin
+%%% development is still a work in progress. However, the system is already
+%%% useable and useful as is. Please, send us comments, feedback and
+%%% improvements.</p>
+
+-module(nodetree_tree_odbc).
+
+-author('christophe.romain@process-one.net').
+
+-include("pubsub.hrl").
+
+-include("jlib.hrl").
+
+-define(PUBSUB, mod_pubsub_odbc).
+
+-define(PLUGIN_PREFIX, <<"node_">>).
+
+-behaviour(gen_pubsub_nodetree).
+
+-export([init/3, terminate/2, options/0, set_node/1,
+ get_node/3, get_node/2, get_node/1, get_nodes/2,
+ get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3,
+ get_subnodes/3, get_subnodes_tree/3, create_node/6,
+ delete_node/2]).
+
+-export([raw_to_node/2]).
+
+%% ================
+%% API definition
+%% ================
+
+%% @spec (Host, ServerHost, Opts) -> any()
+%% Host = mod_pubsub:host()
+%% ServerHost = host()
+%% Opts = list()
+%% @doc <p>Called during pubsub modules initialisation. Any pubsub plugin must
+%% implement this function. It can return anything.</p>
+%% <p>This function is mainly used to trigger the setup task necessary for the
+%% plugin. It can be used for example by the developer to create the specific
+%% module database schema if it does not exists yet.</p>
+%% @spec () -> [Option]
+%% Option = mod_pubsub:nodetreeOption()
+%% @doc Returns the default pubsub node tree options.
+%% @spec (Host, Node, From) -> pubsubNode() | {error, Reason}
+%% Host = mod_pubsub:host()
+%% Node = mod_pubsub:pubsubNode()
+init(_Host, _ServerHost, _Opts) -> ok.
+
+terminate(_Host, _ServerHost) -> ok.
+
+options() -> [{virtual_tree, false}, {odbc, true}].
+
+get_node(Host, Node, _From) -> get_node(Host, Node).
+
+-spec(get_node/2 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId())
+ -> mod_pubsub:pubsubNode()
+ | {error, _}
+).
+get_node(Host, Node) ->
+ H = (?PUBSUB):escape(Host),
+ N = (?PUBSUB):escape(Node),
+ case catch
+ ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
+ "pubsub_node where host='">>,
+ H, <<"' and node='">>, N, <<"';">>])
+ of
+ {selected,
+ [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
+ [RItem]} ->
+ raw_to_node(Host, RItem);
+ {'EXIT', _Reason} ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ _ -> {error, ?ERR_ITEM_NOT_FOUND}
+ end.
+
+-spec(get_node/1 ::
+(
+ NodeIdx::mod_pubsub:nodeIdx())
+ -> mod_pubsub:pubsubNode()
+ | {error, _}
+).
+get_node(NodeIdx) ->
+ case catch
+ ejabberd_odbc:sql_query_t([<<"select host, node, parent, type from "
+ "pubsub_node where nodeid='">>,
+ NodeIdx, <<"';">>])
+ of
+ {selected,
+ [<<"host">>, <<"node">>, <<"parent">>, <<"type">>],
+ [{Host, Node, Parent, Type}]} ->
+ raw_to_node(Host, {Node, Parent, Type, NodeIdx});
+ {'EXIT', _Reason} ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ _ -> {error, ?ERR_ITEM_NOT_FOUND}
+ end.
+
+%% @spec (Host, From) -> [pubsubNode()] | {error, Reason}
+%% Host = mod_pubsub:host() | mod_pubsub:jid()
+get_nodes(Host, _From) -> get_nodes(Host).
+
+-spec(get_nodes/1 ::
+(
+ Host::mod_pubsub:host())
+ -> [mod_pubsub:pubsubNode()]
+).
+get_nodes(Host) ->
+ H = (?PUBSUB):escape(Host),
+ case catch
+ ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
+ "pubsub_node where host='">>,
+ H, <<"';">>])
+ of
+ {selected,
+ [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
+ RItems} ->
+ lists:map(fun (Item) -> raw_to_node(Host, Item) end,
+ RItems);
+ _ -> []
+ end.
+
+%% @spec (Host, Node, From) -> [{Depth, Record}] | {error, Reason}
+%% Host = mod_pubsub:host() | mod_pubsub:jid()
+%% Node = mod_pubsub:pubsubNode()
+%% From = mod_pubsub:jid()
+%% Depth = integer()
+%% Record = pubsubNode()
+%% @doc <p>Default node tree does not handle parents, return empty list.</p>
+%% @spec (Host, Node, From) -> [{Depth, Record}] | {error, Reason}
+%% Host = mod_pubsub:host() | mod_pubsub:jid()
+%% Node = mod_pubsub:pubsubNode()
+%% From = mod_pubsub:jid()
+%% Depth = integer()
+%% Record = pubsubNode()
+%% @doc <p>Default node tree does not handle parents, return a list
+%% containing just this node.</p>
+get_parentnodes(_Host, _Node, _From) -> [].
+
+-spec(get_parentnodes_tree/3 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId(),
+ From :: jid())
+ -> [{0, [mod_pubsub:pubsubNode(),...]}]
+).
+
+get_parentnodes_tree(Host, Node, From) ->
+ case get_node(Host, Node, From) of
+ N when is_record(N, pubsub_node) -> [{0, [N]}];
+ _Error -> []
+ end.
+
+get_subnodes(Host, Node, _From) ->
+ get_subnodes(Host, Node).
+
+%% @spec (Host, Index) -> [pubsubNode()] | {error, Reason}
+%% Host = mod_pubsub:host()
+%% Node = mod_pubsub:pubsubNode()
+-spec(get_subnodes/2 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId())
+ -> [mod_pubsub:pubsubNode()]
+).
+get_subnodes(Host, Node) ->
+ H = (?PUBSUB):escape(Host),
+ N = (?PUBSUB):escape(Node),
+ case catch
+ ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
+ "pubsub_node where host='">>,
+ H, <<"' and parent='">>, N, <<"';">>])
+ of
+ {selected,
+ [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
+ RItems} ->
+ lists:map(fun (Item) -> raw_to_node(Host, Item) end,
+ RItems);
+ _ -> []
+ end.
+
+get_subnodes_tree(Host, Node, _From) ->
+ get_subnodes_tree(Host, Node).
+
+%% @spec (Host, Index) -> [pubsubNode()] | {error, Reason}
+%% Host = mod_pubsub:host()
+%% Node = mod_pubsub:pubsubNode()
+-spec(get_subnodes_tree/2 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId())
+ -> [mod_pubsub:pubsubNode()]
+).
+
+get_subnodes_tree(Host, Node) ->
+ H = (?PUBSUB):escape(Host),
+ N = (?PUBSUB):escape(Node),
+ case catch
+ ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
+ "pubsub_node where host='">>,
+ H, <<"' and node like '">>, N, <<"%';">>])
+ of
+ {selected,
+ [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
+ RItems} ->
+ lists:map(fun (Item) -> raw_to_node(Host, Item) end,
+ RItems);
+ _ -> []
+ end.
+
+%% @spec (Host, Node, Type, Owner, Options, Parents) -> ok | {error, Reason}
+%% Host = mod_pubsub:host() | mod_pubsub:jid()
+%% Node = mod_pubsub:pubsubNode()
+%% NodeType = mod_pubsub:nodeType()
+%% Owner = mod_pubsub:jid()
+%% Options = list()
+%% Parents = list()
+
+-spec(create_node/6 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId(),
+ Type :: binary(),
+ Owner :: jid(),
+ Options :: mod_pubsub:nodeOptions(),
+ Parents :: [mod_pubsub:nodeId()])
+ -> {ok, NodeIdx::mod_pubsub:nodeIdx()}
+ %%%
+ | {error, _}
+).
+
+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} ->
+ 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,
+ case ParentExists of
+ true ->
+ case set_node(#pubsub_node{nodeid = {Host, Node},
+ parents = Parents, type = Type,
+ options = Options})
+ of
+ {result, NodeId} -> {ok, NodeId};
+ Other -> Other
+ end;
+ false -> {error, ?ERR_FORBIDDEN}
+ end;
+ {result, _} -> {error, ?ERR_CONFLICT};
+ Error -> Error
+ end.
+
+%% @spec (Host, Node) -> [mod_pubsub:node()]
+%% Host = mod_pubsub:host() | mod_pubsub:jid()
+%% Node = mod_pubsub:pubsubNode()
+-spec(delete_node/2 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId())
+ -> [mod_pubsub:pubsubNode()]
+).
+
+delete_node(Host, Node) ->
+ H = (?PUBSUB):escape(Host),
+ N = (?PUBSUB):escape(Node),
+ Removed = get_subnodes_tree(Host, Node),
+ catch
+ ejabberd_odbc:sql_query_t([<<"delete from pubsub_node where host='">>,
+ H, <<"' and node like '">>, N, <<"%';">>]),
+ Removed.
+
+%% helpers
+-spec(raw_to_node/2 ::
+(
+ Host :: mod_pubus:host(),
+ _ :: {NodeId::mod_pubsub:nodeId(),
+ Parent::mod_pubsub:nodeId(),
+ Type::binary(),
+ NodeIdx::mod_pubsub:nodeIdx()})
+ -> mod_pubsub:pubsubNode()
+).
+raw_to_node(Host, {Node, Parent, Type, NodeIdx}) ->
+ Options = case catch
+ ejabberd_odbc:sql_query_t([<<"select name,val from pubsub_node_option "
+ "where nodeid='">>,
+ NodeIdx, <<"';">>])
+ of
+ {selected, [<<"name">>, <<"val">>], ROptions} ->
+ DbOpts = lists:map(fun ({Key, Value}) ->
+ RKey =
+ jlib:binary_to_atom(Key),
+ Tokens = element(2,
+ erl_scan:string(<<Value/binary,
+ ".">>)),
+ RValue = element(2,
+ erl_parse:parse_term(Tokens)),
+ {RKey, RValue}
+ end,
+ ROptions),
+ Module =
+ jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
+ Type/binary>>),
+ StdOpts = Module:options(),
+ lists:foldl(fun ({Key, Value}, Acc) ->
+ lists:keyreplace(Key, 1, Acc,
+ {Key, Value})
+ end,
+ StdOpts, DbOpts);
+ _ -> []
+ end,
+%% @spec (NodeRecord) -> ok | {error, Reason}
+%% Record = mod_pubsub:pubsub_node()
+ #pubsub_node{nodeid =
+ {Host, Node},
+ parents = [Parent],
+ id = NodeIdx, type = Type, options = Options}.
+
+-spec(set_node/1 ::
+(
+ Record::mod_pubsub:pubsubNode())
+ -> {result, NodeIdx::mod_pubsub:nodeIdx()}
+ %%%
+ | {error, _}
+).
+set_node(Record) ->
+ {Host, Node} = Record#pubsub_node.nodeid,
+ Parent = case Record#pubsub_node.parents of
+ [] -> <<>>;
+ [First | _] -> First
+ end,
+ Type = Record#pubsub_node.type,
+ H = (?PUBSUB):escape(Host),
+ N = (?PUBSUB):escape(Node),
+ P = (?PUBSUB):escape(Parent),
+ NodeIdx = case nodeid(Host, Node) of
+ {result, OldNodeIdx} ->
+ catch
+ ejabberd_odbc:sql_query_t([<<"delete from pubsub_node_option where "
+ "nodeid='">>,
+ OldNodeIdx, <<"';">>]),
+ catch
+ ejabberd_odbc:sql_query_t([<<"update pubsub_node set host='">>,
+ H, <<"' node='">>, N,
+ <<"' parent='">>, P,
+ <<"' type='">>, Type,
+ <<"' where nodeid='">>,
+ OldNodeIdx, <<"';">>]),
+ OldNodeIdx;
+ _ ->
+ catch
+ ejabberd_odbc:sql_query_t([<<"insert into pubsub_node(host, node, "
+ "parent, type) values('">>,
+ H, <<"', '">>, N, <<"', '">>, P,
+ <<"', '">>, Type, <<"');">>]),
+ case nodeid(Host, Node) of
+ {result, NewNodeIdx} -> NewNodeIdx;
+ _ -> none % this should not happen
+ end
+ end,
+ case NodeIdx of
+ none -> {error, ?ERR_INTERNAL_SERVER_ERROR};
+ _ ->
+ lists:foreach(fun ({Key, Value}) ->
+ SKey = iolist_to_binary(atom_to_list(Key)),
+ SValue =
+ (?PUBSUB):escape(lists:flatten(io_lib:fwrite("~p",
+ [Value]))),
+ catch
+ ejabberd_odbc:sql_query_t([<<"insert into pubsub_node_option(nodeid, "
+ "name, val) values('">>,
+ NodeIdx, <<"', '">>,
+ SKey, <<"', '">>,
+ SValue, <<"');">>])
+ end,
+ Record#pubsub_node.options),
+ {result, NodeIdx}
+ end.
+
+-spec(nodeid/2 ::
+(
+ Host :: mod_pubsub:host(),
+ NodeId :: mod_pubsub:nodeId())
+ -> {result, NodeIdx::mod_pubsub:nodeIdx()}
+ %%%
+ | {error, _}
+).
+
+nodeid(Host, NodeId) ->
+ H = (?PUBSUB):escape(Host),
+ N = (?PUBSUB):escape(NodeId),
+ case catch
+ ejabberd_odbc:sql_query_t([<<"select nodeid from pubsub_node where "
+ "host='">>,
+ H, <<"' and node='">>, N, <<"';">>])
+ of
+ {selected, [<<"nodeid">>], [{NodeIdx}]} ->
+ {result, NodeIdx};
+ {'EXIT', _Reason} ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ _ -> {error, ?ERR_ITEM_NOT_FOUND}
+ end.
+
+-spec(nodeowners/1 ::
+(
+ NodeIdx::mod_pubsub:nodeIdx())
+ -> Node_Owners::[ljid()]
+).
+
+nodeowners(NodeIdx) ->
+ {result, Res} = node_hometree_odbc:get_node_affiliations(NodeIdx),
+ lists:foldl(fun ({LJID, owner}, Acc) -> [LJID | Acc];
+ (_, Acc) -> Acc
+ end,
+ [], Res).