%%%------------------------------------------------------------------- %%% Created : 1 Dec 2018 by Evgeny Khramtsov %%% %%% %%% 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]).