From 50bef3787af73806f7311404bb1210a837999a15 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Sun, 19 Dec 2004 20:47:35 +0000 Subject: [PATCH] * src/mod_roster_odbc.erl: Roster support via ODBC (not completed) * src/ejabberd_auth_internal.erl: Added remove_user hook * src/ejabberd_auth_odbc.erl: Likewise * src/mod_roster.erl: Use remove_user hook * src/mod_offline.erl: Likewise * src/mod_offline_odbc.erl: Likewise * src/mod_last.erl: Likewise * src/mod_last_odbc.erl: Likewise * src/mod_vcard.erl: Likewise * src/mod_private.erl: Likewise * src/mod_roster.erl: Added hooks for functions exported by mod_roster * src/ejabberd_c2s.erl: Likewise * src/ejabberd_sm.erl: Likewise * src/mod_privacy.erl: Likewise * src/mod_last.erl: Likewise * src/mod_last_odbc.erl: Likewise SVN Revision: 294 --- ChangeLog | 22 + src/ejabberd_auth_internal.erl | 12 +- src/ejabberd_auth_odbc.erl | 12 +- src/ejabberd_c2s.erl | 22 +- src/ejabberd_sm.erl | 28 +- src/mod_last.erl | 9 +- src/mod_last_odbc.erl | 9 +- src/mod_offline.erl | 4 + src/mod_offline_odbc.erl | 4 + src/mod_privacy.erl | 12 +- src/mod_private.erl | 4 + src/mod_roster.erl | 45 +- src/mod_roster_odbc.erl | 745 +++++++++++++++++++++++++++++++++ src/mod_vcard.erl | 4 + 14 files changed, 875 insertions(+), 57 deletions(-) create mode 100644 src/mod_roster_odbc.erl diff --git a/ChangeLog b/ChangeLog index 4924e3208..d72d4bb38 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2004-12-19 Alexey Shchepin + + * src/mod_roster_odbc.erl: Roster support via ODBC (not completed) + + * src/ejabberd_auth_internal.erl: Added remove_user hook + * src/ejabberd_auth_odbc.erl: Likewise + * src/mod_roster.erl: Use remove_user hook + * src/mod_offline.erl: Likewise + * src/mod_offline_odbc.erl: Likewise + * src/mod_last.erl: Likewise + * src/mod_last_odbc.erl: Likewise + * src/mod_vcard.erl: Likewise + * src/mod_private.erl: Likewise + + * src/mod_roster.erl: Added hooks for functions exported by + mod_roster + * src/ejabberd_c2s.erl: Likewise + * src/ejabberd_sm.erl: Likewise + * src/mod_privacy.erl: Likewise + * src/mod_last.erl: Likewise + * src/mod_last_odbc.erl: Likewise + 2004-12-14 Alexey Shchepin * src/ejabberd_sm.erl: Updated missed message passing from diff --git a/src/ejabberd_auth_internal.erl b/src/ejabberd_auth_internal.erl index b502eaf95..1743c8478 100644 --- a/src/ejabberd_auth_internal.erl +++ b/src/ejabberd_auth_internal.erl @@ -133,11 +133,7 @@ remove_user(User) -> mnesia:delete({passwd, LUser}) end, mnesia:transaction(F), - catch mod_roster:remove_user(User), - catch mod_offline:remove_user(User), - catch mod_last:remove_user(User), - catch mod_vcard:remove_user(User), - catch mod_private:remove_user(User). + ejabberd_hooks:run(remove_user, [User]). remove_user(User, Password) -> LUser = jlib:nodeprep(User), @@ -154,11 +150,7 @@ remove_user(User, Password) -> end, case mnesia:transaction(F) of {atomic, ok} -> - catch mod_roster:remove_user(User), - catch mod_offline:remove_user(User), - catch mod_last:remove_user(User), - catch mod_vcard:remove_user(User), - catch mod_private:remove_user(User), + ejabberd_hooks:run(remove_user, [User]), ok; {atomic, Res} -> Res; diff --git a/src/ejabberd_auth_odbc.erl b/src/ejabberd_auth_odbc.erl index 8003338f1..35d1f92d0 100644 --- a/src/ejabberd_auth_odbc.erl +++ b/src/ejabberd_auth_odbc.erl @@ -174,11 +174,7 @@ remove_user(User) -> Username = ejabberd_odbc:escape(LUser), catch ejabberd_odbc:sql_query( ["delete from users where username='", Username ,"'"]), - catch mod_roster:remove_user(User), - catch mod_offline:remove_user(User), - catch mod_last:remove_user(User), - catch mod_vcard:remove_user(User), - catch mod_private:remove_user(User) + ejabberd_hooks:run(remove_user, [User]) end. remove_user(User, Password) -> @@ -196,11 +192,7 @@ remove_user(User, Password) -> "where username='", Username, "' and password='", Pass, "';" "commit"]) of {selected, ["password"], [{Password}]} -> - catch mod_roster:remove_user(User), - catch mod_offline:remove_user(User), - catch mod_last:remove_user(User), - catch mod_vcard:remove_user(User), - catch mod_private:remove_user(User), + ejabberd_hooks:run(remove_user, [User]), ok; {selected, ["password"], []} -> not_exists; diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 92b3fb214..c46e77ca3 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -314,7 +314,10 @@ wait_for_auth({xmlstreamelement, El}, StateData) -> Res = setelement(4, Res1, []), send_element(StateData, Res), change_shaper(StateData, JID), - {Fs, Ts} = mod_roster:get_subscription_lists(U), + {Fs, Ts} = ejabberd_hooks:run_fold( + roster_get_subscription_lists, + {[], []}, + [U]), LJID = jlib:jid_tolower( jlib:jid_remove_resource(JID)), Fs1 = [LJID | Fs], @@ -606,7 +609,10 @@ wait_for_session({xmlstreamelement, El}, StateData) -> Res = jlib:make_result_iq_reply(El), send_element(StateData, Res), change_shaper(StateData, JID), - {Fs, Ts} = mod_roster:get_subscription_lists(U), + {Fs, Ts} = ejabberd_hooks:run_fold( + roster_get_subscription_lists, + {[], []}, + [U]), LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), Fs1 = [LJID | Fs], Ts1 = [LJID | Ts], @@ -1179,19 +1185,23 @@ presence_track(From, To, Packet, StateData) -> pres_a = A}; "subscribe" -> ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet), - mod_roster:out_subscription(User, To, subscribe), + ejabberd_hooks:run(roster_out_subscription, + [User, To, subscribe]), StateData; "subscribed" -> ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet), - mod_roster:out_subscription(User, To, subscribed), + ejabberd_hooks:run(roster_out_subscription, + [User, To, subscribed]), StateData; "unsubscribe" -> ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet), - mod_roster:out_subscription(User, To, unsubscribe), + ejabberd_hooks:run(roster_out_subscription, + [User, To, unsubscribe]), StateData; "unsubscribed" -> ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet), - mod_roster:out_subscription(User, To, unsubscribed), + ejabberd_hooks:run(roster_out_subscription, + [User, To, unsubscribed]), StateData; "error" -> ejabberd_router:route(From, To, Packet), diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 4bf59fb12..85b9b8456 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -166,24 +166,28 @@ do_route(From, To, Packet) -> {Pass, Subsc} = case xml:get_attr_s("type", Attrs) of "subscribe" -> - {mod_roster:in_subscription(User, - From, - subscribe), + {ejabberd_hooks:run_fold( + roster_in_subscription, + false, + [User, From, subscribe]), true}; "subscribed" -> - {mod_roster:in_subscription(User, - From, - subscribed), + {ejabberd_hooks:run_fold( + roster_in_subscription, + false, + [User, From, subscribed]), true}; "unsubscribe" -> - {mod_roster:in_subscription(User, - From, - unsubscribe), + {ejabberd_hooks:run_fold( + roster_in_subscription, + false, + [User, From, unsubscribe]), true}; "unsubscribed" -> - {mod_roster:in_subscription(User, - From, - unsubscribed), + {ejabberd_hooks:run_fold( + roster_in_subscription, + false, + [User, From, unsubscribed]), true}; _ -> {true, false} diff --git a/src/mod_last.erl b/src/mod_last.erl index 3e7a73fd9..c20b83b0a 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -35,10 +35,16 @@ start(Opts) -> ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_LAST, ?MODULE, process_sm_iq, IQDisc), + ejabberd_hooks:add(remove_user, + ?MODULE, remove_user, 50), ejabberd_hooks:add(unset_presence_hook, ?MODULE, on_presence_update, 50). stop() -> + ejabberd_hooks:delete(remove_user, + ?MODULE, remove_user, 50), + ejabberd_hooks:delete(unset_presence_hook, + ?MODULE, on_presence_update, 50), gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_LAST), gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_LAST). @@ -63,7 +69,8 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) -> get -> User = To#jid.luser, {Subscription, _Groups} = - mod_roster:get_jid_info(User, From), + ejabberd_hooks:run_fold( + roster_get_jid_info, {none, []}, [User, From]), if (Subscription == both) or (Subscription == from) -> case catch mod_privacy:get_user_list(User) of diff --git a/src/mod_last_odbc.erl b/src/mod_last_odbc.erl index 4937778db..1a670afc1 100644 --- a/src/mod_last_odbc.erl +++ b/src/mod_last_odbc.erl @@ -29,10 +29,16 @@ start(Opts) -> ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_LAST, ?MODULE, process_sm_iq, IQDisc), + ejabberd_hooks:add(remove_user, + ?MODULE, remove_user, 50), ejabberd_hooks:add(unset_presence_hook, ?MODULE, on_presence_update, 50). stop() -> + ejabberd_hooks:delete(remove_user, + ?MODULE, remove_user, 50), + ejabberd_hooks:delete(unset_presence_hook, + ?MODULE, on_presence_update, 50), gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_LAST), gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_LAST). @@ -57,7 +63,8 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) -> get -> User = To#jid.luser, {Subscription, _Groups} = - mod_roster:get_jid_info(User, From), + ejabberd_hooks:run_fold( + roster_get_jid_info, {none, []}, [User, From]), if (Subscription == both) or (Subscription == from) -> case catch mod_privacy:get_user_list(User) of diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 6959dbcef..b59987e4b 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -41,6 +41,8 @@ start(_) -> ?MODULE, store_packet, 50), ejabberd_hooks:add(resend_offline_messages_hook, ?MODULE, pop_offline_messages, 50), + ejabberd_hooks:add(remove_user, + ?MODULE, remove_user, 50), register(?PROCNAME, spawn(?MODULE, init, [])). init() -> @@ -84,6 +86,8 @@ stop() -> ?MODULE, store_packet, 50), ejabberd_hooks:delete(resend_offline_messages_hook, ?MODULE, pop_offline_messages, 50), + ejabberd_hooks:delete(remove_user, + ?MODULE, remove_user, 50), exit(whereis(?PROCNAME), stop), ok. diff --git a/src/mod_offline_odbc.erl b/src/mod_offline_odbc.erl index abc06b471..e2f87c67c 100644 --- a/src/mod_offline_odbc.erl +++ b/src/mod_offline_odbc.erl @@ -35,6 +35,8 @@ start(_) -> ?MODULE, store_packet, 50), ejabberd_hooks:add(resend_offline_messages_hook, ?MODULE, pop_offline_messages, 50), + ejabberd_hooks:add(remove_user, + ?MODULE, remove_user, 50), register(?PROCNAME, spawn(?MODULE, init, [])). init() -> @@ -100,6 +102,8 @@ stop() -> ?MODULE, store_packet, 50), ejabberd_hooks:delete(resend_offline_messages_hook, ?MODULE, pop_offline_messages, 50), + ejabberd_hooks:delete(remove_user, + ?MODULE, remove_user, 50), exit(whereis(?PROCNAME), stop), ok. diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index b89012e96..166a11c40 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -551,25 +551,29 @@ check_packet(User, {message, in} -> LJID = jlib:jid_tolower(From), {Subscription, Groups} = - mod_roster:get_jid_info(User, LJID), + ejabberd_hooks:run_fold( + roster_get_jid_info, {none, []}, [User, LJID]), check_packet_aux(List, message, LJID, Subscription, Groups); {iq, in} -> LJID = jlib:jid_tolower(From), {Subscription, Groups} = - mod_roster:get_jid_info(User, LJID), + ejabberd_hooks:run_fold( + roster_get_jid_info, {none, []}, [User, LJID]), check_packet_aux(List, iq, LJID, Subscription, Groups); {presence, in} -> LJID = jlib:jid_tolower(From), {Subscription, Groups} = - mod_roster:get_jid_info(User, LJID), + ejabberd_hooks:run_fold( + roster_get_jid_info, {none, []}, [User, LJID]), check_packet_aux(List, presence_in, LJID, Subscription, Groups); {presence, out} -> LJID = jlib:jid_tolower(To), {Subscription, Groups} = - mod_roster:get_jid_info(User, LJID), + ejabberd_hooks:run_fold( + roster_get_jid_info, {none, []}, [User, LJID]), check_packet_aux(List, presence_out, LJID, Subscription, Groups); _ -> diff --git a/src/mod_private.erl b/src/mod_private.erl index fa77ab5a1..487386fd9 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -27,10 +27,14 @@ start(Opts) -> mnesia:create_table(private_storage, [{disc_only_copies, [node()]}, {attributes, record_info(fields, private_storage)}]), + ejabberd_hooks:add(remove_user, + ?MODULE, remove_user, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_PRIVATE, ?MODULE, process_sm_iq, IQDisc). stop() -> + ejabberd_hooks:delete(remove_user, + ?MODULE, remove_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_PRIVATE). diff --git a/src/mod_roster.erl b/src/mod_roster.erl index b356b412e..724b8d682 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -15,12 +15,12 @@ -export([start/1, stop/0, process_iq/3, process_local_iq/3, - get_subscription_lists/1, - in_subscription/3, + get_subscription_lists/2, + in_subscription/4, out_subscription/3, set_items/2, remove_user/1, - get_jid_info/2]). + get_jid_info/3]). -include("ejabberd.hrl"). -include("jlib.hrl"). @@ -40,10 +40,30 @@ start(Opts) -> mnesia:create_table(roster,[{disc_copies, [node()]}, {attributes, record_info(fields, roster)}]), mnesia:add_table_index(roster, user), + ejabberd_hooks:add(roster_in_subscription, + ?MODULE, in_subscription, 50), + ejabberd_hooks:add(roster_out_subscription, + ?MODULE, out_subscription, 50), + ejabberd_hooks:add(roster_get_subscription_lists, + ?MODULE, get_subscription_lists, 50), + ejabberd_hooks:add(roster_get_jid_info, + ?MODULE, get_jid_info, 50), + ejabberd_hooks:add(remove_user, + ?MODULE, remove_user, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_ROSTER, ?MODULE, process_iq, IQDisc). stop() -> + ejabberd_hooks:delete(roster_in_subscription, + ?MODULE, in_subscription, 50), + ejabberd_hooks:delete(roster_out_subscription, + ?MODULE, out_subscription, 50), + ejabberd_hooks:delete(roster_get_subscription_lists, + ?MODULE, get_subscription_lists, 50), + ejabberd_hooks:delete(roster_get_jid_info, + ?MODULE, get_jid_info, 50), + ejabberd_hooks:delete(remove_user, + ?MODULE, remove_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_ROSTER). @@ -273,13 +293,12 @@ process_item_els(Item, []) -> push_item(User, From, Item) -> - ejabberd_sm ! {route, - jlib:make_jid("", "", ""), - jlib:make_jid(User, "", ""), - {xmlelement, "broadcast", [], - [{item, - Item#roster.jid, - Item#roster.subscription}]}}, + ejabberd_sm:route(jlib:make_jid("", "", ""), + jlib:make_jid(User, "", ""), + {xmlelement, "broadcast", [], + [{item, + Item#roster.jid, + Item#roster.subscription}]}), lists:foreach(fun(Resource) -> push_item(User, Resource, From, Item) end, ejabberd_sm:get_user_resources(User)). @@ -311,7 +330,7 @@ push_item(User, Resource, From, Item) -> -endif. -get_subscription_lists(User) -> +get_subscription_lists(_, User) -> LUser = jlib:nodeprep(User), case mnesia:dirty_index_read(roster, LUser, #roster.user) of Items when is_list(Items) -> @@ -341,7 +360,7 @@ ask_to_pending(Ask) -> Ask. -in_subscription(User, JID, Type) -> +in_subscription(_, User, JID, Type) -> process_subscription(in, User, JID, Type). out_subscription(User, JID, Type) -> @@ -594,7 +613,7 @@ process_item_attrs_ws(Item, []) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -get_jid_info(User, JID) -> +get_jid_info(_, User, JID) -> LUser = jlib:nodeprep(User), LJID = jlib:jid_tolower(JID), case catch mnesia:dirty_read(roster, {LUser, LJID}) of diff --git a/src/mod_roster_odbc.erl b/src/mod_roster_odbc.erl new file mode 100644 index 000000000..bc6a8e324 --- /dev/null +++ b/src/mod_roster_odbc.erl @@ -0,0 +1,745 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_roster_odbc.erl +%%% Author : Alexey Shchepin +%%% Purpose : Roster management +%%% Created : 15 Dec 2004 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(mod_roster_odbc). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-behaviour(gen_mod). + +-export([start/1, stop/0, + process_iq/3, + process_local_iq/3, + get_subscription_lists/2, + in_subscription/4, + out_subscription/3, + set_items/2, + remove_user/1, + get_jid_info/3]). + +-include("ejabberd.hrl"). +-include("jlib.hrl"). + +-record(roster, {user, + jid, + name = "", + subscription = none, + ask = none}). + +start(Opts) -> + IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), + ejabberd_hooks:add(roster_out_subscription, + ?MODULE, out_subscription, 50), + ejabberd_hooks:add(roster_in_subscription, + ?MODULE, in_subscription, 50), + ejabberd_hooks:add(roster_out_subscription, + ?MODULE, out_subscription, 50), + ejabberd_hooks:add(roster_get_subscription_lists, + ?MODULE, get_subscription_lists, 50), + ejabberd_hooks:add(roster_get_jid_info, + ?MODULE, get_jid_info, 50), + ejabberd_hooks:add(remove_user, + ?MODULE, remove_user, 50), + gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_ROSTER, + ?MODULE, process_iq, IQDisc). + +stop() -> + ejabberd_hooks:delete(roster_in_subscription, + ?MODULE, in_subscription, 50), + ejabberd_hooks:delete(roster_out_subscription, + ?MODULE, out_subscription, 50), + ejabberd_hooks:delete(roster_get_subscription_lists, + ?MODULE, get_subscription_lists, 50), + ejabberd_hooks:delete(roster_get_jid_info, + ?MODULE, get_jid_info, 50), + ejabberd_hooks:delete(remove_user, + ?MODULE, remove_user, 50), + gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_ROSTER). + + +-define(PSI_ROSTER_WORKAROUND, true). + +-ifdef(PSI_ROSTER_WORKAROUND). + +process_iq(From, To, IQ) -> + #iq{sub_el = SubEl} = IQ, + #jid{lserver = LServer} = From, + case ?MYNAME of + LServer -> + ResIQ = process_local_iq(From, To, IQ), + ejabberd_router:route(From, From, + jlib:iq_to_xml(ResIQ)), + ignore; + _ -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]} + end. + +-else. + +process_iq(From, To, IQ) -> + #iq{sub_el = SubEl} = IQ, + #jid{lserver = LServer} = From, + case ?MYNAME of + LServer -> + process_local_iq(From, To, IQ); + _ -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]} + end. + +-endif. + +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) + end. + + + +process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) -> + LUser = From#jid.luser, + Username = ejabberd_odbc:escape(LUser), + case catch ejabberd_odbc:sql_query( + ["select username, jid, nick, subscription, ask, " + "server, subscribe, type from rosterusers " + "where username='", Username, "'"]) of + {selected, ["username", "jid", "nick", "subscription", "ask", + "server", "subscribe", "type"], + Items} when is_list(Items) -> + XItems = lists:flatmap( + fun(I) -> + case raw_to_record(I) of + error -> + []; + R -> + [item_to_xml(R)] + end + end, Items), + IQ#iq{type = result, + sub_el = [{xmlelement, "query", + [{"xmlns", ?NS_ROSTER}], + XItems}]}; + _ -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]} + end. + +item_to_xml(Item) -> + Attrs1 = [{"jid", jlib:jid_to_string(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]; + _ -> + Attrs3 + end, + % TODO + %SubEls = lists:map(fun(G) -> + % {xmlelement, "group", [], [{xmlcdata, G}]} + % end, Item#roster.groups), + SubEls = [], + {xmlelement, "item", Attrs, SubEls}. + + +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), + 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} = From, + case JID1 of + error -> + ok; + _ -> + JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource}, + LJID = jlib:jid_tolower(JID1), + Username = ejabberd_odbc:escape(LUser), + SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)), + case catch ejabberd_odbc:sql_query( + ["select username, jid, nick, subscription, ask, " + "server, subscribe, type from rosterusers " + "where username='", Username, "' " + "and jid='", SJID, "'"]) of + {selected, ["username", "jid", "nick", "subscription", "ask", + "server", "subscribe", "type"], + Res} -> + Item = case Res of + [] -> + #roster{user = LUser, + jid = LJID}; + [I] -> + (raw_to_record(I))#roster{user = LUser, + jid = JID, + name = ""} + end, + Item1 = process_item_attrs(Item, Attrs), + case Item1#roster.subscription of + remove -> + catch ejabberd_odbc:sql_query( + ["begin;" + "delete from rosterusers " + " where username='", Username, "' " + " and jid='", SJID, "';" + "delete from rostergroups " + " where username='", Username, "' " + " and jid='", SJID, "';" + "commit"]); + _ -> + ItemVals = record_to_string(Item1), + catch ejabberd_odbc:sql_query( + ["begin;" + "delete from rosterusers " + " where username='", Username, "' " + " and jid='", SJID, "';" + "insert into rosterusers(" + " username, jid, nick, " + " subscription, ask, " + " server, subscribe, type) " + " values ", ItemVals, ";" + "delete from rostergroups " + " where username='", Username, "' " + " and jid='", SJID, "';" + % TODO + "commit"]) + + %mnesia:write(Item1) + end, + push_item(User, To, Item1), + case Item1#roster.subscription of + remove -> + 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 -> + ejabberd_router:route( + jlib:jid_remove_resource(From), + jlib:make_jid(Item#roster.jid), + {xmlelement, "presence", + [{"type", "unsubscribe"}], + []}); + true -> ok + end, + if IsFrom -> + ejabberd_router:route( + jlib:jid_remove_resource(From), + jlib:make_jid(Item#roster.jid), + {xmlelement, "presence", + [{"type", "unsubscribed"}], + []}); + true -> ok + end, + ok; + _ -> + ok + end; + E -> + ?DEBUG("ROSTER: roster item set error: ~p~n", [E]), + ok + end + end; +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.user, JID1#jid.server, JID1#jid.resource}, + 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. + + +% TODO +%process_item_els(Item, [{xmlelement, Name, Attrs, 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 +% end; +process_item_els(Item, [{xmlcdata, _} | Els]) -> + process_item_els(Item, Els); +process_item_els(Item, []) -> + Item. + + +push_item(User, From, Item) -> + ejabberd_sm:route(jlib:make_jid("", "", ""), + jlib:make_jid(User, "", ""), + {xmlelement, "broadcast", [], + [{item, + Item#roster.jid, + Item#roster.subscription}]}), + lists:foreach(fun(Resource) -> + push_item(User, Resource, From, Item) + end, ejabberd_sm:get_user_resources(User)). + +% TODO: don't push to those who not load roster +-ifdef(PSI_ROSTER_WORKAROUND). + +push_item(User, Resource, _From, Item) -> + ResIQ = #iq{type = set, xmlns = ?NS_ROSTER, + sub_el = [{xmlelement, "query", + [{"xmlns", ?NS_ROSTER}], + [item_to_xml(Item)]}]}, + ejabberd_router:route( + jlib:make_jid(User, ?MYNAME, Resource), + jlib:make_jid(User, ?MYNAME, Resource), + jlib:iq_to_xml(ResIQ)). + +-else. + +push_item(User, Resource, From, Item) -> + ResIQ = #iq{type = set, xmlns = ?NS_ROSTER, + sub_el = [{xmlelement, "query", + [{"xmlns", ?NS_ROSTER}], + [item_to_xml(Item)]}]}, + ejabberd_router:route( + From, + jlib:make_jid(User, ?MYNAME, Resource), + jlib:iq_to_xml(ResIQ)). + +-endif. + +get_subscription_lists(_, User) -> + LUser = jlib:nodeprep(User), + Username = ejabberd_odbc:escape(LUser), + case catch ejabberd_odbc:sql_query( + ["select username, jid, nick, subscription, ask, " + "server, subscribe, type from rosterusers " + "where username='", Username, "'"]) of + {selected, ["username", "jid", "nick", "subscription", "ask", + "server", "subscribe", "type"], + Items} when is_list(Items) -> + fill_subscription_lists(Items, [], []); + _ -> + {[], []} + end. + +fill_subscription_lists([I | Is], F, T) -> + J = I#roster.jid, + case I#roster.subscription of + both -> + fill_subscription_lists(Is, [J | F], [J | T]); + from -> + fill_subscription_lists(Is, [J | F], T); + to -> + fill_subscription_lists(Is, F, [J | T]); + _ -> + fill_subscription_lists(Is, F, T) + end; +fill_subscription_lists([], F, T) -> + {F, T}. + +ask_to_pending(subscribe) -> out; +ask_to_pending(unsubscribe) -> none; +ask_to_pending(Ask) -> Ask. + + + +in_subscription(_, User, JID, Type) -> + process_subscription(in, User, JID, Type). + +out_subscription(User, JID, Type) -> + process_subscription(out, User, JID, Type). + +process_subscription(Direction, User, JID1, Type) -> + LUser = jlib:nodeprep(User), + LJID = jlib:jid_tolower(JID1), + Username = ejabberd_odbc:escape(LUser), + SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)), + Item = case catch ejabberd_odbc:sql_query( + ["select username, jid, nick, subscription, ask, " + "server, subscribe, type from rosterusers " + "where username='", Username, "' " + "and jid='", SJID, "'"]) of + {selected, ["username", "jid", "nick", "subscription", "ask", + "server", "subscribe", "type"], + [I]} -> + raw_to_record(I); + _ -> + #roster{user = LUser, + 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, + Push = case NewState of + none -> + none; + {Subscription, Pending} -> + NewItem = Item#roster{subscription = Subscription, + ask = Pending}, + ItemVals = record_to_string(NewItem), + catch ejabberd_odbc:sql_query( + ["begin;" + "delete from rosterusers " + " where username='", Username, "' " + " and jid='", SJID, "';" + "insert into rosterusers(" + " username, jid, nick, " + " subscription, ask, " + " server, subscribe, type) " + " values ", ItemVals, ";" + "commit"]), + {push, NewItem} + end, + case AutoReply of + none -> + ok; + _ -> + T = case AutoReply of + subscribed -> "subscribed"; + unsubscribed -> "unsubscribed" + end, + ejabberd_router:route( + jlib:make_jid(User, ?MYNAME, ""), JID1, + {xmlelement, "presence", [{"type", T}], []}) + end, + case Push of + {push, PushItem} -> + push_item(User, jlib:make_jid("", ?MYNAME, ""), PushItem), + true; + none -> + false + end. + + +%% in_state_change(Subscription, Pending, Type) -> NewState +%% NewState = none | {NewSubscription, NewPending} + +in_state_change(none, none, subscribe) -> {none, in}; +in_state_change(none, none, subscribed) -> {to, none}; % Workaround for gateways +%in_state_change(none, none, subscribed) -> none; +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) -> {to, in}; % Workaround for gateways +%in_state_change(none, in, subscribed) -> none; +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) -> 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_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) -> 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) -> + LUser = jlib:nodeprep(User), + Username = ejabberd_odbc:escape(LUser), + catch ejabberd_odbc:sql_query( + ["begin;" + "delete from rosterusers " + " where username='", Username, "';" + "delete from rostergroups " + " where username='", Username, "';" + "commit"]), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +set_items(User, SubEl) -> + {xmlelement, _Name, _Attrs, Els} = SubEl, + LUser = jlib:nodeprep(User), + F = fun() -> + lists:foreach(fun(El) -> process_item_set_t(LUser, El) end, Els) + end, + mnesia:transaction(F). + +process_item_set_t(LUser, {xmlelement, _Name, Attrs, 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{user = LUser, + jid = LJID}, + Item1 = process_item_attrs_ws(Item, Attrs), + Item2 = process_item_els(Item1, Els), + case Item2#roster.subscription of + remove -> + mnesia:delete({roster, {LUser, LJID}}); + _ -> + mnesia:write(Item2) + end + end; +process_item_set_t(_LUser, _) -> + 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.user, JID1#jid.server, JID1#jid.resource}, + 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. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_jid_info(_, User, JID) -> +% TODO +% LUser = jlib:nodeprep(User), +% LJID = jlib:jid_tolower(JID), +% case catch mnesia:dirty_read(roster, {LUser, LJID}) of +% [#roster{subscription = Subscription, groups = Groups}] -> +% {Subscription, Groups}; +% _ -> +% LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), +% if +% LRJID == LJID -> +% {none, []}; +% true -> +% case catch mnesia:dirty_read(roster, {LUser, LRJID}) of +% [#roster{subscription = Subscription, +% groups = Groups}] -> +% {Subscription, Groups}; +% _ -> +% {none, []} +% end +% end +% end. +{none, []}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +raw_to_record({User, SJID, Nick, SSubscription, SAsk, + _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{user = User, + jid = LJID, + name = Nick, + subscription = Subscription, + ask = Ask} + end. + +record_to_string(#roster{user = User, + jid = JID, + name = Name, + subscription = Subscription, + ask = Ask}) -> + Username = ejabberd_odbc:escape(User), + SJID = ejabberd_odbc:escape(jlib:jid_to_string(JID)), + Nick = ejabberd_odbc:escape(Name), + SSubscription = case Subscription of + both -> "B"; + to -> "T"; + from -> "F"; + none -> "N" + end, + SAsk = case Ask of + subscribe -> "S"; + unsubscribe -> "U"; + both -> "B"; + out -> "O"; + in -> "I"; + none -> "N" + end, + ["(" + "'", Username, "'," + "'", SJID, "'," + "'", Nick, "'," + "'", SSubscription, "'," + "'", SAsk, "'," + "'N', '', 'item')"]. + diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 786bca78b..08789f115 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -60,6 +60,8 @@ start(Opts) -> mnesia:add_table_index(vcard_search, lorgname), mnesia:add_table_index(vcard_search, lorgunit), + ejabberd_hooks:add(remove_user, + ?MODULE, remove_user, 50), IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_VCARD, ?MODULE, process_local_iq, IQDisc), @@ -98,6 +100,8 @@ loop(Host) -> end. stop() -> + ejabberd_hooks:delete(remove_user, + ?MODULE, remove_user, 50), gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_VCARD), gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_VCARD), catch mod_disco:unregister_sm_feature(?NS_VCARD),