25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00
xmpp.chapril.org-ejabberd/src/mod_privacy_sql.erl

529 lines
18 KiB
Erlang
Raw Normal View History

%%%-------------------------------------------------------------------
2016-12-27 10:44:07 +01:00
%%% File : mod_privacy_sql.erl
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
2016-12-27 10:44:07 +01:00
%%%
%%%
2024-01-22 16:40:01 +01:00
%%% ejabberd, Copyright (C) 2002-2024 ProcessOne
2016-12-27 10:44:07 +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.
%%%
%%%----------------------------------------------------------------------
-module(mod_privacy_sql).
2016-05-04 20:01:05 +02:00
-behaviour(mod_privacy).
%% API
-export([init/2, set_default/3, unset_default/2, set_lists/1,
set_list/4, get_lists/2, get_list/3, remove_lists/2,
remove_list/3, import/1, export/1]).
-export([item_to_raw/1, raw_to_item/1]).
-export([sql_schemas/0]).
-include_lib("xmpp/include/xmpp.hrl").
-include("mod_privacy.hrl").
-include("logger.hrl").
2016-05-04 20:01:05 +02:00
-include("ejabberd_sql_pt.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(Host, _Opts) ->
ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()),
ok.
sql_schemas() ->
[#sql_schema{
version = 1,
tables =
[#sql_table{
name = <<"privacy_default_list">>,
columns =
[#sql_column{name = <<"username">>, type = text},
#sql_column{name = <<"server_host">>, type = text},
#sql_column{name = <<"name">>, type = text}],
indices = [#sql_index{
columns = [<<"server_host">>, <<"username">>],
unique = true}]},
#sql_table{
name = <<"privacy_list">>,
columns =
[#sql_column{name = <<"username">>, type = text},
#sql_column{name = <<"server_host">>, type = text},
#sql_column{name = <<"name">>, type = text},
#sql_column{name = <<"id">>, type = bigserial},
#sql_column{name = <<"created_at">>, type = timestamp,
default = true}],
indices = [#sql_index{
columns = [<<"id">>],
unique = true},
#sql_index{
columns = [<<"server_host">>, <<"username">>,
<<"name">>],
unique = true}]},
#sql_table{
name = <<"privacy_list_data">>,
columns =
[#sql_column{name = <<"id">>, type = bigint,
opts = [#sql_references{
table = <<"privacy_list">>,
column = <<"id">>}]},
#sql_column{name = <<"t">>, type = {char, 1}},
#sql_column{name = <<"value">>, type = text},
#sql_column{name = <<"action">>, type = {char, 1}},
#sql_column{name = <<"ord">>, type = numeric},
#sql_column{name = <<"match_all">>, type = boolean},
#sql_column{name = <<"match_iq">>, type = boolean},
#sql_column{name = <<"match_message">>, type = boolean},
#sql_column{name = <<"match_presence_in">>, type = boolean},
#sql_column{name = <<"match_presence_out">>, type = boolean}],
indices = [#sql_index{columns = [<<"id">>]}]}]}].
unset_default(LUser, LServer) ->
case unset_default_privacy_list(LUser, LServer) of
ok ->
ok;
_Err ->
{error, db_failure}
end.
set_default(LUser, LServer, Name) ->
F = fun () ->
case get_privacy_list_names_t(LUser, LServer) of
{selected, []} ->
{error, notfound};
{selected, Names} ->
case lists:member({Name}, Names) of
true ->
set_default_privacy_list(LUser, LServer, Name);
false ->
{error, notfound}
end
end
end,
transaction(LServer, F).
remove_list(LUser, LServer, Name) ->
F = fun () ->
case get_default_privacy_list_t(LUser, LServer) of
{selected, []} ->
remove_privacy_list_t(LUser, LServer, Name);
{selected, [{Default}]} ->
if Name == Default ->
{error, conflict};
true ->
remove_privacy_list_t(LUser, LServer, Name)
end
end
end,
transaction(LServer, F).
set_lists(#privacy{us = {LUser, LServer},
default = Default,
lists = Lists}) ->
F = fun() ->
lists:foreach(
fun({Name, List}) ->
add_privacy_list(LUser, LServer, Name),
{selected, [{I}]} =
get_privacy_list_id_t(LUser, LServer, Name),
RItems = lists:map(fun item_to_raw/1, List),
set_privacy_list(I, RItems),
if is_binary(Default) ->
set_default_privacy_list(
LUser, LServer, Default);
true ->
ok
end
end, Lists)
end,
transaction(LServer, F).
set_list(LUser, LServer, Name, List) ->
RItems = lists:map(fun item_to_raw/1, List),
F = fun() ->
{ID, New} = case get_privacy_list_id_t(LUser, LServer, Name) of
{selected, []} ->
add_privacy_list(LUser, LServer, Name),
{selected, [{I}]} =
get_privacy_list_id_t(LUser, LServer, Name),
{I, true};
{selected, [{I}]} -> {I, false}
end,
case New of
false ->
set_privacy_list(ID, RItems);
_ ->
set_privacy_list_new(ID, RItems)
end
end,
transaction(LServer, F).
get_list(LUser, LServer, default) ->
case get_default_privacy_list(LUser, LServer) of
{selected, []} ->
error;
{selected, [{Default}]} ->
get_list(LUser, LServer, Default);
_Err ->
{error, db_failure}
end;
get_list(LUser, LServer, Name) ->
case get_privacy_list_data(LUser, LServer, Name) of
{selected, []} ->
error;
{selected, RItems} ->
{ok, {Name, lists:flatmap(fun raw_to_item/1, RItems)}};
_Err ->
{error, db_failure}
end.
get_lists(LUser, LServer) ->
case get_default_privacy_list(LUser, LServer) of
{selected, Selected} ->
Default = case Selected of
[] -> none;
[{DefName}] -> DefName
end,
case get_privacy_list_names(LUser, LServer) of
{selected, Names} ->
case lists:foldl(
fun(_, {error, _} = Err) ->
Err;
({Name}, Acc) ->
case get_privacy_list_data(LUser, LServer, Name) of
{selected, RItems} ->
Items = lists:flatmap(
fun raw_to_item/1,
RItems),
[{Name, Items}|Acc];
_Err ->
{error, db_failure}
end
end, [], Names) of
{error, Reason} ->
{error, Reason};
Lists ->
{ok, #privacy{default = Default,
us = {LUser, LServer},
lists = Lists}}
end;
_Err ->
{error, db_failure}
end;
_Err ->
{error, db_failure}
end.
remove_lists(LUser, LServer) ->
case del_privacy_lists(LUser, LServer) of
ok ->
ok;
_Err ->
{error, db_failure}
end.
export(Server) ->
SqlType = ejabberd_option:sql_type(Server),
2016-04-20 11:27:32 +02:00
case catch ejabberd_sql:sql_query(jid:nameprep(Server),
[<<"select id from privacy_list order by "
"id desc limit 1;">>]) of
{selected, [<<"id">>], [[I]]} ->
put(id, binary_to_integer(I));
_ ->
put(id, 0)
end,
[{privacy,
fun(Host, #privacy{us = {LUser, LServer}, lists = Lists,
default = Default})
when LServer == Host ->
if Default /= none ->
2016-05-04 20:01:05 +02:00
[?SQL("delete from privacy_default_list where"
" username=%(LUser)s and %(LServer)H;"),
?SQL_INSERT(
"privacy_default_list",
["username=%(LUser)s",
"server_host=%(LServer)s",
"name=%(Default)s"])];
true ->
[]
end ++
lists:flatmap(
fun({Name, List}) ->
RItems = lists:map(fun item_to_raw/1, List),
2016-05-04 20:01:05 +02:00
ID = get_id(),
[?SQL("delete from privacy_list where"
" username=%(LUser)s and %(LServer)H and"
2016-05-04 20:01:05 +02:00
" name=%(Name)s;"),
?SQL_INSERT(
"privacy_list",
["username=%(LUser)s",
"server_host=%(LServer)s",
"name=%(Name)s",
"id=%(ID)d"]),
2016-05-04 20:01:05 +02:00
?SQL("delete from privacy_list_data where"
" id=%(ID)d;")] ++
case SqlType of
pgsql ->
[?SQL("insert into privacy_list_data(id, t, "
"value, action, ord, match_all, match_iq, "
"match_message, match_presence_in, "
"match_presence_out) "
"values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
" %(Order)d, CAST(%(MatchAll)b as boolean), CAST(%(MatchIQ)b as boolean),"
" CAST(%(MatchMessage)b as boolean), CAST(%(MatchPresenceIn)b as boolean),"
" CAST(%(MatchPresenceOut)b as boolean));")
|| {SType, SValue, SAction, Order,
MatchAll, MatchIQ,
MatchMessage, MatchPresenceIn,
MatchPresenceOut} <- RItems];
_ ->
2016-05-04 20:01:05 +02:00
[?SQL("insert into privacy_list_data(id, t, "
"value, action, ord, match_all, match_iq, "
"match_message, match_presence_in, "
"match_presence_out) "
"values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
" %(Order)d, %(MatchAll)b, %(MatchIQ)b,"
" %(MatchMessage)b, %(MatchPresenceIn)b,"
" %(MatchPresenceOut)b);")
2016-05-04 20:01:05 +02:00
|| {SType, SValue, SAction, Order,
MatchAll, MatchIQ,
MatchMessage, MatchPresenceIn,
MatchPresenceOut} <- RItems]
end
end,
Lists);
(_Host, _R) ->
[]
end}].
get_id() ->
ID = get(id),
put(id, ID + 1),
ID + 1.
2016-11-22 14:48:01 +01:00
import(_) ->
ok.
%%%===================================================================
%%% Internal functions
%%%===================================================================
transaction(LServer, F) ->
case ejabberd_sql:sql_transaction(LServer, F) of
{atomic, Res} -> Res;
{aborted, _Reason} -> {error, db_failure}
end.
raw_to_item({SType, SValue, SAction, Order, MatchAll,
MatchIQ, MatchMessage, MatchPresenceIn,
MatchPresenceOut} = Row) ->
try
{Type, Value} = case SType of
<<"n">> -> {none, none};
<<"j">> ->
JID = jid:decode(SValue),
{jid, jid:tolower(JID)};
<<"g">> -> {group, SValue};
<<"s">> ->
case SValue of
<<"none">> -> {subscription, none};
<<"both">> -> {subscription, both};
<<"from">> -> {subscription, from};
<<"to">> -> {subscription, to}
end
end,
Action = case SAction of
<<"a">> -> allow;
<<"d">> -> deny
end,
[#listitem{type = Type, value = Value, action = Action,
order = Order, match_all = MatchAll, match_iq = MatchIQ,
match_message = MatchMessage,
match_presence_in = MatchPresenceIn,
match_presence_out = MatchPresenceOut}]
catch _:_ ->
2019-06-24 19:32:34 +02:00
?WARNING_MSG("Failed to parse row: ~p", [Row]),
[]
end.
item_to_raw(#listitem{type = Type, value = Value,
action = Action, order = Order, match_all = MatchAll,
match_iq = MatchIQ, match_message = MatchMessage,
match_presence_in = MatchPresenceIn,
match_presence_out = MatchPresenceOut}) ->
{SType, SValue} = case Type of
none -> {<<"n">>, <<"">>};
jid -> {<<"j">>, jid:encode(Value)};
2016-05-04 20:01:05 +02:00
group -> {<<"g">>, Value};
subscription ->
case Value of
none -> {<<"s">>, <<"none">>};
both -> {<<"s">>, <<"both">>};
from -> {<<"s">>, <<"from">>};
to -> {<<"s">>, <<"to">>}
end
end,
SAction = case Action of
allow -> <<"a">>;
deny -> <<"d">>
end,
{SType, SValue, SAction, Order, MatchAll, MatchIQ,
MatchMessage, MatchPresenceIn, MatchPresenceOut}.
get_default_privacy_list(LUser, LServer) ->
2017-05-23 11:25:13 +02:00
ejabberd_sql:sql_query(
LServer,
?SQL("select @(name)s from privacy_default_list "
"where username=%(LUser)s and %(LServer)H")).
get_default_privacy_list_t(LUser, LServer) ->
2017-05-23 11:25:13 +02:00
ejabberd_sql:sql_query_t(
?SQL("select @(name)s from privacy_default_list "
"where username=%(LUser)s and %(LServer)H")).
get_privacy_list_names(LUser, LServer) ->
2017-05-23 11:25:13 +02:00
ejabberd_sql:sql_query(
LServer,
?SQL("select @(name)s from privacy_list"
" where username=%(LUser)s and %(LServer)H")).
get_privacy_list_names_t(LUser, LServer) ->
2017-05-23 11:25:13 +02:00
ejabberd_sql:sql_query_t(
?SQL("select @(name)s from privacy_list"
" where username=%(LUser)s and %(LServer)H")).
get_privacy_list_id_t(LUser, LServer, Name) ->
2017-05-23 11:25:13 +02:00
ejabberd_sql:sql_query_t(
?SQL("select @(id)d from privacy_list"
" where username=%(LUser)s and %(LServer)H and name=%(Name)s")).
get_privacy_list_data(LUser, LServer, Name) ->
2017-05-23 11:25:13 +02:00
ejabberd_sql:sql_query(
LServer,
?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
"@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
"@(match_presence_out)b from privacy_list_data "
"where id ="
" (select id from privacy_list"
" where username=%(LUser)s and %(LServer)H and name=%(Name)s) "
2017-05-23 11:25:13 +02:00
"order by ord")).
set_default_privacy_list(LUser, LServer, Name) ->
2017-05-23 11:25:13 +02:00
?SQL_UPSERT_T(
"privacy_default_list",
["!username=%(LUser)s",
"!server_host=%(LServer)s",
2017-05-23 11:25:13 +02:00
"name=%(Name)s"]).
unset_default_privacy_list(LUser, LServer) ->
2017-05-23 11:25:13 +02:00
case ejabberd_sql:sql_query(
LServer,
?SQL("delete from privacy_default_list"
" where username=%(LUser)s and %(LServer)H")) of
{updated, _} -> ok;
Err -> Err
end.
remove_privacy_list_t(LUser, LServer, Name) ->
2017-05-23 11:25:13 +02:00
case ejabberd_sql:sql_query_t(
?SQL("delete from privacy_list where"
" username=%(LUser)s and %(LServer)H and name=%(Name)s")) of
{updated, 0} -> {error, notfound};
{updated, _} -> ok;
Err -> Err
end.
add_privacy_list(LUser, LServer, Name) ->
2017-05-23 11:25:13 +02:00
ejabberd_sql:sql_query_t(
?SQL_INSERT(
"privacy_list",
["username=%(LUser)s",
"server_host=%(LServer)s",
"name=%(Name)s"])).
set_privacy_list_new(ID, RItems) ->
2017-05-23 11:25:13 +02:00
lists:foreach(
fun({SType, SValue, SAction, Order, MatchAll, MatchIQ,
MatchMessage, MatchPresenceIn, MatchPresenceOut}) ->
ejabberd_sql:sql_query_t(
?SQL("insert into privacy_list_data(id, t, "
"value, action, ord, match_all, match_iq, "
"match_message, match_presence_in, match_presence_out) "
"values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
" %(Order)d, %(MatchAll)b, %(MatchIQ)b,"
" %(MatchMessage)b, %(MatchPresenceIn)b,"
" %(MatchPresenceOut)b)"))
end,
RItems).
calculate_difference(List1, List2) ->
Set1 = gb_sets:from_list(List1),
Set2 = gb_sets:from_list(List2),
{gb_sets:to_list(gb_sets:subtract(Set1, Set2)),
gb_sets:to_list(gb_sets:subtract(Set2, Set1))}.
set_privacy_list(ID, RItems) ->
case ejabberd_sql:sql_query_t(
?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
"@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
"@(match_presence_out)b from privacy_list_data "
"where id=%(ID)d")) of
{selected, ExistingItems} ->
{ToAdd2, ToDelete2} = calculate_difference(RItems, ExistingItems),
ToAdd3 = if
ToDelete2 /= [] ->
ejabberd_sql:sql_query_t(
?SQL("delete from privacy_list_data where id=%(ID)d")),
RItems;
true ->
ToAdd2
end,
lists:foreach(
fun({SType, SValue, SAction, Order, MatchAll, MatchIQ,
MatchMessage, MatchPresenceIn, MatchPresenceOut}) ->
ejabberd_sql:sql_query_t(
?SQL("insert into privacy_list_data(id, t, "
"value, action, ord, match_all, match_iq, "
"match_message, match_presence_in, match_presence_out) "
"values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
" %(Order)d, %(MatchAll)b, %(MatchIQ)b,"
" %(MatchMessage)b, %(MatchPresenceIn)b,"
" %(MatchPresenceOut)b)"))
end,
ToAdd3);
Err ->
Err
end.
del_privacy_lists(LUser, LServer) ->
2017-05-23 11:25:13 +02:00
case ejabberd_sql:sql_query(
LServer,
?SQL("delete from privacy_list where username=%(LUser)s and %(LServer)H")) of
2017-05-23 11:25:13 +02:00
{updated, _} ->
case ejabberd_sql:sql_query(
LServer,
?SQL("delete from privacy_default_list "
"where username=%(LUser)s and %(LServer)H")) of
2017-05-23 11:25:13 +02:00
{updated, _} -> ok;
Err -> Err
end;
Err ->
Err
end.