From 8870fe9833a5812ebfde674956d3580bf32e831a Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Fri, 16 Jan 2015 15:59:12 +0100 Subject: [PATCH 1/7] unify roster logic inspired by ejabberd's mod_roster.erl --- apps/ejabberd/src/mod_roster.erl | 1051 ++++++++++----------- apps/ejabberd/src/mod_roster_mnesia.erl | 124 +++ apps/ejabberd/src/mod_roster_odbc.erl | 1133 +++++------------------ 3 files changed, 831 insertions(+), 1477 deletions(-) create mode 100644 apps/ejabberd/src/mod_roster_mnesia.erl diff --git a/apps/ejabberd/src/mod_roster.erl b/apps/ejabberd/src/mod_roster.erl index c954a65280d..ea9d7a0b5e6 100644 --- a/apps/ejabberd/src/mod_roster.erl +++ b/apps/ejabberd/src/mod_roster.erl @@ -5,7 +5,7 @@ %%% Created : 11 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2011 ProcessOne +%%% ejabberd, Copyright (C) 2002-2013 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -38,11 +38,13 @@ -behaviour(gen_mod). --export([start/2, stop/1, +-export([start/2, + stop/1, process_iq/3, process_local_iq/3, get_user_roster/2, get_subscription_lists/3, + get_roster/2, in_subscription/6, out_subscription/4, set_items/3, @@ -51,7 +53,8 @@ item_to_xml/1, get_versioning_feature/2, roster_versioning_enabled/1, - roster_version/2]). + roster_version/2, + send_unsubscription_to_rosteritems/2]). -include("ejabberd.hrl"). -include("jlib.hrl"). @@ -61,19 +64,12 @@ -type roster() :: #roster{}. +-define(BACKEND, mod_roster_mnesia). + start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - 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_table(), - mnesia:add_table_index(roster, us), - mnesia:add_table_index(roster_version, us), - mnesia:add_table_copy(roster, node(), disc_copies), - mnesia:add_table_copy(roster_version, node(), disc_copies), + ?BACKEND:init(Host, Opts), ejabberd_hooks:add(roster_get, Host, ?MODULE, get_user_roster, 50), @@ -91,10 +87,6 @@ start(Host, Opts) -> ?MODULE, remove_user, 50), ejabberd_hooks:add(roster_get_versioning_feature, Host, ?MODULE, get_versioning_feature, 50), - ejabberd_hooks:add(webadmin_page_host, Host, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:add(webadmin_user, Host, - ?MODULE, webadmin_user, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER, ?MODULE, process_iq, IQDisc). @@ -115,13 +107,8 @@ stop(Host) -> ?MODULE, remove_user, 50), ejabberd_hooks:delete(roster_get_versioning_feature, Host, ?MODULE, get_versioning_feature, 50), - ejabberd_hooks:delete(webadmin_page_host, Host, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:delete(webadmin_user, Host, - ?MODULE, webadmin_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ROSTER). - process_iq(From, To, IQ) -> #iq{sub_el = SubEl} = IQ, #jid{lserver = LServer} = From, @@ -141,10 +128,9 @@ process_local_iq(From, To, #iq{type = Type} = IQ) -> end. roster_hash(Items) -> - sha:sha1_hex(term_to_binary( - lists:sort( - [R#roster{groups = lists:sort(Grs)} || - R = #roster{groups = Grs} <- Items]))). + L = [R#roster{groups = lists:sort(Grs)} || + R = #roster{groups = Grs} <- Items], + sha:sha1_hex(term_to_binary(lists:sort(L))). roster_versioning_enabled(Host) -> gen_mod:get_module_opt(Host, ?MODULE, versioning, false). @@ -162,199 +148,212 @@ get_versioning_feature(Acc, Host) -> false -> [] end. -roster_version(LServer ,LUser) -> - US = {LUser, LServer}, - case roster_version_on_db(LServer) of - true -> - case mnesia:dirty_read(roster_version, US) of - [#roster_version{version = V}] -> V; - [] -> not_found - end; - false -> - roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US])) - end. - -%% Load roster from DB only if neccesary. +roster_version(LServer, LUser) -> + US = {LUser, LServer}, + case roster_version_on_db(LServer) of + true -> + case read_roster_version(LUser, LServer) of + error -> not_found; + V -> V + end; + false -> + roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US])) + end. + +read_roster_version(LUser, LServer) -> + ?BACKEND:read_roster_version(LUser, LServer). + +write_roster_version(LUser, LServer) -> + write_roster_version(LUser, LServer, false). + +write_roster_version_t(LUser, LServer) -> + write_roster_version(LUser, LServer, true). + +write_roster_version(LUser, LServer, InTransaction) -> + Ver = sha:sha1_hex(term_to_binary(os:timestamp())), + ?BACKEND:write_roster_version(LUser, LServer, InTransaction, Ver), + Ver. + +%% Load roster from DB only if neccesary. %% It is neccesary if %% - roster versioning is disabled in server OR %% - roster versioning is not used by the client OR %% - roster versioning is used by server and client, BUT the server isn't storing versions on db OR %% - the roster version from client don't match current version. process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) -> - LUser = From#jid.luser, LServer = From#jid.lserver, - US = {LUser, LServer}, try - {ItemsToSend, VersionToSend} = - case {xml:get_tag_attr(<<"ver">>, SubEl), - roster_versioning_enabled(LServer), - roster_version_on_db(LServer)} of - {{value, RequestedVersion}, true, true} -> - %% Retrieve version from DB. Only load entire roster - %% when neccesary. - case mnesia:dirty_read(roster_version, US) of - [#roster_version{version = RequestedVersion}] -> - {false, false}; - [#roster_version{version = NewVersion}] -> - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), NewVersion}; - [] -> - RosterVersion = sha:sha1_hex(term_to_binary(now())), - mnesia:dirty_write(#roster_version{us = US, version = RosterVersion}), - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), RosterVersion} - end; - - {{value, RequestedVersion}, true, false} -> - RosterItems = ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [] , [US]), - case roster_hash(RosterItems) of - RequestedVersion -> - {false, false}; - New -> - {lists:map(fun item_to_xml/1, RosterItems), New} - end; - - _ -> - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), false} - end, - IQ#iq{type = result, sub_el = case {ItemsToSend, VersionToSend} of - {false, false} -> []; - {Items, false} -> [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER}], - children = Items}]; - {Items, Version} -> [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER}, {<<"ver">>, Version}], - children = Items}] - end} + AttrVer = xml:get_tag_attr(<<"ver">>, SubEl), + VersioningEnabled = roster_versioning_enabled(LServer), + VersionOnDb = roster_version_on_db(LServer), + {ItemsToSend, VersionToSend} = + get_user_roster_based_on_version(AttrVer, VersioningEnabled, VersionOnDb, + From, To), + IQ#iq{type = result, + sub_el = create_sub_el(ItemsToSend, VersionToSend)} catch _:_ -> - IQ#iq{type =error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]} - end. - - -get_user_roster(Acc, US) -> - case catch mnesia:dirty_index_read(roster, US, #roster.us) of - Items when is_list(Items) -> - lists:filter(fun(#roster{subscription = none, ask = in}) -> - false; - (_) -> - true - end, Items) ++ Acc; - _ -> - Acc + IQ#iq{type = error, + sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]} end. +get_user_roster_based_on_version({value, RequestedVersion}, true, true, + From, To) -> + LUser = From#jid.luser, + LServer = From#jid.lserver, + US = {LUser, LServer}, + case read_roster_version(LUser, LServer) of + error -> + RosterVersion = write_roster_version(LUser, LServer), + {lists:map(fun item_to_xml/1, + ejabberd_hooks:run_fold(roster_get, + To#jid.lserver, + [], + [US])), + RosterVersion}; + RequestedVersion -> + {false, false}; + NewVersion -> + {lists:map(fun item_to_xml/1, + ejabberd_hooks:run_fold(roster_get, + To#jid.lserver, + [], + [US])), + NewVersion} + end; +get_user_roster_based_on_version({value, RequestedVersion}, true, false, + From, To) -> + RosterItems = + ejabberd_hooks:run_fold(roster_get, + To#jid.lserver, + [], + [{From#jid.luser, From#jid.lserver}]), + case roster_hash(RosterItems) of + RequestedVersion -> + {false, false}; + New -> + {lists:map(fun item_to_xml/1, + RosterItems), + New} + end; +get_user_roster_based_on_version(_, _, _, From, To) -> + {lists:map(fun item_to_xml/1, + ejabberd_hooks:run_fold(roster_get, + To#jid.lserver, + [], + [{From#jid.luser, From#jid.lserver}])), + false}. + +create_sub_el(false, false) -> + []; +create_sub_el(Items, false) -> + [#xmlel{name = <<"query">>, + attrs = [{<<"xmlns">>, ?NS_ROSTER}], + children = Items}]; +create_sub_el(Items, Version) -> + [#xmlel{name = <<"query">>, + attrs = [{<<"xmlns">>, ?NS_ROSTER}, + {<<"ver">>, Version}], + children = Items}]. + +get_user_roster(Acc, {LUser, LServer}) -> + Items = get_roster(LUser, LServer), + lists:filter(fun (#roster{subscription = none, ask = in}) -> + false; + (_) -> + true + end, Items) + ++ Acc. + +get_roster(LUser, LServer) -> + ?BACKEND:get_roster(LUser, LServer). item_to_xml(Item) -> - Attrs1 = [{<<"jid">>, jlib:jid_to_binary(Item#roster.jid)}], + Attrs1 = [{<<"jid">>, + jlib:jid_to_binary(Item#roster.jid)}], Attrs2 = case Item#roster.name of - <<>> -> - Attrs1; - Name -> - [{<<"name">>, Name} | Attrs1] + <<"">> -> Attrs1; + Name -> [{<<"name">>, Name} | Attrs1] end, Attrs3 = case Item#roster.subscription of - none -> - [{<<"subscription">>, <<"none">>} | Attrs2]; - from -> - [{<<"subscription">>, <<"from">>} | Attrs2]; - to -> - [{<<"subscription">>, <<"to">>} | Attrs2]; - both -> - [{<<"subscription">>, <<"both">>} | Attrs2]; - remove -> - [{<<"subscription">>, <<"remove">>} | Attrs2] + none -> [{<<"subscription">>, <<"none">>} | Attrs2]; + from -> [{<<"subscription">>, <<"from">>} | Attrs2]; + to -> [{<<"subscription">>, <<"to">>} | Attrs2]; + both -> [{<<"subscription">>, <<"both">>} | Attrs2]; + remove -> [{<<"subscription">>, <<"remove">>} | Attrs2] end, Attrs4 = case ask_to_pending(Item#roster.ask) of - out -> - [{<<"ask">>, <<"subscribe">>} | Attrs3]; - both -> - [{<<"ask">>, <<"subscribe">>} | Attrs3]; - _ -> - Attrs3 + out -> [{<<"ask">>, <<"subscribe">>} | Attrs3]; + both -> [{<<"ask">>, <<"subscribe">>} | Attrs3]; + _ -> Attrs3 end, - SubEls1 = lists:map(fun(G) -> - #xmlel{name = <<"group">>, - children = [#xmlcdata{content = G}]} - end, Item#roster.groups), + SubEls1 = lists:map(fun (G) -> + #xmlel{name = <<"group">>, attrs = [], + children = [{xmlcdata, G}]} + end, + Item#roster.groups), SubEls = SubEls1 ++ Item#roster.xs, - #xmlel{name = <<"item">>, attrs = Attrs4, children = SubEls}. + #xmlel{name = <<"item">>, attrs = Attrs4, + children = SubEls}. +get_roster_by_jid_t(LUser, LServer, LJID) -> + ?BACKEND:get_roster_by_jid_t(LUser, LServer, LJID). -process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) -> +process_iq_set(#jid{lserver = LServer} = From, To, #iq{sub_el = SubEl} = IQ) -> #xmlel{children = Els} = SubEl, - #jid{lserver = LServer} = From, ejabberd_hooks:run(roster_set, LServer, [From, To, SubEl]), lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els), IQ#iq{type = result, sub_el = []}. -process_item_set(From, To, #xmlel{attrs = Attrs, children = Els}) -> +process_item_set(From, To, #xmlel{attrs = Attrs} = El) -> JID1 = jlib:binary_to_jid(xml:get_attr_s(<<"jid">>, Attrs)), - #jid{user = User, luser = LUser, lserver = LServer} = From, - case JID1 of - error -> - ok; - _ -> - JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource}, - LJID = jlib:jid_tolower(JID1), - F = fun() -> - Res = mnesia:read({roster, {LUser, LServer, LJID}}), - Item = case Res of - [] -> - #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, - jid = JID}; - [I] -> - I#roster{jid = JID, - name = <<>>, - groups = [], - xs = []} - end, - Item1 = process_item_attrs(Item, Attrs), - Item2 = process_item_els(Item1, Els), - case Item2#roster.subscription of - remove -> - mnesia:delete({roster, {LUser, LServer, LJID}}); - _ -> - mnesia:write(Item2) - end, - %% If the item exist in shared roster, take the - %% subscription information from there: - Item3 = ejabberd_hooks:run_fold(roster_process_item, - LServer, Item2, [LServer]), - case roster_version_on_db(LServer) of - true -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha1_hex(term_to_binary(now()))}); - false -> ok - end, - {Item, Item3} - end, - case mnesia:transaction(F) of - {atomic, {OldItem, Item}} -> - push_item(User, LServer, To, Item), - case Item#roster.subscription of - remove -> - send_unsubscribing_presence(From, OldItem), - ok; - _ -> - ok - end; - E -> - ?DEBUG("ROSTER: roster item set error: ~p~n", [E]), - ok - end - end; -process_item_set(_From, _To, _) -> - ok. + do_process_item_set(JID1, From, To, El); +process_item_set(_From, _To, _) -> ok. + +do_process_item_set(error, _, _, _) -> ok; +do_process_item_set(JID1, + #jid{user = User, luser = LUser, lserver = LServer} = From, + To, + #xmlel{attrs = Attrs, children = Els}) -> + LJID = jlib:jid_tolower(JID1), + F = fun () -> + Item = get_roster_by_jid_t(LUser, LServer, LJID), + Item1 = process_item_attrs(Item, Attrs), + Item2 = process_item_els(Item1, Els), + case Item2#roster.subscription of + remove -> del_roster_t(LUser, LServer, LJID); + _ -> update_roster_t(LUser, LServer, LJID, Item2) + end, + Item3 = ejabberd_hooks:run_fold(roster_process_item, + LServer, Item2, + [LServer]), + case roster_version_on_db(LServer) of + true -> write_roster_version_t(LUser, LServer); + false -> ok + end, + {Item, Item3} + end, + case transaction(LServer, F) of + {atomic, {OldItem, Item}} -> + push_item(User, LServer, To, Item), + case Item#roster.subscription of + remove -> + send_unsubscribing_presence(From, OldItem), ok; + _ -> ok + end; + E -> + ?DEBUG("ROSTER: roster item set error: ~p~n", [E]), ok + end. +%%TODO remove the case and match in function head process_item_attrs(Item, [{Attr, Val} | Attrs]) -> case Attr of <<"jid">> -> case jlib:binary_to_jid(Val) of - error -> - process_item_attrs(Item, Attrs); + error -> process_item_attrs(Item, Attrs); JID1 -> - JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource}, + JID = {JID1#jid.luser, JID1#jid.lserver, + JID1#jid.lresource}, process_item_attrs(Item#roster{jid = JID}, Attrs) end; <<"name">> -> @@ -363,350 +362,360 @@ process_item_attrs(Item, [{Attr, Val} | Attrs]) -> case Val of <<"remove">> -> process_item_attrs(Item#roster{subscription = remove}, - Attrs); - _ -> - process_item_attrs(Item, Attrs) + Attrs); + _ -> process_item_attrs(Item, Attrs) end; - <<"ask">> -> - process_item_attrs(Item, Attrs); - _ -> - process_item_attrs(Item, Attrs) + <<"ask">> -> process_item_attrs(Item, Attrs); + _ -> process_item_attrs(Item, Attrs) end; -process_item_attrs(Item, []) -> - Item. +process_item_attrs(Item, []) -> Item. - -process_item_els(Item, [XE = #xmlel{name = Name, attrs = Attrs, - children = SEls} | Els]) -> +process_item_els(Item, + [#xmlel{name = Name, attrs = Attrs, children = SEls} + | Els]) -> case Name of <<"group">> -> Groups = [xml:get_cdata(SEls) | Item#roster.groups], process_item_els(Item#roster{groups = Groups}, Els); _ -> case xml:get_attr_s(<<"xmlns">>, Attrs) of - <<>> -> - process_item_els(Item, Els); + <<"">> -> process_item_els(Item, Els); _ -> - XEls = [XE | Item#roster.xs], + XEls = [#xmlel{name = Name, attrs = Attrs, + children = SEls} + | Item#roster.xs], process_item_els(Item#roster{xs = XEls}, Els) end end; -process_item_els(Item, [#xmlcdata{} | Els]) -> +process_item_els(Item, [{xmlcdata, _} | Els]) -> process_item_els(Item, Els); -process_item_els(Item, []) -> - Item. - +process_item_els(Item, []) -> Item. push_item(User, Server, From, Item) -> - ejabberd_sm:route(jlib:make_jid(<<>>, <<>>, <<>>), - jlib:make_jid(User, Server, <<>>), - #xmlel{name = <<"broadcast">>, - children = [{item, - Item#roster.jid, - Item#roster.subscription}]}), + ejabberd_sm:route(jlib:make_jid(<<"">>, <<"">>, <<"">>), + jlib:make_jid(User, Server, <<"">>), + #xmlel{name = <<"broadcast">>, + children = [{item, + Item#roster.jid, + Item#roster.subscription}]}), case roster_versioning_enabled(Server) of true -> - push_item_version(Server, User, From, Item, roster_version(Server, User)); + push_item_version(Server, User, From, Item, + roster_version(Server, User)); false -> - lists:foreach(fun(Resource) -> - push_item(User, Server, Resource, From, Item) - end, ejabberd_sm:get_user_resources(User, Server)) + lists:foreach(fun (Resource) -> + push_item(User, Server, Resource, From, Item) + end, + ejabberd_sm:get_user_resources(User, Server)) end. -% TODO: don't push to those who didn't load roster push_item(User, Server, Resource, From, Item) -> + ejabberd_hooks:run(roster_push, Server, [From, Item]), push_item(User, Server, Resource, From, Item, not_found). push_item(User, Server, Resource, From, Item, RosterVersion) -> - ejabberd_hooks:run(roster_push, Server, [From, Item]), ExtraAttrs = case RosterVersion of - not_found -> []; - _ -> [{<<"ver">>, RosterVersion}] - end, + not_found -> []; + _ -> [{<<"ver">>, RosterVersion}] + end, ResIQ = #iq{type = set, xmlns = ?NS_ROSTER, - id = list_to_binary("push" ++ randoms:get_string()), - sub_el = [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER}|ExtraAttrs], - children = [item_to_xml(Item)]}]}, - ejabberd_router:route( - From, - jlib:make_jid(User, Server, Resource), - jlib:iq_to_xml(ResIQ)). - %% @doc Roster push, calculate and include the version attribute. %% TODO: don't push to those who didn't load roster -push_item_version(Server, User, From, Item, RosterVersion) -> - lists:foreach(fun(Resource) -> - push_item(User, Server, Resource, From, Item, RosterVersion) - end, ejabberd_sm:get_user_resources(User, Server)). + id = list_to_binary("push" ++ randoms:get_string()), + sub_el = + [#xmlel{name = <<"query">>, + attrs = [{<<"xmlns">>, ?NS_ROSTER} | ExtraAttrs], + children = [item_to_xml(Item)]}]}, + ejabberd_router:route(From, + jlib:make_jid(User, Server, Resource), + jlib:iq_to_xml(ResIQ)). + +push_item_version(Server, User, From, Item, + RosterVersion) -> + lists:foreach(fun (Resource) -> + push_item(User, Server, Resource, From, Item, + RosterVersion) + end, + ejabberd_sm:get_user_resources(User, Server)). -get_subscription_lists(_, User, Server) -> +get_subscription_lists(Acc, User, Server) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), - US = {LUser, LServer}, + Items = ?BACKEND:get_subscription_lists(Acc, LUser, LServer), JID = jlib:make_jid(User, Server, <<>>), - case mnesia:dirty_index_read(roster, US, #roster.us) of - Items when is_list(Items) -> - fill_subscription_lists(JID, Items, [], [], []); - _ -> - {[], [], []} - end. + fill_subscription_lists(JID, LServer, Items, [], [], []). + -fill_subscription_lists(JID, [I | Is], F, T, P) -> +fill_subscription_lists(JID, LServer, [#roster{} = I | Is], F, T, P) -> J = element(3, I#roster.usj), + NewP = case I#roster.ask of - Ask when Ask == in; - Ask == both -> + Ask when Ask == in; Ask == both -> Message = I#roster.askmessage, - Status = if is_binary(Message) -> - Message; - true -> - <<>> + Status = if is_binary(Message) -> Message; + true -> <<>> end, - [#xmlel{name = <<"presence">>, - attrs = [{<<"from">>, jlib:jid_to_binary(I#roster.jid)}, - {<<"to">>, jlib:jid_to_binary(JID)}, - {<<"type">>, <<"subscribe">>}], - children = [#xmlel{name = <<"status">>, - children = [#xmlcdata{content = Status}]}]} | P]; + StatusEl = #xmlel{ + name = <<"status">>, + children = [#xmlcdata{content = Status}]}, + El = #xmlel{ + name = <<"presence">>, + attrs = [{<<"from">>, jlib:jid_to_binary(I#roster.jid)}, + {<<"to">>, jlib:jid_to_binary(JID)}, + {<<"type">>, <<"subscribe">>}], + children = [StatusEl]}, + [El | P]; _ -> P end, + case I#roster.subscription of both -> - fill_subscription_lists(JID, Is, [J | F], [J | T], NewP); + fill_subscription_lists(JID, LServer, Is, [J | F], [J | T], NewP); from -> - fill_subscription_lists(JID, Is, [J | F], T, NewP); - to -> - fill_subscription_lists(JID, Is, F, [J | T], NewP); - _ -> - fill_subscription_lists(JID, Is, F, T, NewP) + fill_subscription_lists(JID, LServer, Is, [J | F], T, NewP); + to -> fill_subscription_lists(JID, LServer, Is, F, [J | T], NewP); + _ -> fill_subscription_lists(JID, LServer, Is, F, T, NewP) + end; +fill_subscription_lists(JID, LServer, [RawI | Is], F, T, P) -> + I = ?BACKEND:raw_to_record(LServer, RawI), + case I of + %% Bad JID in database: + error -> fill_subscription_lists(JID, LServer, Is, F, T, P); + _ -> fill_subscription_lists(JID, LServer, [I | Is], F, T, P) end; -fill_subscription_lists(_JID, [], F, T, P) -> - {F, T, P}. +fill_subscription_lists(_, _LServer, [], F, T, P) -> {F, T, P}. ask_to_pending(subscribe) -> out; ask_to_pending(unsubscribe) -> none; ask_to_pending(Ask) -> Ask. +roster_subscribe_t(LUser, LServer, LJID, Item) -> + ?BACKEND:roster_subscribe_t(LUser, LServer, LJID, Item). +transaction(LServer, F) -> + case gen_mod:get_module_opt(LServer, ?MODULE, backend, mnesia) of + mnesia -> mnesia:transaction(F); + odbc -> ejabberd_odbc:sql_transaction(LServer, F) + end. in_subscription(_, User, Server, JID, Type, Reason) -> - process_subscription(in, User, Server, JID, Type, Reason). + process_subscription(in, User, Server, JID, Type, + Reason). out_subscription(User, Server, JID, Type) -> - process_subscription(out, User, Server, JID, Type, <<>>). + process_subscription(out, User, Server, JID, Type, <<"">>). + +get_roster_by_jid_with_groups_t(LUser, LServer, LJID) -> + ?BACKEND:get_roster_by_jid_with_groups_t(LUser, LServer, LJID). -process_subscription(Direction, User, Server, JID1, Type, Reason) -> +process_subscription(Direction, User, Server, JID1, + Type, Reason) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), - US = {LUser, LServer}, LJID = jlib:jid_tolower(JID1), - F = fun() -> - Item = case mnesia:read({roster, {LUser, LServer, LJID}}) of - [] -> - JID = {JID1#jid.user, - JID1#jid.server, - JID1#jid.resource}, - #roster{usj = {LUser, LServer, LJID}, - us = US, - jid = JID}; - [I] -> - I - end, - NewState = case Direction of - out -> - out_state_change(Item#roster.subscription, - Item#roster.ask, - Type); - in -> - in_state_change(Item#roster.subscription, - Item#roster.ask, - Type) - end, - AutoReply = case Direction of - out -> - none; - in -> - in_auto_reply(Item#roster.subscription, - Item#roster.ask, - Type) - end, - AskMessage = case NewState of - {_, both} -> Reason; - {_, in} -> Reason; - _ -> <<>> - end, - case NewState of - none -> - {none, AutoReply}; - {none, none} when Item#roster.subscription == none, - Item#roster.ask == in -> - mnesia:delete({roster, {LUser, LServer, LJID}}), - {none, AutoReply}; - {Subscription, Pending} -> - NewItem = Item#roster{subscription = Subscription, - ask = Pending, - askmessage = AskMessage}, - mnesia:write(NewItem), - case roster_version_on_db(LServer) of - true -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha1_hex(term_to_binary(now()))}); - false -> ok - end, - {{push, NewItem}, AutoReply} - end - end, - case mnesia:transaction(F) of + F = fun () -> + Item = get_roster_by_jid_with_groups_t(LUser, LServer, + LJID), + NewState = case Direction of + out -> + out_state_change(Item#roster.subscription, + Item#roster.ask, Type); + in -> + in_state_change(Item#roster.subscription, + Item#roster.ask, Type) + end, + AutoReply = case Direction of + out -> none; + in -> + in_auto_reply(Item#roster.subscription, + Item#roster.ask, Type) + end, + AskMessage = case NewState of + {_, both} -> Reason; + {_, in} -> Reason; + _ -> <<"">> + end, + case NewState of + none -> {none, AutoReply}; + {none, none} + when Item#roster.subscription == none, + Item#roster.ask == in -> + del_roster_t(LUser, LServer, LJID), {none, AutoReply}; + {Subscription, Pending} -> + NewItem = Item#roster{subscription = Subscription, + ask = Pending, + askmessage = + iolist_to_binary(AskMessage)}, + roster_subscribe_t(LUser, LServer, LJID, NewItem), + case roster_version_on_db(LServer) of + true -> write_roster_version_t(LUser, LServer); + false -> ok + end, + {{push, NewItem}, AutoReply} + end + end, + case transaction(LServer, F) of {atomic, {Push, AutoReply}} -> case AutoReply of - none -> - ok; + none -> ok; _ -> T = case AutoReply of subscribed -> <<"subscribed">>; unsubscribed -> <<"unsubscribed">> end, - ejabberd_router:route( - jlib:make_jid(User, Server, <<>>), JID1, - #xmlel{name = <<"presence">>, attrs = [{<<"type">>, T}]}) + ejabberd_router:route(jlib:make_jid(User, Server, + <<"">>), + JID1, + #xmlel{name = <<"presence">>, + attrs = [{<<"type">>, T}], + children = []}) end, case Push of {push, Item} -> - if - Item#roster.subscription == none, - Item#roster.ask == in -> - ok; + if Item#roster.subscription == none, + Item#roster.ask == in -> + ok; true -> push_item(User, Server, - jlib:make_jid(User, Server, <<>>), Item) + jlib:make_jid(User, Server, <<"">>), Item) end, true; - none -> - false + none -> false end; - _ -> - false + _ -> false end. %% in_state_change(Subscription, Pending, Type) -> NewState %% NewState = none | {NewSubscription, NewPending} -ifdef(ROSTER_GATEWAY_WORKAROUND). + -define(NNSD, {to, none}). + -define(NISD, {to, in}). + -else. + -define(NNSD, none). + -define(NISD, none). + -endif. -in_state_change(none, none, subscribe) -> {none, in}; -in_state_change(none, none, subscribed) -> ?NNSD; -in_state_change(none, none, unsubscribe) -> none; +in_state_change(none, none, subscribe) -> {none, in}; +in_state_change(none, none, subscribed) -> ?NNSD; +in_state_change(none, none, unsubscribe) -> none; in_state_change(none, none, unsubscribed) -> none; -in_state_change(none, out, subscribe) -> {none, both}; -in_state_change(none, out, subscribed) -> {to, none}; -in_state_change(none, out, unsubscribe) -> none; -in_state_change(none, out, unsubscribed) -> {none, none}; -in_state_change(none, in, subscribe) -> none; -in_state_change(none, in, subscribed) -> ?NISD; -in_state_change(none, in, unsubscribe) -> {none, none}; -in_state_change(none, in, unsubscribed) -> none; -in_state_change(none, both, subscribe) -> none; -in_state_change(none, both, subscribed) -> {to, in}; -in_state_change(none, both, unsubscribe) -> {none, out}; +in_state_change(none, out, subscribe) -> {none, both}; +in_state_change(none, out, subscribed) -> {to, none}; +in_state_change(none, out, unsubscribe) -> none; +in_state_change(none, out, unsubscribed) -> + {none, none}; +in_state_change(none, in, subscribe) -> none; +in_state_change(none, in, subscribed) -> ?NISD; +in_state_change(none, in, unsubscribe) -> {none, none}; +in_state_change(none, in, unsubscribed) -> none; +in_state_change(none, both, subscribe) -> none; +in_state_change(none, both, subscribed) -> {to, in}; +in_state_change(none, both, unsubscribe) -> {none, out}; in_state_change(none, both, unsubscribed) -> {none, in}; -in_state_change(to, none, subscribe) -> {to, in}; -in_state_change(to, none, subscribed) -> none; -in_state_change(to, none, unsubscribe) -> none; -in_state_change(to, none, unsubscribed) -> {none, none}; -in_state_change(to, in, subscribe) -> none; -in_state_change(to, in, subscribed) -> none; -in_state_change(to, in, unsubscribe) -> {to, none}; -in_state_change(to, in, unsubscribed) -> {none, in}; -in_state_change(from, none, subscribe) -> none; -in_state_change(from, none, subscribed) -> {both, none}; -in_state_change(from, none, unsubscribe) -> {none, none}; +in_state_change(to, none, subscribe) -> {to, in}; +in_state_change(to, none, subscribed) -> none; +in_state_change(to, none, unsubscribe) -> none; +in_state_change(to, none, unsubscribed) -> {none, none}; +in_state_change(to, in, subscribe) -> none; +in_state_change(to, in, subscribed) -> none; +in_state_change(to, in, unsubscribe) -> {to, none}; +in_state_change(to, in, unsubscribed) -> {none, in}; +in_state_change(from, none, subscribe) -> none; +in_state_change(from, none, subscribed) -> {both, none}; +in_state_change(from, none, unsubscribe) -> + {none, none}; in_state_change(from, none, unsubscribed) -> none; -in_state_change(from, out, subscribe) -> none; -in_state_change(from, out, subscribed) -> {both, none}; -in_state_change(from, out, unsubscribe) -> {none, out}; -in_state_change(from, out, unsubscribed) -> {from, none}; -in_state_change(both, none, subscribe) -> none; -in_state_change(both, none, subscribed) -> none; -in_state_change(both, none, unsubscribe) -> {to, none}; -in_state_change(both, none, unsubscribed) -> {from, none}. - -out_state_change(none, none, subscribe) -> {none, out}; -out_state_change(none, none, subscribed) -> none; -out_state_change(none, none, unsubscribe) -> none; +in_state_change(from, out, subscribe) -> none; +in_state_change(from, out, subscribed) -> {both, none}; +in_state_change(from, out, unsubscribe) -> {none, out}; +in_state_change(from, out, unsubscribed) -> + {from, none}; +in_state_change(both, none, subscribe) -> none; +in_state_change(both, none, subscribed) -> none; +in_state_change(both, none, unsubscribe) -> {to, none}; +in_state_change(both, none, unsubscribed) -> + {from, none}. + +out_state_change(none, none, subscribe) -> {none, out}; +out_state_change(none, none, subscribed) -> none; +out_state_change(none, none, unsubscribe) -> none; out_state_change(none, none, unsubscribed) -> none; -out_state_change(none, out, subscribe) -> {none, out}; %% We need to resend query (RFC3921, section 9.2) -out_state_change(none, out, subscribed) -> none; -out_state_change(none, out, unsubscribe) -> {none, none}; -out_state_change(none, out, unsubscribed) -> none; -out_state_change(none, in, subscribe) -> {none, both}; -out_state_change(none, in, subscribed) -> {from, none}; -out_state_change(none, in, unsubscribe) -> none; -out_state_change(none, in, unsubscribed) -> {none, none}; -out_state_change(none, both, subscribe) -> none; -out_state_change(none, both, subscribed) -> {from, out}; -out_state_change(none, both, unsubscribe) -> {none, in}; -out_state_change(none, both, unsubscribed) -> {none, out}; -out_state_change(to, none, subscribe) -> none; -out_state_change(to, none, subscribed) -> {both, none}; -out_state_change(to, none, unsubscribe) -> {none, none}; -out_state_change(to, none, unsubscribed) -> none; -out_state_change(to, in, subscribe) -> none; -out_state_change(to, in, subscribed) -> {both, none}; -out_state_change(to, in, unsubscribe) -> {none, in}; -out_state_change(to, in, unsubscribed) -> {to, none}; -out_state_change(from, none, subscribe) -> {from, out}; -out_state_change(from, none, subscribed) -> none; -out_state_change(from, none, unsubscribe) -> none; -out_state_change(from, none, unsubscribed) -> {none, none}; -out_state_change(from, out, subscribe) -> none; -out_state_change(from, out, subscribed) -> none; -out_state_change(from, out, unsubscribe) -> {from, none}; -out_state_change(from, out, unsubscribed) -> {none, out}; -out_state_change(both, none, subscribe) -> none; -out_state_change(both, none, subscribed) -> none; -out_state_change(both, none, unsubscribe) -> {from, none}; -out_state_change(both, none, unsubscribed) -> {to, none}. - -in_auto_reply(from, none, subscribe) -> subscribed; -in_auto_reply(from, out, subscribe) -> subscribed; -in_auto_reply(both, none, subscribe) -> subscribed; -in_auto_reply(none, in, unsubscribe) -> unsubscribed; -in_auto_reply(none, both, unsubscribe) -> unsubscribed; -in_auto_reply(to, in, unsubscribe) -> unsubscribed; -in_auto_reply(from, none, unsubscribe) -> unsubscribed; -in_auto_reply(from, out, unsubscribe) -> unsubscribed; -in_auto_reply(both, none, unsubscribe) -> unsubscribed; -in_auto_reply(_, _, _) -> none. - +out_state_change(none, out, subscribe) -> + {none, + out}; %% We need to resend query (RFC3921, section 9.2) +out_state_change(none, out, subscribed) -> none; +out_state_change(none, out, unsubscribe) -> + {none, none}; +out_state_change(none, out, unsubscribed) -> none; +out_state_change(none, in, subscribe) -> {none, both}; +out_state_change(none, in, subscribed) -> {from, none}; +out_state_change(none, in, unsubscribe) -> none; +out_state_change(none, in, unsubscribed) -> + {none, none}; +out_state_change(none, both, subscribe) -> none; +out_state_change(none, both, subscribed) -> {from, out}; +out_state_change(none, both, unsubscribe) -> {none, in}; +out_state_change(none, both, unsubscribed) -> + {none, out}; +out_state_change(to, none, subscribe) -> none; +out_state_change(to, none, subscribed) -> {both, none}; +out_state_change(to, none, unsubscribe) -> {none, none}; +out_state_change(to, none, unsubscribed) -> none; +out_state_change(to, in, subscribe) -> none; +out_state_change(to, in, subscribed) -> {both, none}; +out_state_change(to, in, unsubscribe) -> {none, in}; +out_state_change(to, in, unsubscribed) -> {to, none}; +out_state_change(from, none, subscribe) -> {from, out}; +out_state_change(from, none, subscribed) -> none; +out_state_change(from, none, unsubscribe) -> none; +out_state_change(from, none, unsubscribed) -> + {none, none}; +out_state_change(from, out, subscribe) -> none; +out_state_change(from, out, subscribed) -> none; +out_state_change(from, out, unsubscribe) -> + {from, none}; +out_state_change(from, out, unsubscribed) -> + {none, out}; +out_state_change(both, none, subscribe) -> none; +out_state_change(both, none, subscribed) -> none; +out_state_change(both, none, unsubscribe) -> + {from, none}; +out_state_change(both, none, unsubscribed) -> + {to, none}. + +in_auto_reply(from, none, subscribe) -> subscribed; +in_auto_reply(from, out, subscribe) -> subscribed; +in_auto_reply(both, none, subscribe) -> subscribed; +in_auto_reply(none, in, unsubscribe) -> unsubscribed; +in_auto_reply(none, both, unsubscribe) -> unsubscribed; +in_auto_reply(to, in, unsubscribe) -> unsubscribed; +in_auto_reply(from, none, unsubscribe) -> unsubscribed; +in_auto_reply(from, out, unsubscribe) -> unsubscribed; +in_auto_reply(both, none, unsubscribe) -> unsubscribed; +in_auto_reply(_, _, _) -> none. remove_user(User, Server) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), - US = {LUser, LServer}, - send_unsubscription_to_rosteritems(LUser, LServer), - F = fun() -> - lists:foreach(fun(R) -> - mnesia:delete_object(R) - end, - mnesia:index_read(roster, US, #roster.us)) - end, - mnesia:transaction(F). + ?BACKEND:remove_user(LUser, LServer). %% For each contact with Subscription: %% Both or From, send a "unsubscribed" presence stanza; %% Both or To, send a "unsubscribe" presence stanza. send_unsubscription_to_rosteritems(LUser, LServer) -> RosterItems = get_user_roster([], {LUser, LServer}), - From = jlib:make_jid({LUser, LServer, <<>>}), - lists:foreach(fun(RosterItem) -> - send_unsubscribing_presence(From, RosterItem) - end, - RosterItems). + From = jlib:make_jid({LUser, LServer, <<"">>}), + lists:foreach(fun (RosterItem) -> + send_unsubscribing_presence(From, RosterItem) + end, + RosterItems). %% @spec (From::jid(), Item::roster()) -> ok send_unsubscribing_presence(From, Item) -> @@ -721,24 +730,23 @@ send_unsubscribing_presence(From, Item) -> _ -> false end, if IsTo -> - send_presence_type( - jlib:jid_remove_resource(From), - jlib:make_jid(Item#roster.jid), <<"unsubscribe">>); - true -> ok + send_presence_type(jlib:jid_remove_resource(From), + jlib:make_jid(Item#roster.jid), + <<"unsubscribe">>); + true -> ok end, if IsFrom -> - send_presence_type( - jlib:jid_remove_resource(From), - jlib:make_jid(Item#roster.jid), <<"unsubscribed">>); - true -> ok + send_presence_type(jlib:jid_remove_resource(From), + jlib:make_jid(Item#roster.jid), + <<"unsubscribed">>); + true -> ok end, ok. send_presence_type(From, To, Type) -> - ejabberd_router:route( - From, To, - #xmlel{name = <<"presence">>, attrs = [{<<"type">>, Type}]}). - + ejabberd_router:route(From, To, + #xmlel{name = <<"presence">>, + attrs = [{<<"type">>, Type}], children = []}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -746,45 +754,50 @@ set_items(User, Server, SubEl) -> #xmlel{children = Els} = SubEl, LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), - F = fun() -> - lists:foreach(fun(El) -> - process_item_set_t(LUser, LServer, El) - end, Els) + F = fun () -> + lists:foreach(fun (El) -> + process_item_set_t(LUser, LServer, El) end, - mnesia:transaction(F). + Els) + end, + transaction(LServer, F). + +update_roster_t(LUser, LServer, LJID, Item) -> + ?BACKEND:update_roster_t(LUser, LServer, LJID, Item). + +del_roster_t(LUser, LServer, LJID) -> + ?BACKEND:del_roster_t(LUser, LServer, LJID). -process_item_set_t(LUser, LServer, #xmlel{attrs = Attrs, - children = Els}) -> +process_item_set_t(LUser, LServer, + #xmlel{attrs = Attrs, children = Els}) -> JID1 = jlib:binary_to_jid(xml:get_attr_s(<<"jid">>, Attrs)), case JID1 of - error -> - ok; + error -> ok; _ -> - JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource}, - LJID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource}, + JID = {JID1#jid.user, JID1#jid.server, + JID1#jid.resource}, + LJID = {JID1#jid.luser, JID1#jid.lserver, + JID1#jid.lresource}, Item = #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, - jid = JID}, + us = {LUser, LServer}, jid = JID}, Item1 = process_item_attrs_ws(Item, Attrs), Item2 = process_item_els(Item1, Els), case Item2#roster.subscription of - remove -> - mnesia:delete({roster, {LUser, LServer, LJID}}); - _ -> - mnesia:write(Item2) + remove -> del_roster_t(LUser, LServer, LJID); + _ -> update_roster_t(LUser, LServer, LJID, Item2) end end; -process_item_set_t(_LUser, _LServer, _) -> - ok. +process_item_set_t(_LUser, _LServer, _) -> ok. +%%TODO remove case and match in function head process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) -> case Attr of <<"jid">> -> case jlib:binary_to_jid(Val) of - error -> - process_item_attrs_ws(Item, Attrs); + error -> process_item_attrs_ws(Item, Attrs); JID1 -> - JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource}, + JID = {JID1#jid.luser, JID1#jid.lserver, + JID1#jid.lresource}, process_item_attrs_ws(Item#roster{jid = JID}, Attrs) end; <<"name">> -> @@ -792,114 +805,48 @@ process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) -> <<"subscription">> -> case Val of <<"remove">> -> - process_item_attrs_ws(Item#roster{subscription = remove}, - Attrs); + process_item_attrs_ws(Item#roster{subscription = + remove}, + Attrs); <<"none">> -> process_item_attrs_ws(Item#roster{subscription = none}, - Attrs); + Attrs); <<"both">> -> process_item_attrs_ws(Item#roster{subscription = both}, - Attrs); + Attrs); <<"from">> -> process_item_attrs_ws(Item#roster{subscription = from}, - Attrs); + Attrs); <<"to">> -> process_item_attrs_ws(Item#roster{subscription = to}, - Attrs); - _ -> - process_item_attrs_ws(Item, Attrs) + Attrs); + _ -> process_item_attrs_ws(Item, Attrs) end; - <<"ask">> -> - process_item_attrs_ws(Item, Attrs); - _ -> - process_item_attrs_ws(Item, Attrs) + <<"ask">> -> process_item_attrs_ws(Item, Attrs); + _ -> process_item_attrs_ws(Item, Attrs) end; -process_item_attrs_ws(Item, []) -> - Item. +process_item_attrs_ws(Item, []) -> Item. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -get_jid_info(_, User, Server, JID) -> +read_subscription_and_groups(User, Server, LJID) -> LUser = jlib:nodeprep(User), - LJID = jlib:jid_tolower(JID), LServer = jlib:nameprep(Server), - case catch mnesia:dirty_read(roster, {LUser, LServer, LJID}) of - [#roster{subscription = Subscription, groups = Groups}] -> - {Subscription, Groups}; - _ -> + ?BACKEND:read_subscription_and_groups(LUser, LServer, LJID). + +get_jid_info(_, User, Server, JID) -> + LJID = jlib:jid_tolower(JID), + case read_subscription_and_groups(User, Server, LJID) of + {Subscription, Groups} -> {Subscription, Groups}; + error -> LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - if - LRJID == LJID -> - {none, []}; + if LRJID == LJID -> {none, []}; true -> - case catch mnesia:dirty_read( - roster, {LUser, LServer, LRJID}) of - [#roster{subscription = Subscription, - groups = Groups}] -> - {Subscription, Groups}; - _ -> - {none, []} + case read_subscription_and_groups(User, Server, LRJID) + of + {Subscription, Groups} -> {Subscription, Groups}; + error -> {none, []} end end end. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -update_table() -> - Fields = record_info(fields, roster), - case mnesia:table_info(roster, attributes) of - Fields -> - ok; - [uj, user, jid, name, subscription, ask, groups, xattrs, xs] -> - convert_table1(Fields); - [usj, us, jid, name, subscription, ask, groups, xattrs, xs] -> - convert_table2(Fields); - _ -> - ?INFO_MSG("Recreating roster table", []), - mnesia:transform_table(roster, ignore, Fields) - end. - - -%% Convert roster table to support virtual host -convert_table1(Fields) -> - ?INFO_MSG("Virtual host support: converting roster table from " - "{uj, user, jid, name, subscription, ask, groups, xattrs, xs} format", []), - Host = ?MYNAME, - {atomic, ok} = mnesia:create_table( - mod_roster_tmp_table, - [{disc_only_copies, [node()]}, - {type, bag}, - {local_content, true}, - {record_name, roster}, - {attributes, record_info(fields, roster)}]), - mnesia:del_table_index(roster, user), - mnesia:transform_table(roster, ignore, Fields), - F1 = fun() -> - mnesia:write_lock_table(mod_roster_tmp_table), - mnesia:foldl( - fun(#roster{usj = {U, JID}, us = U} = R, _) -> - mnesia:dirty_write( - mod_roster_tmp_table, - R#roster{usj = {U, Host, JID}, - us = {U, Host}}) - end, ok, roster) - end, - mnesia:transaction(F1), - mnesia:clear_table(roster), - F2 = fun() -> - mnesia:write_lock_table(roster), - mnesia:foldl( - fun(R, _) -> - mnesia:dirty_write(R) - end, ok, mod_roster_tmp_table) - end, - mnesia:transaction(F2), - mnesia:delete_table(mod_roster_tmp_table). - - -%% Convert roster table: xattrs fields become -convert_table2(Fields) -> - ?INFO_MSG("Converting roster table from " - "{usj, us, jid, name, subscription, ask, groups, xattrs, xs} format", []), - mnesia:transform_table(roster, ignore, Fields). diff --git a/apps/ejabberd/src/mod_roster_mnesia.erl b/apps/ejabberd/src/mod_roster_mnesia.erl new file mode 100644 index 00000000000..5e8b08309b9 --- /dev/null +++ b/apps/ejabberd/src/mod_roster_mnesia.erl @@ -0,0 +1,124 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_roster_mnesia.erl +%%% Author : Michał Piotrowski +%%% Purpose : mod_last mnesia backend (XEP-0012) +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2014 ProcessOne +%%% MongooseIM, Copyright (C) 2015 Erlang Solutions Ltd. +%%% +%%%---------------------------------------------------------------------- +-module(mod_roster_mnesia). + +-include("mod_roster.hrl"). +-include("jlib.hrl"). + +%% API +-export([init/2, + read_roster_version/2, + write_roster_version/4, + get_roster/2, + get_roster_by_jid_t/3, + get_subscription_lists/3, + roster_subscribe_t/4, + get_roster_by_jid_with_groups_t/3, + remove_user/2, + update_roster_t/4, + del_roster_t/3, + read_subscription_and_groups/3]). + +-export([raw_to_record/2]). + +-spec init(ejabberd:server(), list()) -> no_return(). +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)}]), + mnesia:add_table_index(roster, us), + mnesia:add_table_index(roster_version, us). + +-spec read_roster_version(ejabberd:luser(), ejabberd:lserver()) + -> binary() | error. +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) -> + US = {LUser, LServer}, + case catch mnesia:dirty_index_read(roster, US, + #roster.us) + of + Items when is_list(Items)-> Items; + _ -> [] + end. + +get_roster_by_jid_t(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_subscription_lists(_, LUser, LServer) -> + US = {LUser, LServer}, + case mnesia:dirty_index_read(roster, US, #roster.us) of + Items when is_list(Items) -> Items; + _ -> [] + end. + +roster_subscribe_t(_LUser, _LServer, _LJID, Item) -> + mnesia:write(Item). + +get_roster_by_jid_with_groups_t(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}, + mod_roster:send_unsubscription_to_rosteritems(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_t(_LUser, _LServer, _LJID, Item) -> + mnesia:write(Item). + +del_roster_t(LUser, LServer, LJID) -> + mnesia:delete({roster, {LUser, LServer, LJID}}). + + +read_subscription_and_groups(LUser, LServer, LJID) -> + case catch mnesia:dirty_read(roster, + {LUser, LServer, LJID}) + of + [#roster{subscription = Subscription, + groups = Groups}] -> + {Subscription, Groups}; + _ -> error + end. + +raw_to_record(_, Item) -> Item. \ No newline at end of file diff --git a/apps/ejabberd/src/mod_roster_odbc.erl b/apps/ejabberd/src/mod_roster_odbc.erl index 8b38229f788..2cdddd0a21f 100644 --- a/apps/ejabberd/src/mod_roster_odbc.erl +++ b/apps/ejabberd/src/mod_roster_odbc.erl @@ -1,951 +1,212 @@ %%%---------------------------------------------------------------------- %%% File : mod_roster_odbc.erl -%%% Author : Alexey Shchepin -%%% Purpose : Roster management -%%% Created : 15 Dec 2004 by Alexey Shchepin +%%% Author : Michał Piotrowski +%%% Purpose : mod_roster_odbc odbc backend (XEP-0012) %%% %%% -%%% ejabberd, Copyright (C) 2002-2011 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., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA +%%% ejabberd, Copyright (C) 2002-2014 ProcessOne +%%% MongooseIM, Copyright (C) 2015 Erlang Solutions Ltd. %%% %%%---------------------------------------------------------------------- -%%% @doc Roster management (Mnesia storage). -%%% -%%% Includes support for XEP-0237: Roster Versioning. -%%% The roster versioning follows an all-or-nothing strategy: -%%% - If the version supplied by the client is the latest, return an empty response. -%%% - If not, return the entire new roster (with updated version string). -%%% Roster version is a hash digest of the entire roster. -%%% No additional data is stored in DB. - -module(mod_roster_odbc). --author('alexey@process-one.net'). --behaviour(gen_mod). +-include("mod_roster.hrl"). +-include("jlib.hrl"). --export([start/2, stop/1, - process_iq/3, - process_local_iq/3, - get_user_roster/2, +%% API +-export([init/2, + read_roster_version/2, + write_roster_version/4, + get_roster/2, + get_roster_by_jid_t/3, get_subscription_lists/3, - get_in_pending_subscriptions/3, - in_subscription/6, - out_subscription/4, - set_items/3, + roster_subscribe_t/4, + get_roster_by_jid_with_groups_t/3, + update_roster_t/4, remove_user/2, - get_jid_info/4, - get_versioning_feature/2, - roster_versioning_enabled/1]). - --include("ejabberd.hrl"). --include("jlib.hrl"). --include("mod_roster.hrl"). - -start(Host, Opts) -> - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - ejabberd_hooks:add(roster_get, Host, - ?MODULE, get_user_roster, 50), - ejabberd_hooks:add(roster_in_subscription, Host, - ?MODULE, in_subscription, 50), - ejabberd_hooks:add(roster_out_subscription, Host, - ?MODULE, out_subscription, 50), - ejabberd_hooks:add(roster_get_subscription_lists, Host, - ?MODULE, get_subscription_lists, 50), - ejabberd_hooks:add(roster_get_jid_info, Host, - ?MODULE, get_jid_info, 50), - ejabberd_hooks:add(remove_user, Host, - ?MODULE, remove_user, 50), - ejabberd_hooks:add(anonymous_purge_hook, Host, - ?MODULE, remove_user, 50), - ejabberd_hooks:add(resend_subscription_requests_hook, Host, - ?MODULE, get_in_pending_subscriptions, 50), - ejabberd_hooks:add(roster_get_versioning_feature, Host, - ?MODULE, get_versioning_feature, 50), - ejabberd_hooks:add(webadmin_page_host, Host, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:add(webadmin_user, Host, - ?MODULE, webadmin_user, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER, - ?MODULE, process_iq, IQDisc). - -stop(Host) -> - ejabberd_hooks:delete(roster_get, Host, - ?MODULE, get_user_roster, 50), - ejabberd_hooks:delete(roster_in_subscription, Host, - ?MODULE, in_subscription, 50), - ejabberd_hooks:delete(roster_out_subscription, Host, - ?MODULE, out_subscription, 50), - ejabberd_hooks:delete(roster_get_subscription_lists, Host, - ?MODULE, get_subscription_lists, 50), - ejabberd_hooks:delete(roster_get_jid_info, Host, - ?MODULE, get_jid_info, 50), - ejabberd_hooks:delete(remove_user, Host, - ?MODULE, remove_user, 50), - ejabberd_hooks:delete(anonymous_purge_hook, Host, - ?MODULE, remove_user, 50), - ejabberd_hooks:delete(resend_subscription_requests_hook, Host, - ?MODULE, get_in_pending_subscriptions, 50), - ejabberd_hooks:delete(roster_get_versioning_feature, Host, - ?MODULE, get_versioning_feature, 50), - ejabberd_hooks:delete(webadmin_page_host, Host, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:delete(webadmin_user, Host, - ?MODULE, webadmin_user, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ROSTER). + del_roster_t/3, + read_subscription_and_groups/3]). +-export([raw_to_record/2]). -process_iq(From, To, IQ) -> - #iq{sub_el = SubEl} = IQ, - #jid{lserver = LServer} = From, - case lists:member(LServer, ?MYHOSTS) of - true -> - process_local_iq(From, To, IQ); - _ -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]} - end. +-spec init(ejabberd:server(), list()) -> no_return(). +init(_Host, _Opts) -> + ok. -process_local_iq(From, To, #iq{type = Type} = IQ) -> - case Type of - set -> - process_iq_set(From, To, IQ); - get -> - process_iq_get(From, To, IQ) +-spec read_roster_version(ejabberd:luser(), ejabberd:lserver()) + -> binary() | error. +read_roster_version(LUser, LServer) -> + Username = ejabberd_odbc:escape(LUser), + case odbc_queries:get_roster_version(LServer, Username) + of + {selected, [<<"version">>], [[Version]]} -> Version; + {selected, [<<"version">>], []} -> error end. -roster_hash(Items) -> - sha:sha1_hex(term_to_binary( - lists:sort( - [R#roster{groups = lists:sort(Grs)} || - R = #roster{groups = Grs} <- Items]))). - -roster_versioning_enabled(Host) -> - gen_mod:get_module_opt(Host, ?MODULE, versioning, false). - -roster_version_on_db(Host) -> - gen_mod:get_module_opt(Host, ?MODULE, store_current_id, false). - -%% Returns a list that may contain an xmlel with the XEP-237 feature if it's enabled. -get_versioning_feature(Acc, Host) -> - case roster_versioning_enabled(Host) of +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 -> - Feature = #xmlel{name = <<"ver">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER_VER}], - children = [#xmlel{name = <<"optional">>}]}, - [Feature | Acc]; - false -> [] + odbc_queries:sql_transaction(LServer, + fun () -> + odbc_queries:set_roster_version(Username, + EVer) + end) end. -roster_version(LServer ,LUser) -> - US = {LUser, LServer}, - case roster_version_on_db(LServer) of - true -> - case odbc_queries:get_roster_version(ejabberd_odbc:escape(LServer), - ejabberd_odbc:escape(LUser)) of - {selected, [<<"version">>], [{Version}]} -> Version; - {selected, [<<"version">>], []} -> not_found - end; - false -> - roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US])) - end. - -%% Load roster from DB only if neccesary. -%% It is neccesary if -%% - roster versioning is disabled in server OR -%% - roster versioning is not used by the client OR -%% - roster versioning is used by server and client, BUT the server isn't storing versions on db OR -%% - the roster version from client don't match current version. -process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) -> - LUser = From#jid.luser, - LServer = From#jid.lserver, - US = {LUser, LServer}, - - try - {ItemsToSend, VersionToSend} = - case {xml:get_tag_attr(<<"ver">>, SubEl), - roster_versioning_enabled(LServer), - roster_version_on_db(LServer)} of - {{value, RequestedVersion}, true, true} -> - %% Retrieve version from DB. Only load entire roster - %% when neccesary. - case odbc_queries:get_roster_version(ejabberd_odbc:escape(LServer), - ejabberd_odbc:escape(LUser)) of - {selected, [<<"version">>], [{RequestedVersion}]} -> - {false, false}; - {selected, [<<"version">>], [{NewVersion}]} -> - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), NewVersion}; - {selected, [<<"version">>], []} -> - RosterVersion = sha:sha1_hex(term_to_binary(now())), - {atomic, {updated,1}} = odbc_queries:sql_transaction(LServer, - fun() -> - odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), RosterVersion) - end), - - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), RosterVersion} - end; - - {{value, RequestedVersion}, true, false} -> - RosterItems = ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [] , [US]), - case roster_hash(RosterItems) of - RequestedVersion -> - {false, false}; - New -> - {lists:map(fun item_to_xml/1, RosterItems), New} - end; - - _ -> - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), false} - end, - IQ#iq{type = result, sub_el = case {ItemsToSend, VersionToSend} of - {false, false} -> []; - {Items, false} -> [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER}], - children = Items}]; - {Items, Version} -> [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER}, - {<<"ver">>, Version}], - children = Items}] - end} - catch - _:_ -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]} - end. - -get_user_roster(Acc, {LUser, LServer}) -> - Items = get_roster(LUser, LServer), - lists:filter(fun(#roster{subscription = none, ask = in}) -> - false; - (_) -> - true - end, Items) ++ Acc. - get_roster(LUser, LServer) -> Username = ejabberd_odbc:escape(LUser), case catch odbc_queries:get_roster(LServer, Username) of - {selected, [<<"username">>, <<"jid">>, <<"nick">>, <<"subscription">>, <<"ask">>, - <<"askmessage">>, <<"server">>, <<"subscribe">>, <<"type">>], - Items} when is_list(Items) -> - JIDGroups = case catch odbc_queries:get_roster_jid_groups(LServer, Username) of + {selected, + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + Items} + when is_list(Items) -> + JIDGroups = case catch + odbc_queries:get_roster_jid_groups(LServer, + Username) + of {selected, [<<"jid">>, <<"grp">>], JGrps} - when is_list(JGrps) -> + when is_list(JGrps) -> JGrps; - _ -> - [] + _ -> [] end, - RItems = lists:flatmap( - fun(I) -> - case raw_to_record(LServer, I) of - %% Bad JID in database: - error -> - []; - R -> - SJID = jlib:jid_to_binary(R#roster.jid), - Groups = lists:flatmap( - fun({S, G}) when S == SJID -> - [G]; - (_) -> - [] - end, JIDGroups), - [R#roster{groups = Groups}] - end - end, Items), + 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 = + jlib:jid_to_binary(R#roster.jid), + Groups = case dict:find(SJID, + GroupsDict) + of + {ok, Gs} -> Gs; + error -> [] + end, + [R#roster{groups = Groups}] + end + end, + Items), RItems; - _ -> - [] + _ -> [] end. - -item_to_xml(Item) -> - Attrs1 = [{"jid", jlib:jid_to_binary(Item#roster.jid)}], - Attrs2 = case Item#roster.name of - <<"">> -> - Attrs1; - Name -> - [{<<"name">>, Name} | Attrs1] - end, - Attrs3 = case Item#roster.subscription of - none -> - [{<<"subscription">>, <<"none">>} | Attrs2]; - from -> - [{<<"subscription">>, <<"from">>} | Attrs2]; - to -> - [{<<"subscription">>, <<"to">>} | Attrs2]; - both -> - [{<<"subscription">>, <<"both">>} | Attrs2]; - remove -> - [{<<"subscription">>, <<"remove">>} | Attrs2] - end, - Attrs = case ask_to_pending(Item#roster.ask) of - out -> - [{<<"ask">>, <<"subscribe">>} | Attrs3]; - both -> - [{<<"ask">>, <<"subscribe">>} | Attrs3]; +get_roster_by_jid_t(LUser, LServer, LJID) -> + Username = ejabberd_odbc:escape(LUser), + SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), + {selected, + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + Res} = + odbc_queries:get_roster_by_jid(LServer, Username, SJID), + 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}; _ -> - Attrs3 - end, - SubEls = lists:map(fun(G) -> - #xmlel{name = <<"group">>, - children = [#xmlcdata{content = G}]} - end, Item#roster.groups), - #xmlel{name = <<"item">>, attrs = Attrs, children = SubEls}. - -process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) -> - #xmlel{children = Els} = SubEl, - #jid{lserver = LServer} = From, - ejabberd_hooks:run(roster_set, LServer, [From, To, SubEl]), - lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els), - IQ#iq{type = result, sub_el = []}. - -process_item_set(From, To, #xmlel{attrs = Attrs, children = Els}) -> - JID1 = jlib:binary_to_jid(xml:get_attr_s(<<"jid">>, Attrs)), - #jid{user = User, luser = LUser, lserver = LServer} = From, - case JID1 of - error -> - ok; - _ -> - LJID = jlib:jid_tolower(JID1), - Username = ejabberd_odbc:escape(LUser), - SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), - F = fun() -> - {selected, - [<<"username">>, <<"jid">>, <<"nick">>, <<"subscription">>, - <<"ask">>, <<"askmessage">>, <<"server">>, <<"subscribe">>, <<"type">>], - Res} = odbc_queries:get_roster_by_jid(LServer, Username, SJID), - Item = 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, - Item1 = process_item_attrs(Item, Attrs), - Item2 = process_item_els(Item1, Els), - case Item2#roster.subscription of - remove -> - odbc_queries:del_roster(LServer, Username, SJID); - _ -> - ItemVals = record_to_string(Item2), - ItemGroups = groups_to_string(Item2), - odbc_queries:update_roster(LServer, Username, SJID, ItemVals, ItemGroups) - end, - %% If the item exist in shared roster, take the - %% subscription information from there: - Item3 = ejabberd_hooks:run_fold(roster_process_item, - LServer, Item2, [LServer]), - case roster_version_on_db(LServer) of - true -> odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), sha:sha1_hex(term_to_binary(now()))); - false -> ok - end, - {Item, Item3} - end, - case odbc_queries:sql_transaction(LServer, F) of - {atomic, {OldItem, Item}} -> - push_item(User, LServer, To, Item), - case Item#roster.subscription of - remove -> - send_unsubscribing_presence(From, OldItem), - ok; - _ -> - ok - end; - E -> - ?DEBUG("ROSTER: roster item set error: ~p~n", [E]), - ok + R#roster{usj = {LUser, LServer, LJID}, + us = {LUser, LServer}, jid = LJID, name = <<"">>} end - end; -process_item_set(_From, _To, _) -> - ok. - -process_item_attrs(Item, [{<<"jid">>, Val} | Attrs]) -> - case jlib:binary_to_jid(Val) of - error -> - process_item_attrs(Item, Attrs); - JID1 -> - JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource}, - process_item_attrs(Item#roster{jid = JID}, Attrs) - end; -process_item_attrs(Item, [{<<"name">>, Val} | Attrs]) -> - process_item_attrs(Item#roster{name = Val}, Attrs); -process_item_attrs(Item, [{<<"subscription">>, <<"remove">>} | Attrs]) -> - process_item_attrs(Item#roster{subscription = remove}, Attrs); -process_item_attrs(Item, [_ | Attrs]) -> - process_item_attrs(Item, Attrs); -process_item_attrs(Item, []) -> - Item. - -process_item_els(Item, [#xmlel{name = <<"group">>, - children = SEls} | Els]) -> - Groups = [xml:get_cdata(SEls) | Item#roster.groups], - process_item_els(Item#roster{groups = Groups}, Els); -process_item_els(Item, [#xmlel{} | Els]) -> - process_item_els(Item, Els); -process_item_els(Item, [#xmlcdata{} | Els]) -> - process_item_els(Item, Els); -process_item_els(Item, []) -> - Item. - -push_item(User, Server, From, Item) -> - ejabberd_sm:route(jlib:make_jid(<<"">>, <<"">>, <<"">>), - jlib:make_jid(User, Server, <<"">>), - #xmlel{name = <<"broadcast">>, - children = [{item, - Item#roster.jid, - Item#roster.subscription}]}), - case roster_versioning_enabled(Server) of - true -> - push_item_version(Server, User, From, Item, roster_version(Server, User)); - false -> - lists:foreach(fun(Resource) -> - push_item(User, Server, Resource, From, Item) - end, ejabberd_sm:get_user_resources(User, Server)) end. -%% TODO: don't push to those who not load roster -push_item(User, Server, Resource, From, Item) -> - ejabberd_hooks:run(roster_push, Server, [From, Item]), - ResIQ = #iq{type = set, xmlns = ?NS_ROSTER, - id = list_to_binary("push" ++ randoms:get_string()), - sub_el = [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER}], - children = [item_to_xml(Item)]}]}, - ejabberd_router:route( - From, - jlib:make_jid(User, Server, Resource), - jlib:iq_to_xml(ResIQ)). - -%% @doc Roster push, calculate and include the version attribute. -%% TODO: don't push to those who didn't load roster -push_item_version(Server, User, From, Item, RosterVersion) -> - lists:foreach(fun(Resource) -> - push_item_version(User, Server, Resource, From, Item, RosterVersion) - end, ejabberd_sm:get_user_resources(User, Server)). - -push_item_version(User, Server, Resource, From, Item, RosterVersion) -> - IQPush = #iq{type = 'set', xmlns = ?NS_ROSTER, - id = list_to_binary("push" ++ randoms:get_string()), - sub_el = [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER}, - {<<"ver">>, RosterVersion}], - children = [item_to_xml(Item)]}]}, - ejabberd_router:route( - From, - jlib:make_jid(User, Server, Resource), - jlib:iq_to_xml(IQPush)). - -get_subscription_lists(_, User, Server) -> - LUser = jlib:nodeprep(User), - LServer = jlib:nameprep(Server), - JID = jlib:make_jid(User, Server, <<>>), +get_subscription_lists(_, LUser, LServer) -> Username = ejabberd_odbc:escape(LUser), case catch odbc_queries:get_roster(LServer, Username) of - {selected, [<<"username">>, <<"jid">>, <<"nick">>, <<"subscription">>, <<"ask">>, - <<"askmessage">>, <<"server">>, <<"subscribe">>, <<"type">>], - Items} when is_list(Items) -> - fill_subscription_lists(JID, LServer, Items, [], [], []); - _ -> - {[], [], []} + {selected, + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + Items} + when is_list(Items) -> + Items; + _ -> [] end. -fill_subscription_lists(JID, LServer, [IRaw | Is], F, T, P) -> - I = raw_to_record(LServer, IRaw), - J = element(3, I#roster.usj), - NewP = case I#roster.ask of - Ask when Ask == in; Ask == both -> - Message = I#roster.askmessage, - Status = if is_binary(Message) -> Message; - true -> <<>> - end, - StatusEl = #xmlel{ - name = <<"status">>, - children = [#xmlcdata{content = Status}]}, - El = #xmlel{ - name = <<"presence">>, - attrs = [{<<"from">>, jlib:jid_to_binary(I#roster.jid)}, - {<<"to">>, jlib:jid_to_binary(JID)}, - {<<"type">>, <<"subscribe">>}], - children = [StatusEl]}, - [El | P]; - _ -> - P - end, - case I#roster.subscription of - both -> - fill_subscription_lists(JID, LServer, Is, [J | F], [J | T], NewP); - from -> - fill_subscription_lists(JID, LServer, Is, [J | F], T, NewP); - to -> - fill_subscription_lists(JID, LServer, Is, F, [J | T], NewP); - _ -> - fill_subscription_lists(JID, LServer, Is, F, T, NewP) - end; -fill_subscription_lists(_JID, _LServer, [], F, T, P) -> - {F, T, P}. - -ask_to_pending(subscribe) -> out; -ask_to_pending(unsubscribe) -> none; -ask_to_pending(Ask) -> Ask. - -in_subscription(_, User, Server, JID, Type, Reason) -> - process_subscription(in, User, Server, JID, Type, Reason). - -out_subscription(User, Server, JID, Type) -> - process_subscription(out, User, Server, JID, Type, []). +roster_subscribe_t(LUser, LServer, LJID, Item) -> + ItemVals = record_to_string(Item), + Username = ejabberd_odbc:escape(LUser), + SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), + odbc_queries:roster_subscribe(LServer, Username, SJID, + ItemVals). -process_subscription(Direction, User, Server, JID1, Type, Reason) -> - LUser = jlib:nodeprep(User), - LServer = jlib:nameprep(Server), - LJID = jlib:jid_tolower(JID1), +get_roster_by_jid_with_groups_t(LUser, LServer, LJID) -> Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), - F = fun() -> - Item = - case odbc_queries:get_roster_by_jid(LServer, Username, SJID) of - {selected, - [<<"username">>, <<"jid">>, <<"nick">>, <<"subscription">>, <<"ask">>, - <<"askmessage">>, <<"server">>, <<"subscribe">>, <<"type">>], - [I]} -> - %% raw_to_record can return error, but - %% jlib_to_string would fail before this point - R = raw_to_record(LServer, I), - Groups = - case odbc_queries:get_roster_groups(LServer, Username, SJID) of - {selected, ["grp"], JGrps} when is_list(JGrps) -> - [JGrp || {JGrp} <- JGrps]; - _ -> - [] - end, - R#roster{groups = Groups}; - {selected, - [<<"username">>, <<"jid">>, <<"nick">>, <<"subscription">>, <<"ask">>, - <<"askmessage">>, <<"server">>, <<"subscribe">>, <<"type">>], - []} -> - #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, - jid = LJID} - end, - NewState = case Direction of - out -> - out_state_change(Item#roster.subscription, - Item#roster.ask, - Type); - in -> - in_state_change(Item#roster.subscription, - Item#roster.ask, - Type) - end, - AutoReply = case Direction of - out -> - none; - in -> - in_auto_reply(Item#roster.subscription, - Item#roster.ask, - Type) - end, - AskMessage = case NewState of - {_, both} -> Reason; - {_, in} -> Reason; - _ -> <<>> - end, - case NewState of - none -> - {none, AutoReply}; - {none, none} when Item#roster.subscription == none, - Item#roster.ask == in -> - odbc_queries:del_roster(LServer, Username, SJID), - {none, AutoReply}; - {Subscription, Pending} -> - NewItem = Item#roster{subscription = Subscription, - ask = Pending, - askmessage = AskMessage}, - ItemVals = record_to_string(NewItem), - odbc_queries:roster_subscribe(LServer, Username, SJID, ItemVals), - case roster_version_on_db(LServer) of - true -> odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), sha:sha1_hex(term_to_binary(now()))); - false -> ok - end, - {{push, NewItem}, AutoReply} - end - end, - case odbc_queries:sql_transaction(LServer, F) of - {atomic, {Push, AutoReply}} -> - case AutoReply of - none -> - ok; - _ -> - T = case AutoReply of - subscribed -> <<"subscribed">>; - unsubscribed -> <<"unsubscribed">> - end, - ejabberd_router:route( - jlib:make_jid(User, Server, <<>>), JID1, - #xmlel{name = <<"presence">>, attrs = [{<<"type">>, T}]}) - end, - case Push of - {push, Item} -> - if - Item#roster.subscription == none, - Item#roster.ask == in -> - ok; - true -> - push_item(User, Server, - jlib:make_jid(User, Server, <<>>), Item) - end, - true; - none -> - false - end; - _ -> - false + case odbc_queries:get_roster_by_jid(LServer, Username, + SJID) + of + {selected, + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + [I]} -> + R = raw_to_record(LServer, I), + Groups = case odbc_queries:get_roster_groups(LServer, + Username, SJID) + of + {selected, [<<"grp">>], JGrps} when is_list(JGrps) -> + [JGrp || [JGrp] <- JGrps]; + _ -> [] + end, + R#roster{groups = Groups}; + {selected, + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + []} -> + #roster{usj = {LUser, LServer, LJID}, + us = {LUser, LServer}, jid = LJID} end. -%% in_state_change(Subscription, Pending, Type) -> NewState -%% NewState = none | {NewSubscription, NewPending} --ifdef(ROSTER_GATEWAY_WORKAROUND). --define(NNSD, {to, none}). --define(NISD, {to, in}). --else. --define(NNSD, none). --define(NISD, none). --endif. - -in_state_change(none, none, subscribe) -> {none, in}; -in_state_change(none, none, subscribed) -> ?NNSD; -in_state_change(none, none, unsubscribe) -> none; -in_state_change(none, none, unsubscribed) -> none; -in_state_change(none, out, subscribe) -> {none, both}; -in_state_change(none, out, subscribed) -> {to, none}; -in_state_change(none, out, unsubscribe) -> none; -in_state_change(none, out, unsubscribed) -> {none, none}; -in_state_change(none, in, subscribe) -> none; -in_state_change(none, in, subscribed) -> ?NISD; -in_state_change(none, in, unsubscribe) -> {none, none}; -in_state_change(none, in, unsubscribed) -> none; -in_state_change(none, both, subscribe) -> none; -in_state_change(none, both, subscribed) -> {to, in}; -in_state_change(none, both, unsubscribe) -> {none, out}; -in_state_change(none, both, unsubscribed) -> {none, in}; -in_state_change(to, none, subscribe) -> {to, in}; -in_state_change(to, none, subscribed) -> none; -in_state_change(to, none, unsubscribe) -> none; -in_state_change(to, none, unsubscribed) -> {none, none}; -in_state_change(to, in, subscribe) -> none; -in_state_change(to, in, subscribed) -> none; -in_state_change(to, in, unsubscribe) -> {to, none}; -in_state_change(to, in, unsubscribed) -> {none, in}; -in_state_change(from, none, subscribe) -> none; -in_state_change(from, none, subscribed) -> {both, none}; -in_state_change(from, none, unsubscribe) -> {none, none}; -in_state_change(from, none, unsubscribed) -> none; -in_state_change(from, out, subscribe) -> none; -in_state_change(from, out, subscribed) -> {both, none}; -in_state_change(from, out, unsubscribe) -> {none, out}; -in_state_change(from, out, unsubscribed) -> {from, none}; -in_state_change(both, none, subscribe) -> none; -in_state_change(both, none, subscribed) -> none; -in_state_change(both, none, unsubscribe) -> {to, none}; -in_state_change(both, none, unsubscribed) -> {from, none}. - -out_state_change(none, none, subscribe) -> {none, out}; -out_state_change(none, none, subscribed) -> none; -out_state_change(none, none, unsubscribe) -> none; -out_state_change(none, none, unsubscribed) -> none; -out_state_change(none, out, subscribe) -> {none, out}; %% We need to resend query (RFC3921, section 9.2) -out_state_change(none, out, subscribed) -> none; -out_state_change(none, out, unsubscribe) -> {none, none}; -out_state_change(none, out, unsubscribed) -> none; -out_state_change(none, in, subscribe) -> {none, both}; -out_state_change(none, in, subscribed) -> {from, none}; -out_state_change(none, in, unsubscribe) -> none; -out_state_change(none, in, unsubscribed) -> {none, none}; -out_state_change(none, both, subscribe) -> none; -out_state_change(none, both, subscribed) -> {from, out}; -out_state_change(none, both, unsubscribe) -> {none, in}; -out_state_change(none, both, unsubscribed) -> {none, out}; -out_state_change(to, none, subscribe) -> none; -out_state_change(to, none, subscribed) -> {both, none}; -out_state_change(to, none, unsubscribe) -> {none, none}; -out_state_change(to, none, unsubscribed) -> none; -out_state_change(to, in, subscribe) -> none; -out_state_change(to, in, subscribed) -> {both, none}; -out_state_change(to, in, unsubscribe) -> {none, in}; -out_state_change(to, in, unsubscribed) -> {to, none}; -out_state_change(from, none, subscribe) -> {from, out}; -out_state_change(from, none, subscribed) -> none; -out_state_change(from, none, unsubscribe) -> none; -out_state_change(from, none, unsubscribed) -> {none, none}; -out_state_change(from, out, subscribe) -> none; -out_state_change(from, out, subscribed) -> none; -out_state_change(from, out, unsubscribe) -> {from, none}; -out_state_change(from, out, unsubscribed) -> {none, out}; -out_state_change(both, none, subscribe) -> none; -out_state_change(both, none, subscribed) -> none; -out_state_change(both, none, unsubscribe) -> {from, none}; -out_state_change(both, none, unsubscribed) -> {to, none}. - -in_auto_reply(from, none, subscribe) -> subscribed; -in_auto_reply(from, out, subscribe) -> subscribed; -in_auto_reply(both, none, subscribe) -> subscribed; -in_auto_reply(none, in, unsubscribe) -> unsubscribed; -in_auto_reply(none, both, unsubscribe) -> unsubscribed; -in_auto_reply(to, in, unsubscribe) -> unsubscribed; -in_auto_reply(from, none, unsubscribe) -> unsubscribed; -in_auto_reply(from, out, unsubscribe) -> unsubscribed; -in_auto_reply(both, none, unsubscribe) -> unsubscribed; -in_auto_reply(_, _, _) -> none. - - -remove_user(User, Server) -> - LUser = jlib:nodeprep(User), - LServer = jlib:nameprep(Server), +remove_user(LUser, LServer) -> Username = ejabberd_odbc:escape(LUser), - send_unsubscription_to_rosteritems(LUser, LServer), + mod_roster:send_unsubscription_to_rosteritems(LUser, LServer), odbc_queries:del_user_roster_t(LServer, Username), ok. -%% For each contact with Subscription: -%% Both or From, send a "unsubscribed" presence stanza; -%% Both or To, send a "unsubscribe" presence stanza. -send_unsubscription_to_rosteritems(LUser, LServer) -> - RosterItems = get_user_roster([], {LUser, LServer}), - From = jlib:make_jid({LUser, LServer, <<>>}), - lists:foreach(fun(RosterItem) -> - send_unsubscribing_presence(From, RosterItem) - end, - RosterItems). - -%% @spec (From::jid(), Item::roster()) -> ok -send_unsubscribing_presence(From, Item) -> - IsTo = case Item#roster.subscription of - both -> true; - to -> true; - _ -> false - end, - IsFrom = case Item#roster.subscription of - both -> true; - from -> true; - _ -> false - end, - if IsTo -> - send_presence_type( - jlib:jid_remove_resource(From), - jlib:make_jid(Item#roster.jid), <<"unsubscribe">>); - true -> ok - end, - if IsFrom -> - send_presence_type( - jlib:jid_remove_resource(From), - jlib:make_jid(Item#roster.jid), <<"unsubscribed">>); - true -> ok - end, - ok. - -send_presence_type(From, To, Type) -> - ejabberd_router:route( - From, To, - #xmlel{name = <<"presence">>, attrs = [{<<"type">>, Type}]}). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -set_items(User, Server, SubEl) -> - #xmlel{children = Els} = SubEl, - LUser = jlib:nodeprep(User), - LServer = jlib:nameprep(Server), - catch odbc_queries:sql_transaction( - LServer, - lists:flatmap(fun(El) -> - process_item_set_t(LUser, LServer, El) - end, Els)). - -process_item_set_t(LUser, LServer, #xmlel{attrs = Attrs, - children = Els}) -> - JID1 = jlib:binary_to_jid(xml:get_attr_s(<<"jid">>, Attrs)), - case JID1 of - error -> - []; - _ -> - LJID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource}, - Username = ejabberd_odbc:escape(LUser), - SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), - Item = #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, - jid = LJID}, - Item1 = process_item_attrs_ws(Item, Attrs), - Item2 = process_item_els(Item1, Els), - case Item2#roster.subscription of - remove -> - odbc_queries:del_roster_sql(Username, SJID); - _ -> - ItemVals = record_to_string(Item1), - ItemGroups = groups_to_string(Item2), - odbc_queries:update_roster_sql(Username, SJID, ItemVals, ItemGroups) - end - end; -process_item_set_t(_LUser, _LServer, _) -> - []. - -process_item_attrs_ws(Item, [{<<"jid">>, Val} | Attrs]) -> - case jlib:binary_to_jid(Val) of - error -> - process_item_attrs_ws(Item, Attrs); - JID1 -> - JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource}, - process_item_attrs_ws(Item#roster{jid = JID}, Attrs) - end; -process_item_attrs_ws(Item, [{<<"name">>, Val} | Attrs]) -> - process_item_attrs_ws(Item#roster{name = Val}, Attrs); -process_item_attrs_ws(Item, [{<<"subscription">>, <<"remove">>} | Attrs]) -> - process_item_attrs_ws(Item#roster{subscription = remove}, Attrs); -process_item_attrs_ws(Item, [{<<"subscription">>, <<"none">>} | Attrs]) -> - process_item_attrs_ws(Item#roster{subscription = none}, Attrs); -process_item_attrs_ws(Item, [{<<"subscription">>, <<"both">>} | Attrs]) -> - process_item_attrs_ws(Item#roster{subscription = both}, Attrs); -process_item_attrs_ws(Item, [{<<"subscription">>, <<"from">>} | Attrs]) -> - process_item_attrs_ws(Item#roster{subscription = from}, Attrs); -process_item_attrs_ws(Item, [{<<"subscription">>, <<"to">>} | Attrs]) -> - process_item_attrs_ws(Item#roster{subscription = to}, Attrs); -process_item_attrs_ws(Item, [_ | Attrs]) -> - process_item_attrs_ws(Item, Attrs); -process_item_attrs_ws(Item, []) -> - Item. - -get_in_pending_subscriptions(Ls, User, Server) -> - JID = jlib:make_jid(User, Server, <<"">>), - LUser = JID#jid.luser, - LServer = JID#jid.lserver, +update_roster_t(LUser, LServer, LJID, Item) -> Username = ejabberd_odbc:escape(LUser), - case catch odbc_queries:get_roster(LServer, Username) of - {selected, [<<"username">>, <<"jid">>, <<"nick">>, <<"subscription">>, <<"ask">>, - <<"askmessage">>, <<"server">>, <<"subscribe">>, <<"type">>], - Items} when is_list(Items) -> - Ls ++ lists:map( - fun(R) -> - Message = R#roster.askmessage, - #xmlel{name = <<"presence">>, - attrs = [{<<"from">>, jlib:jid_to_binary(R#roster.jid)}, - {<<"to">>, jlib:jid_to_binary(JID)}, - {<<"type">>, <<"subscribe">>}], - children = [#xmlel{name = <<"status">>, - children = [#xmlcdata{content = 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. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), + ItemVals = record_to_string(Item), + ItemGroups = groups_to_string(Item), + odbc_queries:update_roster(LServer, Username, SJID, ItemVals, ItemGroups). -get_jid_info(_, User, Server, JID) -> - LUser = jlib:nodeprep(User), - LServer = jlib:nameprep(Server), - LJID = jlib:jid_tolower(JID), +del_roster_t(LUser, LServer, LJID) -> Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), - case catch odbc_queries:get_subscription(LServer, Username, SJID) of - {selected, [<<"subscription">>], [{SSubscription}]} -> - Subscription = case SSubscription of - <<"B">> -> both; - <<"T">> -> to; - <<"F">> -> from; - _ -> none - end, - Groups = case catch odbc_queries:get_rostergroup_by_jid(LServer, Username, SJID) of - {selected, ["grp"], JGrps} when is_list(JGrps) -> - [JGrp || {JGrp} <- JGrps]; - _ -> - [] - end, - {Subscription, Groups}; - _ -> - LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - if - LRJID == LJID -> - {none, []}; - true -> - SRJID = ejabberd_odbc:escape(jlib:jid_to_binary(LRJID)), - case catch odbc_queries:get_subscription(LServer, Username, SRJID) of - {selected, [<<"subscription">>], [{SSubscription}]} -> - Subscription = case SSubscription of - <<"B">> -> both; - <<"T">> -> to; - <<"F">> -> from; - _ -> none - end, - Groups = case catch odbc_queries:get_rostergroup_by_jid(LServer, Username, SRJID) of - {selected, ["grp"], JGrps} when is_list(JGrps) -> - [JGrp || {JGrp} <- JGrps]; - _ -> - [] - end, - {Subscription, Groups}; - _ -> - {none, []} - end - end - end. + odbc_queries:del_roster(LServer, Username, SJID). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -raw_to_record(LServer, {User, BJID, Nick, BSubscription, BAsk, AskMessage, - _Server, _Subscribe, _Type}) -> - case jlib:binary_to_jid(BJID) of - error -> - error; +raw_to_record(LServer, + {User, SJID, Nick, SSubscription, SAsk, SAskMessage, + _SServer, _SSubscribe, _SType}) -> + case jlib:binary_to_jid(SJID) of + error -> error; JID -> LJID = jlib:jid_tolower(JID), - Subscription = case BSubscription of + Subscription = case SSubscription of <<"B">> -> both; <<"T">> -> to; <<"F">> -> from; _ -> none end, - Ask = case BAsk of + Ask = case SAsk of <<"S">> -> subscribe; <<"U">> -> unsubscribe; <<"B">> -> both; @@ -954,51 +215,73 @@ raw_to_record(LServer, {User, BJID, Nick, BSubscription, BAsk, AskMessage, _ -> none end, #roster{usj = {User, LServer, LJID}, - us = {User, LServer}, - jid = LJID, - name = Nick, - subscription = Subscription, - ask = Ask, - askmessage = AskMessage} + us = {User, LServer}, jid = LJID, name = Nick, + subscription = Subscription, ask = Ask, + askmessage = SAskMessage} end. +read_subscription_and_groups(LUser, LServer, LJID) -> + Username = ejabberd_odbc:escape(LUser), + SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), + case catch odbc_queries:get_subscription(LServer, + Username, SJID) + of + {selected, [<<"subscription">>], [{SSubscription}]} -> + Subscription = case SSubscription of + <<"B">> -> both; + <<"T">> -> to; + <<"F">> -> from; + _ -> none + end, + Groups = case catch + odbc_queries:get_rostergroup_by_jid(LServer, Username, + SJID) + of + {selected, [<<"grp">>], JGrps} when is_list(JGrps) -> + [JGrp || [JGrp] <- JGrps]; + _ -> [] + end, + {Subscription, Groups}; + _ -> error + end. + +%%============================================================================== +%% Helper functions +%%============================================================================== + record_to_string(#roster{us = {User, _Server}, - jid = JID, - name = Name, - subscription = Subscription, - ask = Ask, - askmessage = AskMessage}) -> + jid = JID, name = Name, subscription = Subscription, + ask = Ask, askmessage = AskMessage}) -> Username = ejabberd_odbc:escape(User), - SJID = ejabberd_odbc:escape(jlib:jid_to_binary(jlib:jid_tolower(JID))), + SJID = + ejabberd_odbc:escape(jlib:jid_to_binary(jlib:jid_tolower(JID))), Nick = ejabberd_odbc:escape(Name), SSubscription = case Subscription of - both -> "B"; - to -> "T"; - from -> "F"; - none -> "N" + 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" + 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"]. + [Username, SJID, Nick, SSubscription, SAsk, SAskMessage, + <<"N">>, <<"">>, <<"item">>]. groups_to_string(#roster{us = {User, _Server}, - jid = JID, - groups = Groups}) -> + jid = JID, groups = Groups}) -> Username = ejabberd_odbc:escape(User), - SJID = ejabberd_odbc:escape(jlib:jid_to_binary(jlib:jid_tolower(JID))), - - %% Empty groups do not need to be converted to string to be inserted in - %% the database - lists:foldl( - fun([], Acc) -> Acc; - (Group, Acc) -> - G = ejabberd_odbc:escape(Group), - [[Username, SJID, G]|Acc] end, [], Groups). - + SJID = + ejabberd_odbc:escape(jlib:jid_to_binary(jlib:jid_tolower(JID))), + lists:foldl(fun (<<"">>, Acc) -> Acc; + (Group, Acc) -> + G = ejabberd_odbc:escape(Group), + [[Username, SJID, G] | Acc] + end, + [], Groups). From aac94d2e7910045ce73b85c8436d7364ac5e224c Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Mon, 19 Jan 2015 15:44:24 +0100 Subject: [PATCH 2/7] udapte .cfg file --- rel/files/ejabberd.cfg | 2 -- 1 file changed, 2 deletions(-) diff --git a/rel/files/ejabberd.cfg b/rel/files/ejabberd.cfg index c1410d8ddba..5c7b5655984 100755 --- a/rel/files/ejabberd.cfg +++ b/rel/files/ejabberd.cfg @@ -570,8 +570,6 @@ %% %% Modules enabled in all ejabberd virtual hosts. %% For list of possible modules options, check documentation. -%% If module comes in two versions, like mod_roster and mod_roster_odbc, -%% use only one of them. %% {modules, [ From 90233785d6f5e0987a873d9eceea44bee660b6e3 Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Mon, 19 Jan 2015 15:49:18 +0100 Subject: [PATCH 3/7] introduce mod_roster_backend --- apps/ejabberd/src/mod_roster.erl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/apps/ejabberd/src/mod_roster.erl b/apps/ejabberd/src/mod_roster.erl index ea9d7a0b5e6..371fe78e894 100644 --- a/apps/ejabberd/src/mod_roster.erl +++ b/apps/ejabberd/src/mod_roster.erl @@ -64,11 +64,12 @@ -type roster() :: #roster{}. --define(BACKEND, mod_roster_mnesia). +-define(BACKEND, (mod_roster_backend:backend())). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), + start_backend_module(Opts), ?BACKEND:init(Host, Opts), ejabberd_hooks:add(roster_get, Host, @@ -850,3 +851,19 @@ get_jid_info(_, User, Server, JID) -> end end. +start_backend_module(Opts) -> + Backend = gen_mod:get_opt(backend, Opts, mnesia), + {Mod, Code} = dynamic_compile:from_string(mod_roster_backend(Backend)), + code:load_binary(Mod, "mod_last_backend.erl", Code). + +-spec mod_roster_backend(atom()) -> string(). +mod_roster_backend(Backend) when is_atom(Backend) -> + lists:flatten( + ["-module(mod_roster_backend). + -export([backend/0]). + + -spec backend() -> atom(). + backend() -> + mod_roster_", + atom_to_list(Backend), + ".\n"]). From e9d6a6d815f81a8dcabcacde58b3f6e2f2afcc20 Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Tue, 20 Jan 2015 10:06:17 +0100 Subject: [PATCH 4/7] introduce generic code for backend modules --- apps/ejabberd/src/gen_mod.erl | 21 +++++++++++++++++++++ apps/ejabberd/src/mod_last.erl | 23 +---------------------- apps/ejabberd/src/mod_offline.erl | 21 +-------------------- apps/ejabberd/src/mod_privacy.erl | 21 +-------------------- apps/ejabberd/src/mod_private.erl | 21 +-------------------- apps/ejabberd/src/mod_roster.erl | 19 +------------------ apps/ejabberd/src/mod_vcard.erl | 20 +------------------- 7 files changed, 27 insertions(+), 119 deletions(-) diff --git a/apps/ejabberd/src/gen_mod.erl b/apps/ejabberd/src/gen_mod.erl index 02322587a30..de8d6f37c85 100644 --- a/apps/ejabberd/src/gen_mod.erl +++ b/apps/ejabberd/src/gen_mod.erl @@ -29,6 +29,7 @@ -export([start/0, start_module/3, + start_backend_module/2, stop_module/2, stop_module_keep_config/2, reload_module/3, @@ -104,6 +105,26 @@ start_module(Host, Module, Opts0) -> end end. +-spec start_backend_module(module(), list()) -> no_return(). +start_backend_module(Module, Opts) -> + ModuleStr = atom_to_list(Module), + BackendModuleStr = ModuleStr ++ "_backend", + Backend = gen_mod:get_opt(backend, Opts, mnesia), + {Mod, Code} = dynamic_compile:from_string(backend_code(ModuleStr, Backend)), + code:load_binary(Mod, BackendModuleStr ++ ".erl", Code). + +-spec backend_code(string(), atom()) -> string(). +backend_code(Module, Backend) when is_atom(Backend) -> + BackendModule = Module ++ "_backend", + lists:flatten( + ["-module(",BackendModule,"). + -export([backend/0]). + + -spec backend() -> atom(). + backend() ->", + Module,"_", + atom_to_list(Backend), + ".\n"]). -spec is_app_running(_) -> boolean(). is_app_running(AppName) -> diff --git a/apps/ejabberd/src/mod_last.erl b/apps/ejabberd/src/mod_last.erl index 3d8d22a6ea8..cc51e57639f 100644 --- a/apps/ejabberd/src/mod_last.erl +++ b/apps/ejabberd/src/mod_last.erl @@ -82,7 +82,7 @@ start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - start_backend_module(Opts), + gen_mod:start_backend_module(?MODULE, Opts), ?BACKEND:init(Host, Opts), gen_iq_handler:add_iq_handler(ejabberd_local, Host, @@ -109,27 +109,6 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST). -%% ------------------------------------------------------------------ -%% Dynamic modules - -start_backend_module(Opts) -> - Backend = gen_mod:get_opt(backend, Opts, mnesia), - {Mod, Code} = dynamic_compile:from_string(mod_last_backend(Backend)), - code:load_binary(Mod, "mod_last_backend.erl", Code). - --spec mod_last_backend(atom()) -> string(). -mod_last_backend(Backend) when is_atom(Backend) -> - lists:flatten( - ["-module(mod_last_backend). - -export([backend/0]). - - -spec backend() -> atom(). - backend() -> - mod_last_", - atom_to_list(Backend), - ".\n"]). - - %%% %%% Uptime of ejabberd node %%% diff --git a/apps/ejabberd/src/mod_offline.erl b/apps/ejabberd/src/mod_offline.erl index 70d8ae05e9d..8b64d185ae8 100644 --- a/apps/ejabberd/src/mod_offline.erl +++ b/apps/ejabberd/src/mod_offline.erl @@ -78,7 +78,7 @@ start(Host, Opts) -> AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages), - start_backend_module(Opts), + gen_mod:start_backend_module(?MODULE, Opts), ?BACKEND:init(Host, Opts), start_worker(Host, AccessMaxOfflineMsgs), ejabberd_hooks:add(offline_message_hook, Host, @@ -110,25 +110,6 @@ stop(Host) -> ok. -%% Dynamic modules -%% ------------------------------------------------------------------ - -start_backend_module(Opts) -> - Backend = gen_mod:get_opt(backend, Opts, mnesia), - {Mod, Code} = dynamic_compile:from_string(mod_offline_backend(Backend)), - code:load_binary(Mod, "mod_offline_backend.erl", Code). - --spec mod_offline_backend(atom()) -> string(). -mod_offline_backend(Backend) when is_atom(Backend) -> - lists:flatten( - ["-module(mod_offline_backend). - -export([backend/0]). - -spec backend() -> atom(). - backend() -> - mod_offline_", - atom_to_list(Backend), - ".\n"]). - %% Server side functions %% ------------------------------------------------------------------ diff --git a/apps/ejabberd/src/mod_privacy.erl b/apps/ejabberd/src/mod_privacy.erl index 9d2166ae0db..79ec4b7842a 100644 --- a/apps/ejabberd/src/mod_privacy.erl +++ b/apps/ejabberd/src/mod_privacy.erl @@ -114,7 +114,7 @@ %% ------------------------------------------------------------------ start(Host, Opts) -> - start_backend_module(Opts), + gen_mod:start_backend_module(?MODULE, Opts), ?BACKEND:init(Host, Opts), IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), ejabberd_hooks:add(privacy_iq_get, Host, @@ -151,25 +151,6 @@ stop(Host) -> ?MODULE, remove_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY). -%% Dynamic modules -%% ------------------------------------------------------------------ - -start_backend_module(Opts) -> - Backend = gen_mod:get_opt(backend, Opts, mnesia), - {Mod, Code} = dynamic_compile:from_string(mod_privacy_backend(Backend)), - code:load_binary(Mod, "mod_privacy_backend.erl", Code). - --spec mod_privacy_backend(atom()) -> string(). -mod_privacy_backend(Backend) when is_atom(Backend) -> - lists:flatten( - ["-module(mod_privacy_backend). - -export([backend/0]). - -spec backend() -> atom(). - backend() -> - mod_privacy_", - atom_to_list(Backend), - ".\n"]). - %% Handlers %% ------------------------------------------------------------------ diff --git a/apps/ejabberd/src/mod_private.erl b/apps/ejabberd/src/mod_private.erl index 0713d13958a..7f0c8f74855 100644 --- a/apps/ejabberd/src/mod_private.erl +++ b/apps/ejabberd/src/mod_private.erl @@ -71,7 +71,7 @@ %% gen_mod callbacks start(Host, Opts) -> - start_backend_module(Opts), + gen_mod:start_backend_module(?MODULE, Opts), ?BACKEND:init(Host, Opts), IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), @@ -84,25 +84,6 @@ stop(Host) -> ejabberd_hooks:delete(anonymous_purge_hook, Host, ?MODULE, remove_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE). -%% ------------------------------------------------------------------ -%% Dynamic modules - -start_backend_module(Opts) -> - Backend = gen_mod:get_opt(backend, Opts, mnesia), - {Mod, Code} = dynamic_compile:from_string(mod_private_backend(Backend)), - code:load_binary(Mod, "mod_private_backend.erl", Code). - --spec mod_private_backend(atom()) -> string(). -mod_private_backend(Backend) when is_atom(Backend) -> - lists:flatten( - ["-module(mod_private_backend). - -export([backend/0]). - - -spec backend() -> atom(). - backend() -> - mod_private_", - atom_to_list(Backend), - ".\n"]). %% ------------------------------------------------------------------ %% Handlers diff --git a/apps/ejabberd/src/mod_roster.erl b/apps/ejabberd/src/mod_roster.erl index 371fe78e894..5620b6a8ed8 100644 --- a/apps/ejabberd/src/mod_roster.erl +++ b/apps/ejabberd/src/mod_roster.erl @@ -69,7 +69,7 @@ start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - start_backend_module(Opts), + gen_mod:start_backend_module(?MODULE, Opts), ?BACKEND:init(Host, Opts), ejabberd_hooks:add(roster_get, Host, @@ -850,20 +850,3 @@ get_jid_info(_, User, Server, JID) -> end end end. - -start_backend_module(Opts) -> - Backend = gen_mod:get_opt(backend, Opts, mnesia), - {Mod, Code} = dynamic_compile:from_string(mod_roster_backend(Backend)), - code:load_binary(Mod, "mod_last_backend.erl", Code). - --spec mod_roster_backend(atom()) -> string(). -mod_roster_backend(Backend) when is_atom(Backend) -> - lists:flatten( - ["-module(mod_roster_backend). - -export([backend/0]). - - -spec backend() -> atom(). - backend() -> - mod_roster_", - atom_to_list(Backend), - ".\n"]). diff --git a/apps/ejabberd/src/mod_vcard.erl b/apps/ejabberd/src/mod_vcard.erl index 1b2be42d0ac..6b5b3199527 100644 --- a/apps/ejabberd/src/mod_vcard.erl +++ b/apps/ejabberd/src/mod_vcard.erl @@ -100,7 +100,7 @@ %% gen_mod callbacks %%-------------------------------------------------------------------- start(VHost, Opts) -> - start_backend_module(Opts), + gen_mod:start_backend_module(?MODULE, Opts), Proc = gen_mod:get_module_proc(VHost,?PROCNAME), ChildSpec = {Proc, {?MODULE, start_link, [VHost,Opts]}, transient, 1000, worker, [?MODULE]}, @@ -281,24 +281,6 @@ config_change(Acc, Host, ldap, _NewConfig) -> Acc; config_change(Acc, _, _, _) -> Acc. -%% ------------------------------------------------------------------ -%% Dynamic modules -%% ------------------------------------------------------------------ -start_backend_module(Opts) -> - Backend = gen_mod:get_opt(backend, Opts, mnesia), - {Mod, Code} = dynamic_compile:from_string(mod_vcard_backend(Backend)), - code:load_binary(Mod, "mod_vcard_backend.erl", Code). - --spec mod_vcard_backend(atom()) -> string(). -mod_vcard_backend(Backend) when is_atom(Backend) -> - lists:flatten( - ["-module(mod_vcard_backend). - -export([backend/0]). - -spec backend() -> atom(). - backend() -> - mod_vcard_", - atom_to_list(Backend), - ".\n"]). %% ------------------------------------------------------------------ %% Internal From e2480afd8595862309504b8fbfc74909f613ea53 Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Tue, 20 Jan 2015 10:34:14 +0100 Subject: [PATCH 5/7] fix indentation [skip ci] --- apps/ejabberd/src/mod_roster.erl | 300 ++++++++++++------------ apps/ejabberd/src/mod_roster_mnesia.erl | 35 +-- apps/ejabberd/src/mod_roster_odbc.erl | 166 ++++++------- 3 files changed, 251 insertions(+), 250 deletions(-) diff --git a/apps/ejabberd/src/mod_roster.erl b/apps/ejabberd/src/mod_roster.erl index 5620b6a8ed8..c8274872877 100644 --- a/apps/ejabberd/src/mod_roster.erl +++ b/apps/ejabberd/src/mod_roster.erl @@ -175,7 +175,7 @@ write_roster_version(LUser, LServer, InTransaction) -> ?BACKEND:write_roster_version(LUser, LServer, InTransaction, Ver), Ver. -%% Load roster from DB only if neccesary. +%% Load roster from DB only if neccesary. %% It is neccesary if %% - roster versioning is disabled in server OR %% - roster versioning is not used by the client OR @@ -190,12 +190,12 @@ process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) -> {ItemsToSend, VersionToSend} = get_user_roster_based_on_version(AttrVer, VersioningEnabled, VersionOnDb, From, To), - IQ#iq{type = result, - sub_el = create_sub_el(ItemsToSend, VersionToSend)} + IQ#iq{type = result, + sub_el = create_sub_el(ItemsToSend, VersionToSend)} catch _:_ -> IQ#iq{type = error, - sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]} + sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]} end. get_user_roster_based_on_version({value, RequestedVersion}, true, true, @@ -207,43 +207,43 @@ get_user_roster_based_on_version({value, RequestedVersion}, true, true, error -> RosterVersion = write_roster_version(LUser, LServer), {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, - To#jid.lserver, - [], - [US])), - RosterVersion}; + ejabberd_hooks:run_fold(roster_get, + To#jid.lserver, + [], + [US])), + RosterVersion}; RequestedVersion -> {false, false}; NewVersion -> {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, - To#jid.lserver, - [], - [US])), - NewVersion} + ejabberd_hooks:run_fold(roster_get, + To#jid.lserver, + [], + [US])), + NewVersion} end; get_user_roster_based_on_version({value, RequestedVersion}, true, false, From, To) -> RosterItems = - ejabberd_hooks:run_fold(roster_get, - To#jid.lserver, - [], - [{From#jid.luser, From#jid.lserver}]), + ejabberd_hooks:run_fold(roster_get, + To#jid.lserver, + [], + [{From#jid.luser, From#jid.lserver}]), case roster_hash(RosterItems) of RequestedVersion -> {false, false}; New -> {lists:map(fun item_to_xml/1, - RosterItems), - New} + RosterItems), + New} end; get_user_roster_based_on_version(_, _, _, From, To) -> {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, - To#jid.lserver, - [], - [{From#jid.luser, From#jid.lserver}])), - false}. + ejabberd_hooks:run_fold(roster_get, + To#jid.lserver, + [], + [{From#jid.luser, From#jid.lserver}])), + false}. create_sub_el(false, false) -> []; @@ -271,7 +271,7 @@ get_roster(LUser, LServer) -> item_to_xml(Item) -> Attrs1 = [{<<"jid">>, - jlib:jid_to_binary(Item#roster.jid)}], + jlib:jid_to_binary(Item#roster.jid)}], Attrs2 = case Item#roster.name of <<"">> -> Attrs1; Name -> [{<<"name">>, Name} | Attrs1] @@ -289,13 +289,13 @@ item_to_xml(Item) -> _ -> Attrs3 end, SubEls1 = lists:map(fun (G) -> - #xmlel{name = <<"group">>, attrs = [], - children = [{xmlcdata, G}]} - end, - Item#roster.groups), + #xmlel{name = <<"group">>, attrs = [], + children = [{xmlcdata, G}]} + end, + Item#roster.groups), SubEls = SubEls1 ++ Item#roster.xs, #xmlel{name = <<"item">>, attrs = Attrs4, - children = SubEls}. + children = SubEls}. get_roster_by_jid_t(LUser, LServer, LJID) -> ?BACKEND:get_roster_by_jid_t(LUser, LServer, LJID). @@ -318,22 +318,22 @@ do_process_item_set(JID1, #xmlel{attrs = Attrs, children = Els}) -> LJID = jlib:jid_tolower(JID1), F = fun () -> - Item = get_roster_by_jid_t(LUser, LServer, LJID), - Item1 = process_item_attrs(Item, Attrs), - Item2 = process_item_els(Item1, Els), - case Item2#roster.subscription of - remove -> del_roster_t(LUser, LServer, LJID); - _ -> update_roster_t(LUser, LServer, LJID, Item2) - end, - Item3 = ejabberd_hooks:run_fold(roster_process_item, - LServer, Item2, - [LServer]), - case roster_version_on_db(LServer) of - true -> write_roster_version_t(LUser, LServer); - false -> ok + Item = get_roster_by_jid_t(LUser, LServer, LJID), + Item1 = process_item_attrs(Item, Attrs), + Item2 = process_item_els(Item1, Els), + case Item2#roster.subscription of + remove -> del_roster_t(LUser, LServer, LJID); + _ -> update_roster_t(LUser, LServer, LJID, Item2) + end, + Item3 = ejabberd_hooks:run_fold(roster_process_item, + LServer, Item2, + [LServer]), + case roster_version_on_db(LServer) of + true -> write_roster_version_t(LUser, LServer); + false -> ok + end, + {Item, Item3} end, - {Item, Item3} - end, case transaction(LServer, F) of {atomic, {OldItem, Item}} -> push_item(User, LServer, To, Item), @@ -354,7 +354,7 @@ process_item_attrs(Item, [{Attr, Val} | Attrs]) -> error -> process_item_attrs(Item, Attrs); JID1 -> JID = {JID1#jid.luser, JID1#jid.lserver, - JID1#jid.lresource}, + JID1#jid.lresource}, process_item_attrs(Item#roster{jid = JID}, Attrs) end; <<"name">> -> @@ -363,7 +363,7 @@ process_item_attrs(Item, [{Attr, Val} | Attrs]) -> case Val of <<"remove">> -> process_item_attrs(Item#roster{subscription = remove}, - Attrs); + Attrs); _ -> process_item_attrs(Item, Attrs) end; <<"ask">> -> process_item_attrs(Item, Attrs); @@ -372,8 +372,8 @@ process_item_attrs(Item, [{Attr, Val} | Attrs]) -> process_item_attrs(Item, []) -> Item. process_item_els(Item, - [#xmlel{name = Name, attrs = Attrs, children = SEls} - | Els]) -> + [#xmlel{name = Name, attrs = Attrs, children = SEls} + | Els]) -> case Name of <<"group">> -> Groups = [xml:get_cdata(SEls) | Item#roster.groups], @@ -383,8 +383,8 @@ process_item_els(Item, <<"">> -> process_item_els(Item, Els); _ -> XEls = [#xmlel{name = Name, attrs = Attrs, - children = SEls} - | Item#roster.xs], + children = SEls} + | Item#roster.xs], process_item_els(Item#roster{xs = XEls}, Els) end end; @@ -394,20 +394,20 @@ process_item_els(Item, []) -> Item. push_item(User, Server, From, Item) -> ejabberd_sm:route(jlib:make_jid(<<"">>, <<"">>, <<"">>), - jlib:make_jid(User, Server, <<"">>), - #xmlel{name = <<"broadcast">>, - children = [{item, - Item#roster.jid, - Item#roster.subscription}]}), + jlib:make_jid(User, Server, <<"">>), + #xmlel{name = <<"broadcast">>, + children = [{item, + Item#roster.jid, + Item#roster.subscription}]}), case roster_versioning_enabled(Server) of true -> push_item_version(Server, User, From, Item, - roster_version(Server, User)); + roster_version(Server, User)); false -> lists:foreach(fun (Resource) -> - push_item(User, Server, Resource, From, Item) - end, - ejabberd_sm:get_user_resources(User, Server)) + push_item(User, Server, Resource, From, Item) + end, + ejabberd_sm:get_user_resources(User, Server)) end. push_item(User, Server, Resource, From, Item) -> @@ -420,8 +420,8 @@ push_item(User, Server, Resource, From, Item, RosterVersion) -> _ -> [{<<"ver">>, RosterVersion}] end, ResIQ = #iq{type = set, xmlns = ?NS_ROSTER, -%% @doc Roster push, calculate and include the version attribute. -%% TODO: don't push to those who didn't load roster + %% @doc Roster push, calculate and include the version attribute. + %% TODO: don't push to those who didn't load roster id = list_to_binary("push" ++ randoms:get_string()), sub_el = [#xmlel{name = <<"query">>, @@ -504,58 +504,58 @@ in_subscription(_, User, Server, JID, Type, Reason) -> process_subscription(in, User, Server, JID, Type, Reason). -out_subscription(User, Server, JID, Type) -> + out_subscription(User, Server, JID, Type) -> process_subscription(out, User, Server, JID, Type, <<"">>). get_roster_by_jid_with_groups_t(LUser, LServer, LJID) -> ?BACKEND:get_roster_by_jid_with_groups_t(LUser, LServer, LJID). process_subscription(Direction, User, Server, JID1, - Type, Reason) -> + Type, Reason) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), LJID = jlib:jid_tolower(JID1), F = fun () -> - Item = get_roster_by_jid_with_groups_t(LUser, LServer, - LJID), - NewState = case Direction of - out -> - out_state_change(Item#roster.subscription, - Item#roster.ask, Type); - in -> - in_state_change(Item#roster.subscription, - Item#roster.ask, Type) - end, - AutoReply = case Direction of - out -> none; - in -> - in_auto_reply(Item#roster.subscription, - Item#roster.ask, Type) - end, - AskMessage = case NewState of - {_, both} -> Reason; - {_, in} -> Reason; - _ -> <<"">> - end, - case NewState of - none -> {none, AutoReply}; - {none, none} - when Item#roster.subscription == none, - Item#roster.ask == in -> - del_roster_t(LUser, LServer, LJID), {none, AutoReply}; - {Subscription, Pending} -> - NewItem = Item#roster{subscription = Subscription, - ask = Pending, - askmessage = - iolist_to_binary(AskMessage)}, - roster_subscribe_t(LUser, LServer, LJID, NewItem), - case roster_version_on_db(LServer) of - true -> write_roster_version_t(LUser, LServer); - false -> ok - end, - {{push, NewItem}, AutoReply} - end - end, + Item = get_roster_by_jid_with_groups_t(LUser, LServer, + LJID), + NewState = case Direction of + out -> + out_state_change(Item#roster.subscription, + Item#roster.ask, Type); + in -> + in_state_change(Item#roster.subscription, + Item#roster.ask, Type) + end, + AutoReply = case Direction of + out -> none; + in -> + in_auto_reply(Item#roster.subscription, + Item#roster.ask, Type) + end, + AskMessage = case NewState of + {_, both} -> Reason; + {_, in} -> Reason; + _ -> <<"">> + end, + case NewState of + none -> {none, AutoReply}; + {none, none} + when Item#roster.subscription == none, + Item#roster.ask == in -> + del_roster_t(LUser, LServer, LJID), {none, AutoReply}; + {Subscription, Pending} -> + NewItem = Item#roster{subscription = Subscription, + ask = Pending, + askmessage = + iolist_to_binary(AskMessage)}, + roster_subscribe_t(LUser, LServer, LJID, NewItem), + case roster_version_on_db(LServer) of + true -> write_roster_version_t(LUser, LServer); + false -> ok + end, + {{push, NewItem}, AutoReply} + end + end, case transaction(LServer, F) of {atomic, {Push, AutoReply}} -> case AutoReply of @@ -566,20 +566,20 @@ process_subscription(Direction, User, Server, JID1, unsubscribed -> <<"unsubscribed">> end, ejabberd_router:route(jlib:make_jid(User, Server, - <<"">>), - JID1, - #xmlel{name = <<"presence">>, - attrs = [{<<"type">>, T}], - children = []}) + <<"">>), + JID1, + #xmlel{name = <<"presence">>, + attrs = [{<<"type">>, T}], + children = []}) end, case Push of {push, Item} -> if Item#roster.subscription == none, - Item#roster.ask == in -> - ok; - true -> - push_item(User, Server, - jlib:make_jid(User, Server, <<"">>), Item) + Item#roster.ask == in -> + ok; + true -> + push_item(User, Server, + jlib:make_jid(User, Server, <<"">>), Item) end, true; none -> false @@ -650,7 +650,7 @@ out_state_change(none, none, unsubscribe) -> none; out_state_change(none, none, unsubscribed) -> none; out_state_change(none, out, subscribe) -> {none, - out}; %% We need to resend query (RFC3921, section 9.2) + out}; %% We need to resend query (RFC3921, section 9.2) out_state_change(none, out, subscribed) -> none; out_state_change(none, out, unsubscribe) -> {none, none}; @@ -714,9 +714,9 @@ send_unsubscription_to_rosteritems(LUser, LServer) -> RosterItems = get_user_roster([], {LUser, LServer}), From = jlib:make_jid({LUser, LServer, <<"">>}), lists:foreach(fun (RosterItem) -> - send_unsubscribing_presence(From, RosterItem) - end, - RosterItems). + send_unsubscribing_presence(From, RosterItem) + end, + RosterItems). %% @spec (From::jid(), Item::roster()) -> ok send_unsubscribing_presence(From, Item) -> @@ -731,23 +731,23 @@ send_unsubscribing_presence(From, Item) -> _ -> false end, if IsTo -> - send_presence_type(jlib:jid_remove_resource(From), - jlib:make_jid(Item#roster.jid), - <<"unsubscribe">>); - true -> ok + send_presence_type(jlib:jid_remove_resource(From), + jlib:make_jid(Item#roster.jid), + <<"unsubscribe">>); + true -> ok end, if IsFrom -> - send_presence_type(jlib:jid_remove_resource(From), - jlib:make_jid(Item#roster.jid), - <<"unsubscribed">>); - true -> ok + send_presence_type(jlib:jid_remove_resource(From), + jlib:make_jid(Item#roster.jid), + <<"unsubscribed">>); + true -> ok end, ok. send_presence_type(From, To, Type) -> ejabberd_router:route(From, To, - #xmlel{name = <<"presence">>, - attrs = [{<<"type">>, Type}], children = []}). + #xmlel{name = <<"presence">>, + attrs = [{<<"type">>, Type}], children = []}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -756,11 +756,11 @@ set_items(User, Server, SubEl) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), F = fun () -> - lists:foreach(fun (El) -> - process_item_set_t(LUser, LServer, El) + lists:foreach(fun (El) -> + process_item_set_t(LUser, LServer, El) + end, + Els) end, - Els) - end, transaction(LServer, F). update_roster_t(LUser, LServer, LJID, Item) -> @@ -770,17 +770,17 @@ del_roster_t(LUser, LServer, LJID) -> ?BACKEND:del_roster_t(LUser, LServer, LJID). process_item_set_t(LUser, LServer, - #xmlel{attrs = Attrs, children = Els}) -> + #xmlel{attrs = Attrs, children = Els}) -> JID1 = jlib:binary_to_jid(xml:get_attr_s(<<"jid">>, Attrs)), case JID1 of error -> ok; _ -> JID = {JID1#jid.user, JID1#jid.server, - JID1#jid.resource}, + JID1#jid.resource}, LJID = {JID1#jid.luser, JID1#jid.lserver, - JID1#jid.lresource}, + JID1#jid.lresource}, Item = #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = JID}, + us = {LUser, LServer}, jid = JID}, Item1 = process_item_attrs_ws(Item, Attrs), Item2 = process_item_els(Item1, Els), case Item2#roster.subscription of @@ -798,7 +798,7 @@ process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) -> error -> process_item_attrs_ws(Item, Attrs); JID1 -> JID = {JID1#jid.luser, JID1#jid.lserver, - JID1#jid.lresource}, + JID1#jid.lresource}, process_item_attrs_ws(Item#roster{jid = JID}, Attrs) end; <<"name">> -> @@ -807,20 +807,20 @@ process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) -> case Val of <<"remove">> -> process_item_attrs_ws(Item#roster{subscription = - remove}, - Attrs); + remove}, + Attrs); <<"none">> -> process_item_attrs_ws(Item#roster{subscription = none}, - Attrs); + Attrs); <<"both">> -> process_item_attrs_ws(Item#roster{subscription = both}, - Attrs); + Attrs); <<"from">> -> process_item_attrs_ws(Item#roster{subscription = from}, - Attrs); + Attrs); <<"to">> -> process_item_attrs_ws(Item#roster{subscription = to}, - Attrs); + Attrs); _ -> process_item_attrs_ws(Item, Attrs) end; <<"ask">> -> process_item_attrs_ws(Item, Attrs); @@ -842,11 +842,11 @@ get_jid_info(_, User, Server, JID) -> error -> LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), if LRJID == LJID -> {none, []}; - true -> - case read_subscription_and_groups(User, Server, LRJID) - of - {Subscription, Groups} -> {Subscription, Groups}; - error -> {none, []} - end + true -> + case read_subscription_and_groups(User, Server, LRJID) + of + {Subscription, Groups} -> {Subscription, Groups}; + error -> {none, []} + end end end. diff --git a/apps/ejabberd/src/mod_roster_mnesia.erl b/apps/ejabberd/src/mod_roster_mnesia.erl index 5e8b08309b9..207f796d0da 100644 --- a/apps/ejabberd/src/mod_roster_mnesia.erl +++ b/apps/ejabberd/src/mod_roster_mnesia.erl @@ -33,15 +33,15 @@ init(_Host, _Opts) -> mnesia:create_table(roster, [{disc_copies, [node()]}, - {attributes, record_info(fields, roster)}]), + {attributes, record_info(fields, roster)}]), mnesia:create_table(roster_version, [{disc_copies, [node()]}, - {attributes, record_info(fields, roster_version)}]), + {attributes, record_info(fields, roster_version)}]), mnesia:add_table_index(roster, us), mnesia:add_table_index(roster_version, us). -spec read_roster_version(ejabberd:luser(), ejabberd:lserver()) - -> binary() | error. +-> binary() | error. read_roster_version(LUser, LServer) -> US = {LUser, LServer}, case mnesia:dirty_read(roster_version, US) of @@ -52,16 +52,16 @@ read_roster_version(LUser, LServer) -> 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}) + mnesia:write(#roster_version{us = US, version = Ver}); + true -> + mnesia:dirty_write(#roster_version{us = US, + version = Ver}) end. get_roster(LUser, LServer) -> US = {LUser, LServer}, case catch mnesia:dirty_index_read(roster, US, - #roster.us) + #roster.us) of Items when is_list(Items)-> Items; _ -> [] @@ -71,10 +71,10 @@ get_roster_by_jid_t(LUser, LServer, LJID) -> case mnesia:read({roster, {LUser, LServer, LJID}}) of [] -> #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID}; + us = {LUser, LServer}, jid = LJID}; [I] -> I#roster{jid = LJID, name = <<"">>, groups = [], - xs = []} + xs = []} end. get_subscription_lists(_, LUser, LServer) -> @@ -91,7 +91,7 @@ get_roster_by_jid_with_groups_t(LUser, LServer, LJID) -> case mnesia:read({roster, {LUser, LServer, LJID}}) of [] -> #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID}; + us = {LUser, LServer}, jid = LJID}; [I] -> I end. @@ -99,9 +99,9 @@ remove_user(LUser, LServer) -> US = {LUser, LServer}, mod_roster:send_unsubscription_to_rosteritems(LUser, LServer), F = fun () -> - lists:foreach(fun (R) -> mnesia:delete_object(R) end, - mnesia:index_read(roster, US, #roster.us)) - end, + lists:foreach(fun (R) -> mnesia:delete_object(R) end, + mnesia:index_read(roster, US, #roster.us)) + end, mnesia:transaction(F). update_roster_t(_LUser, _LServer, _LJID, Item) -> @@ -113,12 +113,13 @@ del_roster_t(LUser, LServer, LJID) -> read_subscription_and_groups(LUser, LServer, LJID) -> case catch mnesia:dirty_read(roster, - {LUser, LServer, LJID}) + {LUser, LServer, LJID}) of [#roster{subscription = Subscription, - groups = Groups}] -> + groups = Groups}] -> {Subscription, Groups}; _ -> error end. -raw_to_record(_, Item) -> Item. \ No newline at end of file +raw_to_record(_, Item) -> Item. + diff --git a/apps/ejabberd/src/mod_roster_odbc.erl b/apps/ejabberd/src/mod_roster_odbc.erl index 2cdddd0a21f..03620b15272 100644 --- a/apps/ejabberd/src/mod_roster_odbc.erl +++ b/apps/ejabberd/src/mod_roster_odbc.erl @@ -35,7 +35,7 @@ init(_Host, _Opts) -> ok. -spec read_roster_version(ejabberd:luser(), ejabberd:lserver()) - -> binary() | error. +-> binary() | error. read_roster_version(LUser, LServer) -> Username = ejabberd_odbc:escape(LUser), case odbc_queries:get_roster_version(LServer, Username) @@ -48,54 +48,54 @@ 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) + 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) -> Username = ejabberd_odbc:escape(LUser), case catch odbc_queries:get_roster(LServer, Username) of {selected, - [<<"username">>, <<"jid">>, <<"nick">>, - <<"subscription">>, <<"ask">>, <<"askmessage">>, - <<"server">>, <<"subscribe">>, <<"type">>], - Items} - when is_list(Items) -> + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + Items} + when is_list(Items) -> JIDGroups = case catch - odbc_queries:get_roster_jid_groups(LServer, - Username) - of + odbc_queries:get_roster_jid_groups(LServer, + Username) + of {selected, [<<"jid">>, <<"grp">>], JGrps} - when is_list(JGrps) -> + when is_list(JGrps) -> JGrps; _ -> [] end, GroupsDict = lists:foldl(fun ({J, G}, Acc) -> - dict:append(J, G, Acc) - end, - dict:new(), JIDGroups), + 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 = - jlib:jid_to_binary(R#roster.jid), - Groups = case dict:find(SJID, - GroupsDict) - of - {ok, Gs} -> Gs; - error -> [] - end, - [R#roster{groups = Groups}] - end - end, - Items), + case raw_to_record(LServer, I) of + %% Bad JID in database: + error -> []; + R -> + SJID = + jlib:jid_to_binary(R#roster.jid), + Groups = case dict:find(SJID, + GroupsDict) + of + {ok, Gs} -> Gs; + error -> [] + end, + [R#roster{groups = Groups}] + end + end, + Items), RItems; _ -> [] end. @@ -104,25 +104,25 @@ get_roster_by_jid_t(LUser, LServer, LJID) -> Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), {selected, - [<<"username">>, <<"jid">>, <<"nick">>, - <<"subscription">>, <<"ask">>, <<"askmessage">>, - <<"server">>, <<"subscribe">>, <<"type">>], - Res} = - odbc_queries:get_roster_by_jid(LServer, Username, SJID), + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + Res} = + odbc_queries:get_roster_by_jid(LServer, Username, SJID), case Res of [] -> #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID}; + us = {LUser, LServer}, jid = LJID}; [I] -> R = raw_to_record(LServer, I), case R of - %% Bad JID in database: + %% Bad JID in database: error -> #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID}; + us = {LUser, LServer}, jid = LJID}; _ -> R#roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID, name = <<"">>} + us = {LUser, LServer}, jid = LJID, name = <<"">>} end end. @@ -130,11 +130,11 @@ get_subscription_lists(_, LUser, LServer) -> Username = ejabberd_odbc:escape(LUser), case catch odbc_queries:get_roster(LServer, Username) of {selected, - [<<"username">>, <<"jid">>, <<"nick">>, - <<"subscription">>, <<"ask">>, <<"askmessage">>, - <<"server">>, <<"subscribe">>, <<"type">>], - Items} - when is_list(Items) -> + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + Items} + when is_list(Items) -> Items; _ -> [] end. @@ -144,35 +144,35 @@ roster_subscribe_t(LUser, LServer, LJID, Item) -> Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), odbc_queries:roster_subscribe(LServer, Username, SJID, - ItemVals). + ItemVals). get_roster_by_jid_with_groups_t(LUser, LServer, LJID) -> Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), case odbc_queries:get_roster_by_jid(LServer, Username, - SJID) + SJID) of {selected, - [<<"username">>, <<"jid">>, <<"nick">>, - <<"subscription">>, <<"ask">>, <<"askmessage">>, - <<"server">>, <<"subscribe">>, <<"type">>], - [I]} -> + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + [I]} -> R = raw_to_record(LServer, I), Groups = case odbc_queries:get_roster_groups(LServer, - Username, SJID) - of + Username, SJID) + of {selected, [<<"grp">>], JGrps} when is_list(JGrps) -> [JGrp || [JGrp] <- JGrps]; _ -> [] end, R#roster{groups = Groups}; {selected, - [<<"username">>, <<"jid">>, <<"nick">>, - <<"subscription">>, <<"ask">>, <<"askmessage">>, - <<"server">>, <<"subscribe">>, <<"type">>], - []} -> + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + []} -> #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID} + us = {LUser, LServer}, jid = LJID} end. remove_user(LUser, LServer) -> @@ -194,8 +194,8 @@ del_roster_t(LUser, LServer, LJID) -> odbc_queries:del_roster(LServer, Username, SJID). raw_to_record(LServer, - {User, SJID, Nick, SSubscription, SAsk, SAskMessage, - _SServer, _SSubscribe, _SType}) -> + {User, SJID, Nick, SSubscription, SAsk, SAskMessage, + _SServer, _SSubscribe, _SType}) -> case jlib:binary_to_jid(SJID) of error -> error; JID -> @@ -215,16 +215,16 @@ raw_to_record(LServer, _ -> none end, #roster{usj = {User, LServer, LJID}, - us = {User, LServer}, jid = LJID, name = Nick, - subscription = Subscription, ask = Ask, - askmessage = SAskMessage} + us = {User, LServer}, jid = LJID, name = Nick, + subscription = Subscription, ask = Ask, + askmessage = SAskMessage} end. read_subscription_and_groups(LUser, LServer, LJID) -> Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_binary(LJID)), case catch odbc_queries:get_subscription(LServer, - Username, SJID) + Username, SJID) of {selected, [<<"subscription">>], [{SSubscription}]} -> Subscription = case SSubscription of @@ -234,9 +234,9 @@ read_subscription_and_groups(LUser, LServer, LJID) -> _ -> none end, Groups = case catch - odbc_queries:get_rostergroup_by_jid(LServer, Username, - SJID) - of + odbc_queries:get_rostergroup_by_jid(LServer, Username, + SJID) + of {selected, [<<"grp">>], JGrps} when is_list(JGrps) -> [JGrp || [JGrp] <- JGrps]; _ -> [] @@ -250,11 +250,11 @@ read_subscription_and_groups(LUser, LServer, LJID) -> %%============================================================================== record_to_string(#roster{us = {User, _Server}, - jid = JID, name = Name, subscription = Subscription, - ask = Ask, askmessage = AskMessage}) -> + jid = JID, name = Name, subscription = Subscription, + ask = Ask, askmessage = AskMessage}) -> Username = ejabberd_odbc:escape(User), SJID = - ejabberd_odbc:escape(jlib:jid_to_binary(jlib:jid_tolower(JID))), + ejabberd_odbc:escape(jlib:jid_to_binary(jlib:jid_tolower(JID))), Nick = ejabberd_odbc:escape(Name), SSubscription = case Subscription of both -> <<"B">>; @@ -272,16 +272,16 @@ record_to_string(#roster{us = {User, _Server}, end, SAskMessage = ejabberd_odbc:escape(AskMessage), [Username, SJID, Nick, SSubscription, SAsk, SAskMessage, - <<"N">>, <<"">>, <<"item">>]. + <<"N">>, <<"">>, <<"item">>]. groups_to_string(#roster{us = {User, _Server}, - jid = JID, groups = Groups}) -> + jid = JID, groups = Groups}) -> Username = ejabberd_odbc:escape(User), SJID = - ejabberd_odbc:escape(jlib:jid_to_binary(jlib:jid_tolower(JID))), + ejabberd_odbc:escape(jlib:jid_to_binary(jlib:jid_tolower(JID))), lists:foldl(fun (<<"">>, Acc) -> Acc; - (Group, Acc) -> - G = ejabberd_odbc:escape(Group), - [[Username, SJID, G] | Acc] - end, - [], Groups). + (Group, Acc) -> + G = ejabberd_odbc:escape(Group), + [[Username, SJID, G] | Acc] + end, + [], Groups). From 760259018c6330a01635f94849e677d19b550696 Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Tue, 20 Jan 2015 11:21:49 +0100 Subject: [PATCH 6/7] mod_roster rafactoring --- apps/ejabberd/src/mod_roster.erl | 210 +++++++++++++------------------ 1 file changed, 88 insertions(+), 122 deletions(-) diff --git a/apps/ejabberd/src/mod_roster.erl b/apps/ejabberd/src/mod_roster.erl index c8274872877..4d18fb34f37 100644 --- a/apps/ejabberd/src/mod_roster.erl +++ b/apps/ejabberd/src/mod_roster.erl @@ -346,30 +346,22 @@ do_process_item_set(JID1, ?DEBUG("ROSTER: roster item set error: ~p~n", [E]), ok end. -%%TODO remove the case and match in function head -process_item_attrs(Item, [{Attr, Val} | Attrs]) -> - case Attr of - <<"jid">> -> - case jlib:binary_to_jid(Val) of - error -> process_item_attrs(Item, Attrs); - JID1 -> - JID = {JID1#jid.luser, JID1#jid.lserver, - JID1#jid.lresource}, - process_item_attrs(Item#roster{jid = JID}, Attrs) - end; - <<"name">> -> - process_item_attrs(Item#roster{name = Val}, Attrs); - <<"subscription">> -> - case Val of - <<"remove">> -> - process_item_attrs(Item#roster{subscription = remove}, - Attrs); - _ -> process_item_attrs(Item, Attrs) - end; - <<"ask">> -> process_item_attrs(Item, Attrs); - _ -> process_item_attrs(Item, Attrs) +process_item_attrs(Item, [{<<"jid">>, Val} | Attrs]) -> + case jlib:binary_to_jid(Val) of + error -> + process_item_attrs(Item, Attrs); + JID1 -> + JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource}, + process_item_attrs(Item#roster{jid = JID}, Attrs) end; -process_item_attrs(Item, []) -> Item. +process_item_attrs(Item, [{<<"name">>, Val} | Attrs]) -> + process_item_attrs(Item#roster{name = Val}, Attrs); +process_item_attrs(Item, [{<<"subscription">>, <<"remove">>} | Attrs]) -> + process_item_attrs(Item#roster{subscription = remove}, Attrs); +process_item_attrs(Item, [_ | Attrs]) -> + process_item_attrs(Item, Attrs); +process_item_attrs(Item, []) -> + Item. process_item_els(Item, [#xmlel{name = Name, attrs = Attrs, children = SEls} @@ -422,22 +414,22 @@ push_item(User, Server, Resource, From, Item, RosterVersion) -> ResIQ = #iq{type = set, xmlns = ?NS_ROSTER, %% @doc Roster push, calculate and include the version attribute. %% TODO: don't push to those who didn't load roster - id = list_to_binary("push" ++ randoms:get_string()), - sub_el = - [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER} | ExtraAttrs], - children = [item_to_xml(Item)]}]}, + id = list_to_binary("push" ++ randoms:get_string()), + sub_el = + [#xmlel{name = <<"query">>, + attrs = [{<<"xmlns">>, ?NS_ROSTER} | ExtraAttrs], + children = [item_to_xml(Item)]}]}, ejabberd_router:route(From, - jlib:make_jid(User, Server, Resource), - jlib:iq_to_xml(ResIQ)). + jlib:make_jid(User, Server, Resource), + jlib:iq_to_xml(ResIQ)). push_item_version(Server, User, From, Item, - RosterVersion) -> + RosterVersion) -> lists:foreach(fun (Resource) -> - push_item(User, Server, Resource, From, Item, - RosterVersion) - end, - ejabberd_sm:get_user_resources(User, Server)). + push_item(User, Server, Resource, From, Item, + RosterVersion) + end, + ejabberd_sm:get_user_resources(User, Server)). get_subscription_lists(Acc, User, Server) -> LUser = jlib:nodeprep(User), @@ -450,25 +442,7 @@ get_subscription_lists(Acc, User, Server) -> fill_subscription_lists(JID, LServer, [#roster{} = I | Is], F, T, P) -> J = element(3, I#roster.usj), - NewP = case I#roster.ask of - Ask when Ask == in; Ask == both -> - Message = I#roster.askmessage, - Status = if is_binary(Message) -> Message; - true -> <<>> - end, - StatusEl = #xmlel{ - name = <<"status">>, - children = [#xmlcdata{content = Status}]}, - El = #xmlel{ - name = <<"presence">>, - attrs = [{<<"from">>, jlib:jid_to_binary(I#roster.jid)}, - {<<"to">>, jlib:jid_to_binary(JID)}, - {<<"type">>, <<"subscribe">>}], - children = [StatusEl]}, - [El | P]; - _ -> - P - end, + NewP = build_pending(I, JID, P), case I#roster.subscription of both -> @@ -481,12 +455,32 @@ fill_subscription_lists(JID, LServer, [#roster{} = I | Is], F, T, P) -> fill_subscription_lists(JID, LServer, [RawI | Is], F, T, P) -> I = ?BACKEND:raw_to_record(LServer, RawI), case I of - %% Bad JID in database: + %% Bad JID in database: error -> fill_subscription_lists(JID, LServer, Is, F, T, P); _ -> fill_subscription_lists(JID, LServer, [I | Is], F, T, P) end; fill_subscription_lists(_, _LServer, [], F, T, P) -> {F, T, P}. +build_pending(#roster{ask = Ask} = I, JID, P) + when Ask == in; Ask == both -> + Message = I#roster.askmessage, + Status = if is_binary(Message) -> Message; + true -> <<>> + end, + StatusEl = #xmlel{ + name = <<"status">>, + children = [#xmlcdata{content = Status}]}, + El = #xmlel{ + name = <<"presence">>, + attrs = [{<<"from">>, jlib:jid_to_binary(I#roster.jid)}, + {<<"to">>, jlib:jid_to_binary(JID)}, + {<<"type">>, <<"subscribe">>}], + children = [StatusEl]}, + [El | P]; +build_pending(_, _, P) -> + P. + + ask_to_pending(subscribe) -> out; ask_to_pending(unsubscribe) -> none; ask_to_pending(Ask) -> Ask. @@ -502,16 +496,15 @@ transaction(LServer, F) -> in_subscription(_, User, Server, JID, Type, Reason) -> process_subscription(in, User, Server, JID, Type, - Reason). + Reason). - out_subscription(User, Server, JID, Type) -> +out_subscription(User, Server, JID, Type) -> process_subscription(out, User, Server, JID, Type, <<"">>). get_roster_by_jid_with_groups_t(LUser, LServer, LJID) -> ?BACKEND:get_roster_by_jid_with_groups_t(LUser, LServer, LJID). -process_subscription(Direction, User, Server, JID1, - Type, Reason) -> +process_subscription(Direction, User, Server, JID1, Type, Reason) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), LJID = jlib:jid_tolower(JID1), @@ -610,8 +603,7 @@ in_state_change(none, none, unsubscribed) -> none; in_state_change(none, out, subscribe) -> {none, both}; in_state_change(none, out, subscribed) -> {to, none}; in_state_change(none, out, unsubscribe) -> none; -in_state_change(none, out, unsubscribed) -> - {none, none}; +in_state_change(none, out, unsubscribed) -> {none, none}; in_state_change(none, in, subscribe) -> none; in_state_change(none, in, subscribed) -> ?NISD; in_state_change(none, in, unsubscribe) -> {none, none}; @@ -630,41 +622,33 @@ in_state_change(to, in, unsubscribe) -> {to, none}; in_state_change(to, in, unsubscribed) -> {none, in}; in_state_change(from, none, subscribe) -> none; in_state_change(from, none, subscribed) -> {both, none}; -in_state_change(from, none, unsubscribe) -> - {none, none}; +in_state_change(from, none, unsubscribe) -> {none, none}; in_state_change(from, none, unsubscribed) -> none; in_state_change(from, out, subscribe) -> none; in_state_change(from, out, subscribed) -> {both, none}; in_state_change(from, out, unsubscribe) -> {none, out}; -in_state_change(from, out, unsubscribed) -> - {from, none}; +in_state_change(from, out, unsubscribed) -> {from, none}; in_state_change(both, none, subscribe) -> none; in_state_change(both, none, subscribed) -> none; in_state_change(both, none, unsubscribe) -> {to, none}; -in_state_change(both, none, unsubscribed) -> - {from, none}. +in_state_change(both, none, unsubscribed) -> {from, none}. out_state_change(none, none, subscribe) -> {none, out}; out_state_change(none, none, subscribed) -> none; out_state_change(none, none, unsubscribe) -> none; out_state_change(none, none, unsubscribed) -> none; -out_state_change(none, out, subscribe) -> - {none, - out}; %% We need to resend query (RFC3921, section 9.2) +out_state_change(none, out, subscribe) ->{none, out}; %% We need to resend query (RFC3921, section 9.2) out_state_change(none, out, subscribed) -> none; -out_state_change(none, out, unsubscribe) -> - {none, none}; +out_state_change(none, out, unsubscribe) -> {none, none}; out_state_change(none, out, unsubscribed) -> none; out_state_change(none, in, subscribe) -> {none, both}; out_state_change(none, in, subscribed) -> {from, none}; out_state_change(none, in, unsubscribe) -> none; -out_state_change(none, in, unsubscribed) -> - {none, none}; +out_state_change(none, in, unsubscribed) -> {none, none}; out_state_change(none, both, subscribe) -> none; out_state_change(none, both, subscribed) -> {from, out}; out_state_change(none, both, unsubscribe) -> {none, in}; -out_state_change(none, both, unsubscribed) -> - {none, out}; +out_state_change(none, both, unsubscribed) -> {none, out}; out_state_change(to, none, subscribe) -> none; out_state_change(to, none, subscribed) -> {both, none}; out_state_change(to, none, unsubscribe) -> {none, none}; @@ -676,20 +660,15 @@ out_state_change(to, in, unsubscribed) -> {to, none}; out_state_change(from, none, subscribe) -> {from, out}; out_state_change(from, none, subscribed) -> none; out_state_change(from, none, unsubscribe) -> none; -out_state_change(from, none, unsubscribed) -> - {none, none}; +out_state_change(from, none, unsubscribed) -> {none, none}; out_state_change(from, out, subscribe) -> none; out_state_change(from, out, subscribed) -> none; -out_state_change(from, out, unsubscribe) -> - {from, none}; -out_state_change(from, out, unsubscribed) -> - {none, out}; +out_state_change(from, out, unsubscribe) -> {from, none}; +out_state_change(from, out, unsubscribed) -> {none, out}; out_state_change(both, none, subscribe) -> none; out_state_change(both, none, subscribed) -> none; -out_state_change(both, none, unsubscribe) -> - {from, none}; -out_state_change(both, none, unsubscribed) -> - {to, none}. +out_state_change(both, none, unsubscribe) -> {from, none}; +out_state_change(both, none, unsubscribed) -> {to, none}. in_auto_reply(from, none, subscribe) -> subscribed; in_auto_reply(from, out, subscribe) -> subscribed; @@ -790,43 +769,30 @@ process_item_set_t(LUser, LServer, end; process_item_set_t(_LUser, _LServer, _) -> ok. -%%TODO remove case and match in function head -process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) -> - case Attr of - <<"jid">> -> - case jlib:binary_to_jid(Val) of - error -> process_item_attrs_ws(Item, Attrs); - JID1 -> - JID = {JID1#jid.luser, JID1#jid.lserver, - JID1#jid.lresource}, - process_item_attrs_ws(Item#roster{jid = JID}, Attrs) - end; - <<"name">> -> - process_item_attrs_ws(Item#roster{name = Val}, Attrs); - <<"subscription">> -> - case Val of - <<"remove">> -> - process_item_attrs_ws(Item#roster{subscription = - remove}, - Attrs); - <<"none">> -> - process_item_attrs_ws(Item#roster{subscription = none}, - Attrs); - <<"both">> -> - process_item_attrs_ws(Item#roster{subscription = both}, - Attrs); - <<"from">> -> - process_item_attrs_ws(Item#roster{subscription = from}, - Attrs); - <<"to">> -> - process_item_attrs_ws(Item#roster{subscription = to}, - Attrs); - _ -> process_item_attrs_ws(Item, Attrs) - end; - <<"ask">> -> process_item_attrs_ws(Item, Attrs); - _ -> process_item_attrs_ws(Item, Attrs) +process_item_attrs_ws(Item, [{<<"jid">>, Val} | Attrs]) -> + case jlib:binary_to_jid(Val) of + error -> + process_item_attrs_ws(Item, Attrs); + JID1 -> + JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource}, + process_item_attrs_ws(Item#roster{jid = JID}, Attrs) end; -process_item_attrs_ws(Item, []) -> Item. +process_item_attrs_ws(Item, [{<<"name">>, Val} | Attrs]) -> + process_item_attrs_ws(Item#roster{name = Val}, Attrs); +process_item_attrs_ws(Item, [{<<"subscription">>, <<"remove">>} | Attrs]) -> + process_item_attrs_ws(Item#roster{subscription = remove}, Attrs); +process_item_attrs_ws(Item, [{<<"subscription">>, <<"none">>} | Attrs]) -> + process_item_attrs_ws(Item#roster{subscription = none}, Attrs); +process_item_attrs_ws(Item, [{<<"subscription">>, <<"both">>} | Attrs]) -> + process_item_attrs_ws(Item#roster{subscription = both}, Attrs); +process_item_attrs_ws(Item, [{<<"subscription">>, <<"from">>} | Attrs]) -> + process_item_attrs_ws(Item#roster{subscription = from}, Attrs); +process_item_attrs_ws(Item, [{<<"subscription">>, <<"to">>} | Attrs]) -> + process_item_attrs_ws(Item#roster{subscription = to}, Attrs); +process_item_attrs_ws(Item, [_ | Attrs]) -> + process_item_attrs_ws(Item, Attrs); +process_item_attrs_ws(Item, []) -> + Item. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 19e24b6f3e3970f4d54558cc5836f691704e505b Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Tue, 20 Jan 2015 15:16:55 +0100 Subject: [PATCH 7/7] fix roster versioning --- apps/ejabberd/src/mod_roster.erl | 2 +- apps/ejabberd/src/mod_roster_odbc.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ejabberd/src/mod_roster.erl b/apps/ejabberd/src/mod_roster.erl index 4d18fb34f37..bb27f431056 100644 --- a/apps/ejabberd/src/mod_roster.erl +++ b/apps/ejabberd/src/mod_roster.erl @@ -394,7 +394,7 @@ push_item(User, Server, From, Item) -> case roster_versioning_enabled(Server) of true -> push_item_version(Server, User, From, Item, - roster_version(Server, User)); + roster_version(Server, jlib:nodeprep(User))); false -> lists:foreach(fun (Resource) -> push_item(User, Server, Resource, From, Item) diff --git a/apps/ejabberd/src/mod_roster_odbc.erl b/apps/ejabberd/src/mod_roster_odbc.erl index 03620b15272..cef4ed74e21 100644 --- a/apps/ejabberd/src/mod_roster_odbc.erl +++ b/apps/ejabberd/src/mod_roster_odbc.erl @@ -40,7 +40,7 @@ read_roster_version(LUser, LServer) -> Username = ejabberd_odbc:escape(LUser), case odbc_queries:get_roster_version(LServer, Username) of - {selected, [<<"version">>], [[Version]]} -> Version; + {selected, [<<"version">>], [{Version}]} -> Version; {selected, [<<"version">>], []} -> error end.