diff options
Diffstat (limited to 'src/mod_roster_sql.erl')
-rw-r--r-- | src/mod_roster_sql.erl | 438 |
1 files changed, 293 insertions, 145 deletions
diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl index 61f59a990..6fbcaf80a 100644 --- a/src/mod_roster_sql.erl +++ b/src/mod_roster_sql.erl @@ -1,28 +1,44 @@ %%%------------------------------------------------------------------- -%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net> -%%% @copyright (C) 2016, Evgeny Khramtsov -%%% @doc -%%% -%%% @end +%%% File : mod_roster_sql.erl +%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net> %%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net> -%%%------------------------------------------------------------------- +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2019 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_roster_sql). --compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(mod_roster). %% API -export([init/2, read_roster_version/2, write_roster_version/4, - get_roster/2, get_roster_by_jid/3, - roster_subscribe/4, get_roster_by_jid_with_groups/3, - remove_user/2, update_roster/4, del_roster/3, transaction/2, - read_subscription_and_groups/3, get_only_items/2, - import/1, import/2, export/1]). + get_roster/2, get_roster_item/3, roster_subscribe/4, + read_subscription_and_groups/3, remove_user/2, + update_roster/4, del_roster/3, transaction/2, + process_rosteritems/5, + import/3, export/1, raw_to_record/2]). --include("jlib.hrl"). -include("mod_roster.hrl"). -include("ejabberd_sql_pt.hrl"). +-include("logger.hrl"). +-include("jid.hrl"). %%%=================================================================== %%% API @@ -31,27 +47,33 @@ init(_Host, _Opts) -> ok. read_roster_version(LUser, LServer) -> - case sql_queries:get_roster_version(LServer, LUser) of - {selected, [{Version}]} -> Version; - {selected, []} -> error + case ejabberd_sql:sql_query( + LServer, + ?SQL("select @(version)s from roster_version" + " where username = %(LUser)s and %(LServer)H")) of + {selected, [{Version}]} -> {ok, Version}; + {selected, []} -> error; + _ -> {error, db_failure} end. write_roster_version(LUser, LServer, InTransaction, Ver) -> if InTransaction -> - sql_queries:set_roster_version(LUser, Ver); + set_roster_version(LUser, LServer, Ver); true -> - sql_queries:sql_transaction( + transaction( LServer, - fun () -> - sql_queries:set_roster_version(LUser, Ver) - end) + fun () -> set_roster_version(LUser, LServer, Ver) end) end. get_roster(LUser, LServer) -> - case catch sql_queries:get_roster(LServer, LUser) of + case ejabberd_sql:sql_query( + LServer, + ?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s, " + "@(ask)s, @(askmessage)s, @(server)s, @(subscribe)s, " + "@(type)s from rosterusers " + "where username=%(LUser)s and %(LServer)H")) of {selected, Items} when is_list(Items) -> - JIDGroups = case catch sql_queries:get_roster_jid_groups( - LServer, LUser) of + JIDGroups = case get_roster_jid_groups(LServer, LUser) of {selected, JGrps} when is_list(JGrps) -> JGrps; _ -> @@ -61,107 +83,104 @@ get_roster(LUser, LServer) -> dict:append(J, G, Acc) end, dict:new(), JIDGroups), - lists:flatmap( - fun(I) -> - case raw_to_record(LServer, I) of - %% Bad JID in database: - error -> []; - R -> - SJID = jid:to_string(R#roster.jid), - Groups = case dict:find(SJID, GroupsDict) of - {ok, Gs} -> Gs; - error -> [] - end, - [R#roster{groups = Groups}] - end - end, Items); + {ok, lists:flatmap( + fun(I) -> + case raw_to_record(LServer, I) of + %% Bad JID in database: + error -> []; + R -> + SJID = jid:encode(R#roster.jid), + Groups = case dict:find(SJID, GroupsDict) of + {ok, Gs} -> Gs; + error -> [] + end, + [R#roster{groups = Groups}] + end + end, Items)}; _ -> - [] - end. - -get_roster_by_jid(LUser, LServer, LJID) -> - {selected, Res} = - sql_queries:get_roster_by_jid(LServer, LUser, jid:to_string(LJID)), - case Res of - [] -> - #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID}; - [I] -> - R = raw_to_record(LServer, I), - case R of - %% Bad JID in database: - error -> - #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID}; - _ -> - R#roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID, name = <<"">>} - end - end. - -get_only_items(LUser, LServer) -> - case catch sql_queries:get_roster(LServer, LUser) of - {selected, Is} when is_list(Is) -> - lists:map(fun(I) -> raw_to_record(LServer, I) end, Is); - _ -> [] + error end. roster_subscribe(_LUser, _LServer, _LJID, Item) -> ItemVals = record_to_row(Item), - sql_queries:roster_subscribe(ItemVals). + roster_subscribe(ItemVals). transaction(LServer, F) -> ejabberd_sql:sql_transaction(LServer, F). -get_roster_by_jid_with_groups(LUser, LServer, LJID) -> - SJID = jid:to_string(LJID), - case sql_queries:get_roster_by_jid(LServer, LUser, SJID) of +get_roster_item(LUser, LServer, LJID) -> + SJID = jid:encode(LJID), + case get_roster_by_jid(LServer, LUser, SJID) of {selected, [I]} -> - R = raw_to_record(LServer, I), - Groups = - case sql_queries:get_roster_groups(LServer, LUser, SJID) of - {selected, JGrps} when is_list(JGrps) -> - [JGrp || {JGrp} <- JGrps]; - _ -> [] - end, - R#roster{groups = Groups}; + case raw_to_record(LServer, I) of + error -> + error; + R -> + Groups = case get_roster_groups(LServer, LUser, SJID) of + {selected, JGrps} when is_list(JGrps) -> + [JGrp || {JGrp} <- JGrps]; + _ -> [] + end, + {ok, R#roster{groups = Groups}} + end; {selected, []} -> - #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID} + error end. remove_user(LUser, LServer) -> - sql_queries:del_user_roster_t(LServer, LUser), - {atomic, ok}. + transaction( + LServer, + fun () -> + ejabberd_sql:sql_query_t( + ?SQL("delete from rosterusers" + " where username=%(LUser)s and %(LServer)H")), + ejabberd_sql:sql_query_t( + ?SQL("delete from rostergroups" + " where username=%(LUser)s and %(LServer)H")) + end), + ok. update_roster(LUser, LServer, LJID, Item) -> - SJID = jid:to_string(LJID), + SJID = jid:encode(LJID), ItemVals = record_to_row(Item), ItemGroups = Item#roster.groups, - sql_queries:update_roster(LServer, LUser, SJID, ItemVals, - ItemGroups). + roster_subscribe(ItemVals), + ejabberd_sql:sql_query_t( + ?SQL("delete from rostergroups" + " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")), + lists:foreach( + fun(ItemGroup) -> + ejabberd_sql:sql_query_t( + ?SQL_INSERT( + "rostergroups", + ["username=%(LUser)s", + "server_host=%(LServer)s", + "jid=%(SJID)s", + "grp=%(ItemGroup)s"])) + end, + ItemGroups). del_roster(LUser, LServer, LJID) -> - SJID = jid:to_string(LJID), - sql_queries:del_roster(LServer, LUser, SJID). + SJID = jid:encode(LJID), + ejabberd_sql:sql_query_t( + ?SQL("delete from rosterusers" + " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")), + ejabberd_sql:sql_query_t( + ?SQL("delete from rostergroups" + " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). read_subscription_and_groups(LUser, LServer, LJID) -> - SJID = jid:to_string(LJID), - case catch sql_queries:get_subscription(LServer, LUser, SJID) of - {selected, [{SSubscription}]} -> - Subscription = case SSubscription of - <<"B">> -> both; - <<"T">> -> to; - <<"F">> -> from; - _ -> none - end, - Groups = case catch sql_queries:get_rostergroup_by_jid( - LServer, LUser, SJID) of + SJID = jid:encode(LJID), + case get_subscription(LServer, LUser, SJID) of + {selected, [{SSubscription, SAsk}]} -> + Subscription = decode_subscription(LUser, LServer, SSubscription), + Ask = decode_ask(LUser, LServer, SAsk), + Groups = case get_rostergroup_by_jid(LServer, LUser, SJID) of {selected, JGrps} when is_list(JGrps) -> [JGrp || {JGrp} <- JGrps]; _ -> [] end, - {Subscription, Groups}; + {ok, {Subscription, Ask, Groups}}; _ -> error end. @@ -172,90 +191,207 @@ export(_Server) -> when LServer == Host -> ItemVals = record_to_row(R), ItemGroups = R#roster.groups, - sql_queries:update_roster_sql(ItemVals, ItemGroups); + update_roster_sql(ItemVals, ItemGroups); (_Host, _R) -> [] end}, {roster_version, fun(Host, #roster_version{us = {LUser, LServer}, version = Ver}) when LServer == Host -> - [?SQL("delete from roster_version where username=%(LUser)s;"), - ?SQL("insert into roster_version(username, version) values(" - " %(LUser)s, %(Ver)s);")]; + [?SQL("delete from roster_version" + " where username=%(LUser)s and %(LServer)H;"), + ?SQL_INSERT( + "roster_version", + ["username=%(LUser)s", + "server_host=%(LServer)s", + "version=%(Ver)s"])]; (_Host, _R) -> [] end}]. -import(LServer) -> - [{<<"select username, jid, nick, subscription, " - "ask, askmessage, server, subscribe, type from rosterusers;">>, - fun([LUser, JID|_] = Row) -> - Item = raw_to_record(LServer, Row), - Username = ejabberd_sql:escape(LUser), - SJID = ejabberd_sql:escape(JID), - {selected, _, Rows} = - ejabberd_sql:sql_query_t( - [<<"select grp from rostergroups where username='">>, - Username, <<"' and jid='">>, SJID, <<"'">>]), - Groups = [Grp || [Grp] <- Rows], - Item#roster{groups = Groups} - end}, - {<<"select username, version from roster_version;">>, - fun([LUser, Ver]) -> - #roster_version{us = {LUser, LServer}, version = Ver} - end}]. - -import(_, _) -> - pass. +import(_, _, _) -> + ok. %%%=================================================================== %%% Internal functions %%%=================================================================== +set_roster_version(LUser, LServer, Version) -> + ?SQL_UPSERT_T( + "roster_version", + ["!username=%(LUser)s", + "!server_host=%(LServer)s", + "version=%(Version)s"]). + +get_roster_jid_groups(LServer, LUser) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("select @(jid)s, @(grp)s from rostergroups where " + "username=%(LUser)s and %(LServer)H")). + +get_roster_groups(LServer, LUser, SJID) -> + ejabberd_sql:sql_query_t( + ?SQL("select @(grp)s from rostergroups" + " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). + +roster_subscribe({LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage}) -> + ?SQL_UPSERT_T( + "rosterusers", + ["!username=%(LUser)s", + "!server_host=%(LServer)s", + "!jid=%(SJID)s", + "nick=%(Name)s", + "subscription=%(SSubscription)s", + "ask=%(SAsk)s", + "askmessage=%(AskMessage)s", + "server='N'", + "subscribe=''", + "type='item'"]). + +get_roster_by_jid(LServer, LUser, SJID) -> + ejabberd_sql:sql_query_t( + ?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s," + " @(ask)s, @(askmessage)s, @(server)s, @(subscribe)s," + " @(type)s from rosterusers" + " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). + +get_rostergroup_by_jid(LServer, LUser, SJID) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("select @(grp)s from rostergroups" + " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). + +get_subscription(LServer, LUser, SJID) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("select @(subscription)s, @(ask)s from rosterusers " + "where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")). + +update_roster_sql({LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage}, + ItemGroups) -> + [?SQL("delete from rosterusers where" + " username=%(LUser)s and %(LServer)H and jid=%(SJID)s;"), + ?SQL_INSERT( + "rosterusers", + ["username=%(LUser)s", + "server_host=%(LServer)s", + "jid=%(SJID)s", + "nick=%(Name)s", + "subscription=%(SSubscription)s", + "ask=%(SAsk)s", + "askmessage=%(AskMessage)s", + "server='N'", + "subscribe=''", + "type='item'"]), + ?SQL("delete from rostergroups where" + " username=%(LUser)s and %(LServer)H and jid=%(SJID)s;")] + ++ + [?SQL_INSERT( + "rostergroups", + ["username=%(LUser)s", + "server_host=%(LServer)s", + "jid=%(SJID)s", + "grp=%(ItemGroup)s"]) + || ItemGroup <- ItemGroups]. + raw_to_record(LServer, - [User, SJID, Nick, SSubscription, SAsk, SAskMessage, + [User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, _SServer, _SSubscribe, _SType]) -> raw_to_record(LServer, - {User, SJID, Nick, SSubscription, SAsk, SAskMessage, + {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, _SServer, _SSubscribe, _SType}); raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk, SAskMessage, _SServer, _SSubscribe, _SType}) -> - case jid:from_string(SJID) of - error -> error; + raw_to_record(LServer, + {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, + _SServer, _SSubscribe, _SType}); +raw_to_record(LServer, + {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, + _SServer, _SSubscribe, _SType}) -> + try jid:decode(SJID) of JID -> LJID = jid:tolower(JID), - Subscription = case SSubscription of - <<"B">> -> both; - <<"T">> -> to; - <<"F">> -> from; - _ -> none - end, - Ask = case SAsk of - <<"S">> -> subscribe; - <<"U">> -> unsubscribe; - <<"B">> -> both; - <<"O">> -> out; - <<"I">> -> in; - _ -> none - end, + Subscription = decode_subscription(User, LServer, SSubscription), + Ask = decode_ask(User, LServer, SAsk), #roster{usj = {User, LServer, LJID}, us = {User, LServer}, jid = LJID, name = Nick, subscription = Subscription, ask = Ask, askmessage = SAskMessage} + catch _:{bad_jid, _} -> + ?ERROR_MSG("~ts", [format_row_error(User, LServer, {jid, SJID})]), + error end. record_to_row( - #roster{us = {LUser, _LServer}, + #roster{us = {LUser, LServer}, jid = JID, name = Name, subscription = Subscription, ask = Ask, askmessage = AskMessage}) -> - SJID = jid:to_string(jid:tolower(JID)), + SJID = jid:encode(jid:tolower(JID)), + SSubscription = case Subscription of + both -> <<"B">>; + to -> <<"T">>; + from -> <<"F">>; + none -> <<"N">> + end, + SAsk = case Ask of + subscribe -> <<"S">>; + unsubscribe -> <<"U">>; + both -> <<"B">>; + out -> <<"O">>; + in -> <<"I">>; + none -> <<"N">> + end, + {LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage}. + +decode_subscription(User, Server, S) -> + case S of + <<"B">> -> both; + <<"T">> -> to; + <<"F">> -> from; + <<"N">> -> none; + <<"">> -> none; + _ -> + ?ERROR_MSG("~ts", [format_row_error(User, Server, {subscription, S})]), + none + end. + +decode_ask(User, Server, A) -> + case A of + <<"S">> -> subscribe; + <<"U">> -> unsubscribe; + <<"B">> -> both; + <<"O">> -> out; + <<"I">> -> in; + <<"N">> -> none; + <<"">> -> none; + _ -> + ?ERROR_MSG("~ts", [format_row_error(User, Server, {ask, A})]), + none + end. + +format_row_error(User, Server, Why) -> + [case Why of + {jid, JID} -> ["Malformed 'jid' field with value '", JID, "'"]; + {subscription, Sub} -> ["Malformed 'subscription' field with value '", Sub, "'"]; + {ask, Ask} -> ["Malformed 'ask' field with value '", Ask, "'"] + end, + " detected for ", User, "@", Server, " in table 'rosterusers'"]. + +process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) -> + process_rosteritems_sql(ActionS, list_to_atom(SubsS), list_to_atom(AsksS), + list_to_binary(UsersS), list_to_binary(ContactsS)). + +process_rosteritems_sql(ActionS, Subscription, Ask, SLocalJID, SJID) -> + [LUser, LServer] = binary:split(SLocalJID, <<"@">>), SSubscription = case Subscription of + any -> <<"_">>; both -> <<"B">>; to -> <<"T">>; from -> <<"F">>; none -> <<"N">> end, SAsk = case Ask of + any -> <<"_">>; subscribe -> <<"S">>; unsubscribe -> <<"U">>; both -> <<"B">>; @@ -263,4 +399,16 @@ record_to_row( in -> <<"I">>; none -> <<"N">> end, - {LUser, SJID, Name, SSubscription, SAsk, AskMessage}. + {selected, List} = ejabberd_sql:sql_query( + LServer, + ?SQL("select @(username)s, @(jid)s from rosterusers " + "where username LIKE %(LUser)s" + " and %(LServer)H" + " and jid LIKE %(SJID)s" + " and subscription LIKE %(SSubscription)s" + " and ask LIKE %(SAsk)s")), + case ActionS of + "delete" -> [mod_roster:del_roster(User, LServer, jid:tolower(jid:decode(Contact))) || {User, Contact} <- List]; + "list" -> ok + end, + List. |