xmpp.chapril.org-ejabberd/src/mod_mix_sql.erl

236 lines
7.4 KiB
Erlang

%%%-------------------------------------------------------------------
%%% Created : 1 Dec 2018 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
%%%
%%% 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_mix_sql).
-behaviour(mod_mix).
%% API
-export([init/2]).
-export([set_channel/6, get_channels/2, get_channel/3, del_channel/3]).
-export([set_participant/6, get_participant/4, get_participants/3, del_participant/4]).
-export([subscribe/5, unsubscribe/4, unsubscribe/5, get_subscribed/4]).
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
%% TODO
ok.
set_channel(LServer, Channel, Service, CreatorJID, Hidden, Key) ->
{User, Domain, _} = jid:tolower(CreatorJID),
RawJID = jid:encode(jid:remove_resource(CreatorJID)),
case ?SQL_UPSERT(LServer, "mix_channel",
["!channel=%(Channel)s",
"!service=%(Service)s",
"username=%(User)s",
"domain=%(Domain)s",
"jid=%(RawJID)s",
"hidden=%(Hidden)b",
"hmac_key=%(Key)s"]) of
ok -> ok;
_Err -> {error, db_failure}
end.
get_channels(LServer, Service) ->
case ejabberd_sql:sql_query(
LServer,
?SQL("select @(channel)s, @(hidden)b from mix_channel "
"where service=%(Service)s")) of
{selected, Ret} ->
{ok, [Channel || {Channel, Hidden} <- Ret, Hidden == false]};
_Err ->
{error, db_failure}
end.
get_channel(LServer, Channel, Service) ->
SQL = ?SQL("select @(jid)s, @(hidden)b, @(hmac_key)s from mix_channel "
"where channel=%(Channel)s and service=%(Service)s"),
case ejabberd_sql:sql_query(LServer, SQL) of
{selected, [{RawJID, Hidden, Key}]} ->
try jid:decode(RawJID) of
JID -> {ok, {JID, Hidden, Key}}
catch _:{bad_jid, _} ->
report_corrupted(jid, SQL),
{error, db_failure}
end;
{selected, []} -> {error, notfound};
_Err -> {error, db_failure}
end.
del_channel(LServer, Channel, Service) ->
F = fun() ->
ejabberd_sql:sql_query_t(
?SQL("delete from mix_channel where "
"channel=%(Channel)s and service=%(Service)s")),
ejabberd_sql:sql_query_t(
?SQL("delete from mix_participant where "
"channel=%(Channel)s and service=%(Service)s")),
ejabberd_sql:sql_query_t(
?SQL("delete from mix_subscription where "
"channel=%(Channel)s and service=%(Service)s"))
end,
case ejabberd_sql:sql_transaction(LServer, F) of
{atomic, _} -> ok;
_Err -> {error, db_failure}
end.
set_participant(LServer, Channel, Service, JID, ID, Nick) ->
{User, Domain, _} = jid:tolower(JID),
RawJID = jid:encode(jid:remove_resource(JID)),
case ?SQL_UPSERT(LServer, "mix_participant",
["!channel=%(Channel)s",
"!service=%(Service)s",
"!username=%(User)s",
"!domain=%(Domain)s",
"jid=%(RawJID)s",
"id=%(ID)s",
"nick=%(Nick)s"]) of
ok -> ok;
_Err -> {error, db_failure}
end.
get_participant(LServer, Channel, Service, JID) ->
{User, Domain, _} = jid:tolower(JID),
case ejabberd_sql:sql_query(
LServer,
?SQL("select @(id)s, @(nick)s from mix_participant "
"where channel=%(Channel)s and service=%(Service)s "
"and username=%(User)s and domain=%(Domain)s")) of
{selected, [Ret]} -> {ok, Ret};
{selected, []} -> {error, notfound};
_Err -> {error, db_failure}
end.
get_participants(LServer, Channel, Service) ->
SQL = ?SQL("select @(jid)s, @(id)s, @(nick)s from mix_participant "
"where channel=%(Channel)s and service=%(Service)s"),
case ejabberd_sql:sql_query(LServer, SQL) of
{selected, Ret} ->
{ok, lists:filtermap(
fun({RawJID, ID, Nick}) ->
try jid:decode(RawJID) of
JID -> {true, {JID, ID, Nick}}
catch _:{bad_jid, _} ->
report_corrupted(jid, SQL),
false
end
end, Ret)};
_Err ->
{error, db_failure}
end.
del_participant(LServer, Channel, Service, JID) ->
{User, Domain, _} = jid:tolower(JID),
case ejabberd_sql:sql_query(
LServer,
?SQL("delete from mix_participant where "
"channel=%(Channel)s and service=%(Service)s "
"and username=%(User)s and domain=%(Domain)s")) of
{updated, _} -> ok;
_Err -> {error, db_failure}
end.
subscribe(_LServer, _Channel, _Service, _JID, []) ->
ok;
subscribe(LServer, Channel, Service, JID, Nodes) ->
{User, Domain, _} = jid:tolower(JID),
RawJID = jid:encode(jid:remove_resource(JID)),
F = fun() ->
lists:foreach(
fun(Node) ->
?SQL_UPSERT_T(
"mix_subscription",
["!channel=%(Channel)s",
"!service=%(Service)s",
"!username=%(User)s",
"!domain=%(Domain)s",
"!node=%(Node)s",
"jid=%(RawJID)s"])
end, Nodes)
end,
case ejabberd_sql:sql_transaction(LServer, F) of
{atomic, _} -> ok;
_Err -> {error, db_failure}
end.
get_subscribed(LServer, Channel, Service, Node) ->
SQL = ?SQL("select @(jid)s from mix_subscription "
"where channel=%(Channel)s and service=%(Service)s "
"and node=%(Node)s"),
case ejabberd_sql:sql_query(LServer, SQL) of
{selected, Ret} ->
{ok, lists:filtermap(
fun({RawJID}) ->
try jid:decode(RawJID) of
JID -> {true, JID}
catch _:{bad_jid, _} ->
report_corrupted(jid, SQL),
false
end
end, Ret)};
_Err ->
{error, db_failure}
end.
unsubscribe(LServer, Channel, Service, JID) ->
{User, Domain, _} = jid:tolower(JID),
case ejabberd_sql:sql_query(
LServer,
?SQL("delete from mix_subscription "
"where channel=%(Channel)s and service=%(Service)s "
"and username=%(User)s and domain=%(Domain)s")) of
{updated, _} -> ok;
_Err -> {error, db_failure}
end.
unsubscribe(_LServer, _Channel, _Service, _JID, []) ->
ok;
unsubscribe(LServer, Channel, Service, JID, Nodes) ->
{User, Domain, _} = jid:tolower(JID),
F = fun() ->
lists:foreach(
fun(Node) ->
ejabberd_sql:sql_query_t(
?SQL("delete from mix_subscription "
"where channel=%(Channel)s "
"and service=%(Service)s "
"and username=%(User)s "
"and domain=%(Domain)s "
"and node=%(Node)s"))
end, Nodes)
end,
case ejabberd_sql:sql_transaction(LServer, F) of
{atomic, ok} -> ok;
_Err -> {error, db_failure}
end.
%%%===================================================================
%%% Internal functions
%%%===================================================================
-spec report_corrupted(atom(), #sql_query{}) -> ok.
report_corrupted(Column, SQL) ->
?ERROR_MSG("Corrupted value of '~ts' column returned by "
"SQL request: ~ts", [Column, SQL#sql_query.hash]).