diff --git a/include/mod_caps.hrl b/include/mod_caps.hrl new file mode 100644 index 000000000..067df9490 --- /dev/null +++ b/include/mod_caps.hrl @@ -0,0 +1,4 @@ +-record(caps_features, + {node_pair = {<<"">>, <<"">>} :: {binary(), binary()}, + features = [] :: [binary()] | pos_integer() + }). diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 0646d3812..bd0f14f5e 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -80,6 +80,12 @@ -record(state, {host = <<"">> :: binary()}). +-callback init(binary(), gen_mod:opts()) -> any(). +-callback caps_read(binary(), {binary(), binary()}) -> + {ok, non_neg_integer() | [binary()]} | error. +-callback caps_write(binary(), {binary(), binary()}, + non_neg_integer() | [binary()]) -> any(). + start_link(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), gen_server:start_link({local, Proc}, ?MODULE, @@ -300,28 +306,9 @@ c2s_broadcast_recipients(InAcc, Host, C2SState, end; c2s_broadcast_recipients(Acc, _, _, _, _, _) -> Acc. -init_db(mnesia, _Host) -> - case catch mnesia:table_info(caps_features, storage_type) of - {'EXIT', _} -> - ok; - disc_only_copies -> - ok; - _ -> - mnesia:delete_table(caps_features) - end, - mnesia:create_table(caps_features, - [{disc_only_copies, [node()]}, - {local_content, true}, - {attributes, - record_info(fields, caps_features)}]), - update_table(), - mnesia:add_table_copy(caps_features, node(), - disc_only_copies); -init_db(_, _) -> - ok. - init([Host, Opts]) -> - init_db(gen_mod:db_type(Host, Opts), Host), + Mod = gen_mod:db_mod(Host, Opts, ?MODULE), + Mod:init(Host, Opts), MaxSize = gen_mod:get_opt(cache_size, Opts, fun(I) when is_integer(I), I>0 -> I end, 1000), @@ -450,65 +437,13 @@ feature_response(_IQResult, Host, From, Caps, caps_read_fun(Host, Node) -> LServer = jid:nameprep(Host), - DBType = gen_mod:db_type(LServer, ?MODULE), - caps_read_fun(LServer, Node, DBType). - -caps_read_fun(_LServer, Node, mnesia) -> - fun () -> - case mnesia:dirty_read({caps_features, Node}) of - [#caps_features{features = Features}] -> {ok, Features}; - _ -> error - end - end; -caps_read_fun(_LServer, Node, riak) -> - fun() -> - case ejabberd_riak:get(caps_features, caps_features_schema(), Node) of - {ok, #caps_features{features = Features}} -> {ok, Features}; - _ -> error - end - end; -caps_read_fun(LServer, {Node, SubNode}, odbc) -> - fun() -> - SNode = ejabberd_odbc:escape(Node), - SSubNode = ejabberd_odbc:escape(SubNode), - case ejabberd_odbc:sql_query( - LServer, [<<"select feature from caps_features where ">>, - <<"node='">>, SNode, <<"' and subnode='">>, - SSubNode, <<"';">>]) of - {selected, [<<"feature">>], [[H]|_] = Fs} -> - case catch jlib:binary_to_integer(H) of - Int when is_integer(Int), Int>=0 -> - {ok, Int}; - _ -> - {ok, lists:flatten(Fs)} - end; - _ -> - error - end - end. + Mod = gen_mod:db_mod(LServer, ?MODULE), + fun() -> Mod:caps_read(LServer, Node) end. caps_write_fun(Host, Node, Features) -> LServer = jid:nameprep(Host), - DBType = gen_mod:db_type(LServer, ?MODULE), - caps_write_fun(LServer, Node, Features, DBType). - -caps_write_fun(_LServer, Node, Features, mnesia) -> - fun () -> - mnesia:dirty_write(#caps_features{node_pair = Node, - features = Features}) - end; -caps_write_fun(_LServer, Node, Features, riak) -> - fun () -> - ejabberd_riak:put(#caps_features{node_pair = Node, - features = Features}, - caps_features_schema()) - end; -caps_write_fun(LServer, NodePair, Features, odbc) -> - fun () -> - ejabberd_odbc:sql_transaction( - LServer, - sql_write_features_t(NodePair, Features)) - end. + Mod = gen_mod:db_mod(LServer, ?MODULE), + fun() -> Mod:caps_write(LServer, Node, Features) end. make_my_disco_hash(Host) -> JID = jid:make(<<"">>, Host, <<"">>), @@ -658,61 +593,20 @@ is_valid_node(Node) -> false end. -update_table() -> - Fields = record_info(fields, caps_features), - case mnesia:table_info(caps_features, attributes) of - Fields -> - ejabberd_config:convert_table_to_binary( - caps_features, Fields, set, - fun(#caps_features{node_pair = {N, _}}) -> N end, - fun(#caps_features{node_pair = {N, P}, - features = Fs} = R) -> - NewFs = if is_integer(Fs) -> - Fs; - true -> - [iolist_to_binary(F) || F <- Fs] - end, - R#caps_features{node_pair = {iolist_to_binary(N), - iolist_to_binary(P)}, - features = NewFs} - end); - _ -> - ?INFO_MSG("Recreating caps_features table", []), - mnesia:transform_table(caps_features, ignore, Fields) - end. - -sql_write_features_t({Node, SubNode}, Features) -> - SNode = ejabberd_odbc:escape(Node), - SSubNode = ejabberd_odbc:escape(SubNode), - NewFeatures = if is_integer(Features) -> - [jlib:integer_to_binary(Features)]; - true -> - Features - end, - [[<<"delete from caps_features where node='">>, - SNode, <<"' and subnode='">>, SSubNode, <<"';">>]| - [[<<"insert into caps_features(node, subnode, feature) ">>, - <<"values ('">>, SNode, <<"', '">>, SSubNode, <<"', '">>, - ejabberd_odbc:escape(F), <<"');">>] || F <- NewFeatures]]. - caps_features_schema() -> {record_info(fields, caps_features), #caps_features{}}. -export(_Server) -> - [{caps_features, - fun(_Host, #caps_features{node_pair = NodePair, - features = Features}) -> - sql_write_features_t(NodePair, Features); - (_Host, _R) -> - [] - end}]. +export(LServer) -> + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:export(LServer). import_info() -> [{<<"caps_features">>, 4}]. import_start(LServer, DBType) -> ets:new(caps_features_tmp, [private, named_table, bag]), - init_db(DBType, LServer), + Mod = gen_mod:db_mod(DBType, ?MODULE), + Mod:init(LServer, []), ok. import(_LServer, {odbc, _}, _DBType, <<"caps_features">>, diff --git a/src/mod_caps_mnesia.erl b/src/mod_caps_mnesia.erl new file mode 100644 index 000000000..0bf04b2c3 --- /dev/null +++ b/src/mod_caps_mnesia.erl @@ -0,0 +1,73 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 13 Apr 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(mod_caps_mnesia). +-behaviour(mod_caps). + +%% API +-export([init/2, caps_read/2, caps_write/3]). + +-include("mod_caps.hrl"). +-include("logger.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + case catch mnesia:table_info(caps_features, storage_type) of + {'EXIT', _} -> + ok; + disc_only_copies -> + ok; + _ -> + mnesia:delete_table(caps_features) + end, + mnesia:create_table(caps_features, + [{disc_only_copies, [node()]}, + {local_content, true}, + {attributes, + record_info(fields, caps_features)}]), + update_table(), + mnesia:add_table_copy(caps_features, node(), + disc_only_copies). + +caps_read(_LServer, Node) -> + case mnesia:dirty_read({caps_features, Node}) of + [#caps_features{features = Features}] -> {ok, Features}; + _ -> error + end. + +caps_write(_LServer, Node, Features) -> + mnesia:dirty_write(#caps_features{node_pair = Node, + features = Features}). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +update_table() -> + Fields = record_info(fields, caps_features), + case mnesia:table_info(caps_features, attributes) of + Fields -> + ejabberd_config:convert_table_to_binary( + caps_features, Fields, set, + fun(#caps_features{node_pair = {N, _}}) -> N end, + fun(#caps_features{node_pair = {N, P}, + features = Fs} = R) -> + NewFs = if is_integer(Fs) -> + Fs; + true -> + [iolist_to_binary(F) || F <- Fs] + end, + R#caps_features{node_pair = {iolist_to_binary(N), + iolist_to_binary(P)}, + features = NewFs} + end); + _ -> + ?INFO_MSG("Recreating caps_features table", []), + mnesia:transform_table(caps_features, ignore, Fields) + end. diff --git a/src/mod_caps_riak.erl b/src/mod_caps_riak.erl new file mode 100644 index 000000000..6e59ba867 --- /dev/null +++ b/src/mod_caps_riak.erl @@ -0,0 +1,38 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 13 Apr 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(mod_caps_riak). +-behaviour(mod_caps). + +%% API +-export([init/2, caps_read/2, caps_write/3]). + +-include("mod_caps.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + ok. + +caps_read(_LServer, Node) -> + case ejabberd_riak:get(caps_features, caps_features_schema(), Node) of + {ok, #caps_features{features = Features}} -> {ok, Features}; + _ -> error + end. + +caps_write(_LServer, Node, Features) -> + ejabberd_riak:put(#caps_features{node_pair = Node, + features = Features}, + caps_features_schema()). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +caps_features_schema() -> + {record_info(fields, caps_features), #caps_features{}}. diff --git a/src/mod_caps_sql.erl b/src/mod_caps_sql.erl new file mode 100644 index 000000000..353b95b1b --- /dev/null +++ b/src/mod_caps_sql.erl @@ -0,0 +1,71 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 13 Apr 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(mod_caps_sql). +-behaviour(mod_caps). + +%% API +-export([init/2, caps_read/2, caps_write/3, export/1]). + +-include("mod_caps.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + ok. + +caps_read(LServer, {Node, SubNode}) -> + SNode = ejabberd_odbc:escape(Node), + SSubNode = ejabberd_odbc:escape(SubNode), + case ejabberd_odbc:sql_query( + LServer, [<<"select feature from caps_features where ">>, + <<"node='">>, SNode, <<"' and subnode='">>, + SSubNode, <<"';">>]) of + {selected, [<<"feature">>], [[H]|_] = Fs} -> + case catch jlib:binary_to_integer(H) of + Int when is_integer(Int), Int>=0 -> + {ok, Int}; + _ -> + {ok, lists:flatten(Fs)} + end; + _ -> + error + end. + +caps_write(LServer, NodePair, Features) -> + ejabberd_odbc:sql_transaction( + LServer, + sql_write_features_t(NodePair, Features)). + +export(_Server) -> + [{caps_features, + fun(_Host, #caps_features{node_pair = NodePair, + features = Features}) -> + sql_write_features_t(NodePair, Features); + (_Host, _R) -> + [] + end}]. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +sql_write_features_t({Node, SubNode}, Features) -> + SNode = ejabberd_odbc:escape(Node), + SSubNode = ejabberd_odbc:escape(SubNode), + NewFeatures = if is_integer(Features) -> + [jlib:integer_to_binary(Features)]; + true -> + Features + end, + [[<<"delete from caps_features where node='">>, + SNode, <<"' and subnode='">>, SSubNode, <<"';">>]| + [[<<"insert into caps_features(node, subnode, feature) ">>, + <<"values ('">>, SNode, <<"', '">>, SSubNode, <<"', '">>, + ejabberd_odbc:escape(F), <<"');">>] || F <- NewFeatures]]. +