2015-12-15 16:11:29 +01:00
|
|
|
%%%----------------------------------------------------------------------
|
2016-04-20 11:27:32 +02:00
|
|
|
%%% File : pubsub_db_sql.erl
|
2015-12-15 16:11:29 +01:00
|
|
|
%%% Author : Pablo Polvorin <pablo.polvorin@process-one.net>
|
|
|
|
%%% Purpose : Provide helpers for PubSub ODBC backend
|
|
|
|
%%% Created : 7 Aug 2009 by Pablo Polvorin <pablo.polvorin@process-one.net>
|
2009-08-07 10:26:47 +02:00
|
|
|
%%%
|
|
|
|
%%%
|
2017-01-02 21:41:53 +01:00
|
|
|
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
2009-08-07 10:26:47 +02:00
|
|
|
%%%
|
2015-12-15 16:11:29 +01:00
|
|
|
%%% This program is free software; you can redistribute it and/or
|
|
|
|
%%% modify it under the terms of the GNU General Public License as
|
|
|
|
%%% published by the Free Software Foundation; either version 2 of the
|
|
|
|
%%% License, or (at your option) any later version.
|
|
|
|
%%%
|
|
|
|
%%% This program is distributed in the hope that it will be useful,
|
|
|
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
%%% General Public License for more details.
|
|
|
|
%%%
|
|
|
|
%%% You should have received a copy of the GNU General Public License along
|
|
|
|
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
%%%
|
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
|
2016-04-20 11:27:32 +02:00
|
|
|
-module(pubsub_db_sql).
|
2013-03-14 10:33:02 +01:00
|
|
|
|
2017-08-03 15:34:01 +02:00
|
|
|
-compile([{parse_transform, ejabberd_sql_pt}]).
|
|
|
|
|
2009-08-07 10:26:47 +02:00
|
|
|
-author("pablo.polvorin@process-one.net").
|
|
|
|
|
|
|
|
-include("pubsub.hrl").
|
2017-08-03 15:34:01 +02:00
|
|
|
-include("ejabberd_sql_pt.hrl").
|
2009-08-07 10:26:47 +02:00
|
|
|
|
2013-03-14 10:33:02 +01:00
|
|
|
-export([add_subscription/1, read_subscription/1,
|
2015-04-08 17:12:05 +02:00
|
|
|
delete_subscription/1, update_subscription/1]).
|
2017-08-03 15:34:01 +02:00
|
|
|
-export([export/1]).
|
2009-08-07 10:26:47 +02:00
|
|
|
|
2009-08-21 12:24:21 +02:00
|
|
|
%% TODO: Those -spec lines produce errors in old Erlang versions.
|
|
|
|
%% They can be enabled again in ejabberd 3.0 because it uses R12B or higher.
|
|
|
|
%% -spec read_subscription(SubID :: string()) -> {ok, #pubsub_subscription{}} | notfound.
|
2009-08-07 10:26:47 +02:00
|
|
|
read_subscription(SubID) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
case
|
2016-04-20 11:27:32 +02:00
|
|
|
ejabberd_sql:sql_query_t([<<"select opt_name, opt_value from pubsub_subscr"
|
2015-04-08 17:12:05 +02:00
|
|
|
"iption_opt where subid = '">>,
|
2016-04-20 11:27:32 +02:00
|
|
|
ejabberd_sql:escape(SubID), <<"'">>])
|
2015-04-08 17:12:05 +02:00
|
|
|
of
|
|
|
|
{selected, [<<"opt_name">>, <<"opt_value">>], []} ->
|
|
|
|
notfound;
|
|
|
|
{selected, [<<"opt_name">>, <<"opt_value">>], Options} ->
|
|
|
|
{ok,
|
|
|
|
#pubsub_subscription{subid = SubID,
|
2016-04-20 11:27:32 +02:00
|
|
|
options = lists:map(fun subscription_opt_from_sql/1, Options)}}
|
2013-03-14 10:33:02 +01:00
|
|
|
end.
|
2009-08-07 10:26:47 +02:00
|
|
|
|
2009-08-21 12:24:21 +02:00
|
|
|
%% -spec delete_subscription(SubID :: string()) -> ok.
|
2009-08-07 10:26:47 +02:00
|
|
|
delete_subscription(SubID) ->
|
2015-04-08 17:12:05 +02:00
|
|
|
%% -spec update_subscription(#pubsub_subscription{}) -> ok .
|
|
|
|
%% -spec add_subscription(#pubsub_subscription{}) -> ok.
|
|
|
|
%% -------------- Internal utilities -----------------------
|
2016-04-20 11:27:32 +02:00
|
|
|
ejabberd_sql:sql_query_t([<<"delete from pubsub_subscription_opt "
|
2015-04-08 17:12:05 +02:00
|
|
|
"where subid = '">>,
|
2016-04-20 11:27:32 +02:00
|
|
|
ejabberd_sql:escape(SubID), <<"'">>]),
|
2013-03-14 10:33:02 +01:00
|
|
|
ok.
|
|
|
|
|
2015-04-08 17:12:05 +02:00
|
|
|
update_subscription(#pubsub_subscription{subid = SubId} = Sub) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
delete_subscription(SubId), add_subscription(Sub).
|
|
|
|
|
2015-04-08 17:12:05 +02:00
|
|
|
add_subscription(#pubsub_subscription{subid = SubId, options = Opts}) ->
|
2016-04-20 11:27:32 +02:00
|
|
|
EscapedSubId = ejabberd_sql:escape(SubId),
|
2013-03-14 10:33:02 +01:00
|
|
|
lists:foreach(fun (Opt) ->
|
2016-04-20 11:27:32 +02:00
|
|
|
{OdbcOptName, OdbcOptValue} = subscription_opt_to_sql(Opt),
|
|
|
|
ejabberd_sql:sql_query_t([<<"insert into pubsub_subscription_opt(subid, "
|
2015-04-08 17:12:05 +02:00
|
|
|
"opt_name, opt_value)values ('">>,
|
|
|
|
EscapedSubId, <<"','">>,
|
|
|
|
OdbcOptName, <<"','">>,
|
|
|
|
OdbcOptValue, <<"')">>])
|
|
|
|
end,
|
|
|
|
Opts),
|
2013-03-14 10:33:02 +01:00
|
|
|
ok.
|
|
|
|
|
2016-09-08 14:49:27 +02:00
|
|
|
subscription_opt_from_sql([<<"DELIVER">>, Value]) ->
|
2016-04-20 11:27:32 +02:00
|
|
|
{deliver, sql_to_boolean(Value)};
|
2016-09-08 14:49:27 +02:00
|
|
|
subscription_opt_from_sql([<<"DIGEST">>, Value]) ->
|
2016-04-20 11:27:32 +02:00
|
|
|
{digest, sql_to_boolean(Value)};
|
2016-09-08 14:49:27 +02:00
|
|
|
subscription_opt_from_sql([<<"DIGEST_FREQUENCY">>, Value]) ->
|
2016-04-20 11:27:32 +02:00
|
|
|
{digest_frequency, sql_to_integer(Value)};
|
2016-09-08 14:49:27 +02:00
|
|
|
subscription_opt_from_sql([<<"EXPIRE">>, Value]) ->
|
2016-04-20 11:27:32 +02:00
|
|
|
{expire, sql_to_timestamp(Value)};
|
2016-09-08 14:49:27 +02:00
|
|
|
subscription_opt_from_sql([<<"INCLUDE_BODY">>, Value]) ->
|
2016-04-20 11:27:32 +02:00
|
|
|
{include_body, sql_to_boolean(Value)};
|
2009-08-07 10:26:47 +02:00
|
|
|
%%TODO: might be > than 1 show_values value??.
|
|
|
|
%% need to use compact all in only 1 opt.
|
2016-09-08 14:49:27 +02:00
|
|
|
subscription_opt_from_sql([<<"SHOW_VALUES">>, Value]) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
{show_values, Value};
|
2016-09-08 14:49:27 +02:00
|
|
|
subscription_opt_from_sql([<<"SUBSCRIPTION_TYPE">>, Value]) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
{subscription_type,
|
2015-04-08 17:12:05 +02:00
|
|
|
case Value of
|
|
|
|
<<"items">> -> items;
|
|
|
|
<<"nodes">> -> nodes
|
|
|
|
end};
|
2016-09-08 14:49:27 +02:00
|
|
|
subscription_opt_from_sql([<<"SUBSCRIPTION_DEPTH">>, Value]) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
{subscription_depth,
|
2015-04-08 17:12:05 +02:00
|
|
|
case Value of
|
|
|
|
<<"all">> -> all;
|
2016-04-20 11:27:32 +02:00
|
|
|
N -> sql_to_integer(N)
|
2015-04-08 17:12:05 +02:00
|
|
|
end}.
|
2009-08-07 10:26:47 +02:00
|
|
|
|
2016-04-20 11:27:32 +02:00
|
|
|
subscription_opt_to_sql({deliver, Bool}) ->
|
|
|
|
{<<"DELIVER">>, boolean_to_sql(Bool)};
|
|
|
|
subscription_opt_to_sql({digest, Bool}) ->
|
|
|
|
{<<"DIGEST">>, boolean_to_sql(Bool)};
|
|
|
|
subscription_opt_to_sql({digest_frequency, Int}) ->
|
|
|
|
{<<"DIGEST_FREQUENCY">>, integer_to_sql(Int)};
|
|
|
|
subscription_opt_to_sql({expire, Timestamp}) ->
|
|
|
|
{<<"EXPIRE">>, timestamp_to_sql(Timestamp)};
|
|
|
|
subscription_opt_to_sql({include_body, Bool}) ->
|
|
|
|
{<<"INCLUDE_BODY">>, boolean_to_sql(Bool)};
|
|
|
|
subscription_opt_to_sql({show_values, Values}) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
{<<"SHOW_VALUES">>, Values};
|
2016-04-20 11:27:32 +02:00
|
|
|
subscription_opt_to_sql({subscription_type, Type}) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
{<<"SUBSCRIPTION_TYPE">>,
|
2015-04-08 17:12:05 +02:00
|
|
|
case Type of
|
|
|
|
items -> <<"items">>;
|
|
|
|
nodes -> <<"nodes">>
|
|
|
|
end};
|
2016-04-20 11:27:32 +02:00
|
|
|
subscription_opt_to_sql({subscription_depth, Depth}) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
{<<"SUBSCRIPTION_DEPTH">>,
|
2015-04-08 17:12:05 +02:00
|
|
|
case Depth of
|
|
|
|
all -> <<"all">>;
|
2016-04-20 11:27:32 +02:00
|
|
|
N -> integer_to_sql(N)
|
2015-04-08 17:12:05 +02:00
|
|
|
end}.
|
2009-08-07 10:26:47 +02:00
|
|
|
|
2016-11-24 13:06:06 +01:00
|
|
|
integer_to_sql(N) -> integer_to_binary(N).
|
2013-03-14 10:33:02 +01:00
|
|
|
|
2016-04-20 11:27:32 +02:00
|
|
|
boolean_to_sql(true) -> <<"1">>;
|
|
|
|
boolean_to_sql(false) -> <<"0">>.
|
2009-08-07 10:26:47 +02:00
|
|
|
|
2016-11-13 08:44:53 +01:00
|
|
|
timestamp_to_sql(T) -> xmpp_util:encode_timestamp(T).
|
2009-08-07 10:26:47 +02:00
|
|
|
|
2016-09-24 22:34:28 +02:00
|
|
|
sql_to_integer(N) -> binary_to_integer(N).
|
2013-03-14 10:33:02 +01:00
|
|
|
|
2016-04-20 11:27:32 +02:00
|
|
|
sql_to_boolean(B) -> B == <<"1">>.
|
2009-08-07 10:26:47 +02:00
|
|
|
|
2016-11-13 08:44:53 +01:00
|
|
|
sql_to_timestamp(T) -> xmpp_util:decode_timestamp(T).
|
2017-08-03 15:34:01 +02:00
|
|
|
|
|
|
|
%% REVIEW:
|
|
|
|
%% * this code takes NODEID from Itemid2, and forgets about Nodeidx
|
|
|
|
%% * this code assumes Payload only contains one xmlelement()
|
|
|
|
%% * PUBLISHER is taken from Creation
|
|
|
|
export(_Server) ->
|
|
|
|
[{pubsub_item,
|
|
|
|
fun(_Host, #pubsub_item{itemid = {Itemid1, NODEID},
|
|
|
|
%nodeidx = _Nodeidx,
|
|
|
|
creation = {{C1, C2, C3}, Cusr},
|
|
|
|
modification = {{M1, M2, M3}, _Musr},
|
|
|
|
payload = Payload}) ->
|
|
|
|
ITEMID = ejabberd_sql:escape(Itemid1),
|
|
|
|
CREATION = ejabberd_sql:escape(list_to_binary(
|
|
|
|
string:join([string:right(integer_to_list(I),6,$0)||I<-[C1,C2,C3]],":"))),
|
|
|
|
MODIFICATION = ejabberd_sql:escape(list_to_binary(
|
|
|
|
string:join([string:right(integer_to_list(I),6,$0)||I<-[M1,M2,M3]],":"))),
|
|
|
|
PUBLISHER = ejabberd_sql:escape(jid:encode(Cusr)),
|
|
|
|
[PayloadEl] = [El || {xmlel,_,_,_} = El <- Payload],
|
|
|
|
PAYLOAD = ejabberd_sql:escape(fxml:element_to_binary(PayloadEl)),
|
|
|
|
[?SQL("delete from pubsub_item where itemid=%(ITEMID)s;"),
|
|
|
|
?SQL("insert into pubsub_item(itemid,nodeid,creation,modification,publisher,payload) \n"
|
|
|
|
" values (%(ITEMID)s, %(NODEID)d, %(CREATION)s,
|
|
|
|
%(MODIFICATION)s, %(PUBLISHER)s, %(PAYLOAD)s);")];
|
|
|
|
(_Host, _R) ->
|
|
|
|
[]
|
|
|
|
end},
|
|
|
|
%% REVIEW:
|
|
|
|
%% * From the mnesia table, the #pubsub_state.items is not used in ODBC
|
|
|
|
%% * Right now AFFILIATION is the first letter of Affiliation
|
|
|
|
%% * Right now SUBSCRIPTIONS expects only one Subscription
|
|
|
|
%% * Right now SUBSCRIPTIONS letter is the first letter of Subscription
|
|
|
|
{pubsub_state,
|
|
|
|
fun(_Host, #pubsub_state{stateid = {Jid, Stateid},
|
|
|
|
%nodeidx = Nodeidx,
|
|
|
|
items = _Items,
|
|
|
|
affiliation = Affiliation,
|
|
|
|
subscriptions = Subscriptions}) ->
|
|
|
|
STATEID = list_to_binary(integer_to_list(Stateid)),
|
|
|
|
JID = ejabberd_sql:escape(jid:encode(Jid)),
|
|
|
|
NODEID = <<"unknown">>, %% TODO: integer_to_list(Nodeidx),
|
|
|
|
AFFILIATION = list_to_binary(string:substr(atom_to_list(Affiliation),1,1)),
|
|
|
|
SUBSCRIPTIONS = list_to_binary(parse_subscriptions(Subscriptions)),
|
|
|
|
[?SQL("delete from pubsub_state where stateid=%(STATEID)s;"),
|
|
|
|
?SQL("insert into pubsub_state(stateid,jid,nodeid,affiliation,subscriptions)\n"
|
|
|
|
" values (%(STATEID)s, %(JID)s, %(NODEID)s, %(AFFILIATION)s, %(SUBSCRIPTIONS)s);")];
|
|
|
|
(_Host, _R) ->
|
|
|
|
[]
|
|
|
|
end},
|
|
|
|
|
|
|
|
%% REVIEW:
|
|
|
|
%% * Parents is not migrated to PARENTs
|
|
|
|
%% * Probably some option VALs are not correctly represented in mysql
|
|
|
|
{pubsub_node,
|
|
|
|
fun(_Host, #pubsub_node{nodeid = {Hostid, Nodeid},
|
|
|
|
id = Id,
|
|
|
|
parents = _Parents,
|
|
|
|
type = Type,
|
|
|
|
owners = Owners,
|
|
|
|
options = Options}) ->
|
|
|
|
HOST = case Hostid of
|
|
|
|
{U,S,R} -> ejabberd_sql:escape(jid:encode({U,S,R}));
|
|
|
|
_ -> ejabberd_sql:escape(Hostid)
|
|
|
|
end,
|
|
|
|
NODE = ejabberd_sql:escape(Nodeid),
|
|
|
|
PARENT = <<"">>,
|
|
|
|
IdB = integer_to_binary(Id),
|
|
|
|
TYPE = ejabberd_sql:escape(<<Type/binary, "_odbc">>),
|
|
|
|
[?SQL("delete from pubsub_node where nodeid=%(Id)d;"),
|
|
|
|
?SQL("insert into pubsub_node(host,node,nodeid,parent,type) \n"
|
|
|
|
" values (%(HOST)s, %(NODE)s, %(Id)d, %(PARENT)s, %(TYPE)s);"),
|
|
|
|
?SQL("delete from pubsub_node_option where nodeid=%(Id)d;"),
|
|
|
|
[["insert into pubsub_node_option(nodeid,name,val)\n"
|
|
|
|
" values (", IdB, ", '", atom_to_list(Name), "', '",
|
|
|
|
io_lib:format("~p", [Val]), "');\n"] || {Name,Val} <- Options],
|
|
|
|
?SQL("delete from pubsub_node_owner where nodeid=%(Id)d;"),
|
|
|
|
[["insert into pubsub_node_owner(nodeid,owner)\n"
|
|
|
|
" values (", IdB, ", '", jid:encode(Usr), "');\n"] || Usr <- Owners],"\n"];
|
|
|
|
(_Host, _R) ->
|
|
|
|
[]
|
|
|
|
end}].
|
|
|
|
|
|
|
|
parse_subscriptions([]) ->
|
|
|
|
"";
|
|
|
|
parse_subscriptions([{State, Item}]) ->
|
|
|
|
STATE = case State of
|
|
|
|
subscribed -> "s"
|
|
|
|
end,
|
|
|
|
string:join([STATE, Item],":").
|
|
|
|
|