aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-04-14 10:58:32 +0300
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-04-14 10:58:32 +0300
commit398f1de5aecc574d65f8f3308fe3cba037ad8a99 (patch)
tree5d31403456b113c67062e777e48db85b31bf80dc
parentClean mod_muc.erl from DB specific code (diff)
Clean mod_roster.erl from DB specific code
-rw-r--r--src/mod_roster.erl653
-rw-r--r--src/mod_roster_mnesia.erl171
-rw-r--r--src/mod_roster_riak.erl113
-rw-r--r--src/mod_roster_sql.erl308
4 files changed, 658 insertions, 587 deletions
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index 35072e5f8..16354dd8f 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -49,7 +49,6 @@
get_jid_info/4, item_to_xml/1, webadmin_page/3,
webadmin_user/4, get_versioning_feature/2,
roster_versioning_enabled/1, roster_version/2,
- record_to_string/1, groups_to_string/1,
mod_opt_type/1, set_roster/1]).
-include("ejabberd.hrl").
@@ -65,23 +64,27 @@
-export_type([subscription/0]).
+-callback init(binary(), gen_mod:opts()) -> any().
+-callback import(binary(), #roster{} | #roster_version{}) -> ok | pass.
+-callback read_roster_version(binary(), binary()) -> binary() | error.
+-callback write_roster_version(binary(), binary(), boolean(), binary()) -> any().
+-callback get_roster(binary(), binary()) -> [#roster{}].
+-callback get_roster_by_jid(binary(), binary(), ljid()) -> #roster{}.
+-callback get_only_items(binary(), binary()) -> [#roster{}].
+-callback roster_subscribe(binary(), binary(), ljid(), #roster{}) -> any().
+-callback transaction(binary(), function()) -> {atomic, any()} | {aborted, any()}.
+-callback get_roster_by_jid_with_groups(binary(), binary(), ljid()) -> #roster{}.
+-callback remove_user(binary(), binary()) -> {atomic, any()}.
+-callback update_roster(binary(), binary(), ljid(), #roster{}) -> any().
+-callback del_roster(binary(), binary(), ljid()) -> any().
+-callback read_subscription_and_groups(binary(), binary(), ljid()) ->
+ {subscription(), [binary()]}.
+
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
one_queue),
- case gen_mod:db_type(Host, Opts) of
- mnesia ->
- mnesia:create_table(roster,
- [{disc_copies, [node()]},
- {attributes, record_info(fields, roster)}]),
- mnesia:create_table(roster_version,
- [{disc_copies, [node()]},
- {attributes,
- record_info(fields, roster_version)}]),
- update_tables(),
- mnesia:add_table_index(roster, us),
- mnesia:add_table_index(roster_version, us);
- _ -> ok
- end,
+ Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod:init(Host, Opts),
ejabberd_hooks:add(roster_get, Host, ?MODULE,
get_user_roster, 50),
ejabberd_hooks:add(roster_in_subscription, Host,
@@ -194,26 +197,8 @@ roster_version(LServer, LUser) ->
end.
read_roster_version(LUser, LServer) ->
- read_roster_version(LUser, LServer,
- gen_mod:db_type(LServer, ?MODULE)).
-
-read_roster_version(LUser, LServer, mnesia) ->
- US = {LUser, LServer},
- case mnesia:dirty_read(roster_version, US) of
- [#roster_version{version = V}] -> V;
- [] -> error
- end;
-read_roster_version(LUser, LServer, odbc) ->
- case odbc_queries:get_roster_version(LServer, LUser) of
- {selected, [{Version}]} -> Version;
- {selected, []} -> error
- end;
-read_roster_version(LServer, LUser, riak) ->
- case ejabberd_riak:get(roster_version, roster_version_schema(),
- {LUser, LServer}) of
- {ok, #roster_version{version = V}} -> V;
- _Err -> error
- end.
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:read_roster_version(LUser, LServer).
write_roster_version(LUser, LServer) ->
write_roster_version(LUser, LServer, false).
@@ -223,38 +208,10 @@ write_roster_version_t(LUser, LServer) ->
write_roster_version(LUser, LServer, InTransaction) ->
Ver = p1_sha:sha(term_to_binary(p1_time_compat:unique_integer())),
- write_roster_version(LUser, LServer, InTransaction, Ver,
- gen_mod:db_type(LServer, ?MODULE)),
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:write_roster_version(LUser, LServer, InTransaction, Ver),
Ver.
-write_roster_version(LUser, LServer, InTransaction, Ver,
- mnesia) ->
- US = {LUser, LServer},
- if InTransaction ->
- mnesia:write(#roster_version{us = US, version = Ver});
- true ->
- mnesia:dirty_write(#roster_version{us = US,
- version = Ver})
- end;
-write_roster_version(LUser, LServer, InTransaction, Ver,
- odbc) ->
- Username = ejabberd_odbc:escape(LUser),
- EVer = ejabberd_odbc:escape(Ver),
- if InTransaction ->
- odbc_queries:set_roster_version(Username, EVer);
- true ->
- odbc_queries:sql_transaction(LServer,
- fun () ->
- odbc_queries:set_roster_version(Username,
- EVer)
- end)
- end;
-write_roster_version(LUser, LServer, _InTransaction, Ver,
- riak) ->
- US = {LUser, LServer},
- ejabberd_riak:put(#roster_version{us = US, version = Ver},
- roster_version_schema()).
-
%% Load roster from DB only if neccesary.
%% It is neccesary if
%% - roster versioning is disabled in server OR
@@ -350,56 +307,8 @@ get_user_roster(Acc, {LUser, LServer}) ->
++ Acc.
get_roster(LUser, LServer) ->
- get_roster(LUser, LServer,
- gen_mod:db_type(LServer, ?MODULE)).
-
-get_roster(LUser, LServer, mnesia) ->
- US = {LUser, LServer},
- case catch mnesia:dirty_index_read(roster, US,
- #roster.us)
- of
- Items when is_list(Items)-> Items;
- _ -> []
- end;
-get_roster(LUser, LServer, riak) ->
- case ejabberd_riak:get_by_index(roster, roster_schema(),
- <<"us">>, {LUser, LServer}) of
- {ok, Items} -> Items;
- _Err -> []
- end;
-get_roster(LUser, LServer, odbc) ->
- case catch odbc_queries:get_roster(LServer, LUser) of
- {selected, Items} when is_list(Items) ->
- JIDGroups = case catch odbc_queries:get_roster_jid_groups(
- LServer, LUser) of
- {selected, JGrps}
- when is_list(JGrps) ->
- JGrps;
- _ -> []
- end,
- GroupsDict = lists:foldl(fun({J, G}, Acc) ->
- dict:append(J, G, Acc)
- end,
- dict:new(), JIDGroups),
- RItems =
- 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),
- RItems;
- _ -> []
- end.
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:get_roster(LUser, LServer).
set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) ->
transaction(
@@ -437,48 +346,8 @@ item_to_xml(Item) ->
children = SubEls}.
get_roster_by_jid_t(LUser, LServer, LJID) ->
- DBType = gen_mod:db_type(LServer, ?MODULE),
- get_roster_by_jid_t(LUser, LServer, LJID, DBType).
-
-get_roster_by_jid_t(LUser, LServer, LJID, mnesia) ->
- case mnesia:read({roster, {LUser, LServer, LJID}}) of
- [] ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer}, jid = LJID};
- [I] ->
- I#roster{jid = LJID, name = <<"">>, groups = [],
- xs = []}
- end;
-get_roster_by_jid_t(LUser, LServer, LJID, odbc) ->
- {selected, Res} =
- odbc_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_roster_by_jid_t(LUser, LServer, LJID, riak) ->
- case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
- {ok, I} ->
- I#roster{jid = LJID, name = <<"">>, groups = [],
- xs = []};
- {error, notfound} ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer}, jid = LJID};
- Err ->
- exit(Err)
- end.
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:get_roster_by_jid(LUser, LServer, LJID).
try_process_iq_set(From, To, #iq{sub_el = SubEl, lang = Lang} = IQ) ->
#jid{server = Server} = From,
@@ -632,77 +501,37 @@ push_item_version(Server, User, From, Item,
end,
ejabberd_sm:get_user_resources(User, Server)).
-get_subscription_lists(Acc, User, Server) ->
+get_subscription_lists(_Acc, User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
- DBType = gen_mod:db_type(LServer, ?MODULE),
- Items = get_subscription_lists(Acc, LUser, LServer,
- DBType),
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Items = Mod:get_only_items(LUser, LServer),
fill_subscription_lists(LServer, Items, [], []).
-get_subscription_lists(_, LUser, LServer, mnesia) ->
- US = {LUser, LServer},
- case mnesia:dirty_index_read(roster, US, #roster.us) of
- Items when is_list(Items) -> Items;
- _ -> []
- end;
-get_subscription_lists(_, LUser, LServer, odbc) ->
- case catch odbc_queries:get_roster(LServer, LUser) of
- {selected, Items} when is_list(Items) ->
- lists:map(fun(I) -> raw_to_record(LServer, I) end, Items);
- _ -> []
- end;
-get_subscription_lists(_, LUser, LServer, riak) ->
- case ejabberd_riak:get_by_index(roster, roster_schema(),
- <<"us">>, {LUser, LServer}) of
- {ok, Items} -> Items;
- _Err -> []
- end.
-
-fill_subscription_lists(LServer, [#roster{} = I | Is],
- F, T) ->
+fill_subscription_lists(LServer, [I | Is], F, T) ->
J = element(3, I#roster.usj),
case I#roster.subscription of
- both ->
- fill_subscription_lists(LServer, Is, [J | F], [J | T]);
- from ->
- fill_subscription_lists(LServer, Is, [J | F], T);
- to -> fill_subscription_lists(LServer, Is, F, [J | T]);
- _ -> fill_subscription_lists(LServer, Is, F, T)
- end;
-fill_subscription_lists(LServer, [RawI | Is], F, T) ->
- I = raw_to_record(LServer, RawI),
- case I of
- %% Bad JID in database:
- error -> fill_subscription_lists(LServer, Is, F, T);
- _ -> fill_subscription_lists(LServer, [I | Is], F, T)
+ both ->
+ fill_subscription_lists(LServer, Is, [J | F], [J | T]);
+ from ->
+ fill_subscription_lists(LServer, Is, [J | F], T);
+ to -> fill_subscription_lists(LServer, Is, F, [J | T]);
+ _ -> fill_subscription_lists(LServer, Is, F, T)
end;
-fill_subscription_lists(_LServer, [], F, T) -> {F, T}.
+fill_subscription_lists(_LServer, [], F, T) ->
+ {F, T}.
ask_to_pending(subscribe) -> out;
ask_to_pending(unsubscribe) -> none;
ask_to_pending(Ask) -> Ask.
roster_subscribe_t(LUser, LServer, LJID, Item) ->
- DBType = gen_mod:db_type(LServer, ?MODULE),
- roster_subscribe_t(LUser, LServer, LJID, Item, DBType).
-
-roster_subscribe_t(_LUser, _LServer, _LJID, Item,
- mnesia) ->
- mnesia:write(Item);
-roster_subscribe_t(_LUser, _LServer, _LJID, Item, odbc) ->
- ItemVals = record_to_row(Item),
- odbc_queries:roster_subscribe(ItemVals);
-roster_subscribe_t(LUser, LServer, _LJID, Item, riak) ->
- ejabberd_riak:put(Item, roster_schema(),
- [{'2i', [{<<"us">>, {LUser, LServer}}]}]).
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:roster_subscribe(LUser, LServer, LJID, Item).
transaction(LServer, F) ->
- case gen_mod:db_type(LServer, ?MODULE) of
- mnesia -> mnesia:transaction(F);
- odbc -> ejabberd_odbc:sql_transaction(LServer, F);
- riak -> {atomic, F()}
- end.
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:transaction(LServer, F).
in_subscription(_, User, Server, JID, Type, Reason) ->
process_subscription(in, User, Server, JID, Type,
@@ -712,45 +541,8 @@ out_subscription(User, Server, JID, Type) ->
process_subscription(out, User, Server, JID, Type, <<"">>).
get_roster_by_jid_with_groups_t(LUser, LServer, LJID) ->
- DBType = gen_mod:db_type(LServer, ?MODULE),
- get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
- DBType).
-
-get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
- mnesia) ->
- case mnesia:read({roster, {LUser, LServer, LJID}}) of
- [] ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer}, jid = LJID};
- [I] -> I
- end;
-get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
- odbc) ->
- SJID = jid:to_string(LJID),
- case odbc_queries:get_roster_by_jid(LServer, LUser, SJID) of
- {selected, [I]} ->
- R = raw_to_record(LServer, I),
- Groups =
- case odbc_queries:get_roster_groups(LServer, LUser, SJID) of
- {selected, JGrps} when is_list(JGrps) ->
- [JGrp || {JGrp} <- JGrps];
- _ -> []
- end,
- R#roster{groups = Groups};
- {selected, []} ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer}, jid = LJID}
- end;
-get_roster_by_jid_with_groups_t(LUser, LServer, LJID, riak) ->
- case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
- {ok, I} ->
- I;
- {error, notfound} ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer}, jid = LJID};
- Err ->
- exit(Err)
- end.
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:get_roster_by_jid_with_groups(LUser, LServer, LJID).
process_subscription(Direction, User, Server, JID1,
Type, Reason) ->
@@ -948,21 +740,8 @@ remove_user(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
send_unsubscription_to_rosteritems(LUser, LServer),
- remove_user(LUser, LServer,
- gen_mod:db_type(LServer, ?MODULE)).
-
-remove_user(LUser, LServer, mnesia) ->
- US = {LUser, LServer},
- F = fun () ->
- lists:foreach(fun (R) -> mnesia:delete_object(R) end,
- mnesia:index_read(roster, US, #roster.us))
- end,
- mnesia:transaction(F);
-remove_user(LUser, LServer, odbc) ->
- odbc_queries:del_user_roster_t(LServer, LUser),
- ok;
-remove_user(LUser, LServer, riak) ->
- {atomic, ejabberd_riak:delete_by_index(roster, <<"us">>, {LUser, LServer})}.
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:remove_user(LUser, LServer).
%% For each contact with Subscription:
%% Both or From, send a "unsubscribed" presence stanza;
@@ -1020,33 +799,12 @@ set_items(User, Server, SubEl) ->
transaction(LServer, F).
update_roster_t(LUser, LServer, LJID, Item) ->
- DBType = gen_mod:db_type(LServer, ?MODULE),
- update_roster_t(LUser, LServer, LJID, Item, DBType).
-
-update_roster_t(_LUser, _LServer, _LJID, Item,
- mnesia) ->
- mnesia:write(Item);
-update_roster_t(LUser, LServer, LJID, Item, odbc) ->
- SJID = jid:to_string(LJID),
- ItemVals = record_to_row(Item),
- ItemGroups = Item#roster.groups,
- odbc_queries:update_roster(LServer, LUser, SJID, ItemVals,
- ItemGroups);
-update_roster_t(LUser, LServer, _LJID, Item, riak) ->
- ejabberd_riak:put(Item, roster_schema(),
- [{'2i', [{<<"us">>, {LUser, LServer}}]}]).
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:update_roster(LUser, LServer, LJID, Item).
del_roster_t(LUser, LServer, LJID) ->
- DBType = gen_mod:db_type(LServer, ?MODULE),
- del_roster_t(LUser, LServer, LJID, DBType).
-
-del_roster_t(LUser, LServer, LJID, mnesia) ->
- mnesia:delete({roster, {LUser, LServer, LJID}});
-del_roster_t(LUser, LServer, LJID, odbc) ->
- SJID = jid:to_string(LJID),
- odbc_queries:del_roster(LServer, LUser, SJID);
-del_roster_t(LUser, LServer, LJID, riak) ->
- ejabberd_riak:delete(roster, {LUser, LServer, LJID}).
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:del_roster(LUser, LServer, LJID).
process_item_set_t(LUser, LServer,
#xmlel{attrs = Attrs, children = Els}) ->
@@ -1109,13 +867,12 @@ process_item_attrs_ws(Item, []) -> Item.
get_in_pending_subscriptions(Ls, User, Server) ->
LServer = jid:nameprep(Server),
- get_in_pending_subscriptions(Ls, User, Server,
- gen_mod:db_type(LServer, ?MODULE)).
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ get_in_pending_subscriptions(Ls, User, Server, Mod).
-get_in_pending_subscriptions(Ls, User, Server, DBType)
- when DBType == mnesia; DBType == riak ->
+get_in_pending_subscriptions(Ls, User, Server, Mod) ->
JID = jid:make(User, Server, <<"">>),
- Result = get_roster(JID#jid.luser, JID#jid.lserver, DBType),
+ Result = Mod:get_only_items(JID#jid.luser, JID#jid.lserver),
Ls ++ lists:map(fun (R) ->
Message = R#roster.askmessage,
Status = if is_binary(Message) -> (Message);
@@ -1140,93 +897,15 @@ get_in_pending_subscriptions(Ls, User, Server, DBType)
_ -> false
end
end,
- Result));
-get_in_pending_subscriptions(Ls, User, Server, odbc) ->
- JID = jid:make(User, Server, <<"">>),
- LUser = JID#jid.luser,
- LServer = JID#jid.lserver,
- case catch odbc_queries:get_roster(LServer, LUser) of
- {selected, Items} when is_list(Items) ->
- Ls ++
- lists:map(fun (R) ->
- Message = R#roster.askmessage,
- #xmlel{name = <<"presence">>,
- attrs =
- [{<<"from">>,
- jid:to_string(R#roster.jid)},
- {<<"to">>, jid:to_string(JID)},
- {<<"type">>, <<"subscribe">>}],
- children =
- [#xmlel{name = <<"status">>,
- attrs = [],
- children =
- [{xmlcdata, Message}]}]}
- end,
- lists:flatmap(fun (I) ->
- case raw_to_record(LServer, I) of
- %% Bad JID in database:
- error -> [];
- R ->
- case R#roster.ask of
- in -> [R];
- both -> [R];
- _ -> []
- end
- end
- end,
- Items));
- _ -> Ls
- end.
+ Result)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
read_subscription_and_groups(User, Server, LJID) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
- read_subscription_and_groups(LUser, LServer, LJID,
- gen_mod:db_type(LServer, ?MODULE)).
-
-read_subscription_and_groups(LUser, LServer, LJID,
- mnesia) ->
- case catch mnesia:dirty_read(roster,
- {LUser, LServer, LJID})
- of
- [#roster{subscription = Subscription,
- groups = Groups}] ->
- {Subscription, Groups};
- _ -> error
- end;
-read_subscription_and_groups(LUser, LServer, LJID,
- odbc) ->
- SJID = jid:to_string(LJID),
- case catch odbc_queries:get_subscription(LServer, LUser, SJID) of
- {selected, [{SSubscription}]} ->
- Subscription = case SSubscription of
- <<"B">> -> both;
- <<"T">> -> to;
- <<"F">> -> from;
- _ -> none
- end,
- Groups = case catch
- odbc_queries:get_rostergroup_by_jid(LServer, LUser,
- SJID)
- of
- {selected, JGrps} when is_list(JGrps) ->
- [JGrp || {JGrp} <- JGrps];
- _ -> []
- end,
- {Subscription, Groups};
- _ -> error
- end;
-read_subscription_and_groups(LUser, LServer, LJID,
- riak) ->
- case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
- {ok, #roster{subscription = Subscription,
- groups = Groups}} ->
- {Subscription, Groups};
- _ ->
- error
- end.
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:read_subscription_and_groups(LUser, LServer, LJID).
get_jid_info(_, User, Server, JID) ->
LJID = jid:tolower(JID),
@@ -1246,155 +925,6 @@ get_jid_info(_, User, Server, JID) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-raw_to_record(LServer,
- [User, SJID, Nick, SSubscription, SAsk, SAskMessage,
- _SServer, _SSubscribe, _SType]) ->
- raw_to_record(LServer,
- {User, 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;
- 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,
- #roster{usj = {User, LServer, LJID},
- us = {User, LServer}, jid = LJID, name = Nick,
- subscription = Subscription, ask = Ask,
- askmessage = SAskMessage}
- end.
-
-record_to_string(#roster{us = {User, _Server},
- jid = JID, name = Name, subscription = Subscription,
- ask = Ask, askmessage = AskMessage}) ->
- Username = ejabberd_odbc:escape(User),
- SJID =
- ejabberd_odbc:escape(jid:to_string(jid:tolower(JID))),
- Nick = ejabberd_odbc:escape(Name),
- 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,
- SAskMessage = ejabberd_odbc:escape(AskMessage),
- [Username, SJID, Nick, SSubscription, SAsk, SAskMessage,
- <<"N">>, <<"">>, <<"item">>].
-
-record_to_row(
- #roster{us = {LUser, _LServer},
- jid = JID, name = Name, subscription = Subscription,
- ask = Ask, askmessage = AskMessage}) ->
- SJID = jid:to_string(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, SJID, Name, SSubscription, SAsk, AskMessage}.
-
-groups_to_string(#roster{us = {User, _Server},
- jid = JID, groups = Groups}) ->
- Username = ejabberd_odbc:escape(User),
- SJID =
- ejabberd_odbc:escape(jid:to_string(jid:tolower(JID))),
- lists:foldl(fun (<<"">>, Acc) -> Acc;
- (Group, Acc) ->
- G = ejabberd_odbc:escape(Group),
- [[Username, SJID, G] | Acc]
- end,
- [], Groups).
-
-update_tables() ->
- update_roster_table(),
- update_roster_version_table().
-
-update_roster_table() ->
- Fields = record_info(fields, roster),
- case mnesia:table_info(roster, attributes) of
- Fields ->
- ejabberd_config:convert_table_to_binary(
- roster, Fields, set,
- fun(#roster{usj = {U, _, _}}) -> U end,
- fun(#roster{usj = {U, S, {LU, LS, LR}},
- us = {U1, S1},
- jid = {U2, S2, R2},
- name = Name,
- groups = Gs,
- askmessage = Ask,
- xs = Xs} = R) ->
- R#roster{usj = {iolist_to_binary(U),
- iolist_to_binary(S),
- {iolist_to_binary(LU),
- iolist_to_binary(LS),
- iolist_to_binary(LR)}},
- us = {iolist_to_binary(U1),
- iolist_to_binary(S1)},
- jid = {iolist_to_binary(U2),
- iolist_to_binary(S2),
- iolist_to_binary(R2)},
- name = iolist_to_binary(Name),
- groups = [iolist_to_binary(G) || G <- Gs],
- askmessage = try iolist_to_binary(Ask)
- catch _:_ -> <<"">> end,
- xs = [fxml:to_xmlel(X) || X <- Xs]}
- end);
- _ ->
- ?INFO_MSG("Recreating roster table", []),
- mnesia:transform_table(roster, ignore, Fields)
- end.
-
-%% Convert roster table to support virtual host
-%% Convert roster table: xattrs fields become
-update_roster_version_table() ->
- Fields = record_info(fields, roster_version),
- case mnesia:table_info(roster_version, attributes) of
- Fields ->
- ejabberd_config:convert_table_to_binary(
- roster_version, Fields, set,
- fun(#roster_version{us = {U, _}}) -> U end,
- fun(#roster_version{us = {U, S}, version = Ver} = R) ->
- R#roster_version{us = {iolist_to_binary(U),
- iolist_to_binary(S)},
- version = iolist_to_binary(Ver)}
- end);
- _ ->
- ?INFO_MSG("Recreating roster_version table", []),
- mnesia:transform_table(roster_version, ignore, Fields)
- end.
-
webadmin_page(_, Host,
#request{us = _US, path = [<<"user">>, U, <<"roster">>],
q = Query, lang = Lang} =
@@ -1692,68 +1222,17 @@ is_managed_from_id(<<"roster-remotely-managed">>) ->
is_managed_from_id(_Id) ->
false.
-roster_schema() ->
- {record_info(fields, roster), #roster{}}.
-
-roster_version_schema() ->
- {record_info(fields, roster_version), #roster_version{}}.
-
-export(_Server) ->
- [{roster,
- fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)
- when LServer == Host ->
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
- ItemVals = record_to_string(R),
- ItemGroups = groups_to_string(R),
- odbc_queries:update_roster_sql(Username, SJID,
- ItemVals, ItemGroups);
- (_Host, _R) ->
- []
- end},
- {roster_version,
- fun(Host, #roster_version{us = {LUser, LServer}, version = Ver})
- when LServer == Host ->
- Username = ejabberd_odbc:escape(LUser),
- SVer = ejabberd_odbc:escape(Ver),
- [[<<"delete from roster_version where username='">>,
- Username, <<"';">>],
- [<<"insert into roster_version(username, version) values('">>,
- Username, <<"', '">>, SVer, <<"');">>]];
- (_Host, _R) ->
- []
- end}].
+export(LServer) ->
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:export(LServer).
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_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(JID),
- {selected, _, Rows} =
- ejabberd_odbc: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(_LServer, mnesia, #roster{} = R) ->
- mnesia:dirty_write(R);
-import(_LServer, mnesia, #roster_version{} = RV) ->
- mnesia:dirty_write(RV);
-import(_LServer, riak, #roster{us = {LUser, LServer}} = R) ->
- ejabberd_riak:put(R, roster_schema(),
- [{'2i', [{<<"us">>, {LUser, LServer}}]}]);
-import(_LServer, riak, #roster_version{} = RV) ->
- ejabberd_riak:put(RV, roster_version_schema());
-import(_, _, _) ->
- pass.
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:import(LServer).
+
+import(LServer, DBType, R) ->
+ Mod = gen_mod:db_mod(DBType, ?MODULE),
+ Mod:import(LServer, R).
mod_opt_type(access) ->
fun (A) when is_atom(A) -> A end;
diff --git a/src/mod_roster_mnesia.erl b/src/mod_roster_mnesia.erl
new file mode 100644
index 000000000..ddfa34d68
--- /dev/null
+++ b/src/mod_roster_mnesia.erl
@@ -0,0 +1,171 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-module(mod_roster_mnesia).
+
+-behaviour(mod_roster).
+
+%% API
+-export([init/2, read_roster_version/2, write_roster_version/4,
+ get_roster/2, get_roster_by_jid/3, get_only_items/2,
+ 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, import/2]).
+
+-include("jlib.hrl").
+-include("mod_roster.hrl").
+-include("logger.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+init(_Host, _Opts) ->
+ mnesia:create_table(roster,
+ [{disc_copies, [node()]},
+ {attributes, record_info(fields, roster)}]),
+ mnesia:create_table(roster_version,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, roster_version)}]),
+ update_tables(),
+ mnesia:add_table_index(roster, us),
+ mnesia:add_table_index(roster_version, us).
+
+read_roster_version(LUser, LServer) ->
+ US = {LUser, LServer},
+ case mnesia:dirty_read(roster_version, US) of
+ [#roster_version{version = V}] -> V;
+ [] -> error
+ end.
+
+write_roster_version(LUser, LServer, InTransaction, Ver) ->
+ US = {LUser, LServer},
+ if InTransaction ->
+ mnesia:write(#roster_version{us = US, version = Ver});
+ true ->
+ mnesia:dirty_write(#roster_version{us = US, version = Ver})
+ end.
+
+get_roster(LUser, LServer) ->
+ mnesia:dirty_index_read(roster, {LUser, LServer}, #roster.us).
+
+get_roster_by_jid(LUser, LServer, LJID) ->
+ case mnesia:read({roster, {LUser, LServer, LJID}}) of
+ [] ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer}, jid = LJID};
+ [I] ->
+ I#roster{jid = LJID, name = <<"">>, groups = [],
+ xs = []}
+ end.
+
+get_only_items(LUser, LServer) ->
+ get_roster(LUser, LServer).
+
+roster_subscribe(_LUser, _LServer, _LJID, Item) ->
+ mnesia:write(Item).
+
+get_roster_by_jid_with_groups(LUser, LServer, LJID) ->
+ case mnesia:read({roster, {LUser, LServer, LJID}}) of
+ [] ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer}, jid = LJID};
+ [I] -> I
+ end.
+
+remove_user(LUser, LServer) ->
+ US = {LUser, LServer},
+ F = fun () ->
+ lists:foreach(
+ fun (R) -> mnesia:delete_object(R) end,
+ mnesia:index_read(roster, US, #roster.us))
+ end,
+ mnesia:transaction(F).
+
+update_roster(_LUser, _LServer, _LJID, Item) ->
+ mnesia:write(Item).
+
+del_roster(LUser, LServer, LJID) ->
+ mnesia:delete({roster, {LUser, LServer, LJID}}).
+
+read_subscription_and_groups(LUser, LServer, LJID) ->
+ case mnesia:dirty_read(roster, {LUser, LServer, LJID}) of
+ [#roster{subscription = Subscription, groups = Groups}] ->
+ {Subscription, Groups};
+ _ ->
+ error
+ end.
+
+transaction(_LServer, F) ->
+ mnesia:transaction(F).
+
+import(_LServer, #roster{} = R) ->
+ mnesia:dirty_write(R);
+import(_LServer, #roster_version{} = RV) ->
+ mnesia:dirty_write(RV).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+update_tables() ->
+ update_roster_table(),
+ update_roster_version_table().
+
+update_roster_table() ->
+ Fields = record_info(fields, roster),
+ case mnesia:table_info(roster, attributes) of
+ Fields ->
+ ejabberd_config:convert_table_to_binary(
+ roster, Fields, set,
+ fun(#roster{usj = {U, _, _}}) -> U end,
+ fun(#roster{usj = {U, S, {LU, LS, LR}},
+ us = {U1, S1},
+ jid = {U2, S2, R2},
+ name = Name,
+ groups = Gs,
+ askmessage = Ask,
+ xs = Xs} = R) ->
+ R#roster{usj = {iolist_to_binary(U),
+ iolist_to_binary(S),
+ {iolist_to_binary(LU),
+ iolist_to_binary(LS),
+ iolist_to_binary(LR)}},
+ us = {iolist_to_binary(U1),
+ iolist_to_binary(S1)},
+ jid = {iolist_to_binary(U2),
+ iolist_to_binary(S2),
+ iolist_to_binary(R2)},
+ name = iolist_to_binary(Name),
+ groups = [iolist_to_binary(G) || G <- Gs],
+ askmessage = try iolist_to_binary(Ask)
+ catch _:_ -> <<"">> end,
+ xs = [fxml:to_xmlel(X) || X <- Xs]}
+ end);
+ _ ->
+ ?INFO_MSG("Recreating roster table", []),
+ mnesia:transform_table(roster, ignore, Fields)
+ end.
+
+%% Convert roster table to support virtual host
+%% Convert roster table: xattrs fields become
+update_roster_version_table() ->
+ Fields = record_info(fields, roster_version),
+ case mnesia:table_info(roster_version, attributes) of
+ Fields ->
+ ejabberd_config:convert_table_to_binary(
+ roster_version, Fields, set,
+ fun(#roster_version{us = {U, _}}) -> U end,
+ fun(#roster_version{us = {U, S}, version = Ver} = R) ->
+ R#roster_version{us = {iolist_to_binary(U),
+ iolist_to_binary(S)},
+ version = iolist_to_binary(Ver)}
+ end);
+ _ ->
+ ?INFO_MSG("Recreating roster_version table", []),
+ mnesia:transform_table(roster_version, ignore, Fields)
+ end.
diff --git a/src/mod_roster_riak.erl b/src/mod_roster_riak.erl
new file mode 100644
index 000000000..38e873827
--- /dev/null
+++ b/src/mod_roster_riak.erl
@@ -0,0 +1,113 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-module(mod_roster_riak).
+
+
+-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/2]).
+
+-include("jlib.hrl").
+-include("mod_roster.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+init(_Host, _Opts) ->
+ ok.
+
+read_roster_version(LUser, LServer) ->
+ case ejabberd_riak:get(roster_version, roster_version_schema(),
+ {LUser, LServer}) of
+ {ok, #roster_version{version = V}} -> V;
+ _Err -> error
+ end.
+
+write_roster_version(LUser, LServer, _InTransaction, Ver) ->
+ US = {LUser, LServer},
+ ejabberd_riak:put(#roster_version{us = US, version = Ver},
+ roster_version_schema()).
+
+get_roster(LUser, LServer) ->
+ case ejabberd_riak:get_by_index(roster, roster_schema(),
+ <<"us">>, {LUser, LServer}) of
+ {ok, Items} -> Items;
+ _Err -> []
+ end.
+
+get_roster_by_jid(LUser, LServer, LJID) ->
+ case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
+ {ok, I} ->
+ I#roster{jid = LJID, name = <<"">>, groups = [], xs = []};
+ {error, notfound} ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer}, jid = LJID};
+ Err ->
+ exit(Err)
+ end.
+
+get_only_items(LUser, LServer) ->
+ get_roster(LUser, LServer).
+
+roster_subscribe(LUser, LServer, _LJID, Item) ->
+ ejabberd_riak:put(Item, roster_schema(),
+ [{'2i', [{<<"us">>, {LUser, LServer}}]}]).
+
+transaction(_LServer, F) ->
+ {atomic, F()}.
+
+get_roster_by_jid_with_groups(LUser, LServer, LJID) ->
+ case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
+ {ok, I} ->
+ I;
+ {error, notfound} ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer}, jid = LJID};
+ Err ->
+ exit(Err)
+ end.
+
+remove_user(LUser, LServer) ->
+ {atomic, ejabberd_riak:delete_by_index(roster, <<"us">>, {LUser, LServer})}.
+
+update_roster(LUser, LServer, _LJID, Item) ->
+ ejabberd_riak:put(Item, roster_schema(),
+ [{'2i', [{<<"us">>, {LUser, LServer}}]}]).
+
+del_roster(LUser, LServer, LJID) ->
+ ejabberd_riak:delete(roster, {LUser, LServer, LJID}).
+
+read_subscription_and_groups(LUser, LServer, LJID) ->
+ case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
+ {ok, #roster{subscription = Subscription,
+ groups = Groups}} ->
+ {Subscription, Groups};
+ _ ->
+ error
+ end.
+
+import(_LServer, #roster{us = {LUser, LServer}} = R) ->
+ ejabberd_riak:put(R, roster_schema(),
+ [{'2i', [{<<"us">>, {LUser, LServer}}]}]);
+import(_LServer, #roster_version{} = RV) ->
+ ejabberd_riak:put(RV, roster_version_schema()).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+roster_schema() ->
+ {record_info(fields, roster), #roster{}}.
+
+roster_version_schema() ->
+ {record_info(fields, roster_version), #roster_version{}}.
diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl
new file mode 100644
index 000000000..826628659
--- /dev/null
+++ b/src/mod_roster_sql.erl
@@ -0,0 +1,308 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-module(mod_roster_sql).
+
+-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]).
+
+-include("jlib.hrl").
+-include("mod_roster.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+init(_Host, _Opts) ->
+ ok.
+
+read_roster_version(LUser, LServer) ->
+ case odbc_queries:get_roster_version(LServer, LUser) of
+ {selected, [{Version}]} -> Version;
+ {selected, []} -> error
+ end.
+
+write_roster_version(LUser, LServer, InTransaction, Ver) ->
+ Username = ejabberd_odbc:escape(LUser),
+ EVer = ejabberd_odbc:escape(Ver),
+ if InTransaction ->
+ odbc_queries:set_roster_version(Username, EVer);
+ true ->
+ odbc_queries:sql_transaction(
+ LServer,
+ fun () ->
+ odbc_queries:set_roster_version(Username, EVer)
+ end)
+ end.
+
+get_roster(LUser, LServer) ->
+ case catch odbc_queries:get_roster(LServer, LUser) of
+ {selected, Items} when is_list(Items) ->
+ JIDGroups = case catch odbc_queries:get_roster_jid_groups(
+ LServer, LUser) of
+ {selected, JGrps} when is_list(JGrps) ->
+ JGrps;
+ _ ->
+ []
+ end,
+ GroupsDict = lists:foldl(fun({J, G}, Acc) ->
+ 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);
+ _ ->
+ []
+ end.
+
+get_roster_by_jid(LUser, LServer, LJID) ->
+ {selected, Res} =
+ odbc_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 odbc_queries:get_roster(LServer, LUser) of
+ {selected, Is} when is_list(Is) ->
+ lists:map(fun(I) -> raw_to_record(LServer, I) end, Is);
+ _ -> []
+ end.
+
+roster_subscribe(_LUser, _LServer, _LJID, Item) ->
+ ItemVals = record_to_row(Item),
+ odbc_queries:roster_subscribe(ItemVals).
+
+transaction(LServer, F) ->
+ ejabberd_odbc:sql_transaction(LServer, F).
+
+get_roster_by_jid_with_groups(LUser, LServer, LJID) ->
+ SJID = jid:to_string(LJID),
+ case odbc_queries:get_roster_by_jid(LServer, LUser, SJID) of
+ {selected, [I]} ->
+ R = raw_to_record(LServer, I),
+ Groups =
+ case odbc_queries:get_roster_groups(LServer, LUser, SJID) of
+ {selected, JGrps} when is_list(JGrps) ->
+ [JGrp || {JGrp} <- JGrps];
+ _ -> []
+ end,
+ R#roster{groups = Groups};
+ {selected, []} ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer}, jid = LJID}
+ end.
+
+remove_user(LUser, LServer) ->
+ odbc_queries:del_user_roster_t(LServer, LUser),
+ {atomic, ok}.
+
+update_roster(LUser, LServer, LJID, Item) ->
+ SJID = jid:to_string(LJID),
+ ItemVals = record_to_row(Item),
+ ItemGroups = Item#roster.groups,
+ odbc_queries:update_roster(LServer, LUser, SJID, ItemVals,
+ ItemGroups).
+
+del_roster(LUser, LServer, LJID) ->
+ SJID = jid:to_string(LJID),
+ odbc_queries:del_roster(LServer, LUser, SJID).
+
+read_subscription_and_groups(LUser, LServer, LJID) ->
+ SJID = jid:to_string(LJID),
+ case catch odbc_queries:get_subscription(LServer, LUser, SJID) of
+ {selected, [{SSubscription}]} ->
+ Subscription = case SSubscription of
+ <<"B">> -> both;
+ <<"T">> -> to;
+ <<"F">> -> from;
+ _ -> none
+ end,
+ Groups = case catch odbc_queries:get_rostergroup_by_jid(
+ LServer, LUser, SJID) of
+ {selected, JGrps} when is_list(JGrps) ->
+ [JGrp || {JGrp} <- JGrps];
+ _ -> []
+ end,
+ {Subscription, Groups};
+ _ ->
+ error
+ end.
+
+export(_Server) ->
+ [{roster,
+ fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)
+ when LServer == Host ->
+ Username = ejabberd_odbc:escape(LUser),
+ SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
+ ItemVals = record_to_string(R),
+ ItemGroups = groups_to_string(R),
+ odbc_queries:update_roster_sql(Username, SJID,
+ ItemVals, ItemGroups);
+ (_Host, _R) ->
+ []
+ end},
+ {roster_version,
+ fun(Host, #roster_version{us = {LUser, LServer}, version = Ver})
+ when LServer == Host ->
+ Username = ejabberd_odbc:escape(LUser),
+ SVer = ejabberd_odbc:escape(Ver),
+ [[<<"delete from roster_version where username='">>,
+ Username, <<"';">>],
+ [<<"insert into roster_version(username, version) values('">>,
+ Username, <<"', '">>, SVer, <<"');">>]];
+ (_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_odbc:escape(LUser),
+ SJID = ejabberd_odbc:escape(JID),
+ {selected, _, Rows} =
+ ejabberd_odbc: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.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+raw_to_record(LServer,
+ [User, SJID, Nick, SSubscription, SAsk, SAskMessage,
+ _SServer, _SSubscribe, _SType]) ->
+ raw_to_record(LServer,
+ {User, 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;
+ 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,
+ #roster{usj = {User, LServer, LJID},
+ us = {User, LServer}, jid = LJID, name = Nick,
+ subscription = Subscription, ask = Ask,
+ askmessage = SAskMessage}
+ end.
+
+record_to_string(#roster{us = {User, _Server},
+ jid = JID, name = Name, subscription = Subscription,
+ ask = Ask, askmessage = AskMessage}) ->
+ Username = ejabberd_odbc:escape(User),
+ SJID =
+ ejabberd_odbc:escape(jid:to_string(jid:tolower(JID))),
+ Nick = ejabberd_odbc:escape(Name),
+ 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,
+ SAskMessage = ejabberd_odbc:escape(AskMessage),
+ [Username, SJID, Nick, SSubscription, SAsk, SAskMessage,
+ <<"N">>, <<"">>, <<"item">>].
+
+record_to_row(
+ #roster{us = {LUser, _LServer},
+ jid = JID, name = Name, subscription = Subscription,
+ ask = Ask, askmessage = AskMessage}) ->
+ SJID = jid:to_string(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, SJID, Name, SSubscription, SAsk, AskMessage}.
+
+groups_to_string(#roster{us = {User, _Server},
+ jid = JID, groups = Groups}) ->
+ Username = ejabberd_odbc:escape(User),
+ SJID =
+ ejabberd_odbc:escape(jid:to_string(jid:tolower(JID))),
+ lists:foldl(fun (<<"">>, Acc) -> Acc;
+ (Group, Acc) ->
+ G = ejabberd_odbc:escape(Group),
+ [[Username, SJID, G] | Acc]
+ end,
+ [], Groups).