From 9deb294328bb3f9eb6bd2c0e7cd500732e9b5830 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 14 Mar 2013 10:33:02 +0100 Subject: Accumulated patch to binarize and indent code --- src/mod_roster.erl | 2171 +++++++++++++++++++++++++++------------------------- 1 file changed, 1122 insertions(+), 1049 deletions(-) (limited to 'src/mod_roster.erl') diff --git a/src/mod_roster.erl b/src/mod_roster.erl index b15497fab..dfaf7496e 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -34,175 +34,173 @@ %%% No additional data is stored in DB. -module(mod_roster). + -author('alexey@process-one.net'). -behaviour(gen_mod). --export([start/2, stop/1, - process_iq/3, - process_local_iq/3, - get_user_roster/2, - get_subscription_lists/3, - get_in_pending_subscriptions/3, - in_subscription/6, - out_subscription/4, - set_items/3, - remove_user/2, - get_jid_info/4, - item_to_xml/1, - webadmin_page/3, - webadmin_user/4, - get_versioning_feature/2, - roster_versioning_enabled/1, - roster_version/2]). +-export([start/2, stop/1, process_iq/3, export/1, + process_local_iq/3, get_user_roster/2, + get_subscription_lists/3, get_roster/2, + get_in_pending_subscriptions/3, in_subscription/6, + out_subscription/4, set_items/3, remove_user/2, + get_jid_info/4, item_to_xml/1, webadmin_page/3, + webadmin_user/4, get_versioning_feature/2, + roster_versioning_enabled/1, roster_version/2, + record_to_string/1, groups_to_string/1]). -include("ejabberd.hrl"). + -include("jlib.hrl"). + -include("mod_roster.hrl"). + -include("web/ejabberd_http.hrl"). + -include("web/ejabberd_web_admin.hrl"). +-export_type([subscription/0]). start(Host, Opts) -> - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), + IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, + one_queue), case gen_mod:db_type(Opts) of - mnesia -> - mnesia:create_table(roster, - [{disc_copies, [node()]}, - {attributes, record_info(fields, roster)}]), - mnesia:create_table(roster_version, - [{disc_copies, [node()]}, - {attributes, - record_info(fields, roster_version)}]), - update_table(), - mnesia:add_table_index(roster, us), - mnesia:add_table_index(roster_version, us); - _ -> - ok + mnesia -> + mnesia:create_table(roster, + [{disc_copies, [node()]}, + {attributes, record_info(fields, roster)}]), + mnesia:create_table(roster_version, + [{disc_copies, [node()]}, + {attributes, + record_info(fields, roster_version)}]), + update_tables(), + mnesia:add_table_index(roster, us), + mnesia:add_table_index(roster_version, us); + _ -> ok end, - ejabberd_hooks:add(roster_get, Host, - ?MODULE, get_user_roster, 50), + 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_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). + 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_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_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(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). - + 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). 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]} + true -> process_local_iq(From, To, IQ); + _ -> + IQ#iq{type = error, + sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]} end. 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) + set -> process_iq_set(From, To, IQ); + get -> process_iq_get(From, To, IQ) end. roster_hash(Items) -> - sha:sha(term_to_binary( - lists:sort( - [R#roster{groups = lists:sort(Grs)} || - R = #roster{groups = Grs} <- Items]))). - + sha:sha(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). + gen_mod:get_module_opt(Host, ?MODULE, versioning, + fun(B) when is_boolean(B) -> B end, + false). roster_version_on_db(Host) -> - gen_mod:get_module_opt(Host, ?MODULE, store_current_id, false). + gen_mod:get_module_opt(Host, ?MODULE, store_current_id, + fun(B) when is_boolean(B) -> B end, + false). %% Returns a list that may contain an xmlelement with the XEP-237 feature if it's enabled. get_versioning_feature(Acc, Host) -> case roster_versioning_enabled(Host) of - true -> - Feature = {xmlelement, - "ver", - [{"xmlns", ?NS_ROSTER_VER}], - []}, - [Feature | Acc]; - false -> [] + true -> + Feature = #xmlel{name = <<"ver">>, + attrs = [{<<"xmlns">>, ?NS_ROSTER_VER}], + children = []}, + [Feature | Acc]; + false -> [] end. 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])) + 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) -> - read_roster_version(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)). + read_roster_version(LUser, LServer, + gen_mod:db_type(LServer, ?MODULE)). read_roster_version(LUser, LServer, mnesia) -> US = {LUser, LServer}, case mnesia:dirty_read(roster_version, US) of - [#roster_version{version = V}] -> V; - [] -> error + [#roster_version{version = V}] -> V; + [] -> error end; read_roster_version(LServer, LUser, odbc) -> Username = ejabberd_odbc:escape(LUser), - case odbc_queries:get_roster_version(LServer, Username) of - {selected, ["version"], [{Version}]} -> - Version; - {selected, ["version"], []} -> - error + case odbc_queries:get_roster_version(LServer, Username) + of + {selected, [<<"version">>], [[Version]]} -> Version; + {selected, [<<"version">>], []} -> error end. write_roster_version(LUser, LServer) -> @@ -214,27 +212,30 @@ write_roster_version_t(LUser, LServer) -> write_roster_version(LUser, LServer, InTransaction) -> Ver = sha:sha(term_to_binary(now())), write_roster_version(LUser, LServer, InTransaction, Ver, - gen_mod:db_type(LServer, ?MODULE)), + gen_mod:db_type(LServer, ?MODULE)), Ver. -write_roster_version(LUser, LServer, InTransaction, Ver, mnesia) -> +write_roster_version(LUser, LServer, InTransaction, Ver, + mnesia) -> US = {LUser, LServer}, if InTransaction -> - mnesia: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}) + mnesia:dirty_write(#roster_version{us = US, + version = Ver}) end; -write_roster_version(LUser, LServer, InTransaction, Ver, odbc) -> +write_roster_version(LUser, LServer, InTransaction, Ver, + odbc) -> Username = ejabberd_odbc:escape(LUser), EVer = ejabberd_odbc:escape(Ver), if InTransaction -> - odbc_queries:set_roster_version(Username, EVer); + odbc_queries:set_roster_version(Username, EVer); true -> - odbc_queries:sql_transaction( - LServer, - fun() -> - odbc_queries:set_roster_version(Username, EVer) - end) + odbc_queries:sql_transaction(LServer, + fun () -> + odbc_queries:set_roster_version(Username, + EVer) + end) end. %% Load roster from DB only if neccesary. @@ -247,156 +248,172 @@ 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 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; - {{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} -> - [{xmlelement, "query", - [{"xmlns", ?NS_ROSTER}], Items}]; - {Items, Version} -> - [{xmlelement, "query", - [{"xmlns", ?NS_ROSTER}, {"ver", Version}], - Items}] - end} + try {ItemsToSend, VersionToSend} = case + {xml:get_tag_attr(<<"ver">>, SubEl), + roster_versioning_enabled(LServer), + roster_version_on_db(LServer)} + of + {{value, RequestedVersion}, true, + true} -> + 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; + {{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]} + _:_ -> + 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. + lists:filter(fun (#roster{subscription = none, + ask = in}) -> + false; + (_) -> true + end, + Items) + ++ Acc. get_roster(LUser, LServer) -> - get_roster(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)). + get_roster(LUser, LServer, + gen_mod:db_type(LServer, ?MODULE)). get_roster(LUser, LServer, mnesia) -> US = {LUser, LServer}, - case catch mnesia:dirty_index_read(roster, US, #roster.us) of - Items when is_list(Items) -> - Items; - _ -> - [] + case catch mnesia:dirty_index_read(roster, US, + #roster.us) + of + Items when is_list(Items)-> Items; + _ -> [] end; get_roster(LUser, LServer, odbc) -> 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, ["jid","grp"], JGrps} + {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) -> - JGrps; - _ -> - [] - end, - GroupsDict = - lists:foldl( - fun({J, G}, Acc) -> - dict:append(J, G, Acc) - end, dict:new(), JIDGroups), - RItems = lists:flatmap( - fun(I) -> - case raw_to_record(LServer, I) of - %% Bad JID in database: - error -> - []; - R -> - SJID = jlib:jid_to_string(R#roster.jid), - Groups = - case dict:find(SJID, GroupsDict) of - {ok, Gs} -> Gs; - error -> [] - end, - [R#roster{groups = Groups}] - end - end, Items), - RItems; - _ -> - [] + JGrps; + _ -> [] + end, + GroupsDict = lists:foldl(fun ([J, G], Acc) -> + dict:append(J, G, Acc) + end, + dict:new(), JIDGroups), + RItems = lists:flatmap(fun (I) -> + case raw_to_record(LServer, I) of + %% Bad JID in database: + error -> []; + R -> + SJID = + jlib:jid_to_string(R#roster.jid), + Groups = case dict:find(SJID, + GroupsDict) + of + {ok, Gs} -> Gs; + error -> [] + end, + [R#roster{groups = Groups}] + end + end, + Items), + RItems; + _ -> [] end. - item_to_xml(Item) -> - Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}], + Attrs1 = [{<<"jid">>, + jlib:jid_to_string(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) -> - {xmlelement, "group", [], [{xmlcdata, 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, - {xmlelement, "item", Attrs4, SubEls}. + #xmlel{name = <<"item">>, attrs = Attrs4, + children = SubEls}. get_roster_by_jid_t(LUser, LServer, LJID) -> DBType = gen_mod:db_type(LServer, ?MODULE), @@ -404,500 +421,504 @@ get_roster_by_jid_t(LUser, LServer, LJID) -> get_roster_by_jid_t(LUser, LServer, LJID, mnesia) -> case mnesia:read({roster, {LUser, LServer, LJID}}) of - [] -> - #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, - jid = LJID}; - [I] -> - I#roster{jid = LJID, - name = "", - groups = [], - xs = []} + [] -> + #roster{usj = {LUser, LServer, LJID}, + us = {LUser, LServer}, jid = LJID}; + [I] -> + I#roster{jid = LJID, name = <<"">>, groups = [], + xs = []} end; get_roster_by_jid_t(LUser, LServer, LJID, odbc) -> Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_string(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}; - [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 + [] -> + #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. process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) -> - {xmlelement, _Name, _Attrs, Els} = SubEl, - lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els), + #xmlel{children = Els} = SubEl, + lists:foreach(fun (El) -> process_item_set(From, To, El) + end, + Els), IQ#iq{type = result, sub_el = []}. -process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) -> - JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)), - #jid{user = User, luser = LUser, lserver = LServer} = From, +process_item_set(From, To, + #xmlel{attrs = Attrs, children = Els}) -> + JID1 = jlib:string_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), - 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, - %% 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 -> 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 + error -> ok; + _ -> + 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 end; -process_item_set(_From, _To, _) -> - ok. +process_item_set(_From, _To, _) -> ok. process_item_attrs(Item, [{Attr, Val} | Attrs]) -> case Attr of - "jid" -> - case jlib:string_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) + <<"jid">> -> + case jlib:string_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) end; -process_item_attrs(Item, []) -> - Item. - +process_item_attrs(Item, []) -> Item. -process_item_els(Item, [{xmlelement, Name, Attrs, 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); - _ -> - XEls = [{xmlelement, Name, Attrs, SEls} | Item#roster.xs], - process_item_els(Item#roster{xs = XEls}, Els) - end + <<"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); + _ -> + 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, 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, ""), - {xmlelement, "broadcast", [], - [{item, - Item#roster.jid, - Item#roster.subscription}]}), + ejabberd_sm:route(jlib:make_jid(<<"">>, <<"">>, <<"">>), + jlib:make_jid(User, Server, <<"">>), + {broadcast, {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)) + 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 didn't load roster push_item(User, Server, Resource, From, Item) -> - push_item(User, Server, Resource, From, Item, not_found). + push_item(User, Server, Resource, From, Item, + not_found). -push_item(User, Server, Resource, From, Item, RosterVersion) -> +push_item(User, Server, Resource, From, Item, + RosterVersion) -> ExtraAttrs = case RosterVersion of - not_found -> []; - _ -> [{"ver", RosterVersion}] - end, + not_found -> []; + _ -> [{<<"ver">>, RosterVersion}] + end, ResIQ = #iq{type = set, xmlns = ?NS_ROSTER, - id = "push" ++ randoms:get_string(), - sub_el = [{xmlelement, "query", - [{"xmlns", ?NS_ROSTER}|ExtraAttrs], - [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 = <<"push", (randoms:get_string())/binary>>, + 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(Acc, User, Server) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), DBType = gen_mod:db_type(LServer, ?MODULE), - Items = get_subscription_lists(Acc, LUser, LServer, DBType), + Items = get_subscription_lists(Acc, LUser, LServer, + DBType), fill_subscription_lists(LServer, Items, [], []). get_subscription_lists(_, LUser, LServer, mnesia) -> US = {LUser, LServer}, case mnesia:dirty_index_read(roster, US, #roster.us) of - Items when is_list(Items) -> - Items; - _ -> - [] + Items when is_list(Items) -> Items; + _ -> [] end; get_subscription_lists(_, LUser, LServer, odbc) -> 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) -> - Items; - _ -> - [] + {selected, + [<<"username">>, <<"jid">>, <<"nick">>, + <<"subscription">>, <<"ask">>, <<"askmessage">>, + <<"server">>, <<"subscribe">>, <<"type">>], + Items} + when is_list(Items) -> + Items; + _ -> [] end. -fill_subscription_lists(LServer, [#roster{} = I | Is], F, T) -> +fill_subscription_lists(LServer, [#roster{} = I | Is], + F, T) -> J = element(3, I#roster.usj), case I#roster.subscription of - both -> - fill_subscription_lists(LServer, Is, [J | F], [J | T]); - from -> - fill_subscription_lists(LServer, Is, [J | F], T); - to -> - fill_subscription_lists(LServer, Is, F, [J | T]); - _ -> - fill_subscription_lists(LServer, Is, F, T) + both -> + fill_subscription_lists(LServer, Is, [J | F], [J | T]); + from -> + fill_subscription_lists(LServer, Is, [J | F], T); + to -> fill_subscription_lists(LServer, Is, F, [J | T]); + _ -> fill_subscription_lists(LServer, Is, F, T) end; fill_subscription_lists(LServer, [RawI | Is], F, T) -> I = raw_to_record(LServer, RawI), case I of - %% Bad JID in database: - error -> - fill_subscription_lists(LServer, Is, F, T); - _ -> - fill_subscription_lists(LServer, [I | Is], F, T) + %% Bad JID in database: + error -> fill_subscription_lists(LServer, Is, F, T); + _ -> fill_subscription_lists(LServer, [I | Is], F, T) end; -fill_subscription_lists(_LServer, [], F, T) -> - {F, T}. +fill_subscription_lists(_LServer, [], F, T) -> {F, T}. ask_to_pending(subscribe) -> out; ask_to_pending(unsubscribe) -> none; ask_to_pending(Ask) -> Ask. - roster_subscribe_t(LUser, LServer, LJID, Item) -> DBType = gen_mod:db_type(LServer, ?MODULE), roster_subscribe_t(LUser, LServer, LJID, Item, DBType). -roster_subscribe_t(_LUser, _LServer, _LJID, Item, mnesia) -> +roster_subscribe_t(_LUser, _LServer, _LJID, Item, + mnesia) -> mnesia:write(Item); roster_subscribe_t(LUser, LServer, LJID, Item, odbc) -> ItemVals = record_to_string(Item), Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)), - odbc_queries:roster_subscribe(LServer, Username, SJID, ItemVals). + odbc_queries:roster_subscribe(LServer, Username, SJID, + ItemVals). transaction(LServer, F) -> case gen_mod:db_type(LServer, ?MODULE) of - mnesia -> - mnesia:transaction(F); - odbc -> - ejabberd_odbc:sql_transaction(LServer, F) + 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) -> DBType = gen_mod:db_type(LServer, ?MODULE), - get_roster_by_jid_with_groups_t(LUser, LServer, LJID, DBType). + get_roster_by_jid_with_groups_t(LUser, LServer, LJID, + DBType). -get_roster_by_jid_with_groups_t(LUser, LServer, LJID, mnesia) -> +get_roster_by_jid_with_groups_t(LUser, LServer, LJID, + mnesia) -> case mnesia:read({roster, {LUser, LServer, LJID}}) of - [] -> - #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, - jid = LJID}; - [I] -> - I + [] -> + #roster{usj = {LUser, LServer, LJID}, + us = {LUser, LServer}, jid = LJID}; + [I] -> I end; -get_roster_by_jid_with_groups_t(LUser, LServer, LJID, odbc) -> +get_roster_by_jid_with_groups_t(LUser, LServer, LJID, + odbc) -> Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)), - 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} + 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. -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), - F = fun() -> - Item = get_roster_by_jid_with_groups_t( - LUser, LServer, LJID), + 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) + 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) + out -> none; + in -> + in_auto_reply(Item#roster.subscription, + Item#roster.ask, Type) end, AskMessage = case NewState of - {_, both} -> Reason; - {_, in} -> Reason; - _ -> "" + {_, 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 = list_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} + 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; - _ -> - T = case AutoReply of - subscribed -> "subscribed"; - unsubscribed -> "unsubscribed" - end, - ejabberd_router:route( - jlib:make_jid(User, Server, ""), JID1, - {xmlelement, "presence", [{"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) + {atomic, {Push, AutoReply}} -> + case AutoReply of + none -> ok; + _ -> + T = case AutoReply of + subscribed -> <<"subscribed">>; + unsubscribed -> <<"unsubscribed">> end, - true; - none -> - false - end; - _ -> - false + 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; + true -> + push_item(User, Server, + jlib:make_jid(User, Server, <<"">>), Item) + end, + true; + none -> false + end; + _ -> 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), - remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)). + remove_user(LUser, LServer, + gen_mod:db_type(LServer, ?MODULE)). remove_user(LUser, LServer, mnesia) -> US = {LUser, LServer}, send_unsubscription_to_rosteritems(LUser, LServer), - F = fun() -> - lists:foreach(fun(R) -> - mnesia:delete_object(R) - end, + F = fun () -> + lists:foreach(fun (R) -> mnesia:delete_object(R) end, mnesia:index_read(roster, US, #roster.us)) - end, + end, mnesia:transaction(F); remove_user(LUser, LServer, odbc) -> Username = ejabberd_odbc:escape(LUser), @@ -910,8 +931,8 @@ remove_user(LUser, LServer, odbc) -> %% 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) -> + From = jlib:make_jid({LUser, LServer, <<"">>}), + lists:foreach(fun (RosterItem) -> send_unsubscribing_presence(From, RosterItem) end, RosterItems). @@ -919,56 +940,54 @@ send_unsubscription_to_rosteritems(LUser, LServer) -> %% @spec (From::jid(), Item::roster()) -> ok send_unsubscribing_presence(From, Item) -> IsTo = case Item#roster.subscription of - both -> true; - to -> true; - _ -> false + both -> true; + to -> true; + _ -> false end, IsFrom = case Item#roster.subscription of - both -> true; - from -> true; - _ -> false + both -> true; + from -> true; + _ -> false end, if IsTo -> - send_presence_type( - jlib:jid_remove_resource(From), - jlib:make_jid(Item#roster.jid), "unsubscribe"); + 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"); + 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, - {xmlelement, "presence", - [{"type", Type}], - []}). - + ejabberd_router:route(From, To, + #xmlel{name = <<"presence">>, + attrs = [{<<"type">>, Type}], children = []}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% set_items(User, Server, SubEl) -> - {xmlelement, _Name, _Attrs, Els} = 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) - end, + F = fun () -> + lists:foreach(fun (El) -> + process_item_set_t(LUser, LServer, El) + end, + Els) + end, transaction(LServer, F). update_roster_t(LUser, LServer, LJID, Item) -> DBType = gen_mod:db_type(LServer, ?MODULE), update_roster_t(LUser, LServer, LJID, Item, DBType). -update_roster_t(_LUser, _LServer,_LJID, Item, mnesia) -> +update_roster_t(_LUser, _LServer, _LJID, Item, + mnesia) -> mnesia:write(Item); update_roster_t(LUser, LServer, LJID, Item, odbc) -> Username = ejabberd_odbc:escape(LUser), @@ -988,142 +1007,145 @@ del_roster_t(LUser, LServer, LJID, odbc) -> SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)), odbc_queries:del_roster(LServer, Username, SJID). -process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) -> - JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)), +process_item_set_t(LUser, LServer, + #xmlel{attrs = Attrs, children = Els}) -> + JID1 = jlib:string_to_jid(xml:get_attr_s(<<"jid">>, + Attrs)), case JID1 of - error -> - ok; - _ -> - 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}, - Item1 = process_item_attrs_ws(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 + error -> ok; + _ -> + 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}, + Item1 = process_item_attrs_ws(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 end; -process_item_set_t(_LUser, _LServer, _) -> - ok. +process_item_set_t(_LUser, _LServer, _) -> ok. process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) -> case Attr of - "jid" -> - case jlib:string_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) + <<"jid">> -> + case jlib:string_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) end; -process_item_attrs_ws(Item, []) -> - Item. +process_item_attrs_ws(Item, []) -> Item. get_in_pending_subscriptions(Ls, User, Server) -> LServer = jlib:nameprep(Server), get_in_pending_subscriptions(Ls, User, Server, - gen_mod:db_type(LServer, ?MODULE)). + gen_mod:db_type(LServer, ?MODULE)). -get_in_pending_subscriptions(Ls, User, Server, mnesia) -> - JID = jlib:make_jid(User, Server, ""), +get_in_pending_subscriptions(Ls, User, Server, + mnesia) -> + JID = jlib:make_jid(User, Server, <<"">>), US = {JID#jid.luser, JID#jid.lserver}, case mnesia:dirty_index_read(roster, US, #roster.us) of - Result when is_list(Result) -> - Ls ++ lists:map( - fun(R) -> - Message = R#roster.askmessage, - Status = if is_binary(Message) -> - binary_to_list(Message); - true -> - "" - end, - {xmlelement, "presence", - [{"from", jlib:jid_to_string(R#roster.jid)}, - {"to", jlib:jid_to_string(JID)}, - {"type", "subscribe"}], - [{xmlelement, "status", [], - [{xmlcdata, Status}]}]} - end, - lists:filter( - fun(R) -> - case R#roster.ask of - in -> true; - both -> true; - _ -> false - end + Result when is_list(Result) -> + Ls ++ + lists:map(fun (R) -> + Message = R#roster.askmessage, + Status = if is_binary(Message) -> (Message); + true -> <<"">> + end, + #xmlel{name = <<"presence">>, + attrs = + [{<<"from">>, + jlib:jid_to_string(R#roster.jid)}, + {<<"to">>, jlib:jid_to_string(JID)}, + {<<"type">>, <<"subscribe">>}], + children = + [#xmlel{name = <<"status">>, + attrs = [], + children = + [{xmlcdata, Status}]}]} end, - Result)); - _ -> - Ls + lists:filter(fun (R) -> + case R#roster.ask of + in -> true; + both -> true; + _ -> false + end + end, + Result)); + _ -> Ls end; get_in_pending_subscriptions(Ls, User, Server, odbc) -> - JID = jlib:make_jid(User, Server, ""), + JID = jlib:make_jid(User, Server, <<"">>), LUser = JID#jid.luser, LServer = JID#jid.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) -> - Ls ++ lists:map( - fun(R) -> - Message = R#roster.askmessage, - {xmlelement, "presence", - [{"from", jlib:jid_to_string(R#roster.jid)}, - {"to", jlib:jid_to_string(JID)}, - {"type", "subscribe"}], - [{xmlelement, "status", [], - [{xmlcdata, Message}]}]} - end, - lists:flatmap( - fun(I) -> - case raw_to_record(LServer, I) of - %% Bad JID in database: - error -> - []; - R -> - case R#roster.ask of - in -> [R]; - both -> [R]; - _ -> [] - end - end + {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_string(R#roster.jid)}, + {<<"to">>, jlib:jid_to_string(JID)}, + {<<"type">>, <<"subscribe">>}], + children = + [#xmlel{name = <<"status">>, + attrs = [], + children = + [{xmlcdata, Message}]}]} end, - Items)); - _ -> - Ls + 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. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1132,198 +1154,188 @@ read_subscription_and_groups(User, Server, LJID) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), read_subscription_and_groups(LUser, LServer, LJID, - gen_mod:db_type(LServer, ?MODULE)). - -read_subscription_and_groups(LUser, LServer, LJID, mnesia) -> - case catch mnesia:dirty_read(roster, {LUser, LServer, LJID}) of - [#roster{subscription = Subscription, groups = Groups}] -> - {Subscription, Groups}; - _ -> - error + gen_mod:db_type(LServer, ?MODULE)). + +read_subscription_and_groups(LUser, LServer, LJID, + mnesia) -> + case catch mnesia:dirty_read(roster, + {LUser, LServer, LJID}) + of + [#roster{subscription = Subscription, + groups = Groups}] -> + {Subscription, Groups}; + _ -> error end; -read_subscription_and_groups(LUser, LServer, LJID, odbc) -> +read_subscription_and_groups(LUser, LServer, LJID, + odbc) -> Username = ejabberd_odbc:escape(LUser), SJID = ejabberd_odbc:escape(jlib:jid_to_string(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 + 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. 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, []}; - true -> - case read_subscription_and_groups( - User, Server, LRJID) of - {Subscription, Groups} -> - {Subscription, Groups}; - error -> - {none, []} - end - end + {Subscription, Groups} -> {Subscription, Groups}; + 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 + end end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk, SAskMessage, - _SServer, _SSubscribe, _SType}) -> +raw_to_record(LServer, + [User, SJID, Nick, SSubscription, SAsk, SAskMessage, + _SServer, _SSubscribe, _SType]) -> case jlib:string_to_jid(SJID) of - error -> - error; - JID -> - LJID = jlib:jid_tolower(JID), - Subscription = case SSubscription of - "B" -> both; - "T" -> to; - "F" -> from; - _ -> none - end, - Ask = case SAsk of - "S" -> subscribe; - "U" -> unsubscribe; - "B" -> both; - "O" -> out; - "I" -> in; - _ -> none - end, - #roster{usj = {User, LServer, LJID}, - us = {User, LServer}, - jid = LJID, - name = Nick, - subscription = Subscription, - ask = Ask, - askmessage = SAskMessage} + error -> error; + JID -> + LJID = jlib:jid_tolower(JID), + Subscription = case SSubscription of + <<"B">> -> both; + <<"T">> -> to; + <<"F">> -> from; + _ -> none + end, + Ask = case SAsk of + <<"S">> -> subscribe; + <<"U">> -> unsubscribe; + <<"B">> -> both; + <<"O">> -> out; + <<"I">> -> in; + _ -> none + end, + #roster{usj = {User, LServer, LJID}, + us = {User, LServer}, jid = LJID, name = Nick, + subscription = Subscription, ask = Ask, + askmessage = SAskMessage} end. record_to_string(#roster{us = {User, _Server}, - jid = JID, - name = Name, - subscription = Subscription, - ask = Ask, - askmessage = AskMessage}) -> + jid = JID, name = Name, subscription = Subscription, + ask = Ask, askmessage = AskMessage}) -> Username = ejabberd_odbc:escape(User), - SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))), + SJID = + ejabberd_odbc:escape(jlib:jid_to_string(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_string(jlib:jid_tolower(JID))), + SJID = + ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))), + lists:foldl(fun (<<"">>, Acc) -> Acc; + (Group, Acc) -> + G = ejabberd_odbc:escape(Group), + [[Username, SJID, G] | Acc] + end, + [], Groups). - %% 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). +update_tables() -> + update_roster_table(), + update_roster_version_table(). -update_table() -> +update_roster_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) + Fields -> + ejabberd_config:convert_table_to_binary( + roster, Fields, set, + fun(#roster{usj = {U, _, _}}) -> U end, + fun(#roster{usj = {U, S, {LU, LS, LR}}, + us = {U1, S1}, + jid = {U2, S2, R2}, + name = Name, + groups = Gs, + askmessage = Ask, + xs = Xs} = R) -> + R#roster{usj = {iolist_to_binary(U), + iolist_to_binary(S), + {iolist_to_binary(LU), + iolist_to_binary(LS), + iolist_to_binary(LR)}}, + us = {iolist_to_binary(U1), + iolist_to_binary(S1)}, + jid = {iolist_to_binary(U2), + iolist_to_binary(S2), + iolist_to_binary(R2)}, + name = iolist_to_binary(Name), + groups = [iolist_to_binary(G) || G <- Gs], + askmessage = iolist_to_binary(Ask), + xs = [xml:to_xmlel(X) || X <- Xs]} + end); + _ -> + ?INFO_MSG("Recreating roster table", []), + mnesia:transform_table(roster, ignore, Fields) end. - %% Convert roster table to support virtual host -convert_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). - +update_roster_version_table() -> + Fields = record_info(fields, roster_version), + case mnesia:table_info(roster_version, attributes) of + Fields -> + ejabberd_config:convert_table_to_binary( + roster_version, Fields, set, + fun(#roster_version{us = {U, _}}) -> U end, + fun(#roster_version{us = {U, S}, version = Ver} = R) -> + R#roster_version{us = {iolist_to_binary(U), + iolist_to_binary(S)}, + version = iolist_to_binary(Ver)} + end); + _ -> + ?INFO_MSG("Recreating roster_version table", []), + mnesia:transform_table(roster_version, ignore, Fields) + end. webadmin_page(_, Host, - #request{us = _US, - path = ["user", U, "roster"], - q = Query, - lang = Lang} = _Request) -> - Res = user_roster(U, Host, Query, Lang), - {stop, Res}; - + #request{us = _US, path = [<<"user">>, U, <<"roster">>], + q = Query, lang = Lang} = + _Request) -> + Res = user_roster(U, Host, Query, Lang), {stop, Res}; webadmin_page(Acc, _, _) -> Acc. user_roster(User, Server, Query, Lang) -> @@ -1331,167 +1343,228 @@ user_roster(User, Server, Query, Lang) -> LServer = jlib:nameprep(Server), US = {LUser, LServer}, Items1 = get_roster(LUser, LServer), - Res = user_roster_parse_query(User, Server, Items1, Query), + Res = user_roster_parse_query(User, Server, Items1, + Query), Items = get_roster(LUser, LServer), SItems = lists:sort(Items), - FItems = - case SItems of - [] -> - [?CT("None")]; - _ -> - [?XE("table", - [?XE("thead", - [?XE("tr", - [?XCT("td", "Jabber ID"), - ?XCT("td", "Nickname"), - ?XCT("td", "Subscription"), - ?XCT("td", "Pending"), - ?XCT("td", "Groups") - ])]), - ?XE("tbody", - lists:map( - fun(R) -> - Groups = - lists:flatmap( - fun(Group) -> - [?C(Group), ?BR] - end, R#roster.groups), - Pending = ask_to_pending(R#roster.ask), - TDJID = build_contact_jid_td(R#roster.jid), - ?XE("tr", - [TDJID, - ?XAC("td", [{"class", "valign"}], - R#roster.name), - ?XAC("td", [{"class", "valign"}], - atom_to_list(R#roster.subscription)), - ?XAC("td", [{"class", "valign"}], - atom_to_list(Pending)), - ?XAE("td", [{"class", "valign"}], Groups), - if - Pending == in -> - ?XAE("td", [{"class", "valign"}], - [?INPUTT("submit", - "validate" ++ - ejabberd_web_admin:term_to_id(R#roster.jid), - "Validate")]); - true -> - ?X("td") - end, - ?XAE("td", [{"class", "valign"}], - [?INPUTT("submit", - "remove" ++ - ejabberd_web_admin:term_to_id(R#roster.jid), - "Remove")])]) - end, SItems))])] - end, - [?XC("h1", ?T("Roster of ") ++ us_to_list(US))] ++ - case Res of - ok -> [?XREST("Submitted")]; - error -> [?XREST("Bad format")]; - nothing -> [] - end ++ - [?XAE("form", [{"action", ""}, {"method", "post"}], - FItems ++ - [?P, - ?INPUT("text", "newjid", ""), ?C(" "), - ?INPUTT("submit", "addjid", "Add Jabber ID") - ])]. + FItems = case SItems of + [] -> [?CT(<<"None">>)]; + _ -> + [?XE(<<"table">>, + [?XE(<<"thead">>, + [?XE(<<"tr">>, + [?XCT(<<"td">>, <<"Jabber ID">>), + ?XCT(<<"td">>, <<"Nickname">>), + ?XCT(<<"td">>, <<"Subscription">>), + ?XCT(<<"td">>, <<"Pending">>), + ?XCT(<<"td">>, <<"Groups">>)])]), + ?XE(<<"tbody">>, + (lists:map(fun (R) -> + Groups = lists:flatmap(fun + (Group) -> + [?C(Group), + ?BR] + end, + R#roster.groups), + Pending = + ask_to_pending(R#roster.ask), + TDJID = + build_contact_jid_td(R#roster.jid), + ?XE(<<"tr">>, + [TDJID, + ?XAC(<<"td">>, + [{<<"class">>, + <<"valign">>}], + (R#roster.name)), + ?XAC(<<"td">>, + [{<<"class">>, + <<"valign">>}], + (iolist_to_binary(atom_to_list(R#roster.subscription)))), + ?XAC(<<"td">>, + [{<<"class">>, + <<"valign">>}], + (iolist_to_binary(atom_to_list(Pending)))), + ?XAE(<<"td">>, + [{<<"class">>, + <<"valign">>}], + Groups), + if Pending == in -> + ?XAE(<<"td">>, + [{<<"class">>, + <<"valign">>}], + [?INPUTT(<<"submit">>, + <<"validate", + (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>, + <<"Validate">>)]); + true -> ?X(<<"td">>) + end, + ?XAE(<<"td">>, + [{<<"class">>, + <<"valign">>}], + [?INPUTT(<<"submit">>, + <<"remove", + (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>, + <<"Remove">>)])]) + end, + SItems)))])] + end, + [?XC(<<"h1">>, + (<<(?T(<<"Roster of ">>))/binary, (us_to_list(US))/binary>>))] + ++ + case Res of + ok -> [?XREST(<<"Submitted">>)]; + error -> [?XREST(<<"Bad format">>)]; + nothing -> [] + end + ++ + [?XAE(<<"form">>, + [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], + (FItems ++ + [?P, ?INPUT(<<"text">>, <<"newjid">>, <<"">>), + ?C(<<" ">>), + ?INPUTT(<<"submit">>, <<"addjid">>, + <<"Add Jabber ID">>)]))]. build_contact_jid_td(RosterJID) -> - %% Convert {U, S, R} into {jid, U, S, R, U, S, R}: ContactJID = jlib:make_jid(RosterJID), - JIDURI = case {ContactJID#jid.luser, ContactJID#jid.lserver} of - {"", _} -> ""; - {CUser, CServer} -> - case lists:member(CServer, ?MYHOSTS) of - false -> ""; - true -> "/admin/server/" ++ CServer ++ "/user/" ++ CUser ++ "/" - end + JIDURI = case {ContactJID#jid.luser, + ContactJID#jid.lserver} + of + {<<"">>, _} -> <<"">>; + {CUser, CServer} -> + case lists:member(CServer, ?MYHOSTS) of + false -> <<"">>; + true -> + <<"/admin/server/", CServer/binary, "/user/", + CUser/binary, "/">> + end end, case JIDURI of - [] -> - ?XAC("td", [{"class", "valign"}], jlib:jid_to_string(RosterJID)); - URI when is_list(URI) -> - ?XAE("td", [{"class", "valign"}], [?AC(JIDURI, jlib:jid_to_string(RosterJID))]) + <<>> -> + ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], + (jlib:jid_to_string(RosterJID))); + URI when is_binary(URI) -> + ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], + [?AC(JIDURI, (jlib:jid_to_string(RosterJID)))]) end. user_roster_parse_query(User, Server, Items, Query) -> - case lists:keysearch("addjid", 1, Query) of - {value, _} -> - case lists:keysearch("newjid", 1, Query) of - {value, {_, undefined}} -> - error; - {value, {_, SJID}} -> - case jlib:string_to_jid(SJID) of - JID when is_record(JID, jid) -> - user_roster_subscribe_jid(User, Server, JID), - ok; - error -> - error - end; - false -> - error - end; - false -> - case catch user_roster_item_parse_query( - User, Server, Items, Query) of - submitted -> - ok; - {'EXIT', _Reason} -> - error; - _ -> - nothing - end + case lists:keysearch(<<"addjid">>, 1, Query) of + {value, _} -> + case lists:keysearch(<<"newjid">>, 1, Query) of + {value, {_, SJID}} -> + case jlib:string_to_jid(SJID) of + JID when is_record(JID, jid) -> + user_roster_subscribe_jid(User, Server, JID), ok; + error -> error + end; + false -> error + end; + false -> + case catch user_roster_item_parse_query(User, Server, + Items, Query) + of + submitted -> ok; + {'EXIT', _Reason} -> error; + _ -> nothing + end end. - user_roster_subscribe_jid(User, Server, JID) -> out_subscription(User, Server, JID, subscribe), - UJID = jlib:make_jid(User, Server, ""), - ejabberd_router:route( - UJID, JID, {xmlelement, "presence", [{"type", "subscribe"}], []}). - -user_roster_item_parse_query(User, Server, Items, Query) -> - lists:foreach( - fun(R) -> - JID = R#roster.jid, - case lists:keysearch( - "validate" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of - {value, _} -> - JID1 = jlib:make_jid(JID), - out_subscription( - User, Server, JID1, subscribed), - UJID = jlib:make_jid(User, Server, ""), - ejabberd_router:route( - UJID, JID1, {xmlelement, "presence", - [{"type", "subscribed"}], []}), - throw(submitted); - false -> - case lists:keysearch( - "remove" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of - {value, _} -> - UJID = jlib:make_jid(User, Server, ""), - process_iq( - UJID, UJID, - #iq{type = set, - sub_el = {xmlelement, "query", - [{"xmlns", ?NS_ROSTER}], - [{xmlelement, "item", - [{"jid", jlib:jid_to_string(JID)}, - {"subscription", "remove"}], - []}]}}), - throw(submitted); - false -> - ok - end - - end - end, Items), + UJID = jlib:make_jid(User, Server, <<"">>), + ejabberd_router:route(UJID, JID, + #xmlel{name = <<"presence">>, + attrs = [{<<"type">>, <<"subscribe">>}], + children = []}). + +user_roster_item_parse_query(User, Server, Items, + Query) -> + lists:foreach(fun (R) -> + JID = R#roster.jid, + case lists:keysearch(<<"validate", + (ejabberd_web_admin:term_to_id(JID))/binary>>, + 1, Query) + of + {value, _} -> + JID1 = jlib:make_jid(JID), + out_subscription(User, Server, JID1, + subscribed), + UJID = jlib:make_jid(User, Server, <<"">>), + ejabberd_router:route(UJID, JID1, + #xmlel{name = + <<"presence">>, + attrs = + [{<<"type">>, + <<"subscribed">>}], + children = []}), + throw(submitted); + false -> + case lists:keysearch(<<"remove", + (ejabberd_web_admin:term_to_id(JID))/binary>>, + 1, Query) + of + {value, _} -> + UJID = jlib:make_jid(User, Server, + <<"">>), + process_iq(UJID, UJID, + #iq{type = set, + sub_el = + #xmlel{name = + <<"query">>, + attrs = + [{<<"xmlns">>, + ?NS_ROSTER}], + children = + [#xmlel{name + = + <<"item">>, + attrs + = + [{<<"jid">>, + jlib:jid_to_string(JID)}, + {<<"subscription">>, + <<"remove">>}], + children + = + []}]}}), + throw(submitted); + false -> ok + end + end + end, + Items), nothing. us_to_list({User, Server}) -> - jlib:jid_to_string({User, Server, ""}). + jlib:jid_to_string({User, Server, <<"">>}). webadmin_user(Acc, _User, _Server, Lang) -> - Acc ++ [?XE("h3", [?ACT("roster/", "Roster")])]. - + Acc ++ + [?XE(<<"h3">>, [?ACT(<<"roster/">>, <<"Roster">>)])]. + +export(_Server) -> + [{roster, + fun(Host, #roster{usj = {LUser, LServer, LJID}} = R) + when LServer == Host -> + Username = ejabberd_odbc:escape(LUser), + SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)), + ItemVals = record_to_string(R), + ItemGroups = groups_to_string(R), + odbc_queries:update_roster_sql(Username, SJID, + ItemVals, ItemGroups); + (_Host, _R) -> + [] + end}, + {roster_version, + fun(Host, #roster_version{us = {LUser, LServer}, version = Ver}) + when LServer == Host -> + Username = ejabberd_odbc:escape(LUser), + SVer = ejabberd_odbc:escape(Ver), + [[<<"delete from roster_version where username='">>, + Username, <<"';">>], + [<<"insert into roster_version(username, version) values('">>, + Username, <<"', '">>, SVer, <<"');">>]]; + (_Host, _R) -> + [] + end}]. -- cgit v1.2.3